From 7b03706821c604f59fe8263286203d57d634c421 Mon Sep 17 00:00:00 2001 From: Menci Date: Wed, 27 Mar 2019 02:35:15 +0800 Subject: [PATCH] Add restart service option for admins --- app.js | 57 +++++++++++++++++++++++++++++------------ modules/admin.js | 32 +++++++++++++++++++++++ package.json | 1 + views/admin_restart.ejs | 46 +++++++++++++++++++++++++++++++++ views/header.ejs | 1 + 5 files changed, 120 insertions(+), 17 deletions(-) create mode 100644 views/admin_restart.ejs diff --git a/app.js b/app.js index 7ac8114..aeaa9fe 100644 --- a/app.js +++ b/app.js @@ -2,6 +2,7 @@ let fs = require('fs'), path = require('path'), util = require('util'); const serializejs = require('serialize-javascript'); +const UUID = require('uuid'); const commandLineArgs = require('command-line-args'); const optionDefinitions = [ @@ -18,6 +19,7 @@ global.syzoj = { models: [], modules: [], db: null, + serviceID: UUID(), log(obj) { if (obj instanceof ErrorMessage) return; console.log(obj); @@ -38,15 +40,6 @@ global.syzoj = { let winstonLib = require('./libs/winston'); winstonLib.configureWinston(!syzoj.production); - app.server = require('http').createServer(app); - - if (!module.parent) { - // Loaded by `require()`, not node CLI. - app.server.listen(parseInt(syzoj.config.port), syzoj.config.hostname, () => { - this.log(`SYZOJ is listening on ${syzoj.config.hostname}:${parseInt(syzoj.config.port)}...`); - }); - } - // Set assets dir app.use(Express.static(__dirname + '/static', { maxAge: syzoj.production ? '1y' : 0 })); @@ -79,9 +72,7 @@ global.syzoj = { })()); await this.connectDatabase(); - if (!module.parent) { - await this.lib('judger').connect(); - } + this.loadModules(); // redis and redisCache is for syzoj-renderer const redis = require('redis'); @@ -91,7 +82,40 @@ global.syzoj = { set: util.promisify(this.redis.set).bind(this.redis) }; - this.loadModules(); + if (!module.parent) { + // Loaded by node CLI, not by `require()`. + + if (process.send) { + // if it's started by child_process.fork(), it must be requested to restart + // wait until parent process quited. + await new Promise((resolve, reject) => { + process.on('message', message => { + if (message === 'quited') resolve(); + }); + process.send('quit'); + }); + } + + await this.lib('judger').connect(); + + app.server = require('http').createServer(app); + app.server.listen(parseInt(syzoj.config.port), syzoj.config.hostname, () => { + this.log(`SYZOJ is listening on ${syzoj.config.hostname}:${parseInt(syzoj.config.port)}...`); + }); + } + }, + restart() { + console.log('Will now fork a new process.'); + const child = require('child_process').fork(__filename, ['-c', options.config]); + child.on('message', (message) => { + if (message !== 'quit') return; + + console.log('Child process requested "quit".') + child.send('quited', err => { + if (err) console.error('Error sending "quited" to child process:', err); + process.exit(); + }); + }); }, async connectDatabase() { let Sequelize = require('sequelize'); @@ -143,7 +167,7 @@ global.syzoj = { global.Promise = Sequelize.Promise; this.db.countQuery = async (sql, options) => (await this.db.query(`SELECT COUNT(*) FROM (${sql}) AS \`__tmp_table\``, options))[0][0]['COUNT(*)']; - this.loadModels(); + await this.loadModels(); }, loadModules() { fs.readdir('./modules/', (err, files) => { @@ -155,7 +179,7 @@ global.syzoj = { .forEach((file) => this.modules.push(require(`./modules/${file}`))); }); }, - loadModels() { + async loadModels() { fs.readdir('./models/', (err, files) => { if (err) { this.log(err); @@ -163,9 +187,8 @@ global.syzoj = { } files.filter((file) => file.endsWith('.js')) .forEach((file) => require(`./models/${file}`)); - - this.db.sync(); }); + await this.db.sync(); }, lib(name) { return require(`./libs/${name}`); diff --git a/modules/admin.js b/modules/admin.js index 0505b7b..23372f4 100644 --- a/modules/admin.js +++ b/modules/admin.js @@ -442,3 +442,35 @@ app.post('/admin/raw', async (req, res) => { }) } }); + +app.post('/admin/restart', async (req, res) => { + try { + if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); + + syzoj.restart(); + + res.render('admin_restart', { + data: JSON.stringify(syzoj.config, null, 2) + }); + } catch (e) { + syzoj.log(e); + res.render('error', { + err: e + }) + } +}); + +app.get('/admin/serviceID', async (req, res) => { + try { + if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); + + res.send({ + serviceID: syzoj.serviceID + }); + } catch (e) { + syzoj.log(e); + res.render('error', { + err: e + }) + } +}); diff --git a/package.json b/package.json index d4d77c3..d79fdad 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "syzoj-renderer": "^1.0.5", "tempfile": "^2.0.0", "tmp-promise": "^1.0.3", + "uuid": "^3.3.2", "waliyun": "^3.1.1", "winston": "^3.2.1", "xss": "^1.0.6" diff --git a/views/admin_restart.ejs b/views/admin_restart.ejs new file mode 100644 index 0000000..e07b767 --- /dev/null +++ b/views/admin_restart.ejs @@ -0,0 +1,46 @@ +<% this.title = '重启服务' %> +<% include header %> + +
+ +
+
+ 服务重启中 +
+

已等待 0 秒。

+
+
+ + + +<% include footer %> diff --git a/views/header.ejs b/views/header.ejs index 9d786c1..f91a8de 100644 --- a/views/header.ejs +++ b/views/header.ejs @@ -72,6 +72,7 @@ 修改资料 <% if (user.is_admin) { %> 后台管理 + 重启服务 <% } %> 注销