XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

SpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 1 Star 0
返回提交历史

XFEstudio/SpaceNinjaServer

chore: improve handling when config.json is missing & fix logger options (#1460)

Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1460 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>

5c22949c
Sainan <63328889+Sainan@users.noreply.github.com>
提交于

代码差异

6 个文件 +78 -55
Modified src/controllers/custom/renameAccountController.ts +2 -1
@@ -1,6 +1,7 @@
1 1 import { RequestHandler } from "express";
2 2 import { getAccountForRequest, isAdministrator, isNameTaken } from "@/src/services/loginService";
3 import { config, saveConfig } from "@/src/services/configService";
3 import { config } from "@/src/services/configService";
4 import { saveConfig } from "@/src/services/configWatcherService";
4 5
5 6 export const renameAccountController: RequestHandler = async (req, res) => {
6 7 const account = await getAccountForRequest(req);
Modified src/controllers/custom/updateConfigDataController.ts +1 -1
@@ -1,5 +1,5 @@
1 1 import { RequestHandler } from "express";
2 import { updateConfig } from "@/src/services/configService";
2 import { updateConfig } from "@/src/services/configWatcherService";
3 3 import { getAccountForRequest, isAdministrator } from "@/src/services/loginService";
4 4
5 5 const updateConfigDataController: RequestHandler = async (req, res) => {
Modified src/index.ts +13 -5
@@ -1,22 +1,30 @@
1 import { logger } from "./utils/logger";
2
1 // First, init config.
2 import { config, loadConfig } from "@/src/services/configService";
3 try {
4 loadConfig();
5 } catch (e) {
6 console.log("ERROR: Failed to load config.json. You can copy config.json.example to create your config.json.");
7 process.exit(1);
8 }
9
10 // Now we can init the logger with the settings provided in the config.
11 import { logger } from "@/src/utils/logger";
3 12 logger.info("Starting up...");
4 13
14 // Proceed with normal startup: bring up config watcher service, validate config, connect to MongoDB, and finally start listening for HTTP.
5 15 import http from "http";
6 16 import https from "https";
7 17 import fs from "node:fs";
8 18 import { app } from "./app";
9 import { config, validateConfig } from "./services/configService";
10 import { registerLogFileCreationListener } from "@/src/utils/logger";
11 19 import mongoose from "mongoose";
12 20 import { Json, JSONStringify } from "json-with-bigint";
21 import { validateConfig } from "@/src/services/configWatcherService";
13 22
14 23 // Patch JSON.stringify to work flawlessly with Bigints.
15 24 JSON.stringify = (obj: Exclude<Json, undefined>, _replacer?: unknown, space?: string | number): string => {
16 25 return JSONStringify(obj, space);
17 26 };
18 27
19 registerLogFileCreationListener();
20 28 validateConfig();
21 29
22 30 mongoose
Modified src/services/configService.ts +19 -42
@@ -1,33 +1,13 @@
1 1 import fs from "fs";
2 import fsPromises from "fs/promises";
3 2 import path from "path";
4 3 import { repoDir } from "@/src/helpers/pathHelper";
5 import { logger } from "@/src/utils/logger";
6
7 const configPath = path.join(repoDir, "config.json");
8 export const config = JSON.parse(fs.readFileSync(configPath, "utf-8")) as IConfig;
9
10 let amnesia = false;
11 fs.watchFile(configPath, () => {
12 if (amnesia) {
13 amnesia = false;
14 } else {
15 logger.info("Detected a change to config.json, reloading its contents.");
16
17 // Set all values to undefined now so if the new config.json omits some fields that were previously present, it's correct in-memory.
18 for (const key of Object.keys(config)) {
19 // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
20 (config as any)[key] = undefined;
21 }
22
23 Object.assign(config, JSON.parse(fs.readFileSync(configPath, "utf-8")));
24 validateConfig();
25 }
26 });
27 4
28 5 interface IConfig {
29 6 mongodbUrl: string;
30 logger: ILoggerConfig;
7 logger: {
8 files: boolean;
9 level: string; // "fatal" | "error" | "warn" | "info" | "http" | "debug" | "trace";
10 };
31 11 myAddress: string;
32 12 httpPort?: number;
33 13 httpsPort?: number;
@@ -72,26 +52,23 @@ interface IConfig {
72 52 };
73 53 }
74 54
75 interface ILoggerConfig {
76 files: boolean;
77 level: string; // "fatal" | "error" | "warn" | "info" | "http" | "debug" | "trace";
78 }
55 export const configPath = path.join(repoDir, "config.json");
79 56
80 export const updateConfig = async (data: string): Promise<void> => {
81 amnesia = true;
82 await fsPromises.writeFile(configPath, data);
83 Object.assign(config, JSON.parse(data));
57 export const config: IConfig = {
58 mongodbUrl: "mongodb://127.0.0.1:27017/openWF",
59 logger: {
60 files: true,
61 level: "trace"
62 },
63 myAddress: "localhost"
84 64 };
85 65
86 export const saveConfig = async (): Promise<void> => {
87 amnesia = true;
88 await fsPromises.writeFile(configPath, JSON.stringify(config, null, 2));
89 };
90
91 export const validateConfig = (): void => {
92 if (typeof config.administratorNames == "string") {
93 logger.info(`Updating config.json to make administratorNames an array.`);
94 config.administratorNames = [config.administratorNames];
95 void saveConfig();
66 export const loadConfig = (): void => {
67 // Set all values to undefined now so if the new config.json omits some fields that were previously present, it's correct in-memory.
68 for (const key of Object.keys(config)) {
69 // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
70 (config as any)[key] = undefined;
96 71 }
72
73 Object.assign(config, JSON.parse(fs.readFileSync(configPath, "utf-8")));
97 74 };
Added src/services/configWatcherService.ts +39 -0
@@ -0,0 +1,39 @@
1 import fs from "fs";
2 import fsPromises from "fs/promises";
3 import { logger } from "../utils/logger";
4 import { config, configPath, loadConfig } from "./configService";
5
6 let amnesia = false;
7 fs.watchFile(configPath, () => {
8 if (amnesia) {
9 amnesia = false;
10 } else {
11 logger.info("Detected a change to config.json, reloading its contents.");
12 try {
13 loadConfig();
14 } catch (e) {
15 logger.error("Failed to reload config.json. Did you delete it?! Execution cannot continue.");
16 process.exit(1);
17 }
18 validateConfig();
19 }
20 });
21
22 export const validateConfig = (): void => {
23 if (typeof config.administratorNames == "string") {
24 logger.info(`Updating config.json to make administratorNames an array.`);
25 config.administratorNames = [config.administratorNames];
26 void saveConfig();
27 }
28 };
29
30 export const updateConfig = async (data: string): Promise<void> => {
31 amnesia = true;
32 await fsPromises.writeFile(configPath, data);
33 Object.assign(config, JSON.parse(data));
34 };
35
36 export const saveConfig = async (): Promise<void> => {
37 amnesia = true;
38 await fsPromises.writeFile(configPath, JSON.stringify(config, null, 2));
39 };
Modified src/utils/logger.ts +4 -6
@@ -104,9 +104,7 @@ export const logger = createLogger({
104 104
105 105 addColors(logLevels.colors);
106 106
107 export function registerLogFileCreationListener(): void {
108 errorLog.on("new", filename => logger.info(`Using error log file: ${filename}`));
109 combinedLog.on("new", filename => logger.info(`Using combined log file: ${filename}`));
110 errorLog.on("rotate", filename => logger.info(`Rotated error log file: ${filename}`));
111 combinedLog.on("rotate", filename => logger.info(`Rotated combined log file: ${filename}`));
112 }
107 errorLog.on("new", filename => logger.info(`Using error log file: ${filename}`));
108 combinedLog.on("new", filename => logger.info(`Using combined log file: ${filename}`));
109 errorLog.on("rotate", filename => logger.info(`Rotated error log file: ${filename}`));
110 combinedLog.on("rotate", filename => logger.info(`Rotated combined log file: ${filename}`));