mirror of
https://github.com/laochan-eacnet/backend.git
synced 2026-10-01 11:07:58 +03:00
initial commit
This commit is contained in:
@@ -1,42 +0,0 @@
|
||||
import { Delays, greeter } from '../src/main.js';
|
||||
|
||||
describe('greeter function', () => {
|
||||
const name = 'John';
|
||||
let hello: string;
|
||||
|
||||
let timeoutSpy: jest.SpyInstance;
|
||||
|
||||
// Act before assertions
|
||||
beforeAll(async () => {
|
||||
// Read more about fake timers
|
||||
// http://facebook.github.io/jest/docs/en/timer-mocks.html#content
|
||||
// Jest 27 now uses "modern" implementation of fake timers
|
||||
// https://jestjs.io/blog/2021/05/25/jest-27#flipping-defaults
|
||||
// https://github.com/facebook/jest/pull/5171
|
||||
jest.useFakeTimers();
|
||||
timeoutSpy = jest.spyOn(global, 'setTimeout');
|
||||
|
||||
const p: Promise<string> = greeter(name);
|
||||
jest.runOnlyPendingTimers();
|
||||
hello = await p;
|
||||
});
|
||||
|
||||
// Teardown (cleanup) after assertions
|
||||
afterAll(() => {
|
||||
timeoutSpy.mockRestore();
|
||||
});
|
||||
|
||||
// Assert if setTimeout was called properly
|
||||
it('delays the greeting by 2 seconds', () => {
|
||||
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||
expect(setTimeout).toHaveBeenLastCalledWith(
|
||||
expect.any(Function),
|
||||
Delays.Long,
|
||||
);
|
||||
});
|
||||
|
||||
// Assert greeter result
|
||||
it('greets a user with `Hello, {name}` message', () => {
|
||||
expect(hello).toBe(`Hello, ${name}`);
|
||||
});
|
||||
});
|
||||
Generated
+1564
-20
File diff suppressed because it is too large
Load Diff
+14
-1
@@ -8,7 +8,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "~29.5",
|
||||
"@types/koa": "^2.13.12",
|
||||
"@types/node": "~20",
|
||||
"@types/pug": "^2.0.10",
|
||||
"@typescript-eslint/eslint-plugin": "~6.15",
|
||||
"@typescript-eslint/parser": "~6.15",
|
||||
"eslint": "~8.56",
|
||||
@@ -19,10 +21,12 @@
|
||||
"rimraf": "~5.0",
|
||||
"ts-api-utils": "~1.0",
|
||||
"ts-jest": "~29.1",
|
||||
"tsx": "^4.7.0",
|
||||
"typescript": "~5.3"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node build/src/main.js",
|
||||
"watch": "tsx watch src/main.ts",
|
||||
"clean": "rimraf coverage build tmp",
|
||||
"prebuild": "npm run lint",
|
||||
"build": "tsc -p tsconfig.json",
|
||||
@@ -36,7 +40,16 @@
|
||||
"author": "Jakub Synowiec <jsynowiec@users.noreply.github.com>",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "~2.6"
|
||||
"@cordisjs/logger": "^0.1.3",
|
||||
"@kamyu/kbinxml": "^2.0.2",
|
||||
"arc4": "^3.4.0",
|
||||
"dedent-js": "^1.0.1",
|
||||
"fast-xml-parser": "^4.3.2",
|
||||
"koa": "^2.15.0",
|
||||
"pug": "^3.0.2",
|
||||
"reflect-metadata": "^0.2.1",
|
||||
"tslib": "~2.6",
|
||||
"tsyringe": "^4.8.0"
|
||||
},
|
||||
"volta": {
|
||||
"node": "20.10.0"
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import pug from 'pug';
|
||||
import { to_bin } from "@kamyu/kbinxml";
|
||||
|
||||
export function kxml(templatePath: string): 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 = '<?xml version="1.0" encoding="SHIFT_JIS"?>' + template(obj);
|
||||
console.log(xml);
|
||||
const kxml = to_bin(xml);
|
||||
|
||||
return Buffer.from(kxml.data);
|
||||
} as unknown as any;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
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) {
|
||||
const tmp = ctx.service.split('.');
|
||||
const module = tmp[0];
|
||||
const method = tmp[1];
|
||||
|
||||
const xml = dedent`
|
||||
<?xml version="1.0" encoding="SHIFT_JIS"?>
|
||||
<response>
|
||||
<${module} method="${method}" status="0"/>
|
||||
</response>
|
||||
`;
|
||||
|
||||
return Buffer.from(to_bin(xml).data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
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() {
|
||||
return {
|
||||
status: 0,
|
||||
location: {
|
||||
id: 'EA000000',
|
||||
type: 0,
|
||||
country: 'JP',
|
||||
region: 'JP-0',
|
||||
name: 'LaoChan Test',
|
||||
countryName: 'LaoChan',
|
||||
countryJName: '老禅',
|
||||
regionName: 'BianYu',
|
||||
regionJName: '變魚',
|
||||
customerCode: 'LaoChan',
|
||||
companyCode: 'LaoChan',
|
||||
latitude: 0,
|
||||
longitude: 0,
|
||||
accuracy: 0,
|
||||
},
|
||||
line: {
|
||||
id: '0',
|
||||
class: 8,
|
||||
upClass: 8,
|
||||
rtt: 22,
|
||||
},
|
||||
portfw: {
|
||||
globalIp: '127.0.0.1',
|
||||
globalport: 5700,
|
||||
privateport: 5700,
|
||||
},
|
||||
public: {
|
||||
flag: 0,
|
||||
name: 'LaoChan Test',
|
||||
latitude: '0',
|
||||
longitude: '0',
|
||||
},
|
||||
share: {
|
||||
url: '老禅 LaoChan LaoChan 老禅',
|
||||
eapass: 365,
|
||||
eacoin: {
|
||||
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],
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
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() {
|
||||
return {
|
||||
status: 0,
|
||||
expire: 60,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { kxml } from "../../decorators/render-kxml.js";
|
||||
import { ILaochanService } from "../../types.js";
|
||||
import { singleton } from "tsyringe";
|
||||
import { DefaultService } from "../default.js";
|
||||
|
||||
@singleton()
|
||||
export default class extends DefaultService {}
|
||||
@@ -0,0 +1,16 @@
|
||||
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() {
|
||||
return {
|
||||
status: 0,
|
||||
expire: 3600,
|
||||
time: Math.floor(new Date().valueOf() / 1000),
|
||||
ecEnable: 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
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() {
|
||||
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',
|
||||
};
|
||||
}
|
||||
}
|
||||
+82
-28
@@ -1,34 +1,88 @@
|
||||
/**
|
||||
* Some predefined delay values (in milliseconds).
|
||||
*/
|
||||
export enum Delays {
|
||||
Short = 500,
|
||||
Medium = 2000,
|
||||
Long = 5000,
|
||||
import "reflect-metadata";
|
||||
|
||||
import Koa, { DefaultState } from 'koa';
|
||||
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";
|
||||
|
||||
function tryResolve<T>(token: InjectionToken<T>): T | undefined {
|
||||
if (!container.isRegistered(token))
|
||||
return undefined;
|
||||
|
||||
return container.resolve(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Promise<string> that resolves after a given time.
|
||||
*
|
||||
* @param {string} name - A name.
|
||||
* @param {number=} [delay=Delays.Medium] - A number of milliseconds to delay resolution of the Promise.
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function delayedHello(
|
||||
name: string,
|
||||
delay: number = Delays.Medium,
|
||||
): Promise<string> {
|
||||
return new Promise((resolve: (value?: string) => void) =>
|
||||
setTimeout(() => resolve(`Hello, ${name}`), delay),
|
||||
);
|
||||
function register<T>(token: InjectionToken<T>, provider: Provider<T> | { new(...args: any[]): T }): T {
|
||||
if (container.isRegistered(token))
|
||||
return container.resolve(token);
|
||||
|
||||
// @ts-ignore
|
||||
return container.register(token, provider)
|
||||
.resolve(token);
|
||||
}
|
||||
|
||||
// Please see the comment in the .eslintrc.json file about the suppressed rule!
|
||||
// Below is an example of how to use ESLint errors suppression. You can read more
|
||||
// at https://eslint.org/docs/latest/user-guide/configuring/rules#disabling-rules
|
||||
async function main() {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
export async function greeter(name: any) { // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
// The name parameter should be of type string. Any is used only to trigger the rule.
|
||||
return await delayedHello(name, Delays.Long);
|
||||
const logger = new Logger('main');
|
||||
|
||||
const app = new Koa<DefaultState, ILaochanContext>()
|
||||
.use(binaryBody)
|
||||
.use(crypto)
|
||||
.use(parseKxml)
|
||||
.use(async (ctx, next) => {
|
||||
if (typeof ctx.query.f !== 'string') {
|
||||
throw new Error('invaild service');
|
||||
}
|
||||
|
||||
if (typeof ctx.query.model !== 'string') {
|
||||
throw new Error('invaild model');
|
||||
}
|
||||
|
||||
ctx.model = ctx.query.model;
|
||||
ctx.service = ctx.query.f;
|
||||
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);
|
||||
|
||||
if (service === undefined) {
|
||||
try {
|
||||
const module = await import(`./functions/${ctx.service.replace('.', '/')}.js`);
|
||||
service = register(ctx.service, {
|
||||
useClass: module.default,
|
||||
});
|
||||
} catch { }
|
||||
}
|
||||
|
||||
if (service === undefined) {
|
||||
logger.warn(`unimplemented service ${ctx.service}`);
|
||||
service = container.resolve(DefaultService);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
ctx.status = e.status ?? 500;
|
||||
ctx.body = `${e.stack ?? 'Internal Error'}`;
|
||||
}
|
||||
|
||||
logger.info('%s [server]: status = %d', ctx.service, ctx.status);
|
||||
return await next();
|
||||
});
|
||||
|
||||
app.listen(10573);
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Next } from "koa";
|
||||
import { Context } from "../types.js";
|
||||
|
||||
export async function binaryBody(ctx: Context, next: Next) {
|
||||
if (typeof ctx.headers["content-length"] !== 'string') {
|
||||
return next();
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
ctx.req.on('data', (chunk: Buffer) => {
|
||||
chunk.copy(buffer, len);
|
||||
len += chunk.length;
|
||||
|
||||
if (len == length) {
|
||||
resolve(buffer);
|
||||
}
|
||||
});
|
||||
|
||||
ctx.req.on('close', () => {
|
||||
if (len < length) {
|
||||
reject(new Error('socket hanged up unexceptly.'));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return next();
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Next } from "koa";
|
||||
import { Context } from "../types.js";
|
||||
import { rc4Transfrom } from "../utils/rc4.js";
|
||||
|
||||
export async function crypto(ctx: Context, next: Next) {
|
||||
if (typeof ctx.req.headers['x-eamuse-info'] !== 'string') {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (!(ctx.request.body instanceof Buffer)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
{
|
||||
const eamuseInfo: string[] = ctx.req.headers['x-eamuse-info'].split('-');
|
||||
const version = eamuseInfo[0];
|
||||
const time = eamuseInfo[1];
|
||||
const salt = eamuseInfo[2];
|
||||
|
||||
if (version !== '1') {
|
||||
throw new Error(`unsupported crypto version ${version}`);
|
||||
}
|
||||
|
||||
const decrypted = rc4Transfrom(time + salt, ctx.request.body);
|
||||
ctx.request.body = decrypted;
|
||||
}
|
||||
|
||||
await next();
|
||||
|
||||
if (!(ctx.body instanceof Buffer)) {
|
||||
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 encrypted = rc4Transfrom(time + salt, ctx.body);
|
||||
ctx.body = encrypted;
|
||||
|
||||
ctx.set('X-Eamuse-Info', `1-${time}-${salt}`);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
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({
|
||||
ignoreAttributes: false,
|
||||
parseAttributeValue: true,
|
||||
attributeNamePrefix: '$',
|
||||
removeNSPrefix: true,
|
||||
allowBooleanAttributes: true,
|
||||
});
|
||||
|
||||
function parseValue(node: 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));
|
||||
}
|
||||
|
||||
return new Number(node['#text']);
|
||||
}
|
||||
|
||||
if(['b', 'bool'].includes(node.$__type)) {
|
||||
return !!parseInt(node['#text']);
|
||||
}
|
||||
|
||||
if (['bin', 'binary'].includes(node.$__type)) {
|
||||
return Buffer.from(node['#text'], 'hex')
|
||||
}
|
||||
|
||||
if(['ip4', 'str', 'string'].includes(node.$__type)) {
|
||||
return node['#text'];
|
||||
}
|
||||
|
||||
if(node.$__type == 'time') {
|
||||
return new Date(parseInt(node['#text']) * 1000);
|
||||
}
|
||||
|
||||
return {
|
||||
type: node.$__type,
|
||||
value: node['#text'],
|
||||
}
|
||||
}
|
||||
|
||||
function kxmlToObject(node: any) {
|
||||
if (node instanceof Array) {
|
||||
return node.map(v => kxmlToObject(v));
|
||||
}
|
||||
|
||||
if ('$__type' in node) {
|
||||
return parseValue(node);
|
||||
}
|
||||
|
||||
const obj: object = {};
|
||||
|
||||
for (const key in node) {
|
||||
if (key == '?xml') continue;
|
||||
const value = node[key];
|
||||
|
||||
if (key.startsWith('$')) {
|
||||
obj[key.substring(1)] = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
obj[key] = kxmlToObject(node[key]);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
export async function parseKxml(ctx: Context, next: Next) {
|
||||
if (!(ctx.request.body instanceof Buffer)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if ('x-dev' in ctx.request.headers) {
|
||||
ctx.request.body = JSON.parse(ctx.request.body.toString('utf-8'));
|
||||
} else {
|
||||
const xmlResult = to_xml(ctx.request.body);
|
||||
ctx.request.body = kxmlToObject(parser.parse(xmlResult.data));
|
||||
}
|
||||
|
||||
const body = ctx.request.body as any;
|
||||
|
||||
if ('call' in body) {
|
||||
ctx.pcbid = body.call.srcid;
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Logger } from "@cordisjs/logger";
|
||||
import { DefaultState, ParameterizedContext } from "koa";
|
||||
|
||||
export interface ILaochanContext {
|
||||
model?: string;
|
||||
service?: string;
|
||||
pcbid?: string;
|
||||
logger: Logger;
|
||||
|
||||
request: {
|
||||
body: Buffer | object | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export type Context = ParameterizedContext<DefaultState, ILaochanContext, any>;
|
||||
|
||||
export interface ILaochanService {
|
||||
process(ctx: Context): Promise<any>;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { createHash } from "crypto";
|
||||
import rc4 from 'arc4';
|
||||
|
||||
const EAMUSE_RC4_KEY = '69D74627D985EE2187161570D08D93B12455035B6DF0D8205DF5';
|
||||
|
||||
export function rc4Transfrom(keyHex: string, data: Buffer) {
|
||||
const key = createHash('md5')
|
||||
.update(keyHex + EAMUSE_RC4_KEY, 'hex')
|
||||
.digest();
|
||||
|
||||
return rc4('arc4', key)
|
||||
.encodeBuffer(data)
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
response
|
||||
facility(method="get", status=status)
|
||||
location
|
||||
id(__type="str")= location.id
|
||||
type(__type="u8")= location.type
|
||||
country(__type="str")= location.country
|
||||
region(__type="str")= location.region
|
||||
name(__type="str")= location.name
|
||||
countryname(__type="str")= location.countryName
|
||||
countryjname(__type="str")= location.countryJName
|
||||
regionname(__type="str")= location.regionName
|
||||
regionjname(__type="str")= location.regionJName
|
||||
customercode(__type="str")= location.customerCode
|
||||
companycode(__type="str")= location.companyCode
|
||||
latitude(__type="s32")= location.latitude
|
||||
longitude(__type="s32")= location.longitude
|
||||
accuracy(__type="u8")= location.accuracy
|
||||
line
|
||||
id(__type="str")= line.id
|
||||
class(__type="u8")= line.class
|
||||
upclass(__type="u8")= line.upClass
|
||||
rtt(__type="u16")= line.rtt
|
||||
portfw
|
||||
globalip(__type="ip4")= portfw.globalIp
|
||||
globalport(__type="u16")= portfw.globalPort
|
||||
privateport(__type="u16")= portfw.privatePort
|
||||
public
|
||||
flag(__type="u8")= public.flag
|
||||
name(__type="str")= public.name
|
||||
latitude(__type="str")= public.latitude
|
||||
longitude(__type="str")= public.longitude
|
||||
share
|
||||
url
|
||||
epass(__type="str")= share.url
|
||||
arcadefan(__type="str")= share.url
|
||||
konaminetdx(__type="str")= share.url
|
||||
konamiid(__type="str")= share.url
|
||||
eagate(__type="str")= share.url
|
||||
eapass
|
||||
valid(__type="u16")= share.eapass
|
||||
eacoin
|
||||
notchamount(__type="s32")= share.eacoin.notchAmount
|
||||
notchcount(__type="s32")= share.eacoin.notchCount
|
||||
supplylimit(__type="s32")= share.eacoin.supplyLimit
|
||||
calendar
|
||||
year(__type="s16")= calendar.year
|
||||
holiday(__type="s16" __count=calendar.holiday.length)= calendar.holiday.join(' ')
|
||||
@@ -0,0 +1,6 @@
|
||||
response
|
||||
message(
|
||||
expire=expire,
|
||||
method="get",
|
||||
status=status,
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
response
|
||||
pcbtracker(
|
||||
ecenable=ecEnable,
|
||||
expire=expire,
|
||||
method="alive",
|
||||
status=status,
|
||||
time=time,
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
response
|
||||
services(expire=43200 method="get" mode="operation" product_domain=1 status=0)
|
||||
item(name="keepalive" url=keepaliveUrl)
|
||||
item(name="ntp" url=ntpUrl)
|
||||
item(name="services" url=selfUrl)
|
||||
item(name="dlstatus" url=selfUrl)
|
||||
item(name="cardmng" url=selfUrl)
|
||||
item(name="eacoin" url=selfUrl)
|
||||
item(name="userdata" url=selfUrl)
|
||||
item(name="userid" url=selfUrl)
|
||||
item(name="pcbtracker" url=selfUrl)
|
||||
item(name="pcbevent" url=selfUrl)
|
||||
item(name="message" url=selfUrl)
|
||||
item(name="facility" url=selfUrl)
|
||||
item(name="apsmanager" url=selfUrl)
|
||||
item(name="sidmgr" url=selfUrl)
|
||||
item(name="package" url=selfUrl)
|
||||
item(name="uploader" url=selfUrl)
|
||||
item(name="local" url=selfUrl)
|
||||
item(name="local2" url=selfUrl)
|
||||
item(name="lobby" url=selfUrl)
|
||||
+3
-1
@@ -17,7 +17,9 @@
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitAny": false,
|
||||
"noImplicitThis": false,
|
||||
"strictNullChecks": false
|
||||
"strictNullChecks": false,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true
|
||||
},
|
||||
"include": ["src/**/*", "__tests__/**/*"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user