mirror of
https://github.com/YuzuZensai/TrollSSH.git
synced 2026-09-13 15:49:02 +00:00
✨ feat: expand config with playback, rendering, and connection limits
This commit is contained in:
+57
-16
@@ -3,43 +3,84 @@ import { loadConfig } from "./config";
|
|||||||
|
|
||||||
describe("loadConfig", () => {
|
describe("loadConfig", () => {
|
||||||
test("returns defaults when env vars are unset", () => {
|
test("returns defaults when env vars are unset", () => {
|
||||||
const config = loadConfig({});
|
expect(loadConfig({})).toEqual({
|
||||||
expect(config).toEqual({
|
|
||||||
host: "0.0.0.0",
|
host: "0.0.0.0",
|
||||||
port: 22,
|
port: 22,
|
||||||
maxLoop: 5,
|
maxLoop: 5,
|
||||||
|
playbackMode: "loop",
|
||||||
|
allowUserControl: true,
|
||||||
|
switchDebounceMs: 120,
|
||||||
loginDelay: 1500,
|
loginDelay: 1500,
|
||||||
maxConnections: 10,
|
maxConnections: 10,
|
||||||
|
maxTotalConnections: 1000,
|
||||||
|
maxAuthAttempts: 6,
|
||||||
|
handshakeTimeout: 10000,
|
||||||
|
maxDimension: 512,
|
||||||
|
frameResolution: 360,
|
||||||
brightnessThreshold: 40,
|
brightnessThreshold: 40,
|
||||||
|
charset: "detailed",
|
||||||
|
invert: false,
|
||||||
logCredentials: false,
|
logCredentials: false,
|
||||||
|
videoPath: undefined,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("overrides defaults from env vars", () => {
|
test("overrides defaults from env vars", () => {
|
||||||
const config = loadConfig({
|
expect(
|
||||||
HOST: "127.0.0.1",
|
loadConfig({
|
||||||
PORT: "2222",
|
HOST: "127.0.0.1",
|
||||||
MAX_LOOP: "3",
|
PORT: "2222",
|
||||||
LOGIN_DELAY: "500",
|
MAX_LOOP: "3",
|
||||||
MAX_CONNECTIONS: "20",
|
PLAYBACK_MODE: "random",
|
||||||
BRIGHTNESS_THRESHOLD: "60",
|
ALLOW_USER_CONTROL: "false",
|
||||||
LOG_CREDENTIALS: "true",
|
SWITCH_DEBOUNCE_MS: "200",
|
||||||
});
|
LOGIN_DELAY: "500",
|
||||||
expect(config).toEqual({
|
MAX_CONNECTIONS: "20",
|
||||||
|
MAX_TOTAL_CONNECTIONS: "50",
|
||||||
|
MAX_AUTH_ATTEMPTS: "3",
|
||||||
|
HANDSHAKE_TIMEOUT: "5000",
|
||||||
|
MAX_DIMENSION: "200",
|
||||||
|
FRAME_RESOLUTION: "480",
|
||||||
|
BRIGHTNESS_THRESHOLD: "60",
|
||||||
|
CHARSET: "blocks",
|
||||||
|
INVERT: "true",
|
||||||
|
LOG_CREDENTIALS: "true",
|
||||||
|
VIDEO_PATH: "/videos/clip.mkv",
|
||||||
|
})
|
||||||
|
).toEqual({
|
||||||
host: "127.0.0.1",
|
host: "127.0.0.1",
|
||||||
port: 2222,
|
port: 2222,
|
||||||
maxLoop: 3,
|
maxLoop: 3,
|
||||||
|
playbackMode: "random",
|
||||||
|
allowUserControl: false,
|
||||||
|
switchDebounceMs: 200,
|
||||||
loginDelay: 500,
|
loginDelay: 500,
|
||||||
maxConnections: 20,
|
maxConnections: 20,
|
||||||
|
maxTotalConnections: 50,
|
||||||
|
maxAuthAttempts: 3,
|
||||||
|
handshakeTimeout: 5000,
|
||||||
|
maxDimension: 200,
|
||||||
|
frameResolution: 480,
|
||||||
brightnessThreshold: 60,
|
brightnessThreshold: 60,
|
||||||
|
charset: "blocks",
|
||||||
|
invert: true,
|
||||||
logCredentials: true,
|
logCredentials: true,
|
||||||
|
videoPath: "/videos/clip.mkv",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("logCredentials is false for any value other than the string 'true'", () => {
|
test("boolean env vars are true only for the exact string 'true'", () => {
|
||||||
expect(loadConfig({ LOG_CREDENTIALS: "1" }).logCredentials).toBe(false);
|
expect(loadConfig({ LOG_CREDENTIALS: "1" }).logCredentials).toBe(false);
|
||||||
expect(loadConfig({ LOG_CREDENTIALS: "false" }).logCredentials).toBe(
|
expect(loadConfig({ INVERT: "yes" }).invert).toBe(false);
|
||||||
false
|
expect(loadConfig({ INVERT: "true" }).invert).toBe(true);
|
||||||
);
|
});
|
||||||
|
|
||||||
|
test("falls back to defaults for non-numeric and out-of-range values", () => {
|
||||||
|
expect(loadConfig({ PORT: "not-a-number" }).port).toBe(22);
|
||||||
|
expect(loadConfig({ PORT: "99999" }).port).toBe(65535);
|
||||||
|
expect(loadConfig({ MAX_DIMENSION: "0" }).maxDimension).toBe(1);
|
||||||
|
expect(
|
||||||
|
loadConfig({ BRIGHTNESS_THRESHOLD: "999" }).brightnessThreshold
|
||||||
|
).toBe(100);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+62
-10
@@ -4,25 +4,77 @@ export interface Config {
|
|||||||
host: string;
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
maxLoop: number;
|
maxLoop: number;
|
||||||
|
playbackMode: "loop" | "random";
|
||||||
|
allowUserControl: boolean;
|
||||||
|
switchDebounceMs: number;
|
||||||
loginDelay: number;
|
loginDelay: number;
|
||||||
maxConnections: number;
|
maxConnections: number;
|
||||||
|
maxTotalConnections: number;
|
||||||
|
maxAuthAttempts: number;
|
||||||
|
handshakeTimeout: number;
|
||||||
|
maxDimension: number;
|
||||||
|
frameResolution: number;
|
||||||
brightnessThreshold: number;
|
brightnessThreshold: number;
|
||||||
|
charset: string;
|
||||||
|
invert: boolean;
|
||||||
logCredentials: boolean;
|
logCredentials: boolean;
|
||||||
|
videoPath?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseIntEnv(
|
||||||
|
value: string | undefined,
|
||||||
|
fallback: number,
|
||||||
|
{ min, max }: { min?: number; max?: number } = {}
|
||||||
|
): number {
|
||||||
|
if (value === undefined || value.trim() === "") return fallback;
|
||||||
|
const parsed = parseInt(value, 10);
|
||||||
|
if (!Number.isFinite(parsed)) return fallback;
|
||||||
|
let result = parsed;
|
||||||
|
if (typeof min === "number") result = Math.max(min, result);
|
||||||
|
if (typeof max === "number") result = Math.min(max, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseBoolEnv(value: string | undefined): boolean {
|
||||||
|
return value === "true";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadConfig(env: NodeJS.ProcessEnv = process.env): Config {
|
export function loadConfig(env: NodeJS.ProcessEnv = process.env): Config {
|
||||||
|
const videoPath = env.VIDEO_PATH?.trim();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
host: env.HOST ?? "0.0.0.0",
|
host: env.HOST ?? "0.0.0.0",
|
||||||
port: env.PORT ? parseInt(env.PORT, 10) : 22,
|
port: parseIntEnv(env.PORT, 22, { min: 1, max: 65535 }),
|
||||||
maxLoop: env.MAX_LOOP ? parseInt(env.MAX_LOOP, 10) : 5,
|
maxLoop: parseIntEnv(env.MAX_LOOP, 5, { min: 1 }),
|
||||||
loginDelay: env.LOGIN_DELAY ? parseInt(env.LOGIN_DELAY, 10) : 1500,
|
playbackMode:
|
||||||
maxConnections: env.MAX_CONNECTIONS
|
env.PLAYBACK_MODE?.trim().toLowerCase() === "random"
|
||||||
? parseInt(env.MAX_CONNECTIONS, 10)
|
? "random"
|
||||||
: 10,
|
: "loop",
|
||||||
brightnessThreshold: env.BRIGHTNESS_THRESHOLD
|
allowUserControl: env.ALLOW_USER_CONTROL !== "false",
|
||||||
? parseInt(env.BRIGHTNESS_THRESHOLD, 10)
|
switchDebounceMs: parseIntEnv(env.SWITCH_DEBOUNCE_MS, 120, { min: 0 }),
|
||||||
: 40,
|
loginDelay: parseIntEnv(env.LOGIN_DELAY, 1500, { min: 0 }),
|
||||||
logCredentials: env.LOG_CREDENTIALS === "true",
|
maxConnections: parseIntEnv(env.MAX_CONNECTIONS, 10, { min: 1 }),
|
||||||
|
maxTotalConnections: parseIntEnv(env.MAX_TOTAL_CONNECTIONS, 1000, {
|
||||||
|
min: 1,
|
||||||
|
}),
|
||||||
|
maxAuthAttempts: parseIntEnv(env.MAX_AUTH_ATTEMPTS, 6, { min: 1 }),
|
||||||
|
handshakeTimeout: parseIntEnv(env.HANDSHAKE_TIMEOUT, 10000, { min: 0 }),
|
||||||
|
maxDimension: parseIntEnv(env.MAX_DIMENSION, 512, {
|
||||||
|
min: 1,
|
||||||
|
max: 4096,
|
||||||
|
}),
|
||||||
|
frameResolution: parseIntEnv(env.FRAME_RESOLUTION, 360, {
|
||||||
|
min: 16,
|
||||||
|
max: 1080,
|
||||||
|
}),
|
||||||
|
brightnessThreshold: parseIntEnv(env.BRIGHTNESS_THRESHOLD, 40, {
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
}),
|
||||||
|
charset: env.CHARSET?.trim() || "detailed",
|
||||||
|
invert: parseBoolEnv(env.INVERT),
|
||||||
|
logCredentials: parseBoolEnv(env.LOG_CREDENTIALS),
|
||||||
|
videoPath: videoPath ? videoPath : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user