add js object to kxml decorator

add config warpper around environment variables
This commit is contained in:
GEEKiDoS
2024-03-07 14:06:31 +08:00
parent 3845b056e6
commit da66fac569
10 changed files with 268 additions and 48 deletions
+28 -14
View File
@@ -1,20 +1,22 @@
{
"name": "node-typescript-boilerplate",
"name": "@laochan/ea3",
"version": "0.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "node-typescript-boilerplate",
"name": "@laochan/ea3",
"version": "0.0.0",
"license": "Apache-2.0",
"license": "MIT",
"dependencies": {
"@cordisjs/logger": "^0.1.3",
"@kamyu/kbinxml": "^2.0.2",
"arc4": "^3.4.0",
"arc4": "^2.1.4",
"dedent-js": "^1.0.1",
"dotenv": "^16.4.5",
"fast-xml-parser": "^4.3.2",
"koa": "^2.15.0",
"lodash": "^4.17.21",
"pug": "^3.0.2",
"reflect-metadata": "^0.2.1",
"tslib": "~2.6",
@@ -23,6 +25,7 @@
"devDependencies": {
"@types/jest": "~29.5",
"@types/koa": "^2.13.12",
"@types/lodash": "^4.14.202",
"@types/node": "~20",
"@types/pug": "^2.0.10",
"@typescript-eslint/eslint-plugin": "~6.15",
@@ -2042,6 +2045,12 @@
"@types/koa": "*"
}
},
"node_modules/@types/lodash": {
"version": "4.14.202",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz",
"integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
"dev": true
},
"node_modules/@types/mime": {
"version": "1.3.5",
"resolved": "https://registry.npmmirror.com/@types/mime/-/mime-1.3.5.tgz",
@@ -2443,14 +2452,11 @@
}
},
"node_modules/arc4": {
"version": "3.4.0",
"resolved": "https://registry.npmmirror.com/arc4/-/arc4-3.4.0.tgz",
"integrity": "sha512-zFQmSbTwSTrICYV8kldrD3YreCtlLIKEZk+lmlNSlpUKU/cF9o746WFEy9eNqLrqSyUgAAjB3gnD9UXdK23beA==",
"dependencies": {
"lodash": "4.17.4"
},
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/arc4/-/arc4-2.1.4.tgz",
"integrity": "sha512-yYR7Ragh8qYiPMRipZmtoP6BVtENdOmFo7+sdBX7VD/F+hzblmsZjr56ir7G3485AEh3EWBscLv0h4iE+bWzwQ==",
"engines": {
"node": ">=4"
"node": ">=0.10"
}
},
"node_modules/arg": {
@@ -3118,6 +3124,14 @@
"resolved": "https://registry.npmmirror.com/doctypes/-/doctypes-1.1.0.tgz",
"integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ=="
},
"node_modules/dotenv": {
"version": "16.4.5",
"resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.4.5.tgz",
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
"engines": {
"node": ">=12"
}
},
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@@ -5110,9 +5124,9 @@
}
},
"node_modules/lodash": {
"version": "4.17.4",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.4.tgz",
"integrity": "sha512-6X37Sq9KCpLSXEh8uM12AKYlviHPNNk4RxiGBn4cmKGJinbXBneWIV7iE/nXkM928O7ytHcHb6+X6Svl0f4hXg=="
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash.memoize": {
"version": "4.1.2",
+4 -1
View File
@@ -9,6 +9,7 @@
"devDependencies": {
"@types/jest": "~29.5",
"@types/koa": "^2.13.12",
"@types/lodash": "^4.14.202",
"@types/node": "~20",
"@types/pug": "^2.0.10",
"@typescript-eslint/eslint-plugin": "~6.15",
@@ -43,10 +44,12 @@
"dependencies": {
"@cordisjs/logger": "^0.1.3",
"@kamyu/kbinxml": "^2.0.2",
"arc4": "^3.4.0",
"arc4": "^2.1.4",
"dedent-js": "^1.0.1",
"dotenv": "^16.4.5",
"fast-xml-parser": "^4.3.2",
"koa": "^2.15.0",
"lodash": "^4.17.21",
"pug": "^3.0.2",
"reflect-metadata": "^0.2.1",
"tslib": "~2.6",
+1 -1
View File
@@ -1,7 +1,7 @@
import pug from 'pug';
import { to_bin } from '@kamyu/kbinxml';
export function kxml(
export function kxmlTemplate(
templatePath: string,
encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8',
): MethodDecorator {
+152
View File
@@ -0,0 +1,152 @@
import _ from 'lodash';
import { to_bin } from '@kamyu/kbinxml';
function serializeValue(value: any, type: string): string {
if (
[
's8',
'u8',
's16',
'u16',
's32',
'u32',
's64',
'u64',
'float',
'double',
].includes(type)
) {
return value.toString();
}
if (['b', 'bool'].includes('type')) {
return value ? '1' : '0';
}
if (['bin', 'binary'].includes(type)) {
if (!(value instanceof Buffer)) {
throw new Error('binary with non-buffer value');
}
return value.toString('hex');
}
if (['ip4', 'str', 'string'].includes(type)) {
return value;
}
if (type == 'time') {
if (value instanceof Date) {
return Math.floor(value.valueOf() / 1000).toString();
}
if (typeof value === 'number') {
return value.toString();
}
throw new Error('time with not date or number');
}
throw new Error(`unsupported type ${type}`);
}
function serializeObject(obj: Object, name: string, linePrefix: string = '') {
let output = `${linePrefix}<${name}`;
const entries = Object.entries(obj);
const attrs = entries
.filter(v => v[0].startsWith('$'))
.map(v => [v[0].substring(1), v[1]]);
for (const attr of attrs) {
output += ` ${attr[0]}="${_.escape(attr[1])}"`;
}
// handle kxml value shit
const value = entries.find(v => v[0] == '__value');
if (value && value[1] instanceof Array) {
output += ` __count="${value[1].length}"`;
}
output += '>';
if (value) {
const type = attrs.find(v => v[0] == '__type');
if (!type) {
// or normal xml innerXml?
throw new Error(`value with no type in ${name}`);
}
output += (value[1] instanceof Array ? value[1] : [value[1]])
.map(v => serializeValue(v, type[1]))
.join(' ');
} else {
const elements = entries
.filter(v => !v[0].startsWith('$'));
if (elements.length) {
output += '\n';
for (const element of elements) {
if (element[1] instanceof Array) {
for (const item of element[1])
output += serializeObject(item, element[0], linePrefix + ' ');
continue;
}
output += serializeObject(element[1], element[0], linePrefix + ' ');
}
output += linePrefix;
}
}
return output + `</${name}>\n`;
}
export function kxml(topName: string = 'response', encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8'): MethodDecorator {
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 =
`<?xml version="1.0" encoding="${encoding}"?>` + serializeObject(obj, topName);
const kxml = to_bin(xml);
return Buffer.from(kxml.data);
} as unknown as any;
};
}
console.log(serializeObject({
$attr: 'attr1',
sb: {
$attr: 'attr2',
$__type: 'str',
__value: 'sba',
},
sb1: {
$attr: 'attr3',
nested: {
$attr: 'attr4',
$__type: 's32',
__value: [1, 1, 4, 5, 1, 4],
}
},
array: [{
$attr: 'test'
}, {
$attr: 'test2'
}, {
$attr: 'test3'
}, {
$attr: 'test4'
}, {
$attr: 'test5'
}]
}, 'test'))
+10 -14
View File
@@ -1,22 +1,18 @@
import { singleton } from 'tsyringe';
import { Context, ILaochanService } from '../types.js';
import dedent from 'dedent-js';
import { to_bin } from '@kamyu/kbinxml';
import { kxml } from '../decorators/to-kxml.js';
@singleton()
export class DefaultService implements ILaochanService {
async process(ctx: Context): Promise<Buffer> {
const tmp = ctx.service.split('.');
const module = tmp[0];
const method = tmp[1];
@kxml()
async process(ctx: Context) {
const [module, method] = ctx.service.split('.');
const xml = dedent`
<?xml version="1.0" encoding="SHIFT_JIS"?>
<response>
<${module} method="${method}" status="0"/>
</response>
`;
return Buffer.from(to_bin(xml).data);
return {
[module]: {
$method: method,
status: '0',
}
}
}
}
+2 -2
View File
@@ -1,10 +1,10 @@
import { kxml } from '../../decorators/render-kxml.js';
import { kxmlTemplate } from '../../decorators/render-kxml.js';
import { ILaochanService } from '../../types.js';
import { singleton } from 'tsyringe';
@singleton()
export default class implements ILaochanService {
@kxml('facility/get')
@kxmlTemplate('facility/get')
async process(): Promise<object> {
return {
status: 0,
+2 -2
View File
@@ -1,10 +1,10 @@
import { kxml } from '../../decorators/render-kxml.js';
import { kxmlTemplate } from '../../decorators/render-kxml.js';
import { ILaochanService } from '../../types.js';
import { singleton } from 'tsyringe';
@singleton()
export default class implements ILaochanService {
@kxml('message/get')
@kxmlTemplate('message/get')
async process(): Promise<object> {
return {
status: 0,
+9 -6
View File
@@ -1,16 +1,19 @@
import { kxml } from '../../decorators/render-kxml.js';
import { kxml } from '../../decorators/to-kxml.js';
import { ILaochanService } from '../../types.js';
import { singleton } from 'tsyringe';
@singleton()
export default class implements ILaochanService {
@kxml('pcbtracker/alive')
@kxml()
async process(): Promise<object> {
return {
status: 0,
expire: 3600,
time: Math.floor(new Date().valueOf() / 1000),
ecEnable: 1,
pcbtracker: {
$ecenable: 1,
$expire: 3600,
$method: 'alive',
$status: 0,
$time: Math.floor(new Date().valueOf() / 1000),
}
};
}
}
+33 -8
View File
@@ -1,16 +1,41 @@
import { kxml } from '../../decorators/render-kxml.js';
import { ILaochanService } from '../../types.js';
import { singleton } from 'tsyringe';
import config from '../../utils/config.js';
import { kxml } from '../../decorators/to-kxml.js';
@singleton()
export default class implements ILaochanService {
@kxml('services/get')
async process(): Promise<object> {
@kxml()
async process() {
const selfServices = [
'services', 'dlstatus', 'cardmng',
'eacoin', 'userdata', 'userid', 'pcbtracker', 'pcbevent',
'message', 'facility', 'apsmanager', 'sidmgr', 'package',
'uploaders', 'local', 'local2', 'lobby',
]
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',
ntpUrl: 'ntp://pool.ntp.org',
selfUrl: 'http://localhost:10573',
};
services: {
$expire: 43200,
$method: 'get',
$mode: 'operation',
$product_domain: 1,
$statuc: 0,
item: [
{
$name: 'keepalive',
$url: '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',
},
{
$name: 'ntp',
$url: config.ntpUrl,
},
...selfServices.map(v => ({
$name: v,
$url: config.selfUrl,
}))
]
}
}
}
}
+27
View File
@@ -0,0 +1,27 @@
import dotenv from 'dotenv';
dotenv.config();
class Config {
get port() {
return process.env.PORT ? parseInt(process.env.PORT) : 10573;
}
get selfUrl() {
return process.env.SELF_URL ?? 'http://127.0.0.1:10573';
}
get ntpUrl() {
return process.env.NTP_URL ?? 'ntp://pool.ntp.org';
}
get mongoUrl() {
return process.env.MONGO_URL ?? 'mongodb://127.0.0.1:27017';
}
get isDev() {
return process.env.NODE_ENV !== 'production';
}
}
export const config = new Config();
export default config;