import fs from 'fs'; import https from 'https'; import { exec } from 'child_process'; import chokidar from 'chokidar'; import path from 'path'; import util from 'util'; import dotenv from 'dotenv'; // Load environment variables dotenv.config(); if (!process.env.ENVIRONMENT) { console.error("❌ ENVIRONMENT variable is missing."); process.exit(1); } // Determine environment and configuration file const nodeEnv = process.env.ENVIRONMENT || 'dev'; const configFile = `config_${nodeEnv}.json`; // Load configuration file let config; try { config = JSON.parse(fs.readFileSync(path.resolve('../', configFile), 'utf8')); } catch (error) { console.error(`❌ Failed to read configuration file '${configFile}': ${error.message}`); process.exit(1); } // Validate required configurations const requiredFields = [ 'OPENAPI_URL', 'SCHEMA_PATH', 'OUTPUT_DIRECTORY', 'SSL_KEY_PATH', 'SSL_CERT_PATH', ]; for (const field of requiredFields) { if (!config[field]) { console.error(`❌ Missing required configuration: ${field}`); process.exit(1); } } // Resolve paths from the config const OPENAPI_URL = config.OPENAPI_URL; const SCHEMA_PATH = path.resolve(config.SCHEMA_PATH); const OUTPUT_DIRECTORY = path.resolve(config.OUTPUT_DIRECTORY); const SSL_KEY_PATH = path.resolve(config.SSL_KEY_PATH); const SSL_CERT_PATH = path.resolve(config.SSL_CERT_PATH); // Log configuration console.log(`[INFO] Environment: ${nodeEnv}`); console.log(`[INFO] Using SCHEMA_PATH: ${SCHEMA_PATH}`); console.log(`[INFO] Using OUTPUT_DIRECTORY: ${OUTPUT_DIRECTORY}`); // Verify SSL files if (!fs.existsSync(SSL_KEY_PATH) || !fs.existsSync(SSL_CERT_PATH)) { console.error(`❌ SSL files not found: Key Path: ${SSL_KEY_PATH} (exists: ${fs.existsSync(SSL_KEY_PATH)}) Cert Path: ${SSL_CERT_PATH} (exists: ${fs.existsSync(SSL_CERT_PATH)})`); process.exit(1); } const execPromisified = util.promisify(exec); let isGenerating = false; const debounceDelay = 500; // Debounce interval in milliseconds // Debounce function to control rapid re-triggering function debounce(func, delay) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args); }, delay); }; } // Main function to fetch OpenAPI schema and generate services async function fetchAndGenerate() { if (isGenerating) { console.log("⚠️ Generation process is already running."); return; } isGenerating = true; try { // Fetch OpenAPI schema over HTTPS console.log("🚀 Fetching OpenAPI schema..."); const options = { rejectUnauthorized: false, key: fs.readFileSync(SSL_KEY_PATH), cert: fs.readFileSync(SSL_CERT_PATH), }; const res = await new Promise((resolve, reject) => { https.get(OPENAPI_URL, options, resolve).on('error', reject); }); let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', async () => { try { // Save schema file fs.writeFileSync(SCHEMA_PATH, data, 'utf8'); console.log(`✅ OpenAPI schema saved to ${SCHEMA_PATH}`); console.log("🧼 Cleaning output directory..."); await fs.promises.rm(OUTPUT_DIRECTORY, { recursive: true, force: true }); console.log(`✅ Output directory cleaned: ${OUTPUT_DIRECTORY}`); if (!fs.existsSync(OUTPUT_DIRECTORY)) { console.log(`✅ Confirmed removal of ${OUTPUT_DIRECTORY}`); } else { console.error(`❌ Failed to remove output directory: ${OUTPUT_DIRECTORY}`); } // Generate services const command = `npx openapi -i ${SCHEMA_PATH} -o ${OUTPUT_DIRECTORY}`; console.log(`🔧 Executing command: ${command}`); const { stdout, stderr } = await execPromisified(command); if (stderr) { console.error(`⚠️ stderr while generating services: ${stderr}`); } else { console.log(`✅ Service generation completed successfully:\n${stdout}`); } } catch (error) { console.error(`❌ Error during schema processing or generation: ${error.message}`); } isGenerating = false; }); } catch (error) { console.error(`❌ Failed to fetch OpenAPI schema: ${error.message}`); isGenerating = false; } } // Backend directory based on the environment // Backend directory based on the environment const backendDirectory = (() => { switch (nodeEnv) { case 'prod': return path.resolve('/home/jungfrau/heidi-v2/backend/app'); // Production path case 'test': return path.resolve('/home/jungfrau/heidi-v2/backend/app'); // Test path case 'dev': default: return path.resolve('/Users/gotthardg/PycharmProjects/heidi-v2/backend/app'); // Development path } })(); if (!fs.existsSync(backendDirectory)) { console.error(`❌ Backend directory does not exist: ${backendDirectory}`); process.exit(1); } console.log(`👀 Watching for changes in ${backendDirectory}`); // Watcher for change detection const watcher = chokidar.watch(backendDirectory, { persistent: true, ignored: [SCHEMA_PATH, OUTPUT_DIRECTORY], }); watcher .on('add', debounce(fetchAndGenerate, debounceDelay)) .on('change', debounce(fetchAndGenerate, debounceDelay)) .on('unlink', debounce(fetchAndGenerate, debounceDelay)); console.log(`👀 Watching for changes in ${backendDirectory}`);