mirror of
https://github.com/whyour/qinglong.git
synced 2025-05-22 22:36:06 +08:00
添加系统更新操作和设置删除日志频率
This commit is contained in:
parent
9455ca64a2
commit
b1077443a3
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -21,6 +21,7 @@
|
||||||
/.env.local
|
/.env.local
|
||||||
.env
|
.env
|
||||||
.history
|
.history
|
||||||
|
.version.ts
|
||||||
|
|
||||||
/config
|
/config
|
||||||
/log
|
/log
|
||||||
|
|
12
.umirc.ts
12
.umirc.ts
|
@ -33,14 +33,16 @@ export default defineConfig({
|
||||||
'react-dom': 'window.ReactDOM',
|
'react-dom': 'window.ReactDOM',
|
||||||
darkreader: 'window.DarkReader',
|
darkreader: 'window.DarkReader',
|
||||||
codemirror: 'window.CodeMirror',
|
codemirror: 'window.CodeMirror',
|
||||||
|
'sockjs-client': 'window.SockJS',
|
||||||
},
|
},
|
||||||
scripts: [
|
scripts: [
|
||||||
'https://gw.alipayobjects.com/os/lib/react/16.13.1/umd/react.production.min.js',
|
'https://gw.alipayobjects.com/os/lib/react/16.13.1/umd/react.production.min.js',
|
||||||
'https://gw.alipayobjects.com/os/lib/react-dom/16.13.1/umd/react-dom.production.min.js',
|
'https://gw.alipayobjects.com/os/lib/react-dom/16.13.1/umd/react-dom.production.min.js',
|
||||||
'https://cdn.jsdelivr.net/npm/darkreader@4.9.34/darkreader.min.js',
|
'https://cdn.jsdelivr.net/npm/darkreader@4/darkreader.min.js',
|
||||||
'https://cdn.jsdelivr.net/npm/codemirror@5.62.0/lib/codemirror.min.js',
|
'https://cdn.jsdelivr.net/npm/codemirror@5/lib/codemirror.min.js',
|
||||||
'https://cdn.jsdelivr.net/npm/codemirror@5.62.0/mode/shell/shell.js',
|
'https://cdn.jsdelivr.net/npm/codemirror@5/mode/shell/shell.js',
|
||||||
'https://cdn.jsdelivr.net/npm/codemirror@5.62.0/mode/python/python.js',
|
'https://cdn.jsdelivr.net/npm/codemirror@5/mode/python/python.js',
|
||||||
'https://cdn.jsdelivr.net/npm/codemirror@5.62.0/mode/javascript/javascript.js',
|
'https://cdn.jsdelivr.net/npm/codemirror@5/mode/javascript/javascript.js',
|
||||||
|
'https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js',
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
|
@ -272,4 +272,56 @@ export default (app: Router) => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
route.put(
|
||||||
|
'/system/log/remove',
|
||||||
|
celebrate({
|
||||||
|
body: Joi.object({
|
||||||
|
frequency: Joi.number().required(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
const logger: Logger = Container.get('logger');
|
||||||
|
try {
|
||||||
|
const userService = Container.get(UserService);
|
||||||
|
const result = await userService.updateLogRemoveFrequency(
|
||||||
|
req.body.frequency,
|
||||||
|
);
|
||||||
|
res.send(result);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error('🔥 error: %o', e);
|
||||||
|
return next(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
route.put(
|
||||||
|
'/system/update-check',
|
||||||
|
async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
const logger: Logger = Container.get('logger');
|
||||||
|
try {
|
||||||
|
const userService = Container.get(UserService);
|
||||||
|
const result = await userService.checkUpdate();
|
||||||
|
res.send(result);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error('🔥 error: %o', e);
|
||||||
|
return next(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
route.put(
|
||||||
|
'/system/update',
|
||||||
|
async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
const logger: Logger = Container.get('logger');
|
||||||
|
try {
|
||||||
|
const userService = Container.get(UserService);
|
||||||
|
const result = await userService.updateSystem();
|
||||||
|
res.send(result);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error('🔥 error: %o', e);
|
||||||
|
return next(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,7 @@ async function startServer() {
|
||||||
|
|
||||||
await require('./loaders').default({ expressApp: app });
|
await require('./loaders').default({ expressApp: app });
|
||||||
|
|
||||||
app
|
const server = app
|
||||||
.listen(config.port, () => {
|
.listen(config.port, () => {
|
||||||
Logger.info(`
|
Logger.info(`
|
||||||
################################################
|
################################################
|
||||||
|
@ -23,6 +23,8 @@ async function startServer() {
|
||||||
Logger.error(err);
|
Logger.error(err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await require('./loaders/sock').default({ server });
|
||||||
}
|
}
|
||||||
|
|
||||||
startServer();
|
startServer();
|
||||||
|
|
|
@ -4,6 +4,9 @@ import { createRandomString } from './util';
|
||||||
|
|
||||||
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||||
|
|
||||||
|
const lastVersionFile =
|
||||||
|
'https://ghproxy.com/https://raw.githubusercontent.com/whyour/qinglong/master/src/version.ts';
|
||||||
|
|
||||||
const envFound = dotenv.config();
|
const envFound = dotenv.config();
|
||||||
const rootPath = process.cwd();
|
const rootPath = process.cwd();
|
||||||
const envFile = path.join(rootPath, 'config/env.sh');
|
const envFile = path.join(rootPath, 'config/env.sh');
|
||||||
|
@ -26,6 +29,7 @@ const cronDbFile = path.join(rootPath, 'db/crontab.db');
|
||||||
const envDbFile = path.join(rootPath, 'db/env.db');
|
const envDbFile = path.join(rootPath, 'db/env.db');
|
||||||
const appDbFile = path.join(rootPath, 'db/app.db');
|
const appDbFile = path.join(rootPath, 'db/app.db');
|
||||||
const authDbFile = path.join(rootPath, 'db/auth.db');
|
const authDbFile = path.join(rootPath, 'db/auth.db');
|
||||||
|
const versionFile = path.join(rootPath, 'src/version.ts');
|
||||||
|
|
||||||
const configFound = dotenv.config({ path: confFile });
|
const configFound = dotenv.config({ path: confFile });
|
||||||
|
|
||||||
|
@ -84,4 +88,6 @@ export default {
|
||||||
'/api/init/user',
|
'/api/init/user',
|
||||||
'/api/init/notification',
|
'/api/init/notification',
|
||||||
],
|
],
|
||||||
|
versionFile,
|
||||||
|
lastVersionFile,
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,4 +21,5 @@ export enum AuthDataType {
|
||||||
'loginLog' = 'loginLog',
|
'loginLog' = 'loginLog',
|
||||||
'authToken' = 'authToken',
|
'authToken' = 'authToken',
|
||||||
'notification' = 'notification',
|
'notification' = 'notification',
|
||||||
|
'removeLogFrequency' = 'removeLogFrequency',
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ export default ({ app }: { app: Application }) => {
|
||||||
|
|
||||||
app.use(bodyParser.json({ limit: '50mb' }));
|
app.use(bodyParser.json({ limit: '50mb' }));
|
||||||
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
|
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
jwt({
|
jwt({
|
||||||
secret: config.secret as string,
|
secret: config.secret as string,
|
||||||
|
|
|
@ -2,8 +2,9 @@ import expressLoader from './express';
|
||||||
import dependencyInjectorLoader from './dependencyInjector';
|
import dependencyInjectorLoader from './dependencyInjector';
|
||||||
import Logger from './logger';
|
import Logger from './logger';
|
||||||
import initData from './initData';
|
import initData from './initData';
|
||||||
|
import { Application } from 'express';
|
||||||
|
|
||||||
export default async ({ expressApp }: { expressApp: any }) => {
|
export default async ({ expressApp }: { expressApp: Application }) => {
|
||||||
Logger.info('✌️ DB loaded and connected!');
|
Logger.info('✌️ DB loaded and connected!');
|
||||||
|
|
||||||
await dependencyInjectorLoader({
|
await dependencyInjectorLoader({
|
||||||
|
|
40
back/loaders/sock.ts
Normal file
40
back/loaders/sock.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import sockjs from 'sockjs';
|
||||||
|
import { Server } from 'http';
|
||||||
|
import Logger from './logger';
|
||||||
|
import { Container } from 'typedi';
|
||||||
|
import SockService from '../services/sock';
|
||||||
|
import config from '../config/index';
|
||||||
|
import fs from 'fs';
|
||||||
|
import { getPlatform } from '../config/util';
|
||||||
|
|
||||||
|
export default async ({ server }: { server: Server }) => {
|
||||||
|
const echo = sockjs.createServer({ prefix: '/ws' });
|
||||||
|
const sockService = Container.get(SockService);
|
||||||
|
|
||||||
|
echo.on('connection', (conn) => {
|
||||||
|
const data = fs.readFileSync(config.authConfigFile, 'utf8');
|
||||||
|
const platform = getPlatform(conn.headers['user-agent'] || '') || 'desktop';
|
||||||
|
const headerToken = conn.url.replace(`${conn.pathname}?token=`, '');
|
||||||
|
if (data) {
|
||||||
|
const { token = '', tokens = {} } = JSON.parse(data);
|
||||||
|
if (headerToken === token || tokens[platform] === headerToken) {
|
||||||
|
Logger.info('✌️ Sockjs connection success');
|
||||||
|
sockService.addClient(conn);
|
||||||
|
|
||||||
|
conn.on('data', (message) => {
|
||||||
|
conn.write(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
conn.on('close', function () {
|
||||||
|
sockService.removeClient(conn);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.close('404');
|
||||||
|
});
|
||||||
|
|
||||||
|
echo.installHandlers(server);
|
||||||
|
};
|
61
back/services/schedule.ts
Normal file
61
back/services/schedule.ts
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import { Service, Inject } from 'typedi';
|
||||||
|
import winston from 'winston';
|
||||||
|
import nodeSchedule from 'node-schedule';
|
||||||
|
import { Crontab } from '../data/cron';
|
||||||
|
import { exec } from 'child_process';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class ScheduleService {
|
||||||
|
private scheduleStacks = new Map<string, nodeSchedule.Job>();
|
||||||
|
|
||||||
|
constructor(@Inject('logger') private logger: winston.Logger) {}
|
||||||
|
|
||||||
|
async generateSchedule({ _id = '', command, name, schedule }: Crontab) {
|
||||||
|
this.logger.info(
|
||||||
|
'[创建定时任务],任务ID: %s,cron: %s,任务名: %s,任务方法: %s',
|
||||||
|
_id,
|
||||||
|
schedule,
|
||||||
|
name,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.scheduleStacks.set(
|
||||||
|
_id,
|
||||||
|
nodeSchedule.scheduleJob(_id, schedule, async () => {
|
||||||
|
try {
|
||||||
|
exec(command, async (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
await this.logger.info(
|
||||||
|
'执行任务`%s`失败,时间:%s, 错误信息:%j',
|
||||||
|
name,
|
||||||
|
new Date().toLocaleString(),
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
await this.logger.info(
|
||||||
|
'执行任务`%s`失败,时间:%s, 错误信息:%j',
|
||||||
|
name,
|
||||||
|
new Date().toLocaleString(),
|
||||||
|
stderr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
await this.logger.info(
|
||||||
|
'执行任务`%s`失败,时间:%s, 错误信息:%j',
|
||||||
|
name,
|
||||||
|
new Date().toLocaleString(),
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancelSchedule(id: string, jobName: string) {
|
||||||
|
this.logger.info('[取消定时任务],任务名:%s', jobName);
|
||||||
|
this.scheduleStacks.has(id) && this.scheduleStacks.get(id)?.cancel();
|
||||||
|
}
|
||||||
|
}
|
33
back/services/sock.ts
Normal file
33
back/services/sock.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { Service, Inject } from 'typedi';
|
||||||
|
import winston from 'winston';
|
||||||
|
import { Connection } from 'sockjs';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class SockService {
|
||||||
|
private clients: Connection[] = [];
|
||||||
|
|
||||||
|
constructor(@Inject('logger') private logger: winston.Logger) {}
|
||||||
|
|
||||||
|
public getClients() {
|
||||||
|
return this.clients;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addClient(conn: Connection) {
|
||||||
|
if (this.clients.indexOf(conn) === -1) {
|
||||||
|
this.clients.push(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeClient(conn: Connection) {
|
||||||
|
const index = this.clients.indexOf(conn);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.clients.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sendMessage(msg: string) {
|
||||||
|
this.clients.forEach((x) => {
|
||||||
|
x.write(msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,10 @@ import { AuthDataType, AuthInfo, LoginStatus } from '../data/auth';
|
||||||
import { NotificationInfo } from '../data/notify';
|
import { NotificationInfo } from '../data/notify';
|
||||||
import NotificationService from './notify';
|
import NotificationService from './notify';
|
||||||
import { Request } from 'express';
|
import { Request } from 'express';
|
||||||
|
import ScheduleService from './schedule';
|
||||||
|
import { spawn } from 'child_process';
|
||||||
|
import SockService from './sock';
|
||||||
|
import got from 'got';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class UserService {
|
export default class UserService {
|
||||||
|
@ -18,7 +22,11 @@ export default class UserService {
|
||||||
private notificationService!: NotificationService;
|
private notificationService!: NotificationService;
|
||||||
private authDb = new DataStore({ filename: config.authDbFile });
|
private authDb = new DataStore({ filename: config.authDbFile });
|
||||||
|
|
||||||
constructor(@Inject('logger') private logger: winston.Logger) {
|
constructor(
|
||||||
|
@Inject('logger') private logger: winston.Logger,
|
||||||
|
private scheduleService: ScheduleService,
|
||||||
|
private sockService: SockService,
|
||||||
|
) {
|
||||||
this.authDb.loadDatabase((err) => {
|
this.authDb.loadDatabase((err) => {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
});
|
});
|
||||||
|
@ -330,7 +338,7 @@ export default class UserService {
|
||||||
if (err) {
|
if (err) {
|
||||||
resolve({} as NotificationInfo);
|
resolve({} as NotificationInfo);
|
||||||
} else {
|
} else {
|
||||||
resolve(doc.info);
|
resolve({ ...doc.info, _id: doc._id });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -354,4 +362,62 @@ export default class UserService {
|
||||||
return { code: 400, data: '通知发送失败,请检查参数' };
|
return { code: 400, data: '通知发送失败,请检查参数' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async updateLogRemoveFrequency(frequency: number) {
|
||||||
|
const result = await this.updateNotificationDb({
|
||||||
|
type: AuthDataType.removeLogFrequency,
|
||||||
|
info: { frequency },
|
||||||
|
});
|
||||||
|
const cron = {
|
||||||
|
_id: result._id,
|
||||||
|
name: '删除日志',
|
||||||
|
command: `ql rmlog ${frequency}`,
|
||||||
|
schedule: `5 23 */${frequency} * *`,
|
||||||
|
};
|
||||||
|
await this.scheduleService.generateSchedule(cron);
|
||||||
|
return { code: 200, data: { ...cron } };
|
||||||
|
}
|
||||||
|
|
||||||
|
public async checkUpdate() {
|
||||||
|
try {
|
||||||
|
const { version } = await import(config.versionFile);
|
||||||
|
const lastVersionFileContent = await got.get(config.lastVersionFile);
|
||||||
|
const filePath = `${config.rootPath}/.version.ts`;
|
||||||
|
fs.writeFileSync(filePath, lastVersionFileContent.body, {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
});
|
||||||
|
const result = await import(config.versionFile);
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
data: {
|
||||||
|
hasNewVersion: version !== result.version,
|
||||||
|
...result,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
code: 400,
|
||||||
|
data: '获取版本文件失败',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateSystem() {
|
||||||
|
const cp = spawn('ql update', { shell: '/bin/bash' });
|
||||||
|
|
||||||
|
cp.stdout.on('data', (data) => {
|
||||||
|
this.sockService.sendMessage(data.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
cp.stderr.on('data', (data) => {
|
||||||
|
this.sockService.sendMessage(data.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
cp.on('error', (err) => {
|
||||||
|
this.sockService.sendMessage(JSON.stringify(err));
|
||||||
|
});
|
||||||
|
|
||||||
|
return { code: 200 };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ http {
|
||||||
server_tokens off;
|
server_tokens off;
|
||||||
|
|
||||||
client_max_body_size 20m;
|
client_max_body_size 20m;
|
||||||
|
client_body_buffer_size: 20m;
|
||||||
|
|
||||||
keepalive_timeout 65;
|
keepalive_timeout 65;
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
"nodemailer": "^6.6.3",
|
"nodemailer": "^6.6.3",
|
||||||
"p-queue": "6.6.2",
|
"p-queue": "6.6.2",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
|
"sockjs": "^0.3.21",
|
||||||
"typedi": "^0.8.0",
|
"typedi": "^0.8.0",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"winston": "^3.3.3"
|
"winston": "^3.3.3"
|
||||||
|
@ -60,10 +61,12 @@
|
||||||
"@types/nedb": "^1.8.11",
|
"@types/nedb": "^1.8.11",
|
||||||
"@types/node": "^14.11.2",
|
"@types/node": "^14.11.2",
|
||||||
"@types/node-fetch": "^2.5.8",
|
"@types/node-fetch": "^2.5.8",
|
||||||
|
"@types/node-schedule": "^1.3.2",
|
||||||
"@types/nodemailer": "^6.4.4",
|
"@types/nodemailer": "^6.4.4",
|
||||||
"@types/qrcode.react": "^1.0.1",
|
"@types/qrcode.react": "^1.0.1",
|
||||||
"@types/react": "^17.0.0",
|
"@types/react": "^17.0.0",
|
||||||
"@types/react-dom": "^17.0.0",
|
"@types/react-dom": "^17.0.0",
|
||||||
|
"@types/sockjs": "^0.3.33",
|
||||||
"@umijs/plugin-antd": "^0.9.1",
|
"@umijs/plugin-antd": "^0.9.1",
|
||||||
"@umijs/test": "^3.3.9",
|
"@umijs/test": "^3.3.9",
|
||||||
"codemirror": "^5.62.2",
|
"codemirror": "^5.62.2",
|
||||||
|
|
|
@ -13,10 +13,12 @@ specifiers:
|
||||||
'@types/nedb': ^1.8.11
|
'@types/nedb': ^1.8.11
|
||||||
'@types/node': ^14.11.2
|
'@types/node': ^14.11.2
|
||||||
'@types/node-fetch': ^2.5.8
|
'@types/node-fetch': ^2.5.8
|
||||||
|
'@types/node-schedule': ^1.3.2
|
||||||
'@types/nodemailer': ^6.4.4
|
'@types/nodemailer': ^6.4.4
|
||||||
'@types/qrcode.react': ^1.0.1
|
'@types/qrcode.react': ^1.0.1
|
||||||
'@types/react': ^17.0.0
|
'@types/react': ^17.0.0
|
||||||
'@types/react-dom': ^17.0.0
|
'@types/react-dom': ^17.0.0
|
||||||
|
'@types/sockjs': ^0.3.33
|
||||||
'@umijs/plugin-antd': ^0.9.1
|
'@umijs/plugin-antd': ^0.9.1
|
||||||
'@umijs/test': ^3.3.9
|
'@umijs/test': ^3.3.9
|
||||||
body-parser: ^1.19.0
|
body-parser: ^1.19.0
|
||||||
|
@ -51,7 +53,9 @@ specifiers:
|
||||||
react-dnd-html5-backend: ^14.0.0
|
react-dnd-html5-backend: ^14.0.0
|
||||||
react-dom: 17.x
|
react-dom: 17.x
|
||||||
react-split-pane: ^0.1.92
|
react-split-pane: ^0.1.92
|
||||||
|
react-use-websocket: ^2.8.0
|
||||||
reflect-metadata: ^0.1.13
|
reflect-metadata: ^0.1.13
|
||||||
|
sockjs: ^0.3.21
|
||||||
ts-node: ^9.0.0
|
ts-node: ^9.0.0
|
||||||
typedi: ^0.8.0
|
typedi: ^0.8.0
|
||||||
typescript: ^4.1.2
|
typescript: ^4.1.2
|
||||||
|
@ -84,6 +88,7 @@ dependencies:
|
||||||
nodemailer: 6.6.3
|
nodemailer: 6.6.3
|
||||||
p-queue: 6.6.2
|
p-queue: 6.6.2
|
||||||
reflect-metadata: 0.1.13
|
reflect-metadata: 0.1.13
|
||||||
|
sockjs: 0.3.21
|
||||||
typedi: 0.8.0
|
typedi: 0.8.0
|
||||||
uuid: 8.3.2
|
uuid: 8.3.2
|
||||||
winston: 3.3.3
|
winston: 3.3.3
|
||||||
|
@ -100,10 +105,12 @@ devDependencies:
|
||||||
'@types/nedb': 1.8.11
|
'@types/nedb': 1.8.11
|
||||||
'@types/node': 14.14.45
|
'@types/node': 14.14.45
|
||||||
'@types/node-fetch': 2.5.10
|
'@types/node-fetch': 2.5.10
|
||||||
|
'@types/node-schedule': 1.3.2
|
||||||
'@types/nodemailer': 6.4.4
|
'@types/nodemailer': 6.4.4
|
||||||
'@types/qrcode.react': 1.0.1
|
'@types/qrcode.react': 1.0.1
|
||||||
'@types/react': 17.0.5
|
'@types/react': 17.0.5
|
||||||
'@types/react-dom': 17.0.5
|
'@types/react-dom': 17.0.5
|
||||||
|
'@types/sockjs': 0.3.33
|
||||||
'@umijs/plugin-antd': 0.9.1_5ccfec03b6e15849b3687a64fe975f75
|
'@umijs/plugin-antd': 0.9.1_5ccfec03b6e15849b3687a64fe975f75
|
||||||
'@umijs/test': 3.4.20_ts-node@9.1.1
|
'@umijs/test': 3.4.20_ts-node@9.1.1
|
||||||
codemirror: 5.62.2
|
codemirror: 5.62.2
|
||||||
|
@ -120,6 +127,7 @@ devDependencies:
|
||||||
react-dnd-html5-backend: 14.0.0
|
react-dnd-html5-backend: 14.0.0
|
||||||
react-dom: 17.0.2_react@17.0.2
|
react-dom: 17.0.2_react@17.0.2
|
||||||
react-split-pane: 0.1.92_react-dom@17.0.2+react@17.0.2
|
react-split-pane: 0.1.92_react-dom@17.0.2+react@17.0.2
|
||||||
|
react-use-websocket: 2.8.0_react-dom@17.0.2+react@17.0.2
|
||||||
ts-node: 9.1.1_typescript@4.2.4
|
ts-node: 9.1.1_typescript@4.2.4
|
||||||
typescript: 4.2.4
|
typescript: 4.2.4
|
||||||
umi: 3.4.20
|
umi: 3.4.20
|
||||||
|
@ -1214,6 +1222,12 @@ packages:
|
||||||
form-data: 3.0.1
|
form-data: 3.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/node-schedule/1.3.2:
|
||||||
|
resolution: {integrity: sha512-Y0CqdAr+lCpArT8CJJjJq4U2v8Bb5e7ru2nV/NhDdaptCMCRdOL3Y7tAhen39HluQMaIKWvPbDuiFBUQpg7Srw==}
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 14.17.21
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/node/14.14.45:
|
/@types/node/14.14.45:
|
||||||
resolution: {integrity: sha512-DssMqTV9UnnoxDWu959sDLZzfvqCF0qDNRjaWeYSui9xkFe61kKo4l1TWNTQONpuXEm+gLMRvdlzvNHBamzmEw==}
|
resolution: {integrity: sha512-DssMqTV9UnnoxDWu959sDLZzfvqCF0qDNRjaWeYSui9xkFe61kKo4l1TWNTQONpuXEm+gLMRvdlzvNHBamzmEw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -1226,6 +1240,10 @@ packages:
|
||||||
resolution: {integrity: sha512-WiFf2izl01P1CpeY8WqFAeKWwByMueBEkND38EcN8N68qb0aDG3oIS1P5MhAX5kUdr469qRyqsY/MjanLjsFbQ==}
|
resolution: {integrity: sha512-WiFf2izl01P1CpeY8WqFAeKWwByMueBEkND38EcN8N68qb0aDG3oIS1P5MhAX5kUdr469qRyqsY/MjanLjsFbQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/node/14.17.21:
|
||||||
|
resolution: {integrity: sha512-zv8ukKci1mrILYiQOwGSV4FpkZhyxQtuFWGya2GujWg+zVAeRQ4qbaMmWp9vb9889CFA8JECH7lkwCL6Ygg8kA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/nodemailer/6.4.4:
|
/@types/nodemailer/6.4.4:
|
||||||
resolution: {integrity: sha512-Ksw4t7iliXeYGvIQcSIgWQ5BLuC/mljIEbjf615svhZL10PE9t+ei8O9gDaD3FPCasUJn9KTLwz2JFJyiiyuqw==}
|
resolution: {integrity: sha512-Ksw4t7iliXeYGvIQcSIgWQ5BLuC/mljIEbjf615svhZL10PE9t+ei8O9gDaD3FPCasUJn9KTLwz2JFJyiiyuqw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -1337,6 +1355,12 @@ packages:
|
||||||
'@types/node': 14.14.45
|
'@types/node': 14.14.45
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/sockjs/0.3.33:
|
||||||
|
resolution: {integrity: sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==}
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 14.17.21
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/stack-utils/1.0.1:
|
/@types/stack-utils/1.0.1:
|
||||||
resolution: {integrity: sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==}
|
resolution: {integrity: sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -3818,6 +3842,13 @@ packages:
|
||||||
resolution: {integrity: sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==}
|
resolution: {integrity: sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/faye-websocket/0.11.4:
|
||||||
|
resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==}
|
||||||
|
engines: {node: '>=0.8.0'}
|
||||||
|
dependencies:
|
||||||
|
websocket-driver: 0.7.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
/fb-watchman/2.0.1:
|
/fb-watchman/2.0.1:
|
||||||
resolution: {integrity: sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==}
|
resolution: {integrity: sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4319,6 +4350,10 @@ packages:
|
||||||
toidentifier: 1.0.0
|
toidentifier: 1.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/http-parser-js/0.5.3:
|
||||||
|
resolution: {integrity: sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/http-signature/1.2.0:
|
/http-signature/1.2.0:
|
||||||
resolution: {integrity: sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=}
|
resolution: {integrity: sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=}
|
||||||
engines: {node: '>=0.8', npm: '>=1.3.7'}
|
engines: {node: '>=0.8', npm: '>=1.3.7'}
|
||||||
|
@ -8045,6 +8080,16 @@ packages:
|
||||||
tween-functions: 1.2.0
|
tween-functions: 1.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/react-use-websocket/2.8.0_react-dom@17.0.2+react@17.0.2:
|
||||||
|
resolution: {integrity: sha512-0J1gsX7NFTsZYBBfAQo9vKjIyGE/uxBfb0p8yq6Iza+rZF3mQocj3kkIJujFiXCYQIBt00pWJzNj+YI5srfxZg==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>= 16.8.0'
|
||||||
|
react-dom: '>= 16.8.0'
|
||||||
|
dependencies:
|
||||||
|
react: 17.0.2
|
||||||
|
react-dom: 17.0.2_react@17.0.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
/react/16.14.0:
|
/react/16.14.0:
|
||||||
resolution: {integrity: sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==}
|
resolution: {integrity: sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
@ -8774,6 +8819,14 @@ packages:
|
||||||
use: 3.1.1
|
use: 3.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/sockjs/0.3.21:
|
||||||
|
resolution: {integrity: sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==}
|
||||||
|
dependencies:
|
||||||
|
faye-websocket: 0.11.4
|
||||||
|
uuid: 3.4.0
|
||||||
|
websocket-driver: 0.7.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
/sort-keys/1.1.2:
|
/sort-keys/1.1.2:
|
||||||
resolution: {integrity: sha1-RBttTTRnmPG05J6JIK37oOVD+a0=}
|
resolution: {integrity: sha1-RBttTTRnmPG05J6JIK37oOVD+a0=}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
@ -9618,8 +9671,8 @@ packages:
|
||||||
|
|
||||||
/uuid/3.4.0:
|
/uuid/3.4.0:
|
||||||
resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==}
|
resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==}
|
||||||
|
deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
|
||||||
|
|
||||||
/uuid/8.3.2:
|
/uuid/8.3.2:
|
||||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
||||||
|
@ -9790,6 +9843,20 @@ packages:
|
||||||
webpack-sources: 2.2.0
|
webpack-sources: 2.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/websocket-driver/0.7.4:
|
||||||
|
resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==}
|
||||||
|
engines: {node: '>=0.8.0'}
|
||||||
|
dependencies:
|
||||||
|
http-parser-js: 0.5.3
|
||||||
|
safe-buffer: 5.2.1
|
||||||
|
websocket-extensions: 0.1.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/websocket-extensions/0.1.4:
|
||||||
|
resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==}
|
||||||
|
engines: {node: '>=0.8.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/whatwg-encoding/1.0.5:
|
/whatwg-encoding/1.0.5:
|
||||||
resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==}
|
resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState, useRef } from 'react';
|
||||||
import ProLayout, { PageLoading } from '@ant-design/pro-layout';
|
import ProLayout, { PageLoading } from '@ant-design/pro-layout';
|
||||||
import {
|
import {
|
||||||
enable as enableDarkMode,
|
enable as enableDarkMode,
|
||||||
|
@ -16,6 +16,8 @@ import vhCheck from 'vh-check';
|
||||||
import { version, changeLogLink, changeLog } from '../version';
|
import { version, changeLogLink, changeLog } from '../version';
|
||||||
import { useCtx, useTheme } from '@/utils/hooks';
|
import { useCtx, useTheme } from '@/utils/hooks';
|
||||||
import { message, Badge, Modal } from 'antd';
|
import { message, Badge, Modal } from 'antd';
|
||||||
|
// @ts-ignore
|
||||||
|
import SockJS from 'sockjs-client';
|
||||||
|
|
||||||
export default function (props: any) {
|
export default function (props: any) {
|
||||||
const ctx = useCtx();
|
const ctx = useCtx();
|
||||||
|
@ -23,6 +25,7 @@ export default function (props: any) {
|
||||||
const [user, setUser] = useState<any>();
|
const [user, setUser] = useState<any>();
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
const [systemInfo, setSystemInfo] = useState<{ isInitialized: boolean }>();
|
const [systemInfo, setSystemInfo] = useState<{ isInitialized: boolean }>();
|
||||||
|
const ws = useRef<any>(null);
|
||||||
|
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
request.post(`${config.apiPrefix}logout`).then(() => {
|
request.post(`${config.apiPrefix}logout`).then(() => {
|
||||||
|
@ -152,12 +155,44 @@ export default function (props: any) {
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
ws.current = new SockJS(
|
||||||
|
`http://127.0.0.1:5600/ws?token=${localStorage.getItem(config.authKey)}`,
|
||||||
|
);
|
||||||
|
ws.current.onopen = () => {
|
||||||
|
console.log('ws opened');
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.current.onclose = () => console.log('ws closed');
|
||||||
|
const wsCurrent = ws.current;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
wsCurrent.close();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (['/login', '/initialization'].includes(props.location.pathname)) {
|
if (['/login', '/initialization'].includes(props.location.pathname)) {
|
||||||
document.title = `${
|
document.title = `${
|
||||||
(config.documentTitleMap as any)[props.location.pathname]
|
(config.documentTitleMap as any)[props.location.pathname]
|
||||||
} - 控制面板`;
|
} - 控制面板`;
|
||||||
|
if (
|
||||||
|
systemInfo?.isInitialized &&
|
||||||
|
props.location.pathname === '/initialization'
|
||||||
|
) {
|
||||||
|
history.push('/crontab');
|
||||||
|
}
|
||||||
|
|
||||||
if (systemInfo) {
|
if (systemInfo) {
|
||||||
return props.children;
|
return React.Children.map(props.children, (child) => {
|
||||||
|
return React.cloneElement(child, {
|
||||||
|
...ctx,
|
||||||
|
...theme,
|
||||||
|
user,
|
||||||
|
reloadUser,
|
||||||
|
reloadTheme: setTheme,
|
||||||
|
ws: ws.current,
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,6 +282,7 @@ export default function (props: any) {
|
||||||
user,
|
user,
|
||||||
reloadUser,
|
reloadUser,
|
||||||
reloadTheme: setTheme,
|
reloadTheme: setTheme,
|
||||||
|
ws: ws.current,
|
||||||
});
|
});
|
||||||
})}
|
})}
|
||||||
</ProLayout>
|
</ProLayout>
|
||||||
|
|
|
@ -19,7 +19,7 @@ const { Step } = Steps;
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
const { Link } = Typography;
|
const { Link } = Typography;
|
||||||
|
|
||||||
const Login = () => {
|
const Initialization = () => {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [current, setCurrent] = React.useState(0);
|
const [current, setCurrent] = React.useState(0);
|
||||||
const [fields, setFields] = useState<any[]>([]);
|
const [fields, setFields] = useState<any[]>([]);
|
||||||
|
@ -241,4 +241,4 @@ const Login = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Login;
|
export default Initialization;
|
||||||
|
|
133
src/pages/setting/checkUpdate.tsx
Normal file
133
src/pages/setting/checkUpdate.tsx
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
import React, { useEffect, useState, useRef } from 'react';
|
||||||
|
import { Typography, Modal, Tag, Button, Spin, message } from 'antd';
|
||||||
|
import { request } from '@/utils/http';
|
||||||
|
import config from '@/utils/config';
|
||||||
|
import { version } from '../../version';
|
||||||
|
|
||||||
|
const { Text, Link } = Typography;
|
||||||
|
|
||||||
|
const CheckUpdate = ({ ws }: any) => {
|
||||||
|
const [updateLoading, setUpdateLoading] = useState(false);
|
||||||
|
const [value, setValue] = useState('');
|
||||||
|
const modalRef = useRef<any>();
|
||||||
|
|
||||||
|
const checkUpgrade = () => {
|
||||||
|
if (updateLoading) return;
|
||||||
|
setUpdateLoading(true);
|
||||||
|
const hide = message.loading('检查更新中...', 0);
|
||||||
|
request
|
||||||
|
.put(`${config.apiPrefix}system/update-check`)
|
||||||
|
.then((_data: any) => {
|
||||||
|
const { code, data } = _data;
|
||||||
|
if (code === 200 && !data.hasNewVersion) {
|
||||||
|
showConfirmUpdateModal(data);
|
||||||
|
} else {
|
||||||
|
message.success('已经是最新版了!');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
console.log(error);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setUpdateLoading(false);
|
||||||
|
hide();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const showConfirmUpdateModal = (data: any) => {
|
||||||
|
const { version: newVersion, changeLog } = data;
|
||||||
|
Modal.confirm({
|
||||||
|
width: 500,
|
||||||
|
title: (
|
||||||
|
<>
|
||||||
|
<div>更新可用</div>
|
||||||
|
<div style={{ fontSize: 12, fontWeight: 400, marginTop: 5 }}>
|
||||||
|
新版本{newVersion}可用。你使用的版本为{version}。
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
content: (
|
||||||
|
<pre
|
||||||
|
style={{
|
||||||
|
wordBreak: 'break-all',
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
paddingTop: 15,
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 400,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{changeLog}
|
||||||
|
</pre>
|
||||||
|
),
|
||||||
|
okText: '更新',
|
||||||
|
cancelText: '以后再说',
|
||||||
|
onOk() {
|
||||||
|
showUpdatingModal();
|
||||||
|
request
|
||||||
|
.put(`${config.apiPrefix}system/update`)
|
||||||
|
.then((_data: any) => {})
|
||||||
|
.catch((error: any) => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const showUpdatingModal = () => {
|
||||||
|
modalRef.current = Modal.info({
|
||||||
|
width: 600,
|
||||||
|
maskClosable: false,
|
||||||
|
closable: false,
|
||||||
|
okButtonProps: { disabled: true },
|
||||||
|
title: <span></span>,
|
||||||
|
centered: true,
|
||||||
|
content: (
|
||||||
|
<pre
|
||||||
|
style={{
|
||||||
|
wordBreak: 'break-all',
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
paddingTop: 15,
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 400,
|
||||||
|
minHeight: '60vh',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
</pre>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
ws.onmessage = (e) => {
|
||||||
|
modalRef.current.update({
|
||||||
|
content: (
|
||||||
|
<pre
|
||||||
|
style={{
|
||||||
|
wordBreak: 'break-all',
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
paddingTop: 15,
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 400,
|
||||||
|
minHeight: '60vh',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{e.data + value}
|
||||||
|
</pre>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
setValue(e.data);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{value}
|
||||||
|
<Button type="primary" onClick={checkUpgrade}>
|
||||||
|
检查更新
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CheckUpdate;
|
|
@ -31,6 +31,7 @@ import {
|
||||||
import SecuritySettings from './security';
|
import SecuritySettings from './security';
|
||||||
import LoginLog from './loginLog';
|
import LoginLog from './loginLog';
|
||||||
import NotificationSetting from './notification';
|
import NotificationSetting from './notification';
|
||||||
|
import CheckUpdate from './checkUpdate';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
const optionsWithDisabled = [
|
const optionsWithDisabled = [
|
||||||
|
@ -45,6 +46,7 @@ const Setting = ({
|
||||||
user,
|
user,
|
||||||
reloadUser,
|
reloadUser,
|
||||||
reloadTheme,
|
reloadTheme,
|
||||||
|
ws,
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
|
@ -328,11 +330,16 @@ const Setting = ({
|
||||||
buttonStyle="solid"
|
buttonStyle="solid"
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="日志删除频率" name="frequency" initialValue={0}>
|
<Form.Item
|
||||||
<Input addonBefore="每" addonAfter="天" style={{ width: 300 }} />
|
label="日志删除频率"
|
||||||
|
name="frequency"
|
||||||
|
initialValue={0}
|
||||||
|
tooltip="每x天自动删除x天以前的日志"
|
||||||
|
>
|
||||||
|
<Input addonBefore="每" addonAfter="天" style={{ width: 150 }} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="检查更新" name="theme" initialValue={theme}>
|
<Form.Item label="检查更新" name="update">
|
||||||
<Button type="primary">检查更新</Button>
|
<CheckUpdate ws={ws} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user