mirror of
				https://github.com/whyour/qinglong.git
				synced 2025-10-31 08:56: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
	 hanhh
						hanhh