Я пытаюсь создать веб-мастер настройки, который запрашивает у пользователя некоторые значения, выводит их в файл конфигурации, а затем позволяет пользователю запускать серверную программу с использованием этой конфигурации.
Я выбрал ExpressJS, так как он казался относительно легким и легким в освоении. Тем не менее, у меня нет опыта работы с веб-программированием или JavaScript вообще.
Каким-то образом мне удалось построить страницы мастера, хотя мне кажется, что я использую что-то ужасно неправильное (приложение в основном не имеет состояния и перетаскивает пользовательские значения). через различные HTML-страницы путем рендеринга предыдущих значений в скрытые поля в форме новой страницы ...).
После всех страниц мастера появляется страница «управления», где пользователь может видеть (и редактировать)) вычисленная конфигурация. На странице есть две кнопки:
Одна запускает программу для проверки конфигурации и проверки на наличие ошибок. Это делается с помощью child_process.execSync(...)
(так как это не занимает много времени) и работает довольно хорошо.
Другой должен запускать долгосрочную задачу в фоновом режиме. Во время выполнения задачи кнопки на странице должны быть отключены, за исключением кнопки «Прервать», которая должна убить фоновую задачу (еще не реализованную). После завершения задачи страницу необходимо перезагрузить с помощью включенных кнопок и поля состояния, в котором указано, была ли задача выполнена успешно. К сожалению, я не могу заставить это работать.
Я использую ExpressJS с pug.
control.js:
var express = require('express');
var router = express.Router();
var utils = require('./utils');
var createError = require('http-errors');
var child_process = require('child_process');
router.all('/', function(req, res,next) {
var status = null;
if ([req.app.locals.buttons.doStuff].includes(req.body.submit)) {
next();
}
switch(req.body.submit) {
case req.app.locals.buttons.validate:
try {
utils.validate(req.body.config_text); // helper function that basically calls an external program via child_process.execSync
status = "config.yaml is valid!"
} catch (err) {
status = "config.yaml is invalid: " + err
}
break;
case req.app.locals.buttons.doStuff:
res.render('control', { title: req.app.locals.title, buttons: req.app.locals.buttons, config_data: req.body.config_text, status: status, disabled: true });
return;
}
res.render('control', { title: req.app.locals.title, buttons: req.app.locals.buttons, config_data: req.body.config_text, status: status });
});
router.post('/', function(req, res, next) {
var status = null;
console.log("Entering command mode: " + req.body.submit);
switch(req.body.submit) {
case req.app.locals.buttons.doStuff:
status = "Doing stuff ... this will take a while (timeout after 1h)."
console.log(status);
child_process.exec("doStuff arg1 arg2 arg3", {cwd: req.app.locals.dostuff_directory, timeout: 3600000}, function(err, stdout, stderr) {
status = "doStuff successfully finished!"
if (err) {
status = "Error while doing stuff: \n" + stderr;
}
res.render('control', { title: req.app.locals.title, buttons: req.app.locals.buttons, config_data: req.body.config_text, status: status });
});
break;
default:
status = "Unknown command: " + req.body.submit;
res.render('control', { title: req.app.locals.title, buttons: req.app.locals.buttons, config_data: req.body.config_text, status: status });
break;
}
});
module.exports = router;
и соответствующий control.pug:
extends layout // basically just places the content block into <body>
block content
h1= title
h3 Control Page
form(method="post", autocomplete="off")
label(for="config_text") config.yaml
textarea(name="config_text", cols="70", rows="35")
= config_data
pre
code(name="status")
= status
div(class="controls")
input(type="submit", name="submit", value=buttons.validate, disabled=disabled)
input(type="submit", name="submit", value=buttons.doStuff, disabled=disabled)
if (disabled)
input(type="submit", name="submit", value=buttons.abort)
Если я нажму кнопку 'doStuff', кнопки будут отключены, но задача не запустится, и я получу error: Cannot set headers after they are sent to the client
.
Я думаю, я мог бы использовать child_process.execSync
как обходной путь, так как это, кажется, работает, но я предполагаю, что это остановит страницу во время выполнения задачи. У меня также есть ощущение, что я должен использовать другой метод реагирования на нажатия кнопок, чем теги HTML form
.
Как мне добиться желаемого поведения?