249 lines
6.7 KiB
JavaScript
249 lines
6.7 KiB
JavaScript
import express from 'express';
|
|
import session from 'express-session';
|
|
import FileStore from 'session-file-store';
|
|
import bodyParser from 'body-parser';
|
|
import cors from 'cors';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import config from './config.js';
|
|
import { initDatabase } from './db.js';
|
|
import { initOIDC, isOIDCEnabled } from './middleware/oidcMiddleware.js';
|
|
import {
|
|
requestLogger,
|
|
securityHeaders,
|
|
errorHandler,
|
|
} from './middleware/security.js';
|
|
import authRoutes from './routes/authRoutes.js';
|
|
import { authCallback } from './controllers/authController.js';
|
|
import adminRoutes from './routes/adminRoutes.js';
|
|
import dashboardRoutes from './routes/dashboardRoutes.js';
|
|
import reverseProxyMiddleware from './middleware/proxyMiddleware.js';
|
|
|
|
const app = express();
|
|
const FileStoreSession = FileStore(session);
|
|
|
|
// Create sessions directory FIRST (before any middleware)
|
|
try {
|
|
const sessionsDir = path.join(process.cwd(), 'sessions');
|
|
fs.mkdirSync(sessionsDir, { recursive: true });
|
|
} catch (error) {
|
|
console.error('✗ Failed to create sessions directory:', error);
|
|
process.exit(1);
|
|
}
|
|
|
|
// Initialize
|
|
async function initialize() {
|
|
console.log('🚀 Initializing Secure Proxy...');
|
|
|
|
// Database initialization
|
|
try {
|
|
await initDatabase(config.db.path);
|
|
console.log('✓ Database initialized');
|
|
} catch (error) {
|
|
console.error('✗ Database initialization failed:', error);
|
|
process.exit(1);
|
|
}
|
|
|
|
// OIDC initialization
|
|
try {
|
|
await initOIDC();
|
|
} catch (error) {
|
|
console.error('✗ OIDC initialization failed:', error);
|
|
console.log('⚠️ Running in OIDC-disabled mode for development');
|
|
}
|
|
}
|
|
|
|
// Middleware
|
|
app.set('trust proxy', 1);
|
|
app.use(requestLogger);
|
|
app.use(securityHeaders);
|
|
app.use(bodyParser.json({ limit: '10mb' }));
|
|
app.use(bodyParser.urlencoded({ limit: '10mb', extended: true }));
|
|
app.use(cors({
|
|
origin: config.proxyUrl,
|
|
credentials: true,
|
|
}));
|
|
|
|
// Session configuration
|
|
app.use(
|
|
session({
|
|
store: new FileStoreSession({ path: './sessions' }),
|
|
secret: config.sessionSecret,
|
|
resave: false,
|
|
saveUninitialized: false,
|
|
cookie: {
|
|
secure: config.nodeEnv === 'production',
|
|
httpOnly: true,
|
|
sameSite: 'strict',
|
|
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
|
},
|
|
})
|
|
);
|
|
|
|
// Static files
|
|
app.use(express.static('public'));
|
|
|
|
// Development mode: auto-create session for /admin and /api access
|
|
app.use((req, res, next) => {
|
|
// In dev mode without OIDC, create a session automatically
|
|
if (req.path.startsWith('/admin') || req.path.startsWith('/api') || req.path.startsWith('/dashboard')) {
|
|
if (!isOIDCEnabled() && !req.session.user) {
|
|
req.session.user = {
|
|
sub: 'dev-user-' + Date.now(),
|
|
name: 'Dev User',
|
|
email: 'dev@localhost',
|
|
isAdmin: true,
|
|
};
|
|
}
|
|
}
|
|
next();
|
|
});
|
|
|
|
// Routes
|
|
app.use('/auth', authRoutes);
|
|
app.use('/api', adminRoutes);
|
|
app.use('/dashboard', dashboardRoutes);
|
|
|
|
// OAuth callback redirect (Keycloak sends to /callback, not /auth/callback)
|
|
app.post('/callback', authCallback);
|
|
|
|
// Home page
|
|
app.get('/', (req, res) => {
|
|
if (req.session?.user) {
|
|
res.send(`
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Secure Proxy Home</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 20px;
|
|
}
|
|
.container {
|
|
background: white;
|
|
padding: 40px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 10px 25px rgba(0,0,0,0.2);
|
|
max-width: 600px;
|
|
width: 100%;
|
|
}
|
|
h1 {
|
|
color: #333;
|
|
margin-bottom: 20px;
|
|
}
|
|
.user-info {
|
|
background: #f5f5f5;
|
|
padding: 20px;
|
|
border-radius: 4px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.user-info p {
|
|
margin: 8px 0;
|
|
color: #666;
|
|
}
|
|
.buttons {
|
|
display: flex;
|
|
gap: 10px;
|
|
flex-wrap: wrap;
|
|
}
|
|
a, button {
|
|
padding: 10px 20px;
|
|
border-radius: 4px;
|
|
text-decoration: none;
|
|
border: none;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
transition: background 0.3s;
|
|
}
|
|
.btn-primary {
|
|
background: #667eea;
|
|
color: white;
|
|
}
|
|
.btn-primary:hover {
|
|
background: #764ba2;
|
|
}
|
|
.btn-secondary {
|
|
background: #e0e0e0;
|
|
color: #333;
|
|
}
|
|
.btn-secondary:hover {
|
|
background: #d0d0d0;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>Welcome to Secure Proxy</h1>
|
|
<div class="user-info">
|
|
<p><strong>Name:</strong> ${req.session.user.name || 'N/A'}</p>
|
|
<p><strong>Email:</strong> ${req.session.user.email || 'N/A'}</p>
|
|
<p><strong>ID:</strong> ${req.session.user.sub}</p>
|
|
${req.session.user.isAdmin ? '<p><strong>Role:</strong> Administrator</p>' : ''}
|
|
</div>
|
|
<div class="buttons">
|
|
${req.session.user.isAdmin ? '<a href="/admin" class="btn btn-primary">Admin Panel</a>' : ''}
|
|
<a href="/auth/profile" class="btn btn-primary">Profile</a>
|
|
<a href="/auth/logout" class="btn btn-secondary">Logout</a>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
`);
|
|
} else {
|
|
res.redirect('/auth/login-page');
|
|
}
|
|
});
|
|
|
|
// Admin panel
|
|
app.get('/admin', (req, res, next) => {
|
|
if (!req.session?.user?.isAdmin) {
|
|
return res.status(403).send('Access denied');
|
|
}
|
|
res.sendFile('public/admin.html', { root: '.' });
|
|
});
|
|
|
|
// 404 handler before reverse proxy
|
|
app.use((req, res, next) => {
|
|
// Skip reverse proxy for known routes
|
|
if (
|
|
req.path.startsWith('/auth') ||
|
|
req.path.startsWith('/api') ||
|
|
req.path.startsWith('/dashboard') ||
|
|
req.path === '/' ||
|
|
req.path === '/admin'
|
|
) {
|
|
return next();
|
|
}
|
|
|
|
// Apply reverse proxy for other paths
|
|
reverseProxyMiddleware(req, res, next);
|
|
});
|
|
|
|
// Error handling
|
|
app.use(errorHandler);
|
|
|
|
// Start server
|
|
initialize().then(() => {
|
|
app.listen(config.port, () => {
|
|
console.log(`✓ Server running on port ${config.port}`);
|
|
console.log(`✓ Environment: ${config.nodeEnv}`);
|
|
console.log(`✓ Proxy URL: ${config.proxyUrl}`);
|
|
});
|
|
}).catch((error) => {
|
|
console.error('Initialization failed:', error);
|
|
process.exit(1);
|
|
});
|
|
|
|
export default app;
|