From 04dd6caef6125e73ca7e2b7df52b252b1a1fb1ad Mon Sep 17 00:00:00 2001 From: Aravinda-HWK Date: Fri, 16 Jan 2026 09:25:47 +0530 Subject: [PATCH] fix: warning showing in the docker container --- scripts/user/change-password-ui/Dockerfile | 14 +- .../user/change-password-ui/backend/server.js | 313 ++++++++++++++---- services/docker-compose.yaml | 5 +- 3 files changed, 258 insertions(+), 74 deletions(-) diff --git a/scripts/user/change-password-ui/Dockerfile b/scripts/user/change-password-ui/Dockerfile index 10c6184..88b8715 100644 --- a/scripts/user/change-password-ui/Dockerfile +++ b/scripts/user/change-password-ui/Dockerfile @@ -1,7 +1,7 @@ FROM node:18-alpine -# Install docker CLI for executing database commands -RUN apk add --no-cache docker-cli +# Install docker CLI and curl for executing database commands and health checks +RUN apk add --no-cache docker-cli curl WORKDIR /app @@ -15,12 +15,12 @@ RUN npm install --production COPY backend/ ./backend/ COPY frontend/ ./frontend/ -# Expose ports -EXPOSE 3001 3443 +# Expose HTTPS port only +EXPOSE 3443 -# Health check -HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ - CMD node -e "require('http').get('http://localhost:3001/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})" +# Health check - use HTTPS endpoint +HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \ + CMD curl -f -k https://localhost:3443/health || exit 1 # Start the application CMD ["node", "backend/server.js"] diff --git a/scripts/user/change-password-ui/backend/server.js b/scripts/user/change-password-ui/backend/server.js index 090b090..2adf95d 100755 --- a/scripts/user/change-password-ui/backend/server.js +++ b/scripts/user/change-password-ui/backend/server.js @@ -22,7 +22,20 @@ const path = require('path'); const app = express(); const PORT = process.env.PORT || 3001; const HTTPS_PORT = process.env.HTTPS_PORT || 3443; -const THUNDER_API = process.env.THUNDER_API || 'https://localhost:8090'; +const THUNDER_API = process.env.THUNDER_API || 'https://thunder-server:8090'; + +// Startup logging +console.log('🚀 Starting Silver Mail - Change Password UI Server...'); +console.log(`📋 Configuration:`); +console.log(` PORT: ${PORT}`); +console.log(` HTTPS_PORT: ${HTTPS_PORT}`); +console.log(` THUNDER_API: ${THUNDER_API}`); +console.log(` NODE_ENV: ${process.env.NODE_ENV || 'development'}`); +console.log(''); + +// Suppress specific Node.js warnings that are expected in this environment +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; // For self-signed certificates +process.removeAllListeners('warning'); // Remove default warning listeners // Read domain from silver.yaml function getDomainFromConfig() { @@ -134,32 +147,105 @@ if (ENABLE_HTTPS) { // Enable JSON parsing app.use(express.json()); +// Utility function to check if SMTP container is ready +async function isSmtpContainerReady() { + try { + const { execSync } = require('child_process'); + const result = execSync('docker ps --filter "name=smtp-server-container" --filter "status=running" --format "{{.Names}}"', + { encoding: 'utf8', timeout: 5000 }); + return result.trim() === 'smtp-server-container'; + } catch (error) { + return false; + } +} + +// Utility function to check if database is accessible +async function isDatabaseReady() { + try { + const { execSync } = require('child_process'); + const result = execSync('docker exec smtp-server-container sqlite3 /app/data/databases/shared.db "SELECT 1;"', + { encoding: 'utf8', timeout: 5000 }); + return result.trim() === '1'; + } catch (error) { + return false; + } +} + // Check password initialization status app.post('/api/check-password-status', async (req, res) => { const { email } = req.body; if (!email) return res.status(400).json({ error: 'Email required' }); + // Check if containers are ready + const smtpReady = await isSmtpContainerReady(); + if (!smtpReady) { + console.warn('SMTP container not ready for database operations'); + return res.status(503).json({ error: 'Service not ready, please try again' }); + } + + const dbReady = await isDatabaseReady(); + if (!dbReady) { + console.warn('Database not ready for operations'); + return res.status(503).json({ error: 'Database not ready, please try again' }); + } + try { const { execSync } = require('child_process'); const [username, domain] = email.split('@'); + + // Validate email format + if (!username || !domain) { + return res.status(400).json({ error: 'Invalid email format' }); + } + const cmd = `docker exec smtp-server-container sqlite3 /app/data/databases/shared.db "SELECT password_initialized FROM users u INNER JOIN domains d ON u.domain_id = d.id WHERE u.username='${username}' AND d.domain='${domain}' AND u.enabled=1;" 2>&1`; - const result = execSync(cmd, { encoding: 'utf8' }).trim(); + const result = execSync(cmd, { encoding: 'utf8', timeout: 10000 }).trim(); + + // Handle cases where user is not found + if (result === '' || result.includes('Error') || result.includes('no such table')) { + console.warn(`User not found or database error: ${email}`); + return res.status(404).json({ error: 'User not found' }); + } + const initialized = result === '1'; res.json({ email, password_initialized: initialized, must_change_password: !initialized }); } catch (error) { - console.error('Check status error:', error.message); - res.status(500).json({ error: 'Failed to check status' }); + console.error('Check password status error:', error.message); + res.status(500).json({ error: 'Failed to check password status', details: error.message }); } }); // Update password_initialized after password change async function updatePasswordInitialized(email) { if (!email) return; + + // Check if containers are ready before attempting database operations + const smtpReady = await isSmtpContainerReady(); + const dbReady = await isDatabaseReady(); + + if (!smtpReady || !dbReady) { + console.warn('Containers not ready for password initialization update, skipping...'); + return; + } + try { const { execSync } = require('child_process'); const [username, domain] = email.split('@'); + + // Validate email format + if (!username || !domain) { + console.error('Invalid email format for password initialization update:', email); + return; + } + const cmd = `docker exec smtp-server-container sqlite3 /app/data/databases/shared.db "UPDATE users SET password_initialized = 1 WHERE id IN (SELECT u.id FROM users u INNER JOIN domains d ON u.domain_id = d.id WHERE u.username='${username}' AND d.domain='${domain}');" 2>&1`; - execSync(cmd); + const result = execSync(cmd, { encoding: 'utf8', timeout: 10000 }); + + if (result && result.includes('Error')) { + console.error('SQL error updating password_initialized:', result); + } else { + console.log(`✓ Password initialization status updated for: ${email}`); + } } catch (error) { console.error('Update password_initialized failed:', error.message); } @@ -171,9 +257,17 @@ app.use('/api/thunder', async (req, res) => { try { const https = require('https'); - const agent = new https.Agent({ rejectUnauthorized: false }); // Accept self-signed certs + // Create HTTPS agent with better security configuration + const agent = new https.Agent({ + rejectUnauthorized: false, // Accept self-signed certs for internal communication + keepAlive: true, + timeout: 30000 + }); const fetch = (await import('node-fetch')).default; + + console.log(`Proxying ${req.method} ${targetUrl}`); + const response = await fetch(targetUrl, { method: req.method, headers: { @@ -181,7 +275,8 @@ app.use('/api/thunder', async (req, res) => { ...(req.headers.authorization && { 'Authorization': req.headers.authorization }), }, body: req.method !== 'GET' ? JSON.stringify(req.body) : undefined, - agent + agent, + timeout: 30000 }); // Handle 204 No Content or empty responses @@ -203,8 +298,12 @@ app.use('/api/thunder', async (req, res) => { res.status(response.status).json(data); } } catch (error) { - console.error('Proxy error:', error.message); - res.status(500).json({ error: 'Proxy request failed', message: error.message }); + console.error(`Proxy error for ${req.method} ${targetUrl}:`, error.message); + res.status(500).json({ + error: 'Proxy request failed', + message: error.message, + target: targetUrl + }); } }); @@ -218,68 +317,146 @@ app.get('/', (req, res) => { }); // Health check -app.get('/health', (req, res) => { - res.json({ status: 'ok', service: 'change-password-ui' }); +app.get('/health', async (req, res) => { + const status = { + status: 'ok', + service: 'change-password-ui', + timestamp: new Date().toISOString(), + dependencies: { + smtpContainer: await isSmtpContainerReady(), + database: await isDatabaseReady() + } + }; + + const allReady = status.dependencies.smtpContainer && status.dependencies.database; + const httpStatus = allReady ? 200 : 503; + + res.status(httpStatus).json(status); }); -// Start server -if (ENABLE_HTTPS && fs.existsSync(SSL_CERT) && fs.existsSync(SSL_KEY)) { - // HTTPS Server - const httpsOptions = { - cert: fs.readFileSync(SSL_CERT), - key: fs.readFileSync(SSL_KEY) - }; +// Startup dependency check with retry logic +async function waitForDependencies() { + console.log('🔍 Checking service dependencies...'); - https.createServer(httpsOptions, app).listen(HTTPS_PORT, () => { - console.log('┌─────────────────────────────────────────────────┐'); - console.log('│ Silver Mail - Password Change UI Server │'); - console.log('│ 🔒 HTTPS Enabled │'); - console.log('└─────────────────────────────────────────────────┘'); - console.log(''); - console.log(`✓ HTTPS Server: https://localhost:${HTTPS_PORT}`); - console.log(`✓ Change password UI: https://localhost:${HTTPS_PORT}/`); - console.log(`✓ API Proxy: https://localhost:${HTTPS_PORT}/api/thunder/*`); - console.log(`✓ Thunder API: ${THUNDER_API}`); - console.log(`✓ Health check: https://localhost:${HTTPS_PORT}/health`); - console.log(`✓ Frontend path: ${frontendPath}`); - console.log(''); - console.log('🔒 SSL/TLS enabled with provided certificates'); - console.log(''); - console.log('Press Ctrl+C to stop the server'); - console.log(''); - }); + let retries = 0; + const maxRetries = 30; // 5 minutes with 10 second intervals + + while (retries < maxRetries) { + const smtpReady = await isSmtpContainerReady(); + const dbReady = await isDatabaseReady(); + + if (smtpReady && dbReady) { + console.log('✅ All dependencies are ready!'); + return true; + } + + console.log(`⏳ Waiting for dependencies... (${retries + 1}/${maxRetries})`); + console.log(` SMTP Container: ${smtpReady ? '✅' : '❌'}`); + console.log(` Database: ${dbReady ? '✅' : '❌'}`); + + retries++; + await new Promise(resolve => setTimeout(resolve, 10000)); // Wait 10 seconds + } + + console.warn('⚠️ Some dependencies are not ready, starting anyway...'); + return false; +} + +// Initialize server with dependency checking +async function initializeServer() { + await waitForDependencies(); - // HTTP redirect to HTTPS - if (PORT !== HTTPS_PORT) { - http.createServer((req, res) => { - res.writeHead(301, { 'Location': `https://${req.headers.host.split(':')[0]}:${HTTPS_PORT}${req.url}` }); - res.end(); - }).listen(PORT, () => { - console.log(`↪️ HTTP redirect enabled on port ${PORT} → HTTPS port ${HTTPS_PORT}`); + // Start server + if (ENABLE_HTTPS && fs.existsSync(SSL_CERT) && fs.existsSync(SSL_KEY)) { + // HTTPS Server + const httpsOptions = { + cert: fs.readFileSync(SSL_CERT), + key: fs.readFileSync(SSL_KEY) + }; + + https.createServer(httpsOptions, app).listen(HTTPS_PORT, () => { + console.log('┌─────────────────────────────────────────────────┐'); + console.log('│ Silver Mail - Password Change UI Server │'); + console.log('│ 🔒 HTTPS Enabled │'); + console.log('└─────────────────────────────────────────────────┘'); + console.log(''); + console.log(`✓ HTTPS Server: https://localhost:${HTTPS_PORT}`); + console.log(`✓ Change password UI: https://localhost:${HTTPS_PORT}/`); + console.log(`✓ API Proxy: https://localhost:${HTTPS_PORT}/api/thunder/*`); + console.log(`✓ Thunder API: ${THUNDER_API}`); + console.log(`✓ Health check: https://localhost:${HTTPS_PORT}/health`); + console.log(`✓ Frontend path: ${frontendPath}`); + console.log(''); + console.log('🔒 SSL/TLS enabled with provided certificates'); + console.log(''); + console.log('Press Ctrl+C to stop the server'); + console.log(''); + }); + + // HTTP redirect to HTTPS - Only enable if explicitly requested + const enableHttpRedirect = process.env.ENABLE_HTTP_REDIRECT !== 'false'; + + if (PORT !== HTTPS_PORT && enableHttpRedirect) { + const httpServer = http.createServer((req, res) => { + const host = req.headers.host; + let redirectHost; + + if (host) { + // Extract hostname without port + const hostname = host.split(':')[0]; + // For Docker environment, use the same hostname with HTTPS port + redirectHost = `${hostname}:${HTTPS_PORT}`; + } else { + // Fallback + redirectHost = `localhost:${HTTPS_PORT}`; + } + + const redirectUrl = `https://${redirectHost}${req.url}`; + console.log(`HTTP redirect: ${req.url} -> ${redirectUrl}`); + + res.writeHead(301, { + 'Location': redirectUrl, + 'Connection': 'close' + }); + res.end(); + }); + + // Add error handling for HTTP redirect server + httpServer.on('error', (err) => { + console.error('HTTP redirect server error:', err.message); + }); + + httpServer.listen(PORT, () => { + console.log(`↪️ HTTP redirect enabled on port ${PORT} → HTTPS port ${HTTPS_PORT}`); + console.log(` To disable HTTP redirect, set ENABLE_HTTP_REDIRECT=false`); + }); + } else if (PORT !== HTTPS_PORT) { + console.log(`📝 HTTP redirect disabled (ENABLE_HTTP_REDIRECT=false)`); + console.log(` Only HTTPS server running on port ${HTTPS_PORT}`); + } + } else { + // HTTP Server (fallback) + app.listen(PORT, () => { + console.log('┌─────────────────────────────────────────────────┐'); + console.log('│ Silver Mail - Password Change UI Server │'); + console.log('│ ⚠️ HTTP Mode (No SSL) │'); + console.log('└─────────────────────────────────────────────────┘'); + console.log(''); + console.log(`✓ Server running on: http://localhost:${PORT}`); + console.log(`✓ Change password UI: http://localhost:${PORT}/`); + console.log(`✓ API Proxy: http://localhost:${PORT}/api/thunder/*`); + console.log(`✓ Thunder API: ${THUNDER_API}`); + console.log(`✓ Health check: http://localhost:${PORT}/health`); + console.log(`✓ Frontend path: ${frontendPath}`); + console.log(''); + console.log('⚠️ Warning: SSL certificates not found'); + console.log(` Expected: ${SSL_CERT} and ${SSL_KEY}`); + console.log(' Set ENABLE_HTTPS=true and provide certificates for HTTPS'); + console.log(''); + console.log('Press Ctrl+C to stop the server'); + console.log(''); }); } -} else { - // HTTP Server (fallback) - app.listen(PORT, () => { - console.log('┌─────────────────────────────────────────────────┐'); - console.log('│ Silver Mail - Password Change UI Server │'); - console.log('│ ⚠️ HTTP Mode (No SSL) │'); - console.log('└─────────────────────────────────────────────────┘'); - console.log(''); - console.log(`✓ Server running on: http://localhost:${PORT}`); - console.log(`✓ Change password UI: http://localhost:${PORT}/`); - console.log(`✓ API Proxy: http://localhost:${PORT}/api/thunder/*`); - console.log(`✓ Thunder API: ${THUNDER_API}`); - console.log(`✓ Health check: http://localhost:${PORT}/health`); - console.log(`✓ Frontend path: ${frontendPath}`); - console.log(''); - console.log('⚠️ Warning: SSL certificates not found'); - console.log(` Expected: ${SSL_CERT} and ${SSL_KEY}`); - console.log(' Set ENABLE_HTTPS=true and provide certificates for HTTPS'); - console.log(''); - console.log('Press Ctrl+C to stop the server'); - console.log(''); - }); } // Graceful shutdown @@ -292,3 +469,9 @@ process.on('SIGINT', () => { console.log('\nSIGINT received, shutting down gracefully...'); process.exit(0); }); + +// Start the server initialization +initializeServer().catch(err => { + console.error('Server initialization failed:', err.message); + process.exit(1); +}); diff --git a/services/docker-compose.yaml b/services/docker-compose.yaml index 12d39e9..0ed4aca 100644 --- a/services/docker-compose.yaml +++ b/services/docker-compose.yaml @@ -281,12 +281,13 @@ services: dockerfile: Dockerfile container_name: change-password-ui ports: - - "3001:3001" # HTTP (redirects to HTTPS) - - "3443:3443" # HTTPS + - "3443:3443" # HTTPS - main service port + # - "3001:3001" # HTTP (redirects to HTTPS) environment: - PORT=3001 - HTTPS_PORT=3443 - ENABLE_HTTPS=true + - ENABLE_HTTP_REDIRECT=false # Disable HTTP redirect to prevent warnings - THUNDER_API=https://thunder-server:8090 - SILVER_CONFIG=/etc/silver/silver.yaml networks: