134 lines
3.9 KiB
TypeScript
134 lines
3.9 KiB
TypeScript
import 'dotenv/config';
|
|
import { z } from 'zod';
|
|
|
|
/**
|
|
* The configuration schema for the server.
|
|
*/
|
|
const configSchema = z.object({
|
|
|
|
/**
|
|
* The database configuration.
|
|
*/
|
|
database: z.object({
|
|
path: z.string().default('data.db'),
|
|
}),
|
|
|
|
/**
|
|
* The server configuration.
|
|
*/
|
|
server: z.object({
|
|
port: z.coerce.number().int()
|
|
.positive()
|
|
.default(3000),
|
|
host: z.string().default('0.0.0.0'),
|
|
|
|
/** Maximum encoded HTTP body or WebSocket message size in bytes. */
|
|
maxRequestBodyBytes: z.coerce
|
|
.number()
|
|
.int()
|
|
.positive()
|
|
.default(1024 * 1024),
|
|
cors: z
|
|
.object({
|
|
origin: z.string().default('*'),
|
|
methods: z.array(z.string()).default([ 'GET', 'POST', 'PUT', 'DELETE', 'OPTIONS' ]),
|
|
allowedHeaders: z.array(z.string()).default([ 'Content-Type', 'cache-control', 'X-Timestamp', 'X-Public-Key', 'X-Signature' ]),
|
|
})
|
|
.partial()
|
|
.prefault({}),
|
|
}),
|
|
|
|
/**
|
|
* The authentication configuration.
|
|
*/
|
|
auth: z
|
|
.object({
|
|
timestampWindowMs: z.coerce
|
|
.number()
|
|
.int()
|
|
.positive()
|
|
.default(5 * 60 * 1000),
|
|
uniqueRequestCleanupIntervalMs: z.coerce
|
|
.number()
|
|
.int()
|
|
.positive()
|
|
.default(10 * 60 * 1000),
|
|
})
|
|
.prefault({}),
|
|
});
|
|
|
|
/** Raw configuration object accepted before Zod parsing. */
|
|
type ConfigInput = z.input<typeof configSchema>;
|
|
|
|
/** Fully parsed and defaulted configuration shape. */
|
|
type ConfigSchema = z.output<typeof configSchema>;
|
|
|
|
/**
|
|
* Typed, validated server configuration loaded from environment or objects.
|
|
*/
|
|
export class Config {
|
|
/**
|
|
* Creates a new Config from the environment variables.
|
|
* @returns The Config.
|
|
*/
|
|
static fromEnv(): Config {
|
|
return this.from({
|
|
database: {
|
|
path: process.env.DATABASE_PATH,
|
|
},
|
|
server: {
|
|
port: process.env.SERVER_PORT,
|
|
maxRequestBodyBytes: process.env.SERVER_MAX_REQUEST_BODY_BYTES,
|
|
host: process.env.SERVER_HOST,
|
|
cors: {
|
|
origin: process.env.CORS_ORIGIN,
|
|
methods: process.env.CORS_METHODS?.split(','),
|
|
allowedHeaders: process.env.CORS_ALLOWED_HEADERS?.split(','),
|
|
},
|
|
},
|
|
auth: {
|
|
timestampWindowMs: process.env.AUTH_TIMESTAMP_WINDOW_MS ? Number(process.env.AUTH_TIMESTAMP_WINDOW_MS) : undefined,
|
|
uniqueRequestCleanupIntervalMs: process.env.AUTH_UNIQUE_REQUEST_CLEANUP_INTERVAL_MS ? Number(process.env.AUTH_UNIQUE_REQUEST_CLEANUP_INTERVAL_MS) : undefined,
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Creates a new Config from a configuration object.
|
|
* @param config - The configuration object.
|
|
* @returns The Config.
|
|
*/
|
|
static from(config: ConfigInput): Config {
|
|
return new Config(configSchema.parse(config));
|
|
}
|
|
|
|
/**
|
|
* Gets the database configuration.
|
|
* @returns The database configuration.
|
|
*/
|
|
public get database(): Readonly<ConfigSchema['database']> {
|
|
return this.config.database;
|
|
}
|
|
|
|
/**
|
|
* Gets the server configuration.
|
|
* @returns The server configuration.
|
|
*/
|
|
public get server(): Readonly<ConfigSchema['server']> {
|
|
return this.config.server;
|
|
}
|
|
|
|
/**
|
|
* Gets the authentication configuration.
|
|
* @returns The authentication configuration.
|
|
*/
|
|
public get auth(): Readonly<ConfigSchema['auth']> {
|
|
return this.config.auth;
|
|
}
|
|
|
|
/**
|
|
* @param config - Parsed configuration produced by the Zod schema.
|
|
*/
|
|
private constructor(private readonly config: ConfigSchema) {}
|
|
}
|