Files
template_001/server.js
SAAC Daemon 5b37f88477 Harden template for production: add comments, fix bugs, add lock file
- Add inline comments to docker-compose.yml explaining the 5 key rules
  (expose vs ports, DB host, DB name persistence, no Traefik labels)
- Add comments to Dockerfile explaining multi-stage build, layer caching,
  and why .dockerignore excludes client/dist
- Add comments to .dockerignore explaining each exclusion
- Fix dev script: use nodemon (auto-restart) instead of node for server.js
- Add postinstall script to auto-install client deps (cd client && npm install)
- Fix SPA fallback: bare return → next() to prevent hanging requests
- Add root package-lock.json for deterministic server dependency installs
- Remove committed tsconfig.tsbuildinfo build artifact, add *.tsbuildinfo to .gitignore
- Update README: simpler install (npm install handles everything), reference SAAC_DEPLOYMENT.md,
  use npx instead of pnpm dlx for shadcn components
2026-02-18 16:36:33 +01:00

88 lines
2.4 KiB
JavaScript

const express = require('express');
const { Pool } = require('pg');
const redis = require('redis');
const path = require('path');
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(express.json());
// PostgreSQL connection
const pool = new Pool({
host: process.env.POSTGRES_HOST || 'postgres',
port: process.env.POSTGRES_PORT || 5432,
user: process.env.POSTGRES_USER || 'postgres',
password: process.env.POSTGRES_PASSWORD || 'postgres',
database: process.env.POSTGRES_DB || 'postgres'
});
// Redis connection
const redisClient = redis.createClient({
socket: {
host: process.env.REDIS_HOST || 'redis',
port: process.env.REDIS_PORT || 6379
}
});
redisClient.connect().catch(console.error);
pool.query('SELECT NOW()', (err, res) => {
if (err) {
console.error('PostgreSQL connection error:', err);
} else {
console.log('PostgreSQL connected at:', res.rows[0].now);
}
});
redisClient.on('connect', () => console.log('Redis connected'));
redisClient.on('error', (err) => console.error('Redis error:', err));
// --- API Routes ---
// Health check
app.get('/health', async (req, res) => {
try {
const pgResult = await pool.query('SELECT NOW()');
await redisClient.ping();
res.json({
status: 'healthy',
postgres: 'connected',
redis: 'connected',
timestamp: pgResult.rows[0].now,
version: process.env.APP_VERSION || '1.0.0'
});
} catch (error) {
res.status(500).json({ status: 'unhealthy', error: error.message });
}
});
// Example API endpoint — replace with your own
app.get('/api/status', async (req, res) => {
try {
const pgResult = await pool.query('SELECT NOW()');
res.json({ ok: true, time: pgResult.rows[0].now });
} catch (error) {
res.status(500).json({ ok: false, error: error.message });
}
});
// --- Serve React Frontend ---
// In production, serve the built React app from client/dist
const clientDist = path.join(__dirname, 'client', 'dist');
app.use(express.static(clientDist));
// SPA fallback — all non-API routes serve index.html so React Router works
app.get('*', (req, res, next) => {
if (req.path.startsWith('/api/') || req.path === '/health') return next();
res.sendFile(path.join(clientDist, 'index.html'));
});
app.listen(PORT, '0.0.0.0', () => {
console.log(`Server running on port ${PORT}`);
});
// Export for testing
module.exports = { app, pool, redisClient };