import { readFileSync, writeFileSync, existsSync } from 'fs';
import { resolve } from 'path';
import crypto from 'crypto';
export interface ServerConfig {
transport: 'stdio' | 'https';
port: number;
debug: boolean;
production: boolean;
httpsOptions: {
keyPath: string;
certPath: string;
};
}
export interface AuthConfig {
tokenExpiration: number;
issuer: string;
encryptionKey: string;
rsaKeyPath: string;
allowedRedirectUris?: string[];
}
export interface CloudflareConfig {
enabled: boolean;
autoDetectUrl: boolean;
tunnelCommand: string;
currentTunnelUrl?: string;
}
export interface UmbrellaConfig {
baseUrl: string;
frontendBaseUrl?: string;
}
export interface McpConfig {
serverName: string;
serverVersion: string;
}
export interface AppConfig {
server: ServerConfig;
auth: AuthConfig;
cloudflare: CloudflareConfig;
umbrella: UmbrellaConfig;
mcp: McpConfig;
}
export class ConfigLoader {
private static instance: ConfigLoader;
private config!: AppConfig;
private configPath: string;
private constructor() {
this.configPath = resolve(process.cwd(), 'config.json');
this.loadConfig();
}
static getInstance(): ConfigLoader {
if (!ConfigLoader.instance) {
ConfigLoader.instance = new ConfigLoader();
}
return ConfigLoader.instance;
}
private loadConfig(): void {
if (!existsSync(this.configPath)) {
throw new Error(`Configuration file not found at ${this.configPath}`);
}
try {
const configContent = readFileSync(this.configPath, 'utf-8');
this.config = JSON.parse(configContent);
// Generate encryption key if not present
if (!this.config.auth.encryptionKey) {
this.config.auth.encryptionKey = crypto.randomBytes(32).toString('base64');
this.saveConfig();
}
} catch (error) {
throw new Error(`Failed to load configuration: ${error}`);
}
}
getConfig(): AppConfig {
return this.config;
}
updateTunnelUrl(url: string): void {
this.config.cloudflare.currentTunnelUrl = url;
this.config.auth.issuer = url.endsWith('/') ? url : `${url}/`;
this.saveConfig();
if (this.config.server.debug) {
console.log(`[CONFIG] Updated tunnel URL to: ${url}`);
console.log(`[CONFIG] Updated issuer to: ${this.config.auth.issuer}`);
}
}
private saveConfig(): void {
try {
writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));
} catch (error) {
console.error(`Failed to save configuration: ${error}`);
}
}
isDebugMode(): boolean {
return this.config.server.debug;
}
isProduction(): boolean {
return this.config.server.production;
}
getTransportMode(): 'stdio' | 'https' {
return this.config.server.transport;
}
}