'use strict'; require('dotenv').config(); const express = require('express'); const cookieParser = require('cookie-parser'); const fetch = require('node-fetch'); const path = require('path'); const app = express(); // ─── Configuration ──────────────────────────────────────────────────────────── // PUBLIC_BASE – the public-facing prefix behind which this app is mounted // e.g. if Nginx proxies /face_match → this app, set PUBLIC_BASE=/face_match // JAVA_APP_INTERNAL_URL – internal (loopback) URL to reach the Tomcat app // e.g. http://localhost:8080 // LOGIN_URL – where to redirect unauthenticated users (public URL of the login) const PUBLIC_BASE = (process.env.PUBLIC_BASE || '/face_match').replace(/\/$/, ''); const JAVA_INTERNAL_URL = (process.env.JAVA_APP_INTERNAL_URL || 'http://localhost:8080').replace(/\/$/, ''); const LOGIN_URL = process.env.LOGIN_URL || 'https://www.regalamiunsorriso.it/admin/menu/Menu4.abl'; const PORT = process.env.PORT || 3001; // ─── Middleware ─────────────────────────────────────────────────────────────── app.set('view engine', 'ejs'); app.set('views', path.join(__dirname, 'views')); app.use(cookieParser()); // Strip the public base prefix so routes work both mounted and standalone app.use((req, _res, next) => { if (PUBLIC_BASE && req.path.startsWith(PUBLIC_BASE)) { req.url = req.url.slice(PUBLIC_BASE.length) || '/'; } next(); }); // ─── Session validation helper ──────────────────────────────────────────────── /** * Calls the lightweight JSP endpoint on the main Java app, forwarding the * browser's JSESSIONID cookie. Returns { authenticated, userId } or throws. * * The Java app stores the session server-side; both apps are on the same host * so the JSESSIONID issued by Tomcat is forwarded here by the browser. */ async function checkJavaSession(jsessionId) { if (!jsessionId) return { authenticated: false }; const url = `${JAVA_INTERNAL_URL}/admin/pg/checkSession.jsp`; const response = await fetch(url, { method: 'GET', headers: { Cookie: `JSESSIONID=${jsessionId}`, }, // Don't follow redirects – a redirect usually means the session expired redirect: 'manual', }); if (response.status === 401 || response.status >= 300) { return { authenticated: false }; } try { return await response.json(); // { authenticated, userId } } catch { return { authenticated: false }; } } // ─── Auth middleware ────────────────────────────────────────────────────────── async function requireAuth(req, res, next) { const jsessionId = req.cookies['JSESSIONID']; let sessionInfo; try { sessionInfo = await checkJavaSession(jsessionId); } catch (err) { console.error('Session validation request failed:', err.message); sessionInfo = { authenticated: false }; } if (!sessionInfo.authenticated) { // Redirect to the main app login, passing the desired return URL const returnUrl = encodeURIComponent(`https://www.regalamiunsorriso.it${PUBLIC_BASE}${req.path}`); return res.redirect(`${LOGIN_URL}?returnUrl=${returnUrl}`); } // Attach session info for use in route handlers / views req.sessionInfo = sessionInfo; next(); } // ─── Routes ─────────────────────────────────────────────────────────────────── // Main entry point – protected app.get('/', requireAuth, (req, res) => { res.render('index', { userId: req.sessionInfo.userId, publicBase: PUBLIC_BASE, }); }); // Health-check – unprotected (used by reverse proxy / monitoring) app.get('/health', (_req, res) => { res.json({ status: 'ok' }); }); // Catch-all 404 app.use((_req, res) => { res.status(404).send('Not found'); }); // ─── Start ──────────────────────────────────────────────────────────────────── app.listen(PORT, () => { console.log(`facematch app listening on port ${PORT} (base: ${PUBLIC_BASE})`); });