Files
comfyui-serverless/frontend/src/config.ts
Debian 8f050b41a0
All checks were successful
Build and Push Frontend Docker Image / build (push) Successful in 57s
Build and Push Docker Image / build (push) Successful in 30m18s
Fix stuck processing jobs and increase timeouts
Background Job Processor:
- Add src/services/jobProcessor.ts that polls RunPod every 30s for stuck jobs
- Automatically completes or fails jobs that were abandoned (user navigated away)
- Times out jobs after 25 minutes

Client-Side Resume:
- Add GET /api/generate/pending endpoint to fetch user's processing jobs
- Add checkPendingJobs() that runs on login/page load
- Show notification banner when user has jobs generating in background
- Add "View Progress" button to resume polling for a job

Timeout Increases (10min → 25min):
- src/utils/validators.ts: request validation max/default
- src/config.ts: RUNPOD_MAX_TIMEOUT_MS default
- public/js/app.js: client-side polling maxTime
- src/services/jobProcessor.ts: background processor timeout

CI/CD Optimization:
- Add paths-ignore to backend build.yaml to skip rebuilds on frontend-only changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 05:36:53 +00:00

91 lines
2.6 KiB
TypeScript

import { existsSync, mkdirSync } from 'fs';
import { join } from 'path';
function requireEnv(name: string): string {
const value = process.env[name];
if (!value) {
throw new Error(`Missing required environment variable: ${name}`);
}
return value;
}
function optionalEnv(name: string, defaultValue: string): string {
return process.env[name] || defaultValue;
}
function optionalEnvInt(name: string, defaultValue: number): number {
const value = process.env[name];
if (!value) return defaultValue;
const parsed = parseInt(value, 10);
if (isNaN(parsed)) return defaultValue;
return parsed;
}
function optionalEnvBool(name: string, defaultValue: boolean): boolean {
const value = process.env[name];
if (!value) return defaultValue;
return value.toLowerCase() === 'true' || value === '1';
}
const dataDir = optionalEnv('DATA_DIR', './data');
// Ensure data directories exist
if (!existsSync(dataDir)) {
mkdirSync(dataDir, { recursive: true });
}
const contentDir = join(dataDir, 'content');
if (!existsSync(contentDir)) {
mkdirSync(contentDir, { recursive: true });
}
export const config = {
// Server
nodeEnv: optionalEnv('NODE_ENV', 'development'),
port: optionalEnvInt('PORT', 3000),
isProduction: optionalEnv('NODE_ENV', 'development') === 'production',
// Paths
dataDir,
contentDir,
dbPath: join(dataDir, 'app.db'),
// Session
sessionSecret: requireEnv('SESSION_SECRET'),
sessionMaxAge: optionalEnvInt('SESSION_MAX_AGE_HOURS', 24) * 60 * 60 * 1000,
// Initial Admin
adminUsername: optionalEnv('ADMIN_USERNAME', 'admin'),
adminPassword: optionalEnv('ADMIN_PASSWORD', ''),
adminEmail: optionalEnv('ADMIN_EMAIL', ''),
// RunPod
runpod: {
apiKey: requireEnv('RUNPOD_API_KEY'),
endpointId: requireEnv('RUNPOD_ENDPOINT_ID'),
baseUrl: 'https://api.runpod.ai/v2',
pollIntervalMs: optionalEnvInt('RUNPOD_POLL_INTERVAL_MS', 5000),
maxTimeoutMs: optionalEnvInt('RUNPOD_MAX_TIMEOUT_MS', 1500000),
},
// WebAuthn
webauthn: {
rpId: optionalEnv('WEBAUTHN_RP_ID', 'localhost'),
rpName: optionalEnv('WEBAUTHN_RP_NAME', 'ComfyUI Video Generator'),
origin: optionalEnv('WEBAUTHN_ORIGIN', 'http://localhost:3000'),
},
// Security
encryptionKey: requireEnv('ENCRYPTION_KEY'),
trustProxy: optionalEnvBool('TRUST_PROXY', true),
// Rate Limiting
rateLimit: {
windowMs: optionalEnvInt('RATE_LIMIT_WINDOW_MS', 60000),
maxRequests: optionalEnvInt('RATE_LIMIT_MAX_REQUESTS', 100),
},
loginRateLimit: {
windowMs: optionalEnvInt('LOGIN_RATE_LIMIT_WINDOW_MS', 900000),
maxRequests: optionalEnvInt('LOGIN_RATE_LIMIT_MAX', 5),
},
};