monitor/core/boot/getHostVars.ts
2025-04-16 22:30:27 +07:00

117 lines
4.1 KiB
TypeScript

import path from 'node:path';
import { z } from "zod";
import { fromZodError } from "zod-validation-error";
import fatalError from '@lib/fatalError';
import consts from '@shared/consts';
/**
* Schemas for the TXHOST_ env variables
*/
export const hostEnvVarSchemas = {
//General
API_TOKEN: z.union([
z.literal('disabled'),
z.string().regex(
/^[A-Za-z0-9_-]{16,48}$/,
'Token must be alphanumeric, underscores or dashes only, and between 16 and 48 characters long.'
),
]),
DATA_PATH: z.string().min(1).refine(
(val) => path.isAbsolute(val),
'DATA_PATH must be an absolute path'
),
GAME_NAME: z.enum(
['fivem', 'redm'],
{ message: 'GAME_NAME must be either "gta5", "rdr3", or undefined' }
),
MAX_SLOTS: z.coerce.number().int().positive(),
QUIET_MODE: z.preprocess((val) => val === 'true', z.boolean()),
//Networking
TXA_URL: z.string().url(),
TXA_PORT: z.coerce.number().int().positive().refine(
(val) => val !== 30120,
'TXA_PORT cannot be 30120'
),
FXS_PORT: z.coerce.number().int().positive().refine(
(val) => val < 40120 || val > 40150,
'FXS_PORT cannot be between 40120 and 40150'
),
INTERFACE: z.string().ip({ version: "v4" }),
//Provider
PROVIDER_NAME: z.string()
.regex(
/^[a-zA-Z0-9_.\- ]{2,16}$/,
'Provider name must be between 2 and 16 characters long and can only contain letters, numbers, underscores, periods, hyphens and spaces.'
)
.refine(
x => !/[_.\- ]{2,}/.test(x),
'Provider name cannot contain consecutive special characters.'
)
.refine(
x => /^[a-zA-Z0-9].*[a-zA-Z0-9]$/.test(x),
'Provider name must start and end with a letter or number.'
),
PROVIDER_LOGO: z.string().url(),
//Defaults (no reason to coerce or check, except the cfxkey)
DEFAULT_DBHOST: z.string(),
DEFAULT_DBPORT: z.string(),
DEFAULT_DBUSER: z.string(),
DEFAULT_DBPASS: z.string(),
DEFAULT_DBNAME: z.string(),
DEFAULT_ACCOUNT: z.string().refine(
(val) => {
const parts = val.split(':').length;
return parts === 2 || parts === 3;
},
'The account needs to be in the username:fivemId or username:fivemId:bcrypt format',
),
DEFAULT_CFXKEY: z.string().refine(
//apparently zap still uses the old format?
(val) => consts.regexSvLicenseNew.test(val) || consts.regexSvLicenseOld.test(val),
'The key needs to be in the cfxk_xxxxxxxxxxxxxxxxxxxx_yyyyy format'
),
} as const;
export type HostEnvVars = {
[K in keyof typeof hostEnvVarSchemas]: z.infer<typeof hostEnvVarSchemas[K]> | undefined;
}
/**
* Parses the TXHOST_ env variables
*/
export const getHostVars = () => {
const txHostEnv: any = {};
for (const partialKey of Object.keys(hostEnvVarSchemas)) {
const keySchema = hostEnvVarSchemas[partialKey as keyof HostEnvVars];
const fullKey = `TXHOST_` + partialKey;
const value = process.env[fullKey];
if (value === undefined || value === '') continue;
if(/^['"]|['"]$/.test(value)) {
fatalError.GlobalData(20, [
'Invalid value for a TXHOST environment variable.',
'The value is surrounded by quotes (" or \'), and you must remove them.',
'This is likely a mistake in how you declared this env var.',
['Key', fullKey],
['Value', String(value)],
'For more information: https://aka.cfx.re/txadmin-env-config',
]);
}
const res = keySchema.safeParse(value);
if (!res.success) {
fatalError.GlobalData(20, [
'Invalid value for a TXHOST environment variable.',
['Key', fullKey],
['Value', String(value)],
'For more information: https://aka.cfx.re/txadmin-env-config',
], fromZodError(res.error, { prefix: null }));
}
txHostEnv[partialKey] = res.data;
}
return txHostEnv as HostEnvVars;
}