See commit description
* Basic command implementation: - /debug: Sets VerboseLevel - /account: Creates or deletes an account - /exit: Exits the process - /maintenance: Toggle maintenance * Database System * Basic HTTP handling
This commit is contained in:
parent
2317e5cbb5
commit
09b49c52bf
20
config.json
Normal file
20
config.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"VERBOSE_LEVEL": 2,
|
||||
"MONGO_URI": "mongodb://localhost:27017/crepesr",
|
||||
"HTTP": {
|
||||
"HTTP_HOST": "0.0.0.0",
|
||||
"HTTP_PORT": 443
|
||||
},
|
||||
"DISPATCH": [
|
||||
{
|
||||
"DISPATCH_NAME": "CrepeSR",
|
||||
"DISPATCH_URL": "http://localhost/query_gateway"
|
||||
}
|
||||
],
|
||||
"GAMESERVER": {
|
||||
"SERVER_IP": "127.0.0.1",
|
||||
"SERVER_PORT": 22102,
|
||||
"MAINTENANCE": true,
|
||||
"MAINTENANCE_MSG": "Server is in maintenance mode."
|
||||
}
|
||||
}
|
1649
package-lock.json
generated
1649
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -4,5 +4,12 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "ts-node-dev src/index.ts --respawn --transpile-only"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/express": "^4.17.13",
|
||||
"colorts": "^0.1.63",
|
||||
"express": "^4.18.1",
|
||||
"mongodb": "^4.8.0",
|
||||
"protobufjs": "^7.0.0"
|
||||
}
|
||||
}
|
||||
|
51
src/commands/Interface.ts
Normal file
51
src/commands/Interface.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { createInterface } from 'readline';
|
||||
import _alias from './alias.json';
|
||||
import Logger from '../util/Logger';
|
||||
|
||||
const c = new Logger("Command", "blue");
|
||||
const alias: { [key: string]: string } = _alias;
|
||||
|
||||
export class Command {
|
||||
public readonly name: string;
|
||||
public readonly args: string[];
|
||||
|
||||
public constructor(public readonly full: string) {
|
||||
const split = full.split(" ");
|
||||
this.name = split[0];
|
||||
this.args = split.slice(1);
|
||||
}
|
||||
}
|
||||
|
||||
export default class Interface {
|
||||
public static readonly rl = createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
private constructor() { }
|
||||
|
||||
public static readonly start = () => {
|
||||
Interface.rl.question("", (_command) => {
|
||||
if (!_command) {
|
||||
Interface.start();
|
||||
return;
|
||||
}
|
||||
const cmd = new Command(_command);
|
||||
import(`./${alias[cmd.name] || cmd.name}`).then(async module => {
|
||||
await module.default(cmd);
|
||||
}).catch(err => {
|
||||
if (err.code == "MODULE_NOT_FOUND") {
|
||||
c.log(`Command ${cmd.name} not found.`);
|
||||
return;
|
||||
}
|
||||
c.error(err);
|
||||
});
|
||||
Interface.start();
|
||||
});
|
||||
|
||||
Interface.rl.on('close', () => {
|
||||
console.log('Have a great day!');
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
}
|
37
src/commands/account.ts
Normal file
37
src/commands/account.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import Account from "../db/Account";
|
||||
import Logger from "../util/Logger";
|
||||
import { Command } from "./Interface";
|
||||
const c = new Logger("/account", "blue");
|
||||
|
||||
export default async function handle(command: Command) {
|
||||
switch (command.args[0]) {
|
||||
case "create":
|
||||
if (!command.args[1]) {
|
||||
c.log(`Usage: account create <name> [uid]`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const acc = await Account.createAccount(command.args[1], command.args[2]);
|
||||
c.log(`Account ${acc.name} with UID ${acc.uid} created.`);
|
||||
} catch (e) {
|
||||
c.error(e as Error);
|
||||
}
|
||||
break;
|
||||
case "delete":
|
||||
if (!command.args[1]) {
|
||||
c.log(`Usage: account delete <uid>`);
|
||||
return;
|
||||
}
|
||||
const acc = await Account.getAccountByUID(command.args[1]);
|
||||
if (!acc) {
|
||||
c.error(`Account with UID ${command.args[1]} does not exist.`);
|
||||
return;
|
||||
}
|
||||
Account.deleteAccount(command.args[1]);
|
||||
c.log(`Account ${acc.name} with UID ${acc.uid} deleted successfully.`);
|
||||
break;
|
||||
default:
|
||||
c.log(`Usage: account create <name> [uid]`);
|
||||
c.log(`Usage: account delete <uid>`);
|
||||
}
|
||||
}
|
3
src/commands/alias.json
Normal file
3
src/commands/alias.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"close": "exit"
|
||||
}
|
19
src/commands/debug.ts
Normal file
19
src/commands/debug.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import Config from "../util/Config";
|
||||
import Logger, { VerboseLevel } from "../util/Logger";
|
||||
import { Command } from "./Interface";
|
||||
const c = new Logger("/debug", "blue");
|
||||
|
||||
export default async function handle(command: Command) {
|
||||
if (!command.args[0]) c.log(`VerboseLevel: ${Config.VERBOSE_LEVEL}`);
|
||||
else {
|
||||
let level = parseInt(command.args[0]);
|
||||
if (!level) level = 0;
|
||||
if (level > 2 || level < 0) {
|
||||
c.log("Invalid verbose level. Must be between 0 and 2.");
|
||||
return;
|
||||
}
|
||||
|
||||
Config.VERBOSE_LEVEL = level as unknown as VerboseLevel;
|
||||
c.log(`VerboseLevel set to ${Config.VERBOSE_LEVEL} (${VerboseLevel[level]})`);
|
||||
}
|
||||
}
|
8
src/commands/exit.ts
Normal file
8
src/commands/exit.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import Logger from "../util/Logger";
|
||||
import { Command } from "./Interface";
|
||||
const c = new Logger("/exit", "blue");
|
||||
|
||||
export default async function handle(command: Command) {
|
||||
c.log("Good riddance!");
|
||||
process.exit(1);
|
||||
}
|
23
src/commands/maintenance.ts
Normal file
23
src/commands/maintenance.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import Config from "../util/Config";
|
||||
import Logger, { VerboseLevel } from "../util/Logger";
|
||||
import { Command } from "./Interface";
|
||||
const c = new Logger("/maintenance", "blue");
|
||||
|
||||
export default async function handle(command: Command) {
|
||||
switch (command.args[0]) {
|
||||
case "on":
|
||||
Config.GAMESERVER.MAINTENANCE = true;
|
||||
if (command.args[1]) Config.GAMESERVER.MAINTENANCE_MSG = command.args.slice(1).join(" ");
|
||||
c.log("Maintenance mode enabled.");
|
||||
break;
|
||||
case "off":
|
||||
Config.GAMESERVER.MAINTENANCE = false;
|
||||
c.log("Maintenance mode disabled.");
|
||||
break;
|
||||
default:
|
||||
c.log(`Maintenance mode is ${Config.GAMESERVER.MAINTENANCE ? "enabled" : "disabled"}`);
|
||||
c.log(`Maintenance message: ${Config.GAMESERVER.MAINTENANCE_MSG}`);
|
||||
c.log("Usage: /maintenance [on|off] [message]");
|
||||
break;
|
||||
}
|
||||
}
|
68
src/db/Account.ts
Normal file
68
src/db/Account.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import Logger from "../util/Logger";
|
||||
import Database from "./Database";
|
||||
const c = new Logger("Account");
|
||||
|
||||
interface AccountI {
|
||||
uid: string | number;
|
||||
name: string;
|
||||
token: string;
|
||||
}
|
||||
|
||||
export default class Account {
|
||||
private constructor(public readonly uid: string | number, public readonly name: string, public readonly token: string) {
|
||||
|
||||
}
|
||||
|
||||
public static async getAccountByUID(uid: string | number): Promise<Account | undefined> {
|
||||
const db = Database.getInstance();
|
||||
const account = await db.get("accounts", { _id: Number(uid) });
|
||||
if (!account) return;
|
||||
return new Account(Number(account._id.toString()), account.name, account.token);
|
||||
}
|
||||
|
||||
public static async getAccountByUsername(name: string): Promise<Account | undefined> {
|
||||
const db = Database.getInstance();
|
||||
const account = await db.get("accounts", { name });
|
||||
if (!account) return;
|
||||
return new Account(Number(account._id.toString()), account.name, account.token);
|
||||
}
|
||||
|
||||
public static async createAccount(name: string, uid?: string | number): Promise<Account> {
|
||||
const db = Database.getInstance();
|
||||
let selfAssignedUID = true;
|
||||
if (!uid) {
|
||||
uid = Math.round(Math.random() * 50000);
|
||||
selfAssignedUID = false;
|
||||
}
|
||||
|
||||
const account = await db.get("accounts", { uid });
|
||||
if (account) {
|
||||
if (!selfAssignedUID) {
|
||||
return await Account.createAccount(name, uid);
|
||||
} else {
|
||||
throw new Error(`Account with uid ${uid} already exists.`);
|
||||
}
|
||||
}
|
||||
|
||||
const token = generateToken();
|
||||
await db.set("accounts", { _id: Number(uid), name, token });
|
||||
return new Account(Number(uid), name, token);
|
||||
}
|
||||
|
||||
public static async deleteAccount(uid: string | number): Promise<void> {
|
||||
const db = Database.getInstance();
|
||||
const account = await Account.getAccountByUID(uid);
|
||||
if (!account) {
|
||||
throw new Error(`Account with uid ${uid} does not exist.`);
|
||||
}
|
||||
await db.delete("accounts", { _id: Number(uid) });
|
||||
}
|
||||
}
|
||||
|
||||
function generateToken(): string {
|
||||
let token = "";
|
||||
for (let i = 0; i < 16; i++) {
|
||||
token += Math.random().toString(36).substring(2, 15)
|
||||
}
|
||||
return token;
|
||||
}
|
68
src/db/Database.ts
Normal file
68
src/db/Database.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import { MongoClient } from "mongodb";
|
||||
import Config from "../util/Config";
|
||||
import Logger from "../util/Logger";
|
||||
const c = new Logger("Database");
|
||||
|
||||
export default class Database {
|
||||
public static instance: Database;
|
||||
public static client: MongoClient;
|
||||
private constructor() {
|
||||
Database.client = new MongoClient(Config.MONGO_URI);
|
||||
try {
|
||||
Database.client.connect();
|
||||
} catch (e) {
|
||||
c.error(e as Error);
|
||||
}
|
||||
}
|
||||
|
||||
public static getInstance(): Database {
|
||||
if (!Database.instance) {
|
||||
Database.instance = new Database();
|
||||
}
|
||||
return Database.instance;
|
||||
}
|
||||
|
||||
public async get(collection: string, query?: {}) {
|
||||
try {
|
||||
const db = await Database.client.db();
|
||||
const _collection = db.collection(collection);
|
||||
if (!(await db.listCollections({ name: collection }).toArray()).length) {
|
||||
c.warn(`Collection ${collection} does not exist. Creating...`);
|
||||
await _collection.createIndexes([{ key: { id: 1 }, unique: true }]);
|
||||
}
|
||||
const result = query ? await _collection.findOne(query) : await _collection.findOne();
|
||||
return result;
|
||||
} catch (e) {
|
||||
c.error(e as Error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async set(collection: string, payload: any) {
|
||||
try {
|
||||
const db = await Database.client.db();
|
||||
const _collection = db.collection(collection);
|
||||
if (!(await db.listCollections({ name: collection }).toArray()).length) {
|
||||
c.warn(`Collection ${collection} does not exist. Creating...`);
|
||||
await _collection.createIndexes([{ key: { id: 1 }, unique: true }]);
|
||||
}
|
||||
return await _collection.insertOne(payload);
|
||||
} catch (e) {
|
||||
c.error(e as Error);
|
||||
}
|
||||
}
|
||||
|
||||
public async delete(collection: string, query: {}) {
|
||||
try {
|
||||
const db = await Database.client.db();
|
||||
const _collection = db.collection(collection);
|
||||
if (!(await db.listCollections({ name: collection }).toArray()).length) {
|
||||
c.warn(`Collection ${collection} does not exist. Creating...`);
|
||||
await _collection.createIndexes([{ key: { id: 1 }, unique: true }]);
|
||||
}
|
||||
return await _collection.deleteOne(query);
|
||||
} catch (e) {
|
||||
c.error(e as Error);
|
||||
}
|
||||
}
|
||||
}
|
42
src/http/HttpServer.ts
Normal file
42
src/http/HttpServer.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import express from 'express';
|
||||
import https from 'https';
|
||||
import fs from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import Config from '../util/Config';
|
||||
import Logger, { VerboseLevel } from '../util/Logger';
|
||||
const c = new Logger("HTTP");
|
||||
|
||||
function r(...args: string[]) {
|
||||
return fs.readFileSync(resolve(__dirname, ...args)).toString();
|
||||
}
|
||||
|
||||
const HTTPS_CONFIG = {
|
||||
key: r('./cert/cert.key'),
|
||||
cert: r('./cert/cert.crt'),
|
||||
}
|
||||
|
||||
export default class HttpServer {
|
||||
private readonly server;
|
||||
|
||||
public constructor() {
|
||||
this.server = express();
|
||||
this.server.use(express.json());
|
||||
this.server.route('/*').all((req, res) => {
|
||||
if (Logger.VERBOSE_LEVEL > VerboseLevel.WARNS) c.log(`${req.method} ${req.url}`);
|
||||
import(`./routes${req.url.split('?')[0]}`).then(async r => {
|
||||
await r.default(req, res);
|
||||
}).catch(err => {
|
||||
res.send({
|
||||
code: 0
|
||||
});
|
||||
if (err.code === 'MODULE_NOT_FOUND') return;
|
||||
c.error(err);
|
||||
});
|
||||
});
|
||||
|
||||
https.createServer(HTTPS_CONFIG, this.server).listen(Config.HTTP.HTTP_PORT, Config.HTTP.HTTP_PORT);
|
||||
this.server.listen(80, Config.HTTP.HTTP_HOST, () => {
|
||||
c.log(`Listening on ${Config.HTTP.HTTP_HOST}:${Config.HTTP.HTTP_PORT}`);
|
||||
});
|
||||
}
|
||||
}
|
23
src/http/cert/cert.crt
Normal file
23
src/http/cert/cert.crt
Normal file
@ -0,0 +1,23 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID2TCCAsGgAwIBAgIUO4lg/Dj+kZ9fv+AFxQBrMNUJc9cwDQYJKoZIhvcNAQEL
|
||||
BQAwVjESMBAGA1UEAwwJbG9jYWxob3N0MQswCQYDVQQGEwJDWTEOMAwGA1UECAwF
|
||||
Q3JlcGUxDjAMBgNVBAcMBUNyZXBlMRMwEQYDVQQKDApDcmVwZSBJbmMuMB4XDTIx
|
||||
MTIxMjE2NDY0NloXDTMxMTIxMDE2NDY0NlowVjESMBAGA1UEAwwJbG9jYWxob3N0
|
||||
MQswCQYDVQQGEwJDWTEOMAwGA1UECAwFQ3JlcGUxDjAMBgNVBAcMBUNyZXBlMRMw
|
||||
EQYDVQQKDApDcmVwZSBJbmMuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
|
||||
AQEAnPFIkkIHKJdmWdUdmwja9qENmnmVL7iSFrgMhBRuZadsD1gYb78liWHH/CT4
|
||||
5zmVJ44LiON3DjmCP/gAbFe3epPifB5i6CIh+tolqG8fg9WyyWrP71Z+raaGtlV4
|
||||
NIyybRJI/9Gjysf4aehpCtEKJf4BAy82lrfBhNhmHf16W65c0NCGMJoB9Wr+wZCd
|
||||
R6PzcKgWNa33YVfXTD8PiTOU9cLRvRFgwO870f/8jekxVdHggfTdQmVj9rcNet6X
|
||||
MrWvzUI4LnI2JPyyEpMtlAQnDQ2+aGG5A3GdPPkWeaST+vF6CDCTFkg8Dxw0jQ30
|
||||
jc0uQv/zz0mFuqvvivgpGSXeuwIDAQABo4GeMIGbMB0GA1UdDgQWBBS8E5THWThf
|
||||
TVAwTnGez4druLTacDAfBgNVHSMEGDAWgBS8E5THWThfTVAwTnGez4druLTacDAO
|
||||
BgNVHQ8BAf8EBAMCBaAwIAYDVR0lAQH/BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
|
||||
MCcGA1UdEQQgMB6CDCoubWlob3lvLmNvbYIOKi55dWFuc2hlbi5jb20wDQYJKoZI
|
||||
hvcNAQELBQADggEBAIVqx99S1DYtyLqsi15KwTUawShW9iJVsvpk59vK6qbbzuGZ
|
||||
L72DrRJVTMxodv4WLaiM+te1TsW0WmdrhUYUBfIi+JpB67QB6aWKkOCP8fYq+mWn
|
||||
Q3vuAEC6KpWH30j+0S58LVV+2iaGVetXYmYDXKoNslyVuJAM4iZSutTZhctO2Fxm
|
||||
Vicp0fiPq/HJzxsmKHxyFJsgsdV0Dl9ElnlhpH77qxi/nXuUz9YlWZwwQI8KSsKm
|
||||
sOzTUpSHHHpDocT24Yx73bR3Hd8CJam2bCEOOIIJG7sPx2lhTEJ+sKHDh4jHmRUI
|
||||
3rVk5R+x1CcIrAgin+8nH28PhZFdOKs+CYMYGk0=
|
||||
-----END CERTIFICATE-----
|
28
src/http/cert/cert.key
Normal file
28
src/http/cert/cert.key
Normal file
@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCc8UiSQgcol2ZZ
|
||||
1R2bCNr2oQ2aeZUvuJIWuAyEFG5lp2wPWBhvvyWJYcf8JPjnOZUnjguI43cOOYI/
|
||||
+ABsV7d6k+J8HmLoIiH62iWobx+D1bLJas/vVn6tpoa2VXg0jLJtEkj/0aPKx/hp
|
||||
6GkK0Qol/gEDLzaWt8GE2GYd/XpbrlzQ0IYwmgH1av7BkJ1Ho/NwqBY1rfdhV9dM
|
||||
Pw+JM5T1wtG9EWDA7zvR//yN6TFV0eCB9N1CZWP2tw163pcyta/NQjgucjYk/LIS
|
||||
ky2UBCcNDb5oYbkDcZ08+RZ5pJP68XoIMJMWSDwPHDSNDfSNzS5C//PPSYW6q++K
|
||||
+CkZJd67AgMBAAECggEBAJv3KQC4f3a2Zu+1XBObTEc2rFcsprbi/MN5Km8EAuYg
|
||||
6MGi8b3zvrD1rJGGiJj5X6IMhqgGLWXEfw1lP75ruZomZzij1fUNHqm1qyDlNfOF
|
||||
JoUGEhiu43tc95kx/SB0Bklgl40rYFQAQH23itRGA4jYEVeBzwUfHkEP8QOyyKtc
|
||||
YKJHa1jCEvE4XHxLqxFC+z7aNmpFonrW8OerVwrDPIkFbXYrC1alzxqMcNBJVeKE
|
||||
LFFc6EK9ppoPSsm600yac5gOX2ima9JkaYkGKhK25D4NXCd511Ea/TJNctA8IkYA
|
||||
QB7mCzLHdapRlhSplDwwwgNRzlrkImbqyS2+768ejYkCgYEAzyatfH62cDSDP5e1
|
||||
oukWI46AqybfIhd9kHzIbIyPjuSb3RvvuxIfNBFjUxRyh+ggCjnRA14HcZzDcGRd
|
||||
6VpApq80LQk6mhklKicJNRrcuEvdmj+kRJ+XtSbNZhJVUkNf7nmHCOoJkEwRsblL
|
||||
kIZCVmfkWAKFjHnCEqH5RHvTbacCgYEAwfObyw0eMD+X23IpyEsCsTKIwIk4Zl8J
|
||||
iTNnR9yp6B9JdwLSdEWQjhXSltkfmNZ3WXYoyh6kr3jp8d5EqWX1P8JrZBaZikTD
|
||||
y526kKIvs/I/iW3obOoE6CX6LJEGob8bAPkvu2u37Rghign57W02fYtzJUuqPAoK
|
||||
b5mtrQ/ycM0CgYBsR8pthgq1Mi3dAt82DeK9qVKGpGYEewTujttxKjQsPEFg3aZ9
|
||||
Qaa/38rsdYa8ldCRp9EikncPoyLh0ATq4nti5bg/RlC0lipAE3GTqbvwNe/bHiMu
|
||||
n8F8NpEtJq4ktwUhMbMtLLDdFXY2USY3oIZyhhHtEzxdxpN0i+gxLQzChwKBgQCG
|
||||
FFztYGIwRKY8hI2x83km+qJjR/l/e8/h03Fg0oF7ALYO2hqXWsf2EcwFkJAxXoIf
|
||||
jHniUJDU5agFFv0shlmm/Ea1aJI4bhVVG/MvrY+AvMWDwkFdmeJOgoKScKe/BZgr
|
||||
chi3Xl5GP9pfzUnEAy4aWF7/t3E2FFLml7zi2RVnOQKBgQCYmzwikvHpR+HRiZL9
|
||||
m0TB6mayGbwQO3xU/naZ1UyCydsRORQnbKaTWSMZgrr7nTAqDhegfUyJXX+uxhGQ
|
||||
cGu9uLENZWrysXROpMZBhgyNUbDPdO0okIMoJ3kUSocC7L7lpWyZGOxgMwi0a4qK
|
||||
nV1QDKXsA3oBZn9MJpkuQ81jCw==
|
||||
-----END PRIVATE KEY-----
|
9
src/http/routes/Handler.ts
Normal file
9
src/http/routes/Handler.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export default function handle(req: Request, res: Response) {
|
||||
// Test handler
|
||||
res.send({
|
||||
retcode: -1,
|
||||
message: "Invalid endpoint"
|
||||
});
|
||||
}
|
21
src/http/routes/account/risky/api/check.ts
Normal file
21
src/http/routes/account/risky/api/check.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
// Example request:
|
||||
// {
|
||||
// "action_type": "login",
|
||||
// "api_name": "/shield/api/login",
|
||||
// "username": "test"
|
||||
// }
|
||||
|
||||
export default function handle(req: Request, res: Response) {
|
||||
// Test handler
|
||||
res.send({
|
||||
retcode: 0,
|
||||
message: "OK",
|
||||
data: {
|
||||
id: "",
|
||||
action: "ACTION_NONE",
|
||||
geetest: null
|
||||
}
|
||||
});
|
||||
}
|
9
src/http/routes/combo/box/api/config/sdk/combo.ts
Normal file
9
src/http/routes/combo/box/api/config/sdk/combo.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export default function handle(req: Request, res: Response) {
|
||||
res.send({
|
||||
data: null,
|
||||
message: "RetCode_NoConfig",
|
||||
retcode: 7
|
||||
});
|
||||
}
|
10
src/http/routes/data_abtest_api/config/experiment/list.ts
Normal file
10
src/http/routes/data_abtest_api/config/experiment/list.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export default function handle(req: Request, res: Response) {
|
||||
res.send({
|
||||
retcode: 0,
|
||||
success: true,
|
||||
message: "",
|
||||
data: []
|
||||
});
|
||||
}
|
14
src/http/routes/hkrpg/dataUpload.ts
Normal file
14
src/http/routes/hkrpg/dataUpload.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Request, Response } from "express";
|
||||
import Logger, { VerboseLevel } from "../../../util/Logger";
|
||||
const c = new Logger("dataUpload", "green");
|
||||
|
||||
export default function handle(req: Request, res: Response) {
|
||||
try {
|
||||
const content = req.body[0].uploadContent;
|
||||
if (content.LogStr) {
|
||||
c.warn(content.LogStr);
|
||||
if (Logger.VERBOSE_LEVEL == VerboseLevel.ALL) c.trail(content.StackTrace);
|
||||
}
|
||||
} catch { }
|
||||
res.send({ code: 0 });
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export default function handle(req: Request, res: Response) {
|
||||
// Test handler
|
||||
res.send({
|
||||
retcode: 0,
|
||||
message: "OK",
|
||||
data: {
|
||||
modified: true,
|
||||
protocol: {
|
||||
id: 0,
|
||||
app_id: 11,
|
||||
language: "en",
|
||||
user_proto: "",
|
||||
priv_proto: "",
|
||||
major: 1,
|
||||
minimum: 2,
|
||||
create_time: "0",
|
||||
teenager_proto: "",
|
||||
third_proto: ""
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
17
src/http/routes/hkrpg_global/combo/granter/api/getConfig.ts
Normal file
17
src/http/routes/hkrpg_global/combo/granter/api/getConfig.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export default function handle(req: Request, res: Response) {
|
||||
res.send({
|
||||
retcode: 0,
|
||||
message: "OK",
|
||||
data: {
|
||||
protocol: true,
|
||||
qr_enabled: true,
|
||||
log_level: "INFO",
|
||||
announce_url: "https://sdk.hoyoverse.com/hkrpg/announcement/index.html?sdk_presentation_style=fullscreen\u0026sdk_screen_transparent=true\u0026auth_appid=announcement\u0026authkey_ver=1\u0026sign_type=2#/",
|
||||
push_alias_type: 0,
|
||||
disable_ysdk_guard: true,
|
||||
enable_announce_pic_popup: true
|
||||
}
|
||||
});
|
||||
}
|
17
src/http/routes/hkrpg_global/combo/granter/login/v2/login.ts
Normal file
17
src/http/routes/hkrpg_global/combo/granter/login/v2/login.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export default function handle(req: Request, res: Response) {
|
||||
res.send({
|
||||
retcode: 0,
|
||||
message: "OK",
|
||||
data: {
|
||||
combo_id: "0",
|
||||
open_id: "",
|
||||
combo_token: "",
|
||||
data: '{"guest":false}',
|
||||
heartbeat: false,
|
||||
account_type: 1,
|
||||
fatigue_remind: null
|
||||
}
|
||||
});
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export default function handle(req: Request, res: Response) {
|
||||
res.send({
|
||||
retcode: 0,
|
||||
message: "OK",
|
||||
data: {
|
||||
marketing_agreements: []
|
||||
}
|
||||
});
|
||||
}
|
29
src/http/routes/hkrpg_global/mdk/shield/api/loadConfig.ts
Normal file
29
src/http/routes/hkrpg_global/mdk/shield/api/loadConfig.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export default function handle(req: Request, res: Response) {
|
||||
res.send({
|
||||
retcode: 0,
|
||||
message: "OK",
|
||||
data: {
|
||||
id: 24,
|
||||
game_key: "hkrpg_global",
|
||||
client: "PC",
|
||||
identity: "I_IDENTITY",
|
||||
guest: false,
|
||||
ignore_versions: "",
|
||||
scene: "S_NORMAL",
|
||||
name: "崩坏RPG",
|
||||
disable_regist: true,
|
||||
enable_email_captcha: false,
|
||||
thirdparty: [],
|
||||
disable_mmt: false,
|
||||
server_guest: true,
|
||||
thirdparty_ignore: {
|
||||
fb: "",
|
||||
tw: ""
|
||||
},
|
||||
enable_ps_bind_account: false,
|
||||
thirdparty_login_configs: {}
|
||||
}
|
||||
});
|
||||
}
|
32
src/http/routes/hkrpg_global/mdk/shield/api/login.ts
Normal file
32
src/http/routes/hkrpg_global/mdk/shield/api/login.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { Request, Response } from "express";
|
||||
import Account from "../../../../../../db/Account";
|
||||
import Logger from "../../../../../../util/Logger";
|
||||
|
||||
// Example request:
|
||||
// {
|
||||
// account: "test",
|
||||
// (RSA)password: "BKWPZjqKfKr6ZKuO40ONwV5JxOi4dg71aeBcxPVK/U+8FM8d5kc5EjLdEXyn6McBvUOL67CmT89eo9jrdwp9xpFexA/C1d9BCxen0NQ+zCrQUkSc6AFD9PYkAmdTNnila5L15SrveQQRtbsDwZeZ9owVH7kyoXuDGUOOA6dc4qE=",
|
||||
// is_crypto: true
|
||||
// }
|
||||
|
||||
export default async function handle(req: Request, res: Response) {
|
||||
const c = new Logger(req.ip);
|
||||
const acc = await Account.getAccountByUsername(req.body.account);
|
||||
const dataObj: any = {
|
||||
retcode: 0,
|
||||
message: "OK",
|
||||
data: {
|
||||
account: {}
|
||||
}
|
||||
}
|
||||
if (!acc) {
|
||||
dataObj.retcode = -202;
|
||||
dataObj.message = "Account not found";
|
||||
c.warn(`[DISPATCH] Player ${req.body.account} not found`);
|
||||
res.send(dataObj);
|
||||
} else {
|
||||
dataObj.data.account = acc;
|
||||
c.log(`[DISPATCH] Player ${req.body.account} logged in`);
|
||||
res.send(dataObj);
|
||||
}
|
||||
}
|
35
src/http/routes/hkrpg_global/mdk/shield/api/verify.ts
Normal file
35
src/http/routes/hkrpg_global/mdk/shield/api/verify.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { Request, Response } from "express";
|
||||
import Account from "../../../../../../db/Account";
|
||||
import Logger from "../../../../../../util/Logger";
|
||||
|
||||
// Example request:
|
||||
// {"uid":"63884253","token":"ZQmgMdXA1StL9A3aPBUedr8yoiuoLrmV"}
|
||||
|
||||
export default async function handle(req: Request, res: Response) {
|
||||
const c = new Logger(req.ip);
|
||||
const acc = await Account.getAccountByUID(req.body.uid);
|
||||
const dataObj: any = {
|
||||
retcode: 0,
|
||||
message: "OK",
|
||||
data: {
|
||||
account: {}
|
||||
}
|
||||
}
|
||||
if (!acc) {
|
||||
dataObj.retcode = -202;
|
||||
dataObj.message = "Account not found";
|
||||
res.send(dataObj);
|
||||
c.warn(`[DISPATCH] Player ${req.body.uid} not found`);
|
||||
} else {
|
||||
if (acc.token === req.body.token) {
|
||||
dataObj.data.account = acc;
|
||||
c.log(`[DISPATCH] Player ${req.body.uid} logged in`);
|
||||
res.send(dataObj);
|
||||
} else {
|
||||
dataObj.retcode = -202;
|
||||
dataObj.message = "Invalid token";
|
||||
res.send(dataObj);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
27
src/http/routes/query_dispatch.ts
Normal file
27
src/http/routes/query_dispatch.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { Request, Response } from "express";
|
||||
import Config from "../../util/Config";
|
||||
|
||||
interface Region {
|
||||
dispatch_url: string;
|
||||
env_type: string;
|
||||
name: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export default function handle(req: Request, res: Response) {
|
||||
const dataObj = {
|
||||
region_list: [] as Region[],
|
||||
retcode: 0
|
||||
}
|
||||
|
||||
Config.DISPATCH.forEach(item => {
|
||||
dataObj.region_list.push({
|
||||
dispatch_url: item.DISPATCH_URL,
|
||||
env_type: "2",
|
||||
name: item.DISPATCH_NAME,
|
||||
title: "CrepeSR"
|
||||
});
|
||||
});
|
||||
|
||||
res.send(dataObj);
|
||||
}
|
36
src/http/routes/query_gateway.ts
Normal file
36
src/http/routes/query_gateway.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { Request, Response } from "express";
|
||||
import protobuf from 'protobufjs';
|
||||
import { resolve } from 'path';
|
||||
import Config from "../../util/Config";
|
||||
|
||||
const proto = protobuf.loadSync(resolve(__dirname, '../../proto/QueryCurrRegionHttpRsp.proto')).lookup('QueryCurrRegionHttpRsp') as any;
|
||||
|
||||
export default function handle(req: Request, res: Response) {
|
||||
const dataObj = {
|
||||
retcode: 0,
|
||||
msg: "OK",
|
||||
regionName: "CrepeSR",
|
||||
gateserverIp: Config.GAMESERVER.SERVER_IP,
|
||||
gateserverPort: Config.GAMESERVER.SERVER_PORT,
|
||||
} as any;
|
||||
|
||||
if (Config.GAMESERVER.MAINTENANCE) {
|
||||
dataObj.retcode = 2;
|
||||
dataObj.msg = Config.GAMESERVER.MAINTENANCE_MSG;
|
||||
dataObj.stopBeginTime = Date.now();
|
||||
dataObj.stopEndTime = Date.now() * 2;
|
||||
}
|
||||
|
||||
let rsp;
|
||||
try {
|
||||
rsp = proto!.encode(dataObj).finish();
|
||||
} catch {
|
||||
rsp = proto!.encode({
|
||||
retcode: 2,
|
||||
msg: "Internal server error",
|
||||
stopBeginTime: Date.now(),
|
||||
stopEndTime: Date.now() * 2,
|
||||
}).finish();
|
||||
}
|
||||
res.send(Buffer.from(rsp).toString('base64'));
|
||||
}
|
7
src/http/routes/sdk/dataUpload.ts
Normal file
7
src/http/routes/sdk/dataUpload.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { Request, Response } from "express";
|
||||
import Logger from "../../../util/Logger";
|
||||
const c = new Logger("dataUpload", "green");
|
||||
|
||||
export default function handle(req: Request, res: Response) {
|
||||
res.send({ code: 0 });
|
||||
}
|
@ -3,4 +3,11 @@
|
||||
* @author Crepe-Inc
|
||||
* @license AGPL-3.0
|
||||
*/
|
||||
import Interface from "./commands/Interface";
|
||||
import HttpServer from "./http/HttpServer";
|
||||
import Logger from "./util/Logger";
|
||||
const c = new Logger("CrepeSR");
|
||||
|
||||
c.log(`Starting CrepeSR...`);
|
||||
Interface.start();
|
||||
const http = new HttpServer();
|
38
src/proto/QueryCurrRegionHttpRsp.proto
Normal file
38
src/proto/QueryCurrRegionHttpRsp.proto
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright (C) 2022 CrepeSR
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
message QueryCurrRegionHttpRsp {
|
||||
uint32 retcode = 1; // Always present
|
||||
string msg = 2; // Always present
|
||||
string region_name = 3; // Always present
|
||||
string gateserver_ip = 4;
|
||||
uint32 gateserver_port = 5;
|
||||
int64 stop_begin_time = 6;
|
||||
int64 stop_end_time = 7;
|
||||
bool unknown_boolean_one = 8;
|
||||
bool unknown_boolean_two = 9;
|
||||
string resource_url = 10;
|
||||
string data_url = 11;
|
||||
string lua_url = 12;
|
||||
uint32 unknown_int_one = 15;
|
||||
string client_secret_key = 17;
|
||||
string region_config = 18;
|
||||
string feedback_url = 20;
|
||||
string beta_text = 24; // "当前版本为测试版本,不代表游戏最终品质"
|
||||
bool unknown_boolean_three = 25;
|
||||
bool unknown_boolean_four = 26;
|
||||
}
|
85
src/util/Config.ts
Normal file
85
src/util/Config.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import fs from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import { VerboseLevel } from './Logger';
|
||||
|
||||
const DEFAULT_CONFIG = {
|
||||
// General
|
||||
VERBOSE_LEVEL: 1,
|
||||
|
||||
// MongoDB
|
||||
MONGO_URI: "mongodb://localhost:27017/crepesr",
|
||||
|
||||
// HTTP
|
||||
HTTP: {
|
||||
HTTP_HOST: "0.0.0.0",
|
||||
HTTP_PORT: 443
|
||||
},
|
||||
|
||||
// Dispatch
|
||||
DISPATCH: [{
|
||||
DISPATCH_NAME: "CrepeSR",
|
||||
DISPATCH_URL: "http://localhost/query_gateway"
|
||||
}],
|
||||
|
||||
// GameServer
|
||||
GAMESERVER: {
|
||||
SERVER_IP: "0.0.0.0",
|
||||
SERVER_PORT: 22102,
|
||||
MAINTENANCE: false,
|
||||
MAINTENANCE_MSG: "Server is in maintenance mode."
|
||||
}
|
||||
}
|
||||
type DefaultConfig = typeof DEFAULT_CONFIG;
|
||||
|
||||
function r(...args: string[]) {
|
||||
return fs.readFileSync(resolve(__dirname, ...args)).toString();
|
||||
}
|
||||
|
||||
function readConfig(): any {
|
||||
let config: DefaultConfig;
|
||||
try {
|
||||
config = JSON.parse(r('../../config.json'));
|
||||
// Check if config object.keys is the same as DEFAULT_CONFIG.keys
|
||||
const missing = Object.keys(DEFAULT_CONFIG).filter(key => !config.hasOwnProperty(key));
|
||||
|
||||
if (missing.length > 0) {
|
||||
missing.forEach(key => {
|
||||
// @ts-ignore
|
||||
config[key] = DEFAULT_CONFIG[key];
|
||||
});
|
||||
updateConfig(config);
|
||||
console.log(`Added missing config keys: ${missing.join(', ')}`);
|
||||
}
|
||||
} catch {
|
||||
console.error("Could not read config file. Creating one for you...");
|
||||
config = DEFAULT_CONFIG;
|
||||
updateConfig(config);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
function updateConfig(config: any) {
|
||||
fs.writeFileSync('./config.json', JSON.stringify(config, null, 2));
|
||||
}
|
||||
|
||||
export default class Config {
|
||||
public static config = readConfig();
|
||||
public static VERBOSE_LEVEL: VerboseLevel = Config.config.VERBOSE_LEVEL;
|
||||
public static MONGO_URI: string = Config.config.MONGO_URI;
|
||||
public static HTTP: {
|
||||
HTTP_HOST: string,
|
||||
HTTP_PORT: number
|
||||
} = Config.config.HTTP;
|
||||
public static DISPATCH: {
|
||||
DISPATCH_NAME: string;
|
||||
DISPATCH_URL: string;
|
||||
}[] = Config.config.DISPATCH;
|
||||
public static GAMESERVER: {
|
||||
SERVER_IP: string;
|
||||
SERVER_PORT: number;
|
||||
MAINTENANCE: boolean;
|
||||
MAINTENANCE_MSG: string;
|
||||
} = Config.config.GAMESERVER;
|
||||
|
||||
private constructor() { }
|
||||
}
|
53
src/util/Logger.ts
Normal file
53
src/util/Logger.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import 'colorts/lib/string';
|
||||
import Config from './Config';
|
||||
|
||||
export enum VerboseLevel {
|
||||
NONE = 0, // No logging except for errors
|
||||
WARNS = 1, // Log warns
|
||||
ALL = 2, // Warns and (useless) debug
|
||||
}
|
||||
|
||||
type Color = 'red' | 'green' | 'yellow' | 'blue' | 'magenta' | 'cyan' | 'white' | 'gray' | 'black' | 'italic' | 'bold' | 'underline' | 'strikethrough' | 'inverse' | 'bgRed' | 'bgGreen' | 'bgYellow' | 'bgBlue' | 'bgMagenta' | 'bgCyan' | 'bgWhite' | 'bgBlack' | 'bgGray' | 'bgItalic';
|
||||
|
||||
export default class Logger {
|
||||
public static VERBOSE_LEVEL: VerboseLevel = Config.VERBOSE_LEVEL || 1;
|
||||
|
||||
constructor(public name: string, public color: Color = 'cyan') {
|
||||
this.name = name;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
private getDate(): string {
|
||||
return new Date().toLocaleTimeString();
|
||||
}
|
||||
|
||||
private raw(...args: string[]) {
|
||||
// @ts-ignore - Element implicitly has an 'any' type because index expression is not of type 'number'
|
||||
console.log(`[${this.getDate().white.bold}] <${this.name[this.color].bold}>`, ...args);
|
||||
}
|
||||
|
||||
public log(...args: string[]) {
|
||||
this.raw(...args);
|
||||
}
|
||||
|
||||
public trail(...args: any[]) {
|
||||
console.log(`\t↳ ${args.join(' ').gray}`);
|
||||
}
|
||||
|
||||
public error(e: Error | string) {
|
||||
if (typeof e === 'string') e = new Error(e);
|
||||
console.log(`[${this.getDate().white.bold}] ${`ERROR<${this.name}>`.bgRed.bold}`, e.message);
|
||||
if (e.stack) this.trail(e.stack);
|
||||
}
|
||||
|
||||
public warn(...args: string[]) {
|
||||
if (Logger.VERBOSE_LEVEL < VerboseLevel.WARNS) return;
|
||||
console.log(`[${this.getDate().white.bold}] ${`WARN<${this.name}>`.bgYellow.bold}`, ...args);
|
||||
}
|
||||
|
||||
public debug(...args: any) {
|
||||
if (Logger.VERBOSE_LEVEL < VerboseLevel.ALL) return;
|
||||
console.log(`[${this.getDate().white.bold}] ${`DEBUG<${this.name}>`.bgBlue.bold}`, ...args);
|
||||
this.trail(new Error().stack!.split('\n').slice(2).join('\n'));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user