diff --git a/src/api/Server.ts b/src/api/Server.ts index ab0505d7c..de6759bea 100644 --- a/src/api/Server.ts +++ b/src/api/Server.ts @@ -65,13 +65,13 @@ export class SpacebarServer extends Server { // await BcryptWorkerPool.Init(8); // TODO: make configurable await GifProviderManager.init(); - const logRequests = EnvConfig.logging.logRequests !== ""; + const logRequests = EnvConfig.get().logging.logRequests !== ""; if (logRequests) { this.app.use( morgan("combined", { skip: (req, res) => { - let skip = !(EnvConfig.logging.logRequests.includes(res.statusCode.toString()) ?? false); - if (EnvConfig.logging.logRequests.charAt(0) == "-") skip = !skip; + let skip = !(EnvConfig.get().logging.logRequests.includes(res.statusCode.toString()) ?? false); + if (EnvConfig.get().logging.logRequests.charAt(0) == "-") skip = !skip; return skip; }, }), diff --git a/src/api/middlewares/Route.ts b/src/api/middlewares/Route.ts index 9bb1d3161..3ae1d9b9d 100644 --- a/src/api/middlewares/Route.ts +++ b/src/api/middlewares/Route.ts @@ -189,7 +189,7 @@ export function route(opts: RouteOptions) { message: x.message || "", }), ); - if (EnvConfig.logging.logValidationErrors) { + if (EnvConfig.get().logging.logValidationErrors) { console.log(`[VALIDATION ERROR] ${req.method} ${req.originalUrl} - SCHEMA='${opts.requestBody}' -`, validate?.errors); if (process.env.LOG_VALIDATION_ERROR_SCHEMAS) console.log(validate.schema); } diff --git a/src/api/routes/users/@me/settings-proto/1.ts b/src/api/routes/users/@me/settings-proto/1.ts index 9c81c43d1..6ca59014a 100644 --- a/src/api/routes/users/@me/settings-proto/1.ts +++ b/src/api/routes/users/@me/settings-proto/1.ts @@ -142,7 +142,7 @@ async function patchUserSettings(userId: string, updatedSettings: PreloadedUserS }; } - if (EnvConfig.logging.logProtoUpdates.includes("SETTINGS")) console.log(`Updating user settings for user ${userId} with atomic=${atomic}:`, updatedSettings); + if (EnvConfig.get().logging.logProtoUpdates.includes("SETTINGS")) console.log(`Updating user settings for user ${userId} with atomic=${atomic}:`, updatedSettings); if (!atomic) { settings = PreloadedUserSettings.fromJson( diff --git a/src/api/routes/users/@me/settings-proto/2.ts b/src/api/routes/users/@me/settings-proto/2.ts index f595f53b6..560d5acd7 100644 --- a/src/api/routes/users/@me/settings-proto/2.ts +++ b/src/api/routes/users/@me/settings-proto/2.ts @@ -142,7 +142,7 @@ async function patchUserSettings(userId: string, updatedSettings: FrecencyUserSe }; } - if (EnvConfig.logging.logProtoUpdates.includes("FRECENCY")) console.log(`Updating frecency settings for user ${userId} with atomic=${atomic}:`, updatedSettings); + if (EnvConfig.get().logging.logProtoUpdates.includes("FRECENCY")) console.log(`Updating frecency settings for user ${userId} with atomic=${atomic}:`, updatedSettings); if (!atomic) { settings = FrecencyUserSettings.fromJson( diff --git a/src/api/start.ts b/src/api/start.ts index 066bc2d0f..40d32c349 100644 --- a/src/api/start.ts +++ b/src/api/start.ts @@ -41,7 +41,7 @@ if (cluster.isPrimary && process.env.NODE_ENV == "production") { console.log(`Primary PID: ${process.pid}`); // Fork workers. - for (let i = 0; i < EnvConfig.threads; i++) { + for (let i = 0; i < EnvConfig.get().threads; i++) { cluster.fork(); } diff --git a/src/bundle/Server.ts b/src/bundle/Server.ts index 6e0bb3c85..549d78138 100644 --- a/src/bundle/Server.ts +++ b/src/bundle/Server.ts @@ -61,13 +61,13 @@ async function main() { await initDatabase(); await Config.init(); - const logRequests = EnvConfig.logging.logRequests != ""; + const logRequests = EnvConfig.get().logging.logRequests != ""; if (logRequests) { app.use( morgan("combined", { skip: (req, res) => { - let skip = !(EnvConfig.logging.logRequests.includes(res.statusCode.toString()) ?? false); - if (EnvConfig.logging.logRequests.charAt(0) == "-") skip = !skip; + let skip = !(EnvConfig.get().logging.logRequests.includes(res.statusCode.toString()) ?? false); + if (EnvConfig.get().logging.logRequests.charAt(0) == "-") skip = !skip; return skip; }, }), diff --git a/src/bundle/start.ts b/src/bundle/start.ts index a87f04dbc..13f17ec18 100644 --- a/src/bundle/start.ts +++ b/src/bundle/start.ts @@ -33,7 +33,7 @@ import { centerString } from "@spacebar/extensions"; import { getRevInfoOrFail, Logo, EnvConfig } from "@spacebar/util"; import { initStats } from "./stats"; -const cores = EnvConfig.threads; +const cores = EnvConfig.get().threads; if (cluster.isPrimary) { const revInfo = getRevInfoOrFail(); diff --git a/src/cdn/Server.ts b/src/cdn/Server.ts index 1562f11e0..2b7169073 100644 --- a/src/cdn/Server.ts +++ b/src/cdn/Server.ts @@ -49,13 +49,13 @@ export class CDNServer extends Server { (_) => console.log("[CDN] Attachment migration failed"), ); - const logRequests = EnvConfig.logging.logRequests != undefined; + const logRequests = EnvConfig.get().logging.logRequests != undefined; if (logRequests) { this.app.use( morgan("combined", { skip: (req, res) => { - let skip = !(EnvConfig.logging.logRequests.includes(res.statusCode.toString()) ?? false); - if (EnvConfig.logging.logRequests.charAt(0) == "-") skip = !skip; + let skip = !(EnvConfig.get().logging.logRequests.includes(res.statusCode.toString()) ?? false); + if (EnvConfig.get().logging.logRequests.charAt(0) == "-") skip = !skip; return skip; }, }), diff --git a/src/database/Database.ts b/src/database/Database.ts index 1d414992d..b1bd6c3cc 100644 --- a/src/database/Database.ts +++ b/src/database/Database.ts @@ -59,10 +59,10 @@ export const DataSourceOptions = isHeadlessProcess charset: "utf8mb4", url: process.env.DATABASE, entities: [path.join(__dirname, "entities", "*.js")], - synchronize: EnvConfig.database.unsafeSchemaSync, - logging: EnvConfig.logging.logDatabaseQueries, + synchronize: EnvConfig.get().database.unsafeSchemaSync, + logging: EnvConfig.get().logging.logDatabaseQueries, // Figure out why this doesn't work someday. - // relationLoadStrategy: EnvConfig.database.disableJoins ? "query" : undefined, // mirrors DB_NO_JOINS + // relationLoadStrategy: EnvConfig.get().database.disableJoins ? "query" : undefined, // mirrors DB_NO_JOINS bigNumberStrings: false, supportBigNumbers: true, name: "default", @@ -85,7 +85,7 @@ export function getDatabase(): DataSource | null { export async function initDatabase(): Promise { if (dbConnection) return dbConnection; - if (!EnvConfig.database.unsafeSchemaSync) { + if (!EnvConfig.get().database.unsafeSchemaSync) { const supported = ["postgres"]; if (!supported.includes(DatabaseType)) { console.log( diff --git a/src/gateway/events/Connection.ts b/src/gateway/events/Connection.ts index 6933ceb14..00ceb88be 100644 --- a/src/gateway/events/Connection.ts +++ b/src/gateway/events/Connection.ts @@ -113,7 +113,7 @@ export async function Connection(this: WS.Server, rawSocket: WS, request: Incomi console.log(`[Gateway] New connection from ${ipAddress}, total ${this.clients.size}`); - if (EnvConfig.logging.logGatewayEvents) + if (EnvConfig.get().logging.logGatewayEvents) [ "close", "error", diff --git a/src/util/Signing.ts b/src/util/Signing.ts index 0756eacee..e4b446585 100644 --- a/src/util/Signing.ts +++ b/src/util/Signing.ts @@ -45,7 +45,7 @@ export class NewUrlSignatureData extends NewUrlUserSignatureData { throw new Error("Either path or url must be provided for URL signing"); } if (this.path && this.url) { - if (EnvConfig.logging.logCdnSignatures) console.warn("[Signing] Both path and url are provided, using path for signing", this, new Error().stack); + if (EnvConfig.get().logging.logCdnSignatures) console.warn("[Signing] Both path and url are provided, using path for signing", this, new Error().stack); } if (this.url) { try { @@ -94,10 +94,10 @@ export class UrlSignResult { static fromUrl(url: URL | string): UrlSignResult { if (typeof url === "string") { - if (EnvConfig.logging.logCdnSignatures) console.debug("[Signing] Parsing URL from string:", url); + if (EnvConfig.get().logging.logCdnSignatures) console.debug("[Signing] Parsing URL from string:", url); url = new URL(url); } - if (EnvConfig.logging.logCdnSignatures) console.debug("[Signing] Parsing URL from URL object:", url.toString()); + if (EnvConfig.get().logging.logCdnSignatures) console.debug("[Signing] Parsing URL from URL object:", url.toString()); const ex = url.searchParams.get("ex"); const is = url.searchParams.get("is"); const hm = url.searchParams.get("hm"); @@ -149,7 +149,7 @@ function calculateHash(request: UrlSignatureData): UrlSignResult { "[Signing] CDN Signature IP is enabled but we couldn't find the IP field in the request. This may cause issues with signature validation. Please report this to the Spacebar team!", ); else { - if (EnvConfig.logging.logCdnSignatures) console.log("[Signing] CDN Signature IP is enabled, adding IP to hash:", request.ip); + if (EnvConfig.get().logging.logCdnSignatures) console.log("[Signing] CDN Signature IP is enabled, adding IP to hash:", request.ip); data.update(request.ip!); ipData = new Int8Array(new TextEncoder().encode(request.ip!)); } @@ -162,7 +162,7 @@ function calculateHash(request: UrlSignatureData): UrlSignResult { "[Signing] CDN Signature User-Agent is enabled but we couldn't find the user-agent header in the request. This may cause issues with signature validation. Please report this to the Spacebar team!", ); else { - if (EnvConfig.logging.logCdnSignatures) console.log("[Signing] CDN Signature User-Agent is enabled, adding User-Agent to hash:", request.userAgent); + if (EnvConfig.get().logging.logCdnSignatures) console.log("[Signing] CDN Signature User-Agent is enabled, adding User-Agent to hash:", request.userAgent); data.update(request.userAgent!); userAgentData = new Int8Array(new TextEncoder().encode(request.userAgent!)); } @@ -193,7 +193,7 @@ function calculateHash(request: UrlSignatureData): UrlSignResult { expiresAt: request.expiresAt, hash, }); - if (EnvConfig.logging.logCdnSignatures) + if (EnvConfig.get().logging.logCdnSignatures) console.log( "[Signing]", { @@ -214,7 +214,7 @@ export const isExpired = (data: UrlSignResult | UrlSignatureData) => { const expiresAt = parseInt(data.expiresAt, 16); if (Number.isNaN(issuedAt) || Number.isNaN(expiresAt)) { - if (EnvConfig.logging.logCdnSignatures) console.debug("[Signing] Invalid timestamps in query"); + if (EnvConfig.get().logging.logCdnSignatures) console.debug("[Signing] Invalid timestamps in query"); return true; } @@ -222,13 +222,13 @@ export const isExpired = (data: UrlSignResult | UrlSignatureData) => { const isExpired = expiresAt < currentTime; if (isExpired) { - if (EnvConfig.logging.logCdnSignatures) console.debug("[Signing] Signature expired"); + if (EnvConfig.get().logging.logCdnSignatures) console.debug("[Signing] Signature expired"); return true; } const isValidIssuedAt = issuedAt < currentTime; if (!isValidIssuedAt) { - if (EnvConfig.logging.logCdnSignatures) console.debug("[Signing] Signature issued in the future"); + if (EnvConfig.get().logging.logCdnSignatures) console.debug("[Signing] Signature issued in the future"); return true; } @@ -238,13 +238,13 @@ export const isExpired = (data: UrlSignResult | UrlSignatureData) => { export const hasValidSignature = (req: NewUrlUserSignatureData, sig: UrlSignResult) => { // if the required query parameters are not present, return false if (!sig.expiresAt || !sig.issuedAt || !sig.hash) { - if (EnvConfig.logging.logCdnSignatures) console.warn("[Signing] Missing required query parameters for signature validation"); + if (EnvConfig.get().logging.logCdnSignatures) console.warn("[Signing] Missing required query parameters for signature validation"); return false; } // check if the signature is expired if (isExpired(sig)) { - if (EnvConfig.logging.logCdnSignatures) console.warn("[Signing] Signature is expired"); + if (EnvConfig.get().logging.logCdnSignatures) console.warn("[Signing] Signature is expired"); return false; } @@ -279,7 +279,7 @@ export const hasValidSignature = (req: NewUrlUserSignatureData, sig: UrlSignResu const isHashValid = calculated.length === received.length && timingSafeEqual(calculated, received); if (!isHashValid) - if (EnvConfig.logging.logCdnSignatures) + if (EnvConfig.get().logging.logCdnSignatures) console.warn(`Signature validation for ${sig.path} (is=${sig.issuedAt}, ex=${sig.expiresAt}) failed: calculated: ${calcd}, received: ${sig.hash}`); return isHashValid; diff --git a/src/util/config/EnvConfig.ts b/src/util/config/EnvConfig.ts index 25fa531c7..03b3f9b34 100644 --- a/src/util/config/EnvConfig.ts +++ b/src/util/config/EnvConfig.ts @@ -16,36 +16,42 @@ along with this program. If not, see . */ +import os from "node:os"; import { LogEnvConfiguration } from "./envTypes/LogEnvConfiguration"; import { DatabaseEnvConfiguration } from "./envTypes/DatabaseEnvConfiguration"; -import os from "os"; import { ConfigurationEnvConfiguration } from "./envTypes/ConfigurationEnvConfiguration"; +const logConfig = new LogEnvConfiguration(); +const databaseConfig = new DatabaseEnvConfiguration(); +const configurationConfig = new ConfigurationEnvConfiguration(); + export class EnvConfig { - private static logConfig = new LogEnvConfiguration(); - private static databaseConfig = new DatabaseEnvConfiguration(); - private static configurationConfig = new ConfigurationEnvConfiguration(); + private static _instance: EnvConfig; + static get() { + if (!this._instance) this._instance = new EnvConfig(); + return this._instance; + } - static get logging(): LogEnvConfiguration { - return this.logConfig; - } + get logging(): LogEnvConfiguration { + return logConfig; + } - static get database(): DatabaseEnvConfiguration { - return this.databaseConfig; - } + get database(): DatabaseEnvConfiguration { + return databaseConfig; + } - static get configuration(): ConfigurationEnvConfiguration { - return this.configurationConfig; - } + get configuration(): ConfigurationEnvConfiguration { + return configurationConfig; + } - static get threads(): number { - try { - return Number(process.env.THREADS) || os.cpus().length; - } catch { - console.log("[EnvConfig] Failed to get thread count! Using 1..."); - return 1; - } - } + get threads(): number { + try { + return Number(process.env.THREADS) || os.cpus().length; + } catch { + console.log("[EnvConfig] Failed to get thread count! Using 1..."); + return 1; + } + } } // Deprecations: diff --git a/src/util/util/Token.ts b/src/util/util/Token.ts index a1a249606..d584415bc 100644 --- a/src/util/util/Token.ts +++ b/src/util/util/Token.ts @@ -52,7 +52,7 @@ export type UserTokenData = { }; function logAuth(text: string) { - if (!EnvConfig.logging.logAuthentication) return; + if (!EnvConfig.get().logging.logAuthentication) return; console.log(`[AUTH] ${text}`); } diff --git a/src/webrtc/events/Connection.ts b/src/webrtc/events/Connection.ts index 997b59bdc..6bd7d0935 100644 --- a/src/webrtc/events/Connection.ts +++ b/src/webrtc/events/Connection.ts @@ -36,7 +36,7 @@ export async function Connection(this: WS.Server, rawSocket: WS, request: Incomi socket.rawSocket.on("message", (data, isBinary) => onMessage(socket, data as Buffer)); console.log("[WebRTC] new connection", request.url); - if (EnvConfig.logging.logGatewayEvents) { + if (EnvConfig.get().logging.logGatewayEvents) { [ "close", "error",