修改服务启动逻辑

This commit is contained in:
whyour 2025-05-07 09:30:00 +08:00
parent 729b405b0f
commit d871585eee
46 changed files with 802 additions and 564 deletions

View File

@ -1,14 +1,12 @@
UPDATE_PORT=5300 GRPC_PORT=5500
PUBLIC_PORT=5400
CRON_PORT=5500
BACK_PORT=5600 BACK_PORT=5600
PORT=5700 PORT=5700
LOG_LEVEL='debug' LOG_LEVEL='info'
SECRET='whyour' JWT_SECRET=
JWT_EXPIRES_IN=
QINIU_AK='' QINIU_AK=
QINIU_SK='' QINIU_SK=
QINIU_SCOPE='' QINIU_SCOPE=
TEMP=''

View File

@ -16,16 +16,6 @@ export default defineConfig({
favicons: [`https://qn.whyour.cn/favicon.svg`], favicons: [`https://qn.whyour.cn/favicon.svg`],
publicPath: process.env.NODE_ENV === 'production' ? './' : '/', publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
proxy: { proxy: {
[`${baseUrl}api/update`]: {
target: 'http://127.0.0.1:5300/',
changeOrigin: true,
pathRewrite: { [`^${baseUrl}api/update`]: '/api' },
},
[`${baseUrl}api/public`]: {
target: 'http://127.0.0.1:5400/',
changeOrigin: true,
pathRewrite: { [`^${baseUrl}api/public`]: '/api' },
},
[`${baseUrl}api`]: { [`${baseUrl}api`]: {
target: 'http://127.0.0.1:5600/', target: 'http://127.0.0.1:5600/',
changeOrigin: true, changeOrigin: true,

9
back.d.ts vendored
View File

@ -1,9 +0,0 @@
import 'express';
declare global {
namespace Express {
interface Request {
platform: string;
}
}
}

27
back/api/health.ts Normal file
View File

@ -0,0 +1,27 @@
import { Router } from 'express';
import Logger from '../loaders/logger';
import { HealthService } from '../services/health';
import Container from 'typedi';
const route = Router();
export default (app: Router) => {
app.use('/', route);
route.get('/health', async (req, res) => {
try {
const healthService = Container.get(HealthService);
const health = await healthService.check();
res.status(200).send({
code: 200,
data: health,
});
} catch (err: any) {
Logger.error('Health check failed:', err);
res.status(500).send({
code: 500,
message: 'Health check failed',
error: err.message,
});
}
});
};

View File

@ -9,6 +9,8 @@ import open from './open';
import dependence from './dependence'; import dependence from './dependence';
import system from './system'; import system from './system';
import subscription from './subscription'; import subscription from './subscription';
import update from './update';
import health from './health';
export default () => { export default () => {
const app = Router(); const app = Router();
@ -22,6 +24,8 @@ export default () => {
dependence(app); dependence(app);
system(app); system(app);
subscription(app); subscription(app);
update(app);
health(app);
return app; return app;
}; };

51
back/api/update.ts Normal file
View File

@ -0,0 +1,51 @@
import { NextFunction, Request, Response, Router } from 'express';
import Container from 'typedi';
import Logger from '../loaders/logger';
import SystemService from '../services/system';
const route = Router();
export default (app: Router) => {
app.use('/update', route);
route.put(
'/reload',
async (req: Request, res: Response, next: NextFunction) => {
try {
const systemService = Container.get(SystemService);
const result = await systemService.reloadSystem();
res.send(result);
} catch (e) {
Logger.error('🔥 error: %o', e);
return next(e);
}
},
);
route.put(
'/system',
async (req: Request, res: Response, next: NextFunction) => {
try {
const systemService = Container.get(SystemService);
const result = await systemService.reloadSystem('system');
res.send(result);
} catch (e) {
Logger.error('🔥 error: %o', e);
return next(e);
}
},
);
route.put(
'/data',
async (req: Request, res: Response, next: NextFunction) => {
try {
const systemService = Container.get(SystemService);
const result = await systemService.reloadSystem('data');
res.send(result);
} catch (e) {
Logger.error('🔥 error: %o', e);
return next(e);
}
},
);
};

View File

@ -1,30 +1,92 @@
import 'reflect-metadata'; // We need this in order to use @Decorators import 'reflect-metadata';
import config from './config'; import compression from 'compression';
import cors from 'cors';
import express from 'express'; import express from 'express';
import helmet from 'helmet';
import { Container } from 'typedi';
import config from './config';
import Logger from './loaders/logger'; import Logger from './loaders/logger';
import { monitoringMiddleware } from './middlewares/monitoring';
import { GrpcServerService } from './services/grpc';
import { HttpServerService } from './services/http';
import { metricsService } from './services/metrics';
async function startServer() { class Application {
const app = express(); private app: express.Application;
private server: any;
private httpServerService: HttpServerService;
private grpcServerService: GrpcServerService;
private isShuttingDown = false;
await require('./loaders/db').default(); constructor() {
this.app = express();
await require('./loaders/initFile').default(); this.httpServerService = Container.get(HttpServerService);
this.grpcServerService = Container.get(GrpcServerService);
await require('./loaders/app').default({ expressApp: app });
const server = app
.listen(config.port, '0.0.0.0', () => {
Logger.debug(`✌️ 后端服务启动成功!`);
console.debug(`✌️ 后端服务启动成功!`);
process.send?.('ready');
})
.on('error', (err) => {
Logger.error(err);
console.error(err);
process.exit(1);
});
await require('./loaders/server').default({ server });
} }
startServer(); async start() {
try {
await this.initializeDatabase();
this.setupMiddlewares();
await this.initializeServices();
this.setupGracefulShutdown();
process.send?.('ready');
} catch (error) {
Logger.error('Failed to start application:', error);
process.exit(1);
}
}
private async initializeDatabase() {
await require('./loaders/db').default();
}
private setupMiddlewares() {
this.app.use(helmet());
this.app.use(cors(config.cors));
this.app.use(compression());
this.app.use(monitoringMiddleware);
}
private async initializeServices() {
await this.grpcServerService.initialize();
await require('./loaders/app').default({ app: this.app });
this.server = await this.httpServerService.initialize(
this.app,
config.port,
);
await require('./loaders/server').default({ server: this.server });
}
private setupGracefulShutdown() {
const shutdown = async () => {
if (this.isShuttingDown) return;
this.isShuttingDown = true;
Logger.info('Shutting down services...');
try {
await Promise.all([
this.grpcServerService.shutdown(),
this.httpServerService.shutdown(),
]);
process.exit(0);
} catch (error) {
Logger.error('Error during shutdown:', error);
process.exit(1);
}
};
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
}
}
const app = new Application();
app.start().catch((error) => {
Logger.error('Application failed to start:', error);
process.exit(1);
});

View File

@ -2,12 +2,60 @@ import dotenv from 'dotenv';
import path from 'path'; import path from 'path';
import { createRandomString } from './share'; import { createRandomString } from './share';
dotenv.config({
path: path.join(__dirname, '../../.env'),
});
interface Config {
port: number;
grpcPort: number;
nodeEnv: string;
isDevelopment: boolean;
isProduction: boolean;
jwt: {
secret: string;
expiresIn?: string;
};
cors: {
origin: string[];
methods: string[];
};
logs: {
level: string;
};
api: {
prefix: string;
};
}
const config: Config = {
port: parseInt(process.env.BACK_PORT || '5600', 10),
grpcPort: parseInt(process.env.GRPC_PORT || '5500', 10),
nodeEnv: process.env.NODE_ENV || 'development',
isDevelopment: process.env.NODE_ENV === 'development',
isProduction: process.env.NODE_ENV === 'production',
logs: {
level: process.env.LOG_LEVEL || 'silly',
},
api: {
prefix: '/api',
},
jwt: {
secret: process.env.JWT_SECRET || createRandomString(16, 32),
expiresIn: process.env.JWT_EXPIRES_IN,
},
cors: {
origin: process.env.CORS_ORIGIN
? process.env.CORS_ORIGIN.split(',')
: ['*'],
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
},
};
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; process.env.NODE_ENV = process.env.NODE_ENV || 'development';
if (!process.env.QL_DIR) { if (!process.env.QL_DIR) {
// 声明QL_DIR环境变量
let qlHomePath = path.join(__dirname, '../../'); let qlHomePath = path.join(__dirname, '../../');
// 生产环境
if (qlHomePath.endsWith('/static/')) { if (qlHomePath.endsWith('/static/')) {
qlHomePath = path.join(qlHomePath, '../'); qlHomePath = path.join(qlHomePath, '../');
} }
@ -65,17 +113,8 @@ if (envFound.error) {
} }
export default { export default {
port: parseInt(process.env.BACK_PORT as string, 10), ...config,
cronPort: parseInt(process.env.CRON_PORT as string, 10), jwt: config.jwt,
publicPort: parseInt(process.env.PUBLIC_PORT as string, 10),
updatePort: parseInt(process.env.UPDATE_PORT as string, 10),
secret: process.env.SECRET || createRandomString(16, 32),
logs: {
level: process.env.LOG_LEVEL || 'silly',
},
api: {
prefix: '/api',
},
rootPath, rootPath,
tmpPath, tmpPath,
dataPath, dataPath,
@ -118,6 +157,7 @@ export default {
bakPath, bakPath,
apiWhiteList: [ apiWhiteList: [
'/api/user/login', '/api/user/login',
'/api/health',
'/open/auth/token', '/open/auth/token',
'/api/user/two-factor/login', '/api/user/two-factor/login',
'/api/system', '/api/system',

View File

@ -4,7 +4,7 @@ import got from 'got';
import iconv from 'iconv-lite'; import iconv from 'iconv-lite';
import { exec } from 'child_process'; import { exec } from 'child_process';
import FormData from 'form-data'; import FormData from 'form-data';
import psTreeFun from 'pstree.remy'; import psTreeFun from 'ps-tree';
import { promisify } from 'util'; import { promisify } from 'util';
import { load } from 'js-yaml'; import { load } from 'js-yaml';
import config from './index'; import config from './index';
@ -462,11 +462,11 @@ export function parseBody(
export function psTree(pid: number): Promise<number[]> { export function psTree(pid: number): Promise<number[]> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
psTreeFun(pid, (err: any, pids: number[]) => { psTreeFun(pid, (err: any, children) => {
if (err) { if (err) {
reject(err); reject(err);
} }
resolve(pids.filter((x) => !isNaN(x))); resolve(children.map((x) => Number(x.PID)).filter((x) => !isNaN(x)));
}); });
}); });
} }

7
back/index.d.ts vendored
View File

@ -1,7 +0,0 @@
declare namespace Express {
interface Request {
platform: 'desktop' | 'mobile';
}
}
declare module 'pstree.remy';

View File

@ -5,25 +5,24 @@ import initData from './initData';
import { Application } from 'express'; import { Application } from 'express';
import linkDeps from './deps'; import linkDeps from './deps';
import initTask from './initTask'; import initTask from './initTask';
import initFile from './initFile';
export default async ({ expressApp }: { expressApp: Application }) => { export default async ({ app }: { app: Application }) => {
depInjectorLoader(); depInjectorLoader();
Logger.info('✌️ Dependency loaded'); Logger.info('✌️ Dependency loaded');
console.log('✌️ Dependency loaded');
await initData();
Logger.info('✌️ Init data loaded');
console.log('✌️ Init data loaded');
await linkDeps(); await linkDeps();
Logger.info('✌️ Link deps loaded'); Logger.info('✌️ Link deps loaded');
console.log('✌️ Link deps loaded');
initFile();
Logger.info('✌️ Init file loaded');
await initData();
Logger.info('✌️ Init data loaded');
initTask(); initTask();
Logger.info('✌️ Init task loaded'); Logger.info('✌️ Init task loaded');
console.log('✌️ Init task loaded');
expressLoader({ app: expressApp }); expressLoader({ app });
Logger.info('✌️ Express loaded'); Logger.info('✌️ Express loaded');
console.log('✌️ Express loaded');
}; };

View File

@ -57,10 +57,8 @@ export default async () => {
await sequelize.query('alter table Crontabs add column task_after TEXT'); await sequelize.query('alter table Crontabs add column task_after TEXT');
} catch (error) {} } catch (error) {}
console.log('✌️ DB loaded');
Logger.info('✌️ DB loaded'); Logger.info('✌️ DB loaded');
} catch (error) { } catch (error) {
console.error('✌️ DB load failed'); Logger.error('✌️ DB load failed', error);
Logger.error(error);
} }
}; };

View File

@ -7,9 +7,7 @@ import { UnauthorizedError, expressjwt } from 'express-jwt';
import { getPlatform, getToken } from '../config/util'; import { getPlatform, getToken } from '../config/util';
import rewrite from 'express-urlrewrite'; import rewrite from 'express-urlrewrite';
import { errors } from 'celebrate'; import { errors } from 'celebrate';
import { createProxyMiddleware } from 'http-proxy-middleware';
import { serveEnv } from '../config/serverEnv'; import { serveEnv } from '../config/serverEnv';
import Logger from './logger';
import { IKeyvStore, shareStore } from '../shared/store'; import { IKeyvStore, shareStore } from '../shared/store';
export default ({ app }: { app: Application }) => { export default ({ app }: { app: Application }) => {
@ -18,22 +16,12 @@ export default ({ app }: { app: Application }) => {
app.get(`${config.api.prefix}/env.js`, serveEnv); app.get(`${config.api.prefix}/env.js`, serveEnv);
app.use(`${config.api.prefix}/static`, express.static(config.uploadPath)); app.use(`${config.api.prefix}/static`, express.static(config.uploadPath));
app.use(
'/api/public',
createProxyMiddleware({
target: `http://0.0.0.0:${config.publicPort}/api`,
changeOrigin: true,
pathRewrite: { '/api/public': '' },
logger: Logger,
}),
);
app.use(bodyParser.json({ limit: '50mb' })); app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true })); app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
app.use( app.use(
expressjwt({ expressjwt({
secret: config.secret, secret: config.jwt.secret,
algorithms: ['HS384'], algorithms: ['HS384'],
}).unless({ }).unless({
path: [...config.apiWhiteList, /^\/open\//], path: [...config.apiWhiteList, /^\/open\//],
@ -50,7 +38,7 @@ export default ({ app }: { app: Application }) => {
return next(); return next();
}); });
app.use(async (req, res, next) => { app.use(async (req: Request, res, next) => {
const headerToken = getToken(req); const headerToken = getToken(req);
if (req.path.startsWith('/open/')) { if (req.path.startsWith('/open/')) {
const apps = await shareStore.getApps(); const apps = await shareStore.getApps();

View File

@ -122,5 +122,4 @@ export default async () => {
} }
Logger.info('✌️ Init file down'); Logger.info('✌️ Init file down');
console.log('✌️ Init file down');
}; };

View File

@ -4,35 +4,60 @@ import config from '../config';
import path from 'path'; import path from 'path';
const levelMap: Record<string, string> = { const levelMap: Record<string, string> = {
info: '\ue6f5', info: '', // info图标
warn: '\ue880', warn: '⚠️', // 警告图标
error: '\ue602', error: '❌', // 错误图标
debug: '\ue67f' debug: '🐛', // debug调试图标
}
const customFormat = winston.format.combine(
winston.format.splat(),
winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
winston.format.align(),
winston.format.printf((i) => `[${levelMap[i.level]}${i.level}] [${[i.timestamp]}]: ${i.message}`),
);
const defaultOptions = {
format: customFormat,
datePattern: "YYYY-MM-DD",
maxSize: "20m",
maxFiles: "7d",
}; };
const LoggerInstance = winston.createLogger({ const baseFormat = [
level: config.logs.level, winston.format.splat(),
levels: winston.config.npm.levels, winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
transports: [ winston.format.align(),
new winston.transports.DailyRotateFile({ ];
const consoleFormat = winston.format.combine(
winston.format.colorize({ level: true }),
...baseFormat,
winston.format.printf((info) => {
return `[${info.level} ${info.timestamp}]:${info.message}`;
}),
);
const plainFormat = winston.format.combine(
winston.format.uncolorize(),
...baseFormat,
winston.format.printf((info) => {
return `[${levelMap[info.level] || ''}${info.level} ${info.timestamp}]:${
info.message
}`;
}),
);
const consoleTransport = new winston.transports.Console({
format: consoleFormat,
level: 'debug',
});
const fileTransport = new winston.transports.DailyRotateFile({
filename: path.join(config.systemLogPath, '%DATE%.log'), filename: path.join(config.systemLogPath, '%DATE%.log'),
...defaultOptions, datePattern: 'YYYY-MM-DD',
}) maxSize: '20m',
], maxFiles: '7d',
format: plainFormat,
level: config.logs.level || 'info',
});
const LoggerInstance = winston.createLogger({
level: 'debug',
levels: winston.config.npm.levels,
transports: [consoleTransport, fileTransport],
exceptionHandlers: [consoleTransport, fileTransport],
rejectionHandlers: [consoleTransport, fileTransport],
});
LoggerInstance.on('error', (error) => {
console.error('Logger error:', error);
}); });
export default LoggerInstance; export default LoggerInstance;

View File

@ -1,106 +0,0 @@
import bodyParser from 'body-parser';
import { errors } from 'celebrate';
import cors from 'cors';
import { Application, NextFunction, Request, Response } from 'express';
import { expressjwt } from 'express-jwt';
import Container from 'typedi';
import config from '../config';
import SystemService from '../services/system';
import Logger from './logger';
export default ({ app }: { app: Application }) => {
app.set('trust proxy', 'loopback');
app.use(cors());
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
app.use(
expressjwt({
secret: config.secret,
algorithms: ['HS384'],
}),
);
app.put(
'/api/reload',
async (req: Request, res: Response, next: NextFunction) => {
try {
const systemService = Container.get(SystemService);
const result = await systemService.reloadSystem();
res.send(result);
} catch (e) {
Logger.error('🔥 error: %o', e);
return next(e);
}
},
);
app.put(
'/api/system',
async (req: Request, res: Response, next: NextFunction) => {
try {
const systemService = Container.get(SystemService);
const result = await systemService.reloadSystem('system');
res.send(result);
} catch (e) {
Logger.error('🔥 error: %o', e);
return next(e);
}
},
);
app.put(
'/api/data',
async (req: Request, res: Response, next: NextFunction) => {
try {
const systemService = Container.get(SystemService);
const result = await systemService.reloadSystem('data');
res.send(result);
} catch (e) {
Logger.error('🔥 error: %o', e);
return next(e);
}
},
);
app.use((req, res, next) => {
const err: any = new Error('Not Found');
err['status'] = 404;
next(err);
});
app.use(errors());
app.use(
(
err: Error & { status: number },
req: Request,
res: Response,
next: NextFunction,
) => {
if (err.name === 'UnauthorizedError') {
return res
.status(err.status)
.send({ code: 401, message: err.message })
.end();
}
return next(err);
},
);
app.use(
(
err: Error & { status: number },
req: Request,
res: Response,
next: NextFunction,
) => {
res.status(err.status || 500);
res.json({
code: err.status || 500,
message: err.message,
});
},
);
};

View File

@ -0,0 +1,80 @@
import { Request, Response, NextFunction } from 'express';
import Logger from '../loaders/logger';
import { performance } from 'perf_hooks';
import { metricsService } from '../services/metrics';
interface RequestMetrics {
method: string;
path: string;
duration: number;
statusCode: number;
timestamp: number;
platform?: string;
}
const requestMetrics: RequestMetrics[] = [];
export const monitoringMiddleware = (
req: Request,
res: Response,
next: NextFunction,
) => {
const start = performance.now();
const originalEnd = res.end;
res.end = function (chunk?: any, encoding?: any, cb?: any) {
const duration = performance.now() - start;
const metric: RequestMetrics = {
method: req.method,
path: req.path,
duration,
statusCode: res.statusCode,
timestamp: Date.now(),
platform: req.platform,
};
requestMetrics.push(metric);
metricsService.record('http_request', duration, {
method: req.method,
path: req.path,
statusCode: res.statusCode.toString(),
...(req.platform && { platform: req.platform }),
});
if (requestMetrics.length > 1000) {
requestMetrics.shift();
}
if (duration > 1000) {
Logger.warn(
`Slow request detected: ${req.method} ${
req.path
} took ${duration.toFixed(2)}ms`,
);
}
return originalEnd.call(this, chunk, encoding, cb);
};
next();
};
export const getMetrics = () => {
return {
totalRequests: requestMetrics.length,
averageDuration:
requestMetrics.reduce((acc, curr) => acc + curr.duration, 0) /
requestMetrics.length,
requestsByMethod: requestMetrics.reduce((acc, curr) => {
acc[curr.method] = (acc[curr.method] || 0) + 1;
return acc;
}, {} as Record<string, number>),
requestsByPlatform: requestMetrics.reduce((acc, curr) => {
if (curr.platform) {
acc[curr.platform] = (acc[curr.platform] || 0) + 1;
}
return acc;
}, {} as Record<string, number>),
recentRequests: requestMetrics.slice(-10),
};
};

View File

@ -1,35 +0,0 @@
import express from 'express';
import Logger from './loaders/logger';
import config from './config';
import { HealthClient } from './protos/health';
import { credentials } from '@grpc/grpc-js';
const app = express();
const client = new HealthClient(
`0.0.0.0:${config.cronPort}`,
credentials.createInsecure(),
{ 'grpc.enable_http_proxy': 0 },
);
app.get('/api/health', (req, res) => {
client.check({ service: 'cron' }, (err, response) => {
if (err) {
return res.status(200).send({ code: 500, error: err });
}
return res.status(200).send({ code: 200, data: response });
});
});
app
.listen(config.publicPort, '0.0.0.0', async () => {
await require('./loaders/db').default();
Logger.debug(`✌️ 公共服务启动成功!`);
console.debug(`✌️ 公共服务启动成功!`);
process.send?.('ready');
})
.on('error', (err) => {
Logger.error(err);
console.error(err);
process.exit(1);
});

View File

@ -10,7 +10,7 @@ import config from '../config';
class Client { class Client {
private client = new CronClient( private client = new CronClient(
`0.0.0.0:${config.cronPort}`, `0.0.0.0:${config.grpcPort}`,
credentials.createInsecure(), credentials.createInsecure(),
{ 'grpc.enable_http_proxy': 0 }, { 'grpc.enable_http_proxy': 0 },
); );

View File

@ -1,27 +0,0 @@
import { Server, ServerCredentials } from '@grpc/grpc-js';
import { CronService } from '../protos/cron';
import { addCron } from './addCron';
import { delCron } from './delCron';
import { HealthService } from '../protos/health';
import { check } from './health';
import config from '../config';
import Logger from '../loaders/logger';
import { ApiService } from '../protos/api';
import * as Api from './api';
const server = new Server({ 'grpc.enable_http_proxy': 0 });
server.addService(HealthService, { check });
server.addService(CronService, { addCron, delCron });
server.addService(ApiService, Api);
server.bindAsync(
`0.0.0.0:${config.cronPort}`,
ServerCredentials.createInsecure(),
(err, port) => {
if (err) {
throw err;
}
Logger.debug(`✌️ 定时服务启动成功!`);
console.debug(`✌️ 定时服务启动成功!`);
process.send?.('ready');
},
);

64
back/services/grpc.ts Normal file
View File

@ -0,0 +1,64 @@
import { Server, ServerCredentials } from '@grpc/grpc-js';
import { CronService } from '../protos/cron';
import { HealthService } from '../protos/health';
import { ApiService } from '../protos/api';
import { addCron } from '../schedule/addCron';
import { delCron } from '../schedule/delCron';
import { check } from '../schedule/health';
import * as Api from '../schedule/api';
import Logger from '../loaders/logger';
import { promisify } from 'util';
import config from '../config';
import { metricsService } from './metrics';
import { Service } from 'typedi';
@Service()
export class GrpcServerService {
private server: Server = new Server({ 'grpc.enable_http_proxy': 0 });
async initialize() {
try {
this.server.addService(HealthService, { check });
this.server.addService(CronService, { addCron, delCron });
this.server.addService(ApiService, Api);
const grpcPort = config.grpcPort;
const bindAsync = promisify(this.server.bindAsync).bind(this.server);
await bindAsync(
`0.0.0.0:${grpcPort}`,
ServerCredentials.createInsecure(),
);
Logger.debug(`✌️ gRPC service started successfully`);
metricsService.record('grpc_service_start', 1, {
port: grpcPort.toString(),
});
return grpcPort;
} catch (err) {
Logger.error('Failed to start gRPC service:', err);
throw err;
}
}
async shutdown() {
try {
if (this.server) {
await new Promise((resolve) => {
this.server.tryShutdown(() => {
Logger.debug('gRPC service stopped');
metricsService.record('grpc_service_stop', 1);
resolve(null);
});
});
}
} catch (err) {
Logger.error('Error while shutting down gRPC service:', err);
throw err;
}
}
getServer() {
return this.server;
}
}

72
back/services/health.ts Normal file
View File

@ -0,0 +1,72 @@
import { Service } from 'typedi';
import Logger from '../loaders/logger';
import { GrpcServerService } from './grpc';
import { HttpServerService } from './http';
interface HealthStatus {
status: 'ok' | 'error';
services: {
http: boolean;
grpc: boolean;
};
metrics: {
uptime: number;
memory: {
used: number;
total: number;
};
};
}
@Service()
export class HealthService {
private startTime = Date.now();
constructor(
private grpcServerService: GrpcServerService,
private httpServerService: HttpServerService,
) {}
async check(): Promise<HealthStatus> {
const status: HealthStatus = {
status: 'ok',
services: {
http: true,
grpc: true,
},
metrics: {
uptime: Math.floor((Date.now() - this.startTime) / 1000),
memory: {
used: process.memoryUsage().heapUsed,
total: process.memoryUsage().heapTotal,
},
},
};
try {
const httpServer = this.httpServerService.getServer();
if (!httpServer) {
status.services.http = false;
status.status = 'error';
}
} catch (err) {
status.services.http = false;
status.status = 'error';
Logger.error('HTTP server check failed:', err);
}
try {
const grpcServer = this.grpcServerService.getServer();
if (!grpcServer) {
status.services.grpc = false;
status.status = 'error';
}
} catch (err) {
status.services.grpc = false;
status.status = 'error';
Logger.error('gRPC server check failed:', err);
}
return status;
}
}

53
back/services/http.ts Normal file
View File

@ -0,0 +1,53 @@
import express from 'express';
import Logger from '../loaders/logger';
import { metricsService } from './metrics';
import { Service } from 'typedi';
import { Server } from 'http';
@Service()
export class HttpServerService {
private server?: Server = undefined;
async initialize(expressApp: express.Application, port: number) {
try {
return new Promise((resolve, reject) => {
this.server = expressApp.listen(port, '0.0.0.0', () => {
Logger.debug(`✌️ HTTP service started successfully`);
metricsService.record('http_service_start', 1, {
port: port.toString(),
});
resolve(this.server);
});
this.server.on('error', (err: Error) => {
Logger.error('Failed to start HTTP service:', err);
reject(err);
});
});
} catch (err) {
Logger.error('Failed to start HTTP service:', err);
throw err;
}
}
async shutdown() {
try {
if (this.server) {
await new Promise((resolve) => {
this.server?.close(() => {
Logger.debug('HTTP service stopped');
metricsService.record('http_service_stop', 1);
resolve(null);
});
});
}
} catch (err) {
Logger.error('Error while shutting down HTTP service:', err);
throw err;
}
}
getServer() {
return this.server;
}
}

92
back/services/metrics.ts Normal file
View File

@ -0,0 +1,92 @@
import { performance } from 'perf_hooks';
import Logger from '../loaders/logger';
interface Metric {
name: string;
value: number;
timestamp: number;
tags?: Record<string, string>;
}
class MetricsService {
private metrics: Metric[] = [];
private static instance: MetricsService;
private constructor() {
// 定期清理旧数据
setInterval(() => {
const oneHourAgo = Date.now() - 3600000;
this.metrics = this.metrics.filter(m => m.timestamp > oneHourAgo);
}, 60000);
}
static getInstance(): MetricsService {
if (!MetricsService.instance) {
MetricsService.instance = new MetricsService();
}
return MetricsService.instance;
}
record(name: string, value: number, tags?: Record<string, string>) {
this.metrics.push({
name,
value,
timestamp: Date.now(),
tags,
});
}
measure(name: string, fn: () => void, tags?: Record<string, string>) {
const start = performance.now();
try {
fn();
} finally {
const duration = performance.now() - start;
this.record(name, duration, tags);
}
}
async measureAsync(name: string, fn: () => Promise<void>, tags?: Record<string, string>) {
const start = performance.now();
try {
await fn();
} finally {
const duration = performance.now() - start;
this.record(name, duration, tags);
}
}
getMetrics(name?: string, tags?: Record<string, string>) {
let filtered = this.metrics;
if (name) {
filtered = filtered.filter(m => m.name === name);
}
if (tags) {
filtered = filtered.filter(m => {
if (!m.tags) return false;
return Object.entries(tags).every(([key, value]) => m.tags![key] === value);
});
}
return {
count: filtered.length,
average: filtered.reduce((acc, curr) => acc + curr.value, 0) / filtered.length,
min: Math.min(...filtered.map(m => m.value)),
max: Math.max(...filtered.map(m => m.value)),
metrics: filtered,
};
}
report() {
const report = {
timestamp: Date.now(),
metrics: this.getMetrics(),
};
Logger.info('性能指标报告:', report);
return report;
}
}
export const metricsService = MetricsService.getInstance();

View File

@ -357,8 +357,15 @@ export default class SystemService {
public async reloadSystem(target?: 'system' | 'data') { public async reloadSystem(target?: 'system' | 'data') {
const cmd = `real_time=true ql reload ${target || ''}`; const cmd = `real_time=true ql reload ${target || ''}`;
const cp = spawn(cmd, { shell: '/bin/bash' }); const cp = spawn(cmd, {
shell: '/bin/bash',
detached: true,
stdio: 'ignore',
});
cp.unref(); cp.unref();
setTimeout(() => {
process.exit(0);
});
return { code: 200 }; return { code: 200 };
} }

View File

@ -93,9 +93,9 @@ export default class UserService {
} }
if (username === cUsername && password === cPassword) { if (username === cUsername && password === cPassword) {
const data = createRandomString(50, 100); const data = createRandomString(50, 100);
const expiration = twoFactorActivated ? 60 : 20; const expiration = twoFactorActivated ? '60d' : '20d';
let token = jwt.sign({ data }, config.secret as any, { let token = jwt.sign({ data }, config.jwt.secret, {
expiresIn: 60 * 60 * 24 * expiration, expiresIn: config.jwt.expiresIn || expiration,
algorithm: 'HS384', algorithm: 'HS384',
}); });
@ -131,7 +131,14 @@ export default class UserService {
this.getLoginLog(); this.getLoginLog();
return { return {
code: 200, code: 200,
data: { token, lastip, lastaddr, lastlogon, retries, platform }, data: {
token,
lastip,
lastaddr,
lastlogon,
retries,
platform,
},
}; };
} else { } else {
await this.updateAuthInfo(content, { await this.updateAuthInfo(content, {

View File

@ -37,7 +37,7 @@ class TaskLimit {
concurrency: Math.max(os.cpus().length, 4), concurrency: Math.max(os.cpus().length, 4),
}); });
private client = new ApiClient( private client = new ApiClient(
`0.0.0.0:${config.cronPort}`, `0.0.0.0:${config.grpcPort}`,
credentials.createInsecure(), credentials.createInsecure(),
{ 'grpc.enable_http_proxy': 0 }, { 'grpc.enable_http_proxy': 0 },
); );

View File

@ -2,7 +2,6 @@ import 'reflect-metadata';
import OpenService from './services/open'; import OpenService from './services/open';
import { Container } from 'typedi'; import { Container } from 'typedi';
import LoggerInstance from './loaders/logger'; import LoggerInstance from './loaders/logger';
import fs from 'fs';
import config from './config'; import config from './config';
import path from 'path'; import path from 'path';
import os from 'os'; import os from 'os';

View File

@ -2,7 +2,11 @@
"compilerOptions": { "compilerOptions": {
"target": "es2017", "target": "es2017",
"lib": ["ESNext"], "lib": ["ESNext"],
"typeRoots": ["./node_modules/celebrate/lib", "./node_modules/@types"], "typeRoots": [
"./types",
"../node_modules/celebrate/lib",
"../node_modules/@types"
],
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"emitDecoratorMetadata": true, "emitDecoratorMetadata": true,
@ -13,12 +17,11 @@
"module": "commonjs", "module": "commonjs",
"pretty": true, "pretty": true,
"sourceMap": true, "sourceMap": true,
"outDir": "./static/build", "outDir": "../static/build",
"allowJs": true, "allowJs": true,
"noEmit": false, "noEmit": false,
"esModuleInterop": true "esModuleInterop": true
}, },
"include": ["./back/**/*", "./back.d.ts"], "include": ["./**/*"],
"exclude": ["node_modules"], "exclude": ["node_modules"]
"files": ["./back/index.d.ts"]
} }

11
back/types/express.d.ts vendored Normal file
View File

@ -0,0 +1,11 @@
/// <reference types="express" />
export {};
declare global {
namespace Express {
interface Request {
platform: 'desktop' | 'mobile';
}
}
}

View File

@ -1,27 +0,0 @@
import 'reflect-metadata'; // We need this in order to use @Decorators
import config from './config';
import express from 'express';
import depInjectorLoader from './loaders/depInjector';
import Logger from './loaders/logger';
async function startServer() {
const app = express();
depInjectorLoader();
await require('./loaders/update').default({ app });
app
.listen(config.updatePort, '0.0.0.0', () => {
Logger.debug(`✌️ 更新服务启动成功!`);
console.debug(`✌️ 更新服务启动成功!`);
process.send?.('ready');
})
.on('error', (err) => {
Logger.error(err);
console.error(err);
process.exit(1);
});
}
startServer();

View File

@ -86,6 +86,6 @@ COPY --from=builder /tmp/build/node_modules/. /ql/node_modules/
WORKDIR ${QL_DIR} WORKDIR ${QL_DIR}
HEALTHCHECK --interval=5s --timeout=2s --retries=20 \ HEALTHCHECK --interval=5s --timeout=2s --retries=20 \
CMD curl -sf --noproxy '*' http://127.0.0.1:5400/api/health || exit 1 CMD curl -sf --noproxy '*' http://127.0.0.1:5600/api/health || exit 1
ENTRYPOINT ["./docker/docker-entrypoint.sh"] ENTRYPOINT ["./docker/docker-entrypoint.sh"]

View File

@ -86,6 +86,6 @@ COPY --from=builder /tmp/build/node_modules/. /ql/node_modules/
WORKDIR ${QL_DIR} WORKDIR ${QL_DIR}
HEALTHCHECK --interval=5s --timeout=2s --retries=20 \ HEALTHCHECK --interval=5s --timeout=2s --retries=20 \
CMD curl -sf --noproxy '*' http://127.0.0.1:5400/api/health || exit 1 CMD curl -sf --noproxy '*' http://127.0.0.1:5600/api/health || exit 1
ENTRYPOINT ["./docker/docker-entrypoint.sh"] ENTRYPOINT ["./docker/docker-entrypoint.sh"]

View File

@ -21,7 +21,6 @@ nginx -s reload 2>/dev/null || nginx -c /etc/nginx/nginx.conf
echo -e "nginx启动成功...\n" echo -e "nginx启动成功...\n"
echo -e "======================4. 启动pm2服务========================\n" echo -e "======================4. 启动pm2服务========================\n"
reload_update
reload_pm2 reload_pm2
if [[ $AutoStartBot == true ]]; then if [[ $AutoStartBot == true ]]; then

View File

@ -2,14 +2,6 @@ upstream baseApi {
server 0.0.0.0:5600; server 0.0.0.0:5600;
} }
upstream publicApi {
server 0.0.0.0:5400;
}
upstream updateApi {
server 0.0.0.0:5300;
}
map $http_upgrade $connection_upgrade { map $http_upgrade $connection_upgrade {
default keep-alive; default keep-alive;
'websocket' upgrade; 'websocket' upgrade;
@ -20,30 +12,6 @@ server {
IPV6_CONFIG IPV6_CONFIG
ssl_session_timeout 5m; ssl_session_timeout 5m;
location QL_BASE_URLapi/update/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://updateApi/api/;
proxy_buffering off;
proxy_redirect default;
proxy_connect_timeout 1800;
proxy_send_timeout 1800;
proxy_read_timeout 1800;
}
location QL_BASE_URLapi/public/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://publicApi/api/;
proxy_buffering off;
proxy_redirect default;
proxy_connect_timeout 1800;
proxy_send_timeout 1800;
proxy_read_timeout 1800;
}
location QL_BASE_URLapi/ { location QL_BASE_URLapi/ {
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;

View File

@ -1,14 +1,14 @@
module.exports = { module.exports = {
apps: [ apps: [
{ {
name: 'schedule', name: 'qinglong',
max_restarts: 10, max_restarts: 5,
kill_timeout: 15000, kill_timeout: 1000,
wait_ready: true, wait_ready: true,
listen_timeout: 10000, listen_timeout: 5000,
source_map_support: true, source_map_support: true,
time: true, time: true,
script: 'static/build/schedule/index.js', script: 'static/build/app.js',
env: { env: {
http_proxy: '', http_proxy: '',
https_proxy: '', https_proxy: '',
@ -18,25 +18,5 @@ module.exports = {
ALL_PROXY: '', ALL_PROXY: '',
}, },
}, },
{
name: 'public',
max_restarts: 10,
kill_timeout: 15000,
wait_ready: true,
listen_timeout: 10000,
source_map_support: true,
time: true,
script: 'static/build/public.js',
},
{
name: 'panel',
max_restarts: 10,
kill_timeout: 15000,
wait_ready: true,
listen_timeout: 10000,
source_map_support: true,
time: true,
script: 'static/build/app.js',
},
], ],
}; };

View File

@ -1,5 +1,5 @@
{ {
"watch": ["back", ".env"], "watch": ["back", ".env"],
"ext": "js,ts,json", "ext": "js,ts,json",
"exec": "ts-node -P tsconfig.back.json ./back/app.ts" "exec": "ts-node -P ./back/tsconfig.json ./back/app.ts"
} }

View File

@ -1,13 +0,0 @@
module.exports = {
apps: [
{
name: 'update',
max_restarts: 10,
kill_timeout: 15000,
wait_ready: true,
listen_timeout: 10000,
time: true,
script: 'static/build/update.js',
},
],
};

View File

@ -2,17 +2,11 @@
"private": true, "private": true,
"scripts": { "scripts": {
"start": "concurrently -n w: npm:start:*", "start": "concurrently -n w: npm:start:*",
"start:update": "ts-node -P tsconfig.back.json ./back/update.ts",
"start:public": "ts-node -P tsconfig.back.json ./back/public.ts",
"start:rpc": "ts-node -P tsconfig.back.json ./back/schedule/index.ts",
"start:back": "nodemon", "start:back": "nodemon",
"start:front": "max dev", "start:front": "max dev",
"build:front": "max build", "build:front": "max build",
"build:back": "tsc -p tsconfig.back.json", "build:back": "tsc -p back/tsconfig.json",
"panel": "npm run build:back && node static/build/app.js", "panel": "npm run build:back && node static/build/app.js",
"schedule": "npm run build:back && node static/build/schedule/index.js",
"public": "npm run build:back && node static/build/public.js",
"update": "npm run build:back && node static/build/update.js",
"gen:proto": "protoc --experimental_allow_proto3_optional --plugin=./node_modules/.bin/protoc-gen-ts_proto ./back/protos/*.proto --ts_proto_out=./ --ts_proto_opt=outputServices=grpc-js,env=node,esModuleInterop=true,snakeToCamel=false", "gen:proto": "protoc --experimental_allow_proto3_optional --plugin=./node_modules/.bin/protoc-gen-ts_proto ./back/protos/*.proto --ts_proto_out=./ --ts_proto_opt=outputServices=grpc-js,env=node,esModuleInterop=true,snakeToCamel=false",
"gen:api": "python3 -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./back/protos/api.proto", "gen:api": "python3 -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./back/protos/api.proto",
"prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'", "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'",
@ -89,7 +83,7 @@
"nodemailer": "^6.9.16", "nodemailer": "^6.9.16",
"p-queue-cjs": "7.3.4", "p-queue-cjs": "7.3.4",
"@bufbuild/protobuf": "^2.2.3", "@bufbuild/protobuf": "^2.2.3",
"pstree.remy": "^1.1.8", "ps-tree": "^1.2.0",
"reflect-metadata": "^0.2.2", "reflect-metadata": "^0.2.2",
"sequelize": "^6.37.5", "sequelize": "^6.37.5",
"serve-handler": "^6.1.6", "serve-handler": "^6.1.6",
@ -104,7 +98,9 @@
"ip2region": "2.3.0", "ip2region": "2.3.0",
"keyv": "^5.2.3", "keyv": "^5.2.3",
"@keyv/sqlite": "^4.0.1", "@keyv/sqlite": "^4.0.1",
"proper-lockfile": "^4.1.2" "proper-lockfile": "^4.1.2",
"compression": "^1.7.4",
"helmet": "^6.0.1"
}, },
"devDependencies": { "devDependencies": {
"moment": "2.30.1", "moment": "2.30.1",
@ -138,6 +134,7 @@
"@types/uuid": "^8.3.4", "@types/uuid": "^8.3.4",
"@types/request-ip": "0.0.41", "@types/request-ip": "0.0.41",
"@types/proper-lockfile": "^4.1.4", "@types/proper-lockfile": "^4.1.4",
"@types/ps-tree": "^1.1.6",
"@uiw/codemirror-extensions-langs": "^4.21.9", "@uiw/codemirror-extensions-langs": "^4.21.9",
"@uiw/react-codemirror": "^4.21.9", "@uiw/react-codemirror": "^4.21.9",
"@umijs/max": "^4.4.4", "@umijs/max": "^4.4.4",
@ -176,6 +173,7 @@
"typescript": "5.2.2", "typescript": "5.2.2",
"vh-check": "^2.0.5", "vh-check": "^2.0.5",
"virtualizedtableforantd4": "1.3.0", "virtualizedtableforantd4": "1.3.0",
"yorkie": "^2.0.0" "@types/compression": "^1.7.2",
"@types/helmet": "^4.0.0"
} }
} }

View File

@ -32,6 +32,9 @@ dependencies:
chokidar: chokidar:
specifier: ^4.0.1 specifier: ^4.0.1
version: 4.0.1 version: 4.0.1
compression:
specifier: ^1.7.4
version: 1.7.5
cors: cors:
specifier: ^2.8.5 specifier: ^2.8.5
version: 2.8.5 version: 2.8.5
@ -65,6 +68,9 @@ dependencies:
got: got:
specifier: ^11.8.2 specifier: ^11.8.2
version: 11.8.6 version: 11.8.6
helmet:
specifier: ^6.0.1
version: 6.2.0
hpagent: hpagent:
specifier: ^1.2.0 specifier: ^1.2.0
version: 1.2.0 version: 1.2.0
@ -104,9 +110,9 @@ dependencies:
proper-lockfile: proper-lockfile:
specifier: ^4.1.2 specifier: ^4.1.2
version: 4.1.2 version: 4.1.2
pstree.remy: ps-tree:
specifier: ^1.1.8 specifier: ^1.2.0
version: 1.1.8 version: 1.2.0
reflect-metadata: reflect-metadata:
specifier: ^0.2.2 specifier: ^0.2.2
version: 0.2.2 version: 0.2.2
@ -163,6 +169,9 @@ devDependencies:
'@types/body-parser': '@types/body-parser':
specifier: ^1.19.2 specifier: ^1.19.2
version: 1.19.5 version: 1.19.5
'@types/compression':
specifier: ^1.7.2
version: 1.7.5
'@types/cors': '@types/cors':
specifier: ^2.8.12 specifier: ^2.8.12
version: 2.8.17 version: 2.8.17
@ -178,6 +187,9 @@ devDependencies:
'@types/file-saver': '@types/file-saver':
specifier: 2.0.2 specifier: 2.0.2
version: 2.0.2 version: 2.0.2
'@types/helmet':
specifier: ^4.0.0
version: 4.0.0
'@types/js-yaml': '@types/js-yaml':
specifier: ^4.0.5 specifier: ^4.0.5
version: 4.0.9 version: 4.0.9
@ -202,6 +214,9 @@ devDependencies:
'@types/proper-lockfile': '@types/proper-lockfile':
specifier: ^4.1.4 specifier: ^4.1.4
version: 4.1.4 version: 4.1.4
'@types/ps-tree':
specifier: ^1.1.6
version: 1.1.6
'@types/qrcode.react': '@types/qrcode.react':
specifier: ^1.0.2 specifier: ^1.0.2
version: 1.0.5 version: 1.0.5
@ -349,9 +364,6 @@ devDependencies:
virtualizedtableforantd4: virtualizedtableforantd4:
specifier: 1.3.0 specifier: 1.3.0
version: 1.3.0(antd@4.24.16)(react-dom@18.3.1)(react@18.3.1) version: 1.3.0(antd@4.24.16)(react-dom@18.3.1)(react@18.3.1)
yorkie:
specifier: ^2.0.0
version: 2.0.0
packages: packages:
@ -3808,6 +3820,12 @@ packages:
'@types/responselike': 1.0.3 '@types/responselike': 1.0.3
dev: false dev: false
/@types/compression@1.7.5:
resolution: {integrity: sha512-AAQvK5pxMpaT+nDvhHrsBhLSYG5yQdtkaJE1WYieSNY2mVFKAgmU4ks65rkZD5oqnGCFLyQpUr1CqI4DmUMyDg==}
dependencies:
'@types/express': 4.17.21
dev: true
/@types/connect@3.4.38: /@types/connect@3.4.38:
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
dependencies: dependencies:
@ -3878,6 +3896,13 @@ packages:
resolution: {integrity: sha512-oOMFT8vmCTFncsF1engrs04jatz8/Anwx3De9uxnOK4chgSEgWBvFtpSoJo8u3784JNO+ql5tzRR6phHoRnscQ==} resolution: {integrity: sha512-oOMFT8vmCTFncsF1engrs04jatz8/Anwx3De9uxnOK4chgSEgWBvFtpSoJo8u3784JNO+ql5tzRR6phHoRnscQ==}
dev: true dev: true
/@types/helmet@4.0.0:
resolution: {integrity: sha512-ONIn/nSNQA57yRge3oaMQESef/6QhoeX7llWeDli0UZIfz8TQMkfNPTXA8VnnyeA1WUjG2pGqdjEIueYonMdfQ==}
deprecated: This is a stub types definition. helmet provides its own type definitions, so you do not need this installed.
dependencies:
helmet: 6.2.0
dev: true
/@types/hoist-non-react-statics@3.3.5: /@types/hoist-non-react-statics@3.3.5:
resolution: {integrity: sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==} resolution: {integrity: sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==}
dependencies: dependencies:
@ -4011,6 +4036,10 @@ packages:
'@types/retry': 0.12.5 '@types/retry': 0.12.5
dev: true dev: true
/@types/ps-tree@1.1.6:
resolution: {integrity: sha512-PtrlVaOaI44/3pl3cvnlK+GxOM3re2526TJvPvh7W+keHIXdV4TE0ylpPBAcvFQCbGitaTXwL9u+RF7qtVeazQ==}
dev: true
/@types/qrcode.react@1.0.5: /@types/qrcode.react@1.0.5:
resolution: {integrity: sha512-BghPtnlwvrvq8QkGa1H25YnN+5OIgCKFuQruncGWLGJYOzeSKiix/4+B9BtfKF2wf5ja8yfyWYA3OXju995G8w==} resolution: {integrity: sha512-BghPtnlwvrvq8QkGa1H25YnN+5OIgCKFuQruncGWLGJYOzeSKiix/4+B9BtfKF2wf5ja8yfyWYA3OXju995G8w==}
dependencies: dependencies:
@ -6144,10 +6173,6 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
dev: false dev: false
/ci-info@1.6.0:
resolution: {integrity: sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==}
dev: true
/ci-info@3.9.0: /ci-info@3.9.0:
resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -6343,7 +6368,6 @@ packages:
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
dependencies: dependencies:
mime-db: 1.53.0 mime-db: 1.53.0
dev: true
/compression-webpack-plugin@9.2.0: /compression-webpack-plugin@9.2.0:
resolution: {integrity: sha512-R/Oi+2+UHotGfu72fJiRoVpuRifZT0tTC6UqFD/DUo+mv8dbOow9rVOuTvDv5nPPm3GZhHL/fKkwxwIHnJ8Nyw==} resolution: {integrity: sha512-R/Oi+2+UHotGfu72fJiRoVpuRifZT0tTC6UqFD/DUo+mv8dbOow9rVOuTvDv5nPPm3GZhHL/fKkwxwIHnJ8Nyw==}
@ -6371,7 +6395,6 @@ packages:
vary: 1.1.2 vary: 1.1.2
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: true
/compute-scroll-into-view@1.0.20: /compute-scroll-into-view@1.0.20:
resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==}
@ -6581,14 +6604,6 @@ packages:
engines: {node: '>=6.0'} engines: {node: '>=6.0'}
dev: false dev: false
/cross-spawn@5.1.0:
resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}
dependencies:
lru-cache: 4.1.5
shebang-command: 1.2.0
which: 1.3.1
dev: true
/cross-spawn@7.0.6: /cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@ -7107,6 +7122,10 @@ packages:
detect-libc: 1.0.3 detect-libc: 1.0.3
dev: true dev: true
/duplexer@0.1.2:
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
dev: false
/duplexify@4.1.3: /duplexify@4.1.3:
resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==}
dependencies: dependencies:
@ -7755,6 +7774,18 @@ packages:
es5-ext: 0.10.64 es5-ext: 0.10.64
dev: true dev: true
/event-stream@3.3.4:
resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==}
dependencies:
duplexer: 0.1.2
from: 0.1.7
map-stream: 0.1.0
pause-stream: 0.0.11
split: 0.3.3
stream-combiner: 0.0.4
through: 2.3.8
dev: false
/eventemitter3@4.0.7: /eventemitter3@4.0.7:
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
dev: false dev: false
@ -7785,19 +7816,6 @@ packages:
safe-buffer: 5.2.1 safe-buffer: 5.2.1
dev: true dev: true
/execa@0.8.0:
resolution: {integrity: sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA==}
engines: {node: '>=4'}
dependencies:
cross-spawn: 5.1.0
get-stream: 3.0.0
is-stream: 1.1.0
npm-run-path: 2.0.2
p-finally: 1.0.0
signal-exit: 3.0.7
strip-eof: 1.0.0
dev: true
/execa@5.1.1: /execa@5.1.1:
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -8186,6 +8204,10 @@ packages:
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
/from@0.1.7:
resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==}
dev: false
/fs-extra@10.1.0: /fs-extra@10.1.0:
resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -8297,11 +8319,6 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
dev: true dev: true
/get-stream@3.0.0:
resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==}
engines: {node: '>=4'}
dev: true
/get-stream@5.2.0: /get-stream@5.2.0:
resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -8558,6 +8575,10 @@ packages:
hasBin: true hasBin: true
dev: true dev: true
/helmet@6.2.0:
resolution: {integrity: sha512-DWlwuXLLqbrIOltR6tFQXShj/+7Cyp0gLi6uAb8qMdFh/YBBFbKSgQ6nbXmScYd8emMctuthmgIa7tUfo9Rtyg==}
engines: {node: '>=14.0.0'}
/history@5.3.0: /history@5.3.0:
resolution: {integrity: sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==} resolution: {integrity: sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==}
dependencies: dependencies:
@ -8995,13 +9016,6 @@ packages:
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dev: true dev: true
/is-ci@1.2.1:
resolution: {integrity: sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==}
hasBin: true
dependencies:
ci-info: 1.6.0
dev: true
/is-core-module@2.15.1: /is-core-module@2.15.1:
resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -9190,11 +9204,6 @@ packages:
call-bind: 1.0.7 call-bind: 1.0.7
dev: true dev: true
/is-stream@1.1.0:
resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
engines: {node: '>=0.10.0'}
dev: true
/is-stream@2.0.1: /is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -9883,13 +9892,6 @@ packages:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
dev: true dev: true
/lru-cache@4.1.5:
resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
dependencies:
pseudomap: 1.0.2
yallist: 2.1.2
dev: true
/lru-cache@5.1.1: /lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
dependencies: dependencies:
@ -9971,6 +9973,10 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dev: true dev: true
/map-stream@0.1.0:
resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==}
dev: false
/mathml-tag-names@2.1.3: /mathml-tag-names@2.1.3:
resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==}
dev: true dev: true
@ -10071,7 +10077,6 @@ packages:
/mime-db@1.53.0: /mime-db@1.53.0:
resolution: {integrity: sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==} resolution: {integrity: sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
dev: true
/mime-types@2.1.18: /mime-types@2.1.18:
resolution: {integrity: sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==} resolution: {integrity: sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==}
@ -10521,11 +10526,6 @@ packages:
validate-npm-package-license: 3.0.4 validate-npm-package-license: 3.0.4
dev: true dev: true
/normalize-path@1.0.0:
resolution: {integrity: sha512-7WyT0w8jhpDStXRq5836AMmihQwq2nrUVQrgjvUo/p/NZf9uy/MeJ246lBJVmWuYXMlJuG9BNZHF0hWjfTbQUA==}
engines: {node: '>=0.10.0'}
dev: true
/normalize-path@3.0.0: /normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -10549,13 +10549,6 @@ packages:
resolution: {integrity: sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==} resolution: {integrity: sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==}
dev: true dev: true
/npm-run-path@2.0.2:
resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==}
engines: {node: '>=4'}
dependencies:
path-key: 2.0.1
dev: true
/npm-run-path@4.0.1: /npm-run-path@4.0.1:
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -10695,7 +10688,6 @@ packages:
/on-headers@1.0.2: /on-headers@1.0.2:
resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
dev: true
/once@1.4.0: /once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
@ -10779,11 +10771,6 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dev: false dev: false
/p-finally@1.0.0:
resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
engines: {node: '>=4'}
dev: true
/p-limit@2.3.0: /p-limit@2.3.0:
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -10917,11 +10904,6 @@ packages:
resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==} resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==}
dev: false dev: false
/path-key@2.0.1:
resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==}
engines: {node: '>=4'}
dev: true
/path-key@3.1.1: /path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -10978,7 +10960,6 @@ packages:
resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==}
dependencies: dependencies:
through: 2.3.8 through: 2.3.8
dev: true
/pbkdf2@3.1.2: /pbkdf2@3.1.2:
resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==}
@ -11786,12 +11767,17 @@ packages:
dev: true dev: true
optional: true optional: true
/pseudomap@1.0.2: /ps-tree@1.2.0:
resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==}
dev: true engines: {node: '>= 0.10'}
hasBin: true
dependencies:
event-stream: 3.3.4
dev: false
/pstree.remy@1.1.8: /pstree.remy@1.1.8:
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
dev: true
/public-encrypt@4.0.3: /public-encrypt@4.0.3:
resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==}
@ -13635,24 +13621,12 @@ packages:
resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==}
dev: true dev: true
/shebang-command@1.2.0:
resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
engines: {node: '>=0.10.0'}
dependencies:
shebang-regex: 1.0.0
dev: true
/shebang-command@2.0.0: /shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'} engines: {node: '>=8'}
dependencies: dependencies:
shebang-regex: 3.0.0 shebang-regex: 3.0.0
/shebang-regex@1.0.0:
resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
engines: {node: '>=0.10.0'}
dev: true
/shebang-regex@3.0.0: /shebang-regex@3.0.0:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -13894,6 +13868,12 @@ packages:
engines: {node: '>= 10.x'} engines: {node: '>= 10.x'}
dev: true dev: true
/split@0.3.3:
resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==}
dependencies:
through: 2.3.8
dev: false
/sprintf-js@1.0.3: /sprintf-js@1.0.3:
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
dev: true dev: true
@ -13953,6 +13933,12 @@ packages:
readable-stream: 2.3.8 readable-stream: 2.3.8
dev: true dev: true
/stream-combiner@0.0.4:
resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==}
dependencies:
duplexer: 0.1.2
dev: false
/stream-http@2.8.3: /stream-http@2.8.3:
resolution: {integrity: sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==} resolution: {integrity: sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==}
dependencies: dependencies:
@ -14077,11 +14063,6 @@ packages:
ansi-regex: 6.1.0 ansi-regex: 6.1.0
dev: true dev: true
/strip-eof@1.0.0:
resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==}
engines: {node: '>=0.10.0'}
dev: true
/strip-final-newline@2.0.0: /strip-final-newline@2.0.0:
resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -14092,11 +14073,6 @@ packages:
engines: {node: '>=12'} engines: {node: '>=12'}
dev: true dev: true
/strip-indent@2.0.0:
resolution: {integrity: sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==}
engines: {node: '>=4'}
dev: true
/strip-indent@3.0.0: /strip-indent@3.0.0:
resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -14420,7 +14396,6 @@ packages:
/through@2.3.8: /through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
dev: true
/timers-browserify@2.0.12: /timers-browserify@2.0.12:
resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==}
@ -15341,10 +15316,6 @@ packages:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'} engines: {node: '>=10'}
/yallist@2.1.2:
resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==}
dev: true
/yallist@3.1.1: /yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
dev: true dev: true
@ -15393,17 +15364,6 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
dev: true dev: true
/yorkie@2.0.0:
resolution: {integrity: sha512-jcKpkthap6x63MB4TxwCyuIGkV0oYP/YRyuQU5UO0Yz/E/ZAu+653/uov+phdmO54n6BcvFRyyt0RRrWdN2mpw==}
engines: {node: '>=4'}
requiresBuild: true
dependencies:
execa: 0.8.0
is-ci: 1.2.1
normalize-path: 1.0.0
strip-indent: 2.0.0
dev: true
/zod-validation-error@2.1.0(zod@3.23.8): /zod-validation-error@2.1.0(zod@3.23.8):
resolution: {integrity: sha512-VJh93e2wb4c3tWtGgTa0OF/dTt/zoPCPzXq4V11ZjxmEAFaPi/Zss1xIZdEB5RD8GD00U0/iVXgqkF77RV7pdQ==} resolution: {integrity: sha512-VJh93e2wb4c3tWtGgTa0OF/dTt/zoPCPzXq4V11ZjxmEAFaPi/Zss1xIZdEB5RD8GD00U0/iVXgqkF77RV7pdQ==}
engines: {node: '>=18.0.0'} engines: {node: '>=18.0.0'}

