Skip to content

Commit 6389dec

Browse files
committed
api: move env loading into separate file
1 parent 8da0c51 commit 6389dec

File tree

2 files changed

+103
-94
lines changed

2 files changed

+103
-94
lines changed

api/src/config.js

Lines changed: 3 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,17 @@
1-
import { Constants } from "youtubei.js";
21
import { getVersion } from "@imput/version-info";
3-
import { services } from "./processing/service-config.js";
4-
import { supportsReusePort } from "./misc/cluster.js";
2+
import { loadEnvs, validateEnvs } from "./core/env.js";
53

64
const version = await getVersion();
75

8-
const disabledServices = process.env.DISABLED_SERVICES?.split(',') || [];
9-
const enabledServices = new Set(Object.keys(services).filter(e => {
10-
if (!disabledServices.includes(e)) {
11-
return e;
12-
}
13-
}));
14-
15-
const forceLocalProcessingOptions = ["never", "session", "always"];
16-
17-
const env = {
18-
apiURL: process.env.API_URL || '',
19-
apiPort: process.env.API_PORT || 9000,
20-
tunnelPort: process.env.API_PORT || 9000,
21-
22-
listenAddress: process.env.API_LISTEN_ADDRESS,
23-
freebindCIDR: process.platform === 'linux' && process.env.FREEBIND_CIDR,
24-
25-
corsWildcard: process.env.CORS_WILDCARD !== '0',
26-
corsURL: process.env.CORS_URL,
27-
28-
cookiePath: process.env.COOKIE_PATH,
29-
30-
rateLimitWindow: (process.env.RATELIMIT_WINDOW && parseInt(process.env.RATELIMIT_WINDOW)) || 60,
31-
rateLimitMax: (process.env.RATELIMIT_MAX && parseInt(process.env.RATELIMIT_MAX)) || 20,
32-
33-
tunnelRateLimitWindow: (process.env.TUNNEL_RATELIMIT_WINDOW && parseInt(process.env.TUNNEL_RATELIMIT_WINDOW)) || 60,
34-
tunnelRateLimitMax: (process.env.TUNNEL_RATELIMIT_MAX && parseInt(process.env.TUNNEL_RATELIMIT_MAX)) || 40,
35-
36-
sessionRateLimitWindow: (process.env.SESSION_RATELIMIT_WINDOW && parseInt(process.env.SESSION_RATELIMIT_WINDOW)) || 60,
37-
sessionRateLimit: (process.env.SESSION_RATELIMIT && parseInt(process.env.SESSION_RATELIMIT)) || 10,
38-
39-
durationLimit: (process.env.DURATION_LIMIT && parseInt(process.env.DURATION_LIMIT)) || 10800,
40-
streamLifespan: (process.env.TUNNEL_LIFESPAN && parseInt(process.env.TUNNEL_LIFESPAN)) || 90,
41-
42-
processingPriority: process.platform !== 'win32'
43-
&& process.env.PROCESSING_PRIORITY
44-
&& parseInt(process.env.PROCESSING_PRIORITY),
45-
46-
externalProxy: process.env.API_EXTERNAL_PROXY,
47-
48-
turnstileSitekey: process.env.TURNSTILE_SITEKEY,
49-
turnstileSecret: process.env.TURNSTILE_SECRET,
50-
jwtSecret: process.env.JWT_SECRET,
51-
jwtLifetime: process.env.JWT_EXPIRY || 120,
52-
53-
sessionEnabled: process.env.TURNSTILE_SITEKEY
54-
&& process.env.TURNSTILE_SECRET
55-
&& process.env.JWT_SECRET,
56-
57-
apiKeyURL: process.env.API_KEY_URL && new URL(process.env.API_KEY_URL),
58-
authRequired: process.env.API_AUTH_REQUIRED === '1',
59-
redisURL: process.env.API_REDIS_URL,
60-
instanceCount: (process.env.API_INSTANCE_COUNT && parseInt(process.env.API_INSTANCE_COUNT)) || 1,
61-
keyReloadInterval: 900,
62-
63-
enabledServices,
64-
65-
customInnertubeClient: process.env.CUSTOM_INNERTUBE_CLIENT,
66-
ytSessionServer: process.env.YOUTUBE_SESSION_SERVER,
67-
ytSessionReloadInterval: 300,
68-
ytSessionInnertubeClient: process.env.YOUTUBE_SESSION_INNERTUBE_CLIENT,
69-
ytAllowBetterAudio: process.env.YOUTUBE_ALLOW_BETTER_AUDIO !== "0",
70-
71-
// "never" | "session" | "always"
72-
forceLocalProcessing: process.env.FORCE_LOCAL_PROCESSING ?? "never",
73-
}
6+
let env = loadEnvs();
747

