diff --git a/.eslintrc.json b/.eslintrc.json index a7a3f42..07c19b8 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -22,6 +22,8 @@ // examples, and because it is not a recommended rule, you should either // disable it, or understand what it enforces. // https://typescript-eslint.io/rules/explicit-function-return-type/ - "@typescript-eslint/explicit-function-return-type": "warn" + "@typescript-eslint/explicit-function-return-type": "warn", + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/no-explicit-any": "off" } } diff --git a/package.json b/package.json index 174ec54..02b74f9 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "build:watch": "tsc -w -p tsconfig.json", "build:release": "npm run clean && tsc -p tsconfig.release.json", "lint": "eslint . --ext .ts --ext .mts", + "lint:fix": "eslint . --ext .ts --ext .mts --fix", "test": "jest --coverage", "prettier": "prettier --config .prettierrc --write .", "test:watch": "jest --watch" diff --git a/src/decorators/render-kxml.ts b/src/decorators/render-kxml.ts index 8a8f633..5a54947 100644 --- a/src/decorators/render-kxml.ts +++ b/src/decorators/render-kxml.ts @@ -1,17 +1,21 @@ import pug from 'pug'; -import { to_bin } from "@kamyu/kbinxml"; +import { to_bin } from '@kamyu/kbinxml'; -export function kxml(templatePath: string, encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8'): MethodDecorator { +export function kxml( + templatePath: string, + encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8', +): MethodDecorator { const template = pug.compileFile('templates/' + templatePath + '.pug'); return function (_target, _propertyKey, descriptor) { const orig = descriptor.value as Function; descriptor.value = async function (...args: any[]) { const obj = await orig.apply(this, args); - const xml = `` + template(obj); + const xml = + `` + template(obj); const kxml = to_bin(xml); return Buffer.from(kxml.data); } as unknown as any; - } + }; } diff --git a/src/functions/default.ts b/src/functions/default.ts index e87ac2d..723c3b1 100644 --- a/src/functions/default.ts +++ b/src/functions/default.ts @@ -1,11 +1,11 @@ -import { singleton } from "tsyringe"; -import { Context, ILaochanService } from "../types.js"; -import dedent from 'dedent-js' -import { to_bin } from "@kamyu/kbinxml"; +import { singleton } from 'tsyringe'; +import { Context, ILaochanService } from '../types.js'; +import dedent from 'dedent-js'; +import { to_bin } from '@kamyu/kbinxml'; @singleton() export class DefaultService implements ILaochanService { - async process(ctx: Context) { + async process(ctx: Context): Promise { const tmp = ctx.service.split('.'); const module = tmp[0]; const method = tmp[1]; diff --git a/src/functions/facility/get.ts b/src/functions/facility/get.ts index c10d908..0cce88d 100644 --- a/src/functions/facility/get.ts +++ b/src/functions/facility/get.ts @@ -1,11 +1,11 @@ -import { kxml } from "../../decorators/render-kxml.js"; -import { ILaochanService } from "../../types.js"; -import { singleton } from "tsyringe"; +import { kxml } from '../../decorators/render-kxml.js'; +import { ILaochanService } from '../../types.js'; +import { singleton } from 'tsyringe'; @singleton() export default class implements ILaochanService { @kxml('facility/get') - async process() { + async process(): Promise { return { status: 0, location: { @@ -48,12 +48,16 @@ export default class implements ILaochanService { notchAmount: 0, notchCount: 0, supplyLimit: 100000, - } + }, }, calendar: { year: 2024, - holiday: [0, 1, 8, 41, 53, 79, 118, 122, 123, 124, 197, 222, 260, 265, 281, 306, 326, 365, 372, 406, 407, 418, 444, 484, 488, 489, 490, 491, 561, 588, 589, 624, 630, 631, 652, 672, 673, 692], - } + holiday: [ + 0, 1, 8, 41, 53, 79, 118, 122, 123, 124, 197, 222, 260, 265, 281, 306, + 326, 365, 372, 406, 407, 418, 444, 484, 488, 489, 490, 491, 561, 588, + 589, 624, 630, 631, 652, 672, 673, 692, + ], + }, }; } } diff --git a/src/functions/message/get.ts b/src/functions/message/get.ts index df24ab1..8fd36a8 100644 --- a/src/functions/message/get.ts +++ b/src/functions/message/get.ts @@ -1,11 +1,11 @@ -import { kxml } from "../../decorators/render-kxml.js"; -import { ILaochanService } from "../../types.js"; -import { singleton } from "tsyringe"; +import { kxml } from '../../decorators/render-kxml.js'; +import { ILaochanService } from '../../types.js'; +import { singleton } from 'tsyringe'; @singleton() export default class implements ILaochanService { @kxml('message/get') - async process() { + async process(): Promise { return { status: 0, expire: 60, diff --git a/src/functions/package/list.ts b/src/functions/package/list.ts index 631ba67..fbe365b 100644 --- a/src/functions/package/list.ts +++ b/src/functions/package/list.ts @@ -1,5 +1,5 @@ -import { singleton } from "tsyringe"; -import { DefaultService } from "../default.js"; +import { singleton } from 'tsyringe'; +import { DefaultService } from '../default.js'; @singleton() export default class extends DefaultService {} diff --git a/src/functions/pcbevent/put.ts b/src/functions/pcbevent/put.ts index 631ba67..fbe365b 100644 --- a/src/functions/pcbevent/put.ts +++ b/src/functions/pcbevent/put.ts @@ -1,5 +1,5 @@ -import { singleton } from "tsyringe"; -import { DefaultService } from "../default.js"; +import { singleton } from 'tsyringe'; +import { DefaultService } from '../default.js'; @singleton() export default class extends DefaultService {} diff --git a/src/functions/pcbtracker/alive.ts b/src/functions/pcbtracker/alive.ts index 4c98bc7..c6d54aa 100644 --- a/src/functions/pcbtracker/alive.ts +++ b/src/functions/pcbtracker/alive.ts @@ -1,11 +1,11 @@ -import { kxml } from "../../decorators/render-kxml.js"; -import { ILaochanService } from "../../types.js"; -import { singleton } from "tsyringe"; +import { kxml } from '../../decorators/render-kxml.js'; +import { ILaochanService } from '../../types.js'; +import { singleton } from 'tsyringe'; @singleton() export default class implements ILaochanService { @kxml('pcbtracker/alive') - async process() { + async process(): Promise { return { status: 0, expire: 3600, diff --git a/src/functions/services/get.ts b/src/functions/services/get.ts index 96dba38..1103027 100644 --- a/src/functions/services/get.ts +++ b/src/functions/services/get.ts @@ -1,13 +1,14 @@ -import { kxml } from "../../decorators/render-kxml.js"; -import { ILaochanService } from "../../types.js"; -import { singleton } from "tsyringe"; +import { kxml } from '../../decorators/render-kxml.js'; +import { ILaochanService } from '../../types.js'; +import { singleton } from 'tsyringe'; @singleton() export default class implements ILaochanService { @kxml('services/get') - async process() { + async process(): Promise { return { - keepaliveUrl: 'ping://127.0.0.1/?pa=127.0.0.1&ia=127.0.0.1&ga=127.0.0.1ma=127.0.0.1&t1=2&t2=15&rt=2', + keepaliveUrl: + 'ping://127.0.0.1/?pa=127.0.0.1&ia=127.0.0.1&ga=127.0.0.1ma=127.0.0.1&t1=2&t2=15&rt=2', ntpUrl: 'ntp://pool.ntp.org', selfUrl: 'http://localhost:10573', }; diff --git a/src/main.ts b/src/main.ts index db052fe..47e18e3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,34 +1,33 @@ -import "reflect-metadata"; +import 'reflect-metadata'; import Koa, { DefaultState } from 'koa'; -import { InjectionToken, Provider, container } from "tsyringe"; +import { InjectionToken, Provider, container } from 'tsyringe'; import { ILaochanContext, ILaochanService } from './types.js'; -import { DefaultService } from "./functions/default.js"; -import { Logger } from "@cordisjs/logger"; -import { crypto } from "./middlewares/crypto.js"; -import { binaryBody } from "./middlewares/binarybody.js"; -import { parseKxml } from "./middlewares/parseKxml.js"; -import { decompress } from "./middlewares/decompress.js"; +import { DefaultService } from './functions/default.js'; +import { Logger } from '@cordisjs/logger'; +import { crypto } from './middlewares/crypto.js'; +import { binaryBody } from './middlewares/binarybody.js'; +import { parseKxml } from './middlewares/parseKxml.js'; +import { decompress } from './middlewares/decompress.js'; function tryResolve(token: InjectionToken): T | undefined { - if (!container.isRegistered(token)) - return undefined; + if (!container.isRegistered(token)) return undefined; return container.resolve(token); } -function register(token: InjectionToken, provider: Provider | { new(...args: any[]): T }): T { - if (container.isRegistered(token)) - return container.resolve(token); +function register( + token: InjectionToken, + provider: Provider | { new (...args: any[]): T }, +): T { + if (container.isRegistered(token)) return container.resolve(token); - // @ts-ignore - return container.register(token, provider) - .resolve(token); + // @ts-expect-error: noob tsc + return container.register(token, provider).resolve(token); } -async function main() { - +async function main(): Promise { const logger = new Logger('main'); const app = new Koa() @@ -47,20 +46,30 @@ async function main() { ctx.model = ctx.query.model; ctx.service = ctx.query.f; - ctx.logger = ctx.service ? register(`logger:${ctx.service}`, { - useValue: new Logger(ctx.service), - }) : logger; + ctx.logger = ctx.service + ? register(`logger:${ctx.service}`, { + useValue: new Logger(ctx.service), + }) + : logger; - let service: ILaochanService | undefined = ctx.service ? - tryResolve(ctx.service) : container.resolve(DefaultService); + let service: ILaochanService | undefined = ctx.service + ? tryResolve(ctx.service) + : container.resolve(DefaultService); if (service === undefined) { try { - const module = await import(`./functions/${ctx.service.replace('.', '/')}.js`); + const module = await import( + `./functions/${ctx.service.replace('.', '/')}.js` + ); service = register(ctx.service, { useClass: module.default, }); - } catch { } + } catch (e) { + logger.error( + `load service error %s`, + JSON.stringify(e, Object.getOwnPropertyNames(e), 2), + ); + } } if (service === undefined) { @@ -68,13 +77,21 @@ async function main() { service = container.resolve(DefaultService); } - logger.info('%s [%s]: request = %s', ctx.service, ctx.pcbid, JSON.stringify(ctx.request.body)); + logger.info( + '%s [%s]: request = %s', + ctx.service, + ctx.pcbid, + JSON.stringify(ctx.request.body), + ); try { ctx.body = await service.process(ctx); ctx.status = 200; } catch (e) { - logger.error('unexcepted error: %s', JSON.stringify(e, Object.getOwnPropertyNames(e), 2)); + logger.error( + 'unexcepted error: %s', + JSON.stringify(e, Object.getOwnPropertyNames(e), 2), + ); ctx.status = e.status ?? 500; ctx.body = `${e.stack ?? 'Internal Error'}`; diff --git a/src/middlewares/binarybody.ts b/src/middlewares/binarybody.ts index 03b41be..ab3ccc1 100644 --- a/src/middlewares/binarybody.ts +++ b/src/middlewares/binarybody.ts @@ -1,19 +1,19 @@ -import { Next } from "koa"; -import { Context } from "../types.js"; +import { Next } from 'koa'; +import { Context } from '../types.js'; -export async function binaryBody(ctx: Context, next: Next) { - if (typeof ctx.headers["content-length"] !== 'string') { +export async function binaryBody(ctx: Context, next: Next): Promise { + if (typeof ctx.headers['content-length'] !== 'string') { return next(); } - const length = parseInt(ctx.headers["content-length"]); + const length = parseInt(ctx.headers['content-length']); if (length === 0) { return next(); } ctx.request.body = await new Promise((resolve, reject) => { const buffer = Buffer.allocUnsafe(length); - let len = 0 + let len = 0; ctx.req.on('data', (chunk: Buffer) => { chunk.copy(buffer, len); @@ -28,7 +28,7 @@ export async function binaryBody(ctx: Context, next: Next) { if (len < length) { reject(new Error('socket hanged up unexceptly.')); } - }) + }); }); return next(); diff --git a/src/middlewares/crypto.ts b/src/middlewares/crypto.ts index 7da4db6..b2e0650 100644 --- a/src/middlewares/crypto.ts +++ b/src/middlewares/crypto.ts @@ -1,8 +1,8 @@ -import { Next } from "koa"; -import { Context } from "../types.js"; -import { rc4Transfrom } from "../utils/rc4.js"; +import { Next } from 'koa'; +import { Context } from '../types.js'; +import { rc4Transfrom } from '../utils/rc4.js'; -export async function crypto(ctx: Context, next: Next) { +export async function crypto(ctx: Context, next: Next): Promise { if (typeof ctx.req.headers['x-eamuse-info'] !== 'string') { return next(); } @@ -31,8 +31,12 @@ export async function crypto(ctx: Context, next: Next) { return; } - const time = Math.floor(new Date().valueOf() / 1000).toString(16).padStart(8, '0'); - const salt = Math.floor(Math.random() * 0xFFFF).toString(16).padStart(4, '0'); + const time = Math.floor(new Date().valueOf() / 1000) + .toString(16) + .padStart(8, '0'); + const salt = Math.floor(Math.random() * 0xffff) + .toString(16) + .padStart(4, '0'); const encrypted = rc4Transfrom(time + salt, ctx.body); ctx.body = encrypted; diff --git a/src/middlewares/decompress.ts b/src/middlewares/decompress.ts index 58f2aa9..b6b4097 100644 --- a/src/middlewares/decompress.ts +++ b/src/middlewares/decompress.ts @@ -1,8 +1,8 @@ -import { Next } from "koa"; -import { Context } from "../types.js"; -import { lz77Decode } from "../utils/lz77.js"; +import { Next } from 'koa'; +import { Context } from '../types.js'; +import { lz77Decode } from '../utils/lz77.js'; -export async function decompress(ctx: Context, next: Next) { +export async function decompress(ctx: Context, next: Next): Promise { if (!(ctx.request.body instanceof Buffer)) { return next(); } diff --git a/src/middlewares/parseKxml.ts b/src/middlewares/parseKxml.ts index 529a69d..40df3ef 100644 --- a/src/middlewares/parseKxml.ts +++ b/src/middlewares/parseKxml.ts @@ -1,6 +1,6 @@ -import { Next } from "koa"; -import { Context } from "../types.js"; -import { to_xml } from "@kamyu/kbinxml"; +import { Next } from 'koa'; +import { Context } from '../types.js'; +import { to_xml } from '@kamyu/kbinxml'; import { XMLParser } from 'fast-xml-parser'; const parser = new XMLParser({ @@ -11,46 +11,59 @@ const parser = new XMLParser({ allowBooleanAttributes: true, }); -function parseValue(node: any) { +function parseValue(node: { $__type: string; $__count?: unknown }): any { const isArray = '$__count' in node; - if (['s8', 'u8', 's16', 'u16', 's32', 'u32', 's64', 'u64', 'float', 'double'].includes(node.$__type)) { - if (isArray) { - return node['#text'].split(' ').map(v => new Number(v)); - } + if ( + [ + 's8', + 'u8', + 's16', + 'u16', + 's32', + 'u32', + 's64', + 'u64', + 'float', + 'double', + ].includes(node.$__type) + ) { + if (isArray) { + return node['#text'].split(' ').map((v) => new Number(v)); + } - return new Number(node['#text']); + return new Number(node['#text']); } - if(['b', 'bool'].includes(node.$__type)) { + if (['b', 'bool'].includes(node.$__type)) { return !!parseInt(node['#text']); } if (['bin', 'binary'].includes(node.$__type)) { - return Buffer.from(node['#text'], 'hex') + return Buffer.from(node['#text'], 'hex'); } - if(['ip4', 'str', 'string'].includes(node.$__type)) { + if (['ip4', 'str', 'string'].includes(node.$__type)) { return node['#text']; } - if(node.$__type == 'time') { + if (node.$__type == 'time') { return new Date(parseInt(node['#text']) * 1000); } return { type: node.$__type, value: node['#text'], - } + }; } -function kxmlToObject(node: any) { +function kxmlToObject(node: Record): any { if (node instanceof Array) { - return node.map(v => kxmlToObject(v)); + return node.map((v) => kxmlToObject(v)); } if ('$__type' in node) { - return parseValue(node); + return parseValue(node as { $__type: string; $__count?: unknown }); } const obj: object = {}; @@ -64,13 +77,13 @@ function kxmlToObject(node: any) { continue; } - obj[key] = kxmlToObject(node[key]); + obj[key] = kxmlToObject(node[key] as Record); } return obj; } -export async function parseKxml(ctx: Context, next: Next) { +export async function parseKxml(ctx: Context, next: Next): Promise { if (!(ctx.request.body instanceof Buffer)) { return next(); } diff --git a/src/types.ts b/src/types.ts index b9b6efb..9acf3a2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,5 @@ -import { Logger } from "@cordisjs/logger"; -import { DefaultState, ParameterizedContext } from "koa"; +import { Logger } from '@cordisjs/logger'; +import { DefaultState, ParameterizedContext } from 'koa'; export interface ILaochanContext { model?: string; @@ -9,7 +9,7 @@ export interface ILaochanContext { request: { body: Buffer | object | undefined; - } + }; } export type Context = ParameterizedContext; diff --git a/src/utils/lz77.ts b/src/utils/lz77.ts index 5d8b8c6..412cf28 100644 --- a/src/utils/lz77.ts +++ b/src/utils/lz77.ts @@ -10,21 +10,18 @@ export function lz77Decode(data: Buffer): Buffer { if (flag & (1 << bit)) { output.push(data[offset]); offset += 1; - } else { - if (offset >= data.length) - break; + if (offset >= data.length) break; const lookbackFlag = data.readUInt16BE(offset); const lookbackLength = (lookbackFlag & 0x000f) + 3; const lookbackOffset = lookbackFlag >> 4; offset += 2; - if (lookbackFlag == 0) - break; + if (lookbackFlag == 0) break; for (let i = 0; i < lookbackLength; i++) { - let loffset = output.length - lookbackOffset; + const loffset = output.length - lookbackOffset; if (loffset <= 0 || loffset >= output.length) { output.push(0); diff --git a/src/utils/rc4.ts b/src/utils/rc4.ts index 35503bc..cf7fe43 100644 --- a/src/utils/rc4.ts +++ b/src/utils/rc4.ts @@ -1,13 +1,12 @@ -import { createHash } from "crypto"; +import { createHash } from 'crypto'; import rc4 from 'arc4'; const EAMUSE_RC4_KEY = '69D74627D985EE2187161570D08D93B12455035B6DF0D8205DF5'; -export function rc4Transfrom(keyHex: string, data: Buffer) { +export function rc4Transfrom(keyHex: string, data: Buffer): Buffer { const key = createHash('md5') - .update(keyHex + EAMUSE_RC4_KEY, 'hex') - .digest(); + .update(keyHex + EAMUSE_RC4_KEY, 'hex') + .digest(); - return rc4('arc4', key) - .encodeBuffer(data) + return rc4('arc4', key).encodeBuffer(data); }