mirror of
https://github.com/whyour/qinglong.git
synced 2026-08-08 18:04:32 +08:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 56eb0c5408 | |||
| fe55929959 | |||
| 01e2bd007d | |||
| 4e091b0c3e | |||
| db94cd3799 | |||
| a15192b9b2 | |||
| efd4f1d5ab | |||
| bd166ee794 | |||
| 93e94ea94c | |||
| 88b87de391 | |||
| 8affff96f3 | |||
| 936b565fb1 | |||
| b69ff2895e | |||
| f3791cbb62 | |||
| b0f3b51736 | |||
| 3aa112e373 | |||
| 683482e067 | |||
| 5a2caeb66b | |||
| ee6e5bd8b4 | |||
| f970322f0a | |||
| 1718120623 | |||
| 702c3160ec |
+2
-1
@@ -28,4 +28,5 @@
|
||||
/db
|
||||
/manual_log
|
||||
/scripts
|
||||
/bak
|
||||
/bak
|
||||
/.tmp
|
||||
@@ -35,6 +35,24 @@ Timed task management platform supporting Python3, JavaScript, Shell, Typescript
|
||||
- Support dark mode
|
||||
- Support cell phone operation
|
||||
|
||||
## Version
|
||||
### docker
|
||||
|
||||
The `latest` image is built on `alpine` and the `debian` image is built on `debian-slim`. If you need to use a dependency that is not supported by `alpine`, it is recommended that you use the `debian` image.
|
||||
|
||||
```bash
|
||||
docker pull whyour/qinglong:latest
|
||||
docker pull whyour/qinglong:debian
|
||||
```
|
||||
|
||||
### npm
|
||||
|
||||
The npm version supports `debian/ubuntu/centos/alpine` systems and requires `node/python3` to be installed.
|
||||
|
||||
```bash
|
||||
npm i @whyour/qinglong
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
### Docker (Recommended)
|
||||
|
||||
@@ -37,6 +37,24 @@ Timed task management platform supporting Python3, JavaScript, Shell, Typescript
|
||||
- 支持暗黑模式
|
||||
- 支持手机端操作
|
||||
|
||||
## 版本
|
||||
### docker
|
||||
|
||||
`latest` 镜像是基于 `alpine` 构建,`debian` 镜像是基于 `debian-slim` 构建。如果需要使用 `alpine` 不支持的依赖,建议使用 `debian` 镜像
|
||||
|
||||
```bash
|
||||
docker pull whyour/qinglong:latest
|
||||
docker pull whyour/qinglong:debian
|
||||
```
|
||||
|
||||
### npm
|
||||
|
||||
npm 版本支持 `debian/ubuntu/centos/alpine` 系统,需要自行安装 `node/python3`
|
||||
|
||||
```bash
|
||||
npm i @whyour/qinglong
|
||||
```
|
||||
|
||||
## 部署
|
||||
|
||||
### docker (推荐)
|
||||
|
||||
+64
-25
@@ -14,8 +14,18 @@ import {
|
||||
promiseExec,
|
||||
} from '../config/util';
|
||||
import dayjs from 'dayjs';
|
||||
import multer from 'multer';
|
||||
|
||||
const route = Router();
|
||||
const storage = multer.diskStorage({
|
||||
destination: function (req, file, cb) {
|
||||
cb(null, config.tmpPath);
|
||||
},
|
||||
filename: function (req, file, cb) {
|
||||
cb(null, 'data.tgz');
|
||||
},
|
||||
});
|
||||
const upload = multer({ storage: storage });
|
||||
|
||||
export default (app: Router) => {
|
||||
app.use('/system', route);
|
||||
@@ -25,23 +35,9 @@ export default (app: Router) => {
|
||||
try {
|
||||
const userService = Container.get(UserService);
|
||||
const authInfo = await userService.getUserInfo();
|
||||
const envCount = await EnvModel.count();
|
||||
const { version, changeLog, changeLogLink } = await parseVersion(
|
||||
const { version, changeLog, changeLogLink, publishTime } = await parseVersion(
|
||||
config.versionFile,
|
||||
);
|
||||
const lastCommitTime = (
|
||||
await promiseExec(
|
||||
`cd ${config.rootPath} && git show -s --format=%ai | head -1`,
|
||||
)
|
||||
).replace('\n', '');
|
||||
const lastCommitId = (
|
||||
await promiseExec(`cd ${config.rootPath} && git rev-parse --short HEAD`)
|
||||
).replace('\n', '');
|
||||
const branch = (
|
||||
await promiseExec(
|
||||
`cd ${config.rootPath} && git symbolic-ref --short HEAD`,
|
||||
)
|
||||
).replace('\n', '');
|
||||
|
||||
let isInitialized = true;
|
||||
if (
|
||||
@@ -56,9 +52,8 @@ export default (app: Router) => {
|
||||
data: {
|
||||
isInitialized,
|
||||
version,
|
||||
lastCommitTime: dayjs(lastCommitTime).unix(),
|
||||
lastCommitId,
|
||||
branch,
|
||||
publishTime: dayjs(publishTime).unix(),
|
||||
branch: process.env.QL_BRANCH || 'master',
|
||||
changeLog,
|
||||
changeLogLink,
|
||||
},
|
||||
@@ -70,12 +65,12 @@ export default (app: Router) => {
|
||||
});
|
||||
|
||||
route.get(
|
||||
'/log/remove',
|
||||
'/config',
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
const logger: Logger = Container.get('logger');
|
||||
try {
|
||||
const systemService = Container.get(SystemService);
|
||||
const data = await systemService.getLogRemoveFrequency();
|
||||
const data = await systemService.getSystemConfig();
|
||||
res.send({ code: 200, data });
|
||||
} catch (e) {
|
||||
return next(e);
|
||||
@@ -84,19 +79,18 @@ export default (app: Router) => {
|
||||
);
|
||||
|
||||
route.put(
|
||||
'/log/remove',
|
||||
'/config',
|
||||
celebrate({
|
||||
body: Joi.object({
|
||||
frequency: Joi.number().required(),
|
||||
logRemoveFrequency: Joi.number().optional().allow(null),
|
||||
cronConcurrency: Joi.number().optional().allow(null),
|
||||
}),
|
||||
}),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
const logger: Logger = Container.get('logger');
|
||||
try {
|
||||
const systemService = Container.get(SystemService);
|
||||
const result = await systemService.updateLogRemoveFrequency(
|
||||
req.body.frequency,
|
||||
);
|
||||
const result = await systemService.updateSystemConfig(req.body);
|
||||
res.send(result);
|
||||
} catch (e) {
|
||||
return next(e);
|
||||
@@ -132,6 +126,25 @@ export default (app: Router) => {
|
||||
},
|
||||
);
|
||||
|
||||
route.put(
|
||||
'/reload',
|
||||
celebrate({
|
||||
body: Joi.object({
|
||||
type: Joi.string().required(),
|
||||
}),
|
||||
}),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
const logger: Logger = Container.get('logger');
|
||||
try {
|
||||
const systemService = Container.get(SystemService);
|
||||
const result = await systemService.reloadSystem(req.body.type);
|
||||
res.send(result);
|
||||
} catch (e) {
|
||||
return next(e);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
route.put(
|
||||
'/notify',
|
||||
celebrate({
|
||||
@@ -211,4 +224,30 @@ export default (app: Router) => {
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
route.put(
|
||||
'/data/export',
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const systemService = Container.get(SystemService);
|
||||
await systemService.exportData(res);
|
||||
} catch (e) {
|
||||
return next(e);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
route.put(
|
||||
'/data/import',
|
||||
upload.single('data'),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const systemService = Container.get(SystemService);
|
||||
const result = await systemService.importData();
|
||||
res.send(result);
|
||||
} catch (e) {
|
||||
return next(e);
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ const rootPath = process.env.QL_DIR as string;
|
||||
const envFound = dotenv.config({ path: path.join(rootPath, '.env') });
|
||||
|
||||
const dataPath = path.join(rootPath, 'data/');
|
||||
const tmpPath = path.join(rootPath, '.tmp/');
|
||||
const samplePath = path.join(rootPath, 'sample/');
|
||||
const configPath = path.join(dataPath, 'config/');
|
||||
const scriptPath = path.join(dataPath, 'scripts/');
|
||||
@@ -42,6 +43,7 @@ const authError = '错误的用户名密码,请重试';
|
||||
const loginFaild = '请先登录!';
|
||||
const configString = 'config sample crontab shareCode diy';
|
||||
const versionFile = path.join(rootPath, 'version.yaml');
|
||||
const dataTgzFile = path.join(tmpPath, 'data.tgz');
|
||||
|
||||
if (envFound.error) {
|
||||
throw new Error("⚠️ Couldn't find .env file ⚠️");
|
||||
@@ -59,6 +61,9 @@ export default {
|
||||
prefix: '/api',
|
||||
},
|
||||
rootPath,
|
||||
tmpPath,
|
||||
dataPath,
|
||||
dataTgzFile,
|
||||
configString,
|
||||
loginFaild,
|
||||
authError,
|
||||
|
||||
@@ -399,6 +399,18 @@ export function promiseExec(command: string): Promise<string> {
|
||||
});
|
||||
}
|
||||
|
||||
export function promiseExecSuccess(command: string): Promise<string> {
|
||||
return new Promise((resolve) => {
|
||||
exec(
|
||||
command,
|
||||
{ maxBuffer: 200 * 1024 * 1024, encoding: 'utf8' },
|
||||
(err, stdout, stderr) => {
|
||||
resolve(stdout || '');
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function parseHeaders(headers: string) {
|
||||
if (!headers) return {};
|
||||
|
||||
@@ -505,6 +517,7 @@ interface IVersion {
|
||||
version: string;
|
||||
changeLogLink: string;
|
||||
changeLog: string;
|
||||
publishTime: string;
|
||||
}
|
||||
|
||||
export async function parseVersion(path: string): Promise<IVersion> {
|
||||
|
||||
+19
-2
@@ -1,10 +1,11 @@
|
||||
import { sequelize } from '.';
|
||||
import { DataTypes, Model, ModelDefined } from 'sequelize';
|
||||
import { NotificationInfo } from './notify';
|
||||
|
||||
export class AuthInfo {
|
||||
ip?: string;
|
||||
type: AuthDataType;
|
||||
info?: any;
|
||||
info?: AuthModelInfo;
|
||||
id?: number;
|
||||
|
||||
constructor(options: AuthInfo) {
|
||||
@@ -25,9 +26,25 @@ export enum AuthDataType {
|
||||
'authToken' = 'authToken',
|
||||
'notification' = 'notification',
|
||||
'removeLogFrequency' = 'removeLogFrequency',
|
||||
'systemConfig' = 'systemConfig',
|
||||
}
|
||||
|
||||
interface AuthInstance extends Model<AuthInfo, AuthInfo>, AuthInfo {}
|
||||
export interface SystemConfigInfo {
|
||||
logRemoveFrequency?: number;
|
||||
cronConcurrency?: number;
|
||||
}
|
||||
|
||||
export interface LoginLogInfo {
|
||||
timestamp?: number;
|
||||
address?: string;
|
||||
ip?: string;
|
||||
platform?: string;
|
||||
status?: LoginStatus,
|
||||
}
|
||||
|
||||
export type AuthModelInfo = SystemConfigInfo & Partial<NotificationInfo> & LoginLogInfo;
|
||||
|
||||
export interface AuthInstance extends Model<AuthInfo, AuthInfo>, AuthInfo { }
|
||||
export const AuthModel = sequelize.define<AuthInstance>('Auth', {
|
||||
ip: DataTypes.STRING,
|
||||
type: DataTypes.STRING,
|
||||
|
||||
+4
-4
@@ -43,13 +43,13 @@ export class Crontab {
|
||||
}
|
||||
|
||||
export enum CrontabStatus {
|
||||
'running',
|
||||
'idle',
|
||||
'running' = 0,
|
||||
'queued' = 0.5,
|
||||
'idle' = 1,
|
||||
'disabled',
|
||||
'queued',
|
||||
}
|
||||
|
||||
interface CronInstance extends Model<Crontab, Crontab>, Crontab {}
|
||||
export interface CronInstance extends Model<Crontab, Crontab>, Crontab {}
|
||||
export const CrontabModel = sequelize.define<CronInstance>('Crontab', {
|
||||
name: {
|
||||
unique: 'compositeIndex',
|
||||
|
||||
@@ -39,7 +39,7 @@ export class CrontabView {
|
||||
}
|
||||
}
|
||||
|
||||
interface CronViewInstance
|
||||
export interface CronViewInstance
|
||||
extends Model<CrontabView, CrontabView>,
|
||||
CrontabView {}
|
||||
export const CrontabViewModel = sequelize.define<CronViewInstance>(
|
||||
|
||||
+20
-8
@@ -4,9 +4,9 @@ import { DataTypes, Model, ModelDefined } from 'sequelize';
|
||||
export class Dependence {
|
||||
timestamp?: string;
|
||||
id?: number;
|
||||
status?: DependenceStatus;
|
||||
type?: DependenceTypes;
|
||||
name?: number;
|
||||
status: DependenceStatus;
|
||||
type: DependenceTypes;
|
||||
name: string;
|
||||
log?: string[];
|
||||
remark?: string;
|
||||
|
||||
@@ -18,7 +18,7 @@ export class Dependence {
|
||||
: DependenceStatus.queued;
|
||||
this.type = options.type || DependenceTypes.nodejs;
|
||||
this.timestamp = new Date().toString();
|
||||
this.name = options.name;
|
||||
this.name = options.name.trim();
|
||||
this.log = options.log || [];
|
||||
this.remark = options.remark || '';
|
||||
}
|
||||
@@ -42,19 +42,31 @@ export enum DependenceTypes {
|
||||
|
||||
export enum InstallDependenceCommandTypes {
|
||||
'pnpm add -g',
|
||||
'pip3 install',
|
||||
'pip3 install --disable-pip-version-check --root-user-action=ignore',
|
||||
'apk add',
|
||||
}
|
||||
|
||||
export enum GetDependenceCommandTypes {
|
||||
'pnpm ls -g ',
|
||||
'pip3 list --disable-pip-version-check',
|
||||
'apk info',
|
||||
}
|
||||
|
||||
export enum versionDependenceCommandTypes {
|
||||
'@',
|
||||
'==',
|
||||
'=',
|
||||
}
|
||||
|
||||
export enum unInstallDependenceCommandTypes {
|
||||
'pnpm remove -g',
|
||||
'pip3 uninstall -y',
|
||||
'pip3 uninstall --disable-pip-version-check --root-user-action=ignore -y',
|
||||
'apk del',
|
||||
}
|
||||
|
||||
interface DependenceInstance
|
||||
export interface DependenceInstance
|
||||
extends Model<Dependence, Dependence>,
|
||||
Dependence {}
|
||||
Dependence { }
|
||||
export const DependenceModel = sequelize.define<DependenceInstance>(
|
||||
'Dependence',
|
||||
{
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ export const initPosition = 4500000000000000;
|
||||
export const stepPosition = 10000000000;
|
||||
export const minPosition = 100;
|
||||
|
||||
interface EnvInstance extends Model<Env, Env>, Env {}
|
||||
export interface EnvInstance extends Model<Env, Env>, Env {}
|
||||
export const EnvModel = sequelize.define<EnvInstance>('Env', {
|
||||
value: { type: DataTypes.STRING, unique: 'compositeIndex' },
|
||||
timestamp: DataTypes.STRING,
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ export interface AppToken {
|
||||
|
||||
export type AppScope = 'envs' | 'crons' | 'configs' | 'scripts' | 'logs';
|
||||
|
||||
interface AppInstance extends Model<App, App>, App {}
|
||||
export interface AppInstance extends Model<App, App>, App {}
|
||||
export const AppModel = sequelize.define<AppInstance>('App', {
|
||||
name: { type: DataTypes.STRING, unique: 'name' },
|
||||
scopes: DataTypes.JSON,
|
||||
|
||||
+2
-1
@@ -16,4 +16,5 @@ export type SockMessageType =
|
||||
| 'uninstallDependence'
|
||||
| 'updateSystemVersion'
|
||||
| 'manuallyRunScript'
|
||||
| 'runSubscriptionEnd';
|
||||
| 'runSubscriptionEnd'
|
||||
| 'reloadSystem';
|
||||
|
||||
@@ -70,7 +70,7 @@ export enum SubscriptionStatus {
|
||||
'queued',
|
||||
}
|
||||
|
||||
interface SubscriptionInstance
|
||||
export interface SubscriptionInstance
|
||||
extends Model<Subscription, Subscription>,
|
||||
Subscription {}
|
||||
export const SubscriptionModel = sequelize.define<SubscriptionInstance>(
|
||||
|
||||
@@ -32,7 +32,7 @@ export default async () => {
|
||||
// 初始化更新所有任务状态为空闲
|
||||
await CrontabModel.update(
|
||||
{ status: CrontabStatus.idle },
|
||||
{ where: { status: [CrontabStatus.running, CrontabStatus.queued] } },
|
||||
{ where: {} },
|
||||
);
|
||||
|
||||
// 初始化时安装所有处于安装中,安装成功,安装失败的依赖
|
||||
|
||||
@@ -18,10 +18,13 @@ const confFile = path.join(configPath, 'config.sh');
|
||||
const authConfigFile = path.join(configPath, 'auth.json');
|
||||
const sampleConfigFile = path.join(samplePath, 'config.sample.sh');
|
||||
const sampleAuthFile = path.join(samplePath, 'auth.sample.json');
|
||||
const sampleTaskShellFile = path.join(samplePath, 'task.sample.sh');
|
||||
const sampleNotifyJsFile = path.join(samplePath, 'notify.js');
|
||||
const sampleNotifyPyFile = path.join(samplePath, 'notify.py');
|
||||
const scriptNotifyJsFile = path.join(scriptPath, 'sendNotify.js');
|
||||
const scriptNotifyPyFile = path.join(scriptPath, 'notify.py');
|
||||
const TaskBeforeFile = path.join(configPath, 'task_before.sh');
|
||||
const TaskAfterFile = path.join(configPath, 'task_after.sh');
|
||||
const homedir = os.homedir();
|
||||
const sshPath = path.resolve(homedir, '.ssh');
|
||||
const sshdPath = path.join(dataPath, 'ssh.d');
|
||||
@@ -39,6 +42,8 @@ export default async () => {
|
||||
const tmpDirExist = await fileExist(tmpPath);
|
||||
const scriptNotifyJsFileExist = await fileExist(scriptNotifyJsFile);
|
||||
const scriptNotifyPyFileExist = await fileExist(scriptNotifyPyFile);
|
||||
const TaskBeforeFileExist = await fileExist(TaskBeforeFile);
|
||||
const TaskAfterFileExist = await fileExist(TaskAfterFile);
|
||||
|
||||
if (!configDirExist) {
|
||||
fs.mkdirSync(configPath);
|
||||
@@ -89,6 +94,14 @@ export default async () => {
|
||||
fs.writeFileSync(scriptNotifyPyFile, fs.readFileSync(sampleNotifyPyFile));
|
||||
}
|
||||
|
||||
if (!TaskBeforeFileExist) {
|
||||
fs.writeFileSync(TaskBeforeFile, fs.readFileSync(sampleTaskShellFile));
|
||||
}
|
||||
|
||||
if (!TaskAfterFileExist) {
|
||||
fs.writeFileSync(TaskAfterFile, fs.readFileSync(sampleTaskShellFile));
|
||||
}
|
||||
|
||||
dotenv.config({ path: confFile });
|
||||
|
||||
Logger.info('✌️ Init file down');
|
||||
|
||||
@@ -29,16 +29,16 @@ export default async () => {
|
||||
});
|
||||
|
||||
// 运行删除日志任务
|
||||
const data = await systemService.getLogRemoveFrequency();
|
||||
if (data && data.info && data.info.frequency) {
|
||||
const data = await systemService.getSystemConfig();
|
||||
if (data && data.info && data.info.logRemoveFrequency) {
|
||||
const rmlogCron = {
|
||||
id: data.id,
|
||||
id: data.id as number,
|
||||
name: '删除日志',
|
||||
command: `ql rmlog ${data.info.frequency}`,
|
||||
command: `ql rmlog ${data.info.logRemoveFrequency}`,
|
||||
};
|
||||
await scheduleService.cancelIntervalTask(rmlogCron);
|
||||
scheduleService.createIntervalTask(rmlogCron, {
|
||||
days: data.info.frequency,
|
||||
days: data.info.logRemoveFrequency,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+27
-11
@@ -5,18 +5,34 @@ import Sock from './sock';
|
||||
export default async ({ server }: { server: Server }) => {
|
||||
await Sock({ server });
|
||||
Logger.info('✌️ Sock loaded');
|
||||
let exitTime = 0;
|
||||
let timer: NodeJS.Timeout;
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
Logger.info('✌️ Server need close');
|
||||
server.close(() => {
|
||||
setTimeout(() => {
|
||||
process.exit();
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
console.log('Forcing server close !!!');
|
||||
process.on('SIGINT', (singal) => {
|
||||
Logger.warn(`Server need close, singal ${singal}`);
|
||||
exitTime++;
|
||||
if (exitTime >= 3) {
|
||||
Logger.warn('Forcing server close');
|
||||
clearTimeout(timer);
|
||||
process.exit(1);
|
||||
}, 15000);
|
||||
}
|
||||
server.close(() => {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
timer = setTimeout(() => {
|
||||
process.exit();
|
||||
}, 15000);
|
||||
});
|
||||
});
|
||||
|
||||
process.on('uncaughtException', (error) => {
|
||||
Logger.error('Uncaught exception:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
Logger.error('Unhandled rejection:', reason);
|
||||
process.exit(1);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -7,21 +7,20 @@ import fs from 'fs';
|
||||
import cron_parser from 'cron-parser';
|
||||
import {
|
||||
getFileContentByName,
|
||||
concurrentRun,
|
||||
fileExist,
|
||||
killTask,
|
||||
} from '../config/util';
|
||||
import { promises, existsSync } from 'fs';
|
||||
import { Op, where, col as colFn, FindOptions } from 'sequelize';
|
||||
import { Op, where, col as colFn, FindOptions, fn } from 'sequelize';
|
||||
import path from 'path';
|
||||
import { TASK_PREFIX, QL_PREFIX } from '../config/const';
|
||||
import cronClient from '../schedule/client';
|
||||
import { runWithCpuLimit } from '../shared/pLimit';
|
||||
import taskLimit from '../shared/pLimit';
|
||||
import { spawn } from 'cross-spawn';
|
||||
|
||||
@Service()
|
||||
export default class CronService {
|
||||
constructor(@Inject('logger') private logger: winston.Logger) {}
|
||||
constructor(@Inject('logger') private logger: winston.Logger) { }
|
||||
|
||||
private isSixCron(cron: Crontab) {
|
||||
const { schedule } = cron;
|
||||
@@ -268,7 +267,7 @@ export default class CronService {
|
||||
let q: any = {};
|
||||
if (!filterQuery[key]) continue;
|
||||
if (key === 'status') {
|
||||
if (filterQuery[key].includes(2)) {
|
||||
if (filterQuery[key].includes(CrontabStatus.disabled)) {
|
||||
q = { [Op.or]: [{ [key]: filterQuery[key] }, { isDisabled: 1 }] };
|
||||
} else {
|
||||
q = { [Op.and]: [{ [key]: filterQuery[key] }, { isDisabled: 0 }] };
|
||||
@@ -387,7 +386,7 @@ export default class CronService {
|
||||
}
|
||||
|
||||
private async runSingle(cronId: number): Promise<number> {
|
||||
return runWithCpuLimit(() => {
|
||||
return taskLimit.runWithCpuLimit(() => {
|
||||
return new Promise(async (resolve: any) => {
|
||||
const cron = await this.getDb({ id: cronId });
|
||||
if (cron.status !== CrontabStatus.queued) {
|
||||
|
||||
+57
-15
@@ -8,20 +8,22 @@ import {
|
||||
DependenceTypes,
|
||||
unInstallDependenceCommandTypes,
|
||||
DependenceModel,
|
||||
GetDependenceCommandTypes,
|
||||
versionDependenceCommandTypes,
|
||||
} from '../data/dependence';
|
||||
import { spawn } from 'cross-spawn';
|
||||
import SockService from './sock';
|
||||
import { FindOptions, Op } from 'sequelize';
|
||||
import { concurrentRun } from '../config/util';
|
||||
import { promiseExecSuccess } from '../config/util';
|
||||
import dayjs from 'dayjs';
|
||||
import { runOneByOne, runWithCpuLimit } from '../shared/pLimit';
|
||||
import taskLimit from '../shared/pLimit';
|
||||
|
||||
@Service()
|
||||
export default class DependenceService {
|
||||
constructor(
|
||||
@Inject('logger') private logger: winston.Logger,
|
||||
private sockService: SockService,
|
||||
) { }
|
||||
) {}
|
||||
|
||||
public async create(payloads: Dependence[]): Promise<Dependence[]> {
|
||||
const tabs = payloads.map((x) => {
|
||||
@@ -137,9 +139,17 @@ export default class DependenceService {
|
||||
}
|
||||
|
||||
private async updateLog(ids: number[], log: string): Promise<void> {
|
||||
const doc = await DependenceModel.findOne({ where: { id: ids } });
|
||||
const newLog = doc?.log ? [...doc.log, log] : [log];
|
||||
await DependenceModel.update({ log: newLog }, { where: { id: ids } });
|
||||
taskLimit.updateDepLog(async () => {
|
||||
const docs = await DependenceModel.findAll({ where: { id: ids } });
|
||||
for (const doc of docs) {
|
||||
const newLog = doc?.log ? [...doc.log, log] : [log];
|
||||
await DependenceModel.update(
|
||||
{ log: newLog },
|
||||
{ where: { id: doc.id } },
|
||||
);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public installOrUninstallDependency(
|
||||
@@ -147,7 +157,7 @@ export default class DependenceService {
|
||||
isInstall: boolean = true,
|
||||
force: boolean = false,
|
||||
) {
|
||||
return runOneByOne(() => {
|
||||
return taskLimit.runOneByOne(() => {
|
||||
return new Promise(async (resolve) => {
|
||||
const depIds = [dependency.id!];
|
||||
const status = isInstall
|
||||
@@ -155,15 +165,15 @@ export default class DependenceService {
|
||||
: DependenceStatus.removing;
|
||||
await DependenceModel.update({ status }, { where: { id: depIds } });
|
||||
|
||||
const socketMessageType = !force
|
||||
const socketMessageType = isInstall
|
||||
? 'installDependence'
|
||||
: 'uninstallDependence';
|
||||
const depName = dependency.name;
|
||||
const depName = dependency.name.trim();
|
||||
const depRunCommand = (
|
||||
isInstall
|
||||
? InstallDependenceCommandTypes
|
||||
: unInstallDependenceCommandTypes
|
||||
)[dependency.type as any];
|
||||
)[dependency.type];
|
||||
const actionText = isInstall ? '安装' : '删除';
|
||||
const startTime = dayjs();
|
||||
|
||||
@@ -175,7 +185,39 @@ export default class DependenceService {
|
||||
message,
|
||||
references: depIds,
|
||||
});
|
||||
await this.updateLog(depIds, message);
|
||||
this.updateLog(depIds, message);
|
||||
|
||||
// 判断是否已经安装过依赖
|
||||
if (isInstall) {
|
||||
const getCommandPrefix = GetDependenceCommandTypes[dependency.type];
|
||||
const depVersionStr = versionDependenceCommandTypes[dependency.type];
|
||||
const [_depName] = dependency.name.trim().split(depVersionStr);
|
||||
const depInfo = (
|
||||
await promiseExecSuccess(
|
||||
dependency.type === DependenceTypes.linux
|
||||
? `${getCommandPrefix} ${_depName}`
|
||||
: `${getCommandPrefix} | grep "${_depName}"`,
|
||||
)
|
||||
).replace(/\s{2,}/, ' ');
|
||||
|
||||
if (depInfo) {
|
||||
const endTime = dayjs();
|
||||
const _message = `检测到已经安装 ${_depName}\n\n${depInfo}\n跳过安装\n\n依赖${actionText}成功,结束时间 ${endTime.format(
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
)},耗时 ${endTime.diff(startTime, 'second')} 秒`;
|
||||
this.sockService.sendMessage({
|
||||
type: socketMessageType,
|
||||
message: _message,
|
||||
references: depIds,
|
||||
});
|
||||
this.updateLog(depIds, _message);
|
||||
await DependenceModel.update(
|
||||
{ status: DependenceStatus.installed },
|
||||
{ where: { id: depIds } },
|
||||
);
|
||||
return resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
const cp = spawn(`${depRunCommand} ${depName}`, {
|
||||
shell: '/bin/bash',
|
||||
@@ -187,7 +229,7 @@ export default class DependenceService {
|
||||
message: data.toString(),
|
||||
references: depIds,
|
||||
});
|
||||
await this.updateLog(depIds, data.toString());
|
||||
this.updateLog(depIds, data.toString());
|
||||
});
|
||||
|
||||
cp.stderr.on('data', async (data) => {
|
||||
@@ -196,7 +238,7 @@ export default class DependenceService {
|
||||
message: data.toString(),
|
||||
references: depIds,
|
||||
});
|
||||
await this.updateLog(depIds, data.toString());
|
||||
this.updateLog(depIds, data.toString());
|
||||
});
|
||||
|
||||
cp.on('error', async (err) => {
|
||||
@@ -205,7 +247,7 @@ export default class DependenceService {
|
||||
message: JSON.stringify(err),
|
||||
references: depIds,
|
||||
});
|
||||
await this.updateLog(depIds, JSON.stringify(err));
|
||||
this.updateLog(depIds, JSON.stringify(err));
|
||||
});
|
||||
|
||||
cp.on('close', async (code) => {
|
||||
@@ -221,7 +263,7 @@ export default class DependenceService {
|
||||
message,
|
||||
references: depIds,
|
||||
});
|
||||
await this.updateLog(depIds, message);
|
||||
this.updateLog(depIds, message);
|
||||
|
||||
let status = null;
|
||||
if (isSucceed) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
Task,
|
||||
} from 'toad-scheduler';
|
||||
import dayjs from 'dayjs';
|
||||
import { runWithCpuLimit } from '../shared/pLimit';
|
||||
import taskLimit from '../shared/pLimit';
|
||||
import { spawn } from 'cross-spawn';
|
||||
|
||||
interface ScheduleTaskType {
|
||||
@@ -49,7 +49,7 @@ export default class ScheduleService {
|
||||
callbacks: TaskCallbacks = {},
|
||||
completionTime: 'start' | 'end' = 'end',
|
||||
) {
|
||||
return runWithCpuLimit(() => {
|
||||
return taskLimit.runWithCpuLimit(() => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const startTime = dayjs();
|
||||
|
||||
+87
-26
@@ -1,8 +1,14 @@
|
||||
import { Response } from 'express';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import winston from 'winston';
|
||||
import config from '../config';
|
||||
import * as fs from 'fs';
|
||||
import { AuthDataType, AuthInfo, AuthModel, LoginStatus } from '../data/auth';
|
||||
import {
|
||||
AuthDataType,
|
||||
AuthInfo,
|
||||
AuthInstance,
|
||||
AuthModel,
|
||||
AuthModelInfo,
|
||||
} from '../data/auth';
|
||||
import { NotificationInfo } from '../data/notify';
|
||||
import NotificationService from './notify';
|
||||
import ScheduleService, { TaskCallbacks } from './schedule';
|
||||
@@ -14,8 +20,12 @@ import {
|
||||
killTask,
|
||||
parseContentVersion,
|
||||
parseVersion,
|
||||
promiseExec,
|
||||
} from '../config/util';
|
||||
import { TASK_COMMAND } from '../config/const';
|
||||
import taskLimit from '../shared/pLimit';
|
||||
import tar from 'tar';
|
||||
import path from 'path';
|
||||
|
||||
@Service()
|
||||
export default class SystemService {
|
||||
@@ -26,22 +36,22 @@ export default class SystemService {
|
||||
@Inject('logger') private logger: winston.Logger,
|
||||
private scheduleService: ScheduleService,
|
||||
private sockService: SockService,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
public async getLogRemoveFrequency() {
|
||||
const doc = await this.getDb({ type: AuthDataType.removeLogFrequency });
|
||||
return doc || {};
|
||||
public async getSystemConfig() {
|
||||
const doc = await this.getDb({ type: AuthDataType.systemConfig });
|
||||
return doc || ({} as AuthInstance);
|
||||
}
|
||||
|
||||
private async updateAuthDb(payload: AuthInfo): Promise<any> {
|
||||
private async updateAuthDb(payload: AuthInfo): Promise<AuthInstance> {
|
||||
await AuthModel.upsert({ ...payload });
|
||||
const doc = await this.getDb({ type: payload.type });
|
||||
return doc;
|
||||
}
|
||||
|
||||
public async getDb(query: any): Promise<any> {
|
||||
public async getDb(query: any): Promise<AuthInstance> {
|
||||
const doc: any = await AuthModel.findOne({ where: { ...query } });
|
||||
return doc && (doc.get({ plain: true }) as any);
|
||||
return doc && doc.get({ plain: true });
|
||||
}
|
||||
|
||||
public async updateNotificationMode(notificationInfo: NotificationInfo) {
|
||||
@@ -62,25 +72,30 @@ export default class SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
public async updateLogRemoveFrequency(frequency: number) {
|
||||
const oDoc = await this.getLogRemoveFrequency();
|
||||
public async updateSystemConfig(info: AuthModelInfo) {
|
||||
const oDoc = await this.getSystemConfig();
|
||||
const result = await this.updateAuthDb({
|
||||
...oDoc,
|
||||
type: AuthDataType.removeLogFrequency,
|
||||
info: { frequency },
|
||||
type: AuthDataType.systemConfig,
|
||||
info,
|
||||
});
|
||||
const cron = {
|
||||
id: result.id,
|
||||
name: '删除日志',
|
||||
command: `ql rmlog ${frequency}`,
|
||||
};
|
||||
await this.scheduleService.cancelIntervalTask(cron);
|
||||
if (frequency > 0) {
|
||||
this.scheduleService.createIntervalTask(cron, {
|
||||
days: frequency,
|
||||
});
|
||||
if (info.logRemoveFrequency) {
|
||||
const cron = {
|
||||
id: result.id,
|
||||
name: '删除日志',
|
||||
command: `ql rmlog ${info.logRemoveFrequency}`,
|
||||
};
|
||||
await this.scheduleService.cancelIntervalTask(cron);
|
||||
if (info.logRemoveFrequency > 0) {
|
||||
this.scheduleService.createIntervalTask(cron, {
|
||||
days: info.logRemoveFrequency,
|
||||
});
|
||||
}
|
||||
}
|
||||
return { code: 200, data: { ...cron } };
|
||||
if (info.cronConcurrency) {
|
||||
await taskLimit.setCustomLimit(info.cronConcurrency);
|
||||
}
|
||||
return { code: 200, data: info };
|
||||
}
|
||||
|
||||
public async checkUpdate() {
|
||||
@@ -96,7 +111,7 @@ export default class SystemService {
|
||||
},
|
||||
);
|
||||
lastVersionContent = await parseContentVersion(result.body);
|
||||
} catch (error) {}
|
||||
} catch (error) { }
|
||||
|
||||
if (!lastVersionContent) {
|
||||
lastVersionContent = currentVersionContent;
|
||||
@@ -142,7 +157,7 @@ export default class SystemService {
|
||||
}
|
||||
|
||||
public async updateSystem() {
|
||||
const cp = spawn('ql -l update', { shell: '/bin/bash' });
|
||||
const cp = spawn('ql -l update false', { shell: '/bin/bash' });
|
||||
|
||||
cp.stdout.on('data', (data) => {
|
||||
this.sockService.sendMessage({
|
||||
@@ -168,6 +183,33 @@ export default class SystemService {
|
||||
return { code: 200 };
|
||||
}
|
||||
|
||||
public async reloadSystem(target: 'system' | 'data') {
|
||||
const cp = spawn(`ql -l reload ${target || ''}`, { shell: '/bin/bash' });
|
||||
|
||||
cp.stdout.on('data', (data) => {
|
||||
this.sockService.sendMessage({
|
||||
type: 'reloadSystem',
|
||||
message: data.toString(),
|
||||
});
|
||||
});
|
||||
|
||||
cp.stderr.on('data', (data) => {
|
||||
this.sockService.sendMessage({
|
||||
type: 'reloadSystem',
|
||||
message: data.toString(),
|
||||
});
|
||||
});
|
||||
|
||||
cp.on('error', (err) => {
|
||||
this.sockService.sendMessage({
|
||||
type: 'reloadSystem',
|
||||
message: JSON.stringify(err),
|
||||
});
|
||||
});
|
||||
|
||||
return { code: 200 };
|
||||
}
|
||||
|
||||
public async notify({ title, content }: { title: string; content: string }) {
|
||||
const isSuccess = await this.notificationService.notify(title, content);
|
||||
if (isSuccess) {
|
||||
@@ -211,4 +253,23 @@ export default class SystemService {
|
||||
return { code: 400, message: '任务未找到' };
|
||||
}
|
||||
}
|
||||
|
||||
public async exportData(res: Response) {
|
||||
try {
|
||||
await tar.create({ gzip: true, file: config.dataTgzFile, cwd: config.rootPath }, ['data'])
|
||||
res.download(config.dataTgzFile);
|
||||
} catch (error: any) {
|
||||
return res.send({ code: 400, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
public async importData() {
|
||||
try {
|
||||
await promiseExec(`rm -rf ${path.join(config.tmpPath, 'data')}`);
|
||||
await tar.x({ file: config.dataTgzFile, cwd: config.tmpPath });
|
||||
return { code: 200 };
|
||||
} catch (error: any) {
|
||||
return { code: 400, message: error.message };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import config from '../config';
|
||||
import * as fs from 'fs';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { authenticator } from '@otplib/preset-default';
|
||||
import { AuthDataType, AuthInfo, AuthModel, LoginStatus } from '../data/auth';
|
||||
import { AuthDataType, AuthInfo, AuthModel, AuthModelInfo, LoginStatus } from '../data/auth';
|
||||
import { NotificationInfo } from '../data/notify';
|
||||
import NotificationService from './notify';
|
||||
import { Request } from 'express';
|
||||
@@ -27,7 +27,7 @@ export default class UserService {
|
||||
@Inject('logger') private logger: winston.Logger,
|
||||
private scheduleService: ScheduleService,
|
||||
private sockService: SockService,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
public async login(
|
||||
payloads: {
|
||||
@@ -119,8 +119,7 @@ export default class UserService {
|
||||
});
|
||||
await this.notificationService.notify(
|
||||
'登录通知',
|
||||
`你于${dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')}在 ${address} ${
|
||||
req.platform
|
||||
`你于${dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')}在 ${address} ${req.platform
|
||||
}端 登录成功,ip地址 ${ip}`,
|
||||
);
|
||||
await this.getLoginLog();
|
||||
@@ -148,8 +147,7 @@ export default class UserService {
|
||||
});
|
||||
await this.notificationService.notify(
|
||||
'登录通知',
|
||||
`你于${dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')}在 ${address} ${
|
||||
req.platform
|
||||
`你于${dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')}在 ${address} ${req.platform
|
||||
}端 登录失败,ip地址 ${ip}`,
|
||||
);
|
||||
await this.getLoginLog();
|
||||
@@ -187,12 +185,12 @@ export default class UserService {
|
||||
});
|
||||
}
|
||||
|
||||
public async getLoginLog(): Promise<AuthInfo[]> {
|
||||
public async getLoginLog(): Promise<Array<AuthModelInfo | undefined>> {
|
||||
const docs = await AuthModel.findAll({
|
||||
where: { type: AuthDataType.loginLog },
|
||||
});
|
||||
if (docs && docs.length > 0) {
|
||||
const result = docs.sort((a, b) => b.info.timestamp - a.info.timestamp);
|
||||
const result = docs.sort((a, b) => b.info!.timestamp! - a.info!.timestamp!);
|
||||
if (result.length > 100) {
|
||||
await AuthModel.destroy({
|
||||
where: { id: result[result.length - 1].id },
|
||||
|
||||
+32
-11
@@ -1,17 +1,38 @@
|
||||
import pLimit from "p-limit";
|
||||
import os from 'os';
|
||||
import { AuthDataType, AuthModel } from "../data/auth";
|
||||
|
||||
const cpuLimit = pLimit(os.cpus().length);
|
||||
const oneLimit = pLimit(1);
|
||||
class TaskLimit {
|
||||
private oneLimit = pLimit(1);
|
||||
private updateLogLimit = pLimit(1);
|
||||
private cpuLimit = pLimit(Math.max(os.cpus().length, 4));
|
||||
|
||||
export function runWithCpuLimit<T>(fn: () => Promise<T>): Promise<T> {
|
||||
return cpuLimit(() => {
|
||||
return fn();
|
||||
});
|
||||
constructor() {
|
||||
this.setCustomLimit();
|
||||
}
|
||||
|
||||
public async setCustomLimit(limit?: number) {
|
||||
if (limit) {
|
||||
this.cpuLimit = pLimit(limit);
|
||||
return;
|
||||
}
|
||||
const doc = await AuthModel.findOne({ where: { type: AuthDataType.systemConfig } });
|
||||
if (doc?.info?.cronConcurrency) {
|
||||
this.cpuLimit = pLimit(doc?.info?.cronConcurrency);
|
||||
}
|
||||
}
|
||||
|
||||
public runWithCpuLimit<T>(fn: () => Promise<T>): Promise<T> {
|
||||
return this.cpuLimit(fn);
|
||||
}
|
||||
|
||||
public runOneByOne<T>(fn: () => Promise<T>): Promise<T> {
|
||||
return this.oneLimit(fn);
|
||||
}
|
||||
|
||||
public updateDepLog<T>(fn: () => Promise<T>): Promise<T> {
|
||||
return this.updateLogLimit(fn);
|
||||
}
|
||||
}
|
||||
|
||||
export function runOneByOne<T>(fn: () => Promise<T>): Promise<T> {
|
||||
return oneLimit(() => {
|
||||
return fn();
|
||||
});
|
||||
}
|
||||
export default new TaskLimit();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { spawn } from 'cross-spawn';
|
||||
import { runWithCpuLimit } from "./pLimit";
|
||||
import taskLimit from "./pLimit";
|
||||
import Logger from '../loaders/logger';
|
||||
|
||||
export function runCron(cmd: string): Promise<number> {
|
||||
return runWithCpuLimit(() => {
|
||||
return taskLimit.runWithCpuLimit(() => {
|
||||
return new Promise(async (resolve: any) => {
|
||||
Logger.silly('运行命令: ' + cmd);
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ dir_shell=/ql/shell
|
||||
. $dir_shell/share.sh
|
||||
link_shell
|
||||
|
||||
export isFirstStartServer=true
|
||||
|
||||
echo -e "======================1. 检测配置文件========================\n"
|
||||
make_dir /etc/nginx/conf.d
|
||||
make_dir /run/nginx
|
||||
@@ -25,7 +23,10 @@ if [[ "$is_equal_registry" == "" ]]; then
|
||||
cd && pnpm config set registry $NpmMirror
|
||||
pnpm install -g
|
||||
fi
|
||||
update_depend
|
||||
if [[ ! -s $dir_scripts/package.json ]] || [[ $(diff $dir_sample/package.json $dir_scripts/package.json) ]]; then
|
||||
cp -f $dir_sample/package.json $dir_scripts/package.json
|
||||
npm_install_2 $dir_scripts
|
||||
fi
|
||||
echo
|
||||
|
||||
echo -e "======================3. 启动nginx========================\n"
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ http {
|
||||
|
||||
server_tokens off;
|
||||
|
||||
client_max_body_size 20m;
|
||||
client_max_body_size 4096m;
|
||||
client_body_buffer_size 20m;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
+6
-2
@@ -91,6 +91,7 @@
|
||||
"serve-handler": "^6.1.3",
|
||||
"sockjs": "^0.3.24",
|
||||
"sqlite3": "git+https://github.com/whyour/node-sqlite3.git#v1.0.3",
|
||||
"tar": "^6.1.15",
|
||||
"toad-scheduler": "^1.6.0",
|
||||
"typedi": "^0.10.0",
|
||||
"uuid": "^8.3.2",
|
||||
@@ -108,6 +109,7 @@
|
||||
"@types/cross-spawn": "^6.0.2",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/express-jwt": "^6.0.4",
|
||||
"@types/file-saver": "^2.0.5",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/jsonwebtoken": "^8.5.8",
|
||||
"@types/lodash": "^4.14.185",
|
||||
@@ -123,15 +125,18 @@
|
||||
"@types/serve-handler": "^6.1.1",
|
||||
"@types/sockjs": "^0.3.33",
|
||||
"@types/sockjs-client": "^1.5.1",
|
||||
"@types/tar": "^6.1.5",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@umijs/max": "^4.0.55",
|
||||
"@umijs/max": "^4.0.72",
|
||||
"@umijs/ssr-darkreader": "^4.9.45",
|
||||
"ansi-to-react": "^6.1.6",
|
||||
"antd": "^4.24.8",
|
||||
"antd-img-crop": "^4.2.3",
|
||||
"axios": "^1.4.0",
|
||||
"codemirror": "^5.65.2",
|
||||
"compression-webpack-plugin": "9.2.0",
|
||||
"concurrently": "^7.0.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"lint-staged": "^13.0.3",
|
||||
"monaco-editor": "0.33.0",
|
||||
"nodemon": "^2.0.15",
|
||||
@@ -154,7 +159,6 @@
|
||||
"tslib": "^2.4.0",
|
||||
"tsx": "^3.12.3",
|
||||
"typescript": "4.8.4",
|
||||
"umi-request": "^1.4.0",
|
||||
"vh-check": "^2.0.5",
|
||||
"virtualizedtableforantd4": "1.3.0",
|
||||
"webpack": "^5.70.0",
|
||||
|
||||
Generated
+152
-124
@@ -109,6 +109,9 @@ dependencies:
|
||||
sqlite3:
|
||||
specifier: git+https://github.com/whyour/node-sqlite3.git#v1.0.3
|
||||
version: github.com/whyour/node-sqlite3/3a00af0b5d7603b7f1b290032507320b18a6b741
|
||||
tar:
|
||||
specifier: ^6.1.15
|
||||
version: 6.1.15
|
||||
toad-scheduler:
|
||||
specifier: ^1.6.0
|
||||
version: 1.6.1
|
||||
@@ -156,6 +159,9 @@ devDependencies:
|
||||
'@types/express-jwt':
|
||||
specifier: ^6.0.4
|
||||
version: 6.0.4
|
||||
'@types/file-saver':
|
||||
specifier: ^2.0.5
|
||||
version: 2.0.5
|
||||
'@types/js-yaml':
|
||||
specifier: ^4.0.5
|
||||
version: 4.0.5
|
||||
@@ -201,12 +207,15 @@ devDependencies:
|
||||
'@types/sockjs-client':
|
||||
specifier: ^1.5.1
|
||||
version: 1.5.1
|
||||
'@types/tar':
|
||||
specifier: ^6.1.5
|
||||
version: 6.1.5
|
||||
'@types/uuid':
|
||||
specifier: ^8.3.4
|
||||
version: 8.3.4
|
||||
'@umijs/max':
|
||||
specifier: ^4.0.55
|
||||
version: 4.0.70(@types/node@17.0.45)(@types/react-dom@18.2.4)(@types/react@18.2.8)(prettier@2.8.8)(react-dom@18.2.0)(react@18.2.0)(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1)
|
||||
specifier: ^4.0.72
|
||||
version: 4.0.72(@types/node@17.0.45)(@types/react-dom@18.2.4)(@types/react@18.2.8)(prettier@2.8.8)(react-dom@18.2.0)(react@18.2.0)(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1)
|
||||
'@umijs/ssr-darkreader':
|
||||
specifier: ^4.9.45
|
||||
version: 4.9.45
|
||||
@@ -219,6 +228,9 @@ devDependencies:
|
||||
antd-img-crop:
|
||||
specifier: ^4.2.3
|
||||
version: 4.12.2(antd@4.24.10)(react-dom@18.2.0)(react@18.2.0)
|
||||
axios:
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
codemirror:
|
||||
specifier: ^5.65.2
|
||||
version: 5.65.13
|
||||
@@ -228,6 +240,9 @@ devDependencies:
|
||||
concurrently:
|
||||
specifier: ^7.0.0
|
||||
version: 7.6.0
|
||||
file-saver:
|
||||
specifier: ^2.0.5
|
||||
version: 2.0.5
|
||||
lint-staged:
|
||||
specifier: ^13.0.3
|
||||
version: 13.2.2
|
||||
@@ -294,9 +309,6 @@ devDependencies:
|
||||
typescript:
|
||||
specifier: 4.8.4
|
||||
version: 4.8.4
|
||||
umi-request:
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
vh-check:
|
||||
specifier: ^2.0.5
|
||||
version: 2.0.5
|
||||
@@ -3450,7 +3462,7 @@ packages:
|
||||
react:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
'@babel/runtime': 7.22.3
|
||||
hoist-non-react-statics: 3.3.2
|
||||
react: 18.1.0
|
||||
react-is: 16.13.1
|
||||
@@ -3465,7 +3477,7 @@ packages:
|
||||
react:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
'@babel/runtime': 7.22.3
|
||||
hoist-non-react-statics: 3.3.2
|
||||
react: 18.2.0
|
||||
react-is: 16.13.1
|
||||
@@ -3879,7 +3891,7 @@ packages:
|
||||
postcss:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/core': 7.21.0
|
||||
'@babel/core': 7.22.1
|
||||
postcss: 8.4.24
|
||||
postcss-syntax: 0.36.2(postcss@8.4.24)
|
||||
transitivePeerDependencies:
|
||||
@@ -4248,6 +4260,10 @@ packages:
|
||||
'@types/qs': 6.9.7
|
||||
'@types/serve-static': 1.15.1
|
||||
|
||||
/@types/file-saver@2.0.5:
|
||||
resolution: {integrity: sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==}
|
||||
dev: true
|
||||
|
||||
/@types/graceful-fs@4.1.6:
|
||||
resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==}
|
||||
dependencies:
|
||||
@@ -4457,6 +4473,13 @@ packages:
|
||||
'@types/node': 17.0.45
|
||||
dev: true
|
||||
|
||||
/@types/tar@6.1.5:
|
||||
resolution: {integrity: sha512-qm2I/RlZij5RofuY7vohTpYNaYcrSQlN2MyjucQc7ZweDwaEWkdN/EeNh6e9zjK6uEm6PwjdMXkcj05BxZdX1Q==}
|
||||
dependencies:
|
||||
'@types/node': 17.0.45
|
||||
minipass: 4.2.8
|
||||
dev: true
|
||||
|
||||
/@types/triple-beam@1.3.2:
|
||||
resolution: {integrity: sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==}
|
||||
dev: false
|
||||
@@ -4692,21 +4715,21 @@ packages:
|
||||
eslint-visitor-keys: 3.4.1
|
||||
dev: true
|
||||
|
||||
/@umijs/ast@4.0.70:
|
||||
resolution: {integrity: sha512-scrAlEGzgD3Ks/cRSJZza5QCPsdnZdtgPNcgpPU8xV4mXyWGyg98u9o2EE08awQDjqlPbHIvo7ZVZkvcC9nxnQ==}
|
||||
/@umijs/ast@4.0.72:
|
||||
resolution: {integrity: sha512-WatRvU09vsx4Hlu5hemPA7a+QK4pJvzmQz/9LxN/KVgn+wZXi717qHFLu5eoV6XO7HlFZaEBGq2aHpDj0ngA8w==}
|
||||
dependencies:
|
||||
'@umijs/bundler-utils': 4.0.70
|
||||
'@umijs/bundler-utils': 4.0.72
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@umijs/babel-preset-umi@4.0.70:
|
||||
resolution: {integrity: sha512-xF2KHPw3VH65nziiC8Bu68dQsm2+/DNqYC4upgEyiuNwsLx4P+yzffFGkKfDsEpWAtYqAfClDT9m6o46inRxrA==}
|
||||
/@umijs/babel-preset-umi@4.0.72:
|
||||
resolution: {integrity: sha512-9L2zwcux8iMOD9ji6YK1kiFbA9ZI1o0O/9NJo69QdCv3N41ENXWeNfXVu72GD6+mytmKZEWNCtD0qAzBTXj5jQ==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
'@bloomberg/record-tuple-polyfill': 0.0.4
|
||||
'@umijs/bundler-utils': 4.0.70
|
||||
'@umijs/utils': 4.0.70
|
||||
'@umijs/bundler-utils': 4.0.72
|
||||
'@umijs/utils': 4.0.72
|
||||
babel-plugin-styled-components: 2.1.1
|
||||
core-js: 3.28.0
|
||||
transitivePeerDependencies:
|
||||
@@ -4714,12 +4737,12 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@umijs/bundler-esbuild@4.0.70:
|
||||
resolution: {integrity: sha512-elDtAGD/sVgY626E6OfhSmZbgXYmYBIe1uiTunQrbWzlHUP2lQ9iB4wJ6GGcoqNU/7itKXRf3rcihCIq2DCDtQ==}
|
||||
/@umijs/bundler-esbuild@4.0.72:
|
||||
resolution: {integrity: sha512-T7nonD78F6RG94xATF5n/KkdJCOVYukokGFDAd4nPTNhbdYVakgNqwpRVwLEFofYMAN9uJ7rIUKFEt3qMpFR7w==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@umijs/bundler-utils': 4.0.70
|
||||
'@umijs/utils': 4.0.70
|
||||
'@umijs/bundler-utils': 4.0.72
|
||||
'@umijs/utils': 4.0.72
|
||||
enhanced-resolve: 5.9.3
|
||||
postcss: 8.4.24
|
||||
postcss-flexbugs-fixes: 5.0.2(postcss@8.4.24)
|
||||
@@ -4728,10 +4751,10 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@umijs/bundler-utils@4.0.70:
|
||||
resolution: {integrity: sha512-ZvM2Ga+BoHo8OonrmptCR1Bo/mjbtbXJVJmMQCSrb/mtn2ZFvOGddZ/0YTL+ysXnBIA7vALnlNhGWnvArCls6w==}
|
||||
/@umijs/bundler-utils@4.0.72:
|
||||
resolution: {integrity: sha512-ROGNx6dy3tiMwhC29F6xvWC9O3F4CXnND2raupljTk+QDuvc1hmwUiB/gmCWrts/98cKN2959js03ivIPn9NNw==}
|
||||
dependencies:
|
||||
'@umijs/utils': 4.0.70
|
||||
'@umijs/utils': 4.0.72
|
||||
esbuild: 0.17.19
|
||||
regenerate: 1.4.2
|
||||
regenerate-unicode-properties: 10.1.0
|
||||
@@ -4740,13 +4763,13 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@umijs/bundler-vite@4.0.70(@types/node@17.0.45):
|
||||
resolution: {integrity: sha512-19aDfNxPbOVfFttNSHEp9DnZFB/fFMEpsH6nBPkOIkXhr0UnmaWeOk7HJPbbT9T7vBA0mPxHA/vEIZPSWy84PQ==}
|
||||
/@umijs/bundler-vite@4.0.72(@types/node@17.0.45):
|
||||
resolution: {integrity: sha512-dsinf6yMW66ZAijHYTrNgzwPfSBXtEd+UxsB2gZFgsdVDDvE19Htuee4HqBCGBr+W66SLn99addckT8mzivfiA==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@svgr/core': 6.5.1
|
||||
'@umijs/bundler-utils': 4.0.70
|
||||
'@umijs/utils': 4.0.70
|
||||
'@umijs/bundler-utils': 4.0.72
|
||||
'@umijs/utils': 4.0.72
|
||||
'@vitejs/plugin-react': 4.0.0(vite@4.3.1)
|
||||
less: 4.1.3
|
||||
postcss-preset-env: 7.5.0(postcss@8.4.24)
|
||||
@@ -4763,8 +4786,8 @@ packages:
|
||||
- terser
|
||||
dev: true
|
||||
|
||||
/@umijs/bundler-webpack@4.0.70(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1):
|
||||
resolution: {integrity: sha512-QHaIEFesPzFSHnIMhHui9Ru54VYwNdnO683CzSoIoBjYAIcpDsGM7Tl3hIesujbfhrZl2eCJQVc//ZJ0SEFiTw==}
|
||||
/@umijs/bundler-webpack@4.0.72(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1):
|
||||
resolution: {integrity: sha512-0oTvna4AdMoSvRWeF0E8u8/aKASwll226DkLKvjYDs5FE9nhbxIHevsoSRX7TnlEDPNfV/a3kVRnRG1dR2oJnQ==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@pmmmwh/react-refresh-webpack-plugin': 0.5.10(react-refresh@0.14.0)(sockjs-client@1.6.1)(webpack@5.85.1)
|
||||
@@ -4772,11 +4795,11 @@ packages:
|
||||
'@svgr/plugin-jsx': 6.5.1(@svgr/core@6.5.1)
|
||||
'@svgr/plugin-svgo': 6.5.1(@svgr/core@6.5.1)
|
||||
'@types/hapi__joi': 17.1.9
|
||||
'@umijs/babel-preset-umi': 4.0.70
|
||||
'@umijs/bundler-utils': 4.0.70
|
||||
'@umijs/babel-preset-umi': 4.0.72
|
||||
'@umijs/bundler-utils': 4.0.72
|
||||
'@umijs/case-sensitive-paths-webpack-plugin': 1.0.1
|
||||
'@umijs/mfsu': 4.0.70
|
||||
'@umijs/utils': 4.0.70
|
||||
'@umijs/mfsu': 4.0.72
|
||||
'@umijs/utils': 4.0.72
|
||||
cors: 2.8.5
|
||||
css-loader: 6.7.1(webpack@5.85.1)
|
||||
es5-imcompatible-versions: 0.1.83
|
||||
@@ -4805,11 +4828,11 @@ packages:
|
||||
resolution: {integrity: sha512-kDKJ8yTarxwxGJDInG33hOpaQRZ//XpNuuznQ/1Mscypw6kappzFmrBr2dOYave++K7JHouoANF354UpbEQw0Q==}
|
||||
dev: true
|
||||
|
||||
/@umijs/core@4.0.70:
|
||||
resolution: {integrity: sha512-l2Hv8dRAJ6F9FD7VCUBAD2ars+yRBf7woQu8O88cWOgLbI/YOYEnt1n74g0uTfQBFdvnKNAsZhTY8yX8i0olGQ==}
|
||||
/@umijs/core@4.0.72:
|
||||
resolution: {integrity: sha512-E4+V/SuM8hcnmX/B+phU24LtNvV5Y7DY2ggtkQbJXtEGVFIImeC2ZEEWvGCFzq3soRCCTYG3Iwun4/TpInWIdg==}
|
||||
dependencies:
|
||||
'@umijs/bundler-utils': 4.0.70
|
||||
'@umijs/utils': 4.0.70
|
||||
'@umijs/bundler-utils': 4.0.72
|
||||
'@umijs/utils': 4.0.72
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -4850,6 +4873,7 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@@ -4859,6 +4883,7 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@@ -4868,6 +4893,7 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@@ -4877,6 +4903,7 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@@ -4917,19 +4944,19 @@ packages:
|
||||
/@umijs/history@5.3.1:
|
||||
resolution: {integrity: sha512-/e0cEGrR2bIWQD7pRl3dl9dcyRGeC9hoW0OCvUTT/hjY0EfUrkd6G8ZanVghPMpDuY5usxq9GVcvrT8KNXLWvA==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
'@babel/runtime': 7.22.3
|
||||
query-string: 6.14.1
|
||||
dev: true
|
||||
|
||||
/@umijs/lint@4.0.70(eslint@8.35.0)(stylelint@14.8.2)(typescript@4.8.4):
|
||||
resolution: {integrity: sha512-89+1BC/taDfEcubrWGXI6Yzk6hVb3br21jx+7eYYOwJjOXDMULy3+8GCFqZN+TxIz9WXOG3NFHehcFehx9YPwg==}
|
||||
/@umijs/lint@4.0.72(eslint@8.35.0)(stylelint@14.8.2)(typescript@4.8.4):
|
||||
resolution: {integrity: sha512-kH3L81Rex+jj5WeyJjR2G6yI1/0KFpr91ZtXeMy9Iyd4G7mEUJl3Fl/9iUEwZ2sgUa7kEJ+28H43eER5W4A6bg==}
|
||||
dependencies:
|
||||
'@babel/core': 7.21.0
|
||||
'@babel/eslint-parser': 7.19.1(@babel/core@7.21.0)(eslint@8.35.0)
|
||||
'@stylelint/postcss-css-in-js': 0.38.0(postcss-syntax@0.36.2)(postcss@8.4.24)
|
||||
'@typescript-eslint/eslint-plugin': 5.48.1(@typescript-eslint/parser@5.48.1)(eslint@8.35.0)(typescript@4.8.4)
|
||||
'@typescript-eslint/parser': 5.48.1(eslint@8.35.0)(typescript@4.8.4)
|
||||
'@umijs/babel-preset-umi': 4.0.70
|
||||
'@umijs/babel-preset-umi': 4.0.72
|
||||
eslint-plugin-jest: 27.2.1(@typescript-eslint/eslint-plugin@5.48.1)(eslint@8.35.0)(typescript@4.8.4)
|
||||
eslint-plugin-react: 7.32.2(eslint@8.35.0)
|
||||
eslint-plugin-react-hooks: 4.6.0(eslint@8.35.0)
|
||||
@@ -4950,16 +4977,16 @@ packages:
|
||||
- typescript
|
||||
dev: true
|
||||
|
||||
/@umijs/max@4.0.70(@types/node@17.0.45)(@types/react-dom@18.2.4)(@types/react@18.2.8)(prettier@2.8.8)(react-dom@18.2.0)(react@18.2.0)(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1):
|
||||
resolution: {integrity: sha512-5Fsob8SV1OLLunQOs4jmRQfCbp/pefkmaRkDEIisklBkSdEz+Oj6fa+TzijBoSR1h71aHg1iJIu5UWLRDOhKSg==}
|
||||
/@umijs/max@4.0.72(@types/node@17.0.45)(@types/react-dom@18.2.4)(@types/react@18.2.8)(prettier@2.8.8)(react-dom@18.2.0)(react@18.2.0)(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1):
|
||||
resolution: {integrity: sha512-5e5BwaSBCdGWlj0PZ2I73+WLLe/8AibgDt2OtXc86YujRkhSKGY740pYgqy2PAszV8MjDMg8Sqs+gZG13C5z8w==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@umijs/lint': 4.0.70(eslint@8.35.0)(stylelint@14.8.2)(typescript@4.8.4)
|
||||
'@umijs/plugins': 4.0.70(@types/react-dom@18.2.4)(@types/react@18.2.8)(antd@4.24.10)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@umijs/lint': 4.0.72(eslint@8.35.0)(stylelint@14.8.2)(typescript@4.8.4)
|
||||
'@umijs/plugins': 4.0.72(@types/react-dom@18.2.4)(@types/react@18.2.8)(antd@4.24.10)(react-dom@18.2.0)(react@18.2.0)
|
||||
antd: 4.24.10(react-dom@18.2.0)(react@18.2.0)
|
||||
eslint: 8.35.0
|
||||
stylelint: 14.8.2
|
||||
umi: 4.0.70(@types/node@17.0.45)(@types/react@18.2.8)(eslint@8.35.0)(prettier@2.8.8)(react-dom@18.2.0)(react@18.2.0)(sockjs-client@1.6.1)(stylelint@14.8.2)(typescript@4.8.4)(webpack@5.85.1)
|
||||
umi: 4.0.72(@types/node@17.0.45)(@types/react@18.2.8)(eslint@8.35.0)(prettier@2.8.8)(react-dom@18.2.0)(react@18.2.0)(sockjs-client@1.6.1)(stylelint@14.8.2)(typescript@4.8.4)(webpack@5.85.1)
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- '@reduxjs/toolkit'
|
||||
@@ -5001,26 +5028,26 @@ packages:
|
||||
- webpack-plugin-serve
|
||||
dev: true
|
||||
|
||||
/@umijs/mfsu@4.0.70:
|
||||
resolution: {integrity: sha512-Kg4SfEvU90DW9Nxfr/WozWduQmGvKAWEyUUrn6ND8i3AapUA8MOYDWRVZ/61HKHBcPt9Y6ZPg2cLWSFeNk529g==}
|
||||
/@umijs/mfsu@4.0.72:
|
||||
resolution: {integrity: sha512-oyWNIRVK6/FCewMZ8jjn5ICTWWZ7VWP7javN1/zLczvPy2s3cMp5lZuN2Ca39v2Px/DfkCBKSiB/EI6Kts0G8A==}
|
||||
dependencies:
|
||||
'@umijs/bundler-esbuild': 4.0.70
|
||||
'@umijs/bundler-utils': 4.0.70
|
||||
'@umijs/utils': 4.0.70
|
||||
'@umijs/bundler-esbuild': 4.0.72
|
||||
'@umijs/bundler-utils': 4.0.72
|
||||
'@umijs/utils': 4.0.72
|
||||
enhanced-resolve: 5.9.3
|
||||
is-equal: 1.6.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@umijs/plugin-run@4.0.70:
|
||||
resolution: {integrity: sha512-9hTRdY3+UVqptiK7SFFqojGDFMrHngyPPHQNu1amxIIi2oBEim4mLq3XOGPzy4c3hA7K6BHLEZlWSsmTGpbudw==}
|
||||
/@umijs/plugin-run@4.0.72:
|
||||
resolution: {integrity: sha512-z5p5z8BNcDb5LxbbeLoB7EfDesaSxSO9zFa3IbulgGFM/qfnaPPwfUEJDYLXkggxiDhbiUP95TDvC1R6AGDWBg==}
|
||||
dependencies:
|
||||
tsx: 3.12.7
|
||||
dev: true
|
||||
|
||||
/@umijs/plugins@4.0.70(@types/react-dom@18.2.4)(@types/react@18.2.8)(antd@4.24.10)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-3Hu79VZJpwcn9HFZWH/katqrk6YryMpfMYJOdgzbdyAW4mipPt9Oa9x+ZYVWgr754kqY2X4IFqLhb3Y6+rO0Jg==}
|
||||
/@umijs/plugins@4.0.72(@types/react-dom@18.2.4)(@types/react@18.2.8)(antd@4.24.10)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-AaCEjVdvSF+ilnATisBApMYMWVsLvBD4BUjkNV3SIaKHCLpSDeVNVyIFBm4A8tWLz+tz0p25Cu7SWGuggqQdQA==}
|
||||
dependencies:
|
||||
'@ahooksjs/use-request': 2.8.15(react@18.2.0)
|
||||
'@ant-design/antd-theme-variable': 1.0.0
|
||||
@@ -5030,7 +5057,7 @@ packages:
|
||||
'@ant-design/pro-components': 2.5.5(antd@4.24.10)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@tanstack/react-query': 4.29.12(react-dom@18.2.0)(react@18.2.0)
|
||||
'@tanstack/react-query-devtools': 4.29.12(@tanstack/react-query@4.29.12)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@umijs/bundler-utils': 4.0.70
|
||||
'@umijs/bundler-utils': 4.0.72
|
||||
'@umijs/valtio': 1.0.3(react@18.2.0)
|
||||
antd-dayjs-webpack-plugin: 1.0.6(dayjs@1.11.8)
|
||||
axios: 0.27.2
|
||||
@@ -5067,28 +5094,28 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@umijs/preset-umi@4.0.70(@types/node@17.0.45)(@types/react@18.2.8)(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1):
|
||||
resolution: {integrity: sha512-N9TQbuaZNz+3HTtXm1QG+LpALJx/XLEJk1CYfff8Ey3hgQVRtvR/hTo6EhsUtovoqdcfBClxidHAfy9dvJ9Ebw==}
|
||||
/@umijs/preset-umi@4.0.72(@types/node@17.0.45)(@types/react@18.2.8)(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1):
|
||||
resolution: {integrity: sha512-DDRzPCyP2K667YrOHrexAPcEacTdA9/TBn73erwreZpi+c4qhFZ2eYQo27SAGAhfZlYDYthL8JAfbgfhYmLo1A==}
|
||||
dependencies:
|
||||
'@iconify/utils': 2.1.1
|
||||
'@svgr/core': 6.5.1
|
||||
'@umijs/ast': 4.0.70
|
||||
'@umijs/babel-preset-umi': 4.0.70
|
||||
'@umijs/bundler-esbuild': 4.0.70
|
||||
'@umijs/bundler-utils': 4.0.70
|
||||
'@umijs/bundler-vite': 4.0.70(@types/node@17.0.45)
|
||||
'@umijs/bundler-webpack': 4.0.70(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1)
|
||||
'@umijs/core': 4.0.70
|
||||
'@umijs/ast': 4.0.72
|
||||
'@umijs/babel-preset-umi': 4.0.72
|
||||
'@umijs/bundler-esbuild': 4.0.72
|
||||
'@umijs/bundler-utils': 4.0.72
|
||||
'@umijs/bundler-vite': 4.0.72(@types/node@17.0.45)
|
||||
'@umijs/bundler-webpack': 4.0.72(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1)
|
||||
'@umijs/core': 4.0.72
|
||||
'@umijs/did-you-know': 1.0.3
|
||||
'@umijs/es-module-parser': 0.0.7
|
||||
'@umijs/history': 5.3.1
|
||||
'@umijs/mfsu': 4.0.70
|
||||
'@umijs/plugin-run': 4.0.70
|
||||
'@umijs/renderer-react': 4.0.70(react-dom@18.1.0)(react@18.1.0)
|
||||
'@umijs/server': 4.0.70
|
||||
'@umijs/mfsu': 4.0.72
|
||||
'@umijs/plugin-run': 4.0.72
|
||||
'@umijs/renderer-react': 4.0.72(react-dom@18.1.0)(react@18.1.0)
|
||||
'@umijs/server': 4.0.72
|
||||
'@umijs/ui': 3.0.1
|
||||
'@umijs/utils': 4.0.70
|
||||
'@umijs/zod2ts': 4.0.70
|
||||
'@umijs/utils': 4.0.72
|
||||
'@umijs/zod2ts': 4.0.72
|
||||
babel-plugin-dynamic-import-node: 2.3.3
|
||||
click-to-react-component: 1.0.8(@types/react@18.2.8)(react-dom@18.1.0)(react@18.1.0)
|
||||
core-js: 3.28.0
|
||||
@@ -5124,8 +5151,8 @@ packages:
|
||||
- webpack-plugin-serve
|
||||
dev: true
|
||||
|
||||
/@umijs/renderer-react@4.0.70(react-dom@18.1.0)(react@18.1.0):
|
||||
resolution: {integrity: sha512-TcqCd6uwkVyy7vvZ+yi49q/dcfxHOXihz6GJbOBkH4grkeaX2hwisJkU6RG1kwXeyv0ShIZCi4ewiCTnCSJb2g==}
|
||||
/@umijs/renderer-react@4.0.72(react-dom@18.1.0)(react@18.1.0):
|
||||
resolution: {integrity: sha512-eOJgxbwFR23wWMvR2FFcSy0Ba8d7MtOit68SE+PfX43Gy/51Ywsa1BF1G9QNgs3UAjWKK5DpK1UCHrgQ4cgegA==}
|
||||
peerDependencies:
|
||||
react: '>=16.8 || 18'
|
||||
react-dom: '>=16.8 || 18'
|
||||
@@ -5144,8 +5171,8 @@ packages:
|
||||
react-router-dom: 6.3.0(react-dom@18.1.0)(react@18.1.0)
|
||||
dev: true
|
||||
|
||||
/@umijs/renderer-react@4.0.70(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-TcqCd6uwkVyy7vvZ+yi49q/dcfxHOXihz6GJbOBkH4grkeaX2hwisJkU6RG1kwXeyv0ShIZCi4ewiCTnCSJb2g==}
|
||||
/@umijs/renderer-react@4.0.72(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-eOJgxbwFR23wWMvR2FFcSy0Ba8d7MtOit68SE+PfX43Gy/51Ywsa1BF1G9QNgs3UAjWKK5DpK1UCHrgQ4cgegA==}
|
||||
peerDependencies:
|
||||
react: '>=16.8 || 18'
|
||||
react-dom: '>=16.8 || 18'
|
||||
@@ -5177,10 +5204,10 @@ packages:
|
||||
resolution: {integrity: sha512-+1ixf1BTOLuH+ORb4x8vYMPeIt38n9q0fJDwhv9nSxrV46mxbLF0nmELIo9CKQB2gHfuC4+hww6xejJ6VYnBHQ==}
|
||||
dev: true
|
||||
|
||||
/@umijs/server@4.0.70:
|
||||
resolution: {integrity: sha512-aoTjXCe1hDjWTNxJ8c5XZRbur+H7feifG07SBe+2kc6EIpykvtRwgp7dZmMHlgmv7ptaXmSAZjWAUDdS0NhLyg==}
|
||||
/@umijs/server@4.0.72:
|
||||
resolution: {integrity: sha512-J6seC7HPZIRoDirCEelEdXzeTJsvg5FpxRkV3BP+ZS3xsg140ckGQRdCzqykJ2ZZHjhxpdJsLtdleAmE8Iq6Ew==}
|
||||
dependencies:
|
||||
'@umijs/bundler-utils': 4.0.70
|
||||
'@umijs/bundler-utils': 4.0.72
|
||||
history: 5.3.0
|
||||
react: 18.1.0
|
||||
react-dom: 18.1.0(react@18.1.0)
|
||||
@@ -5193,13 +5220,13 @@ packages:
|
||||
resolution: {integrity: sha512-XlcwzSYQ/SRZpHdwIyMDS4FOGX5kP4U/2g2mykyn/iPQTK4xTiQAyBu6UnnDnn7d5P8s7Atzh1C7H0ETNOypJg==}
|
||||
dev: true
|
||||
|
||||
/@umijs/test@4.0.70:
|
||||
resolution: {integrity: sha512-DFu65yo8QIPKvw/p0/7Tm87ViLk+fCtfbWMQ8e4o2u17mCKgmaQP6X+GCHWs94lgtRYHjM3zHjUUvM+UKynLHg==}
|
||||
/@umijs/test@4.0.72:
|
||||
resolution: {integrity: sha512-sSknbprNSQhVcyIIfOlRMay9yyG189XdmIgoJWHG0sqKOaJLt0iEJ4RkOWGv9YVnW7M06cE6+8+FGUEF8/lFXw==}
|
||||
dependencies:
|
||||
'@babel/plugin-transform-modules-commonjs': 7.21.2
|
||||
'@jest/types': 27.5.1
|
||||
'@umijs/bundler-utils': 4.0.70
|
||||
'@umijs/utils': 4.0.70
|
||||
'@umijs/bundler-utils': 4.0.72
|
||||
'@umijs/utils': 4.0.72
|
||||
babel-jest: 29.5.0
|
||||
esbuild: 0.17.19
|
||||
identity-obj-proxy: 3.0.0
|
||||
@@ -5224,8 +5251,8 @@ packages:
|
||||
react: 18.2.0
|
||||
dev: true
|
||||
|
||||
/@umijs/utils@4.0.70:
|
||||
resolution: {integrity: sha512-ZfDrtE7GtfYsdd5QwJiZHLMql8ZbyzUw37S7eCgIl5RTOxec1Ojqbzpfis7j8nyWyMGd/PpsuUl5909gA0U9bg==}
|
||||
/@umijs/utils@4.0.72:
|
||||
resolution: {integrity: sha512-+BOOGCipnr3iEzAliYrfFQeyQd3DrT1vMMXlsBqyD3Qh1owrSb/FsTvFTUYU0jrVgBc3MR5UneEBcPbqxq36Pw==}
|
||||
dependencies:
|
||||
chokidar: 3.5.3
|
||||
pino: 7.11.0
|
||||
@@ -5239,8 +5266,8 @@ packages:
|
||||
- react
|
||||
dev: true
|
||||
|
||||
/@umijs/zod2ts@4.0.70:
|
||||
resolution: {integrity: sha512-W7Uvyb9Rx3OjUuxrgTaUdkffHNH8yEVG2TW0AgEirNL0os/mC6Wp60r5lRCftgVe+sh9d6L0x2eqp05065fTzg==}
|
||||
/@umijs/zod2ts@4.0.72:
|
||||
resolution: {integrity: sha512-qjfoAT7yODzKaj9AxOM0qh8e3rpPe7xvUq6ux+0yaxNrt+JbjC6gy66S3f836NpforMBCWEOuSLbfdyVhzKxNQ==}
|
||||
dev: true
|
||||
|
||||
/@vitejs/plugin-react@4.0.0(vite@4.3.1):
|
||||
@@ -5829,6 +5856,16 @@ packages:
|
||||
- debug
|
||||
dev: true
|
||||
|
||||
/axios@1.4.0:
|
||||
resolution: {integrity: sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==}
|
||||
dependencies:
|
||||
follow-redirects: 1.15.2
|
||||
form-data: 4.0.0
|
||||
proxy-from-env: 1.1.0
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: true
|
||||
|
||||
/babel-jest@29.5.0:
|
||||
resolution: {integrity: sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
@@ -7404,8 +7441,11 @@ packages:
|
||||
|
||||
/encoding@0.1.13:
|
||||
resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
iconv-lite: 0.6.3
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/end-of-stream@1.4.4:
|
||||
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
|
||||
@@ -8085,6 +8125,10 @@ packages:
|
||||
flat-cache: 3.0.4
|
||||
dev: true
|
||||
|
||||
/file-saver@2.0.5:
|
||||
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
|
||||
dev: true
|
||||
|
||||
/file-uri-to-path@2.0.0:
|
||||
resolution: {integrity: sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -8628,7 +8672,7 @@ packages:
|
||||
/history@5.3.0:
|
||||
resolution: {integrity: sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
'@babel/runtime': 7.22.3
|
||||
dev: true
|
||||
|
||||
/hmac-drbg@1.0.1:
|
||||
@@ -9323,13 +9367,6 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/isomorphic-fetch@2.2.1:
|
||||
resolution: {integrity: sha512-9c4TNAKYXM5PRyVcwUZrF3W09nQ+sO7+jydgs4ZGW9dhsLG2VOlISJABombdQqQRXCwuYG3sYV/puGf5rp0qmA==}
|
||||
dependencies:
|
||||
node-fetch: 1.7.3
|
||||
whatwg-fetch: 3.6.2
|
||||
dev: true
|
||||
|
||||
/isomorphic-unfetch@4.0.2:
|
||||
resolution: {integrity: sha512-1Yd+CF/7al18/N2BDbsLBcp6RO3tucSW+jcLq24dqdX5MNbCNTw1z4BsGsp4zNmjr/Izm2cs/cEqZPp4kvWSCA==}
|
||||
dependencies:
|
||||
@@ -9645,6 +9682,7 @@ packages:
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@@ -9654,6 +9692,7 @@ packages:
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@@ -9663,6 +9702,7 @@ packages:
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@@ -9672,6 +9712,7 @@ packages:
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@@ -10212,6 +10253,11 @@ packages:
|
||||
yallist: 4.0.0
|
||||
dev: false
|
||||
|
||||
/minipass@4.2.8:
|
||||
resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/minipass@5.0.0:
|
||||
resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -10362,13 +10408,6 @@ packages:
|
||||
engines: {node: '>=10.5.0'}
|
||||
dev: true
|
||||
|
||||
/node-fetch@1.7.3:
|
||||
resolution: {integrity: sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==}
|
||||
dependencies:
|
||||
encoding: 0.1.13
|
||||
is-stream: 1.1.0
|
||||
dev: true
|
||||
|
||||
/node-fetch@2.6.11:
|
||||
resolution: {integrity: sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
@@ -12855,7 +12894,7 @@ packages:
|
||||
react-dom:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
'@babel/runtime': 7.22.3
|
||||
invariant: 2.2.4
|
||||
prop-types: 15.8.1
|
||||
react: 18.1.0
|
||||
@@ -12875,7 +12914,7 @@ packages:
|
||||
react-dom:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
'@babel/runtime': 7.22.3
|
||||
invariant: 2.2.4
|
||||
prop-types: 15.8.1
|
||||
react: 18.2.0
|
||||
@@ -14702,28 +14741,21 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/umi-request@1.4.0:
|
||||
resolution: {integrity: sha512-OknwtQZddZHi0Ggi+Vr/olJ7HNMx4AzlywyK0W3NZBT7B0stjeZ9lcztA85dBgdAj3KVk8uPJPZSnGaDjELhrA==}
|
||||
dependencies:
|
||||
isomorphic-fetch: 2.2.1
|
||||
qs: 6.11.2
|
||||
dev: true
|
||||
|
||||
/umi@4.0.70(@types/node@17.0.45)(@types/react@18.2.8)(eslint@8.35.0)(prettier@2.8.8)(react-dom@18.2.0)(react@18.2.0)(sockjs-client@1.6.1)(stylelint@14.8.2)(typescript@4.8.4)(webpack@5.85.1):
|
||||
resolution: {integrity: sha512-e6GwzZXC1U+XPJhLaOMIr6IBWpi8mGap6ExRkApidfbYZ8HxilvrVHnaLUYSykp206RhZBnJWI7r99mYu3e5eQ==}
|
||||
/umi@4.0.72(@types/node@17.0.45)(@types/react@18.2.8)(eslint@8.35.0)(prettier@2.8.8)(react-dom@18.2.0)(react@18.2.0)(sockjs-client@1.6.1)(stylelint@14.8.2)(typescript@4.8.4)(webpack@5.85.1):
|
||||
resolution: {integrity: sha512-VXXwhHtZAApRR02c2F+uDv84m/Bf5g56pMKrArtIUFsrWM8hqS3f7whzgpdjzh0H8EFjpXwan0kJrkXFr6dAPg==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
'@umijs/bundler-utils': 4.0.70
|
||||
'@umijs/bundler-webpack': 4.0.70(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1)
|
||||
'@umijs/core': 4.0.70
|
||||
'@umijs/lint': 4.0.70(eslint@8.35.0)(stylelint@14.8.2)(typescript@4.8.4)
|
||||
'@umijs/preset-umi': 4.0.70(@types/node@17.0.45)(@types/react@18.2.8)(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1)
|
||||
'@umijs/renderer-react': 4.0.70(react-dom@18.2.0)(react@18.2.0)
|
||||
'@umijs/server': 4.0.70
|
||||
'@umijs/test': 4.0.70
|
||||
'@umijs/utils': 4.0.70
|
||||
'@umijs/bundler-utils': 4.0.72
|
||||
'@umijs/bundler-webpack': 4.0.72(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1)
|
||||
'@umijs/core': 4.0.72
|
||||
'@umijs/lint': 4.0.72(eslint@8.35.0)(stylelint@14.8.2)(typescript@4.8.4)
|
||||
'@umijs/preset-umi': 4.0.72(@types/node@17.0.45)(@types/react@18.2.8)(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1)
|
||||
'@umijs/renderer-react': 4.0.72(react-dom@18.2.0)(react@18.2.0)
|
||||
'@umijs/server': 4.0.72
|
||||
'@umijs/test': 4.0.72
|
||||
'@umijs/utils': 4.0.72
|
||||
prettier-plugin-organize-imports: 3.2.2(prettier@2.8.8)(typescript@4.8.4)
|
||||
prettier-plugin-packagejson: 2.4.3(prettier@2.8.8)
|
||||
transitivePeerDependencies:
|
||||
@@ -15197,10 +15229,6 @@ packages:
|
||||
resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==}
|
||||
engines: {node: '>=0.8.0'}
|
||||
|
||||
/whatwg-fetch@3.6.2:
|
||||
resolution: {integrity: sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==}
|
||||
dev: true
|
||||
|
||||
/whatwg-url@5.0.0:
|
||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||
dependencies:
|
||||
|
||||
@@ -91,6 +91,10 @@ export TG_API_HOST=""
|
||||
export DD_BOT_TOKEN=""
|
||||
export DD_BOT_SECRET=""
|
||||
|
||||
## 企业微信反向代理地址
|
||||
## (环境变量名 QYWX_ORIGIN)
|
||||
export QYWX_ORIGIN=""
|
||||
|
||||
## 5. 企业微信机器人
|
||||
## 官方说明文档:https://work.weixin.qq.com/api/doc/90000/90136/91770
|
||||
## 下方填写密钥,企业微信推送 webhook 后面的 key
|
||||
|
||||
+6
-70
@@ -2,6 +2,7 @@
|
||||
|
||||
## 目录
|
||||
dir_root=$QL_DIR
|
||||
dir_tmp=$dir_root/.tmp
|
||||
dir_data=$dir_root/data
|
||||
dir_shell=$dir_root/shell
|
||||
dir_sample=$dir_root/sample
|
||||
@@ -175,6 +176,7 @@ define_cmd() {
|
||||
}
|
||||
|
||||
fix_config() {
|
||||
make_dir $dir_tmp
|
||||
make_dir $dir_static
|
||||
make_dir $dir_data
|
||||
make_dir $dir_config
|
||||
@@ -263,6 +265,7 @@ npm_install_sub() {
|
||||
else
|
||||
pnpm install --loglevel error --production
|
||||
fi
|
||||
exit_status=$?
|
||||
}
|
||||
|
||||
npm_install_2() {
|
||||
@@ -283,90 +286,22 @@ diff_and_copy() {
|
||||
fi
|
||||
}
|
||||
|
||||
update_depend() {
|
||||
local dir_current=$(pwd)
|
||||
|
||||
if [[ ! -s $dir_scripts/package.json ]] || [[ $(diff $dir_sample/package.json $dir_scripts/package.json) ]]; then
|
||||
cp -f $dir_sample/package.json $dir_scripts/package.json
|
||||
npm_install_2 $dir_scripts
|
||||
fi
|
||||
|
||||
cd $dir_current
|
||||
}
|
||||
|
||||
git_clone_scripts() {
|
||||
local url="$1"
|
||||
local dir="$2"
|
||||
local branch="$3"
|
||||
local proxy="$4"
|
||||
[[ $branch ]] && local part_cmd="-b $branch "
|
||||
echo -e "开始克隆仓库 $url 到 $dir\n"
|
||||
echo -e "开始拉取仓库 ${uniq_path} 到 $dir\n"
|
||||
|
||||
set_proxy "$proxy"
|
||||
|
||||
git clone --depth=1 $part_cmd $url $dir
|
||||
exit_status=$?
|
||||
|
||||
reset_branch "$branch" "$dir"
|
||||
|
||||
unset_proxy
|
||||
}
|
||||
|
||||
git_pull_scripts() {
|
||||
local dir_current=$(pwd)
|
||||
local dir_work="$1"
|
||||
local branch="$2"
|
||||
local proxy="$3"
|
||||
cd $dir_work
|
||||
echo -e "开始更新仓库:$dir_work"
|
||||
set_proxy "$proxy"
|
||||
|
||||
if [[ ! $branch ]]; then
|
||||
branch=$(cd $dir_work && git remote show origin | grep 'HEAD branch' | cut -d' ' -f5)
|
||||
fi
|
||||
|
||||
local pre_commit_id=$(git rev-parse --short HEAD)
|
||||
reset_branch "$branch" "$dir_work"
|
||||
|
||||
git fetch --depth 1 origin $branch
|
||||
exit_status=$?
|
||||
|
||||
reset_branch "$branch" "$dir_work"
|
||||
local cur_commit_id=$(git rev-parse --short HEAD)
|
||||
if [[ $cur_commit_id != $pre_commit_id ]]; then
|
||||
exit_status=0
|
||||
fi
|
||||
|
||||
unset_proxy
|
||||
cd $dir_current
|
||||
}
|
||||
|
||||
reset_romote_url() {
|
||||
local dir_current=$(pwd)
|
||||
local dir_work=$1
|
||||
local url=$2
|
||||
local branch="$3"
|
||||
|
||||
cd $dir_work
|
||||
if [[ -d "$dir_work/.git" ]]; then
|
||||
[[ -f ".git/index.lock" ]] && rm -f .git/index.lock >/dev/null
|
||||
git remote set-url origin $url &>/dev/null
|
||||
else
|
||||
git init
|
||||
git remote add origin $url &>/dev/null
|
||||
fi
|
||||
|
||||
cd $dir_current
|
||||
}
|
||||
|
||||
reset_branch() {
|
||||
local branch="$1"
|
||||
local part_cmd="origin/${branch}"
|
||||
git remote set-branches origin $branch
|
||||
git reset --hard $part_cmd &>/dev/null
|
||||
git checkout -b $branch $part_cmd &>/dev/null
|
||||
}
|
||||
|
||||
random_range() {
|
||||
local beg=$1
|
||||
local end=$2
|
||||
@@ -378,7 +313,8 @@ reload_pm2() {
|
||||
# 代理会影响 grpc 服务
|
||||
unset_proxy
|
||||
pm2 flush &>/dev/null
|
||||
pm2 startOrGracefulReload $file_ecosystem_js
|
||||
pm2 startOrGracefulReload $file_ecosystem_js --update-env
|
||||
pm2 sendSignal SIGKILL panel &>/dev/null
|
||||
}
|
||||
|
||||
diff_time() {
|
||||
|
||||
+70
-34
@@ -130,13 +130,9 @@ update_repo() {
|
||||
make_dir "${dir_scripts}/${uniq_path}"
|
||||
|
||||
local formatUrl="$url"
|
||||
if [[ -d ${repo_path}/.git ]]; then
|
||||
reset_romote_url ${repo_path} "${formatUrl}" "${branch}"
|
||||
git_pull_scripts ${repo_path} "${branch}" "${proxy}"
|
||||
else
|
||||
rm -rf ${repo_path} &>/dev/null
|
||||
git_clone_scripts "${formatUrl}" ${repo_path} "${branch}" "${proxy}"
|
||||
fi
|
||||
rm -rf ${repo_path} &>/dev/null
|
||||
git_clone_scripts "${formatUrl}" ${repo_path} "${branch}" "${proxy}"
|
||||
|
||||
if [[ $exit_status -eq 0 ]]; then
|
||||
echo -e "\n更新${repo_path}成功...\n"
|
||||
diff_scripts "$repo_path" "$author" "$path" "$blackword" "$dependence" "$extensions" "$autoAddCron" "$autoDelCron"
|
||||
@@ -233,62 +229,98 @@ usage() {
|
||||
echo -e "9. $cmd_update resettfa # 禁用两步登录"
|
||||
}
|
||||
|
||||
reload_qinglong() {
|
||||
local reload_target="${1}"
|
||||
local primary_branch="master"
|
||||
if [[ "${QL_BRANCH}" == "develop" ]]; then
|
||||
primary_branch="develop"
|
||||
fi
|
||||
|
||||
if [[ "$reload_target" == 'system' ]]; then
|
||||
cp -rf ${dir_tmp}/qinglong-${primary_branch}/* ${dir_root}/
|
||||
rm -rf $dir_static/*
|
||||
cp -rf ${dir_tmp}/qinglong-static-${primary_branch}/* ${dir_static}/
|
||||
cp -f $file_config_sample $dir_config/config.sample.sh
|
||||
fi
|
||||
|
||||
if [[ "$reload_target" == 'data' ]]; then
|
||||
cp -rf ${dir_tmp}/data ${dir_root}/
|
||||
fi
|
||||
|
||||
reload_pm2
|
||||
}
|
||||
|
||||
## 更新qinglong
|
||||
update_qinglong() {
|
||||
rm -rf ${dir_tmp}/*
|
||||
local mirror="gitee"
|
||||
local downloadQLUrl="https://gitee.com/whyour/qinglong/repository/archive"
|
||||
local downloadStaticUrl="https://gitee.com/whyour/qinglong-static/repository/archive"
|
||||
local githubStatus=$(curl -s -m 2 -IL "https://google.com" | grep 200)
|
||||
if [[ ! -z $githubStatus ]]; then
|
||||
mirror="github"
|
||||
downloadQLUrl="https://github.com/whyour/qinglong/archive/refs/heads"
|
||||
downloadStaticUrl="https://github.com/whyour/qinglong-static/archive/refs/heads"
|
||||
fi
|
||||
echo -e "使用 ${mirror} 源更新...\n"
|
||||
export isFirstStartServer=false
|
||||
|
||||
local primary_branch="master"
|
||||
if [[ "${QL_BRANCH}" == "develop" ]]; then
|
||||
primary_branch="develop"
|
||||
fi
|
||||
[[ -f $dir_root/package.json ]] && ql_depend_old=$(cat $dir_root/package.json)
|
||||
reset_romote_url ${dir_root} "https://${mirror}.com/whyour/qinglong.git" ${primary_branch}
|
||||
git_pull_scripts $dir_root ${primary_branch}
|
||||
|
||||
wget -cqO "${dir_tmp}/ql.zip" "${downloadQLUrl}/${primary_branch}.zip"
|
||||
exit_status=$?
|
||||
|
||||
if [[ $exit_status -eq 0 ]]; then
|
||||
echo -e "\n更新青龙源文件成功...\n"
|
||||
cp -f $file_config_sample $dir_config/config.sample.sh
|
||||
update_depend
|
||||
|
||||
[[ -f $dir_root/package.json ]] && ql_depend_new=$(cat $dir_root/package.json)
|
||||
[[ "$ql_depend_old" != "$ql_depend_new" ]] && npm_install_2 $dir_root
|
||||
unzip -oq ${dir_tmp}/ql.zip -d ${dir_tmp}
|
||||
|
||||
update_qinglong_static "$1" "$primary_branch"
|
||||
update_qinglong_static
|
||||
else
|
||||
echo -e "\n更新青龙源文件失败,请检查网络...\n"
|
||||
fi
|
||||
}
|
||||
|
||||
update_qinglong_static() {
|
||||
local no_restart="$1"
|
||||
local primary_branch="$2"
|
||||
local url="https://${mirror}.com/whyour/qinglong-static.git"
|
||||
if [[ -d ${ql_static_repo}/.git ]]; then
|
||||
reset_romote_url ${ql_static_repo} ${url} ${primary_branch}
|
||||
git_pull_scripts ${ql_static_repo} ${primary_branch}
|
||||
else
|
||||
rm -rf ${ql_static_repo} &>/dev/null
|
||||
git_clone_scripts ${url} ${ql_static_repo} ${primary_branch}
|
||||
fi
|
||||
wget -cqO "${dir_tmp}/static.zip" "${downloadStaticUrl}/${primary_branch}.zip"
|
||||
exit_status=$?
|
||||
|
||||
if [[ $exit_status -eq 0 ]]; then
|
||||
echo -e "\n更新青龙静态资源成功...\n"
|
||||
unzip -oq ${dir_tmp}/static.zip -d ${dir_tmp}
|
||||
|
||||
check_update_dep
|
||||
else
|
||||
echo -e "\n更新青龙静态资源失败,请检查网络...\n"
|
||||
fi
|
||||
}
|
||||
|
||||
check_update_dep() {
|
||||
echo -e "\n开始检测依赖...\n"
|
||||
if [[ $(diff $dir_sample/package.json $dir_scripts/package.json) ]]; then
|
||||
cp -f $dir_sample/package.json $dir_scripts/package.json
|
||||
npm_install_2 $dir_scripts
|
||||
fi
|
||||
if [[ $(diff $dir_root/package.json ${dir_tmp}/qinglong-${primary_branch}/package.json) ]]; then
|
||||
npm_install_2 "${dir_tmp}/qinglong-${primary_branch}"
|
||||
fi
|
||||
|
||||
if [[ $exit_status -eq 0 ]]; then
|
||||
echo -e "\n依赖检测安装成功...\n"
|
||||
echo -e "\n更新包下载成功...\n"
|
||||
|
||||
if [[ "$needRestart" == 'true' ]]; then
|
||||
cp -rf ${dir_tmp}/qinglong-${primary_branch}/* ${dir_root}/
|
||||
rm -rf $dir_static/*
|
||||
cp -rf ${dir_tmp}/qinglong-static-${primary_branch}/* ${dir_static}/
|
||||
cp -f $file_config_sample $dir_config/config.sample.sh
|
||||
|
||||
rm -rf $dir_static/*
|
||||
cp -rf $ql_static_repo/* $dir_static
|
||||
if [[ $no_restart != "no-restart" ]]; then
|
||||
nginx -s reload 2>/dev/null || nginx -c /etc/nginx/nginx.conf
|
||||
echo -e "重启面板中..."
|
||||
sleep 3
|
||||
reload_pm2
|
||||
fi
|
||||
else
|
||||
echo -e "\n更新青龙静态资源失败,请检查网络...\n"
|
||||
echo -e "\n依赖检测安装失败,请检查网络...\n"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -464,7 +496,11 @@ main() {
|
||||
case $p1 in
|
||||
update)
|
||||
fix_config
|
||||
eval update_qinglong "$2" $cmd
|
||||
local needRestart=${p2:-"true"}
|
||||
eval update_qinglong $cmd
|
||||
;;
|
||||
reload)
|
||||
eval reload_qinglong "$p2" $cmd
|
||||
;;
|
||||
extra)
|
||||
eval run_extra_shell $cmd
|
||||
|
||||
@@ -49,8 +49,7 @@ export interface SharedContext {
|
||||
interface TSystemInfo {
|
||||
branch: 'develop' | 'master';
|
||||
isInitialized: boolean;
|
||||
lastCommitId: string;
|
||||
lastCommitTime: number;
|
||||
publishTime: number;
|
||||
version: string;
|
||||
changeLog: string;
|
||||
changeLogLink: string;
|
||||
|
||||
@@ -51,9 +51,7 @@ const Config = () => {
|
||||
: value;
|
||||
|
||||
request
|
||||
.post(`${config.apiPrefix}configs/save`, {
|
||||
data: { content, name: select },
|
||||
})
|
||||
.post(`${config.apiPrefix}configs/save`, { content, name: select })
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
message.success('保存成功');
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
PauseCircleOutlined,
|
||||
FullscreenOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { CrontabStatus } from './index';
|
||||
import { CrontabStatus } from './type';
|
||||
import { diffTime } from '@/utils/date';
|
||||
import { request } from '@/utils/http';
|
||||
import config from '@/utils/config';
|
||||
@@ -184,11 +184,9 @@ const CronDetailModal = ({
|
||||
return new Promise((resolve, reject) => {
|
||||
request
|
||||
.put(`${config.apiPrefix}scripts`, {
|
||||
data: {
|
||||
filename: scriptInfo.filename,
|
||||
path: scriptInfo.parent || '',
|
||||
content,
|
||||
},
|
||||
filename: scriptInfo.filename,
|
||||
path: scriptInfo.parent || '',
|
||||
content,
|
||||
})
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
@@ -220,7 +218,7 @@ const CronDetailModal = ({
|
||||
),
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}crons/run`, { data: [currentCron.id] })
|
||||
.put(`${config.apiPrefix}crons/run`, [currentCron.id])
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
setCurrentCron({ ...currentCron, status: CrontabStatus.running });
|
||||
@@ -250,7 +248,7 @@ const CronDetailModal = ({
|
||||
),
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}crons/stop`, { data: [currentCron.id] })
|
||||
.put(`${config.apiPrefix}crons/stop`, [currentCron.id] )
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
setCurrentCron({ ...currentCron, status: CrontabStatus.idle });
|
||||
@@ -282,9 +280,7 @@ const CronDetailModal = ({
|
||||
`${config.apiPrefix}crons/${
|
||||
currentCron.isDisabled === 1 ? 'enable' : 'disable'
|
||||
}`,
|
||||
{
|
||||
data: [currentCron.id],
|
||||
},
|
||||
[currentCron.id],
|
||||
)
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
@@ -320,9 +316,7 @@ const CronDetailModal = ({
|
||||
`${config.apiPrefix}crons/${
|
||||
currentCron.isPinned === 1 ? 'unpin' : 'pin'
|
||||
}`,
|
||||
{
|
||||
data: [currentCron.id],
|
||||
},
|
||||
[currentCron.id],
|
||||
)
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
|
||||
+13
-56
@@ -54,51 +54,11 @@ import useTableScrollHeight from '@/hooks/useTableScrollHeight';
|
||||
import { getCommandScript, parseCrontab } from '@/utils';
|
||||
import { ColumnProps } from 'antd/lib/table';
|
||||
import { useVT } from 'virtualizedtableforantd4';
|
||||
import { ICrontab, OperationName, OperationPath, CrontabStatus } from './type';
|
||||
|
||||
const { Text, Paragraph } = Typography;
|
||||
const { Search } = Input;
|
||||
|
||||
export enum CrontabStatus {
|
||||
'running',
|
||||
'idle',
|
||||
'disabled',
|
||||
'queued',
|
||||
}
|
||||
|
||||
const CrontabSort: any = { 0: 0, 5: 1, 3: 2, 1: 3, 4: 4 };
|
||||
|
||||
enum OperationName {
|
||||
'启用',
|
||||
'禁用',
|
||||
'运行',
|
||||
'停止',
|
||||
'置顶',
|
||||
'取消置顶',
|
||||
}
|
||||
|
||||
enum OperationPath {
|
||||
'enable',
|
||||
'disable',
|
||||
'run',
|
||||
'stop',
|
||||
'pin',
|
||||
'unpin',
|
||||
}
|
||||
|
||||
export interface ICrontab {
|
||||
name: string;
|
||||
command: string;
|
||||
schedule: string;
|
||||
id: number;
|
||||
status: number;
|
||||
isDisabled?: 1 | 0;
|
||||
isPinned?: 1 | 0;
|
||||
labels?: string[];
|
||||
last_running_time?: number;
|
||||
last_execution_time?: number;
|
||||
nextRunTime: Date;
|
||||
}
|
||||
|
||||
const Crontab = () => {
|
||||
const { headerStyle, isPhone, theme } = useOutletContext<SharedContext>();
|
||||
const columns: ColumnProps<ICrontab>[] = [
|
||||
@@ -264,19 +224,19 @@ const Crontab = () => {
|
||||
filters: [
|
||||
{
|
||||
text: '运行中',
|
||||
value: 0,
|
||||
value: CrontabStatus.running,
|
||||
},
|
||||
{
|
||||
text: '空闲中',
|
||||
value: 1,
|
||||
value: CrontabStatus.idle,
|
||||
},
|
||||
{
|
||||
text: '已禁用',
|
||||
value: 2,
|
||||
value: CrontabStatus.disabled,
|
||||
},
|
||||
{
|
||||
text: '队列中',
|
||||
value: 3,
|
||||
value: CrontabStatus.queued,
|
||||
},
|
||||
],
|
||||
render: (text, record) => (
|
||||
@@ -497,7 +457,7 @@ const Crontab = () => {
|
||||
),
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}crons/run`, { data: [record.id] })
|
||||
.put(`${config.apiPrefix}crons/run`, [record.id])
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
const result = [...value];
|
||||
@@ -532,7 +492,7 @@ const Crontab = () => {
|
||||
),
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}crons/stop`, { data: [record.id] })
|
||||
.put(`${config.apiPrefix}crons/stop`, [record.id])
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
const result = [...value];
|
||||
@@ -573,9 +533,7 @@ const Crontab = () => {
|
||||
`${config.apiPrefix}crons/${
|
||||
record.isDisabled === 1 ? 'enable' : 'disable'
|
||||
}`,
|
||||
{
|
||||
data: [record.id],
|
||||
},
|
||||
[record.id],
|
||||
)
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
@@ -617,9 +575,7 @@ const Crontab = () => {
|
||||
`${config.apiPrefix}crons/${
|
||||
record.isPinned === 1 ? 'unpin' : 'pin'
|
||||
}`,
|
||||
{
|
||||
data: [record.id],
|
||||
},
|
||||
[record.id],
|
||||
)
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
@@ -765,9 +721,10 @@ const Crontab = () => {
|
||||
content: <>确认{OperationName[operationStatus]}选中的定时任务吗</>,
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}crons/${OperationPath[operationStatus]}`, {
|
||||
data: selectedRowIds,
|
||||
})
|
||||
.put(
|
||||
`${config.apiPrefix}crons/${OperationPath[operationStatus]}`,
|
||||
selectedRowIds,
|
||||
)
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
getCrons();
|
||||
|
||||
@@ -8,13 +8,8 @@ import {
|
||||
} from '@ant-design/icons';
|
||||
import { PageLoading } from '@ant-design/pro-layout';
|
||||
import { logEnded } from '@/utils';
|
||||
import { CrontabStatus } from './type';
|
||||
|
||||
enum CrontabStatus {
|
||||
'running',
|
||||
'idle',
|
||||
'disabled',
|
||||
'queued',
|
||||
}
|
||||
const { Countdown } = Statistic;
|
||||
|
||||
const CronLogModal = ({
|
||||
@@ -51,10 +46,7 @@ const CronLogModal = ({
|
||||
const log = data as string;
|
||||
setValue(log || '暂无日志');
|
||||
const hasNext = Boolean(
|
||||
log &&
|
||||
!logEnded(log) &&
|
||||
!log.includes('重启面板') &&
|
||||
!log.includes('任务未运行'),
|
||||
log && !logEnded(log) && !log.includes('任务未运行'),
|
||||
);
|
||||
setExecuting(hasNext);
|
||||
autoScroll();
|
||||
@@ -63,29 +55,6 @@ const CronLogModal = ({
|
||||
getCronLog();
|
||||
}, 2000);
|
||||
}
|
||||
if (
|
||||
log &&
|
||||
log.includes('重启面板') &&
|
||||
cron.status === CrontabStatus.running
|
||||
) {
|
||||
message.warning({
|
||||
content: (
|
||||
<span>
|
||||
系统将在
|
||||
<Countdown
|
||||
className="inline-countdown"
|
||||
format="ss"
|
||||
value={Date.now() + 1000 * 30}
|
||||
/>
|
||||
秒后自动刷新
|
||||
</span>
|
||||
),
|
||||
duration: 10,
|
||||
});
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 30000);
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -25,9 +25,10 @@ const CronModal = ({
|
||||
payload.id = cron.id;
|
||||
}
|
||||
try {
|
||||
const { code, data } = await request[method](`${config.apiPrefix}crons`, {
|
||||
data: payload,
|
||||
});
|
||||
const { code, data } = await request[method](
|
||||
`${config.apiPrefix}crons`,
|
||||
payload,
|
||||
);
|
||||
|
||||
if (code === 200) {
|
||||
message.success(cron ? '更新Cron成功' : '新建Cron成功');
|
||||
@@ -130,9 +131,7 @@ const CronLabelModal = ({
|
||||
try {
|
||||
const { code, data } = await request[action](
|
||||
`${config.apiPrefix}crons/labels`,
|
||||
{
|
||||
data: payload,
|
||||
},
|
||||
payload,
|
||||
);
|
||||
|
||||
if (code === 200) {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
export enum CrontabStatus {
|
||||
'running' = 0,
|
||||
'queued' = 0.5,
|
||||
'idle' = 1,
|
||||
'disabled',
|
||||
}
|
||||
|
||||
export enum OperationName {
|
||||
'启用',
|
||||
'禁用',
|
||||
'运行',
|
||||
'停止',
|
||||
'置顶',
|
||||
'取消置顶',
|
||||
}
|
||||
|
||||
export enum OperationPath {
|
||||
'enable',
|
||||
'disable',
|
||||
'run',
|
||||
'stop',
|
||||
'pin',
|
||||
'unpin',
|
||||
}
|
||||
|
||||
export interface ICrontab {
|
||||
name: string;
|
||||
command: string;
|
||||
schedule: string;
|
||||
id: number;
|
||||
status: number;
|
||||
isDisabled?: 1 | 0;
|
||||
isPinned?: 1 | 0;
|
||||
labels?: string[];
|
||||
last_running_time?: number;
|
||||
last_execution_time?: number;
|
||||
nextRunTime: Date;
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import config from '@/utils/config';
|
||||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import IconFont from '@/components/iconfont';
|
||||
import get from 'lodash/get';
|
||||
import { CrontabStatus } from './type';
|
||||
|
||||
const PROPERTIES = [
|
||||
{ name: '命令', value: 'command' },
|
||||
@@ -47,9 +48,9 @@ const SORTTYPES = [
|
||||
|
||||
const STATUS_MAP = {
|
||||
status: [
|
||||
{ name: '运行中', value: 0 },
|
||||
{ name: '空闲中', value: 1 },
|
||||
{ name: '已禁用', value: 2 },
|
||||
{ name: '运行中', value: CrontabStatus.running },
|
||||
{ name: '空闲中', value: CrontabStatus.idle },
|
||||
{ name: '已禁用', value: CrontabStatus.disabled },
|
||||
],
|
||||
};
|
||||
|
||||
@@ -79,9 +80,7 @@ const ViewCreateModal = ({
|
||||
try {
|
||||
const { code, data } = await request[method](
|
||||
`${config.apiPrefix}crons/views`,
|
||||
{
|
||||
data: view ? { ...values, id: view.id } : values,
|
||||
},
|
||||
view ? { ...values, id: view.id } : values,
|
||||
);
|
||||
|
||||
if (code === 200) {
|
||||
|
||||
@@ -168,9 +168,9 @@ const ViewManageModal = ({
|
||||
|
||||
const onShowChange = (checked: boolean, record: any, index: number) => {
|
||||
request
|
||||
.put(`${config.apiPrefix}crons/views/${checked ? 'enable' : 'disable'}`, {
|
||||
data: [record.id],
|
||||
})
|
||||
.put(`${config.apiPrefix}crons/views/${checked ? 'enable' : 'disable'}`, [
|
||||
record.id,
|
||||
])
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
const _list = [...list];
|
||||
@@ -195,7 +195,9 @@ const ViewManageModal = ({
|
||||
const dragRow = list[dragIndex];
|
||||
request
|
||||
.put(`${config.apiPrefix}crons/views/move`, {
|
||||
data: { fromIndex: dragIndex, toIndex: hoverIndex, id: dragRow.id },
|
||||
fromIndex: dragIndex,
|
||||
toIndex: hoverIndex,
|
||||
id: dragRow.id,
|
||||
})
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
|
||||
@@ -33,6 +33,7 @@ import DependenceLogModal from './logModal';
|
||||
import { useOutletContext } from '@umijs/max';
|
||||
import { SharedContext } from '@/layouts';
|
||||
import useTableScrollHeight from '@/hooks/useTableScrollHeight';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const { Text } = Typography;
|
||||
const { Search } = Input;
|
||||
@@ -123,27 +124,20 @@ const Dependence = () => {
|
||||
dataIndex: 'remark',
|
||||
key: 'remark',
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
key: 'updatedAt',
|
||||
dataIndex: 'updatedAt',
|
||||
render: (text: string) => {
|
||||
return <span>{dayjs(text).format('YYYY-MM-DD HH:mm:ss')}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'timestamp',
|
||||
dataIndex: 'timestamp',
|
||||
render: (text: string, record: any) => {
|
||||
const language = navigator.language || navigator.languages[0];
|
||||
const time = record.createdAt || record.timestamp;
|
||||
const date = new Date(time)
|
||||
.toLocaleString(language, {
|
||||
hour12: false,
|
||||
})
|
||||
.replace(' 24:', ' 00:');
|
||||
return (
|
||||
<Tooltip
|
||||
placement="topLeft"
|
||||
title={date}
|
||||
trigger={['hover', 'click']}
|
||||
>
|
||||
<span>{date}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
key: 'createdAt',
|
||||
dataIndex: 'createdAt',
|
||||
render: (text: string) => {
|
||||
return <span>{dayjs(text).format('YYYY-MM-DD HH:mm:ss')}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -275,9 +269,7 @@ const Dependence = () => {
|
||||
),
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}dependencies/reinstall`, {
|
||||
data: [record.id],
|
||||
})
|
||||
.put(`${config.apiPrefix}dependencies/reinstall`, [record.id])
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
handleDependence(data[0]);
|
||||
@@ -348,9 +340,7 @@ const Dependence = () => {
|
||||
content: <>确认重新安装选中的依赖吗</>,
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}dependencies/reinstall`, {
|
||||
data: selectedRowIds,
|
||||
})
|
||||
.put(`${config.apiPrefix}dependencies/reinstall`, selectedRowIds)
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
setSelectedRowIds([]);
|
||||
|
||||
@@ -48,9 +48,7 @@ const DependenceModal = ({
|
||||
try {
|
||||
const { code, data } = await request[method](
|
||||
`${config.apiPrefix}dependencies`,
|
||||
{
|
||||
data: payload,
|
||||
},
|
||||
payload,
|
||||
);
|
||||
|
||||
if (code === 200) {
|
||||
@@ -122,7 +120,11 @@ const DependenceModal = ({
|
||||
name="name"
|
||||
label="名称"
|
||||
rules={[
|
||||
{ required: true, message: '请输入依赖名称', whitespace: true },
|
||||
{
|
||||
required: true,
|
||||
message: '请输入依赖名称,支持指定版本',
|
||||
whitespace: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input.TextArea
|
||||
|
||||
@@ -48,7 +48,8 @@ const Diff = () => {
|
||||
|
||||
request
|
||||
.post(`${config.apiPrefix}configs/save`, {
|
||||
data: { content, name: current },
|
||||
content,
|
||||
name: current,
|
||||
})
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
|
||||
Vendored
+2
-4
@@ -19,10 +19,8 @@ const EditNameModal = ({
|
||||
setLoading(true);
|
||||
try {
|
||||
const { code, data } = await request.put(`${config.apiPrefix}envs/name`, {
|
||||
data: {
|
||||
ids,
|
||||
name: values.name,
|
||||
},
|
||||
ids,
|
||||
name: values.name,
|
||||
});
|
||||
|
||||
if (code === 200) {
|
||||
|
||||
Vendored
+8
-10
@@ -248,9 +248,7 @@ const Env = () => {
|
||||
`${config.apiPrefix}envs/${
|
||||
record.status === Status.已禁用 ? 'enable' : 'disable'
|
||||
}`,
|
||||
{
|
||||
data: [record.id],
|
||||
},
|
||||
[record.id],
|
||||
)
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
@@ -388,7 +386,8 @@ const Env = () => {
|
||||
const dragRow = value[dragIndex];
|
||||
request
|
||||
.put(`${config.apiPrefix}envs/${dragRow.id}/move`, {
|
||||
data: { fromIndex: dragIndex, toIndex: hoverIndex },
|
||||
fromIndex: dragIndex,
|
||||
toIndex: hoverIndex,
|
||||
})
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
@@ -438,9 +437,10 @@ const Env = () => {
|
||||
content: <>确认{OperationName[operationStatus]}选中的变量吗</>,
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}envs/${OperationPath[operationStatus]}`, {
|
||||
data: selectedRowIds,
|
||||
})
|
||||
.put(
|
||||
`${config.apiPrefix}envs/${OperationPath[operationStatus]}`,
|
||||
selectedRowIds,
|
||||
)
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
getEnvs();
|
||||
@@ -477,9 +477,7 @@ const Env = () => {
|
||||
try {
|
||||
const { code, data } = await request.post(
|
||||
`${config.apiPrefix}envs/upload`,
|
||||
{
|
||||
data: formData,
|
||||
},
|
||||
formData,
|
||||
);
|
||||
|
||||
if (code === 200) {
|
||||
|
||||
Vendored
+4
-3
@@ -37,9 +37,10 @@ const EnvModal = ({
|
||||
payload = { ...values, id: env.id };
|
||||
}
|
||||
try {
|
||||
const { code, data } = await request[method](`${config.apiPrefix}envs`, {
|
||||
data: payload,
|
||||
});
|
||||
const { code, data } = await request[method](
|
||||
`${config.apiPrefix}envs`,
|
||||
payload,
|
||||
);
|
||||
|
||||
if (code === 200) {
|
||||
message.success(env ? '更新变量成功' : '新建变量成功');
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
.error-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ const Error = () => {
|
||||
</Typography.Paragraph>
|
||||
</div>
|
||||
) : (
|
||||
<PageLoading style={{ paddingTop: 0 }} tip="启动中,请稍后..." />
|
||||
<PageLoading tip="启动中,请稍后..." />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -36,10 +36,8 @@ const Initialization = () => {
|
||||
setLoading(true);
|
||||
request
|
||||
.put(`${config.apiPrefix}user/init`, {
|
||||
data: {
|
||||
username: values.username,
|
||||
password: values.password,
|
||||
},
|
||||
username: values.username,
|
||||
password: values.password,
|
||||
})
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
@@ -53,9 +51,7 @@ const Initialization = () => {
|
||||
setLoading(true);
|
||||
request
|
||||
.put(`${config.apiPrefix}user/notification/init`, {
|
||||
data: {
|
||||
...values,
|
||||
},
|
||||
values,
|
||||
})
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
|
||||
@@ -35,10 +35,8 @@ const Login = () => {
|
||||
setWaitTime(null);
|
||||
request
|
||||
.post(`${config.apiPrefix}user/login`, {
|
||||
data: {
|
||||
username: values.username,
|
||||
password: values.password,
|
||||
},
|
||||
username: values.username,
|
||||
password: values.password,
|
||||
})
|
||||
.then((data) => {
|
||||
checkResponse(data, values);
|
||||
@@ -54,7 +52,8 @@ const Login = () => {
|
||||
setVerifying(true);
|
||||
request
|
||||
.put(`${config.apiPrefix}user/two-factor/login`, {
|
||||
data: { ...loginInfo, code: values.code },
|
||||
...loginInfo,
|
||||
code: values.code,
|
||||
})
|
||||
.then((data: any) => {
|
||||
checkResponse(data);
|
||||
|
||||
@@ -86,11 +86,9 @@ const EditModal = ({
|
||||
const content = editorRef.current.getValue().replace(/\r\n/g, '\n');
|
||||
request
|
||||
.put(`${config.apiPrefix}scripts/run`, {
|
||||
data: {
|
||||
filename: cNode.title,
|
||||
path: cNode.parent || '',
|
||||
content,
|
||||
},
|
||||
filename: cNode.title,
|
||||
path: cNode.parent || '',
|
||||
content,
|
||||
})
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
@@ -106,11 +104,9 @@ const EditModal = ({
|
||||
}
|
||||
request
|
||||
.put(`${config.apiPrefix}scripts/stop`, {
|
||||
data: {
|
||||
filename: cNode.title,
|
||||
path: cNode.parent || '',
|
||||
pid: currentPid,
|
||||
},
|
||||
filename: cNode.title,
|
||||
path: cNode.parent || '',
|
||||
pid: currentPid,
|
||||
})
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
|
||||
@@ -44,9 +44,7 @@ const EditScriptNameModal = ({
|
||||
formData.append('content', '');
|
||||
formData.append('directory', directory);
|
||||
request
|
||||
.post(`${config.apiPrefix}scripts`, {
|
||||
data: formData,
|
||||
})
|
||||
.post(`${config.apiPrefix}scripts`, formData)
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
message.success(directory ? '新建文件夹成功' : '新建文件成功');
|
||||
|
||||
@@ -223,11 +223,9 @@ const Script = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
request
|
||||
.put(`${config.apiPrefix}scripts`, {
|
||||
data: {
|
||||
filename: currentNode.title,
|
||||
path: currentNode.parent || '',
|
||||
content,
|
||||
},
|
||||
filename: currentNode.title,
|
||||
path: currentNode.parent || '',
|
||||
content,
|
||||
})
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
@@ -341,9 +339,7 @@ const Script = () => {
|
||||
const downloadFile = () => {
|
||||
request
|
||||
.post(`${config.apiPrefix}scripts/download`, {
|
||||
data: {
|
||||
filename: currentNode.title,
|
||||
},
|
||||
filename: currentNode.title,
|
||||
})
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
|
||||
@@ -21,11 +21,9 @@ const RenameModal = ({
|
||||
const { code, data } = await request.put(
|
||||
`${config.apiPrefix}scripts/rename`,
|
||||
{
|
||||
data: {
|
||||
filename: currentNode.title,
|
||||
path: currentNode.parent || '',
|
||||
newFilename: values.name,
|
||||
},
|
||||
filename: currentNode.title,
|
||||
path: currentNode.parent || '',
|
||||
newFilename: values.name,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -19,9 +19,7 @@ const SaveModal = ({
|
||||
setLoading(true);
|
||||
const payload = { ...file, ...values, originFilename: file.title };
|
||||
request
|
||||
.post(`${config.apiPrefix}scripts`, {
|
||||
data: payload,
|
||||
})
|
||||
.post(`${config.apiPrefix}scripts`, payload)
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
message.success('保存文件成功');
|
||||
|
||||
@@ -19,9 +19,7 @@ const SettingModal = ({
|
||||
setLoading(true);
|
||||
const payload = { ...file, ...values };
|
||||
request
|
||||
.post(`${config.apiPrefix}scripts`, {
|
||||
data: payload,
|
||||
})
|
||||
.post(`${config.apiPrefix}scripts`, payload)
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
message.success('保存文件成功');
|
||||
|
||||
@@ -31,13 +31,10 @@ const About = ({ systemInfo }: { systemInfo: SharedContext['systemInfo'] }) => {
|
||||
{TVersion[systemInfo.branch]} v{systemInfo.version}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="更新时间" span={3}>
|
||||
{dayjs(systemInfo.lastCommitTime * 1000).format(
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
{dayjs(systemInfo.publishTime * 1000).format(
|
||||
'YYYY-MM-DD HH:mm',
|
||||
)}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="更新ID" span={3}>
|
||||
{systemInfo.lastCommitId}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="更新日志" span={3}>
|
||||
<Link
|
||||
href={`https://qn.whyour.cn/version.yaml?t=${Date.now()}`}
|
||||
|
||||
@@ -23,9 +23,10 @@ const AppModal = ({
|
||||
payload.id = app.id;
|
||||
}
|
||||
try {
|
||||
const { code, data } = await request[method](`${config.apiPrefix}apps`, {
|
||||
data: payload,
|
||||
});
|
||||
const { code, data } = await request[method](
|
||||
`${config.apiPrefix}apps`,
|
||||
payload,
|
||||
);
|
||||
|
||||
if (code === 200) {
|
||||
message.success(app ? '更新应用成功' : '新建应用成功');
|
||||
|
||||
@@ -47,7 +47,7 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
|
||||
</div>
|
||||
</>
|
||||
),
|
||||
okText: '强制更新',
|
||||
okText: '重新下载',
|
||||
onOk() {
|
||||
showUpdatingModal();
|
||||
request
|
||||
@@ -82,7 +82,7 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
|
||||
{lastLog}
|
||||
</pre>
|
||||
),
|
||||
okText: '更新',
|
||||
okText: '下载更新',
|
||||
cancelText: '以后再说',
|
||||
onOk() {
|
||||
showUpdatingModal();
|
||||
@@ -104,7 +104,7 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
|
||||
closable: false,
|
||||
keyboard: false,
|
||||
okButtonProps: { disabled: true },
|
||||
title: '更新中...',
|
||||
title: '下载更新中...',
|
||||
centered: true,
|
||||
content: (
|
||||
<pre
|
||||
@@ -119,6 +119,50 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
|
||||
});
|
||||
};
|
||||
|
||||
const showReloadModal = () => {
|
||||
Modal.confirm({
|
||||
width: 600,
|
||||
maskClosable: false,
|
||||
title: '确认重启',
|
||||
centered: true,
|
||||
content: '系统安装包下载成功,确认重启',
|
||||
okText: '重启',
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}system/reload`, { type: 'system' })
|
||||
.then((_data: any) => {
|
||||
message.success({
|
||||
content: (
|
||||
<span>
|
||||
系统将在
|
||||
<Countdown
|
||||
className="inline-countdown"
|
||||
format="ss"
|
||||
value={Date.now() + 1000 * 30}
|
||||
/>
|
||||
秒后自动刷新
|
||||
</span>
|
||||
),
|
||||
duration: 30,
|
||||
});
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 30000);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
onCancel() {
|
||||
modalRef.current.update({
|
||||
maskClosable: true,
|
||||
closable: true,
|
||||
okButtonProps: { disabled: false },
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!modalRef.current || !socketMessage) {
|
||||
return;
|
||||
@@ -130,7 +174,7 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
|
||||
}
|
||||
|
||||
const newMessage = `${value}${_message}`;
|
||||
const updateFailed = newMessage.includes('失败,请检查');
|
||||
const updateFailed = newMessage.includes('失败');
|
||||
|
||||
modalRef.current.update({
|
||||
maskClosable: updateFailed,
|
||||
@@ -162,24 +206,10 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
|
||||
.getElementById('log-identifier')!
|
||||
.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
|
||||
if (_message.includes('重启面板')) {
|
||||
message.warning({
|
||||
content: (
|
||||
<span>
|
||||
系统将在
|
||||
<Countdown
|
||||
className="inline-countdown"
|
||||
format="ss"
|
||||
value={Date.now() + 1000 * 30}
|
||||
/>
|
||||
秒后自动刷新
|
||||
</span>
|
||||
),
|
||||
duration: 30,
|
||||
});
|
||||
if (_message.includes('更新包下载成功')) {
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 30000);
|
||||
showReloadModal();
|
||||
}, 1000);
|
||||
}
|
||||
}, [socketMessage]);
|
||||
|
||||
|
||||
@@ -20,9 +20,7 @@ const NotificationSetting = ({ data }: any) => {
|
||||
|
||||
request
|
||||
.put(`${config.apiPrefix}user/notification`, {
|
||||
data: {
|
||||
...values,
|
||||
},
|
||||
values,
|
||||
})
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
|
||||
+148
-26
@@ -1,11 +1,25 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Button, InputNumber, Form, Radio, message, Input } from 'antd';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import {
|
||||
Button,
|
||||
InputNumber,
|
||||
Form,
|
||||
Radio,
|
||||
message,
|
||||
Input,
|
||||
Upload,
|
||||
Modal,
|
||||
Progress,
|
||||
} from 'antd';
|
||||
import * as DarkReader from '@umijs/ssr-darkreader';
|
||||
import config from '@/utils/config';
|
||||
import { request } from '@/utils/http';
|
||||
import CheckUpdate from './checkUpdate';
|
||||
import { SharedContext } from '@/layouts';
|
||||
import { saveAs } from 'file-saver';
|
||||
import './index.less';
|
||||
import { UploadOutlined } from '@ant-design/icons';
|
||||
import Countdown from 'antd/lib/statistic/Countdown';
|
||||
import useProgress from './progress';
|
||||
|
||||
const optionsWithDisabled = [
|
||||
{ label: '亮色', value: 'light' },
|
||||
@@ -19,8 +33,15 @@ const Other = ({
|
||||
reloadTheme,
|
||||
}: Pick<SharedContext, 'socketMessage' | 'reloadTheme' | 'systemInfo'>) => {
|
||||
const defaultTheme = localStorage.getItem('qinglong_dark_theme') || 'auto';
|
||||
const [logRemoveFrequency, setLogRemoveFrequency] = useState<number | null>();
|
||||
const [systemConfig, setSystemConfig] = useState<{
|
||||
logRemoveFrequency?: number | null;
|
||||
cronConcurrency?: number | null;
|
||||
}>();
|
||||
const [form] = Form.useForm();
|
||||
const modalRef = useRef<any>();
|
||||
const [exportLoading, setExportLoading] = useState(false);
|
||||
const showUploadProgress = useProgress('上传');
|
||||
const showDownloadProgress = useProgress('下载');
|
||||
|
||||
const {
|
||||
enable: enableDarkMode,
|
||||
@@ -45,13 +66,12 @@ const Other = ({
|
||||
reloadTheme();
|
||||
};
|
||||
|
||||
const getLogRemoveFrequency = () => {
|
||||
const getSystemConfig = () => {
|
||||
request
|
||||
.get(`${config.apiPrefix}system/log/remove`)
|
||||
.get(`${config.apiPrefix}system/config`)
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200 && data.info) {
|
||||
const { frequency } = data.info;
|
||||
setLogRemoveFrequency(frequency);
|
||||
setSystemConfig(data.info);
|
||||
}
|
||||
})
|
||||
.catch((error: any) => {
|
||||
@@ -59,25 +79,83 @@ const Other = ({
|
||||
});
|
||||
};
|
||||
|
||||
const updateRemoveLogFrequency = () => {
|
||||
setTimeout(() => {
|
||||
request
|
||||
.put(`${config.apiPrefix}system/log/remove`, {
|
||||
data: { frequency: logRemoveFrequency },
|
||||
})
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
message.success('更新成功');
|
||||
}
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.log(error);
|
||||
});
|
||||
const updateSystemConfig = () => {
|
||||
request
|
||||
.put(`${config.apiPrefix}system/config`, systemConfig)
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
message.success('更新成功');
|
||||
}
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
const exportData = () => {
|
||||
setExportLoading(true);
|
||||
request
|
||||
.put<Blob>(
|
||||
`${config.apiPrefix}system/data/export`,
|
||||
{},
|
||||
{
|
||||
responseType: 'blob',
|
||||
timeout: 86400000,
|
||||
onDownloadProgress: (e) => {
|
||||
if (e.progress) {
|
||||
showDownloadProgress(parseFloat((e.progress * 100).toFixed(1)));
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
.then((res) => {
|
||||
saveAs(res, 'data.tgz');
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.log(error);
|
||||
})
|
||||
.finally(() => setExportLoading(false));
|
||||
};
|
||||
|
||||
const showReloadModal = () => {
|
||||
Modal.confirm({
|
||||
width: 600,
|
||||
maskClosable: false,
|
||||
title: '确认重启',
|
||||
centered: true,
|
||||
content: '备份数据上传成功,确认覆盖数据',
|
||||
okText: '重启',
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}system/reload`, { type: 'data' })
|
||||
.then(() => {
|
||||
message.success({
|
||||
content: (
|
||||
<span>
|
||||
系统将在
|
||||
<Countdown
|
||||
className="inline-countdown"
|
||||
format="ss"
|
||||
value={Date.now() + 1000 * 30}
|
||||
/>
|
||||
秒后自动刷新
|
||||
</span>
|
||||
),
|
||||
duration: 30,
|
||||
});
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 30000);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getLogRemoveFrequency();
|
||||
getSystemConfig();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -100,16 +178,60 @@ const Other = ({
|
||||
<InputNumber
|
||||
addonBefore="每"
|
||||
addonAfter="天"
|
||||
style={{ width: 150 }}
|
||||
style={{ width: 142 }}
|
||||
min={0}
|
||||
value={logRemoveFrequency}
|
||||
onChange={(value) => setLogRemoveFrequency(value)}
|
||||
value={systemConfig?.logRemoveFrequency}
|
||||
onChange={(value) => {
|
||||
setSystemConfig({ ...systemConfig, logRemoveFrequency: value });
|
||||
}}
|
||||
/>
|
||||
<Button type="primary" onClick={updateRemoveLogFrequency}>
|
||||
<Button type="primary" onClick={updateSystemConfig}>
|
||||
确认
|
||||
</Button>
|
||||
</Input.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="定时任务并发数" name="frequency">
|
||||
<Input.Group compact>
|
||||
<InputNumber
|
||||
style={{ width: 142 }}
|
||||
min={1}
|
||||
value={systemConfig?.cronConcurrency}
|
||||
onChange={(value) => {
|
||||
setSystemConfig({ ...systemConfig, cronConcurrency: value });
|
||||
}}
|
||||
/>
|
||||
<Button type="primary" onClick={updateSystemConfig}>
|
||||
确认
|
||||
</Button>
|
||||
</Input.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="数据备份还原" name="frequency">
|
||||
<Button type="primary" onClick={exportData} loading={exportLoading}>
|
||||
备份
|
||||
</Button>
|
||||
<Upload
|
||||
method="put"
|
||||
showUploadList={false}
|
||||
maxCount={1}
|
||||
action="/api/system/data/import"
|
||||
onChange={(e) => {
|
||||
if (e.event?.percent) {
|
||||
showUploadProgress(parseFloat(e.event?.percent.toFixed(1)));
|
||||
if (e.event?.percent === 100) {
|
||||
showReloadModal();
|
||||
}
|
||||
}
|
||||
}}
|
||||
name="data"
|
||||
headers={{
|
||||
Authorization: `Bearer ${localStorage.getItem(config.authKey)}`,
|
||||
}}
|
||||
>
|
||||
<Button icon={<UploadOutlined />} style={{ marginLeft: 8 }}>
|
||||
还原数据
|
||||
</Button>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
<Form.Item label="检查更新" name="update">
|
||||
<CheckUpdate systemInfo={systemInfo} socketMessage={socketMessage} />
|
||||
</Form.Item>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Modal, Progress } from 'antd';
|
||||
import { useRef } from 'react';
|
||||
|
||||
export default function useProgress(title: string) {
|
||||
const modalRef = useRef<ReturnType<typeof Modal.info>>();
|
||||
|
||||
const ProgressElement = ({ percent }: { percent: number }) => (
|
||||
<Progress
|
||||
style={{ display: 'flex', justifyContent: 'center' }}
|
||||
type="circle"
|
||||
percent={percent}
|
||||
/>
|
||||
);
|
||||
|
||||
const showProgress = (percent: number) => {
|
||||
if (modalRef.current) {
|
||||
modalRef.current.update({
|
||||
title: `${title}${percent >= 100 ? '成功' : '中...'}`,
|
||||
content: <ProgressElement percent={percent} />,
|
||||
});
|
||||
} else {
|
||||
modalRef.current = Modal.info({
|
||||
width: 600,
|
||||
maskClosable: false,
|
||||
title: `${title}${percent >= 100 ? '成功' : '中...'}`,
|
||||
centered: true,
|
||||
content: <ProgressElement percent={percent} />,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return showProgress;
|
||||
}
|
||||
@@ -22,10 +22,8 @@ const SecuritySettings = ({ user, userChange }: any) => {
|
||||
const handleOk = (values: any) => {
|
||||
request
|
||||
.put(`${config.apiPrefix}user`, {
|
||||
data: {
|
||||
username: values.username,
|
||||
password: values.password,
|
||||
},
|
||||
username: values.username,
|
||||
password: values.password,
|
||||
})
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
@@ -64,7 +62,7 @@ const SecuritySettings = ({ user, userChange }: any) => {
|
||||
const completeTowFactor = () => {
|
||||
setLoading(true);
|
||||
request
|
||||
.put(`${config.apiPrefix}user/two-factor/active`, { data: { code } })
|
||||
.put(`${config.apiPrefix}user/two-factor/active`, { code })
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
if (data) {
|
||||
@@ -241,7 +239,7 @@ const SecuritySettings = ({ user, userChange }: any) => {
|
||||
头像
|
||||
</div>
|
||||
<Avatar size={128} shape="square" icon={<UserOutlined />} src={avatar} />
|
||||
<ImgCrop rotate>
|
||||
<ImgCrop rotationSlider>
|
||||
<Upload
|
||||
method="put"
|
||||
showUploadList={false}
|
||||
|
||||
@@ -254,7 +254,7 @@ const Subscription = () => {
|
||||
),
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}subscriptions/run`, { data: [record.id] })
|
||||
.put(`${config.apiPrefix}subscriptions/run`, [record.id])
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
const result = [...value];
|
||||
@@ -289,7 +289,7 @@ const Subscription = () => {
|
||||
),
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}subscriptions/stop`, { data: [record.id] })
|
||||
.put(`${config.apiPrefix}subscriptions/stop`, [record.id])
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
const result = [...value];
|
||||
@@ -386,9 +386,7 @@ const Subscription = () => {
|
||||
`${config.apiPrefix}subscriptions/${
|
||||
record.is_disabled === 1 ? 'enable' : 'disable'
|
||||
}`,
|
||||
{
|
||||
data: [record.id],
|
||||
},
|
||||
[record.id],
|
||||
)
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
|
||||
@@ -47,9 +47,7 @@ const SubscriptionModal = ({
|
||||
try {
|
||||
const { code, data } = await request[method](
|
||||
`${config.apiPrefix}subscriptions`,
|
||||
{
|
||||
data: payload,
|
||||
},
|
||||
payload,
|
||||
);
|
||||
if (code === 200) {
|
||||
message.success(subscription ? '更新订阅成功' : '新建订阅成功');
|
||||
|
||||
+68
-25
@@ -1,17 +1,36 @@
|
||||
import { extend } from 'umi-request';
|
||||
import { message } from 'antd';
|
||||
import config from './config';
|
||||
import { history } from '@umijs/max';
|
||||
import axios, {
|
||||
AxiosError,
|
||||
AxiosInstance,
|
||||
AxiosRequestConfig,
|
||||
} from 'axios';
|
||||
|
||||
interface IResponseData {
|
||||
code?: number;
|
||||
data?: any;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
type Override<
|
||||
T,
|
||||
K extends Partial<{ [P in keyof T]: any }> | string,
|
||||
> = K extends string
|
||||
? Omit<T, K> & { [P in keyof T]: T[P] | unknown }
|
||||
: Omit<T, keyof K> & K;
|
||||
|
||||
message.config({
|
||||
duration: 2,
|
||||
});
|
||||
|
||||
const time = Date.now();
|
||||
const errorHandler = function (error: any) {
|
||||
const errorHandler = function (
|
||||
error: AxiosError,
|
||||
) {
|
||||
if (error.response) {
|
||||
const msg = error.data
|
||||
? error.data.message || error.message || error.data
|
||||
const msg = error.response.data
|
||||
? error.response.data.message || error.message || error.response.data
|
||||
: error.response.statusText;
|
||||
const responseStatus = error.response.status;
|
||||
if ([502, 504].includes(responseStatus)) {
|
||||
@@ -32,10 +51,14 @@ const errorHandler = function (error: any) {
|
||||
console.log(error.message);
|
||||
}
|
||||
|
||||
throw error; // 如果throw. 错误将继续抛出.
|
||||
return Promise.reject(error);
|
||||
};
|
||||
|
||||
const _request = extend({ timeout: 60000, params: { t: time }, errorHandler });
|
||||
let _request = axios.create({
|
||||
timeout: 60000,
|
||||
params: { t: time },
|
||||
});
|
||||
|
||||
const apiWhiteList = [
|
||||
'/api/user/login',
|
||||
'/open/auth/token',
|
||||
@@ -45,15 +68,13 @@ const apiWhiteList = [
|
||||
'/api/user/notification/init',
|
||||
];
|
||||
|
||||
_request.interceptors.request.use((url, options) => {
|
||||
_request.interceptors.request.use((_config) => {
|
||||
const token = localStorage.getItem(config.authKey);
|
||||
if (token && !apiWhiteList.includes(url)) {
|
||||
const headers = {
|
||||
Authorization: `Bearer ${token}`,
|
||||
};
|
||||
return { url, options: { ...options, headers } };
|
||||
if (token && !apiWhiteList.includes(_config.url!)) {
|
||||
_config.headers.Authorization = `Bearer ${token}`;
|
||||
return _config;
|
||||
}
|
||||
return { url, options };
|
||||
return _config;
|
||||
});
|
||||
|
||||
_request.interceptors.response.use(async (response) => {
|
||||
@@ -66,18 +87,40 @@ _request.interceptors.response.use(async (response) => {
|
||||
history.push('/login');
|
||||
}
|
||||
} else {
|
||||
const res = await response.clone().json();
|
||||
if (res.code !== 200) {
|
||||
const msg = res.message || res.data;
|
||||
msg &&
|
||||
message.error({
|
||||
content: msg,
|
||||
style: { maxWidth: 500, margin: '0 auto' },
|
||||
});
|
||||
}
|
||||
return res;
|
||||
try {
|
||||
const res = response.data;
|
||||
if (res.code !== 200) {
|
||||
const msg = res.message || res.data;
|
||||
msg &&
|
||||
message.error({
|
||||
content: msg,
|
||||
style: { maxWidth: 500, margin: '0 auto' },
|
||||
});
|
||||
}
|
||||
return res;
|
||||
} catch (error) { }
|
||||
return response;
|
||||
}
|
||||
return response;
|
||||
});
|
||||
}, errorHandler);
|
||||
|
||||
export const request = _request;
|
||||
export const request = _request as Override<AxiosInstance, {
|
||||
get<T = IResponseData, D = any>(
|
||||
url: string,
|
||||
config?: AxiosRequestConfig<D>,
|
||||
): Promise<T>;
|
||||
delete<T = IResponseData, D = any>(
|
||||
url: string,
|
||||
config?: AxiosRequestConfig<D>,
|
||||
): Promise<T>;
|
||||
post<T = IResponseData, D = any>(
|
||||
url: string,
|
||||
data?: D,
|
||||
config?: AxiosRequestConfig<D>,
|
||||
): Promise<T>;
|
||||
put<T = IResponseData, D = any>(
|
||||
url: string,
|
||||
data?: D,
|
||||
config?: AxiosRequestConfig<D>,
|
||||
): Promise<T>;
|
||||
}>;
|
||||
|
||||
+6
-10
@@ -1,11 +1,7 @@
|
||||
version: 2.15.16
|
||||
changeLogLink: https://t.me/jiao_long/378
|
||||
version: 2.15.18
|
||||
changeLogLink: https://t.me/jiao_long/384
|
||||
publishTime: 2023-07-19 21:21
|
||||
changeLog: |
|
||||
1. 企业微信通知增加代理地址配置 QYWX_ORIGIN
|
||||
2. 重构任务并发执行逻辑,依赖并发安装逻辑
|
||||
3. 修复关闭全部任务视图,默认视图筛选错误
|
||||
4. 增加初始化文件写入,修复参数含有空格影响 task/ql 命令
|
||||
5. 修复初始化界面、侧边栏、错误页样式
|
||||
6. 修复拉取订阅文件包含空格出错
|
||||
7. 修改 api 限流策略,修复检查检查日志
|
||||
8. 修复环境变量列表搜索字符转码
|
||||
1. 增加备份恢复功能
|
||||
2. 重构系统检查更新逻辑
|
||||
3. 修复系统设置自动删除日志启动时失效
|
||||
|
||||
Reference in New Issue
Block a user