cursorpro's picture
Upload 57 files
5ec2e9b verified
import { z, ZodError } from "zod";
import { parse } from "@std/toml";
export const ConfigSchema = z.object({
server: z.object({
port: z.number().default(Number(Deno.env.get("PORT")) || 8282),
host: z.string().default(Deno.env.get("HOST") || "127.0.0.1"),
use_unix_socket: z.boolean().default(
Deno.env.get("SERVER_USE_UNIX_SOCKET") === "true" || false,
),
unix_socket_path: z.string().default(
Deno.env.get("SERVER_UNIX_SOCKET_PATH") ||
"/tmp/invidious-companion.sock",
),
base_path: z.string()
.default(Deno.env.get("SERVER_BASE_PATH") || "/companion")
.refine(
(path) => path.startsWith("/"),
{
message:
"SERVER_BASE_PATH must start with a forward slash (/). Example: '/companion'",
},
)
.refine(
(path) => !path.endsWith("/") || path === "/",
{
message:
"SERVER_BASE_PATH must not end with a forward slash (/) unless it's the root path. Example: '/companion' not '/companion/'",
},
)
.refine(
(path) => !path.includes("//"),
{
message:
"SERVER_BASE_PATH must not contain double slashes (//). Example: '/companion' not '//companion' or '/comp//anion'",
},
),
secret_key: z.preprocess(
(val) =>
val === undefined
? Deno.env.get("SERVER_SECRET_KEY") || ""
: val,
z.string().min(1),
).default(undefined),
verify_requests: z.boolean().default(
Deno.env.get("SERVER_VERIFY_REQUESTS") === "true" || false,
),
encrypt_query_params: z.boolean().default(
Deno.env.get("SERVER_ENCRYPT_QUERY_PARAMS") === "true" || false,
),
enable_metrics: z.boolean().default(
Deno.env.get("SERVER_ENABLE_METRICS") === "true" || false,
),
}).strict().default({}),
cache: z.object({
enabled: z.boolean().default(
Deno.env.get("CACHE_ENABLED") === "false" ? false : true,
),
directory: z.string().default(
Deno.env.get("CACHE_DIRECTORY") || "/var/tmp",
),
}).strict().default({}),
networking: z.object({
proxy: z.string().nullable().default(Deno.env.get("PROXY") || null),
auto_proxy: z.boolean().default(
Deno.env.get("NETWORKING_AUTO_PROXY") === "true" || false,
),
vpn_source: z.number().default(
Number(Deno.env.get("NETWORKING_VPN_SOURCE")) || 1,
),
ipv6_block: z.string().nullable().default(
Deno.env.get("NETWORKING_IPV6_BLOCK") || null,
),
fetch: z.object({
timeout_ms: z.number().default(
Number(Deno.env.get("NETWORKING_FETCH_TIMEOUT_MS")) || 30_000,
),
retry: z.object({
enabled: z.boolean().default(
Deno.env.get("NETWORKING_FETCH_RETRY_ENABLED") === "true" ||
false,
),
times: z.number().optional().default(
Number(Deno.env.get("NETWORKING_FETCH_RETRY_TIMES")) || 1,
),
initial_debounce: z.number().optional().default(
Number(
Deno.env.get("NETWORKING_FETCH_RETRY_INITIAL_DEBOUNCE"),
) || 0,
),
debounce_multiplier: z.number().optional().default(
Number(
Deno.env.get(
"NETWORKING_FETCH_RETRY_DEBOUNCE_MULTIPLIER",
),
) || 0,
),
}).strict().default({}),
}).strict().default({}),
videoplayback: z.object({
ump: z.boolean().default(
Deno.env.get("NETWORKING_VIDEOPLAYBACK_UMP") === "true" ||
false,
),
video_fetch_chunk_size_mb: z.number().default(
Number(
Deno.env.get(
"NETWORKING_VIDEOPLAYBACK_VIDEO_FETCH_CHUNK_SIZE_MB",
),
) || 5,
),
}).strict().default({}),
}).strict().default({}),
jobs: z.object({
youtube_session: z.object({
po_token_enabled: z.boolean().default(
Deno.env.get("JOBS_YOUTUBE_SESSION_PO_TOKEN_ENABLED") ===
"false"
? false
: true,
),
frequency: z.string().default(
Deno.env.get("JOBS_YOUTUBE_SESSION_FREQUENCY") || "*/5 * * * *",
),
}).strict().default({}),
}).strict().default({}),
youtube_session: z.object({
oauth_enabled: z.boolean().default(
Deno.env.get("YOUTUBE_SESSION_OAUTH_ENABLED") === "true" || false,
),
cookies: z.string().default(
Deno.env.get("YOUTUBE_SESSION_COOKIES") || "",
),
}).strict().default({}),
}).strict();
export type Config = z.infer<typeof ConfigSchema>;
export async function parseConfig() {
const configFileName = Deno.env.get("CONFIG_FILE") || "config/config.toml";
const configFileContents = await Deno.readTextFile(configFileName).catch(
() => null,
);
if (configFileContents) {
console.log("[INFO] Using custom settings local file");
} else {
console.log(
"[INFO] No local config file found, using default config",
);
}
try {
const rawConfig = configFileContents ? parse(configFileContents) : {};
const validatedConfig = ConfigSchema.parse(rawConfig);
console.log("Loaded Configuration", validatedConfig);
return validatedConfig;
} catch (err) {
let errorMessage =
"There is an error in your configuration, check your environment variables";
if (configFileContents) {
errorMessage +=
` or in your configuration file located at ${configFileName}`;
}
console.log(errorMessage);
if (err instanceof ZodError) {
console.log(err.issues);
// Include detailed error information in the thrown error for testing
const detailedMessage = err.issues.map((issue) =>
`${issue.path.join(".")}: ${issue.message}`
).join("; ");
throw new Error(
`Failed to parse configuration file: ${detailedMessage}`,
);
}
// rethrow error if not Zod
throw err;
}
}