feat: added redis and asynchronous support

This commit is contained in:
rabbit0w0 2023-01-23 17:58:09 +08:00
parent 01b42b1040
commit b97c976163
No known key found for this signature in database
GPG Key ID: 963D0503CBE23EB3
19 changed files with 490 additions and 150 deletions

70
package-lock.json generated
View File

@ -218,6 +218,47 @@
"make-plural": "^7.0.0"
}
},
"@redis/bloom": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.1.0.tgz",
"integrity": "sha512-9QovlxmpRtvxVbN0UBcv8WfdSMudNZZTFqCsnBszcQXqaZb/TVe30ScgGEO7u1EAIacTPAo7/oCYjYAxiHLanQ==",
"optional": true
},
"@redis/client": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.4.2.tgz",
"integrity": "sha512-oUdEjE0I7JS5AyaAjkD3aOXn9NhO7XKyPyXEyrgFDu++VrVBHUPnV6dgEya9TcMuj5nIJRuCzCm8ZP+c9zCHPw==",
"optional": true,
"requires": {
"cluster-key-slot": "1.1.1",
"generic-pool": "3.9.0",
"yallist": "4.0.0"
}
},
"@redis/graph": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz",
"integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==",
"optional": true
},
"@redis/json": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz",
"integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==",
"optional": true
},
"@redis/search": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.0.tgz",
"integrity": "sha512-NyFZEVnxIJEybpy+YskjgOJRNsfTYqaPbK/Buv6W2kmFNaRk85JiqjJZA5QkRmWvGbyQYwoO5QfDi2wHskKrQQ==",
"optional": true
},
"@redis/time-series": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz",
"integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==",
"optional": true
},
"@socket.io/component-emitter": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
@ -1059,6 +1100,12 @@
"shallow-clone": "^3.0.0"
}
},
"cluster-key-slot": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz",
"integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==",
"optional": true
},
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@ -1709,6 +1756,12 @@
"integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
"dev": true
},
"generic-pool": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
"optional": true
},
"get-intrinsic": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
@ -2738,6 +2791,20 @@
"resolve": "^1.9.0"
}
},
"redis": {
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/redis/-/redis-4.5.1.tgz",
"integrity": "sha512-oxXSoIqMJCQVBTfxP6BNTCtDMyh9G6Vi5wjdPdV/sRKkufyZslDqCScSGcOr6XGR/reAWZefz7E4leM31RgdBA==",
"optional": true,
"requires": {
"@redis/bloom": "1.1.0",
"@redis/client": "1.4.2",
"@redis/graph": "1.1.0",
"@redis/json": "1.0.4",
"@redis/search": "1.1.0",
"@redis/time-series": "1.0.4"
}
},
"regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
@ -3470,8 +3537,7 @@
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"yeast": {
"version": "0.1.2",

View File

@ -55,5 +55,8 @@
"webpack": "^5.73.0",
"webpack-cli": "^4.10.0",
"webpack-node-externals": "^3.0.0"
},
"optionalDependencies": {
"redis": "^4.5.1"
}
}

View File