View File

@ -81,7 +81,6 @@ main() {
check_ql check_ql
check_nginx check_nginx
check_pm2 check_pm2
reload_update
reload_pm2 reload_pm2
echo -e "\n=====> 检测结束\n" echo -e "\n=====> 检测结束\n"
} }

View File

@ -308,13 +308,6 @@ reload_pm2() {
pm2 startOrGracefulReload ecosystem.config.js pm2 startOrGracefulReload ecosystem.config.js
} }
reload_update() {
cd $dir_root
restore_env_vars
pm2 flush &>/dev/null
pm2 startOrGracefulReload other.config.js
}
diff_time() { diff_time() {
local format="$1" local format="$1"
local begin_time="$2" local begin_time="$2"

View File

@ -227,7 +227,10 @@ usage() {
} }
reload_qinglong() { reload_qinglong() {
echo -e "[reload_qinglong] deleting Triggered at $(date)" >>${dir_log}/reload.log
sleep 3
delete_pm2 delete_pm2
echo -e "[reload_qinglong] deleted Triggered at $(date)" >>${dir_log}/reload.log
local reload_target="${1}" local reload_target="${1}"
local primary_branch="master" local primary_branch="master"
@ -247,8 +250,9 @@ reload_qinglong() {
rm -rf ${dir_data}/* rm -rf ${dir_data}/*
mv -f ${dir_tmp}/data/* ${dir_data}/ mv -f ${dir_tmp}/data/* ${dir_data}/
fi fi
echo -e "[reload_qinglong] starting Triggered at $(date)" >>${dir_log}/reload.log
reload_pm2 reload_pm2
echo -e "[reload_qinglong] started Triggered at $(date)\n" >>${dir_log}/reload.log
} }
## 更新 qinglong ## 更新 qinglong
@ -309,15 +313,7 @@ check_update_dep() {
echo -e "更新包下载成功..." echo -e "更新包下载成功..."
if [[ "$needRestart" == 'true' ]]; then if [[ "$needRestart" == 'true' ]]; then
delete_pm2 reload_qinglong "system"
rm -rf ${dir_root}/back ${dir_root}/cli ${dir_root}/docker ${dir_root}/sample ${dir_root}/shell ${dir_root}/src
mv -f ${dir_tmp}/qinglong-${primary_branch}/* ${dir_root}/
rm -rf $dir_static/*
mv -f ${dir_tmp}/qinglong-static-${primary_branch}/* ${dir_static}/
cp -f $file_config_sample $dir_config/config.sample.sh
reload_pm2
fi fi
else else
echo -e "\n依赖检测安装失败请检查网络...\n" echo -e "\n依赖检测安装失败请检查网络...\n"

View File

@ -101,9 +101,9 @@ export default function () {
const getHealthStatus = () => { const getHealthStatus = () => {
request request
.get(`${config.apiPrefix}public/health`) .get(`${config.apiPrefix}health`)
.then((res) => { .then((res) => {
if (res?.data?.status === 1) { if (res?.data?.status === 'ok') {
getSystemInfo(); getSystemInfo();
} else { } else {
history.push('/error'); history.push('/error');

View File

@ -17,9 +17,9 @@ const Error = () => {
const getHealthStatus = (needLoading: boolean = true) => { const getHealthStatus = (needLoading: boolean = true) => {
needLoading && setLoading(true); needLoading && setLoading(true);
request request
.get(`${config.apiPrefix}public/health`) .get(`${config.apiPrefix}health`)
.then(({ error, data }) => { .then(({ error, data }) => {
if (data?.status === 1) { if (data?.status === 'ok') {
if (retryTimes.current > 1) { if (retryTimes.current > 1) {
setTimeout(() => { setTimeout(() => {
window.location.reload(); window.location.reload();

View File

@ -6,10 +6,10 @@ import {
} from '@codemirror/view'; } from '@codemirror/view';
import { RangeSet, RangeSetBuilder } from '@codemirror/state'; import { RangeSet, RangeSetBuilder } from '@codemirror/state';
const infoWord = /\[\ue6f5info\]/g; const infoWord = /\[info/g;
const debugWord = /\[\ue67fdebug\]/g; const debugWord = /\[debug/g;
const warnWord = /\[\ue880warn\]/g; const warnWord = /\[❌warn/g;
const errorWord = /\[\ue602error\]/g; const errorWord = /\[🐛error/g;
const customWordClassMap = { const customWordClassMap = {
info: 'system-log-info', info: 'system-log-info',