Compare commits

..
20 Commits
Author SHA1 Message Date
whyour 6131fe0110 更新版本 v2.15.19 2023-07-20 23:14:16 +08:00
whyour 665c344ae4 修复依赖是否已经安装判断 2023-07-20 22:54:11 +08:00
雨思andGitHub 373b8c97d7 增加PushMe消息通道 (#2018),修复系统设置保存通知 2023-07-20 13:19:39 +08:00
whyour 56eb0c5408 更新版本 v2.15.18 2023-07-19 21:17:22 +08:00
whyour fe55929959 更新 readme 2023-07-19 21:02:52 +08:00
whyour 01e2bd007d 修复重启提示持续时间 2023-07-18 21:39:03 +08:00
whyour 4e091b0c3e 修改 nginx body 限制,服务重启延迟时间 2023-07-18 15:30:43 +08:00
whyour db94cd3799 修复备份数据超时时间 2023-07-18 13:43:02 +08:00
whyour a15192b9b2 升级 @umijs/max 版本 2023-07-17 23:18:48 +08:00
whyour efd4f1d5ab umi-request 替换为 axios 2023-07-17 23:13:06 +08:00
whyour bd166ee794 修复 pm2 重启逻辑 2023-07-17 13:25:56 +08:00
whyour 93e94ea94c 系统设置增加数据恢复功能 2023-07-16 22:02:30 +08:00
whyour 88b87de391 增加数据备份功能 2023-07-16 00:23:29 +08:00
whyour 8affff96f3 修改系统内更新系统逻辑 2023-07-13 22:09:45 +08:00
whyour 936b565fb1 修改系统更新时间和版本获取 2023-07-12 00:05:46 +08:00
whyour b69ff2895e 增加依赖是否已经安装判断 2023-07-10 23:48:05 +08:00
whyour f3791cbb62 修复编译错误 2023-07-09 17:46:02 +08:00
whyour b0f3b51736 修复服务启动时定时删除日志失效 2023-07-08 20:58:36 +08:00
whyour 3aa112e373 修改 node 进程错误监控 2023-07-08 00:27:05 +08:00
whyour 683482e067 修改 ql update 逻辑 2023-07-08 00:18:09 +08:00
63 changed files with 1025 additions and 547 deletions
+2 -1
View File
@@ -28,4 +28,5 @@
/db /db
/manual_log /manual_log
/scripts /scripts
/bak /bak
/.tmp
+18
View File
@@ -35,6 +35,24 @@ Timed task management platform supporting Python3, JavaScript, Shell, Typescript
- Support dark mode - Support dark mode
- Support cell phone operation - 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 ## Deployment
### Docker (Recommended) ### Docker (Recommended)
+18
View File
@@ -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 (推荐) ### docker (推荐)
+59 -21
View File
@@ -14,8 +14,18 @@ import {
promiseExec, promiseExec,
} from '../config/util'; } from '../config/util';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import multer from 'multer';
const route = Router(); 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) => { export default (app: Router) => {
app.use('/system', route); app.use('/system', route);
@@ -25,23 +35,9 @@ export default (app: Router) => {
try { try {
const userService = Container.get(UserService); const userService = Container.get(UserService);
const authInfo = await userService.getUserInfo(); const authInfo = await userService.getUserInfo();
const envCount = await EnvModel.count(); const { version, changeLog, changeLogLink, publishTime } = await parseVersion(
const { version, changeLog, changeLogLink } = await parseVersion(
config.versionFile, 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; let isInitialized = true;
if ( if (
@@ -56,9 +52,8 @@ export default (app: Router) => {
data: { data: {
isInitialized, isInitialized,
version, version,
lastCommitTime: dayjs(lastCommitTime).unix(), publishTime: dayjs(publishTime).unix(),
lastCommitId, branch: process.env.QL_BRANCH || 'master',
branch,
changeLog, changeLog,
changeLogLink, changeLogLink,
}, },
@@ -95,9 +90,7 @@ export default (app: Router) => {
const logger: Logger = Container.get('logger'); const logger: Logger = Container.get('logger');
try { try {
const systemService = Container.get(SystemService); const systemService = Container.get(SystemService);
const result = await systemService.updateSystemConfig( const result = await systemService.updateSystemConfig(req.body);
req.body,
);
res.send(result); res.send(result);
} catch (e) { } catch (e) {
return next(e); return next(e);
@@ -133,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( route.put(
'/notify', '/notify',
celebrate({ celebrate({
@@ -212,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);
}
},
);
}; };
+5
View File
@@ -20,6 +20,7 @@ const rootPath = process.env.QL_DIR as string;
const envFound = dotenv.config({ path: path.join(rootPath, '.env') }); const envFound = dotenv.config({ path: path.join(rootPath, '.env') });
const dataPath = path.join(rootPath, 'data/'); const dataPath = path.join(rootPath, 'data/');
const tmpPath = path.join(rootPath, '.tmp/');
const samplePath = path.join(rootPath, 'sample/'); const samplePath = path.join(rootPath, 'sample/');
const configPath = path.join(dataPath, 'config/'); const configPath = path.join(dataPath, 'config/');
const scriptPath = path.join(dataPath, 'scripts/'); const scriptPath = path.join(dataPath, 'scripts/');
@@ -42,6 +43,7 @@ const authError = '错误的用户名密码,请重试';
const loginFaild = '请先登录!'; const loginFaild = '请先登录!';
const configString = 'config sample crontab shareCode diy'; const configString = 'config sample crontab shareCode diy';
const versionFile = path.join(rootPath, 'version.yaml'); const versionFile = path.join(rootPath, 'version.yaml');
const dataTgzFile = path.join(tmpPath, 'data.tgz');
if (envFound.error) { if (envFound.error) {
throw new Error("⚠️ Couldn't find .env file ⚠️"); throw new Error("⚠️ Couldn't find .env file ⚠️");
@@ -59,6 +61,9 @@ export default {
prefix: '/api', prefix: '/api',
}, },
rootPath, rootPath,
tmpPath,
dataPath,
dataTgzFile,
configString, configString,
loginFaild, loginFaild,
authError, authError,
+13
View File
@@ -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) { export function parseHeaders(headers: string) {
if (!headers) return {}; if (!headers) return {};
@@ -505,6 +517,7 @@ interface IVersion {
version: string; version: string;
changeLogLink: string; changeLogLink: string;
changeLog: string; changeLog: string;
publishTime: string;
} }
export async function parseVersion(path: string): Promise<IVersion> { export async function parseVersion(path: string): Promise<IVersion> {
+1 -1
View File
@@ -44,7 +44,7 @@ export interface LoginLogInfo {
export type AuthModelInfo = SystemConfigInfo & Partial<NotificationInfo> & LoginLogInfo; export type AuthModelInfo = SystemConfigInfo & Partial<NotificationInfo> & LoginLogInfo;
interface AuthInstance extends Model<AuthInfo, AuthInfo>, AuthInfo { } export interface AuthInstance extends Model<AuthInfo, AuthInfo>, AuthInfo { }
export const AuthModel = sequelize.define<AuthInstance>('Auth', { export const AuthModel = sequelize.define<AuthInstance>('Auth', {
ip: DataTypes.STRING, ip: DataTypes.STRING,
type: DataTypes.STRING, type: DataTypes.STRING,
+1 -1
View File
@@ -49,7 +49,7 @@ export enum CrontabStatus {
'disabled', 'disabled',
} }
interface CronInstance extends Model<Crontab, Crontab>, Crontab {} export interface CronInstance extends Model<Crontab, Crontab>, Crontab {}
export const CrontabModel = sequelize.define<CronInstance>('Crontab', { export const CrontabModel = sequelize.define<CronInstance>('Crontab', {
name: { name: {
unique: 'compositeIndex', unique: 'compositeIndex',
+1 -1
View File
@@ -39,7 +39,7 @@ export class CrontabView {
} }
} }
interface CronViewInstance export interface CronViewInstance
extends Model<CrontabView, CrontabView>, extends Model<CrontabView, CrontabView>,
CrontabView {} CrontabView {}
export const CrontabViewModel = sequelize.define<CronViewInstance>( export const CrontabViewModel = sequelize.define<CronViewInstance>(
+20 -8
View File
@@ -4,9 +4,9 @@ import { DataTypes, Model, ModelDefined } from 'sequelize';
export class Dependence { export class Dependence {
timestamp?: string; timestamp?: string;
id?: number; id?: number;
status?: DependenceStatus; status: DependenceStatus;
type?: DependenceTypes; type: DependenceTypes;
name?: number; name: string;
log?: string[]; log?: string[];
remark?: string; remark?: string;
@@ -18,7 +18,7 @@ export class Dependence {
: DependenceStatus.queued; : DependenceStatus.queued;
this.type = options.type || DependenceTypes.nodejs; this.type = options.type || DependenceTypes.nodejs;
this.timestamp = new Date().toString(); this.timestamp = new Date().toString();
this.name = options.name; this.name = options.name.trim();
this.log = options.log || []; this.log = options.log || [];
this.remark = options.remark || ''; this.remark = options.remark || '';
} }
@@ -42,19 +42,31 @@ export enum DependenceTypes {
export enum InstallDependenceCommandTypes { export enum InstallDependenceCommandTypes {
'pnpm add -g', 'pnpm add -g',
'pip3 install', 'pip3 install --disable-pip-version-check --root-user-action=ignore',
'apk add', 'apk add',
} }
export enum GetDependenceCommandTypes {
'pnpm ls -g ',
'pip3 show --disable-pip-version-check',
'apk info',
}
export enum versionDependenceCommandTypes {
'@',
'==',
'=',
}
export enum unInstallDependenceCommandTypes { export enum unInstallDependenceCommandTypes {
'pnpm remove -g', 'pnpm remove -g',
'pip3 uninstall -y', 'pip3 uninstall --disable-pip-version-check --root-user-action=ignore -y',
'apk del', 'apk del',
} }
interface DependenceInstance export interface DependenceInstance
extends Model<Dependence, Dependence>, extends Model<Dependence, Dependence>,
Dependence {} Dependence { }
export const DependenceModel = sequelize.define<DependenceInstance>( export const DependenceModel = sequelize.define<DependenceInstance>(
'Dependence', 'Dependence',
{ {
+1 -1
View File
@@ -34,7 +34,7 @@ export const initPosition = 4500000000000000;
export const stepPosition = 10000000000; export const stepPosition = 10000000000;
export const minPosition = 100; 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', { export const EnvModel = sequelize.define<EnvInstance>('Env', {
value: { type: DataTypes.STRING, unique: 'compositeIndex' }, value: { type: DataTypes.STRING, unique: 'compositeIndex' },
timestamp: DataTypes.STRING, timestamp: DataTypes.STRING,
+6
View File
@@ -15,6 +15,7 @@ export enum NotificationMode {
'iGot' = 'iGot', 'iGot' = 'iGot',
'pushPlus' = 'pushPlus', 'pushPlus' = 'pushPlus',
'email' = 'email', 'email' = 'email',
'pushMe' = 'pushMe',
'feishu' = 'feishu', 'feishu' = 'feishu',
'webhook' = 'webhook', 'webhook' = 'webhook',
} }
@@ -101,6 +102,10 @@ export class EmailNotification extends NotificationBaseInfo {
public emailPass: string = ''; public emailPass: string = '';
} }
export class PushMeNotification extends NotificationBaseInfo {
public pushMeKey: string = '';
}
export class WebhookNotification extends NotificationBaseInfo { export class WebhookNotification extends NotificationBaseInfo {
public webhookHeaders: string = ''; public webhookHeaders: string = '';
public webhookBody: string = ''; public webhookBody: string = '';
@@ -131,5 +136,6 @@ export interface NotificationInfo
IGotNotification, IGotNotification,
PushPlusNotification, PushPlusNotification,
EmailNotification, EmailNotification,
PushMeNotification,
WebhookNotification, WebhookNotification,
LarkNotification {} LarkNotification {}
+1 -1
View File
@@ -26,7 +26,7 @@ export interface AppToken {
export type AppScope = 'envs' | 'crons' | 'configs' | 'scripts' | 'logs'; 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', { export const AppModel = sequelize.define<AppInstance>('App', {
name: { type: DataTypes.STRING, unique: 'name' }, name: { type: DataTypes.STRING, unique: 'name' },
scopes: DataTypes.JSON, scopes: DataTypes.JSON,
+2 -1
View File
@@ -16,4 +16,5 @@ export type SockMessageType =
| 'uninstallDependence' | 'uninstallDependence'
| 'updateSystemVersion' | 'updateSystemVersion'
| 'manuallyRunScript' | 'manuallyRunScript'
| 'runSubscriptionEnd'; | 'runSubscriptionEnd'
| 'reloadSystem';
+1 -1
View File
@@ -70,7 +70,7 @@ export enum SubscriptionStatus {
'queued', 'queued',
} }
interface SubscriptionInstance export interface SubscriptionInstance
extends Model<Subscription, Subscription>, extends Model<Subscription, Subscription>,
Subscription {} Subscription {}
export const SubscriptionModel = sequelize.define<SubscriptionInstance>( export const SubscriptionModel = sequelize.define<SubscriptionInstance>(
+4 -4
View File
@@ -30,15 +30,15 @@ export default async () => {
// 运行删除日志任务 // 运行删除日志任务
const data = await systemService.getSystemConfig(); const data = await systemService.getSystemConfig();
if (data && data.info && data.info.frequency) { if (data && data.info && data.info.logRemoveFrequency) {
const rmlogCron = { const rmlogCron = {
id: data.id, id: data.id as number,
name: '删除日志', name: '删除日志',
command: `ql rmlog ${data.info.frequency}`, command: `ql rmlog ${data.info.logRemoveFrequency}`,
}; };
await scheduleService.cancelIntervalTask(rmlogCron); await scheduleService.cancelIntervalTask(rmlogCron);
scheduleService.createIntervalTask(rmlogCron, { scheduleService.createIntervalTask(rmlogCron, {
days: data.info.frequency, days: data.info.logRemoveFrequency,
}); });
} }
+27 -11
View File
@@ -5,18 +5,34 @@ import Sock from './sock';
export default async ({ server }: { server: Server }) => { export default async ({ server }: { server: Server }) => {
await Sock({ server }); await Sock({ server });
Logger.info('✌️ Sock loaded'); Logger.info('✌️ Sock loaded');
let exitTime = 0;
let timer: NodeJS.Timeout;
process.on('SIGINT', () => { process.on('SIGINT', (singal) => {
Logger.info('✌️ Server need close'); Logger.warn(`Server need close, singal ${singal}`);
server.close(() => { exitTime++;
setTimeout(() => { if (exitTime >= 3) {
process.exit(); Logger.warn('Forcing server close');
}, 10000); clearTimeout(timer);
});
setTimeout(() => {
console.log('Forcing server close !!!');
process.exit(1); 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);
}); });
}; };
+65 -12
View File
@@ -8,11 +8,13 @@ import {
DependenceTypes, DependenceTypes,
unInstallDependenceCommandTypes, unInstallDependenceCommandTypes,
DependenceModel, DependenceModel,
GetDependenceCommandTypes,
versionDependenceCommandTypes,
} from '../data/dependence'; } from '../data/dependence';
import { spawn } from 'cross-spawn'; import { spawn } from 'cross-spawn';
import SockService from './sock'; import SockService from './sock';
import { FindOptions, Op } from 'sequelize'; import { FindOptions, Op } from 'sequelize';
import { concurrentRun } from '../config/util'; import { promiseExecSuccess } from '../config/util';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import taskLimit from '../shared/pLimit'; import taskLimit from '../shared/pLimit';
@@ -137,9 +139,17 @@ export default class DependenceService {
} }
private async updateLog(ids: number[], log: string): Promise<void> { private async updateLog(ids: number[], log: string): Promise<void> {
const doc = await DependenceModel.findOne({ where: { id: ids } }); taskLimit.updateDepLog(async () => {
const newLog = doc?.log ? [...doc.log, log] : [log]; const docs = await DependenceModel.findAll({ where: { id: ids } });
await DependenceModel.update({ log: newLog }, { 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( public installOrUninstallDependency(
@@ -155,15 +165,15 @@ export default class DependenceService {
: DependenceStatus.removing; : DependenceStatus.removing;
await DependenceModel.update({ status }, { where: { id: depIds } }); await DependenceModel.update({ status }, { where: { id: depIds } });
const socketMessageType = !force const socketMessageType = isInstall
? 'installDependence' ? 'installDependence'
: 'uninstallDependence'; : 'uninstallDependence';
const depName = dependency.name; const depName = dependency.name.trim();
const depRunCommand = ( const depRunCommand = (
isInstall isInstall
? InstallDependenceCommandTypes ? InstallDependenceCommandTypes
: unInstallDependenceCommandTypes : unInstallDependenceCommandTypes
)[dependency.type as any]; )[dependency.type];
const actionText = isInstall ? '安装' : '删除'; const actionText = isInstall ? '安装' : '删除';
const startTime = dayjs(); const startTime = dayjs();
@@ -175,7 +185,50 @@ export default class DependenceService {
message, message,
references: depIds, 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, _depVersion] = dependency.name
.trim()
.split(depVersionStr);
const isNodeDependence = dependency.type === DependenceTypes.nodejs;
const isLinuxDependence = dependency.type === DependenceTypes.linux;
const isPythonDependence = dependency.type === DependenceTypes.python3;
const depInfo = (
await promiseExecSuccess(
isNodeDependence
? `${getCommandPrefix} | grep "${_depName}" | head -1`
: `${getCommandPrefix} ${_depName}`,
)
).replace(/\s{2,}/, ' ');
if (
depInfo &&
((isNodeDependence && depInfo.split(' ')?.[0] === _depName) ||
(isLinuxDependence && depInfo.toLocaleLowerCase().includes('installed')) ||
isPythonDependence) &&
(!_depVersion || depInfo.includes(_depVersion))
) {
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}`, { const cp = spawn(`${depRunCommand} ${depName}`, {
shell: '/bin/bash', shell: '/bin/bash',
@@ -187,7 +240,7 @@ export default class DependenceService {
message: data.toString(), message: data.toString(),
references: depIds, references: depIds,
}); });
await this.updateLog(depIds, data.toString()); this.updateLog(depIds, data.toString());
}); });
cp.stderr.on('data', async (data) => { cp.stderr.on('data', async (data) => {
@@ -196,7 +249,7 @@ export default class DependenceService {
message: data.toString(), message: data.toString(),
references: depIds, references: depIds,
}); });
await this.updateLog(depIds, data.toString()); this.updateLog(depIds, data.toString());
}); });
cp.on('error', async (err) => { cp.on('error', async (err) => {
@@ -205,7 +258,7 @@ export default class DependenceService {
message: JSON.stringify(err), message: JSON.stringify(err),
references: depIds, references: depIds,
}); });
await this.updateLog(depIds, JSON.stringify(err)); this.updateLog(depIds, JSON.stringify(err));
}); });
cp.on('close', async (code) => { cp.on('close', async (code) => {
@@ -221,7 +274,7 @@ export default class DependenceService {
message, message,
references: depIds, references: depIds,
}); });
await this.updateLog(depIds, message); this.updateLog(depIds, message);
let status = null; let status = null;
if (isSucceed) { if (isSucceed) {
+23
View File
@@ -28,6 +28,7 @@ export default class NotificationService {
['iGot', this.iGot], ['iGot', this.iGot],
['pushPlus', this.pushPlus], ['pushPlus', this.pushPlus],
['email', this.email], ['email', this.email],
['pushMe', this.pushMe],
['webhook', this.webhook], ['webhook', this.webhook],
['lark', this.lark], ['lark', this.lark],
]); ]);
@@ -561,6 +562,28 @@ export default class NotificationService {
} }
} }
private async pushMe() {
const { pushMeKey } = this.params;
try {
const res: any = await got
.post(`https://push.i-i.me/?push_key=${pushMeKey}`, {
...this.gotOption,
json: {
title: this.title,
content: this.content
},
headers: { 'Content-Type': 'application/json' },
});
if (res.body === 'success') {
return true;
} else {
throw new Error(res.body);
}
} catch (error: any) {
throw new Error(error.response ? error.response.body : error);
}
}
private async webhook() { private async webhook() {
const { const {
webhookUrl, webhookUrl,
+65 -10
View File
@@ -1,8 +1,14 @@
import { Response } from 'express';
import { Service, Inject } from 'typedi'; import { Service, Inject } from 'typedi';
import winston from 'winston'; import winston from 'winston';
import config from '../config'; import config from '../config';
import * as fs from 'fs'; import {
import { AuthDataType, AuthInfo, AuthModel, AuthModelInfo } from '../data/auth'; AuthDataType,
AuthInfo,
AuthInstance,
AuthModel,
AuthModelInfo,
} from '../data/auth';
import { NotificationInfo } from '../data/notify'; import { NotificationInfo } from '../data/notify';
import NotificationService from './notify'; import NotificationService from './notify';
import ScheduleService, { TaskCallbacks } from './schedule'; import ScheduleService, { TaskCallbacks } from './schedule';
@@ -14,9 +20,12 @@ import {
killTask, killTask,
parseContentVersion, parseContentVersion,
parseVersion, parseVersion,
promiseExec,
} from '../config/util'; } from '../config/util';
import { TASK_COMMAND } from '../config/const'; import { TASK_COMMAND } from '../config/const';
import taskLimit from '../shared/pLimit' import taskLimit from '../shared/pLimit';
import tar from 'tar';
import path from 'path';
@Service() @Service()
export default class SystemService { export default class SystemService {
@@ -27,22 +36,22 @@ export default class SystemService {
@Inject('logger') private logger: winston.Logger, @Inject('logger') private logger: winston.Logger,
private scheduleService: ScheduleService, private scheduleService: ScheduleService,
private sockService: SockService, private sockService: SockService,
) {} ) { }
public async getSystemConfig() { public async getSystemConfig() {
const doc = await this.getDb({ type: AuthDataType.systemConfig }); const doc = await this.getDb({ type: AuthDataType.systemConfig });
return doc || {}; return doc || ({} as AuthInstance);
} }
private async updateAuthDb(payload: AuthInfo): Promise<any> { private async updateAuthDb(payload: AuthInfo): Promise<AuthInstance> {
await AuthModel.upsert({ ...payload }); await AuthModel.upsert({ ...payload });
const doc = await this.getDb({ type: payload.type }); const doc = await this.getDb({ type: payload.type });
return doc; return doc;
} }
public async getDb(query: any): Promise<any> { public async getDb(query: any): Promise<AuthInstance> {
const doc: any = await AuthModel.findOne({ where: { ...query } }); 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) { public async updateNotificationMode(notificationInfo: NotificationInfo) {
@@ -102,7 +111,7 @@ export default class SystemService {
}, },
); );
lastVersionContent = await parseContentVersion(result.body); lastVersionContent = await parseContentVersion(result.body);
} catch (error) {} } catch (error) { }
if (!lastVersionContent) { if (!lastVersionContent) {
lastVersionContent = currentVersionContent; lastVersionContent = currentVersionContent;
@@ -148,7 +157,7 @@ export default class SystemService {
} }
public async updateSystem() { 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) => { cp.stdout.on('data', (data) => {
this.sockService.sendMessage({ this.sockService.sendMessage({
@@ -174,6 +183,33 @@ export default class SystemService {
return { code: 200 }; 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 }) { public async notify({ title, content }: { title: string; content: string }) {
const isSuccess = await this.notificationService.notify(title, content); const isSuccess = await this.notificationService.notify(title, content);
if (isSuccess) { if (isSuccess) {
@@ -217,4 +253,23 @@ export default class SystemService {
return { code: 400, message: '任务未找到' }; 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 };
}
}
} }
+7 -6
View File
@@ -4,6 +4,7 @@ import { AuthDataType, AuthModel } from "../data/auth";
class TaskLimit { class TaskLimit {
private oneLimit = pLimit(1); private oneLimit = pLimit(1);
private updateLogLimit = pLimit(1);
private cpuLimit = pLimit(Math.max(os.cpus().length, 4)); private cpuLimit = pLimit(Math.max(os.cpus().length, 4));
constructor() { constructor() {
@@ -22,15 +23,15 @@ class TaskLimit {
} }
public runWithCpuLimit<T>(fn: () => Promise<T>): Promise<T> { public runWithCpuLimit<T>(fn: () => Promise<T>): Promise<T> {
return this.cpuLimit(() => { return this.cpuLimit(fn);
return fn();
});
} }
public runOneByOne<T>(fn: () => Promise<T>): Promise<T> { public runOneByOne<T>(fn: () => Promise<T>): Promise<T> {
return this.oneLimit(() => { return this.oneLimit(fn);
return fn(); }
});
public updateDepLog<T>(fn: () => Promise<T>): Promise<T> {
return this.updateLogLimit(fn);
} }
} }
+4 -3
View File
@@ -4,8 +4,6 @@ dir_shell=/ql/shell
. $dir_shell/share.sh . $dir_shell/share.sh
link_shell link_shell
export isFirstStartServer=true
echo -e "======================1. 检测配置文件========================\n" echo -e "======================1. 检测配置文件========================\n"
make_dir /etc/nginx/conf.d make_dir /etc/nginx/conf.d
make_dir /run/nginx make_dir /run/nginx
@@ -25,7 +23,10 @@ if [[ "$is_equal_registry" == "" ]]; then
cd && pnpm config set registry $NpmMirror cd && pnpm config set registry $NpmMirror
pnpm install -g pnpm install -g
fi 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
echo -e "======================3. 启动nginx========================\n" echo -e "======================3. 启动nginx========================\n"
+1 -1
View File
@@ -14,7 +14,7 @@ http {
server_tokens off; server_tokens off;
client_max_body_size 20m; client_max_body_size 4096m;
client_body_buffer_size 20m; client_body_buffer_size 20m;
keepalive_timeout 65; keepalive_timeout 65;
+6 -2
View File
@@ -91,6 +91,7 @@
"serve-handler": "^6.1.3", "serve-handler": "^6.1.3",
"sockjs": "^0.3.24", "sockjs": "^0.3.24",
"sqlite3": "git+https://github.com/whyour/node-sqlite3.git#v1.0.3", "sqlite3": "git+https://github.com/whyour/node-sqlite3.git#v1.0.3",
"tar": "^6.1.15",
"toad-scheduler": "^1.6.0", "toad-scheduler": "^1.6.0",
"typedi": "^0.10.0", "typedi": "^0.10.0",
"uuid": "^8.3.2", "uuid": "^8.3.2",
@@ -108,6 +109,7 @@
"@types/cross-spawn": "^6.0.2", "@types/cross-spawn": "^6.0.2",
"@types/express": "^4.17.13", "@types/express": "^4.17.13",
"@types/express-jwt": "^6.0.4", "@types/express-jwt": "^6.0.4",
"@types/file-saver": "^2.0.5",
"@types/js-yaml": "^4.0.5", "@types/js-yaml": "^4.0.5",
"@types/jsonwebtoken": "^8.5.8", "@types/jsonwebtoken": "^8.5.8",
"@types/lodash": "^4.14.185", "@types/lodash": "^4.14.185",
@@ -123,15 +125,18 @@
"@types/serve-handler": "^6.1.1", "@types/serve-handler": "^6.1.1",
"@types/sockjs": "^0.3.33", "@types/sockjs": "^0.3.33",
"@types/sockjs-client": "^1.5.1", "@types/sockjs-client": "^1.5.1",
"@types/tar": "^6.1.5",
"@types/uuid": "^8.3.4", "@types/uuid": "^8.3.4",
"@umijs/max": "^4.0.55", "@umijs/max": "^4.0.72",
"@umijs/ssr-darkreader": "^4.9.45", "@umijs/ssr-darkreader": "^4.9.45",
"ansi-to-react": "^6.1.6", "ansi-to-react": "^6.1.6",
"antd": "^4.24.8", "antd": "^4.24.8",
"antd-img-crop": "^4.2.3", "antd-img-crop": "^4.2.3",
"axios": "^1.4.0",
"codemirror": "^5.65.2", "codemirror": "^5.65.2",
"compression-webpack-plugin": "9.2.0", "compression-webpack-plugin": "9.2.0",
"concurrently": "^7.0.0", "concurrently": "^7.0.0",
"file-saver": "^2.0.5",
"lint-staged": "^13.0.3", "lint-staged": "^13.0.3",
"monaco-editor": "0.33.0", "monaco-editor": "0.33.0",
"nodemon": "^2.0.15", "nodemon": "^2.0.15",
@@ -154,7 +159,6 @@
"tslib": "^2.4.0", "tslib": "^2.4.0",
"tsx": "^3.12.3", "tsx": "^3.12.3",
"typescript": "4.8.4", "typescript": "4.8.4",
"umi-request": "^1.4.0",
"vh-check": "^2.0.5", "vh-check": "^2.0.5",
"virtualizedtableforantd4": "1.3.0", "virtualizedtableforantd4": "1.3.0",
"webpack": "^5.70.0", "webpack": "^5.70.0",
+152 -124
View File
@@ -109,6 +109,9 @@ dependencies:
sqlite3: sqlite3:
specifier: git+https://github.com/whyour/node-sqlite3.git#v1.0.3 specifier: git+https://github.com/whyour/node-sqlite3.git#v1.0.3
version: github.com/whyour/node-sqlite3/3a00af0b5d7603b7f1b290032507320b18a6b741 version: github.com/whyour/node-sqlite3/3a00af0b5d7603b7f1b290032507320b18a6b741
tar:
specifier: ^6.1.15
version: 6.1.15
toad-scheduler: toad-scheduler:
specifier: ^1.6.0 specifier: ^1.6.0
version: 1.6.1 version: 1.6.1
@@ -156,6 +159,9 @@ devDependencies:
'@types/express-jwt': '@types/express-jwt':
specifier: ^6.0.4 specifier: ^6.0.4
version: 6.0.4 version: 6.0.4
'@types/file-saver':
specifier: ^2.0.5
version: 2.0.5
'@types/js-yaml': '@types/js-yaml':
specifier: ^4.0.5 specifier: ^4.0.5
version: 4.0.5 version: 4.0.5
@@ -201,12 +207,15 @@ devDependencies:
'@types/sockjs-client': '@types/sockjs-client':
specifier: ^1.5.1 specifier: ^1.5.1
version: 1.5.1 version: 1.5.1
'@types/tar':
specifier: ^6.1.5
version: 6.1.5
'@types/uuid': '@types/uuid':
specifier: ^8.3.4 specifier: ^8.3.4
version: 8.3.4 version: 8.3.4
'@umijs/max': '@umijs/max':
specifier: ^4.0.55 specifier: ^4.0.72
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) 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': '@umijs/ssr-darkreader':
specifier: ^4.9.45 specifier: ^4.9.45
version: 4.9.45 version: 4.9.45
@@ -219,6 +228,9 @@ devDependencies:
antd-img-crop: antd-img-crop:
specifier: ^4.2.3 specifier: ^4.2.3
version: 4.12.2(antd@4.24.10)(react-dom@18.2.0)(react@18.2.0) 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: codemirror:
specifier: ^5.65.2 specifier: ^5.65.2
version: 5.65.13 version: 5.65.13
@@ -228,6 +240,9 @@ devDependencies:
concurrently: concurrently:
specifier: ^7.0.0 specifier: ^7.0.0
version: 7.6.0 version: 7.6.0
file-saver:
specifier: ^2.0.5
version: 2.0.5
lint-staged: lint-staged:
specifier: ^13.0.3 specifier: ^13.0.3
version: 13.2.2 version: 13.2.2
@@ -294,9 +309,6 @@ devDependencies:
typescript: typescript:
specifier: 4.8.4 specifier: 4.8.4
version: 4.8.4 version: 4.8.4
umi-request:
specifier: ^1.4.0
version: 1.4.0
vh-check: vh-check:
specifier: ^2.0.5 specifier: ^2.0.5
version: 2.0.5 version: 2.0.5
@@ -3450,7 +3462,7 @@ packages:
react: react:
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.21.0 '@babel/runtime': 7.22.3
hoist-non-react-statics: 3.3.2 hoist-non-react-statics: 3.3.2
react: 18.1.0 react: 18.1.0
react-is: 16.13.1 react-is: 16.13.1
@@ -3465,7 +3477,7 @@ packages:
react: react:
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.21.0 '@babel/runtime': 7.22.3
hoist-non-react-statics: 3.3.2 hoist-non-react-statics: 3.3.2
react: 18.2.0 react: 18.2.0
react-is: 16.13.1 react-is: 16.13.1
@@ -3879,7 +3891,7 @@ packages:
postcss: postcss:
optional: true optional: true
dependencies: dependencies:
'@babel/core': 7.21.0 '@babel/core': 7.22.1
postcss: 8.4.24 postcss: 8.4.24
postcss-syntax: 0.36.2(postcss@8.4.24) postcss-syntax: 0.36.2(postcss@8.4.24)
transitivePeerDependencies: transitivePeerDependencies:
@@ -4248,6 +4260,10 @@ packages:
'@types/qs': 6.9.7 '@types/qs': 6.9.7
'@types/serve-static': 1.15.1 '@types/serve-static': 1.15.1
/@types/file-saver@2.0.5:
resolution: {integrity: sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==}
dev: true
/@types/graceful-fs@4.1.6: /@types/graceful-fs@4.1.6:
resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==}
dependencies: dependencies:
@@ -4457,6 +4473,13 @@ packages:
'@types/node': 17.0.45 '@types/node': 17.0.45
dev: true 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: /@types/triple-beam@1.3.2:
resolution: {integrity: sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==} resolution: {integrity: sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==}
dev: false dev: false
@@ -4692,21 +4715,21 @@ packages:
eslint-visitor-keys: 3.4.1 eslint-visitor-keys: 3.4.1
dev: true dev: true
/@umijs/ast@4.0.70: /@umijs/ast@4.0.72:
resolution: {integrity: sha512-scrAlEGzgD3Ks/cRSJZza5QCPsdnZdtgPNcgpPU8xV4mXyWGyg98u9o2EE08awQDjqlPbHIvo7ZVZkvcC9nxnQ==} resolution: {integrity: sha512-WatRvU09vsx4Hlu5hemPA7a+QK4pJvzmQz/9LxN/KVgn+wZXi717qHFLu5eoV6XO7HlFZaEBGq2aHpDj0ngA8w==}
dependencies: dependencies:
'@umijs/bundler-utils': 4.0.70 '@umijs/bundler-utils': 4.0.72
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: true dev: true
/@umijs/babel-preset-umi@4.0.70: /@umijs/babel-preset-umi@4.0.72:
resolution: {integrity: sha512-xF2KHPw3VH65nziiC8Bu68dQsm2+/DNqYC4upgEyiuNwsLx4P+yzffFGkKfDsEpWAtYqAfClDT9m6o46inRxrA==} resolution: {integrity: sha512-9L2zwcux8iMOD9ji6YK1kiFbA9ZI1o0O/9NJo69QdCv3N41ENXWeNfXVu72GD6+mytmKZEWNCtD0qAzBTXj5jQ==}
dependencies: dependencies:
'@babel/runtime': 7.21.0 '@babel/runtime': 7.21.0
'@bloomberg/record-tuple-polyfill': 0.0.4 '@bloomberg/record-tuple-polyfill': 0.0.4
'@umijs/bundler-utils': 4.0.70 '@umijs/bundler-utils': 4.0.72
'@umijs/utils': 4.0.70 '@umijs/utils': 4.0.72
babel-plugin-styled-components: 2.1.1 babel-plugin-styled-components: 2.1.1
core-js: 3.28.0 core-js: 3.28.0
transitivePeerDependencies: transitivePeerDependencies:
@@ -4714,12 +4737,12 @@ packages:
- supports-color - supports-color
dev: true dev: true
/@umijs/bundler-esbuild@4.0.70: /@umijs/bundler-esbuild@4.0.72:
resolution: {integrity: sha512-elDtAGD/sVgY626E6OfhSmZbgXYmYBIe1uiTunQrbWzlHUP2lQ9iB4wJ6GGcoqNU/7itKXRf3rcihCIq2DCDtQ==} resolution: {integrity: sha512-T7nonD78F6RG94xATF5n/KkdJCOVYukokGFDAd4nPTNhbdYVakgNqwpRVwLEFofYMAN9uJ7rIUKFEt3qMpFR7w==}
hasBin: true hasBin: true
dependencies: dependencies:
'@umijs/bundler-utils': 4.0.70 '@umijs/bundler-utils': 4.0.72
'@umijs/utils': 4.0.70 '@umijs/utils': 4.0.72
enhanced-resolve: 5.9.3 enhanced-resolve: 5.9.3
postcss: 8.4.24 postcss: 8.4.24
postcss-flexbugs-fixes: 5.0.2(postcss@8.4.24) postcss-flexbugs-fixes: 5.0.2(postcss@8.4.24)
@@ -4728,10 +4751,10 @@ packages:
- supports-color - supports-color
dev: true dev: true
/@umijs/bundler-utils@4.0.70: /@umijs/bundler-utils@4.0.72:
resolution: {integrity: sha512-ZvM2Ga+BoHo8OonrmptCR1Bo/mjbtbXJVJmMQCSrb/mtn2ZFvOGddZ/0YTL+ysXnBIA7vALnlNhGWnvArCls6w==} resolution: {integrity: sha512-ROGNx6dy3tiMwhC29F6xvWC9O3F4CXnND2raupljTk+QDuvc1hmwUiB/gmCWrts/98cKN2959js03ivIPn9NNw==}
dependencies: dependencies:
'@umijs/utils': 4.0.70 '@umijs/utils': 4.0.72
esbuild: 0.17.19 esbuild: 0.17.19
regenerate: 1.4.2 regenerate: 1.4.2
regenerate-unicode-properties: 10.1.0 regenerate-unicode-properties: 10.1.0
@@ -4740,13 +4763,13 @@ packages:
- supports-color - supports-color
dev: true dev: true
/@umijs/bundler-vite@4.0.70(@types/node@17.0.45): /@umijs/bundler-vite@4.0.72(@types/node@17.0.45):
resolution: {integrity: sha512-19aDfNxPbOVfFttNSHEp9DnZFB/fFMEpsH6nBPkOIkXhr0UnmaWeOk7HJPbbT9T7vBA0mPxHA/vEIZPSWy84PQ==} resolution: {integrity: sha512-dsinf6yMW66ZAijHYTrNgzwPfSBXtEd+UxsB2gZFgsdVDDvE19Htuee4HqBCGBr+W66SLn99addckT8mzivfiA==}
hasBin: true hasBin: true
dependencies: dependencies:
'@svgr/core': 6.5.1 '@svgr/core': 6.5.1
'@umijs/bundler-utils': 4.0.70 '@umijs/bundler-utils': 4.0.72
'@umijs/utils': 4.0.70 '@umijs/utils': 4.0.72
'@vitejs/plugin-react': 4.0.0(vite@4.3.1) '@vitejs/plugin-react': 4.0.0(vite@4.3.1)
less: 4.1.3 less: 4.1.3
postcss-preset-env: 7.5.0(postcss@8.4.24) postcss-preset-env: 7.5.0(postcss@8.4.24)
@@ -4763,8 +4786,8 @@ packages:
- terser - terser
dev: true dev: true
/@umijs/bundler-webpack@4.0.70(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1): /@umijs/bundler-webpack@4.0.72(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1):
resolution: {integrity: sha512-QHaIEFesPzFSHnIMhHui9Ru54VYwNdnO683CzSoIoBjYAIcpDsGM7Tl3hIesujbfhrZl2eCJQVc//ZJ0SEFiTw==} resolution: {integrity: sha512-0oTvna4AdMoSvRWeF0E8u8/aKASwll226DkLKvjYDs5FE9nhbxIHevsoSRX7TnlEDPNfV/a3kVRnRG1dR2oJnQ==}
hasBin: true hasBin: true
dependencies: dependencies:
'@pmmmwh/react-refresh-webpack-plugin': 0.5.10(react-refresh@0.14.0)(sockjs-client@1.6.1)(webpack@5.85.1) '@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-jsx': 6.5.1(@svgr/core@6.5.1)
'@svgr/plugin-svgo': 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 '@types/hapi__joi': 17.1.9
'@umijs/babel-preset-umi': 4.0.70 '@umijs/babel-preset-umi': 4.0.72
'@umijs/bundler-utils': 4.0.70 '@umijs/bundler-utils': 4.0.72
'@umijs/case-sensitive-paths-webpack-plugin': 1.0.1 '@umijs/case-sensitive-paths-webpack-plugin': 1.0.1
'@umijs/mfsu': 4.0.70 '@umijs/mfsu': 4.0.72
'@umijs/utils': 4.0.70 '@umijs/utils': 4.0.72
cors: 2.8.5 cors: 2.8.5
css-loader: 6.7.1(webpack@5.85.1) css-loader: 6.7.1(webpack@5.85.1)
es5-imcompatible-versions: 0.1.83 es5-imcompatible-versions: 0.1.83
@@ -4805,11 +4828,11 @@ packages:
resolution: {integrity: sha512-kDKJ8yTarxwxGJDInG33hOpaQRZ//XpNuuznQ/1Mscypw6kappzFmrBr2dOYave++K7JHouoANF354UpbEQw0Q==} resolution: {integrity: sha512-kDKJ8yTarxwxGJDInG33hOpaQRZ//XpNuuznQ/1Mscypw6kappzFmrBr2dOYave++K7JHouoANF354UpbEQw0Q==}
dev: true dev: true
/@umijs/core@4.0.70: /@umijs/core@4.0.72:
resolution: {integrity: sha512-l2Hv8dRAJ6F9FD7VCUBAD2ars+yRBf7woQu8O88cWOgLbI/YOYEnt1n74g0uTfQBFdvnKNAsZhTY8yX8i0olGQ==} resolution: {integrity: sha512-E4+V/SuM8hcnmX/B+phU24LtNvV5Y7DY2ggtkQbJXtEGVFIImeC2ZEEWvGCFzq3soRCCTYG3Iwun4/TpInWIdg==}
dependencies: dependencies:
'@umijs/bundler-utils': 4.0.70 '@umijs/bundler-utils': 4.0.72
'@umijs/utils': 4.0.70 '@umijs/utils': 4.0.72
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: true dev: true
@@ -4850,6 +4873,7 @@ packages:
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
@@ -4859,6 +4883,7 @@ packages:
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
@@ -4868,6 +4893,7 @@ packages:
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
@@ -4877,6 +4903,7 @@ packages:
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
@@ -4917,19 +4944,19 @@ packages:
/@umijs/history@5.3.1: /@umijs/history@5.3.1:
resolution: {integrity: sha512-/e0cEGrR2bIWQD7pRl3dl9dcyRGeC9hoW0OCvUTT/hjY0EfUrkd6G8ZanVghPMpDuY5usxq9GVcvrT8KNXLWvA==} resolution: {integrity: sha512-/e0cEGrR2bIWQD7pRl3dl9dcyRGeC9hoW0OCvUTT/hjY0EfUrkd6G8ZanVghPMpDuY5usxq9GVcvrT8KNXLWvA==}
dependencies: dependencies:
'@babel/runtime': 7.21.0 '@babel/runtime': 7.22.3
query-string: 6.14.1 query-string: 6.14.1
dev: true dev: true
/@umijs/lint@4.0.70(eslint@8.35.0)(stylelint@14.8.2)(typescript@4.8.4): /@umijs/lint@4.0.72(eslint@8.35.0)(stylelint@14.8.2)(typescript@4.8.4):
resolution: {integrity: sha512-89+1BC/taDfEcubrWGXI6Yzk6hVb3br21jx+7eYYOwJjOXDMULy3+8GCFqZN+TxIz9WXOG3NFHehcFehx9YPwg==} resolution: {integrity: sha512-kH3L81Rex+jj5WeyJjR2G6yI1/0KFpr91ZtXeMy9Iyd4G7mEUJl3Fl/9iUEwZ2sgUa7kEJ+28H43eER5W4A6bg==}
dependencies: dependencies:
'@babel/core': 7.21.0 '@babel/core': 7.21.0
'@babel/eslint-parser': 7.19.1(@babel/core@7.21.0)(eslint@8.35.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) '@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/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) '@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-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: 7.32.2(eslint@8.35.0)
eslint-plugin-react-hooks: 4.6.0(eslint@8.35.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.35.0)
@@ -4950,16 +4977,16 @@ packages:
- typescript - typescript
dev: true 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): /@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-5Fsob8SV1OLLunQOs4jmRQfCbp/pefkmaRkDEIisklBkSdEz+Oj6fa+TzijBoSR1h71aHg1iJIu5UWLRDOhKSg==} resolution: {integrity: sha512-5e5BwaSBCdGWlj0PZ2I73+WLLe/8AibgDt2OtXc86YujRkhSKGY740pYgqy2PAszV8MjDMg8Sqs+gZG13C5z8w==}
hasBin: true hasBin: true
dependencies: dependencies:
'@umijs/lint': 4.0.70(eslint@8.35.0)(stylelint@14.8.2)(typescript@4.8.4) '@umijs/lint': 4.0.72(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/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) antd: 4.24.10(react-dom@18.2.0)(react@18.2.0)
eslint: 8.35.0 eslint: 8.35.0
stylelint: 14.8.2 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: transitivePeerDependencies:
- '@babel/core' - '@babel/core'
- '@reduxjs/toolkit' - '@reduxjs/toolkit'
@@ -5001,26 +5028,26 @@ packages:
- webpack-plugin-serve - webpack-plugin-serve
dev: true dev: true
/@umijs/mfsu@4.0.70: /@umijs/mfsu@4.0.72:
resolution: {integrity: sha512-Kg4SfEvU90DW9Nxfr/WozWduQmGvKAWEyUUrn6ND8i3AapUA8MOYDWRVZ/61HKHBcPt9Y6ZPg2cLWSFeNk529g==} resolution: {integrity: sha512-oyWNIRVK6/FCewMZ8jjn5ICTWWZ7VWP7javN1/zLczvPy2s3cMp5lZuN2Ca39v2Px/DfkCBKSiB/EI6Kts0G8A==}
dependencies: dependencies:
'@umijs/bundler-esbuild': 4.0.70 '@umijs/bundler-esbuild': 4.0.72
'@umijs/bundler-utils': 4.0.70 '@umijs/bundler-utils': 4.0.72
'@umijs/utils': 4.0.70 '@umijs/utils': 4.0.72
enhanced-resolve: 5.9.3 enhanced-resolve: 5.9.3
is-equal: 1.6.4 is-equal: 1.6.4
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: true dev: true
/@umijs/plugin-run@4.0.70: /@umijs/plugin-run@4.0.72:
resolution: {integrity: sha512-9hTRdY3+UVqptiK7SFFqojGDFMrHngyPPHQNu1amxIIi2oBEim4mLq3XOGPzy4c3hA7K6BHLEZlWSsmTGpbudw==} resolution: {integrity: sha512-z5p5z8BNcDb5LxbbeLoB7EfDesaSxSO9zFa3IbulgGFM/qfnaPPwfUEJDYLXkggxiDhbiUP95TDvC1R6AGDWBg==}
dependencies: dependencies:
tsx: 3.12.7 tsx: 3.12.7
dev: true 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): /@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-3Hu79VZJpwcn9HFZWH/katqrk6YryMpfMYJOdgzbdyAW4mipPt9Oa9x+ZYVWgr754kqY2X4IFqLhb3Y6+rO0Jg==} resolution: {integrity: sha512-AaCEjVdvSF+ilnATisBApMYMWVsLvBD4BUjkNV3SIaKHCLpSDeVNVyIFBm4A8tWLz+tz0p25Cu7SWGuggqQdQA==}
dependencies: dependencies:
'@ahooksjs/use-request': 2.8.15(react@18.2.0) '@ahooksjs/use-request': 2.8.15(react@18.2.0)
'@ant-design/antd-theme-variable': 1.0.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) '@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': 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) '@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) '@umijs/valtio': 1.0.3(react@18.2.0)
antd-dayjs-webpack-plugin: 1.0.6(dayjs@1.11.8) antd-dayjs-webpack-plugin: 1.0.6(dayjs@1.11.8)
axios: 0.27.2 axios: 0.27.2
@@ -5067,28 +5094,28 @@ packages:
- supports-color - supports-color
dev: true 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): /@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-N9TQbuaZNz+3HTtXm1QG+LpALJx/XLEJk1CYfff8Ey3hgQVRtvR/hTo6EhsUtovoqdcfBClxidHAfy9dvJ9Ebw==} resolution: {integrity: sha512-DDRzPCyP2K667YrOHrexAPcEacTdA9/TBn73erwreZpi+c4qhFZ2eYQo27SAGAhfZlYDYthL8JAfbgfhYmLo1A==}
dependencies: dependencies:
'@iconify/utils': 2.1.1 '@iconify/utils': 2.1.1
'@svgr/core': 6.5.1 '@svgr/core': 6.5.1
'@umijs/ast': 4.0.70 '@umijs/ast': 4.0.72
'@umijs/babel-preset-umi': 4.0.70 '@umijs/babel-preset-umi': 4.0.72
'@umijs/bundler-esbuild': 4.0.70 '@umijs/bundler-esbuild': 4.0.72
'@umijs/bundler-utils': 4.0.70 '@umijs/bundler-utils': 4.0.72
'@umijs/bundler-vite': 4.0.70(@types/node@17.0.45) '@umijs/bundler-vite': 4.0.72(@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/bundler-webpack': 4.0.72(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1)
'@umijs/core': 4.0.70 '@umijs/core': 4.0.72
'@umijs/did-you-know': 1.0.3 '@umijs/did-you-know': 1.0.3
'@umijs/es-module-parser': 0.0.7 '@umijs/es-module-parser': 0.0.7
'@umijs/history': 5.3.1 '@umijs/history': 5.3.1
'@umijs/mfsu': 4.0.70 '@umijs/mfsu': 4.0.72
'@umijs/plugin-run': 4.0.70 '@umijs/plugin-run': 4.0.72
'@umijs/renderer-react': 4.0.70(react-dom@18.1.0)(react@18.1.0) '@umijs/renderer-react': 4.0.72(react-dom@18.1.0)(react@18.1.0)
'@umijs/server': 4.0.70 '@umijs/server': 4.0.72
'@umijs/ui': 3.0.1 '@umijs/ui': 3.0.1
'@umijs/utils': 4.0.70 '@umijs/utils': 4.0.72
'@umijs/zod2ts': 4.0.70 '@umijs/zod2ts': 4.0.72
babel-plugin-dynamic-import-node: 2.3.3 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) 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 core-js: 3.28.0
@@ -5124,8 +5151,8 @@ packages:
- webpack-plugin-serve - webpack-plugin-serve
dev: true dev: true
/@umijs/renderer-react@4.0.70(react-dom@18.1.0)(react@18.1.0): /@umijs/renderer-react@4.0.72(react-dom@18.1.0)(react@18.1.0):
resolution: {integrity: sha512-TcqCd6uwkVyy7vvZ+yi49q/dcfxHOXihz6GJbOBkH4grkeaX2hwisJkU6RG1kwXeyv0ShIZCi4ewiCTnCSJb2g==} resolution: {integrity: sha512-eOJgxbwFR23wWMvR2FFcSy0Ba8d7MtOit68SE+PfX43Gy/51Ywsa1BF1G9QNgs3UAjWKK5DpK1UCHrgQ4cgegA==}
peerDependencies: peerDependencies:
react: '>=16.8 || 18' react: '>=16.8 || 18'
react-dom: '>=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) react-router-dom: 6.3.0(react-dom@18.1.0)(react@18.1.0)
dev: true dev: true
/@umijs/renderer-react@4.0.70(react-dom@18.2.0)(react@18.2.0): /@umijs/renderer-react@4.0.72(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-TcqCd6uwkVyy7vvZ+yi49q/dcfxHOXihz6GJbOBkH4grkeaX2hwisJkU6RG1kwXeyv0ShIZCi4ewiCTnCSJb2g==} resolution: {integrity: sha512-eOJgxbwFR23wWMvR2FFcSy0Ba8d7MtOit68SE+PfX43Gy/51Ywsa1BF1G9QNgs3UAjWKK5DpK1UCHrgQ4cgegA==}
peerDependencies: peerDependencies:
react: '>=16.8 || 18' react: '>=16.8 || 18'
react-dom: '>=16.8 || 18' react-dom: '>=16.8 || 18'
@@ -5177,10 +5204,10 @@ packages:
resolution: {integrity: sha512-+1ixf1BTOLuH+ORb4x8vYMPeIt38n9q0fJDwhv9nSxrV46mxbLF0nmELIo9CKQB2gHfuC4+hww6xejJ6VYnBHQ==} resolution: {integrity: sha512-+1ixf1BTOLuH+ORb4x8vYMPeIt38n9q0fJDwhv9nSxrV46mxbLF0nmELIo9CKQB2gHfuC4+hww6xejJ6VYnBHQ==}
dev: true dev: true
/@umijs/server@4.0.70: /@umijs/server@4.0.72:
resolution: {integrity: sha512-aoTjXCe1hDjWTNxJ8c5XZRbur+H7feifG07SBe+2kc6EIpykvtRwgp7dZmMHlgmv7ptaXmSAZjWAUDdS0NhLyg==} resolution: {integrity: sha512-J6seC7HPZIRoDirCEelEdXzeTJsvg5FpxRkV3BP+ZS3xsg140ckGQRdCzqykJ2ZZHjhxpdJsLtdleAmE8Iq6Ew==}
dependencies: dependencies:
'@umijs/bundler-utils': 4.0.70 '@umijs/bundler-utils': 4.0.72
history: 5.3.0 history: 5.3.0
react: 18.1.0 react: 18.1.0
react-dom: 18.1.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==} resolution: {integrity: sha512-XlcwzSYQ/SRZpHdwIyMDS4FOGX5kP4U/2g2mykyn/iPQTK4xTiQAyBu6UnnDnn7d5P8s7Atzh1C7H0ETNOypJg==}
dev: true dev: true
/@umijs/test@4.0.70: /@umijs/test@4.0.72:
resolution: {integrity: sha512-DFu65yo8QIPKvw/p0/7Tm87ViLk+fCtfbWMQ8e4o2u17mCKgmaQP6X+GCHWs94lgtRYHjM3zHjUUvM+UKynLHg==} resolution: {integrity: sha512-sSknbprNSQhVcyIIfOlRMay9yyG189XdmIgoJWHG0sqKOaJLt0iEJ4RkOWGv9YVnW7M06cE6+8+FGUEF8/lFXw==}
dependencies: dependencies:
'@babel/plugin-transform-modules-commonjs': 7.21.2 '@babel/plugin-transform-modules-commonjs': 7.21.2
'@jest/types': 27.5.1 '@jest/types': 27.5.1
'@umijs/bundler-utils': 4.0.70 '@umijs/bundler-utils': 4.0.72
'@umijs/utils': 4.0.70 '@umijs/utils': 4.0.72
babel-jest: 29.5.0 babel-jest: 29.5.0
esbuild: 0.17.19 esbuild: 0.17.19
identity-obj-proxy: 3.0.0 identity-obj-proxy: 3.0.0
@@ -5224,8 +5251,8 @@ packages:
react: 18.2.0 react: 18.2.0
dev: true dev: true
/@umijs/utils@4.0.70: /@umijs/utils@4.0.72:
resolution: {integrity: sha512-ZfDrtE7GtfYsdd5QwJiZHLMql8ZbyzUw37S7eCgIl5RTOxec1Ojqbzpfis7j8nyWyMGd/PpsuUl5909gA0U9bg==} resolution: {integrity: sha512-+BOOGCipnr3iEzAliYrfFQeyQd3DrT1vMMXlsBqyD3Qh1owrSb/FsTvFTUYU0jrVgBc3MR5UneEBcPbqxq36Pw==}
dependencies: dependencies:
chokidar: 3.5.3 chokidar: 3.5.3
pino: 7.11.0 pino: 7.11.0
@@ -5239,8 +5266,8 @@ packages:
- react - react
dev: true dev: true
/@umijs/zod2ts@4.0.70: /@umijs/zod2ts@4.0.72:
resolution: {integrity: sha512-W7Uvyb9Rx3OjUuxrgTaUdkffHNH8yEVG2TW0AgEirNL0os/mC6Wp60r5lRCftgVe+sh9d6L0x2eqp05065fTzg==} resolution: {integrity: sha512-qjfoAT7yODzKaj9AxOM0qh8e3rpPe7xvUq6ux+0yaxNrt+JbjC6gy66S3f836NpforMBCWEOuSLbfdyVhzKxNQ==}
dev: true dev: true
/@vitejs/plugin-react@4.0.0(vite@4.3.1): /@vitejs/plugin-react@4.0.0(vite@4.3.1):
@@ -5829,6 +5856,16 @@ packages:
- debug - debug
dev: true 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: /babel-jest@29.5.0:
resolution: {integrity: sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==} resolution: {integrity: sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -7404,8 +7441,11 @@ packages:
/encoding@0.1.13: /encoding@0.1.13:
resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
requiresBuild: true
dependencies: dependencies:
iconv-lite: 0.6.3 iconv-lite: 0.6.3
dev: false
optional: true
/end-of-stream@1.4.4: /end-of-stream@1.4.4:
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
@@ -8085,6 +8125,10 @@ packages:
flat-cache: 3.0.4 flat-cache: 3.0.4
dev: true 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: /file-uri-to-path@2.0.0:
resolution: {integrity: sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==} resolution: {integrity: sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==}
engines: {node: '>= 6'} engines: {node: '>= 6'}
@@ -8628,7 +8672,7 @@ packages:
/history@5.3.0: /history@5.3.0:
resolution: {integrity: sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==} resolution: {integrity: sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==}
dependencies: dependencies:
'@babel/runtime': 7.21.0 '@babel/runtime': 7.22.3
dev: true dev: true
/hmac-drbg@1.0.1: /hmac-drbg@1.0.1:
@@ -9323,13 +9367,6 @@ packages:
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dev: true 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: /isomorphic-unfetch@4.0.2:
resolution: {integrity: sha512-1Yd+CF/7al18/N2BDbsLBcp6RO3tucSW+jcLq24dqdX5MNbCNTw1z4BsGsp4zNmjr/Izm2cs/cEqZPp4kvWSCA==} resolution: {integrity: sha512-1Yd+CF/7al18/N2BDbsLBcp6RO3tucSW+jcLq24dqdX5MNbCNTw1z4BsGsp4zNmjr/Izm2cs/cEqZPp4kvWSCA==}
dependencies: dependencies:
@@ -9645,6 +9682,7 @@ packages:
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
@@ -9654,6 +9692,7 @@ packages:
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
@@ -9663,6 +9702,7 @@ packages:
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
@@ -9672,6 +9712,7 @@ packages:
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
@@ -10212,6 +10253,11 @@ packages:
yallist: 4.0.0 yallist: 4.0.0
dev: false dev: false
/minipass@4.2.8:
resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==}
engines: {node: '>=8'}
dev: true
/minipass@5.0.0: /minipass@5.0.0:
resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -10362,13 +10408,6 @@ packages:
engines: {node: '>=10.5.0'} engines: {node: '>=10.5.0'}
dev: true 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: /node-fetch@2.6.11:
resolution: {integrity: sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==} resolution: {integrity: sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==}
engines: {node: 4.x || >=6.0.0} engines: {node: 4.x || >=6.0.0}
@@ -12855,7 +12894,7 @@ packages:
react-dom: react-dom:
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.21.0 '@babel/runtime': 7.22.3
invariant: 2.2.4 invariant: 2.2.4
prop-types: 15.8.1 prop-types: 15.8.1
react: 18.1.0 react: 18.1.0
@@ -12875,7 +12914,7 @@ packages:
react-dom: react-dom:
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.21.0 '@babel/runtime': 7.22.3
invariant: 2.2.4 invariant: 2.2.4
prop-types: 15.8.1 prop-types: 15.8.1
react: 18.2.0 react: 18.2.0
@@ -14702,28 +14741,21 @@ packages:
hasBin: true hasBin: true
dev: true dev: true
/umi-request@1.4.0: /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-OknwtQZddZHi0Ggi+Vr/olJ7HNMx4AzlywyK0W3NZBT7B0stjeZ9lcztA85dBgdAj3KVk8uPJPZSnGaDjELhrA==} resolution: {integrity: sha512-VXXwhHtZAApRR02c2F+uDv84m/Bf5g56pMKrArtIUFsrWM8hqS3f7whzgpdjzh0H8EFjpXwan0kJrkXFr6dAPg==}
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==}
engines: {node: '>=14'} engines: {node: '>=14'}
hasBin: true hasBin: true
dependencies: dependencies:
'@babel/runtime': 7.21.0 '@babel/runtime': 7.21.0
'@umijs/bundler-utils': 4.0.70 '@umijs/bundler-utils': 4.0.72
'@umijs/bundler-webpack': 4.0.70(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1) '@umijs/bundler-webpack': 4.0.72(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1)
'@umijs/core': 4.0.70 '@umijs/core': 4.0.72
'@umijs/lint': 4.0.70(eslint@8.35.0)(stylelint@14.8.2)(typescript@4.8.4) '@umijs/lint': 4.0.72(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/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.70(react-dom@18.2.0)(react@18.2.0) '@umijs/renderer-react': 4.0.72(react-dom@18.2.0)(react@18.2.0)
'@umijs/server': 4.0.70 '@umijs/server': 4.0.72
'@umijs/test': 4.0.70 '@umijs/test': 4.0.72
'@umijs/utils': 4.0.70 '@umijs/utils': 4.0.72
prettier-plugin-organize-imports: 3.2.2(prettier@2.8.8)(typescript@4.8.4) 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) prettier-plugin-packagejson: 2.4.3(prettier@2.8.8)
transitivePeerDependencies: transitivePeerDependencies:
@@ -15197,10 +15229,6 @@ packages:
resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==}
engines: {node: '>=0.8.0'} engines: {node: '>=0.8.0'}
/whatwg-fetch@3.6.2:
resolution: {integrity: sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==}
dev: true
/whatwg-url@5.0.0: /whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
dependencies: dependencies:
+44
View File
@@ -141,6 +141,11 @@ let SMTP_EMAIL = '';
let SMTP_PASSWORD = ''; let SMTP_PASSWORD = '';
let SMTP_NAME = ''; let SMTP_NAME = '';
// =======================================PushMe通知设置区域===========================================
//官方文档:https://push.i-i.me/
//此处填你的PushMe KEY.
let PUSHME_KEY = '';
//==========================云端环境变量的判断与接收========================= //==========================云端环境变量的判断与接收=========================
if (process.env.GOTIFY_URL) { if (process.env.GOTIFY_URL) {
GOTIFY_URL = process.env.GOTIFY_URL; GOTIFY_URL = process.env.GOTIFY_URL;
@@ -288,6 +293,9 @@ if (process.env.SMTP_PASSWORD) {
if (process.env.SMTP_NAME) { if (process.env.SMTP_NAME) {
SMTP_NAME = process.env.SMTP_NAME; SMTP_NAME = process.env.SMTP_NAME;
} }
if (process.env.PUSHME_KEY) {
PUSHME_KEY = process.env.PUSHME_KEY;
}
//==========================云端环境变量的判断与接收========================= //==========================云端环境变量的判断与接收=========================
/** /**
@@ -336,6 +344,7 @@ async function sendNotify(
aibotkNotify(text, desp), //智能微秘书 aibotkNotify(text, desp), //智能微秘书
fsBotNotify(text, desp), //飞书机器人 fsBotNotify(text, desp), //飞书机器人
smtpNotify(text, desp), //SMTP 邮件 smtpNotify(text, desp), //SMTP 邮件
PushMeNotify(text, desp), //PushMe
]); ]);
} }
@@ -1117,6 +1126,41 @@ function smtpNotify(text, desp) {
}); });
} }
function PushMeNotify(text, desp) {
return new Promise((resolve) => {
if (PUSHME_KEY) {
const options = {
url: `https://push.i-i.me?push_key=${PUSHME_KEY}`,
json: { title: text, content: desp },
headers: {
'Content-Type': 'application/json',
},
timeout,
};
$.post(options, (err, resp, data) => {
try {
if (err) {
console.log('PushMeNotify发送通知调用API失败!!\n');
console.log(err);
} else {
if (data === 'success') {
console.log('PushMe发送通知消息成功🎉\n');
} else {
console.log(`${data}\n`);
}
}
} catch (e) {
$.logErr(e, resp);
} finally {
resolve(data);
}
});
} else {
resolve();
}
});
}
module.exports = { module.exports = {
sendNotify, sendNotify,
BARK_PUSH, BARK_PUSH,
+25
View File
@@ -98,6 +98,8 @@ push_config = {
'SMTP_EMAIL': '', # SMTP 收发件邮箱,通知将会由自己发给自己 'SMTP_EMAIL': '', # SMTP 收发件邮箱,通知将会由自己发给自己
'SMTP_PASSWORD': '', # SMTP 登录密码,也可能为特殊口令,视具体邮件服务商说明而定 'SMTP_PASSWORD': '', # SMTP 登录密码,也可能为特殊口令,视具体邮件服务商说明而定
'SMTP_NAME': '', # SMTP 收发件人姓名,可随意填写 'SMTP_NAME': '', # SMTP 收发件人姓名,可随意填写
'PUSHME_KEY': '', # PushMe 酱的 PUSHME_KEY
} }
notify_function = [] notify_function = []
# fmt: on # fmt: on
@@ -637,6 +639,27 @@ def smtp(title: str, content: str) -> None:
except Exception as e: except Exception as e:
print(f"SMTP 邮件 推送失败!{e}") print(f"SMTP 邮件 推送失败!{e}")
def pushme(title: str, content: str) -> None:
"""
使用 PushMe 推送消息。
"""
if not push_config.get("PUSHME_KEY"):
print("PushMe 服务的 PUSHME_KEY 未设置!!\n取消推送")
return
print("PushMe 服务启动")
url = f'https://push.i-i.me/?push_key={push_config.get("PUSHME_KEY")}'
data = {
"title": title,
"content": content,
}
response = requests.post(url, data=data)
if response == 'success':
print("PushMe 推送成功!")
else:
print("PushMe 推送失败!{response}")
def one() -> str: def one() -> str:
""" """
@@ -692,6 +715,8 @@ if (
and push_config.get("SMTP_NAME") and push_config.get("SMTP_NAME")
): ):
notify_function.append(smtp) notify_function.append(smtp)
if push_config.get("PUSHME_KEY"):
notify_function.append(pushme)
def send(title: str, content: str) -> None: def send(title: str, content: str) -> None:
+6 -70
View File
@@ -2,6 +2,7 @@
## 目录 ## 目录
dir_root=$QL_DIR dir_root=$QL_DIR
dir_tmp=$dir_root/.tmp
dir_data=$dir_root/data dir_data=$dir_root/data
dir_shell=$dir_root/shell dir_shell=$dir_root/shell
dir_sample=$dir_root/sample dir_sample=$dir_root/sample
@@ -175,6 +176,7 @@ define_cmd() {
} }
fix_config() { fix_config() {
make_dir $dir_tmp
make_dir $dir_static make_dir $dir_static
make_dir $dir_data make_dir $dir_data
make_dir $dir_config make_dir $dir_config
@@ -263,6 +265,7 @@ npm_install_sub() {
else else
pnpm install --loglevel error --production pnpm install --loglevel error --production
fi fi
exit_status=$?
} }
npm_install_2() { npm_install_2() {
@@ -283,90 +286,22 @@ diff_and_copy() {
fi 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() { git_clone_scripts() {
local url="$1" local url="$1"
local dir="$2" local dir="$2"
local branch="$3" local branch="$3"
local proxy="$4" local proxy="$4"
[[ $branch ]] && local part_cmd="-b $branch " [[ $branch ]] && local part_cmd="-b $branch "
echo -e "开始拉取 $url$dir\n" echo -e "开始拉取仓库 ${uniq_path}$dir\n"
set_proxy "$proxy" set_proxy "$proxy"
git clone --depth=1 $part_cmd $url $dir git clone --depth=1 $part_cmd $url $dir
exit_status=$? exit_status=$?
reset_branch "$branch" "$dir"
unset_proxy 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() { random_range() {
local beg=$1 local beg=$1
local end=$2 local end=$2
@@ -378,7 +313,8 @@ reload_pm2() {
# 代理会影响 grpc 服务 # 代理会影响 grpc 服务
unset_proxy unset_proxy
pm2 flush &>/dev/null pm2 flush &>/dev/null
pm2 startOrGracefulReload $file_ecosystem_js pm2 startOrGracefulReload $file_ecosystem_js --update-env
pm2 sendSignal SIGKILL panel &>/dev/null
} }
diff_time() { diff_time() {
+66 -22
View File
@@ -229,58 +229,98 @@ usage() {
echo -e "9. $cmd_update resettfa # 禁用两步登录" 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 ## 更新qinglong
update_qinglong() { update_qinglong() {
rm -rf ${dir_tmp}/*
local mirror="gitee" 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) local githubStatus=$(curl -s -m 2 -IL "https://google.com" | grep 200)
if [[ ! -z $githubStatus ]]; then if [[ ! -z $githubStatus ]]; then
mirror="github" mirror="github"
downloadQLUrl="https://github.com/whyour/qinglong/archive/refs/heads"
downloadStaticUrl="https://github.com/whyour/qinglong-static/archive/refs/heads"
fi fi
echo -e "使用 ${mirror} 源更新...\n" echo -e "使用 ${mirror} 源更新...\n"
export isFirstStartServer=false
local primary_branch="master" local primary_branch="master"
if [[ "${QL_BRANCH}" == "develop" ]]; then if [[ "${QL_BRANCH}" == "develop" ]]; then
primary_branch="develop" primary_branch="develop"
fi 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} wget -cqO "${dir_tmp}/ql.zip" "${downloadQLUrl}/${primary_branch}.zip"
git_pull_scripts $dir_root ${primary_branch} exit_status=$?
if [[ $exit_status -eq 0 ]]; then if [[ $exit_status -eq 0 ]]; then
echo -e "\n更新青龙源文件成功...\n" 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) unzip -oq ${dir_tmp}/ql.zip -d ${dir_tmp}
[[ "$ql_depend_old" != "$ql_depend_new" ]] && npm_install_2 $dir_root
update_qinglong_static "$1" "$primary_branch" update_qinglong_static
else else
echo -e "\n更新青龙源文件失败,请检查网络...\n" echo -e "\n更新青龙源文件失败,请检查网络...\n"
fi fi
} }
update_qinglong_static() { update_qinglong_static() {
local no_restart="$1" wget -cqO "${dir_tmp}/static.zip" "${downloadStaticUrl}/${primary_branch}.zip"
local primary_branch="$2" exit_status=$?
local url="https://${mirror}.com/whyour/qinglong-static.git"
rm -rf ${ql_static_repo} &>/dev/null
git_clone_scripts ${url} ${ql_static_repo} ${primary_branch}
if [[ $exit_status -eq 0 ]]; then if [[ $exit_status -eq 0 ]]; then
echo -e "\n更新青龙静态资源成功...\n" 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 reload_pm2
fi fi
else else
echo -e "\n更新青龙静态资源失败,请检查网络...\n" echo -e "\n依赖检测安装失败,请检查网络...\n"
fi fi
} }
@@ -456,7 +496,11 @@ main() {
case $p1 in case $p1 in
update) update)
fix_config fix_config
eval update_qinglong "$2" $cmd local needRestart=${p2:-"true"}
eval update_qinglong $cmd
;;
reload)
eval reload_qinglong "$p2" $cmd
;; ;;
extra) extra)
eval run_extra_shell $cmd eval run_extra_shell $cmd
+1 -2
View File
@@ -49,8 +49,7 @@ export interface SharedContext {
interface TSystemInfo { interface TSystemInfo {
branch: 'develop' | 'master'; branch: 'develop' | 'master';
isInitialized: boolean; isInitialized: boolean;
lastCommitId: string; publishTime: number;
lastCommitTime: number;
version: string; version: string;
changeLog: string; changeLog: string;
changeLogLink: string; changeLogLink: string;
+1 -3
View File
@@ -51,9 +51,7 @@ const Config = () => {
: value; : value;
request request
.post(`${config.apiPrefix}configs/save`, { .post(`${config.apiPrefix}configs/save`, { content, name: select })
data: { content, name: select },
})
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('保存成功'); message.success('保存成功');
+7 -13
View File
@@ -184,11 +184,9 @@ const CronDetailModal = ({
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
request request
.put(`${config.apiPrefix}scripts`, { .put(`${config.apiPrefix}scripts`, {
data: { filename: scriptInfo.filename,
filename: scriptInfo.filename, path: scriptInfo.parent || '',
path: scriptInfo.parent || '', content,
content,
},
}) })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
@@ -220,7 +218,7 @@ const CronDetailModal = ({
), ),
onOk() { onOk() {
request request
.put(`${config.apiPrefix}crons/run`, { data: [currentCron.id] }) .put(`${config.apiPrefix}crons/run`, [currentCron.id])
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
setCurrentCron({ ...currentCron, status: CrontabStatus.running }); setCurrentCron({ ...currentCron, status: CrontabStatus.running });
@@ -250,7 +248,7 @@ const CronDetailModal = ({
), ),
onOk() { onOk() {
request request
.put(`${config.apiPrefix}crons/stop`, { data: [currentCron.id] }) .put(`${config.apiPrefix}crons/stop`, [currentCron.id] )
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
setCurrentCron({ ...currentCron, status: CrontabStatus.idle }); setCurrentCron({ ...currentCron, status: CrontabStatus.idle });
@@ -282,9 +280,7 @@ const CronDetailModal = ({
`${config.apiPrefix}crons/${ `${config.apiPrefix}crons/${
currentCron.isDisabled === 1 ? 'enable' : 'disable' currentCron.isDisabled === 1 ? 'enable' : 'disable'
}`, }`,
{ [currentCron.id],
data: [currentCron.id],
},
) )
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
@@ -320,9 +316,7 @@ const CronDetailModal = ({
`${config.apiPrefix}crons/${ `${config.apiPrefix}crons/${
currentCron.isPinned === 1 ? 'unpin' : 'pin' currentCron.isPinned === 1 ? 'unpin' : 'pin'
}`, }`,
{ [currentCron.id],
data: [currentCron.id],
},
) )
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
+8 -11
View File
@@ -457,7 +457,7 @@ const Crontab = () => {
), ),
onOk() { onOk() {
request request
.put(`${config.apiPrefix}crons/run`, { data: [record.id] }) .put(`${config.apiPrefix}crons/run`, [record.id])
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
const result = [...value]; const result = [...value];
@@ -492,7 +492,7 @@ const Crontab = () => {
), ),
onOk() { onOk() {
request request
.put(`${config.apiPrefix}crons/stop`, { data: [record.id] }) .put(`${config.apiPrefix}crons/stop`, [record.id])
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
const result = [...value]; const result = [...value];
@@ -533,9 +533,7 @@ const Crontab = () => {
`${config.apiPrefix}crons/${ `${config.apiPrefix}crons/${
record.isDisabled === 1 ? 'enable' : 'disable' record.isDisabled === 1 ? 'enable' : 'disable'
}`, }`,
{ [record.id],
data: [record.id],
},
) )
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
@@ -577,9 +575,7 @@ const Crontab = () => {
`${config.apiPrefix}crons/${ `${config.apiPrefix}crons/${
record.isPinned === 1 ? 'unpin' : 'pin' record.isPinned === 1 ? 'unpin' : 'pin'
}`, }`,
{ [record.id],
data: [record.id],
},
) )
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
@@ -725,9 +721,10 @@ const Crontab = () => {
content: <>{OperationName[operationStatus]}</>, content: <>{OperationName[operationStatus]}</>,
onOk() { onOk() {
request request
.put(`${config.apiPrefix}crons/${OperationPath[operationStatus]}`, { .put(
data: selectedRowIds, `${config.apiPrefix}crons/${OperationPath[operationStatus]}`,
}) selectedRowIds,
)
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
getCrons(); getCrons();
+1 -27
View File
@@ -46,10 +46,7 @@ const CronLogModal = ({
const log = data as string; const log = data as string;
setValue(log || '暂无日志'); setValue(log || '暂无日志');
const hasNext = Boolean( const hasNext = Boolean(
log && log && !logEnded(log) && !log.includes('任务未运行'),
!logEnded(log) &&
!log.includes('重启面板') &&
!log.includes('任务未运行'),
); );
setExecuting(hasNext); setExecuting(hasNext);
autoScroll(); autoScroll();
@@ -58,29 +55,6 @@ const CronLogModal = ({
getCronLog(); getCronLog();
}, 2000); }, 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(() => { .finally(() => {
+5 -6
View File
@@ -25,9 +25,10 @@ const CronModal = ({
payload.id = cron.id; payload.id = cron.id;
} }
try { try {
const { code, data } = await request[method](`${config.apiPrefix}crons`, { const { code, data } = await request[method](
data: payload, `${config.apiPrefix}crons`,
}); payload,
);
if (code === 200) { if (code === 200) {
message.success(cron ? '更新Cron成功' : '新建Cron成功'); message.success(cron ? '更新Cron成功' : '新建Cron成功');
@@ -130,9 +131,7 @@ const CronLabelModal = ({
try { try {
const { code, data } = await request[action]( const { code, data } = await request[action](
`${config.apiPrefix}crons/labels`, `${config.apiPrefix}crons/labels`,
{ payload,
data: payload,
},
); );
if (code === 200) { if (code === 200) {
+1 -3
View File
@@ -80,9 +80,7 @@ const ViewCreateModal = ({
try { try {
const { code, data } = await request[method]( const { code, data } = await request[method](
`${config.apiPrefix}crons/views`, `${config.apiPrefix}crons/views`,
{ view ? { ...values, id: view.id } : values,
data: view ? { ...values, id: view.id } : values,
},
); );
if (code === 200) { if (code === 200) {
+6 -4
View File
@@ -168,9 +168,9 @@ const ViewManageModal = ({
const onShowChange = (checked: boolean, record: any, index: number) => { const onShowChange = (checked: boolean, record: any, index: number) => {
request request
.put(`${config.apiPrefix}crons/views/${checked ? 'enable' : 'disable'}`, { .put(`${config.apiPrefix}crons/views/${checked ? 'enable' : 'disable'}`, [
data: [record.id], record.id,
}) ])
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
const _list = [...list]; const _list = [...list];
@@ -195,7 +195,9 @@ const ViewManageModal = ({
const dragRow = list[dragIndex]; const dragRow = list[dragIndex];
request request
.put(`${config.apiPrefix}crons/views/move`, { .put(`${config.apiPrefix}crons/views/move`, {
data: { fromIndex: dragIndex, toIndex: hoverIndex, id: dragRow.id }, fromIndex: dragIndex,
toIndex: hoverIndex,
id: dragRow.id,
}) })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
+15 -25
View File
@@ -33,6 +33,7 @@ import DependenceLogModal from './logModal';
import { useOutletContext } from '@umijs/max'; import { useOutletContext } from '@umijs/max';
import { SharedContext } from '@/layouts'; import { SharedContext } from '@/layouts';
import useTableScrollHeight from '@/hooks/useTableScrollHeight'; import useTableScrollHeight from '@/hooks/useTableScrollHeight';
import dayjs from 'dayjs';
const { Text } = Typography; const { Text } = Typography;
const { Search } = Input; const { Search } = Input;
@@ -123,27 +124,20 @@ const Dependence = () => {
dataIndex: 'remark', dataIndex: 'remark',
key: 'remark', key: 'remark',
}, },
{
title: '更新时间',
key: 'updatedAt',
dataIndex: 'updatedAt',
render: (text: string) => {
return <span>{dayjs(text).format('YYYY-MM-DD HH:mm:ss')}</span>;
},
},
{ {
title: '创建时间', title: '创建时间',
key: 'timestamp', key: 'createdAt',
dataIndex: 'timestamp', dataIndex: 'createdAt',
render: (text: string, record: any) => { render: (text: string) => {
const language = navigator.language || navigator.languages[0]; return <span>{dayjs(text).format('YYYY-MM-DD HH:mm:ss')}</span>;
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>
);
}, },
}, },
{ {
@@ -275,9 +269,7 @@ const Dependence = () => {
), ),
onOk() { onOk() {
request request
.put(`${config.apiPrefix}dependencies/reinstall`, { .put(`${config.apiPrefix}dependencies/reinstall`, [record.id])
data: [record.id],
})
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
handleDependence(data[0]); handleDependence(data[0]);
@@ -348,9 +340,7 @@ const Dependence = () => {
content: <></>, content: <></>,
onOk() { onOk() {
request request
.put(`${config.apiPrefix}dependencies/reinstall`, { .put(`${config.apiPrefix}dependencies/reinstall`, selectedRowIds)
data: selectedRowIds,
})
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
setSelectedRowIds([]); setSelectedRowIds([]);
+6 -4
View File
@@ -48,9 +48,7 @@ const DependenceModal = ({
try { try {
const { code, data } = await request[method]( const { code, data } = await request[method](
`${config.apiPrefix}dependencies`, `${config.apiPrefix}dependencies`,
{ payload,
data: payload,
},
); );
if (code === 200) { if (code === 200) {
@@ -122,7 +120,11 @@ const DependenceModal = ({
name="name" name="name"
label="名称" label="名称"
rules={[ rules={[
{ required: true, message: '请输入依赖名称', whitespace: true }, {
required: true,
message: '请输入依赖名称,支持指定版本',
whitespace: true,
},
]} ]}
> >
<Input.TextArea <Input.TextArea
+2 -1
View File
@@ -48,7 +48,8 @@ const Diff = () => {
request request
.post(`${config.apiPrefix}configs/save`, { .post(`${config.apiPrefix}configs/save`, {
data: { content, name: current }, content,
name: current,
}) })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
+2 -4
View File
@@ -19,10 +19,8 @@ const EditNameModal = ({
setLoading(true); setLoading(true);
try { try {
const { code, data } = await request.put(`${config.apiPrefix}envs/name`, { const { code, data } = await request.put(`${config.apiPrefix}envs/name`, {
data: { ids,
ids, name: values.name,
name: values.name,
},
}); });
if (code === 200) { if (code === 200) {
+8 -10
View File
@@ -248,9 +248,7 @@ const Env = () => {
`${config.apiPrefix}envs/${ `${config.apiPrefix}envs/${
record.status === Status. ? 'enable' : 'disable' record.status === Status. ? 'enable' : 'disable'
}`, }`,
{ [record.id],
data: [record.id],
},
) )
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
@@ -388,7 +386,8 @@ const Env = () => {
const dragRow = value[dragIndex]; const dragRow = value[dragIndex];
request request
.put(`${config.apiPrefix}envs/${dragRow.id}/move`, { .put(`${config.apiPrefix}envs/${dragRow.id}/move`, {
data: { fromIndex: dragIndex, toIndex: hoverIndex }, fromIndex: dragIndex,
toIndex: hoverIndex,
}) })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
@@ -438,9 +437,10 @@ const Env = () => {
content: <>{OperationName[operationStatus]}</>, content: <>{OperationName[operationStatus]}</>,
onOk() { onOk() {
request request
.put(`${config.apiPrefix}envs/${OperationPath[operationStatus]}`, { .put(
data: selectedRowIds, `${config.apiPrefix}envs/${OperationPath[operationStatus]}`,
}) selectedRowIds,
)
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
getEnvs(); getEnvs();
@@ -477,9 +477,7 @@ const Env = () => {
try { try {
const { code, data } = await request.post( const { code, data } = await request.post(
`${config.apiPrefix}envs/upload`, `${config.apiPrefix}envs/upload`,
{ formData,
data: formData,
},
); );
if (code === 200) { if (code === 200) {
+4 -3
View File
@@ -37,9 +37,10 @@ const EnvModal = ({
payload = { ...values, id: env.id }; payload = { ...values, id: env.id };
} }
try { try {
const { code, data } = await request[method](`${config.apiPrefix}envs`, { const { code, data } = await request[method](
data: payload, `${config.apiPrefix}envs`,
}); payload,
);
if (code === 200) { if (code === 200) {
message.success(env ? '更新变量成功' : '新建变量成功'); message.success(env ? '更新变量成功' : '新建变量成功');
+3 -9
View File
@@ -36,10 +36,8 @@ const Initialization = () => {
setLoading(true); setLoading(true);
request request
.put(`${config.apiPrefix}user/init`, { .put(`${config.apiPrefix}user/init`, {
data: { username: values.username,
username: values.username, password: values.password,
password: values.password,
},
}) })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
@@ -52,11 +50,7 @@ const Initialization = () => {
const submitNotification = (values: any) => { const submitNotification = (values: any) => {
setLoading(true); setLoading(true);
request request
.put(`${config.apiPrefix}user/notification/init`, { .put(`${config.apiPrefix}user/notification/init`, values)
data: {
...values,
},
})
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
next(); next();
+4 -5
View File
@@ -35,10 +35,8 @@ const Login = () => {
setWaitTime(null); setWaitTime(null);
request request
.post(`${config.apiPrefix}user/login`, { .post(`${config.apiPrefix}user/login`, {
data: { username: values.username,
username: values.username, password: values.password,
password: values.password,
},
}) })
.then((data) => { .then((data) => {
checkResponse(data, values); checkResponse(data, values);
@@ -54,7 +52,8 @@ const Login = () => {
setVerifying(true); setVerifying(true);
request request
.put(`${config.apiPrefix}user/two-factor/login`, { .put(`${config.apiPrefix}user/two-factor/login`, {
data: { ...loginInfo, code: values.code }, ...loginInfo,
code: values.code,
}) })
.then((data: any) => { .then((data: any) => {
checkResponse(data); checkResponse(data);
+6 -10
View File
@@ -86,11 +86,9 @@ const EditModal = ({
const content = editorRef.current.getValue().replace(/\r\n/g, '\n'); const content = editorRef.current.getValue().replace(/\r\n/g, '\n');
request request
.put(`${config.apiPrefix}scripts/run`, { .put(`${config.apiPrefix}scripts/run`, {
data: { filename: cNode.title,
filename: cNode.title, path: cNode.parent || '',
path: cNode.parent || '', content,
content,
},
}) })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
@@ -106,11 +104,9 @@ const EditModal = ({
} }
request request
.put(`${config.apiPrefix}scripts/stop`, { .put(`${config.apiPrefix}scripts/stop`, {
data: { filename: cNode.title,
filename: cNode.title, path: cNode.parent || '',
path: cNode.parent || '', pid: currentPid,
pid: currentPid,
},
}) })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
+1 -3
View File
@@ -44,9 +44,7 @@ const EditScriptNameModal = ({
formData.append('content', ''); formData.append('content', '');
formData.append('directory', directory); formData.append('directory', directory);
request request
.post(`${config.apiPrefix}scripts`, { .post(`${config.apiPrefix}scripts`, formData)
data: formData,
})
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success(directory ? '新建文件夹成功' : '新建文件成功'); message.success(directory ? '新建文件夹成功' : '新建文件成功');
+4 -8
View File
@@ -223,11 +223,9 @@ const Script = () => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
request request
.put(`${config.apiPrefix}scripts`, { .put(`${config.apiPrefix}scripts`, {
data: { filename: currentNode.title,
filename: currentNode.title, path: currentNode.parent || '',
path: currentNode.parent || '', content,
content,
},
}) })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
@@ -341,9 +339,7 @@ const Script = () => {
const downloadFile = () => { const downloadFile = () => {
request request
.post(`${config.apiPrefix}scripts/download`, { .post(`${config.apiPrefix}scripts/download`, {
data: { filename: currentNode.title,
filename: currentNode.title,
},
}) })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
+3 -5
View File
@@ -21,11 +21,9 @@ const RenameModal = ({
const { code, data } = await request.put( const { code, data } = await request.put(
`${config.apiPrefix}scripts/rename`, `${config.apiPrefix}scripts/rename`,
{ {
data: { filename: currentNode.title,
filename: currentNode.title, path: currentNode.parent || '',
path: currentNode.parent || '', newFilename: values.name,
newFilename: values.name,
},
}, },
); );
+1 -3
View File
@@ -19,9 +19,7 @@ const SaveModal = ({
setLoading(true); setLoading(true);
const payload = { ...file, ...values, originFilename: file.title }; const payload = { ...file, ...values, originFilename: file.title };
request request
.post(`${config.apiPrefix}scripts`, { .post(`${config.apiPrefix}scripts`, payload)
data: payload,
})
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('保存文件成功'); message.success('保存文件成功');
+1 -3
View File
@@ -19,9 +19,7 @@ const SettingModal = ({
setLoading(true); setLoading(true);
const payload = { ...file, ...values }; const payload = { ...file, ...values };
request request
.post(`${config.apiPrefix}scripts`, { .post(`${config.apiPrefix}scripts`, payload)
data: payload,
})
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('保存文件成功'); message.success('保存文件成功');
+2 -5
View File
@@ -31,13 +31,10 @@ const About = ({ systemInfo }: { systemInfo: SharedContext['systemInfo'] }) => {
{TVersion[systemInfo.branch]} v{systemInfo.version} {TVersion[systemInfo.branch]} v{systemInfo.version}
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="更新时间" span={3}> <Descriptions.Item label="更新时间" span={3}>
{dayjs(systemInfo.lastCommitTime * 1000).format( {dayjs(systemInfo.publishTime * 1000).format(
'YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm',
)} )}
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="更新ID" span={3}>
{systemInfo.lastCommitId}
</Descriptions.Item>
<Descriptions.Item label="更新日志" span={3}> <Descriptions.Item label="更新日志" span={3}>
<Link <Link
href={`https://qn.whyour.cn/version.yaml?t=${Date.now()}`} href={`https://qn.whyour.cn/version.yaml?t=${Date.now()}`}
+4 -3
View File
@@ -23,9 +23,10 @@ const AppModal = ({
payload.id = app.id; payload.id = app.id;
} }
try { try {
const { code, data } = await request[method](`${config.apiPrefix}apps`, { const { code, data } = await request[method](
data: payload, `${config.apiPrefix}apps`,
}); payload,
);
if (code === 200) { if (code === 200) {
message.success(app ? '更新应用成功' : '新建应用成功'); message.success(app ? '更新应用成功' : '新建应用成功');
+51 -21
View File
@@ -47,7 +47,7 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
</div> </div>
</> </>
), ),
okText: '强制更新', okText: '重新下载',
onOk() { onOk() {
showUpdatingModal(); showUpdatingModal();
request request
@@ -82,7 +82,7 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
{lastLog} {lastLog}
</pre> </pre>
), ),
okText: '更新', okText: '下载更新',
cancelText: '以后再说', cancelText: '以后再说',
onOk() { onOk() {
showUpdatingModal(); showUpdatingModal();
@@ -104,7 +104,7 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
closable: false, closable: false,
keyboard: false, keyboard: false,
okButtonProps: { disabled: true }, okButtonProps: { disabled: true },
title: '更新中...', title: '下载更新中...',
centered: true, centered: true,
content: ( content: (
<pre <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(() => { useEffect(() => {
if (!modalRef.current || !socketMessage) { if (!modalRef.current || !socketMessage) {
return; return;
@@ -130,7 +174,7 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
} }
const newMessage = `${value}${_message}`; const newMessage = `${value}${_message}`;
const updateFailed = newMessage.includes('失败,请检查'); const updateFailed = newMessage.includes('失败');
modalRef.current.update({ modalRef.current.update({
maskClosable: updateFailed, maskClosable: updateFailed,
@@ -162,24 +206,10 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
.getElementById('log-identifier')! .getElementById('log-identifier')!
.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); .scrollIntoView({ behavior: 'smooth', block: 'nearest' });
if (_message.includes('重启面板')) { if (_message.includes('更新包下载成功')) {
message.warning({
content: (
<span>
<Countdown
className="inline-countdown"
format="ss"
value={Date.now() + 1000 * 30}
/>
</span>
),
duration: 30,
});
setTimeout(() => { setTimeout(() => {
window.location.reload(); showReloadModal();
}, 30000); }, 1000);
} }
}, [socketMessage]); }, [socketMessage]);
+1 -5
View File
@@ -19,11 +19,7 @@ const NotificationSetting = ({ data }: any) => {
} }
request request
.put(`${config.apiPrefix}user/notification`, { .put(`${config.apiPrefix}user/notification`, values)
data: {
...values,
},
})
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success(values.type ? '通知发送成功' : '通知关闭成功'); message.success(values.type ? '通知发送成功' : '通知关闭成功');
+110 -5
View File
@@ -1,11 +1,25 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { Button, InputNumber, Form, Radio, message, Input } from 'antd'; import {
Button,
InputNumber,
Form,
Radio,
message,
Input,
Upload,
Modal,
Progress,
} from 'antd';
import * as DarkReader from '@umijs/ssr-darkreader'; import * as DarkReader from '@umijs/ssr-darkreader';
import config from '@/utils/config'; import config from '@/utils/config';
import { request } from '@/utils/http'; import { request } from '@/utils/http';
import CheckUpdate from './checkUpdate'; import CheckUpdate from './checkUpdate';
import { SharedContext } from '@/layouts'; import { SharedContext } from '@/layouts';
import { saveAs } from 'file-saver';
import './index.less'; import './index.less';
import { UploadOutlined } from '@ant-design/icons';
import Countdown from 'antd/lib/statistic/Countdown';
import useProgress from './progress';
const optionsWithDisabled = [ const optionsWithDisabled = [
{ label: '亮色', value: 'light' }, { label: '亮色', value: 'light' },
@@ -24,6 +38,10 @@ const Other = ({
cronConcurrency?: number | null; cronConcurrency?: number | null;
}>(); }>();
const [form] = Form.useForm(); const [form] = Form.useForm();
const modalRef = useRef<any>();
const [exportLoading, setExportLoading] = useState(false);
const showUploadProgress = useProgress('上传');
const showDownloadProgress = useProgress('下载');
const { const {
enable: enableDarkMode, enable: enableDarkMode,
@@ -63,9 +81,7 @@ const Other = ({
const updateSystemConfig = () => { const updateSystemConfig = () => {
request request
.put(`${config.apiPrefix}system/config`, { .put(`${config.apiPrefix}system/config`, systemConfig)
data: { ...systemConfig },
})
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('更新成功'); message.success('更新成功');
@@ -76,6 +92,68 @@ const Other = ({
}); });
}; };
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(() => { useEffect(() => {
getSystemConfig(); getSystemConfig();
}, []); }, []);
@@ -127,6 +205,33 @@ const Other = ({
</Button> </Button>
</Input.Group> </Input.Group>
</Form.Item> </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"> <Form.Item label="检查更新" name="update">
<CheckUpdate systemInfo={systemInfo} socketMessage={socketMessage} /> <CheckUpdate systemInfo={systemInfo} socketMessage={socketMessage} />
</Form.Item> </Form.Item>
+33
View File
@@ -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;
}
+4 -6
View File
@@ -22,10 +22,8 @@ const SecuritySettings = ({ user, userChange }: any) => {
const handleOk = (values: any) => { const handleOk = (values: any) => {
request request
.put(`${config.apiPrefix}user`, { .put(`${config.apiPrefix}user`, {
data: { username: values.username,
username: values.username, password: values.password,
password: values.password,
},
}) })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
@@ -64,7 +62,7 @@ const SecuritySettings = ({ user, userChange }: any) => {
const completeTowFactor = () => { const completeTowFactor = () => {
setLoading(true); setLoading(true);
request request
.put(`${config.apiPrefix}user/two-factor/active`, { data: { code } }) .put(`${config.apiPrefix}user/two-factor/active`, { code })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
if (data) { if (data) {
@@ -241,7 +239,7 @@ const SecuritySettings = ({ user, userChange }: any) => {
</div> </div>
<Avatar size={128} shape="square" icon={<UserOutlined />} src={avatar} /> <Avatar size={128} shape="square" icon={<UserOutlined />} src={avatar} />
<ImgCrop rotate> <ImgCrop rotationSlider>
<Upload <Upload
method="put" method="put"
showUploadList={false} showUploadList={false}
+3 -5
View File
@@ -254,7 +254,7 @@ const Subscription = () => {
), ),
onOk() { onOk() {
request request
.put(`${config.apiPrefix}subscriptions/run`, { data: [record.id] }) .put(`${config.apiPrefix}subscriptions/run`, [record.id])
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
const result = [...value]; const result = [...value];
@@ -289,7 +289,7 @@ const Subscription = () => {
), ),
onOk() { onOk() {
request request
.put(`${config.apiPrefix}subscriptions/stop`, { data: [record.id] }) .put(`${config.apiPrefix}subscriptions/stop`, [record.id])
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
const result = [...value]; const result = [...value];
@@ -386,9 +386,7 @@ const Subscription = () => {
`${config.apiPrefix}subscriptions/${ `${config.apiPrefix}subscriptions/${
record.is_disabled === 1 ? 'enable' : 'disable' record.is_disabled === 1 ? 'enable' : 'disable'
}`, }`,
{ [record.id],
data: [record.id],
},
) )
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
+1 -3
View File
@@ -47,9 +47,7 @@ const SubscriptionModal = ({
try { try {
const { code, data } = await request[method]( const { code, data } = await request[method](
`${config.apiPrefix}subscriptions`, `${config.apiPrefix}subscriptions`,
{ payload,
data: payload,
},
); );
if (code === 200) { if (code === 200) {
message.success(subscription ? '更新订阅成功' : '新建订阅成功'); message.success(subscription ? '更新订阅成功' : '新建订阅成功');
+8
View File
@@ -96,6 +96,7 @@ export default {
{ value: 'chat', label: '群晖chat' }, { value: 'chat', label: '群晖chat' },
{ value: 'email', label: '邮箱' }, { value: 'email', label: '邮箱' },
{ value: 'lark', label: '飞书机器人' }, { value: 'lark', label: '飞书机器人' },
{ value: 'pushMe', label: 'PushMe' },
{ value: 'webhook', label: '自定义通知' }, { value: 'webhook', label: '自定义通知' },
{ value: 'closed', label: '已关闭' }, { value: 'closed', label: '已关闭' },
], ],
@@ -268,6 +269,13 @@ export default {
{ label: 'emailUser', tip: '邮箱地址', required: true }, { label: 'emailUser', tip: '邮箱地址', required: true },
{ label: 'emailPass', tip: '邮箱SMTP授权码', required: true }, { label: 'emailPass', tip: '邮箱SMTP授权码', required: true },
], ],
pushMe: [
{
label: 'pushMeKey',
tip: 'PushMe的Keyhttps://push.i-i.me/',
required: true,
},
],
webhook: [ webhook: [
{ {
label: 'webhookMethod', label: 'webhookMethod',
+68 -25
View File
@@ -1,17 +1,36 @@
import { extend } from 'umi-request';
import { message } from 'antd'; import { message } from 'antd';
import config from './config'; import config from './config';
import { history } from '@umijs/max'; 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({ message.config({
duration: 2, duration: 2,
}); });
const time = Date.now(); const time = Date.now();
const errorHandler = function (error: any) { const errorHandler = function (
error: AxiosError,
) {
if (error.response) { if (error.response) {
const msg = error.data const msg = error.response.data
? error.data.message || error.message || error.data ? error.response.data.message || error.message || error.response.data
: error.response.statusText; : error.response.statusText;
const responseStatus = error.response.status; const responseStatus = error.response.status;
if ([502, 504].includes(responseStatus)) { if ([502, 504].includes(responseStatus)) {
@@ -32,10 +51,14 @@ const errorHandler = function (error: any) {
console.log(error.message); 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 = [ const apiWhiteList = [
'/api/user/login', '/api/user/login',
'/open/auth/token', '/open/auth/token',
@@ -45,15 +68,13 @@ const apiWhiteList = [
'/api/user/notification/init', '/api/user/notification/init',
]; ];
_request.interceptors.request.use((url, options) => { _request.interceptors.request.use((_config) => {
const token = localStorage.getItem(config.authKey); const token = localStorage.getItem(config.authKey);
if (token && !apiWhiteList.includes(url)) { if (token && !apiWhiteList.includes(_config.url!)) {
const headers = { _config.headers.Authorization = `Bearer ${token}`;
Authorization: `Bearer ${token}`, return _config;
};
return { url, options: { ...options, headers } };
} }
return { url, options }; return _config;
}); });
_request.interceptors.response.use(async (response) => { _request.interceptors.response.use(async (response) => {
@@ -66,18 +87,40 @@ _request.interceptors.response.use(async (response) => {
history.push('/login'); history.push('/login');
} }
} else { } else {
const res = await response.clone().json(); try {
if (res.code !== 200) { const res = response.data;
const msg = res.message || res.data; if (res.code !== 200) {
msg && const msg = res.message || res.data;
message.error({ msg &&
content: msg, message.error({
style: { maxWidth: 500, margin: '0 auto' }, content: msg,
}); style: { maxWidth: 500, margin: '0 auto' },
} });
return res; }
return res;
} catch (error) { }
return response;
} }
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 -5
View File
@@ -1,6 +1,7 @@
version: 2.15.17 version: 2.15.19
changeLogLink: https://t.me/jiao_long/383 changeLogLink: https://t.me/jiao_long/385
publishTime: 2023-07-20 23:59
changeLog: | changeLog: |
1. 系统设置增加定时任务并发数设置 1. 通知支持 pushMe
2. 修改默认并发数 2. 修复系统通知设置保存失败
3. 修改更新仓库逻辑 3. 修复判断依赖已安装逻辑