add basic server
This commit is contained in:
1 parent
34c8ee5101
commit
818dcbdc01
11 files changed
+1495
No files matched your search
@@ -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"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user