add basic server

This commit is contained in:
polv committed 2022-05-05 09:42:02 +07:00
1 parent 34c8ee5101
commit 818dcbdc01
11 files changed
+1495

No files matched your search

+30
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
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,
};
}
@@ -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
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
View File
@@ -0,0 +1,7 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"rootDir": ".",
"outDir": "../lib"
}
}