758
const genericUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36";
769
const cobaltUserAgent = `cobalt/${version} (+https://github.com/imputnet/cobalt)`;
7710

7811
export const setTunnelPort = (port) => env.tunnelPort = port;
7912
export const isCluster = env.instanceCount > 1;
8013

81-
if (env.sessionEnabled && env.jwtSecret.length < 16) {
82-
throw new Error("JWT_SECRET env is too short (must be at least 16 characters long)");
83-
}
84-
85-
if (env.instanceCount > 1 && !env.redisURL) {
86-
throw new Error("API_REDIS_URL is required when API_INSTANCE_COUNT is >= 2");
87-
} else if (env.instanceCount > 1 && !await supportsReusePort()) {
88-
console.error('API_INSTANCE_COUNT is not supported in your environment. to use this env, your node.js');
89-
console.error('version must be >= 23.1.0, and you must be running a recent enough version of linux');
90-
console.error('(or other OS that supports it). for more info, see `reusePort` option on');
91-
console.error('https://nodejs.org/api/net.html#serverlistenoptions-callback');
92-
throw new Error('SO_REUSEPORT is not supported');
93-
}
94-
95-
if (env.customInnertubeClient && !Constants.SUPPORTED_CLIENTS.includes(env.customInnertubeClient)) {
96-
console.error("CUSTOM_INNERTUBE_CLIENT is invalid. Provided client is not supported.");
97-
console.error(`Supported clients are: ${Constants.SUPPORTED_CLIENTS.join(', ')}\n`);
98-
throw new Error("Invalid CUSTOM_INNERTUBE_CLIENT");
99-
}
100-
101-
if (env.forceLocalProcessing && !forceLocalProcessingOptions.includes(env.forceLocalProcessing)) {
102-
console.error("FORCE_LOCAL_PROCESSING is invalid.");
103-
console.error(`Supported options are are: ${forceLocalProcessingOptions.join(', ')}\n`);
104-
throw new Error("Invalid FORCE_LOCAL_PROCESSING");
105-
}
14+
await validateEnvs(env);
10615

