151 lines
3.9 KiB
TypeScript
151 lines
3.9 KiB
TypeScript
import dotenv from "dotenv";
|
|
import JSON5 from "json5";
|
|
import { z } from "zod";
|
|
import { fromZodError } from "zod-validation-error";
|
|
import fs from "fs";
|
|
|
|
dotenv.config();
|
|
|
|
const logger = console;
|
|
|
|
const confLocation = process.env.TOA_CONFIG ?? "./config.json5";
|
|
|
|
let configFile;
|
|
|
|
try {
|
|
configFile = fs.readFileSync(confLocation, "utf-8");
|
|
} catch (err) {
|
|
logger.error("Error while trying to open conf.json5. Is one present?", { err });
|
|
|
|
process.exit(1);
|
|
}
|
|
|
|
const config: unknown = JSON5.parse(configFile);
|
|
|
|
const zod16bitNumber = z.number().gt(0).lte(65535);
|
|
const zodLogLevel = z.enum(["crit", "debug", "error", "info", "verbose", "warn"]);
|
|
const zodHexString = z.string().regex(/^[0-9a-z]+$/iu, "value is not a hex string");
|
|
const zodHexString16 = zodHexString.length(16);
|
|
|
|
const configSchema = z.object({
|
|
NAME: z.string().default("Kozukata Toa"),
|
|
DATABASE_PATH: z.string().default("data/db.sqlite3"),
|
|
LISTEN_ADDRESS: z.string().ip().default("0.0.0.0"),
|
|
LOGGER_CONFIG: z.object({
|
|
LOG_LEVEL: zodLogLevel.default("info"),
|
|
CONSOLE: z.boolean().default(true),
|
|
FOLDER: z.string().default("logs"),
|
|
}),
|
|
ALLNET_CONFIG: z
|
|
.object({
|
|
ENABLED: z.boolean().default(true),
|
|
PORT: zod16bitNumber.default(80),
|
|
ALLOW_UNREGISTERED_SERIALS: z.boolean().default(true),
|
|
UPDATE_CFG_FOLDER: z.string().optional(),
|
|
HOSTNAME: z.string().optional(),
|
|
})
|
|
.refine((c) => {
|
|
if (c.UPDATE_CFG_FOLDER) {
|
|
return !!c.HOSTNAME;
|
|
}
|
|
|
|
return true;
|
|
}, "Must specify ALL.Net hostname if allowing updates."),
|
|
AIMEDB_CONFIG: z.object({
|
|
ENABLED: z.boolean().default(true),
|
|
PORT: zod16bitNumber.default(22345),
|
|
KEY: z.string().length(16).default("Copyright(C)SEGA"),
|
|
AIME_MOBILE_CARD_KEY: zodHexString16.default("5CD3E81B9024F67A"),
|
|
RESERVED_CARD_PREFIX: z.string().length(5).default("01053"),
|
|
RESERVED_CARD_KEY: zodHexString16.default("E8179645DB3FC02A"),
|
|
}),
|
|
TITLES_CONFIG: z.object({
|
|
ENABLED: z.boolean().default(true),
|
|
PORT: zod16bitNumber.default(8080),
|
|
HOSTNAME: z.string().default("localhost"),
|
|
}),
|
|
CHUNITHM_CONFIG: z.object({
|
|
ENABLE: z.boolean().default(true),
|
|
MODS: z
|
|
.object({
|
|
TEAM_NAME: z.string().optional(),
|
|
USE_LOGIN_BONUS: z.boolean().default(false),
|
|
FORCE_UNLOCK_ALL: z.boolean().default(false),
|
|
})
|
|
.default({ USE_LOGIN_BONUS: false, FORCE_UNLOCK_ALL: false }),
|
|
VERSIONS: z
|
|
.record(
|
|
z.object({
|
|
rom: z.string(),
|
|
data: z.string(),
|
|
})
|
|
)
|
|
.default({
|
|
"200": {
|
|
rom: "2.00.00",
|
|
data: "2.00.00",
|
|
},
|
|
"205": {
|
|
rom: "2.05.00",
|
|
data: "2.05.00",
|
|
},
|
|
"210": {
|
|
rom: "2.10.00",
|
|
data: "2.10.00",
|
|
},
|
|
}),
|
|
CRYPTO: z
|
|
.object({
|
|
ENCRYPTED_ONLY: z.boolean().default(false),
|
|
KEYS: z.record(z.array(zodHexString).min(2).max(3)).optional(),
|
|
})
|
|
.refine((arg) => {
|
|
if (arg.ENCRYPTED_ONLY) {
|
|
return !!arg.KEYS && Object.keys(arg.KEYS).length >= 1;
|
|
}
|
|
|
|
return true;
|
|
}, "Must provide keys for at least one version if running in encrypted only mode.")
|
|
.default({
|
|
ENCRYPTED_ONLY: false,
|
|
KEYS: {
|
|
"210": [
|
|
"75695c3d265f434c3953454c5830522b4b3c4d7b42482a312e5627216b2b4060",
|
|
"31277c37707044377141595058345a6b",
|
|
"04780206ca5f36f4",
|
|
],
|
|
},
|
|
}),
|
|
}),
|
|
});
|
|
|
|
const parseResult = configSchema.safeParse(config);
|
|
|
|
if (!parseResult.success) {
|
|
const humanFriendlyMessage = fromZodError(parseResult.error);
|
|
|
|
throw new Error(`Invalid config.json5 file: ${humanFriendlyMessage}`);
|
|
}
|
|
|
|
export const Config = parseResult.data;
|
|
|
|
// Environment Variable Validation
|
|
|
|
const nodeEnv = process.env.NODE_ENV ?? "";
|
|
|
|
if (!nodeEnv) {
|
|
logger.error(`No NODE_ENV specified in environment. Terminating.`);
|
|
process.exit(1);
|
|
}
|
|
|
|
if (!["dev", "production", "staging", "test"].includes(nodeEnv)) {
|
|
logger.error(
|
|
`Invalid NODE_ENV set in environment. Expected dev, production, test or staging. Got ${nodeEnv}.`
|
|
);
|
|
process.exit(1);
|
|
}
|
|
|
|
export const Environment = {
|
|
nodeEnv: nodeEnv as "dev" | "production" | "staging" | "test",
|
|
};
|