Browse Source

add basic server

main
parent
commit
818dcbdc01
11 changed files with 1495 additions and 0 deletions
  1. +0
    -0
      __packages__/.gitkeep
  2. +1
    -0
      __packages__/server/.gitignore
  3. +54
    -0
      __packages__/server/assets/vocabulary/外す.md
  4. +30
    -0
      __packages__/server/package.json
  5. +30
    -0
      __packages__/server/src/index.ts
  6. +98
    -0
      __packages__/server/src/router/kanji.ts
  7. +100
    -0
      __packages__/server/src/router/vocabulary.ts
  8. +32
    -0
      __packages__/server/src/shared.ts
  9. +7
    -0
      __packages__/server/src/tsconfig.json
  10. +3
    -0
      __packages__/server/tsconfig.json
  11. +1140
    -0
      __packages__/server/yarn.lock

+ 0
- 0
__packages__/.gitkeep View File


+ 1
- 0
__packages__/server/.gitignore View File

@ -0,0 +1 @@
/lib/

+ 54
- 0
__packages__/server/assets/vocabulary/外す.md View File

@ -0,0 +1,54 @@
---
meanings:
- meaning:
- To Disconnect Something
- To Remove Something
- to take off something
examples:
- ja: 眼鏡を外す
en: take off one's glasses
- ja: 上着のボタンを外す
en: unbutton [undo the buttons of] one's coat
- ja: 犬の鎖を外す
en: unchain a dog
- ja: 箱のふたを外した
en: I removed the lid from the box.
- ja: ドアの鍵かぎ[留め金]を外しなさい
en: Unlock [Unlatch] the door.
- ja: 彼は先発メンバーから外された
en: He was removed [dropped/scratched] from the starting lineup.
- meaning:
- to leave the room
- to leave the seat
- to leave
skip: true
examples:
- ja: 席を外す
en: leave one's seat
- ja: 彼はいつの間にか席を外していた
en: He had slipped out of the room unnoticed.
- ja: 彼はただいま席を外しております
en: 〔電話などで〕He is not at his desk just now.
- meaning:
- to elude
- to dodge
- to evade
examples:
- ja: 相手の打撃を巧みに外した
en: He skillfully dodged his opponent's blow.
- ja: 彼女は私の質問を外した
en: She evaded my question.
- ja: 投手が次の一球を外した
en: The pitcher wasted the next pitch.
- ja: 投手はゆるい球で打者のタイミングを外した
en: The pitcher threw the batter's timing off [upset the batter's timing] with a slow pitch.
- meaning:
- To Miss Something
- to miss the mark
examples:
- ja: 機会[的]を外す
en: miss a chance [the mark]
- meaning:
- To Exclude Something
- to exclude someone
---

+ 30
- 0
__packages__/server/package.json View File

@ -0,0 +1,30 @@
{
"name": "@wk-extra/server",
"private": true,
"version": "1.0.0",
"main": "lib/index.js",
"license": "MIT",
"scripts": {
"build:server": "tsc -P src/tsconfig.json",
"dev": "yarn tsmon src/index.ts",
"ts": "ts-node -r tsconfig-paths/register",
"tsmon": "NODE_ENV=development ts-node-dev -r tsconfig-paths/register"
},
"dependencies": {
"@fastify/cors": "^7.0.0",
"fastify": "^3.29.0",
"gray-matter": "^4.0.3",
"js-yaml": "^4.1.0",
"jsonschema-definer": "^1.3.2",
"sanitize-filename": "^1.6.3"
},
"devDependencies": {
"@types/js-yaml": "^4.0.5",
"fast-glob": "^3.2.11",
"pino-pretty": "^7.6.1",
"ts-node": "^10.7.0",
"ts-node-dev": "^1.1.8",
"tsconfig-paths": "^4.0.0",
"typescript": "^4.6.4"
}
}

+ 30
- 0
__packages__/server/src/index.ts View File

@ -0,0 +1,30 @@
import fastifyCors from '@fastify/cors';
import fastify from 'fastify';
import kanjiRouter from './router/kanji';
import vocabularyRouter from './router/vocabulary';
import { isDev } from './shared';
async function main() {
const port = parseInt(process.env['PORT']!) || 13358;
process.env['PORT'] = port.toString();
const app = fastify({
logger: {
prettyPrint: isDev,
},
});
app.register(fastifyCors);
app.register(kanjiRouter, {
prefix: '/api/kanji',
});
app.register(vocabularyRouter, {
prefix: '/api/vocabulary',
});
await app.listen(port, isDev ? '' : '0.0.0.0');
}
main();

+ 98
- 0
__packages__/server/src/router/kanji.ts View File

