昨天已经把大部分的 GUI 弄完了,之前也已经写好了服务器的程序,今天我们把两边拼起来吧!
我们把之前写的 server.js 搬过来新的专案资料夹中:
然後因为前面没有 config.js 帮忙档怪东西,输出也改成在 GUI 中,所以我们也得改一下 server.js。
因为现在我们的输出应该要在 GUI 上面,而不是本来的 Terminal,所以新增一个 Logger 类别:
class Logger {
#events = {};
constructor(log, logfile = null) {
this._log = log;
this._logfile = logfile;
if (this._log && this._logfile) {
this._stream = fs.createWriteStream(this._logfile, { flags: "a" });
this._stream.write(`\n========\n`);
this.closed = false;
}
}
log(type = "INFO", message) {
if (typeof this.#events[type] === "function") this.#events[type](message);
if (this._stream && !this.closed) this._stream.write(`${new Date().toISOString()} [${type}] ${message}\n`);
return this;
}
info(message) {
return this.log("INFO", message);
}
success(message) {
return this.log("SUCCESS", message);
}
error(message) {
return this.log("ERROR", message);
}
warn(message) {
return this.log("WARN", message);
}
debug(message) {
return this.log("DEBUG", message);
}
close() {
if (this._stream && !this.closed) {
this._stream.end();
this.closed = true;
}
return this;
}
on(event, callback) {
this.#events[event] = callback;
return this;
}
}
这个 Logger 很简单,就...写纪录而已,但你可以用 on
来挂事件监听。
因为现在少了 config.js 来阻挡怪怪的东西,所以新增一个 check 函式做型别检查,希望从 agent.js 往後丢时就把型别处里好。
function check({ port, folder, log, logfile }) {
if (typeof port !== "number") return false;
if (typeof folder !== "string") return false;
if (typeof log !== "boolean") return false;
if (log && typeof logfile !== "string") return false;
return true;
}
原本的 createServer
函式需要稍做修改才能套用 Logger 和 check。
function createServer({ port, folder, log, logfile }, on = {}) {
const logger = new Logger(log, logfile);
for (const event in on) logger.on(event.toUpperCase(), on[event]);
if (!check({ port, folder, log, logfile })) {
logger.error("Invalid server configuration");
return null;
}
try {
const app = new Koa();
app.use(async (ctx, next) => {
logger.info(`Process ${ctx.request.method} ${ctx.request.url} from ${ctx.request.ip}`);
await next();
});
app.use(require("koa-static")(folder));
const server = app.listen(port);
logger.success(`Server started at port ${port}`);
logger.success(`Serving static files from ${folder}`);
logger.success(`Visit http://localhost:${port}/ to see your website.`);
if (log) logger.info(`Log file: ${logfile}`);
return async () => {
server.close();
logger.info(`Server closed.`);
logger.close();
};
} catch (error) {
logger.error(error);
logger.close();
console.error(error);
return null;
}
}
我们让 createServer
有了第二个参数,用来挂上 Logger 的事件监听。
然後用 check 来检查参数。
(但这其实有个小问题,就是 log 和 logfile 在还没检查就丢进 Logger 了,因为检查出错误时需要 logger)
其它的部分因为有 Logger ,所以简化了一些 log 相关的程序。
其实就是加上 logs 的 CSS 还有加一些 id 和 class 而已。
log 的 CSS,用伪元素来标示类别:
.log {
padding: 0 8px;
word-break: break-all;
transition: all 0.2s;
}
.log:hover {
background: var(--nord4);
}
.log.success {
color: var(--nord14);
text-shadow: 0 0 var(--nord1);
}
.log.success::before {
content: "[success] ";
}
.log.info {
color: var(--nord9);
}
.log.info::before {
content: "[info] ";
}
.log.error {
color: var(--nord11);
}
.log.error::before {
content: "[error] ";
}
.log.warn {
color: var(--nord12);
}
.log.warn::before {
content: "[warn] ";
}
我们让 agent.js 用 IPC 向 main.js 传送请求及接收资讯。
我们在 registerListener
中加上启动和停止的请求机制:
document.querySelector("#launch").addEventListener("click", async () => {
if (document.querySelector("#launch").classList.contains("launched")) {
ipc.send("server-stop", +document.querySelector("#port").value);
return;
} else {
// 取得输入值
const folder = document.querySelector("#folder").value;
const port = +document.querySelector("#port").value;
const log = document.querySelector("#log").checked;
const logfile = document.querySelector("#logfile").value;
// 将输入值传给 main.js
ipc.send("server-launch", { folder, port, log, logfile });
document.querySelector("#launch").classList.add("launched");
document.querySelector("#launch").innerHTML = "停止";
}
});
我们用启动按钮的 class 来判断是启动还是停止。
然後停止後必须回复启动按钮状态:
ipc.on("server-stopped", async (evt) => {
document.querySelector("#launch").classList.remove("launched");
document.querySelector("#launch").innerHTML = "启动";
});
服务器停止後会用 IPC 向前端通知。
在 agent.js 中接收各种讯息并新增至画面上:
ipc.on("log-info", async (evt, msg) => {
const logs = document.querySelector("#logs");
const log = document.createElement("div");
log.classList.add("log", "info");
log.innerHTML = msg;
logs.appendChild(log);
});
ipc.on("log-success", async (evt, msg) => {
const logs = document.querySelector("#logs");
const log = document.createElement("div");
log.classList.add("log", "success");
log.innerHTML = msg;
logs.appendChild(log);
});
ipc.on("log-error", async (evt, msg) => {
const logs = document.querySelector("#logs");
const log = document.createElement("div");
log.classList.add("log", "error");
log.innerHTML = msg;
logs.appendChild(log);
});
ipc.on("log-warn", async (evt, msg) => {
const logs = document.querySelector("#logs");
const log = document.createElement("div");
log.classList.add("log", "warn");
log.innerHTML = msg;
logs.appendChild(log);
});
各种讯息大同小异,差别只在套用的 class 所用的 CSS。
我们的 main.js 会收到两种服务器相关的请求,一是启动,二是停止:
const createServer = require("./server");
const Server = {}; // 保留未来扩充多服务器的可能
ipc.on("server-launch", async (evt, config) => {
console.log("server-launch", config);
Servers[config.port] = createServer(config, {
info: (msg) => evt.sender.send("log-info", msg),
success: (msg) => evt.sender.send("log-success", msg),
error: (msg) => evt.sender.send("log-error", msg),
warn: (msg) => evt.sender.send("log-warn", msg),
});
if (Servers[config.port] === null) {
delete Servers[config.port];
evt.sender.send("log-error", "启动服务器失败");
evt.sender.send("server-stopped");
}
});
ipc.on("server-stop", async (evt, port) => {
console.log("server-stop", port);
await Servers[port]();
delete Servers[port];
evt.sender.send("server-stopped");
});
main.js 的部分相对简单,只需要转发两种请求并挂上监听而已。
程序就完成了。
大概就是这样,错误也很正常的喷回前端了。
当然就是打包成跨平台应用程序啊。
以 10/11 20:00 ~ 10/12 20:00 文章观看数增加值排名
+143
Day 21: 人工智慧在音乐领域的应用 (AI作曲-基因演算法四 掌握生杀大权-Interactive Fitness Function)
+138
Day27 海鲜义大利炖饭Risotto
+136
Day 22: 人工智慧在音乐领域的应用 (AI作曲-基因演算法五 基於规则(Rule-Based)的Fitness Function)
+129
表单处理 Object 里的 Array
+129
[职场]不放过每个细节,完成一场 0 失误的专案 Demo!
+117
Day 23: 人工智慧在音乐领域的应用 (AI作曲-基因演算法六 总要敬老尊贤吧?)
+117
Proxmox VE 设定客体机高可用性
+113
Day 27: 人工智慧在音乐领域的应用 (索尼-Flow Machine、谷歌-Magenta )
+108
Day 26: 人工智慧在音乐领域的应用 (AI作曲 - 生成对抗网路 Gan (干) )
+102
【Day 27】Google Apps Script - API Blueprint 篇 - Apiary 建立专案与版本控制
<<: Youtube Reports API 教学 - 最後一次做 OAuth2.0 授权
今天带大家简单制作一张首页的画面。 导览列 选用Dektop(1440px X 1024px)的fr...
NSIS (Nullsoft Scriptable Install System) 是一个建立安装...
https://github.com/PacktPublishing/Machine-Learni...
1.版本 但是遇到旧的也不要更改,因为可能别人版本还没这麽新 <!DOCTYPE html&g...
Self-attention 首先先把a1乘上Wq,就会得到q1,q的意思是query,也就是查询的...