113 lines
4.0 KiB
TypeScript
113 lines
4.0 KiB
TypeScript
import os from 'node:os';
|
|
import path from 'node:path';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
function envString(key: string, fallback: string): string {
|
|
return process.env[key] ?? fallback;
|
|
}
|
|
|
|
function envInt(key: string, fallback: number): number {
|
|
const raw = process.env[key];
|
|
if (raw === undefined) return fallback;
|
|
const parsed = Number.parseInt(raw, 10);
|
|
if (Number.isNaN(parsed)) {
|
|
// eslint-disable-next-line no-console
|
|
console.error(`[config] Invalid integer for ${key}="${raw}", using default ${fallback}`);
|
|
return fallback;
|
|
}
|
|
return parsed;
|
|
}
|
|
|
|
function envBool(key: string, fallback: boolean): boolean {
|
|
const raw = process.env[key];
|
|
if (raw === undefined) return fallback;
|
|
// Accept common truthy / falsy strings
|
|
if (['true', '1', 'yes'].includes(raw.toLowerCase())) return true;
|
|
if (['false', '0', 'no'].includes(raw.toLowerCase())) return false;
|
|
return fallback;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// HOST safety check — must run before exporting config
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const host = envString('HOST', '127.0.0.1');
|
|
|
|
if (host === '0.0.0.0' || host === '::') {
|
|
const allow = process.env['ALLOW_REMOTE'];
|
|
if (allow !== 'yes-i-understand-the-risk') {
|
|
// Use console.error directly — the logger module depends on config,
|
|
// so it is not available yet at this point.
|
|
// eslint-disable-next-line no-console
|
|
console.error(
|
|
`[FATAL] HOST is set to "${host}" which exposes the service to the network.\n` +
|
|
`If you really intend to do this, set ALLOW_REMOTE=yes-i-understand-the-risk\n` +
|
|
`Refusing to start.`,
|
|
);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Operation timeouts (milliseconds)
|
|
// Matches the tiers described in PLAN.md section 6.1
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const operationTimeouts: Record<string, number> = {
|
|
like: 15_000, // 15s — quick interactions
|
|
favorite: 15_000, // 15s
|
|
comment: 20_000, // 20s
|
|
reply: 20_000, // 20s
|
|
feed_list: 30_000, // 30s — page load + extraction
|
|
search: 30_000, // 30s
|
|
feed_detail: 60_000, // 60s — includes scroll loading
|
|
user_profile: 60_000, // 60s
|
|
publish: 300_000, // 5min — upload may be slow
|
|
login: 300_000, // 5min — user interaction
|
|
default: 60_000, // 1min — fallback
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Config type
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface AppConfig {
|
|
/** HTTP port */
|
|
port: number;
|
|
/** HTTP bind address */
|
|
host: string;
|
|
/** Run browser in headless mode */
|
|
headless: boolean;
|
|
/** Custom browser executable path (optional) */
|
|
browserBin: string | undefined;
|
|
/** Pino log level */
|
|
logLevel: string;
|
|
/** NODE_ENV */
|
|
nodeEnv: string;
|
|
/** Directory for per-platform cookie storage */
|
|
cookieDir: string;
|
|
/** Max pending operations per platform queue */
|
|
maxQueueDepth: number;
|
|
/** Per-operation-type timeout in ms */
|
|
operationTimeouts: Record<string, number>;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Exported config singleton
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const config: AppConfig = {
|
|
port: envInt('PORT', 9527),
|
|
host,
|
|
headless: envBool('HEADLESS', true),
|
|
browserBin: process.env['BROWSER_BIN'] || undefined,
|
|
logLevel: envString('LOG_LEVEL', 'info'),
|
|
nodeEnv: envString('NODE_ENV', 'development'),
|
|
cookieDir: envString('COOKIE_DIR', path.join(os.homedir(), '.social-mcp')),
|
|
maxQueueDepth: envInt('MAX_QUEUE_DEPTH', 10),
|
|
operationTimeouts,
|
|
};
|