10716
export {
10817
env,

api/src/core/env.js

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { Constants } from "youtubei.js";
2+
import { supportsReusePort } from "../misc/cluster.js";
3+
import { services } from "../processing/service-config.js";
4+
5+
const forceLocalProcessingOptions = ["never", "session", "always"];
6+
7+
export const loadEnvs = (env = process.env) => {
8+
const disabledServices = env.DISABLED_SERVICES?.split(',') || [];
9+
const enabledServices = new Set(Object.keys(services).filter(e => {
10+
if (!disabledServices.includes(e)) {
11+
return e;
12+
}
13+
}));
14+
15+
return {
16+
apiURL: env.API_URL || '',
17+
apiPort: env.API_PORT || 9000,
18+
tunnelPort: env.API_PORT || 9000,
19+
20+
listenAddress: env.API_LISTEN_ADDRESS,
21+
freebindCIDR: process.platform === 'linux' && env.FREEBIND_CIDR,
22+
23+
corsWildcard: env.CORS_WILDCARD !== '0',
24+
corsURL: env.CORS_URL,
25+
26+
cookiePath: env.COOKIE_PATH,
27+
28+
rateLimitWindow: (env.RATELIMIT_WINDOW && parseInt(env.RATELIMIT_WINDOW)) || 60,
29+
rateLimitMax: (env.RATELIMIT_MAX && parseInt(env.RATELIMIT_MAX)) || 20,
30+
31+
tunnelRateLimitWindow: (env.TUNNEL_RATELIMIT_WINDOW && parseInt(env.TUNNEL_RATELIMIT_WINDOW)) || 60,
32+
tunnelRateLimitMax: (env.TUNNEL_RATELIMIT_MAX && parseInt(env.TUNNEL_RATELIMIT_MAX)) || 40,
33+
34+
sessionRateLimitWindow: (env.SESSION_RATELIMIT_WINDOW && parseInt(env.SESSION_RATELIMIT_WINDOW)) || 60,
35+
sessionRateLimit: (env.SESSION_RATELIMIT && parseInt(env.SESSION_RATELIMIT)) || 10,
36+
37+
durationLimit: (env.DURATION_LIMIT && parseInt(env.DURATION_LIMIT)) || 10800,
38+
streamLifespan: (env.TUNNEL_LIFESPAN && parseInt(env.TUNNEL_LIFESPAN)) || 90,
39+
40+
processingPriority: process.platform !== 'win32'
41+
&& env.PROCESSING_PRIORITY
42+
&& parseInt(env.PROCESSING_PRIORITY),
43+
44+
externalProxy: env.API_EXTERNAL_PROXY,
45+
46+
turnstileSitekey: env.TURNSTILE_SITEKEY,
47+
turnstileSecret: env.TURNSTILE_SECRET,
48+
jwtSecret: env.JWT_SECRET,
49+
jwtLifetime: env.JWT_EXPIRY || 120,
50+
51+
sessionEnabled: env.TURNSTILE_SITEKEY
52+
&& env.TURNSTILE_SECRET
53+
&& env.JWT_SECRET,
54+
55+
apiKeyURL: env.API_KEY_URL && new URL(env.API_KEY_URL),
56+
authRequired: env.API_AUTH_REQUIRED === '1',
57+
redisURL: env.API_REDIS_URL,
58+
instanceCount: (env.API_INSTANCE_COUNT && parseInt(env.API_INSTANCE_COUNT)) || 1,
59+
keyReloadInterval: 900,
60+
61+
enabledServices,
62+
63+
customInnertubeClient: env.CUSTOM_INNERTUBE_CLIENT,
64+
ytSessionServer: env.YOUTUBE_SESSION_SERVER,
65+
ytSessionReloadInterval: 300,
66+
ytSessionInnertubeClient: env.YOUTUBE_SESSION_INNERTUBE_CLIENT,
67+
ytAllowBetterAudio: env.YOUTUBE_ALLOW_BETTER_AUDIO !== "0",
68+
69+
// "never" | "session" | "always"
70+
forceLocalProcessing: env.FORCE_LOCAL_PROCESSING ?? "never",
71+
};
72+
}
73+
74+
export const validateEnvs = async (env) => {
75+
if (env.sessionEnabled && env.jwtSecret.length < 16) {
76+
throw new Error("JWT_SECRET env is too short (must be at least 16 characters long)");
77+
}
78+
79+
if (env.instanceCount > 1 && !env.redisURL) {
80+
throw new Error("API_REDIS_URL is required when API_INSTANCE_COUNT is >= 2");
81+
} else if (env.instanceCount > 1 && !await supportsReusePort()) {
82+
console.error('API_INSTANCE_COUNT is not supported in your environment. to use this env, your node.js');
83+
console.error('version must be >= 23.1.0, and you must be running a recent enough version of linux');
84+
console.error('(or other OS that supports it). for more info, see `reusePort` option on');
85+
console.error('https://nodejs.org/api/net.html#serverlistenoptions-callback');
86+
throw new Error('SO_REUSEPORT is not supported');
87+
}
88+
89+
if (env.customInnertubeClient && !Constants.SUPPORTED_CLIENTS.includes(env.customInnertubeClient)) {
90+
console.error("CUSTOM_INNERTUBE_CLIENT is invalid. Provided client is not supported.");
91+
console.error(`Supported clients are: ${Constants.SUPPORTED_CLIENTS.join(', ')}\n`);
92+
throw new Error("Invalid CUSTOM_INNERTUBE_CLIENT");
93+
}
94+
95+
if (env.forceLocalProcessing && !forceLocalProcessingOptions.includes(env.forceLocalProcessing)) {
96+
console.error("FORCE_LOCAL_PROCESSING is invalid.");
97+
console.error(`Supported options are are: ${forceLocalProcessingOptions.join(', ')}\n`);
98+
throw new Error("Invalid FORCE_LOCAL_PROCESSING");
99+
}
100+
}

0 commit comments

Comments
 (0)