Files
laochan-eacnet_backend/src/main.ts
T
2024-04-11 15:20:36 +08:00

157 lines
4.0 KiB
TypeScript

import 'reflect-metadata';
import cors from '@koa/cors';
import Koa, { DefaultState } from 'koa';
import bodyParser from 'koa-bodyparser';
import { InjectionToken, Provider, container } from 'tsyringe';
import { ILaochanContext } from './types.js';
import { DefaultService } from './controllers/default.js';
import { Logger } from '@cordisjs/logger';
import { eacnet } from './middlewares/eacnet.js';
import config from './utils/config.js';
import { MongoClient, Db } from 'mongodb';
import { apiRoutes } from './apis/index.js';
function tryResolve<T>(token: InjectionToken<T>): T | undefined {
if (!container.isRegistered(token)) return undefined;
return container.resolve(token);
}
function register<T>(
token: InjectionToken<T>,
provider: Provider<T> | { new(...args: any[]): T },
): T {
if (container.isRegistered(token)) return container.resolve(token);
// @ts-expect-error: noob tsc
return container.register(token, provider).resolve(token);
}
async function main(): Promise<void> {
const logger = new Logger('laochan-eacnet');
logger.info('initialization...');
{
logger.info('connecting to mongodb...');
const client = await MongoClient.connect(config.mongoUrl);
container.register(Db, {
useValue: client.db(config.dbName),
});
}
logger.info('creating koa app...');
const app = new Koa<DefaultState, ILaochanContext>()
.use(async (ctx, next) => {
try {
await next();
} catch (e) {
logger.error(
'unexcepted error: %s',
JSON.stringify(e, Object.getOwnPropertyNames(e), 2),
);
ctx.status = e.status ?? 500;
ctx.body = `${e.stack ?? 'Internal Error'}`;
}
})
.use(cors())
.use(bodyParser({
formLimit: '1mb',
}))
.use(eacnet)
.use(async (ctx, next) => {
ctx.logger = ctx.service
? register(`logger:${ctx.service.name}:${ctx.service.method}`, {
useValue: new Logger(`${ctx.service.name}:${ctx.service.method}`),
})
: logger;
if (!ctx.service) {
return await next();
}
let service: object | undefined = tryResolve(ctx.service.name);
if (service === undefined) {
try {
let module: any = null;
try {
module = await import(
`./controllers/${ctx.service.name}/index.js`
);
} catch (e) {
if (e.code != 'ERR_MODULE_NOT_FOUND') {
ctx.logger.error(
`load service error %s`,
JSON.stringify(e, Object.getOwnPropertyNames(e), 2),
);
} else {
module = await import(
`./controllers/${ctx.service.name}.js`
);
}
}
service = register(ctx.service.name, {
useClass: module.default,
});
} catch (e) {
if (e.code != 'ERR_MODULE_NOT_FOUND') {
ctx.logger.error(
`load controller error %s`,
JSON.stringify(e, Object.getOwnPropertyNames(e), 2),
);
}
}
}
ctx.logger.info(
'%s [%s]: request = %s',
ctx.url,
ctx.token,
JSON.stringify(ctx.body, (key, value) => {
if (key === 'pdata') {
return '<PDATA>';
}
return value;
}),
);
let method = ctx.service.method;
if (!service || !(service[method])?.bind) {
ctx.logger.warn(`unimplemented method.`);
service = container.resolve(DefaultService);
method = 'default';
}
ctx.body = await (service[method] as Function).bind(service)(ctx);
ctx.status = 200;
ctx.logger.info('[server]: status = %d', ctx.status);
})
.use(apiRoutes)
.use(async (ctx, next) => {
if (!ctx.body) {
ctx.body = 'Laochan-Eacnet is running.';
}
return next();
});
app.context.resolve = container.resolve.bind(container);
logger.info('listening on %d', config.port);
app.listen(config.port);
}
main();