| import 'dotenv/config'; |
|
|
| export const config = { |
| |
| supabaseUrl: process.env.SUPABASE_URL || '', |
| supabaseServiceRoleKey: process.env.SUPABASE_SERVICE_ROLE_KEY || '', |
|
|
| |
| workerId: process.env.WORKER_ID || `worker-${process.pid}`, |
| maxWorkers: parseInt(process.env.MAX_WORKERS || '2', 10), |
| pollIntervalMs: parseInt(process.env.POLL_INTERVAL_MS || '3000', 10), |
|
|
| |
| turnitinSharedPassword: process.env.TURNITIN_SHARED_PASSWORD || '', |
| credentialEncryptionKey: process.env.TURNITIN_CREDENTIAL_ENCRYPTION_KEY || '', |
| turnitinTargetUrl: |
| process.env.TARGET_URL || |
| 'https://www.turnitin.com/login_page.asp?lang=en_us', |
| turnitinClassTitle: process.env.CLASS_TITLE || 'Summer Reading 2026', |
| turnitinAssignmentTitle: process.env.ASSIGNMENT_TITLE || 'HTLLP notes', |
|
|
| |
| headless: process.env.PLAYWRIGHT_HEADLESS !== 'false', |
|
|
| |
| storageProvider: process.env.STORAGE_PROVIDER === 'r2' ? 'r2' : 'supabase', |
| inputBucket: process.env.INPUT_BUCKET || 'turnitin-inputs', |
| reportBucket: process.env.REPORT_BUCKET || 'turnitin-reports', |
| diagnosticsBucket: process.env.DIAGNOSTICS_BUCKET || 'turnitin-diagnostics', |
| sessionBucket: process.env.SESSION_BUCKET || 'turnitin-sessions', |
| |
| |
| supabaseInputBucket: |
| process.env.SUPABASE_INPUT_BUCKET || 'turnitin-inputs', |
| supabaseReportBucket: |
| process.env.SUPABASE_REPORT_BUCKET || 'turnitin-reports', |
| supabaseSessionBucket: |
| process.env.SUPABASE_SESSION_BUCKET || 'turnitin-sessions', |
| r2AccountId: process.env.R2_ACCOUNT_ID || '', |
| r2AccessKeyId: process.env.R2_ACCESS_KEY_ID || '', |
| r2SecretAccessKey: process.env.R2_SECRET_ACCESS_KEY || '', |
| r2Endpoint: process.env.R2_ENDPOINT || '', |
| r2Region: process.env.R2_REGION || 'auto', |
|
|
| |
| quotaCheckInterval: parseInt(process.env.QUOTA_CHECK_INTERVAL || '30', 10), |
| quotaCheckAccountDelayMs: parseInt( |
| process.env.QUOTA_CHECK_ACCOUNT_DELAY_MS || String(5 * 60 * 1000), |
| 10, |
| ), |
| quotaCheckOrder: process.env.QUOTA_CHECK_ORDER === 'newest' ? 'newest' : 'oldest', |
| cleanupInterval: parseInt(process.env.CLEANUP_INTERVAL || '15', 10), |
| staleRecoveryInterval: parseInt(process.env.STALE_RECOVERY_INTERVAL || '5', 10), |
|
|
| |
| reportRetentionHours: parseInt(process.env.REPORT_RETENTION_HOURS || '24', 10), |
|
|
| |
| port: parseInt(process.env.PORT || '7860', 10), |
| internalSecret: process.env.INTERNAL_SECRET || '', |
| allowedOrigins: (process.env.ALLOWED_ORIGINS || process.env.FRONTEND_ORIGIN || '') |
| .split(',') |
| .map((origin) => origin.trim()) |
| .filter(Boolean), |
| enableWorker: process.env.ENABLE_WORKER !== 'false', |
| enableCron: process.env.ENABLE_CRON !== 'false', |
|
|
| |
| similarityTimeoutMs: parseInt(process.env.SIMILARITY_TIMEOUT_MS || '180000', 10), |
| similarityPollMs: parseInt(process.env.SIMILARITY_POLL_MS || '5000', 10), |
| similarityRefreshAfterMs: parseInt(process.env.SIMILARITY_REFRESH_AFTER_MS || '90000', 10), |
| |
| |
| |
| leaseMinutes: parseInt(process.env.LEASE_MINUTES || '35', 10), |
| } as const; |
|
|
| |
| export function validateConfig(): void { |
| const required: (keyof typeof config)[] = [ |
| 'supabaseUrl', |
| 'supabaseServiceRoleKey', |
| 'turnitinSharedPassword', |
| ]; |
|
|
| const missing = required.filter((key) => !config[key]); |
| if (config.storageProvider === 'r2') { |
| const r2Required: (keyof typeof config)[] = [ |
| 'r2AccountId', |
| 'r2AccessKeyId', |
| 'r2SecretAccessKey', |
| 'r2Endpoint', |
| ]; |
| missing.push(...r2Required.filter((key) => !config[key])); |
| } |
| if (missing.length > 0) { |
| throw new Error(`Missing required environment variables: ${missing.join(', ')}`); |
| } |
| } |
|
|