@ -0,0 +1,98 @@
import glob from 'fast-glob';
import { FastifyPluginAsync } from 'fastify';
import yaml from 'js-yaml';
import S from 'jsonschema-definer';
import sanitize from 'sanitize-filename';
import { oItem, readYAML } from '../shared';
export const kanjiRouter: FastifyPluginAsync = async (f) => {
f.get<{
Params: {
entry: string;
};
}>(':entry.md', async (req) => {
const { entry } = req.params;
const m = getEntry(entry);
if (!m) {
throw { statusCode: 404 };
}
const { data, content } = m;
if (content.trim()) return content;
let md = '';
if (data.meanings) {
md += data.meanings
.map((v, i) => {
const out = [`${i + 1}. ${v.meaning.join('; ')}`];
if (v.examples) {
out.push(
...v.examples.map(
(ex) => ' - ' + ex.ja + (ex.en ? `\n - ` + ex.en : ''),
),
);
}
return out.join('\n');
})
.join('\n');
}
return (
md ||
yaml
.dump(data)
.split('\n')
.map((ln) => (ln ? ' ' + ln : ln))
.join('\n')
);
});
f.get<{
Params: {
entry: string;
};
}>(':entry', async (req): Promise<typeof sEntry.type> => {
const { entry } = req.params;
const m = getEntry(entry);
if (!m) {
throw { statusCode: 404 };
}
return m.data;
});
};
export default kanjiRouter;
const sEntry = S.shape({
readings: S.list(
S.shape({
reading: S.string(),
type: S.string(),
...oItem,
}).additionalProperties(true),
).optional(),
meanings: S.list(
S.shape({
meaning: S.list(S.string()),
...oItem,
}).additionalProperties(true),
).optional(),
}).additionalProperties(true);
function getEntry(entry: string) {
const filepath = glob.sync(`assets/kanji/**/${sanitize(entry)}.md`).sort()[0];
if (!filepath) return null;
const { data, content } = readYAML(filepath);
return {
data: sEntry.ensure(data),
content,
};
}

+ 100
- 0
__packages__/server/src/router/vocabulary.ts View File

@ -0,0 +1,100 @@
import glob from 'fast-glob';
import { FastifyPluginAsync } from 'fastify';
import yaml from 'js-yaml';
import S from 'jsonschema-definer';
import sanitize from 'sanitize-filename';
import { oItem, readYAML } from '../shared';
export const vocabularyRouter: FastifyPluginAsync = async (f) => {
f.get<{
Params: {
entry: string;
};
}>('/:entry.md', async (req) => {
const { entry } = req.params;
const m = getEntry(entry);
if (!m) {
throw { statusCode: 404 };
}
const { data, content } = m;
if (content.trim()) return content;
let md = '';
if (data.meanings) {
md += data.meanings
.map((v, i) => {
const out = [`${i + 1}. ${v.meaning.join('; ')}`];
if (v.examples) {
out.push(
...v.examples.map(
(ex) => ' - ' + ex.ja + (ex.en ? `\n - ` + ex.en : ''),
),
);
}
return out.join('\n');
})
.join('\n');
}
return (
md ||
yaml
.dump(data)
.split('\n')
.map((ln) => (ln ? ' ' + ln : ln))
.join('\n')
);
});
f.get<{
Params: {
entry: string;
};
}>('/:entry', async (req): Promise<typeof sEntry.type> => {
const { entry } = req.params;
const m = getEntry(entry);
if (!m) {
throw { statusCode: 404 };
}
return m.data;
});
};
export default vocabularyRouter;
const sEntry = S.shape({
readings: S.list(
S.shape({
reading: S.list(S.string()),
...oItem,
}).additionalProperties(true),
).optional(),
meanings: S.list(
S.shape({
meaning: S.list(S.string()),
...oItem,
}).additionalProperties(true),
).optional(),
}).additionalProperties(true);
function getEntry(entry: string) {
console.log(entry);
const filepath = glob
.sync(`assets/vocabulary/**/${sanitize(entry)}.md`)
.sort()[0];
if (!filepath) return null;
const { data, content } = readYAML(filepath);
return {
data: sEntry.ensure(data),
content,
};
}

+ 32
- 0
__packages__/server/src/shared.ts View File

@ -0,0 +1,32 @@
import { readFileSync } from 'fs';
import matter from 'gray-matter';
import yaml from 'js-yaml';
import S from 'jsonschema-definer';
export const isDev = process.env['NODE_ENV'] === 'development';
export function readYAML(filepath: string) {
return matter(readFileSync(filepath, 'utf-8'), {
engines: {
yaml: {
parse: (s) =>
s
? (yaml.load(s, {
schema: yaml.JSON_SCHEMA,
}) as Record<string, any>)
: {},
},
},
});
}
export const oItem = {
skip: S.boolean().optional(),
examples: S.list(
S.shape({
ja: S.string(),
en: S.string().optional(),
}).additionalProperties(true),
).optional(),
};

+ 7
- 0
__packages__/server/src/tsconfig.json View File

@ -0,0 +1,7 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"rootDir": ".",
"outDir": "../lib"
}
}

+ 3
- 0
__packages__/server/tsconfig.json View File

@ -0,0 +1,3 @@
{
"extends": "../../tsconfig.json"
}

+ 1140
- 0
__packages__/server/yarn.lock
File diff suppressed because it is too large
View File


Loading…
Cancel
Save