add basic server
This commit is contained in:
1 parent
34c8ee5101
commit
818dcbdc01
11 files changed
+1495
No files matched your search
Whitespace-only changes.
@@ -0,0 +1 @@
|
|||||||
|
/lib/
|
||||||
@@ -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
|
||||||
|
---
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
@@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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(),
|
||||||
|
};
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": ".",
|
||||||
|
"outDir": "../lib"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json"
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user