UNPKG

nostr-deploy-server

Version:

Node.js server for hosting static websites under npub subdomains using Nostr protocol and Blossom servers

231 lines 11.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConfigManager = void 0; const dotenv = __importStar(require("dotenv")); // @ts-ignore - xbytes doesn't have TypeScript declarations const xbytes_1 = __importDefault(require("xbytes")); // Load environment variables dotenv.config(); class ConfigManager { constructor() { this.config = { port: parseInt(process.env.PORT || '3000', 10), baseDomain: process.env.BASE_DOMAIN || '', defaultRelays: this.parseCommaSeparated(process.env.DEFAULT_RELAYS || 'wss://relay.nostr.band,wss://nostrue.com,wss://purplerelay.com,wss://relay.primal.net,wss://nos.lol,wss://relay.damus.io,wss://relay.nsite.lol'), defaultBlossomServers: this.parseCommaSeparated(process.env.DEFAULT_BLOSSOM_SERVERS || 'https://cdn.hzrd149.com,https://blossom.primal.net,https://blossom.band,https://loratu.bitcointxoko.com,https://blossom.f7z.io,https://cdn.sovbit.host'), cacheTtlSeconds: parseInt(process.env.CACHE_TTL_SECONDS || '300', 10), maxCacheSize: parseInt(process.env.MAX_CACHE_SIZE || '100', 10), rateLimitWindowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS || '60000', 10), rateLimitMaxRequests: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS || '100', 10), logLevel: process.env.LOG_LEVEL || 'info', corsOrigin: process.env.CORS_ORIGIN || '*', trustProxy: process.env.TRUST_PROXY === 'true', requestTimeoutMs: parseInt(process.env.REQUEST_TIMEOUT_MS || '30000', 10), maxFileSizeMB: parseInt(process.env.MAX_FILE_SIZE_MB || '50', 10), // SSR Configuration ssrEnabled: process.env.SSR_ENABLED === 'true', // Default is false ssrTimeoutMs: parseInt(process.env.SSR_TIMEOUT_MS || '60000', 10), // Increased to 60 seconds ssrCacheTtlSeconds: parseInt(process.env.SSR_CACHE_TTL_SECONDS || '1800', 10), // 30 minutes ssrViewportWidth: parseInt(process.env.SSR_VIEWPORT_WIDTH || '1920', 10), ssrViewportHeight: parseInt(process.env.SSR_VIEWPORT_HEIGHT || '1080', 10), ssrMaxConcurrentPages: parseInt(process.env.SSR_MAX_CONCURRENT_PAGES || '3', 10), // WebSocket Connection Pooling Configuration wsConnectionTimeoutMs: parseInt(process.env.WS_CONNECTION_TIMEOUT_MS || '3600000', 10), // 1 hour default wsCleanupIntervalMs: parseInt(process.env.WS_CLEANUP_INTERVAL_MS || '300000', 10), // 5 minutes default // Cache TTL Configuration negativeCacheTtlMs: parseInt(process.env.NEGATIVE_CACHE_TTL_MS || '10000', 10), // 10 seconds default positiveCacheTtlMs: parseInt(process.env.POSITIVE_CACHE_TTL_MS || '300000', 10), // 5 minutes default fileContentCacheTtlMs: parseInt(process.env.FILE_CONTENT_CACHE_TTL_MS || '1800000', 10), // 30 minutes default errorCacheTtlMs: parseInt(process.env.ERROR_CACHE_TTL_MS || '60000', 10), // 1 minute default // Query Timeout Configuration relayQueryTimeoutMs: parseInt(process.env.RELAY_QUERY_TIMEOUT_MS || '3000', 10), // Reduced from 10s to 3s for faster responses // Advanced Cache Configuration cachePath: process.env.CACHE_PATH, cacheTime: parseInt(process.env.CACHE_TIME || '3600', 10), maxFileSize: process.env.MAX_FILE_SIZE ? xbytes_1.default.parseSize(process.env.MAX_FILE_SIZE) : Infinity, // Real-time Cache Invalidation Configuration realtimeCacheInvalidation: process.env.REALTIME_CACHE_INVALIDATION === 'true', invalidationRelays: this.parseCommaSeparated(process.env.INVALIDATION_RELAYS || 'wss://relay.primal.net,wss://relay.damus.io,wss://relay.nostr.band' // Use fast, reliable relays ), invalidationTimeoutMs: parseInt(process.env.INVALIDATION_TIMEOUT_MS || '30000', 10), // 30 seconds invalidationReconnectDelayMs: parseInt(process.env.INVALIDATION_RECONNECT_DELAY_MS || '5000', 10), // 5 seconds // Sliding Expiration Configuration slidingExpiration: process.env.SLIDING_EXPIRATION === 'true', // Default is false for backward compatibility }; this.validateConfig(); } static getInstance() { if (!ConfigManager.instance) { ConfigManager.instance = new ConfigManager(); } return ConfigManager.instance; } getConfig() { return { ...this.config }; } updateConfig(updates) { this.config = { ...this.config, ...updates }; this.validateConfig(); } parseCommaSeparated(value) { return value .split(',') .map((item) => item.trim()) .filter((item) => item.length > 0); } validateConfig() { const { config } = this; if (config.port < 1 || config.port > 65535) { throw new Error(`Invalid port: ${config.port}. Must be between 1-65535`); } if (!config.baseDomain) { throw new Error('BASE_DOMAIN is required'); } if (config.defaultRelays.length === 0) { throw new Error('At least one default relay is required'); } if (config.defaultBlossomServers.length === 0) { throw new Error('At least one default Blossom server is required'); } // Validate relay URLs config.defaultRelays.forEach((relay) => { if (!relay.startsWith('wss://') && !relay.startsWith('ws://')) { throw new Error(`Invalid relay URL: ${relay}. Must start with ws:// or wss://`); } }); // Validate Blossom server URLs config.defaultBlossomServers.forEach((server) => { if (!server.startsWith('https://') && !server.startsWith('http://')) { throw new Error(`Invalid Blossom server URL: ${server}. Must start with http:// or https://`); } }); if (config.cacheTtlSeconds < 0) { throw new Error('Cache TTL cannot be negative'); } if (config.maxCacheSize < 1) { throw new Error('Max cache size must be at least 1'); } if (config.rateLimitWindowMs < 1000) { throw new Error('Rate limit window must be at least 1000ms'); } if (config.rateLimitMaxRequests < 1) { throw new Error('Rate limit max requests must be at least 1'); } if (config.requestTimeoutMs < 1000) { throw new Error('Request timeout must be at least 1000ms'); } if (config.maxFileSizeMB < 1) { throw new Error('Max file size must be at least 1MB'); } // SSR validation if (config.ssrTimeoutMs < 1000) { throw new Error('SSR timeout must be at least 1000ms'); } if (config.ssrCacheTtlSeconds < 0) { throw new Error('SSR cache TTL cannot be negative'); } if (config.ssrViewportWidth < 320 || config.ssrViewportWidth > 3840) { throw new Error('SSR viewport width must be between 320-3840 pixels'); } if (config.ssrViewportHeight < 240 || config.ssrViewportHeight > 2160) { throw new Error('SSR viewport height must be between 240-2160 pixels'); } if (config.ssrMaxConcurrentPages < 1 || config.ssrMaxConcurrentPages > 10) { throw new Error('SSR max concurrent pages must be between 1-10'); } // WebSocket Connection Pooling Configuration if (config.wsConnectionTimeoutMs < 1000) { throw new Error('WS connection timeout must be at least 1000ms'); } if (config.wsCleanupIntervalMs < 1000) { throw new Error('WS cleanup interval must be at least 1000ms'); } // Cache TTL Configuration if (config.negativeCacheTtlMs < 0) { throw new Error('Negative cache TTL cannot be negative'); } if (config.positiveCacheTtlMs < 1000) { throw new Error('Positive cache TTL must be at least 1000ms'); } if (config.fileContentCacheTtlMs < 1000) { throw new Error('File content cache TTL must be at least 1000ms'); } if (config.errorCacheTtlMs < 0) { throw new Error('Error cache TTL cannot be negative'); } // Query Timeout Configuration if (config.relayQueryTimeoutMs < 1000) { throw new Error('Relay query timeout must be at least 1000ms'); } // Real-time Cache Invalidation Configuration if (config.realtimeCacheInvalidation) { if (config.invalidationRelays.length === 0) { throw new Error('At least one invalidation relay is required when real-time cache invalidation is enabled'); } // Validate invalidation relay URLs config.invalidationRelays.forEach((relay) => { if (!relay.startsWith('wss://') && !relay.startsWith('ws://')) { throw new Error(`Invalid invalidation relay URL: ${relay}. Must start with ws:// or wss://`); } }); if (config.invalidationTimeoutMs < 5000) { throw new Error('Invalidation timeout must be at least 5000ms'); } if (config.invalidationReconnectDelayMs < 1000) { throw new Error('Invalidation reconnect delay must be at least 1000ms'); } } } isProduction() { return process.env.NODE_ENV === 'production'; } isDevelopment() { return process.env.NODE_ENV === 'development'; } isTest() { return process.env.NODE_ENV === 'test'; } } exports.ConfigManager = ConfigManager; //# sourceMappingURL=config.js.map