@ -6,32 +6,17 @@ import os from "os";
import { $t, i18next } from "./app/i18n";
import { initVersionManager, getVersion } from "./app/version";
// load global configuration file
// Storage
import RedisStorage from "./app/common/storage/redis_storage";
import Storage from "./app/common/storage/sys_storage";
import { initSystemConfig, systemConfig } from "./app/setting";
initSystemConfig();
import SystemUser from "./app/service/system_user";
import SystemRemoteService from "./app/service/system_remote_service";
initVersionManager();
const VERSION = getVersion();
// show product Logo
console.log(`______ _______________________ ___
___ |/ /_ ____/_ ___/__ |/ /_____ _____________ _______ _____________
__ /|_/ /_ / _____ \\__ /|_/ /_ __ \`/_ __ \\ __ \`/_ __ \`/ _ \\_ ___/
_ / / / / /___ ____/ /_ / / / / /_/ /_ / / / /_/ /_ /_/ // __/ /
/_/ /_/ \\____/ /____/ /_/ /_/ \\__,_/ /_/ /_/\\__,_/ _\\__, / \\___//_/
/____/
+ Copyright (C) 2023 MCSManager <mcsmanager-dev@outlook.com>
+ Version ${VERSION}
`);
// Development environment detection before startup
import fs from "fs";
if (!fs.existsSync("public")) {
console.log($t("app.developInfo"));
process.exit(0);
}
// Http server requirements
import Koa from "koa";
import { v4 } from "uuid";
import path from "path";
@ -44,78 +29,11 @@ import open from "open";
import { logger } from "./app/service/log";
import { middleware as protocolMiddleware } from "./app/middleware/protocol";
const BASE_PATH = __dirname;
const app = new Koa();
// Listen for Koa errors
app.on("error", (error) => {
// Block all Koa framework level events
// When Koa is attacked by a short connection flood, it is easy for error messages to swipe the screen, which may indirectly affect the operation of some applications
});
app.use(
koaBody({
multipart: true,
parsedMethods: ["POST", "PUT", "DELETE", "GET"]
})
);
app.keys = [v4()];
app.use(
session(
{
key: v4(),
maxAge: 86400000,
overwrite: true,
httpOnly: true,
signed: true,
rolling: false,
renew: false,
secure: false
},
app
)
);
// Http log and print
app.use(async (ctx, next) => {
const ignoreUrls = ["/api/overview", "/api/files/status"];
for (const iterator of ignoreUrls) {
if (ctx.URL.pathname.includes(iterator)) return await next();
}
logger.info(`[HTTP] ${ctx.method}: ${ctx.URL.href}`);
logger.info(`[HTTP] IP: ${ctx.ip} USER: ${ctx.session.userName} UUID: ${ctx.session.uuid}`);
await next();
});
// Protocol middleware
app.use(protocolMiddleware);
// static file routing
app.use(koaStatic(path.join(BASE_PATH, "public")));
// load all routes
// Routes
import { index } from "./app/index";
// Websocket routing (useless for now)
// import SocketService from "./app/service/socket_service";
index(app);
// Error reporting
process.on("uncaughtException", function (err) {
logger.error(`ERROR (uncaughtException):`, err);
});
// Error reporting
process.on("unhandledRejection", (reason, p) => {
logger.error(`ERROR (unhandledRejection):`, reason, p);
});
// start the HTTP service
function startUp(port: number, host?: string) {
const httpServer = http.createServer(app.callback());
function startUp(koaApp: Koa, port: number, host?: string) {
const httpServer = http.createServer(koaApp.callback());
httpServer.on("error", (err) => {
logger.error($t("app.httpSetupError"));
@ -136,11 +54,11 @@ function startUp(port: number, host?: string) {
logger.info("==================================");
if (os.platform() == "win32") {
open(`http://localhost:${port}/`).then(() => {});
open(`http://localhost:${port}/`).then(() => {
});
}
}
startUp(systemConfig.httpPort, systemConfig.httpIp);
async function processExit() {
try {
@ -154,7 +72,110 @@ async function processExit() {
}
}
["SIGTERM", "SIGINT", "SIGQUIT"].forEach(function (sig) {
(async () => {
// load global configuration file
initSystemConfig();
if (systemConfig.redisUrl.length != 0) {
await RedisStorage.initialize(systemConfig.redisUrl);
Storage.setStorageType(1);
}
initVersionManager();
const VERSION = getVersion();
// show product Logo
console.log(`______ _______________________ ___
___ |/ /_ ____/_ ___/__ |/ /_____ _____________ _______ _____________
__ /|_/ /_ / _____ \\__ /|_/ /_ __ \`/_ __ \\ __ \`/_ __ \`/ _ \\_ ___/
_ / / / / /___ ____/ /_ / / / / /_/ /_ / / / /_/ /_ /_/ // __/ /
/_/ /_/ \\____/ /____/ /_/ /_/ \\__,_/ /_/ /_/\\__,_/ _\\__, / \\___//_/
/____/
+ Copyright (C) 2023 MCSManager <mcsmanager-dev@outlook.com>
+ Version ${VERSION}
`);
// Development environment detection before startup
if (!fs.existsSync(path.join(__dirname, "public"))) {
console.log($t("app.developInfo"));
process.exit(0);
}
await SystemUser.initialize();
await SystemRemoteService.initialize();
const BASE_PATH = __dirname;
const app = new Koa();
// Listen for Koa errors
app.on("error", (error) => {
// Block all Koa framework level events
// When Koa is attacked by a short connection flood, it is easy for error messages to swipe the screen, which may indirectly affect the operation of some applications
});
app.use(
koaBody({
multipart: true,
parsedMethods: ["POST", "PUT", "DELETE", "GET"]
})
);
app.keys = [v4()];
app.use(
session(
{
key: v4(),
maxAge: 86400000,
overwrite: true,
httpOnly: true,
signed: true,
rolling: false,
renew: false,
secure: false
},
app
)
);
// Http log and print
app.use(async (ctx, next) => {
const ignoreUrls = ["/api/overview", "/api/files/status"];
for (const iterator of ignoreUrls) {
if (ctx.URL.pathname.includes(iterator)) return await next();
}
logger.info(`[HTTP] ${ctx.method}: ${ctx.URL.href}`);
logger.info(`[HTTP] IP: ${ctx.ip} USER: ${ctx.session.userName} UUID: ${ctx.session.uuid}`);
await next();
});
// Protocol middleware
app.use(protocolMiddleware);
// static file routing
app.use(koaStatic(path.join(BASE_PATH, "public")));
// Websocket routing (useless for now)
// import SocketService from "./app/service/socket_service";
index(app);
// Error reporting
process.on("uncaughtException", function(err) {
logger.error(`ERROR (uncaughtException):`, err);
});
// Error reporting
process.on("unhandledRejection", (reason, p) => {
logger.error(`ERROR (unhandledRejection):`, reason, p);
});
// start the HTTP service
startUp(app, systemConfig.httpPort, systemConfig.httpIp);
})();
["SIGTERM", "SIGINT", "SIGQUIT"].forEach(function(sig) {
process.on(sig, () => {
logger.warn(`${sig} close process signal detected.`);
processExit();

View File

@ -0,0 +1,98 @@
// Copyright (C) 2022 MCSManager <mcsmanager-dev@outlook.com>
import { $t } from "../../i18n";
import path from "path";
import fs from "fs-extra";
import { IStorage } from "./storage_interface";
interface IClassz {
name: string;
}
class FileStorageSubsystem implements IStorage {
public static readonly STIRAGE_DATA_PATH = path.normalize(path.join(process.cwd(), "data"));
public static readonly STIRAGE_INDEX_PATH = path.normalize(path.join(process.cwd(), "data", "index"));
private checkFileName(name: string) {
const blackList = ["\\", "/", ".."];
for (const ch of blackList) {
if (name.includes(ch)) return false;
}
return true;
}
/**
* Stored in local file based on class definition and identifier
*/
public async store(category: string, uuid: string, object: any) {
const dirPath = path.join(FileStorageSubsystem.STIRAGE_DATA_PATH, category);
if (!fs.existsSync(dirPath)) fs.mkdirsSync(dirPath);
if (!this.checkFileName(uuid)) throw new Error($t("common.uuidIrregular", { uuid: uuid }));
const filePath = path.join(dirPath, `${uuid}.json`);
const data = JSON.stringify(object, null, 4);
fs.writeFileSync(filePath, data, { encoding: "utf-8" });
}
// deep copy of the primitive type with the copy target as the prototype
// target copy target object copy source
protected defineAttr(target: any, object: any): any {
for (const v of Object.keys(target)) {
const objectValue = object[v];
if (objectValue === undefined) continue;
if (objectValue instanceof Array) {
target[v] = objectValue;
continue;
}
if (objectValue instanceof Object && typeof objectValue === "object") {
this.defineAttr(target[v], objectValue);
continue;
}
target[v] = objectValue;
}
return target;
}
/**
* Instantiate an object based on the class definition and identifier
*/
public async load(category: string, classz: any, uuid: string) {
const dirPath = path.join(FileStorageSubsystem.STIRAGE_DATA_PATH, category);
if (!fs.existsSync(dirPath)) fs.mkdirsSync(dirPath);
if (!this.checkFileName(uuid)) throw new Error($t("common.uuidIrregular", { uuid: uuid }));
const filePath = path.join(dirPath, `${uuid}.json`);
if (!fs.existsSync(filePath)) return null;
const data = fs.readFileSync(filePath, { encoding: "utf-8" });
const dataObject = JSON.parse(data);
const target = new classz();
// for (const v of Object. keys(target)) {
// if (dataObject[v] !== undefined) target[v] = dataObject[v];
// }
// deep object copy
return this.defineAttr(target, dataObject);
}
/**
* Return all identifiers related to this class through the class definition
*/
public async list(category: string) {
const dirPath = path.join(FileStorageSubsystem.STIRAGE_DATA_PATH, category);
if (!fs.existsSync(dirPath)) fs.mkdirsSync(dirPath);
const files = fs.readdirSync(dirPath);
const result = new Array<string>();
files.forEach((name) => {
result.push(name.replace(path.extname(name), ""));
});
return result;
}
/**
* Delete an identifier instance of the specified type through the class definition
*/
public async delete(category: string, uuid: string) {
const filePath = path.join(FileStorageSubsystem.STIRAGE_DATA_PATH, category, `${uuid}.json`);
if (!fs.existsSync(filePath)) return;
fs.removeSync(filePath);
}
}
export default new FileStorageSubsystem();

View File

@ -0,0 +1,121 @@
// Copyright (C) 2023 MCSManager <mcsmanager-dev@outlook.com>
import { $t } from "../../i18n";
import { createClient} from "redis";
import { IStorage } from "./storage_interface";
import Storage from "./sys_storage";
import { logger } from "../../service/log";
class RedisStorageSubsystem implements IStorage {
private client = createClient()
private async connect() {
try {
await this.client.connect();
} catch (e) {
logger.error("Error occurred while trying to dial redis\n" + e);
logger.warn("Due to an unrecoverable error, daemon will temporarily store data in files.")
Storage.setStorageType(0)
}
}
/*
Redis commands
*/
private async keys(param: string) {
return await this.client.keys(param);
}
private async set(key: string, value: string) {
await this.client.set(key, value);
}
private async get(key: string) {
return await this.client.get(key);
}
private async del(key: string) {
await this.client.del(key);
}
public async initialize(url: string) {
this.client = createClient({ url: url });
logger.info("Attempting to connect to redis...");
await this.connect();
logger.info("Connected to redis!");
}
// Keep behavior same
private checkFileName(name: string) {
const blackList = ["\\", "/", ".."];
for (const ch of blackList) {
if (name.includes(ch)) return false;
}
return true;
}
/**
* Stored in local file based on class definition and identifier
*/
public async store(category: string, uuid: string, object: any) {
if (!this.checkFileName(uuid)) throw new Error($t("common.uuidIrregular", { uuid: uuid }));
await this.set(category + ":" + uuid, JSON.stringify(object));
}
/**
* Instantiate an object based on the class definition and identifier
*/
public async load(category: string, classz: any, uuid: string) {
if (!this.checkFileName(uuid)) throw new Error($t("common.uuidIrregular", { uuid: uuid }));
let result = await this.get(category + ":" + uuid)
if (result == null) {
return null;
}
const dataObject = JSON.parse(result);
const target = new classz();
return this.defineAttr(target, dataObject);
}
/**
* Return all identifiers related to this class through the class definition
*/
public async list(category: string) {
let result: string[] = [];
let m = Array<string>();
result = await this.keys(category + "*")
if (result != null && result.length != 0) {
for (let i of result) m.push(i.replace(category + ":", ""));
}
return m;
}
/**
* Delete an identifier instance of the specified type through the class definition
*/
public async delete(category: string, uuid: string) {
if (!this.checkFileName(uuid)) throw new Error($t("common.uuidIrregular", { uuid: uuid }));
await this.del(category + ":" + uuid);
}
// deep copy of the primitive type with the copy target as the prototype
// target copy target object copy source
protected defineAttr(target: any, object: any): any {
for (const v of Object.keys(target)) {
const objectValue = object[v];
if (objectValue === undefined) continue;
if (objectValue instanceof Array) {
target[v] = objectValue;
continue;
}
if (objectValue instanceof Object && typeof objectValue === "object") {
this.defineAttr(target[v], objectValue);
continue;
}
target[v] = objectValue;
}
return target;
}
}
export default new RedisStorageSubsystem();

View File

@ -0,0 +1,6 @@
export interface IStorage {
store(category: string, uuid: string, object: any): Promise<void>;
load(category: string, classz: any, uuid: string): Promise<any>;
list(category: string): Promise<Array<string>>;
delete(category: string, uuid: string): Promise<void>;
}

View File

@ -0,0 +1,33 @@
import { IStorage } from "./storage_interface";
import file_storage from "./file_storage";
import redis_storage from "./redis_storage";
class SystemStorage {
private exclusions = ["Config", "SystemConfig"]; // Config is from daemon and SystemConfig is from panel
private storageSystem: IStorage = file_storage;
public setStorageType(type: number) {
switch (type) {
case 0: {
// Using fs-based storage
this.storageSystem = file_storage;
break;
}
case 1: {
// Using redis-based storage
this.storageSystem = redis_storage;
break;
}
default: {
this.storageSystem = file_storage;
break;
}
}
}
public getStorage() {
return this.storageSystem;
}
}
export default new SystemStorage();

View File

@ -34,4 +34,5 @@ export default class SystemConfig {
// Quick installation address for the Chinese market
quickInstallAddr = "https://mcsmanager.oss-cn-guangzhou.aliyuncs.com/quick_install.json";
redisUrl = "";
}

View File

@ -18,7 +18,7 @@ export class DataStructure {
if (!fs.existsSync(this.__filename__)) return;
let data = fs.readFileSync(this.__filename__, "utf-8");
let ele = JSON.parse(data);
for (var key in ele) {
for (const key in ele) {
this[key] = ele[key];
}
}

View File

@ -21,8 +21,7 @@ router.post(
const permission = Number(ctx.request.body.permission);
if (!userSystem.validatePassword(passWord)) throw new Error($t("router.user.invalidPassword"));
if (userSystem.existUserName(userName)) throw new Error($t("router.user.existsUserName"));
const result = register(ctx, userName, passWord, permission);
ctx.body = result;
ctx.body = await register(ctx, userName, passWord, permission);
}
);
@ -31,7 +30,7 @@ router.del("/", permission({ level: 10 }), async (ctx: Koa.ParameterizedContext)
const uuids = ctx.request.body;
try {
for (const iterator of uuids) {
userSystem.deleteInstance(iterator);
await userSystem.deleteInstance(iterator);
}
ctx.body = true;
} catch (error) {

View File

@ -100,7 +100,7 @@ router.post(
async (ctx) => {
const parameter = ctx.request.body;
// do asynchronous registration
const instance = RemoteServiceSubsystem.registerRemoteService({
const instance = await RemoteServiceSubsystem.registerRemoteService({
apiKey: parameter.apiKey,
port: parameter.port,
ip: parameter.ip,

View File

@ -16,7 +16,7 @@ router.put("/", permission({ level: 10 }), async (ctx: Koa.ParameterizedContext)
if (passWord && !userSystem.validatePassword(passWord))
throw new Error($t("router.user.passwordCheck"));
try {
userSystem.edit(uuid, config);
await userSystem.edit(uuid, config);
ctx.body = true;
} catch (error) {
ctx.throw(500, error.message);

View File

@ -1,13 +1,9 @@
// Copyright (C) 2022 MCSManager <mcsmanager-dev@outlook.com>
import Koa from "koa";
import Router from "@koa/router";
import permission from "../../middleware/permission";
import validator from "../../middleware/validator";
import { check, login, logout, register, getUserUuid } from "../../service/passport_service";
import userSystem from "../../service/system_user";
import { ICompleteUser } from "../../entity/entity_interface";
import { getToken, isAjax } from "../../service/passport_service";
import { getUserUuid } from "../../service/passport_service";
import RemoteServiceSubsystem from "../../service/system_remote_service";
import RemoteRequest from "../../service/remote_command";
import { isHaveInstanceByUuid } from "../../service/permission_service";
@ -45,11 +41,10 @@ router.put(
stopCommand: String(config.stopCommand)
};
const remoteService = RemoteServiceSubsystem.getInstance(serviceUuid);
const result = await new RemoteRequest(remoteService).request("instance/update", {
ctx.body = await new RemoteRequest(remoteService).request("instance/update", {
instanceUuid,
config: believableConfig
});
ctx.body = result;
} catch (err) {
ctx.body = err;
}

View File

@ -123,7 +123,7 @@ router.put(
const config = ctx.request.body;
const { passWord, isInit } = config;
if (!userSystem.validatePassword(passWord)) throw new Error($t("router.user.passwordCheck"));
userSystem.edit(userUuid, { passWord, isInit });
await userSystem.edit(userUuid, { passWord, isInit });
ctx.body = true;
}
}
@ -144,11 +144,11 @@ router.put(
if (user) {
if (enable) {
newKey = v4().replace(/-/gim, "");
userSystem.edit(userUuid, {
await userSystem.edit(userUuid, {
apiKey: newKey
});
} else {
userSystem.edit(userUuid, {
await userSystem.edit(userUuid, {
apiKey: ""
});
}

View File

@ -84,7 +84,7 @@ router.all(
if (userSystem.objects.size === 0) {
if (!userSystem.validatePassword(passWord)) throw new Error($t("router.user.passwordCheck"));
logger.info($t("router.login.init", { userName }));
userSystem.create({
await userSystem.create({
userName,
passWord,
permission: 10

View File

@ -59,7 +59,7 @@ export function logout(ctx: Koa.ParameterizedContext): boolean {
return true;
}
export function register(
export async function register(
ctx: Koa.ParameterizedContext,
userName: string,
passWord: string,
@ -72,7 +72,7 @@ export function register(
});
if (f) {
// Next. UUID and other data will be automatically generated.
userSystem.create({
await userSystem.create({
userName,
passWord,
permission

View File

@ -4,22 +4,20 @@ import { logger } from "./log";
import { IRemoteService, RemoteServiceConfig } from "../entity/entity_interface";
import RemoteService from "../entity/remote_service";
import { UniversalRemoteSubsystem } from "./base/urs";
import StorageSubsystem from "../common/system_storage";
import Storage from "../common/storage/sys_storage";
import fs from "fs-extra";
import path from "path";
import { $t } from "../i18n";
import RemoteRequest from "./remote_command";
// The RemoteServiceSubsystem will be one of the most important systems
// main function is to store remote services everywhere
// Scan local services, unified management, remote calls and proxies, etc.
class RemoteServiceSubsystem extends UniversalRemoteSubsystem<RemoteService> {
constructor() {
super();
async initialize() {
// If it is the first startup, it will automatically try to connect to "LocalHost",
// otherwise it will automatically read from the configuration file and connect to all remote services.
StorageSubsystem.list("RemoteServiceConfig").forEach((uuid) => {
const config = StorageSubsystem.load(
for (const uuid of (await Storage.getStorage().list("RemoteServiceConfig"))) {
const config = await Storage.getStorage().load(
"RemoteServiceConfig",
RemoteServiceConfig,
uuid
@ -27,11 +25,11 @@ class RemoteServiceSubsystem extends UniversalRemoteSubsystem<RemoteService> {
const newService = new RemoteService(uuid, config);
this.setInstance(uuid, newService);
newService.connect();
});
}
// If there is no daemon process, check whether there is a daemon process locally
if (this.services.size === 0) {
this.initConnectLocalhost("");
await this.initConnectLocalhost("");
}
logger.info($t("systemRemoteService.nodeCount", { n: this.services.size }));
@ -46,49 +44,49 @@ class RemoteServiceSubsystem extends UniversalRemoteSubsystem<RemoteService> {
// apiKey: "test_key",
// port: 24444
// });
registerRemoteService(config: IRemoteService) {
const instance = this.newInstance(config);
StorageSubsystem.store("RemoteServiceConfig", instance.uuid, instance.config);
async registerRemoteService(config: IRemoteService) {
const instance = await this.newInstance(config);
await Storage.getStorage().store("RemoteServiceConfig", instance.uuid, instance.config);
instance.connect();
return instance;
}
// Delete the specified remote service based on UUID
deleteRemoteService(uuid: string) {
async deleteRemoteService(uuid: string) {
if (this.getInstance(uuid)) {
this.getInstance(uuid).disconnect();
this.deleteInstance(uuid);
StorageSubsystem.delete("RemoteServiceConfig", uuid);
await Storage.getStorage().delete("RemoteServiceConfig", uuid);
}
}
// According to the IRemoteService, New a RemoteService object
// Used to initialize objects.
newInstance(config: IRemoteService) {
async newInstance(config: IRemoteService) {
const instance = new RemoteService(
config.uuid || this.randdomUuid(),
new RemoteServiceConfig()
);
this.setInstance(instance.uuid, instance);
this.edit(instance.uuid, config);
await this.edit(instance.uuid, config);
return instance;
}
// Edit the configuration file of the instance
edit(uuid: string, config: IRemoteService) {
async edit(uuid: string, config: IRemoteService) {
const instance = this.getInstance(uuid);
if (config.remarks) instance.config.remarks = config.remarks;
if (config.ip) instance.config.ip = config.ip;
if (config.port) instance.config.port = config.port;
if (config.apiKey) instance.config.apiKey = config.apiKey;
StorageSubsystem.store("RemoteServiceConfig", instance.uuid, instance.config);
await Storage.getStorage().store("RemoteServiceConfig", instance.uuid, instance.config);
}
// Scannce localhost service
// First use, need to scan the local host
// Note: Every time you execute "initConnectLocalhost",
// it will be managed by the subsystem (regardless of whether the target exists).
async initConnectLocalhost(key?: string) {
async initConnectLocalhost(key?: string): Promise<RemoteService> {
const ip = "localhost";
const localKeyFilePath = path.normalize(
path.join(process.cwd(), "../daemon/data/Config/global.json")
@ -101,10 +99,10 @@ class RemoteServiceSubsystem extends UniversalRemoteSubsystem<RemoteService> {
);
const localKey = localDaemonConfig.key;
const localPort = localDaemonConfig.port;
return this.registerRemoteService({ apiKey: localKey, port: localPort, ip });
return await this.registerRemoteService({ apiKey: localKey, port: localPort, ip });
} else if (key) {
const port = 24444;
return this.registerRemoteService({ apiKey: key, port, ip });
return await this.registerRemoteService({ apiKey: key, port, ip });
}
logger.warn($t("systemRemoteService.error"));

View File

@ -5,7 +5,7 @@ import { v4 } from "uuid";
import { IUserApp, User, UserPassWordType } from "../entity/user";
import { logger } from "./log";
import { IUser } from "../entity/entity_interface";
import StorageSubsystem from "../common/system_storage";
import Storage from "../common/storage/sys_storage";
import { QueryWrapper, LocalFileSource } from "../common/query_wrapper";
import { $t } from "../i18n";
import bcrypt from "bcryptjs";
@ -13,15 +13,15 @@ import bcrypt from "bcryptjs";
class UserSubsystem {
public readonly objects: Map<string, User> = new Map();
constructor() {
StorageSubsystem.list("User").forEach((uuid) => {
const user = StorageSubsystem.load("User", User, uuid) as User;
async initialize() {
for (const uuid of (await Storage.getStorage().list("User"))) {
const user = await Storage.getStorage().load("User", User, uuid) as User;
this.objects.set(uuid, user);
});
}
logger.info($t("systemUser.userCount", { n: this.objects.size }));
}
create(config: IUser): User {
async create(config: IUser): Promise<User> {
const newUuid = v4().replace(/-/gim, "");
// Initialize necessary user data
const instance = new User();
@ -29,13 +29,13 @@ class UserSubsystem {
instance.registerTime = new Date().toLocaleString();
// add to the user system
this.setInstance(newUuid, instance);
this.edit(instance.uuid, config);
await this.edit(instance.uuid, config);
// Persistently save user information
StorageSubsystem.store("User", instance.uuid, instance);
await Storage.getStorage().store("User", instance.uuid, instance);
return instance;
}
edit(uuid: string, config: any) {
async edit(uuid: string, config: any) {
const instance = this.getInstance(uuid);
if (config.userName) instance.userName = config.userName;
if (config.isInit != null) instance.isInit = Boolean(config.isInit);
@ -48,7 +48,7 @@ class UserSubsystem {
}
if (config.instances) this.setUserInstances(uuid, config.instances);
if (config.apiKey != null) instance.apiKey = config.apiKey;
StorageSubsystem.store("User", uuid, instance);
await Storage.getStorage().store("User", uuid, instance);
}
validatePassword(password = "") {
@ -113,10 +113,10 @@ class UserSubsystem {
return this.objects.has(uuid);
}
deleteInstance(uuid: string) {
async deleteInstance(uuid: string) {
if (this.hasInstance(uuid)) {
this.objects.delete(uuid);
StorageSubsystem.delete("User", uuid);
await Storage.getStorage().delete("User", uuid);
}
}
@ -125,4 +125,4 @@ class UserSubsystem {
}
}
export = new UserSubsystem();
export default new UserSubsystem();

View File

@ -6,7 +6,6 @@
"esModuleInterop": true,
"allowJs": false,
"module": "commonjs",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"target": "ES2018",
"noImplicitAny": true,