eacnet framework

This commit is contained in:
GEEKiDoS
2024-04-08 20:30:45 +08:00
parent 6fc9a884f9
commit 7803d4bc85
31 changed files with 759 additions and 837 deletions
+2 -1
View File
@@ -24,6 +24,7 @@
// https://typescript-eslint.io/rules/explicit-function-return-type/
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/no-explicit-any": "off"
"@typescript-eslint/no-explicit-any": "off",
"no-constant-condition": "off"
}
}
+199 -27
View File
@@ -16,6 +16,7 @@
"dotenv": "^16.4.5",
"fast-xml-parser": "^4.3.2",
"koa": "^2.15.0",
"koa-bodyparser": "^4.4.1",
"lodash": "^4.17.21",
"mongodb": "^6.4.0",
"pug": "^3.0.2",
@@ -26,6 +27,7 @@
"devDependencies": {
"@types/jest": "~29.5",
"@types/koa": "^2.13.12",
"@types/koa-bodyparser": "^4.3.12",
"@types/lodash": "^4.14.202",
"@types/node": "~20",
"@types/pug": "^2.0.10",
@@ -2045,6 +2047,15 @@
"@types/node": "*"
}
},
"node_modules/@types/koa-bodyparser": {
"version": "4.3.12",
"resolved": "https://registry.npmmirror.com/@types/koa-bodyparser/-/koa-bodyparser-4.3.12.tgz",
"integrity": "sha512-hKMmRMVP889gPIdLZmmtou/BijaU1tHPyMNmcK7FAHAdATnRcGQQy78EqTTxLH1D4FTsrxIzklAQCso9oGoebQ==",
"dev": true,
"dependencies": {
"@types/koa": "*"
}
},
"node_modules/@types/koa-compose": {
"version": "3.2.8",
"resolved": "https://registry.npmmirror.com/@types/koa-compose/-/koa-compose-3.2.8.tgz",
@@ -2736,6 +2747,14 @@
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/cache-content-type": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/cache-content-type/-/cache-content-type-1.0.1.tgz",
@@ -2749,13 +2768,18 @@
}
},
"node_modules/call-bind": {
"version": "1.0.5",
"resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.5.tgz",
"integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
"version": "1.0.7",
"resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.7.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.1",
"set-function-length": "^1.1.1"
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/callsites": {
@@ -2873,6 +2897,17 @@
"node": ">= 0.12.0"
}
},
"node_modules/co-body": {
"version": "6.1.0",
"resolved": "https://registry.npmmirror.com/co-body/-/co-body-6.1.0.tgz",
"integrity": "sha512-m7pOT6CdLN7FuXUcpuz/8lfQ/L77x8SchHCF4G0RBTJO20Wzmhn5Sp4/5WsKy8OSpifBSUrmg83qEqaDHdyFuQ==",
"dependencies": {
"inflation": "^2.0.0",
"qs": "^6.5.2",
"raw-body": "^2.3.3",
"type-is": "^1.6.16"
}
},
"node_modules/collect-v8-coverage": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz",
@@ -2949,6 +2984,11 @@
"node": ">= 0.8"
}
},
"node_modules/copy-to": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/copy-to/-/copy-to-2.0.1.tgz",
"integrity": "sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w=="
},
"node_modules/cordis": {
"version": "3.5.1",
"resolved": "https://registry.npmmirror.com/cordis/-/cordis-3.5.1.tgz",
@@ -3062,13 +3102,13 @@
}
},
"node_modules/define-data-property": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.1.tgz",
"integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
"version": "1.1.4",
"resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dependencies": {
"get-intrinsic": "^1.2.1",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.0"
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
@@ -3214,6 +3254,25 @@
"is-arrayish": "^0.2.1"
}
},
"node_modules/es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"dependencies": {
"get-intrinsic": "^1.2.4"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/esbuild": {
"version": "0.19.11",
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.19.11.tgz",
@@ -3873,14 +3932,18 @@
}
},
"node_modules/get-intrinsic": {
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
"integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
"version": "1.2.4",
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/get-package-type": {
@@ -4009,17 +4072,17 @@
}
},
"node_modules/has-property-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
"integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dependencies": {
"get-intrinsic": "^1.2.2"
"es-define-property": "^1.0.0"
}
},
"node_modules/has-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.0.1.tgz",
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.0.3.tgz",
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
"engines": {
"node": ">= 0.4"
}
@@ -4104,6 +4167,17 @@
"node": ">=10.17.0"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ignore": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
@@ -4157,6 +4231,14 @@
"node": ">=0.8.19"
}
},
"node_modules/inflation": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/inflation/-/inflation-2.1.0.tgz",
"integrity": "sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==",
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -5093,6 +5175,19 @@
"node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4"
}
},
"node_modules/koa-bodyparser": {
"version": "4.4.1",
"resolved": "https://registry.npmmirror.com/koa-bodyparser/-/koa-bodyparser-4.4.1.tgz",
"integrity": "sha512-kBH3IYPMb+iAXnrxIhXnW+gXV8OTzCu8VPDqvcDHW9SQrbkHmqPQtiZwrltNmSq6/lpipHnT7k7PsjlVD7kK0w==",
"dependencies": {
"co-body": "^6.0.0",
"copy-to": "^2.0.1",
"type-is": "^1.6.18"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/koa-compose": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/koa-compose/-/koa-compose-4.1.0.tgz",
@@ -5916,6 +6011,17 @@
}
]
},
"node_modules/qs": {
"version": "6.12.0",
"resolved": "https://registry.npmmirror.com/qs/-/qs-6.12.0.tgz",
"integrity": "sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==",
"dependencies": {
"side-channel": "^1.0.6"
},
"engines": {
"node": ">=0.6"
}
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -5936,6 +6042,43 @@
}
]
},
"node_modules/raw-body": {
"version": "2.5.2",
"resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/raw-body/node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/raw-body/node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
@@ -6140,6 +6283,11 @@
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
@@ -6174,14 +6322,16 @@
"dev": true
},
"node_modules/set-function-length": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.1.1.tgz",
"integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"dependencies": {
"define-data-property": "^1.1.1",
"get-intrinsic": "^1.2.1",
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.0"
"has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
@@ -6213,6 +6363,20 @@
"node": ">=8"
}
},
"node_modules/side-channel": {
"version": "1.0.6",
"resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.0.6.tgz",
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
"dependencies": {
"call-bind": "^1.0.7",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4",
"object-inspect": "^1.13.1"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@@ -6724,6 +6888,14 @@
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/update-browserslist-db": {
"version": "1.0.13",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
+2
View File
@@ -9,6 +9,7 @@
"devDependencies": {
"@types/jest": "~29.5",
"@types/koa": "^2.13.12",
"@types/koa-bodyparser": "^4.3.12",
"@types/lodash": "^4.14.202",
"@types/node": "~20",
"@types/pug": "^2.0.10",
@@ -49,6 +50,7 @@
"dotenv": "^16.4.5",
"fast-xml-parser": "^4.3.2",
"koa": "^2.15.0",
"koa-bodyparser": "^4.4.1",
"lodash": "^4.17.21",
"mongodb": "^6.4.0",
"pug": "^3.0.2",
@@ -1,5 +1,6 @@
import _ from 'lodash';
import { to_bin } from '@kamyu/kbinxml';
import { LZ77 } from '../utils/lz77.js';
function serializeValue(value: any, type: string): string {
if (
@@ -115,7 +116,7 @@ function serializeObject(obj: Object, name: string, linePrefix: string = '') {
return output + `</${name}>\n`;
}
export function kxml(topName: string = 'response', encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8'): MethodDecorator {
export function eacnet(topName: string, 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[]) {
@@ -123,27 +124,17 @@ export function kxml(topName: string = 'response', encoding: 'UTF-8' | 'SHIFT_JI
const xml =
`<?xml version="1.0" encoding="${encoding}"?>` + serializeObject(obj, topName);
const kxml = to_bin(xml);
const compressed = LZ77.compress(kxml.data);
const result = Buffer.allocUnsafe(compressed.length + 0x2E);
return Buffer.from(kxml.data);
result.write('P2D:2015091800', 0, 'ascii');
result.set(compressed, 0x2E);
return result;
} as unknown as any;
}
}
export function serviceKxml(service: string, topName: string = 'response', encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8'): (method: string) => MethodDecorator {
return function (method: string) {
const toKxml = kxml(topName, encoding);
return function (_target, _propertyKey, descriptor) {
const orig = descriptor.value as Function;
descriptor.value = async function (...args: any[]) {
const obj = await orig.apply(this, args);
return { [service]: { method, ...obj } };
} as unknown as any;
toKxml(_target, _propertyKey, descriptor);
}
}
}
// console.log(serializeObject({
// $attr: 'attr1',
// sb: {
-21
View File
@@ -1,21 +0,0 @@
import pug from 'pug';
import { to_bin } from '@kamyu/kbinxml';
export function kxmlTemplate(
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 =
`<?xml version="1.0" encoding="${encoding}"?>` + template(obj);
const kxml = to_bin(xml);
return Buffer.from(kxml.data);
} as unknown as any;
};
}
+37 -45
View File
@@ -1,15 +1,13 @@
import 'reflect-metadata';
import Koa, { DefaultState } from 'koa';
import bodyParser from 'koa-bodyparser';
import { InjectionToken, Provider, container } from 'tsyringe';
import { ILaochanContext } from './types.js';
import { DefaultService } from './services/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 { eacnet } from './middlewares/eacnet.js';
import config from './utils/config.js';
import { MongoClient, Db } from 'mongodb';
@@ -30,7 +28,7 @@ function register<T>(
}
async function main(): Promise<void> {
const logger = new Logger('laochan-net');
const logger = new Logger('laochan-eacnet');
logger.info('initialization...');
@@ -59,38 +57,22 @@ async function main(): Promise<void> {
ctx.body = `${e.stack ?? 'Internal Error'}`;
}
})
.use(binaryBody)
.use(crypto)
.use(decompress)
.use(parseKxml)
.use(bodyParser({
formLimit: '1mb',
}))
.use(eacnet)
.use(async (ctx, next) => {
if (typeof ctx.query.f !== 'string') {
if (!ctx.service) {
throw new Error('invaild service');
}
if (typeof ctx.query.model !== 'string') {
throw new Error('invaild model');
}
// eslint-disable-next-line prefer-const
let [serviceName, method] = ctx.query.f.split('.');
ctx.model = ctx.query.model;
ctx.service = {
name: serviceName,
method,
};
ctx.body = ctx.body[ctx.service.name];
ctx.logger = ctx.service
? register(`logger:${ctx.service.name}`, {
useValue: new Logger(ctx.service.name),
? register(`logger:${ctx.service.name}:${ctx.service.method}`, {
useValue: new Logger(`${ctx.service.name}:${ctx.service.method}`),
})
: logger;
let service: object | undefined = ctx.service
? tryResolve(ctx.service.name)
: container.resolve(DefaultService);
let service: object | undefined = tryResolve(ctx.service.name);
if (service === undefined) {
try {
@@ -100,43 +82,53 @@ async function main(): Promise<void> {
module = await import(
`./services/${ctx.service.name}/index.js`
);
} catch {
module = await import(
`./services/${ctx.service.name}.js`
);
} catch (e) {
if (e.code != 'ERR_MODULE_NOT_FOUND') {
logger.error(
`load service error %s`,
JSON.stringify(e, Object.getOwnPropertyNames(e), 2),
);
} else {
module = await import(
`./services/${ctx.service.name}.js`
);
}
}
service = register(ctx.service.name, {
useClass: module.default,
});
} catch (e) {
logger.error(
`load service error %s`,
JSON.stringify(e, Object.getOwnPropertyNames(e), 2),
);
if (e.code != 'ERR_MODULE_NOT_FOUND') {
logger.error(
`load service error %s`,
JSON.stringify(e, Object.getOwnPropertyNames(e), 2),
);
}
}
}
logger.info(
'%s.$s [%s]: request = %s',
'%s.%s [%s]: request = %s',
ctx.service.name,
ctx.service.method,
ctx.pcbid,
JSON.stringify(ctx.request.body),
ctx.token,
JSON.stringify(ctx.body),
);
if (service === undefined || !(method in service)) {
logger.warn(`unimplemented method ${ctx.service.method} in ${ctx.service.name}`);
let method = ctx.service.method;
if (!service || !(service[method])?.bind) {
logger.warn(`unimplemented method ${method} in ${ctx.service.name}`);
service = container.resolve(DefaultService);
method = 'default';
}
ctx.body = await service[method](ctx);
ctx.body = await (service[method] as Function).bind(service)(ctx);
ctx.status = 200;
logger.info('%s [server]: status = %d', ctx.service, ctx.status);
logger.info('%s.%s [server]: status = %d', ctx.service.name, ctx.service.method, ctx.status);
return await next();
});
-35
View File
@@ -1,35 +0,0 @@
import { Next } from 'koa';
import { Context } from '../types.js';
export async function binaryBody(ctx: Context, next: Next): Promise<any> {
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();
}
-45
View File
@@ -1,45 +0,0 @@
import { Next } from 'koa';
import { Context } from '../types.js';
import { rc4Transfrom } from '../utils/rc4.js';
export async function crypto(ctx: Context, next: Next): Promise<any> {
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}`);
}
-27
View File
@@ -1,27 +0,0 @@
import { Next } from 'koa';
import { Context } from '../types.js';
import { lz77Decode } from '../utils/lz77.js';
export async function decompress(ctx: Context, next: Next): Promise<any> {
if (!(ctx.request.body instanceof Buffer)) {
return next();
}
if (typeof ctx.request.headers['x-compress'] !== 'string') {
return next();
}
// no compression for response
ctx.set('X-Compress', 'none');
if (ctx.request.headers['x-compress'] == 'none') {
return next();
}
if (ctx.request.headers['x-compress'] != 'lz77') {
throw new Error('unsupported compression mehotd');
}
ctx.request.body = lz77Decode(ctx.request.body);
return next();
}
@@ -1,5 +1,6 @@
import { Next } from 'koa';
import { Context } from '../types.js';
import { LZ77 } from '../utils/lz77.js';
import { to_xml } from '@kamyu/kbinxml';
import { XMLParser } from 'fast-xml-parser';
@@ -83,26 +84,43 @@ function kxmlToObject(node: Record<string, unknown>): any {
return obj;
}
export async function parseKxml(ctx: Context, next: Next): Promise<object> {
if (!(ctx.request.body instanceof Buffer)) {
export async function eacnet(ctx: Context, next: Next): Promise<any> {
const body = ctx.request.body as {
request: string;
p2d_token: string;
} | undefined;
if (!body || !body.request) {
return next();
}
if ('x-dev' in ctx.request.headers) {
ctx.request.body = JSON.parse(ctx.request.body.toString('utf-8'));
const { request, p2d_token: token } = body;
const buffer = Buffer.from(request
.replaceAll(' ', '+')
.replaceAll('-', '+')
.replaceAll('_', '/') + '===',
'base64'
);
const decoded = LZ77.decompress(buffer);
const xmlResult = kxmlToObject(parser.parse(to_xml(decoded).data));
const game = Object.keys(xmlResult)[0];
if (!(game in xmlResult)) {
return next();
}
if(xmlResult[game].params) {
ctx.body = xmlResult.p2d.params;
} else {
const xmlResult = to_xml(ctx.request.body);
ctx.request.body = kxmlToObject(parser.parse(xmlResult.data));
ctx.body = {};
}
const body = ctx.request.body as any;
if ('call' in body) {
ctx.pcbid = body.call.srcid;
ctx.tag = body.call.tag;
ctx.body = body.call;
ctx.service = {
name: game,
method: xmlResult[game].method,
}
ctx.token = token;
return next();
}
-52
View File
@@ -1,52 +0,0 @@
import { serviceKxml } from "../../decorators/to-kxml.js";
// import { v } from "../../utils/kxml-value.js";
const resp = serviceKxml('IIDX31music');
export default class {
@resp('crate')
async crate() {
return {
$expire: 1800,
c: [
// v.s32('114514', { mid: 11451 }),
],
};
}
@resp('reg')
async reg() {
return {
$clid: 0,
$crate: 0,
$frate: 0,
$mid: 0,
$rankside: 0,
$status: 0,
shopdata: {
$rank: 252,
},
ranklist: {
$total_user_num: 1,
data: [{
$clflg: 4,
$dgrade: -1,
$sgrade: -1,
$iidx_id: '114514',
$myFlg: '0',
$name: 'MK_',
$opname: '雷窝',
$pid: 0,
$rnum: 0,
$score: 0,
$update: 0,
$body: 0,
$face: 0,
$hair: 0,
$hand: 0,
$head: 0,
}]
}
}
}
}
-194
View File
@@ -1,194 +0,0 @@
import { serviceKxml } from "../../decorators/to-kxml.js";
import { Context } from "../../types.js";
import { v } from "../../utils/kxml-value.js";
interface GetPCDataCall {
ccode: string,
cid: string,
ctype: string,
did: string,
lid: string,
pid: string,
rid: string,
}
const resp = serviceKxml('IIDX31pc');
export default class {
@resp('common')
async common() {
return {
system_voice_phase: {
$phase: 0,
},
ir: {
$beat: 0,
},
vip_pass_black: {},
common_evnet: {
$flg: -1,
},
play_video: {},
music_retry: {},
display_asio_logo: {},
lane_gacha: {},
tourism_booster: {},
cm_movie_info: {
$type: 3,
},
monthly_mranking: v.u16([]),
total_mranking: v.u16([]),
hitchart: [{
$kind: 0,
$period: 0,
ranking: [{
$music_id: 31021,
$rank: 1
}]
}],
$expire: 180,
};
}
@resp('get')
get(ctx: Context) {
const { cid } = ctx.body as GetPCDataCall;
return {
pcdata: this.getPlayerPcData(cid),
bind_eaappli: {},
secret: {
flg1: v.s64([-1, -1, 1048575]),
flg2: v.s64([-1, -1, 0]),
flg3: v.s64([-1, -1, 0]),
flg4: v.s64([-1, -1, 0]),
},
favorite: {
sp_mlist: v.bin(Buffer.alloc(1, 0)),
sp_clist: v.bin(Buffer.alloc(1, 0)),
dp_mlist: v.bin(Buffer.alloc(1, 0)),
dp_clist: v.bin(Buffer.alloc(1, 0)),
},
extra_favorite: [{
$folder_id: 0,
sp_mlist: v.bin(Buffer.alloc(1, 0)),
sp_clist: v.bin(Buffer.alloc(1, 0)),
dp_mlist: v.bin(Buffer.alloc(1, 0)),
dp_clist: v.bin(Buffer.alloc(1, 0)),
}, {
$folder_id: 0,
sp_mlist: v.bin(Buffer.alloc(1, 0)),
sp_clist: v.bin(Buffer.alloc(1, 0)),
dp_mlist: v.bin(Buffer.alloc(1, 0)),
dp_clist: v.bin(Buffer.alloc(1, 0)),
}],
grade: {
$dgid: -1,
$sgid: -1,
g: v.u8([0, 13, 3, 58]),
},
skin: v.s16(new Array(20).fill(0)),
tdjskin: v.s16([0, 0, 0, 0]),
qprodata: v.s16([0, 0, 0, 0, 0]),
spdp_rival: {
$flg: 1,
},
rlist: {},
ex: {},
secret_course_data: {},
weekly: {
$mid: 31048,
$wid: 2041,
},
weekly_score: [],
visitor: {
$anum: 1,
$pnum: 1,
$snum: 1,
$vs_flg: 0,
},
step: {
}
}
}
private getPlayerPcData(id: string) {
const data = {
d_auto_adjust: 0,
d_auto_scrach: 0,
d_camera_layout: 0,
d_classic_hispeed: 0,
d_disp_judge: 0,
d_gauge_disp: 0,
d_ghost_score: 0,
d_gno: 0,
d_graph_score: 0,
d_gtype: 0,
d_hispeed: 0.000000,
d_judge: 0,
d_judgeAdj: 0,
d_lane_brignt: 0,
d_liflen: 0,
d_notes: 0.000000,
d_opstyle: 0,
d_pace: 0,
d_sdlen: 0,
d_sdtype: 0,
d_sorttype: 0,
d_sub_gno: 0,
d_timing: 0,
d_timing_split: 0,
d_tsujigiri_disp: 0,
d_visualization: 0,
dach: 0,
dp_opt: 1048577,
dp_opt2: 1048577,
dpnum: 0,
gpos: 0,
id: 0,
idstr: '00000000',
mode: 3,
name: 'MK_',
ngrade: 0,
pid: 48,
player_kind: 0,
pmode: 0,
rtype: 0,
s_auto_adjust: 1,
s_auto_scrach: 0,
s_camera_layout: 0,
s_classic_hispeed: 0,
s_disp_judge: 1,
s_gauge_disp: 0,
s_ghost_score: 0,
s_gno: 0,
s_graph_score: 0,
s_gtype: 1,
s_hispeed: 7.888014,
s_judge: 0,
s_judgeAdj: -22,
s_lane_brignt: 0,
s_liflen: 90,
s_notes: 28.497925,
s_opstyle: 2,
s_pace: 0,
s_sdlen: 258,
s_sdtype: 1,
s_sorttype: 0,
s_sub_gno: 0,
s_timing: 1,
s_timing_split: 1,
s_tsujigiri_disp: 0,
s_visualization: 0,
sach: 49,
sp_opt: 17180950528,
spnum: 2,
cardid: id,
};
return Object.fromEntries(
Object.entries(data).map(v => ['$' + v[0], v[1]])
);
}
}
-22
View File
@@ -1,22 +0,0 @@
import { serviceKxml } from "../../decorators/to-kxml.js";
const resp = serviceKxml('IIDX31shop');
export default class {
@resp('getname')
async getname() {
return {
$cls_opt: 14400,
$expire: 0,
$opname: '原神, 启动',
$pid: 30,
};
}
@resp('sentinfo')
async sentinfo() {
return {
$status: 0,
};
}
}
-13
View File
@@ -1,13 +0,0 @@
import { serviceKxml } from "../../decorators/to-kxml.js";
const resp = serviceKxml('IIDX31streaming');
export default class {
@resp('common')
common() {
return {
$expire: Math.floor(new Date().valueOf() / 1000) + 259200,
cm_info: {},
};
}
}
-58
View File
@@ -1,58 +0,0 @@
import { serviceKxml } from "../../decorators/to-kxml.js";
import { Context } from "../../types.js";
interface CardInquire {
cardid: string,
cardtype: string,
}
interface CardAuth {
pass: string,
refid: string,
}
interface GetRefId {
cardid: string,
passwd: string;
}
const resp = serviceKxml('cardmng');
export default class {
@resp('authpass')
authpass(ctx: Context) {
const { refid, pass } = ctx.body as CardAuth;
ctx.logger.info('auth %s with pass %s', refid, pass);
return {
$status: 0,
};
}
@resp('getrefid')
getrefid(ctx: Context) {
const { cardid, passwd } = ctx.body as GetRefId;
ctx.logger.info('registering %s with passwd %s.', cardid, passwd);
return {
$dataid: cardid,
$refid: cardid,
}
}
@resp('inquire')
inquire(ctx: Context) {
const { cardid, cardtype } = ctx.body as CardInquire;
ctx.logger.info('card %s (type %d) inquire', cardid, cardtype);
return {
$binded: 1,
$newflag: 0,
$ecflag: 1,
$expired: 0,
$dataid: cardid,
$refid: cardid,
$status: 0,
};
}
}
+14 -8
View File
@@ -1,14 +1,20 @@
import { Context } from '../types.js';
import { kxml } from '../decorators/to-kxml.js';
import { eacnet } from '../decorators/eacnet.js';
import { v } from '../utils/kxml-value.js';
export class DefaultService {
@kxml()
async default(ctx: Context) {
return {
[ctx.service.name]: {
$method: ctx.service.method,
status: '0',
}
}
const descriptor = {
value: () => ({
status: v.s32(0),
error: v.s32(0),
result: {
dummy: v.u8(0),
}
})
};
eacnet(ctx.service.name)(undefined, undefined, descriptor);
return descriptor.value();
}
}
-29
View File
@@ -1,29 +0,0 @@
import { serviceKxml } from "../../decorators/to-kxml.js";
import { v } from "../../utils/kxml-value.js";
const resp = serviceKxml('eacoin');
export default class {
@resp('checkin')
checkin() {
return {
$status: 0,
balance: v.s32(114514),
sessid: v.str('0000000000000000'),
acstatus: v.u8(0),
sequence: v.s16(1),
acid: v.str('0000000000000000'),
acname: v.str('0000000000000000'),
}
}
@resp('consume')
consume() {
return {
$status: 0,
autocharge: v.u8(0),
acstatus: v.u8(0),
balance: v.u8(114514),
}
}
}
-62
View File
@@ -1,62 +0,0 @@
import { kxmlTemplate } from '../../decorators/render-kxml.js';
import { singleton } from 'tsyringe';
@singleton()
export default class {
@kxmlTemplate('facility/get')
async get(): Promise<object> {
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,
],
},
};
}
}
-13
View File
@@ -1,13 +0,0 @@
import { serviceKxml } from '../../decorators/to-kxml.js';
const resp = serviceKxml('message');
export default class {
@resp('get')
async get(): Promise<object> {
return {
$status: 0,
$expire: 60,
};
}
}
+21
View File
@@ -0,0 +1,21 @@
import { Service } from "./service.js";
function Combine(...modules: ({ new(): any })[]) {
class Modules {
constructor() {
modules.forEach(v => Object.assign(this, new v()));
}
}
modules.forEach(v => {
Object.getOwnPropertyNames(v.prototype)
.filter(p => p != 'constructor')
.forEach(p => Modules.prototype[p] = v.prototype[p])
});
return Modules;
}
export default class extends Combine(Service) {
}
+379
View File
@@ -0,0 +1,379 @@
import { eacnet } from '../../decorators/eacnet.js';
import { Context } from '../../types.js';
import config from '../../utils/config.js';
import { v } from '../../utils/kxml-value.js';
export class Service {
@eacnet('p2d')
async getServerState() {
return {
status: v.s32(0),
error: v.s32(0),
result: {
server_state: v.s32(1),
mainte_start_clock: v.u64(0),
mainte_end_clock: v.u64(0),
}
}
}
@eacnet('p2d')
async getServerClock() {
return {
status: v.s32(0),
error: v.s32(0),
result: {
server_clock: v.u64(new Date().valueOf()),
}
}
}
@eacnet('p2d')
async getServerValues() {
return {
status: v.s32(0),
error: v.s32(0),
result: {
bit_boost_magnification: v.str('2'),
debug_user_force_subscription: v.str('TRUE'),
devline_ignore_maintenance: v.str('TRUE'),
enable_file_upload: v.str('TRUE'),
frame_bit_magnification: v.str('TRUE'),
open_bemani_linkage_hinabita2023: v.str('TRUE'),
open_bemani_linkage_knst2024sp: v.str('TRUE'),
open_bemani_linkage_mixup: v.str('TRUE'),
}
}
}
@eacnet('p2d')
async checkGameVersion() {
return {
status: v.s32(0),
error: v.s32(0),
result: {
permission: v.s32(1),
}
};
}
@eacnet('p2d')
async heartbeat(ctx: Context) {
return {
status: v.s32(0),
error: v.s32(0),
result: {
state: v.s32(1),
next_clock: v.u64(new Date().valueOf() + 36e5),
token: ctx.token,
time_remain: v.s32(0x7fffffff),
subscription_status: v.s32(1),
}
};
}
@eacnet('p2d')
async checkSendLogAvailable() {
return {
status: v.s32(0),
error: v.s32(0),
result: {
status: v.s32(1),
daily_count: v.s32(999),
}
};
}
@eacnet('p2d')
async getServices() {
return {
status: v.s32(0),
error: v.s32(0),
result: {
service_num: v.s32(63),
service: [
{
service_name: v.str(`addPoint`),
url: v.str(`${config.selfUrl}/needAuth/AddPoint`),
},
{
service_name: v.str(`addPointDirect`),
url: v.str(`${config.selfUrl}/needAuth/AddPointDirect`),
},
{
service_name: v.str(`cancelReserveChangePoint`),
url: v.str(
`${config.selfUrl}/needAuth/CancelReserveChangePoint`,
),
},
{
service_name: v.str(`cancelReserveConsumeItem`),
url: v.str(
`${config.selfUrl}/needAuth/CancelReserveConsumeItem`,
),
},
{
service_name: v.str(`checkChampionshipInfo`),
url: v.str(
`${config.selfUrl}/needlessAuth/CheckChampionshipInfo`,
),
},
{
service_name: v.str(`checkGameStart`),
url: v.str(`${config.selfUrl}/preProcess/CheckGameStart`),
},
{
service_name: v.str(`checkGameVersion`),
url: v.str(`${config.selfUrl}/needlessAuth/CheckGameVersion`),
},
{
service_name: v.str(`checkPlayData`),
url: v.str(`${config.selfUrl}/needAuth/CheckPlayData`),
},
{
service_name: v.str(`checkPlayableMusicList`),
url: v.str(
`${config.selfUrl}/needAuth/CheckPlayableMusicList`,
),
},
{
service_name: v.str(`checkSendLogAvailable`),
url: v.str(
`${config.selfUrl}/needlessAuth/CheckSendLogAvailable`,
),
},
{
service_name: v.str(`checkTakeover`),
url: v.str(`${config.selfUrl}/needAuth/CheckTakeover`),
},
{
service_name: v.str(`checkUpdate`),
url: v.str(`${config.selfUrl}/needlessAuth/CheckUpdate`),
},
{
service_name: v.str(`checkVersion`),
url: v.str(
`${config.selfUrl}/needlessAuth/CheckLauncherVersion`,
),
},
{
service_name: v.str(`consumeItem`),
url: v.str(`${config.selfUrl}/needAuth/ConsumeItem`),
},
{
service_name: v.str(`gameEnd`),
url: v.str(`${config.selfUrl}/needAuth/GameEnd`),
},
{
service_name: v.str(`getAdvertise`),
url: v.str(`${config.selfUrl}/needlessAuth/GetAdvertise`),
},
{
service_name: v.str(`getChampionshipInfo`),
url: v.str(
`${config.selfUrl}/needlessAuth/GetChampionshipInfo`,
),
},
{
service_name: v.str(`getChampionshipRank`),
url: v.str(`${config.selfUrl}/needAuth/GetChampionshipRank`),
},
{
service_name: v.str(`getChampionshipRecord`),
url: v.str(`${config.selfUrl}/needAuth/GetChampionshipRecord`),
},
{
service_name: v.str(`getClearRate`),
url: v.str(`${config.selfUrl}/needlessAuth/GetClearRate`),
},
{
service_name: v.str(`getCompeScoreData`),
url: v.str(`${config.selfUrl}/needAuth/GetCompeScoreData`),
},
{
service_name: v.str(`getCustomizeSetting`),
url: v.str(`${config.selfUrl}/needAuth/GetCustomizeSetting`),
},
{
service_name: v.str(`getDownloadResource`),
url: v.str(
'https://d1rc4pwxnc0pe0.cloudfront.net/v2/resource/distribution/ondemand',
),
},
{
service_name: v.str(`getGoodsList`),
url: v.str(`${config.selfUrl}/needlessAuth/GetGoodsList`),
},
{
service_name: v.str(`getHash`),
url: v.str(`${config.selfUrl}/needlessAuth/GetHash`),
},
{
service_name: v.str(`getInformation`),
url: v.str(`${config.selfUrl}/needlessAuth/GetInformation`),
},
{
service_name: v.str(`getItemList`),
url: v.str(`${config.selfUrl}/needAuth/GetItemList`),
},
{
service_name: v.str(`getItemNum`),
url: v.str(`${config.selfUrl}/needAuth/GetItemNum`),
},
{
service_name: v.str(`getMusicData`),
url: v.str(`${config.selfUrl}/needAuth/GetMusicData`),
},
{
service_name: v.str(`getMusicGhost`),
url: v.str(`${config.selfUrl}/needAuth/GetMusicGhost`),
},
{
service_name: v.str(`getMusicList`),
url: v.str(`${config.selfUrl}/needAuth/GetMusicList`),
},
{
service_name: v.str(`getPackageList`),
url: v.str(`${config.selfUrl}/needlessAuth/GetPackageList`),
},
{
service_name: v.str(`getPlayData`),
url: v.str(`${config.selfUrl}/needAuth/GetPlayData`),
},
{
service_name: v.str(`getPlayableMusicList`),
url: v.str(`${config.selfUrl}/needAuth/GetPlayableMusicList`),
},
{
service_name: v.str(`getPointList`),
url: v.str(`${config.selfUrl}/needAuth/GetPointList`),
},
{
service_name: v.str(`getPrivilegeClient`),
url: v.str(`${config.selfUrl}/needAuth/GetPrivilegeClient`),
},
{
service_name: v.str(`getPrivilegeServer`),
url: v.str(`${config.selfUrl}/needAuth/GetPrivilegeServer`),
},
{
service_name: v.str(`getResourceInfo`),
url: v.str(`${config.selfUrl}/needlessAuth/GetFile`),
},
{
service_name: v.str(`getRival`),
url: v.str(`${config.selfUrl}/needAuth/GetRival`),
},
{
service_name: v.str(`getRivalChallenge`),
url: v.str(`${config.selfUrl}/needAuth/GetRivalChallenge`),
},
{
service_name: v.str(`getRivalInfo`),
url: v.str(`${config.selfUrl}/needAuth/GetRivalInfo`),
},
{
service_name: v.str(`getServerClock`),
url: v.str(`${config.selfUrl}/preProcess/GetServerClock`),
},
{
service_name: v.str(`getServerState`),
url: v.str(`${config.selfUrl}/preProcess/GetServerState`),
},
{
service_name: v.str(`getServerValues`),
url: v.str(`${config.selfUrl}/needlessAuth/GetServerValues`),
},
{
service_name: v.str(`getServices`),
url: v.str(`${config.selfUrl}/preProcess/GetServices`),
},
{
service_name: v.str(`getTemporaryFiles`),
url: v.str(`${config.selfUrl}/needlessAuth/GetTemporaryFiles`),
},
{
service_name: v.str(`heartbeat`),
url: v.str(`${config.selfUrl}/needAuth/Heartbeat`),
},
{
service_name: v.str(`loginEX`),
url: v.str(
'https://p.eagate.573.jp/game/eac2dx/infinitas/API/page/loginEX.html',
),
},
{
service_name: v.str(`registPlayer`),
url: v.str(`${config.selfUrl}/needAuth/RegistPlayer`),
},
{
service_name: v.str(`reportMusicResult`),
url: v.str(`${config.selfUrl}/needAuth/ReportMusicResult`),
},
{
service_name: v.str(`reserveChangePoint`),
url: v.str(`${config.selfUrl}/needAuth/ReserveChangePoint`),
},
{
service_name: v.str(`reserveConsumeItem`),
url: v.str(`${config.selfUrl}/needAuth/ReserveConsumeItem`),
},
{
service_name: v.str(`saveChampionshipRecord`),
url: v.str(
`${config.selfUrl}/needAuth/SaveChampionshipRecord`,
),
},
{
service_name: v.str(`savePlayData`),
url: v.str(`${config.selfUrl}/needAuth/SavePlayData`),
},
{
service_name: v.str(`sendGradeCertificationLog`),
url: v.str(
`${config.selfUrl}/needAuth/SendGradeCertificationLog`,
),
},
{
service_name: v.str(`sendLog`),
url: v.str(`${config.selfUrl}/needlessAuth/SendLog`),
},
{
service_name: v.str(`unlockMusic`),
url: v.str(`${config.selfUrl}/needAuth/UnlockMusic`),
},
{
service_name: v.str(`uploadFile`),
url: v.str(
`${config.selfUrl}/needlessAuth/UploadFileMultipart`,
),
},
{
service_name: v.str(`urlAgreement`),
url: v.str(
'https://p.eagate.573.jp/game/infinitas/2/jump/agreement.html',
),
},
{
service_name: v.str(`urlEaShop`),
url: v.str('https://p.eagate.573.jp/game/eac2dx/jump/eashop.html'),
},
{
service_name: v.str(`urlKAC5th`),
url: v.str(
'http://p.eagate.573.jp/game/kac5th/detail.html?id=infinitas&amp;group=outline&amp;lang=ja',
),
},
{
service_name: v.str(`usePoint`),
url: v.str(`${config.selfUrl}/needAuth/UsePoint`),
},
{
service_name: v.str(`usePointDirect`),
url: v.str(`${config.selfUrl}/needAuth/UsePointDirect`),
},
],
},
};
}
}
-5
View File
@@ -1,5 +0,0 @@
import { DefaultService } from '../default.js';
export default class extends DefaultService {
list = this.default;
}
-5
View File
@@ -1,5 +0,0 @@
import { DefaultService } from '../default.js';
export default class extends DefaultService {
put = this.default;
}
-15
View File
@@ -1,15 +0,0 @@
import { serviceKxml } from '../../decorators/to-kxml.js';
const resp = serviceKxml('pcbtracker');
export default class {
@resp('alive')
async alive(): Promise<object> {
return {
$ecenable: 1,
$expire: 3600,
$status: 0,
$time: Math.floor(new Date().valueOf() / 1000),
};
}
}
-39
View File
@@ -1,39 +0,0 @@
import { singleton } from 'tsyringe';
import config from '../../utils/config.js';
import { serviceKxml } from '../../decorators/to-kxml.js';
const resp = serviceKxml('services');
@singleton()
export default class {
@resp('get')
async get() {
const selfServices = [
'services', 'dlstatus', 'cardmng',
'eacoin', 'userdata', 'userid', 'pcbtracker', 'pcbevent',
'message', 'facility', 'apsmanager', 'sidmgr', 'package',
'uploaders', 'local', 'local2', 'lobby',
]
return {
$expire: 43200,
$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,
}))
]
};
}
}
+2 -7
View File
@@ -2,9 +2,7 @@ import { Logger } from '@cordisjs/logger';
import { DefaultState, ParameterizedContext } from 'koa';
export interface ILaochanContext {
model?: string;
tag?: string;
pcbid?: string;
token?: string;
service: {
name: string,
@@ -12,10 +10,7 @@ export interface ILaochanContext {
};
logger: Logger;
request: {
body: Buffer | object | undefined;
};
body?: unknown;
}
export type Context = ParameterizedContext<DefaultState, ILaochanContext, any>;
+4 -4
View File
@@ -3,11 +3,11 @@ dotenv.config();
class Config {
get port() {
return process.env.PORT ? parseInt(process.env.PORT) : 10573;
return process.env.PORT ? parseInt(process.env.PORT) : 25730;
}
get selfUrl() {
return process.env.SELF_URL ?? 'http://127.0.0.1:10573';
return process.env.SELF_URL ?? 'http://localhost:25730';
}
get ntpUrl() {
@@ -15,11 +15,11 @@ class Config {
}
get mongoUrl() {
return process.env.MONGO_URL ?? 'mongodb://127.0.0.1:27017';
return process.env.MONGO_URL ?? 'mongodb://localhost:27017';
}
get dbName() {
return process.env.DB_NAME ?? 'laochan-net';
return process.env.DB_NAME ?? 'laochan-eacnet';
}
get isDev() {
+1 -1
View File
@@ -27,7 +27,7 @@ function vg(type: ValueTypes) {
}
}
return attrs;
return result;
};
}
+58 -31
View File
@@ -1,37 +1,64 @@
export function lz77Decode(data: Buffer): Buffer {
let offset = 0;
const output: number[] = [];
while (offset < data.length) {
const flag = data[offset];
offset += 1;
for (let bit = 0; bit < 8; bit++) {
if (flag & (1 << bit)) {
output.push(data[offset]);
offset += 1;
} else {
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;
for (let i = 0; i < lookbackLength; i++) {
const loffset = output.length - lookbackOffset;
if (loffset <= 0 || loffset >= output.length) {
output.push(0);
} else {
output.push(output[loffset]);
}
export class LZ77 {
static compress(input: ArrayLike<number>) {
const a = Uint8Array.from(input);
const res = new Uint8Array(a.length + a.length / 8 + 3);
let p = 0;
for (let i = 0; i < a.length; ++i) {
if (i % 8 === 0) {
if (a.length - i < 8) {
res[p] = (Math.pow(2, a.length - i) - 1);
} else {
res[p] = 255;
}
p++;
}
res[p] = a[i];
p++;
}
res[p] = 0;
res[p + 1] = 0;
return Buffer.from(res);
}
return Buffer.from(output);
static decompress(a: ArrayLike<number>) {
const res = new Uint8Array(0x190000);
let p = 0;
let r = 0;
let t = 0;
let b = 8;
let mask = 0;
while (true) {
if (b === 8) {
mask = a[p];
p++;
b = 0;
}
if ((mask & 1) === 1) {
res[r] = a[p];
r++;
p++;
} else {
let distance = a[p];
let count = a[p + 1];
if (distance === 0 && count === 0) break;
p += 2;
distance <<= 4;
distance |= count >> 4;
count = (count & 0x0F) + 3;
t = r - distance;
for (let i = 0; i < count; ++i) {
res[r] = t < 0 ? 0x00 : res[t];
r++;
t++;
}
}
mask >>= 1;
b += 1;
}
const o = res.slice(0, r);
return Buffer.from(o);
}
}
-47
View File
@@ -1,47 +0,0 @@
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(' ')
+1 -1
View File
@@ -4,7 +4,7 @@
"module": "node16",
"lib": ["ES2022"],
"moduleResolution": "node16",
"rootDir": ".",
"rootDir": "./src",
"outDir": "build",
"allowSyntheticDefaultImports": true,
"importHelpers": true,