PSA/server/index.ts
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

106 lines
3.6 KiB
TypeScript

import express from 'express';
import next from 'next';
import cookieParser from 'cookie-parser';
import { runWithTenant } from '@alga-psa/db';
import {
apiKeyAuthMiddleware,
sessionAuthMiddleware,
tenantHeaderMiddleware,
authorizationMiddleware
} from './src/middleware/express/authMiddleware';
import { getAppVersion } from './src/lib/utils/version';
const dev = globalThis.process.env.NODE_ENV !== 'production';
const hostname = globalThis.process.env.HOSTNAME || 'localhost';
const port = parseInt(globalThis.process.env.PORT || '3000', 10);
// Initialize Next.js
const app = next({ dev, hostname, port });
const handle = app.getRequestHandler();
async function createServer() {
try {
console.log('Preparing Next.js application...');
await app.prepare();
console.log('Next.js application prepared successfully');
const server = express();
// Enable cookie parsing for NextAuth compatibility
server.use(cookieParser() as any);
// Next.js will handle its internal routes (/_next, HMR, static files) automatically
// Apply authentication middleware in order (after Next.js internal routes)
server.use(apiKeyAuthMiddleware); // Handle API key authentication for API routes
server.use(sessionAuthMiddleware); // Handle NextAuth sessions for web routes
server.use(authorizationMiddleware); // Handle additional authorization checks
server.use(tenantHeaderMiddleware); // Add tenant headers to responses
// Basic health check endpoints for Kubernetes
server.get('/healthz', (_req, res) => {
res.status(200).json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: globalThis.process.uptime(),
version: getAppVersion()
});
});
server.get('/readyz', (_req, res) => {
// TODO: Add database and dependency checks
res.status(200).json({
status: 'ready',
timestamp: new Date().toISOString(),
uptime: globalThis.process.uptime(),
version: getAppVersion()
});
});
// Handle all other requests with Next.js
// This catches any request that wasn't handled by explicit routes above
server.use((req, res, next) => {
// Don't let Next.js handle health endpoints
if (req.path === '/healthz' || req.path === '/readyz') {
// These should have been handled above, if we get here something is wrong
return next();
}
const tenant =
(req as any).apiKey?.tenant ||
(req as any).user?.tenant ||
(req.headers['x-tenant-id'] as string | undefined) ||
(req.headers['x-auth-tenant'] as string | undefined);
// Ensure tenant context is available to server actions / DB helpers that use AsyncLocalStorage.
// This is the earliest point where we have reliable tenant info from either API key auth
// or NextAuth session auth, and it wraps the entire Next.js request lifecycle.
if (tenant) {
return runWithTenant(tenant, async () => handle(req, res));
}
// Let Next.js handle everything else
return handle(req, res);
});
const httpServer = server.listen(port, '0.0.0.0', () => {
console.log(`> Ready on http://${hostname}:${port}`);
console.log(`> Environment: ${dev ? 'development' : 'production'}`);
console.log(`> Version: ${getAppVersion()}`);
});
httpServer.on('error', (err) => {
console.error('HTTP server error:', err);
});
// Next.js handles WebSocket upgrades for HMR automatically
} catch (error) {
console.error('Error starting server:', error);
globalThis.process.exit(1);
}
}
// Start the server
void createServer();