feat(server): add security middleware and website-specific routes
Server Infrastructure Updates: - Added response sanitization middleware (fixes Date serialization) - Added CSRF protection middleware (double-submit cookie pattern) - Enhanced rate limiting (public, form, auth limiters) - Added cache control middleware for static assets - Added cookie parser for CSRF support Route Organization: - Reorganized routes for website (auth, documents, blog, newsletter) - Separated admin routes with /admin prefix - Added koha routes for donations - Added demo routes for interactive demonstrations - Dev/test routes only in development environment Config Updates: - Updated app config for website platform - Added website-specific configuration options Model Updates: - Updated model exports for website collections - Added blog, media, newsletter models These changes support the website platform while maintaining the underlying Tractatus governance framework.
This commit is contained in:
parent
792a9e55b6
commit
4c656385fe
4 changed files with 317 additions and 101 deletions
|
|
@ -1,13 +1,12 @@
|
||||||
/**
|
/**
|
||||||
* Application Configuration
|
* Application Configuration
|
||||||
* Generic configuration template for Tractatus Framework implementations
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
// Server
|
// Server
|
||||||
port: process.env.PORT || 9000,
|
port: process.env.PORT || 9000,
|
||||||
env: process.env.NODE_ENV || 'development',
|
env: process.env.NODE_ENV || 'development',
|
||||||
appName: process.env.APP_NAME || 'Tractatus Framework',
|
appName: process.env.APP_NAME || 'Tractatus',
|
||||||
|
|
||||||
// MongoDB
|
// MongoDB
|
||||||
mongodb: {
|
mongodb: {
|
||||||
|
|
@ -15,12 +14,30 @@ module.exports = {
|
||||||
db: process.env.MONGODB_DB || 'tractatus_dev'
|
db: process.env.MONGODB_DB || 'tractatus_dev'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// JWT
|
||||||
|
jwt: {
|
||||||
|
secret: process.env.JWT_SECRET || 'CHANGE_THIS_IN_PRODUCTION',
|
||||||
|
expiry: process.env.JWT_EXPIRY || '7d'
|
||||||
|
},
|
||||||
|
|
||||||
|
// Admin
|
||||||
|
admin: {
|
||||||
|
email: process.env.ADMIN_EMAIL || 'john.stroh.nz@pm.me'
|
||||||
|
},
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
logging: {
|
logging: {
|
||||||
level: process.env.LOG_LEVEL || 'info',
|
level: process.env.LOG_LEVEL || 'info',
|
||||||
file: process.env.LOG_FILE || 'logs/app.log'
|
file: process.env.LOG_FILE || 'logs/app.log'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Feature Flags
|
||||||
|
features: {
|
||||||
|
aiCuration: process.env.ENABLE_AI_CURATION === 'true',
|
||||||
|
mediaTriage: process.env.ENABLE_MEDIA_TRIAGE === 'true',
|
||||||
|
caseSubmissions: process.env.ENABLE_CASE_SUBMISSIONS === 'true'
|
||||||
|
},
|
||||||
|
|
||||||
// Security
|
// Security
|
||||||
security: {
|
security: {
|
||||||
rateLimitWindowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS || '900000'), // 15 min
|
rateLimitWindowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS || '900000'), // 15 min
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,28 @@
|
||||||
/**
|
/**
|
||||||
* Models Index
|
* Models Index
|
||||||
* Export all Tractatus Framework models
|
* Export all models
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const AuditLog = require('./AuditLog.model');
|
const Document = require('./Document.model');
|
||||||
const DeliberationSession = require('./DeliberationSession.model');
|
const BlogPost = require('./BlogPost.model');
|
||||||
|
const MediaInquiry = require('./MediaInquiry.model');
|
||||||
|
const CaseSubmission = require('./CaseSubmission.model');
|
||||||
|
const Resource = require('./Resource.model');
|
||||||
|
const ModerationQueue = require('./ModerationQueue.model');
|
||||||
|
const User = require('./User.model');
|
||||||
const GovernanceLog = require('./GovernanceLog.model');
|
const GovernanceLog = require('./GovernanceLog.model');
|
||||||
const GovernanceRule = require('./GovernanceRule.model');
|
const DeliberationSession = require('./DeliberationSession.model');
|
||||||
const Precedent = require('./Precedent.model');
|
const Precedent = require('./Precedent.model');
|
||||||
const Project = require('./Project.model');
|
|
||||||
const SessionState = require('./SessionState.model');
|
|
||||||
const VariableValue = require('./VariableValue.model');
|
|
||||||
const VerificationLog = require('./VerificationLog.model');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
AuditLog,
|
Document,
|
||||||
DeliberationSession,
|
BlogPost,
|
||||||
|
MediaInquiry,
|
||||||
|
CaseSubmission,
|
||||||
|
Resource,
|
||||||
|
ModerationQueue,
|
||||||
|
User,
|
||||||
GovernanceLog,
|
GovernanceLog,
|
||||||
GovernanceRule,
|
DeliberationSession,
|
||||||
Precedent,
|
Precedent
|
||||||
Project,
|
|
||||||
SessionState,
|
|
||||||
VariableValue,
|
|
||||||
VerificationLog
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,71 +1,147 @@
|
||||||
/**
|
/**
|
||||||
* Routes Index
|
* Routes Index
|
||||||
* Central routing configuration for Tractatus Framework API
|
* Central routing configuration
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
// Import framework route modules
|
// Import route modules
|
||||||
|
const authRoutes = require('./auth.routes');
|
||||||
|
const documentsRoutes = require('./documents.routes');
|
||||||
|
const blogRoutes = require('./blog.routes');
|
||||||
|
const newsletterRoutes = require('./newsletter.routes');
|
||||||
|
const mediaRoutes = require('./media.routes');
|
||||||
|
const casesRoutes = require('./cases.routes');
|
||||||
|
const adminRoutes = require('./admin.routes');
|
||||||
|
const hooksMetricsRoutes = require('./hooks-metrics.routes');
|
||||||
|
const syncHealthRoutes = require('./sync-health.routes');
|
||||||
const rulesRoutes = require('./rules.routes');
|
const rulesRoutes = require('./rules.routes');
|
||||||
const projectsRoutes = require('./projects.routes');
|
const projectsRoutes = require('./projects.routes');
|
||||||
const auditRoutes = require('./audit.routes');
|
const auditRoutes = require('./audit.routes');
|
||||||
const governanceRoutes = require('./governance.routes');
|
const governanceRoutes = require('./governance.routes');
|
||||||
|
const kohaRoutes = require('./koha.routes');
|
||||||
|
const demoRoutes = require('./demo.routes');
|
||||||
|
|
||||||
// Mount framework routes
|
// Development/test routes (only in development)
|
||||||
router.use('/rules', rulesRoutes);
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
router.use('/projects', projectsRoutes);
|
const testRoutes = require('./test.routes');
|
||||||
router.use('/audit', auditRoutes);
|
router.use('/test', testRoutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount routes
|
||||||
|
router.use('/auth', authRoutes);
|
||||||
|
router.use('/documents', documentsRoutes);
|
||||||
|
router.use('/blog', blogRoutes);
|
||||||
|
router.use('/newsletter', newsletterRoutes);
|
||||||
|
router.use('/media', mediaRoutes);
|
||||||
|
router.use('/cases', casesRoutes);
|
||||||
|
router.use('/admin', adminRoutes);
|
||||||
|
router.use('/admin/hooks', hooksMetricsRoutes);
|
||||||
|
router.use('/admin/sync', syncHealthRoutes);
|
||||||
|
router.use('/admin/rules', rulesRoutes);
|
||||||
|
router.use('/admin/projects', projectsRoutes);
|
||||||
|
router.use('/admin', auditRoutes);
|
||||||
router.use('/governance', governanceRoutes);
|
router.use('/governance', governanceRoutes);
|
||||||
|
router.use('/koha', kohaRoutes);
|
||||||
|
router.use('/demo', demoRoutes);
|
||||||
|
|
||||||
// API root endpoint
|
// API root endpoint - redirect browsers to documentation
|
||||||
router.get('/', (req, res) => {
|
router.get('/', (req, res) => {
|
||||||
|
// Check if request is from a browser (Accept: text/html)
|
||||||
|
const acceptsHtml = req.accepts('html');
|
||||||
|
const acceptsJson = req.accepts('json');
|
||||||
|
|
||||||
|
// If browser request, redirect to API documentation page
|
||||||
|
if (acceptsHtml && !acceptsJson) {
|
||||||
|
return res.redirect(302, '/api-reference.html');
|
||||||
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
name: 'Tractatus AI Safety Framework API',
|
name: 'Tractatus AI Safety Framework API',
|
||||||
version: '3.5.0',
|
version: '1.0.0',
|
||||||
status: 'operational',
|
status: 'operational',
|
||||||
documentation: 'https://agenticgovernance.digital',
|
|
||||||
endpoints: {
|
endpoints: {
|
||||||
|
auth: {
|
||||||
|
login: 'POST /api/auth/login',
|
||||||
|
me: 'GET /api/auth/me',
|
||||||
|
logout: 'POST /api/auth/logout'
|
||||||
|
},
|
||||||
|
documents: {
|
||||||
|
list: 'GET /api/documents',
|
||||||
|
get: 'GET /api/documents/:identifier',
|
||||||
|
search: 'GET /api/documents/search?q=query',
|
||||||
|
create: 'POST /api/documents (admin)',
|
||||||
|
update: 'PUT /api/documents/:id (admin)',
|
||||||
|
delete: 'DELETE /api/documents/:id (admin)'
|
||||||
|
},
|
||||||
|
blog: {
|
||||||
|
list: 'GET /api/blog',
|
||||||
|
get: 'GET /api/blog/:slug',
|
||||||
|
create: 'POST /api/blog (admin)',
|
||||||
|
update: 'PUT /api/blog/:id (admin)',
|
||||||
|
publish: 'POST /api/blog/:id/publish (admin)',
|
||||||
|
delete: 'DELETE /api/blog/:id (admin)',
|
||||||
|
admin_list: 'GET /api/blog/admin/posts?status=draft (admin)',
|
||||||
|
admin_get: 'GET /api/blog/admin/:id (admin)',
|
||||||
|
suggest_topics: 'POST /api/blog/suggest-topics (admin)'
|
||||||
|
},
|
||||||
|
newsletter: {
|
||||||
|
subscribe: 'POST /api/newsletter/subscribe',
|
||||||
|
verify: 'GET /api/newsletter/verify/:token',
|
||||||
|
unsubscribe: 'POST /api/newsletter/unsubscribe',
|
||||||
|
preferences: 'PUT /api/newsletter/preferences',
|
||||||
|
stats: 'GET /api/newsletter/admin/stats (admin)',
|
||||||
|
subscriptions: 'GET /api/newsletter/admin/subscriptions (admin)',
|
||||||
|
export: 'GET /api/newsletter/admin/export (admin)',
|
||||||
|
delete: 'DELETE /api/newsletter/admin/subscriptions/:id (admin)'
|
||||||
|
},
|
||||||
|
media: {
|
||||||
|
submit: 'POST /api/media/inquiries',
|
||||||
|
list: 'GET /api/media/inquiries (admin)',
|
||||||
|
urgent: 'GET /api/media/inquiries/urgent (admin)',
|
||||||
|
get: 'GET /api/media/inquiries/:id (admin)',
|
||||||
|
assign: 'POST /api/media/inquiries/:id/assign (admin)',
|
||||||
|
respond: 'POST /api/media/inquiries/:id/respond (admin)',
|
||||||
|
delete: 'DELETE /api/media/inquiries/:id (admin)'
|
||||||
|
},
|
||||||
|
cases: {
|
||||||
|
submit: 'POST /api/cases/submit',
|
||||||
|
list: 'GET /api/cases/submissions (admin)',
|
||||||
|
high_relevance: 'GET /api/cases/submissions/high-relevance (admin)',
|
||||||
|
get: 'GET /api/cases/submissions/:id (admin)',
|
||||||
|
approve: 'POST /api/cases/submissions/:id/approve (admin)',
|
||||||
|
reject: 'POST /api/cases/submissions/:id/reject (admin)',
|
||||||
|
request_info: 'POST /api/cases/submissions/:id/request-info (admin)',
|
||||||
|
delete: 'DELETE /api/cases/submissions/:id (admin)'
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
moderation_queue: 'GET /api/admin/moderation',
|
||||||
|
moderation_item: 'GET /api/admin/moderation/:id',
|
||||||
|
review: 'POST /api/admin/moderation/:id/review',
|
||||||
|
stats: 'GET /api/admin/stats',
|
||||||
|
activity: 'GET /api/admin/activity'
|
||||||
|
},
|
||||||
governance: {
|
governance: {
|
||||||
status: 'GET /api/governance',
|
status: 'GET /api/governance',
|
||||||
classify: 'POST /api/governance/classify',
|
classify: 'POST /api/governance/classify (admin)',
|
||||||
validate: 'POST /api/governance/validate',
|
validate: 'POST /api/governance/validate (admin)',
|
||||||
enforce: 'POST /api/governance/enforce',
|
enforce: 'POST /api/governance/enforce (admin)',
|
||||||
pressure: 'POST /api/governance/pressure',
|
pressure: 'POST /api/governance/pressure (admin)',
|
||||||
verify: 'POST /api/governance/verify'
|
verify: 'POST /api/governance/verify (admin)'
|
||||||
},
|
},
|
||||||
rules: {
|
koha: {
|
||||||
list: 'GET /api/rules',
|
checkout: 'POST /api/koha/checkout',
|
||||||
get: 'GET /api/rules/:id',
|
webhook: 'POST /api/koha/webhook',
|
||||||
create: 'POST /api/rules',
|
transparency: 'GET /api/koha/transparency',
|
||||||
update: 'PUT /api/rules/:id',
|
cancel: 'POST /api/koha/cancel',
|
||||||
delete: 'DELETE /api/rules/:id',
|
verify: 'GET /api/koha/verify/:sessionId',
|
||||||
search: 'GET /api/rules/search'
|
statistics: 'GET /api/koha/statistics (admin)'
|
||||||
},
|
|
||||||
projects: {
|
|
||||||
list: 'GET /api/projects',
|
|
||||||
get: 'GET /api/projects/:id',
|
|
||||||
create: 'POST /api/projects',
|
|
||||||
update: 'PUT /api/projects/:id',
|
|
||||||
delete: 'DELETE /api/projects/:id'
|
|
||||||
},
|
|
||||||
audit: {
|
|
||||||
logs: 'GET /api/audit/logs',
|
|
||||||
stats: 'GET /api/audit/stats'
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
framework: {
|
framework: 'Tractatus-Based LLM Safety Architecture',
|
||||||
name: 'Tractatus Framework',
|
documentation: '/api/docs',
|
||||||
description: 'AI governance framework enforcing architectural safety constraints at runtime',
|
health: '/health'
|
||||||
services: [
|
|
||||||
'InstructionPersistenceClassifier',
|
|
||||||
'CrossReferenceValidator',
|
|
||||||
'BoundaryEnforcer',
|
|
||||||
'ContextPressureMonitor',
|
|
||||||
'MetacognitiveVerifier',
|
|
||||||
'PluralisticDeliberationOrchestrator'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
199
src/server.js
199
src/server.js
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* Tractatus Framework Server
|
* Tractatus Express Server
|
||||||
* Generic Express server template for Tractatus implementations
|
* Main application entry point
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
|
|
@ -8,14 +8,21 @@ require('dotenv').config();
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const helmet = require('helmet');
|
const helmet = require('helmet');
|
||||||
const cors = require('cors');
|
const cors = require('cors');
|
||||||
|
const rateLimit = require('express-rate-limit');
|
||||||
|
const cookieParser = require('cookie-parser');
|
||||||
|
// const csrf = require('csurf'); // Disabled - deprecated package, will implement modern solution in Phase 3
|
||||||
|
|
||||||
const config = require('./config/app.config');
|
const config = require('./config/app.config');
|
||||||
const logger = require('./utils/logger.util');
|
const logger = require('./utils/logger.util');
|
||||||
const { connect: connectDb, close: closeDb } = require('./utils/db.util');
|
const { connect: connectDb, close: closeDb } = require('./utils/db.util');
|
||||||
const { connect: connectMongoose, close: closeMongoose } = require('./utils/mongoose.util');
|
const { connect: connectMongoose, close: closeMongoose } = require('./utils/mongoose.util');
|
||||||
const { notFound, errorHandler } = require('./middleware/error.middleware');
|
const { notFound, errorHandler } = require('./middleware/error.middleware');
|
||||||
|
|
||||||
|
// Security middleware (Quick Wins)
|
||||||
const { securityHeadersMiddleware } = require('./middleware/security-headers.middleware');
|
const { securityHeadersMiddleware } = require('./middleware/security-headers.middleware');
|
||||||
const { publicRateLimiter } = require('./middleware/rate-limit.middleware');
|
const { publicRateLimiter, formRateLimiter, authRateLimiter } = require('./middleware/rate-limit.middleware');
|
||||||
|
const { sanitizeErrorResponse, sanitizeResponseData } = require('./middleware/response-sanitization.middleware');
|
||||||
|
const { setCsrfToken, csrfProtection, getCsrfToken } = require('./middleware/csrf-protection.middleware');
|
||||||
|
|
||||||
// Create Express app
|
// Create Express app
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
@ -24,35 +31,90 @@ const app = express();
|
||||||
app.set('trust proxy', 1);
|
app.set('trust proxy', 1);
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// SECURITY MIDDLEWARE
|
// SECURITY MIDDLEWARE (Quick Wins - inst_041-046)
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
// Security headers
|
// Enhanced security headers (replaces helmet CSP with more specific policy)
|
||||||
app.use(securityHeadersMiddleware);
|
app.use(securityHeadersMiddleware);
|
||||||
|
|
||||||
// Helmet for additional security
|
// Keep helmet for other security features (but CSP already set above)
|
||||||
app.use(helmet({
|
app.use(helmet({
|
||||||
contentSecurityPolicy: false // Using custom CSP in securityHeadersMiddleware
|
contentSecurityPolicy: false, // Disabled - using our custom CSP in securityHeadersMiddleware
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// CORS
|
// CORS
|
||||||
app.use(cors(config.cors));
|
app.use(cors(config.cors));
|
||||||
|
|
||||||
// Body parsers
|
// Cookie parser (required for CSRF)
|
||||||
|
app.use(cookieParser());
|
||||||
|
|
||||||
|
// Set CSRF token cookie on all requests
|
||||||
|
app.use(setCsrfToken);
|
||||||
|
|
||||||
|
// Response data sanitization (removes sensitive fields)
|
||||||
|
app.use(sanitizeResponseData);
|
||||||
|
|
||||||
|
// Raw body capture for Stripe webhooks (must be before JSON parser)
|
||||||
|
app.use('/api/koha/webhook', express.raw({ type: 'application/json' }), (req, res, next) => {
|
||||||
|
req.rawBody = req.body;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Body parsers (reduced limit from 10mb to 1mb for security)
|
||||||
app.use(express.json({ limit: '1mb' }));
|
app.use(express.json({ limit: '1mb' }));
|
||||||
app.use(express.urlencoded({ extended: true, limit: '1mb' }));
|
app.use(express.urlencoded({ extended: true, limit: '1mb' }));
|
||||||
|
|
||||||
// Request logging
|
// Request logging
|
||||||
app.use(logger.request);
|
app.use(logger.request);
|
||||||
|
|
||||||
// Rate limiting
|
// CSRF Protection (Modern Implementation - Phase 0 Complete)
|
||||||
|
// Uses SameSite cookies + double-submit cookie pattern
|
||||||
|
// Protection is applied selectively to state-changing routes (POST, PUT, DELETE, PATCH)
|
||||||
|
// Webhooks and public endpoints are excluded
|
||||||
|
|
||||||
|
// Enhanced rate limiting (Quick Wins)
|
||||||
|
// Public endpoints: 100 requests per 15 minutes per IP
|
||||||
app.use(publicRateLimiter);
|
app.use(publicRateLimiter);
|
||||||
|
|
||||||
// ============================================================
|
// Cache control middleware for static assets
|
||||||
// ROUTES
|
app.use((req, res, next) => {
|
||||||
// ============================================================
|
const path = req.path;
|
||||||
|
|
||||||
// Health check endpoint
|
// Version manifest and service worker: No cache (always fetch fresh)
|
||||||
|
if (path === '/version.json' || path === '/service-worker.js') {
|
||||||
|
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
||||||
|
res.setHeader('Pragma', 'no-cache');
|
||||||
|
res.setHeader('Expires', '0');
|
||||||
|
}
|
||||||
|
// HTML files: No cache (always fetch fresh - users must see updates immediately)
|
||||||
|
else if (path.endsWith('.html') || path === '/') {
|
||||||
|
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0');
|
||||||
|
res.setHeader('Pragma', 'no-cache');
|
||||||
|
}
|
||||||
|
// CSS and JS files: Longer cache (we use version parameters)
|
||||||
|
else if (path.endsWith('.css') || path.endsWith('.js')) {
|
||||||
|
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable'); // 1 year
|
||||||
|
}
|
||||||
|
// Images and fonts: Long cache
|
||||||
|
else if (path.match(/\.(jpg|jpeg|png|gif|svg|ico|woff|woff2|ttf|eot)$/)) {
|
||||||
|
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable'); // 1 year
|
||||||
|
}
|
||||||
|
// PWA manifest: Medium cache
|
||||||
|
else if (path === '/manifest.json') {
|
||||||
|
res.setHeader('Cache-Control', 'public, max-age=86400'); // 1 day
|
||||||
|
}
|
||||||
|
// Everything else: Short cache
|
||||||
|
else {
|
||||||
|
res.setHeader('Cache-Control', 'public, max-age=3600'); // 1 hour
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Static files
|
||||||
|
app.use(express.static('public'));
|
||||||
|
|
||||||
|
// Health check endpoint (minimal, no sensitive data)
|
||||||
app.get('/health', (req, res) => {
|
app.get('/health', (req, res) => {
|
||||||
res.json({
|
res.json({
|
||||||
status: 'ok',
|
status: 'ok',
|
||||||
|
|
@ -60,47 +122,104 @@ app.get('/health', (req, res) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// CSRF token endpoint (modern implementation)
|
||||||
|
// Returns the CSRF token from cookie for client-side usage
|
||||||
|
app.get('/api/csrf-token', getCsrfToken);
|
||||||
|
|
||||||
// API routes
|
// API routes
|
||||||
const apiRoutes = require('./routes/index');
|
const apiRoutes = require('./routes/index');
|
||||||
app.use('/api', apiRoutes);
|
app.use('/api', apiRoutes);
|
||||||
|
|
||||||
// Homepage
|
// Homepage (temporary)
|
||||||
app.get('/', (req, res) => {
|
app.get('/', (req, res) => {
|
||||||
res.json({
|
res.send(`
|
||||||
name: 'Tractatus AI Safety Framework',
|
<!DOCTYPE html>
|
||||||
version: '3.5.0',
|
<html>
|
||||||
status: 'operational',
|
<head>
|
||||||
documentation: 'https://agenticgovernance.digital',
|
<title>Tractatus AI Safety Framework</title>
|
||||||
endpoints: {
|
<meta charset="utf-8">
|
||||||
health: 'GET /health',
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
api: 'GET /api'
|
<style>
|
||||||
}
|
body {
|
||||||
});
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 50px auto;
|
||||||
|
padding: 20px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
h1 { color: #2563eb; }
|
||||||
|
.status { color: #059669; font-weight: bold; }
|
||||||
|
code { background: #f3f4f6; padding: 2px 6px; border-radius: 3px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Tractatus AI Safety Framework</h1>
|
||||||
|
<p class="status">✓ Server Running</p>
|
||||||
|
<p>Development environment for the Tractatus-Based LLM Safety Framework website.</p>
|
||||||
|
|
||||||
|
<h2>Status</h2>
|
||||||
|
<ul>
|
||||||
|
<li>✓ MongoDB connected (port 27017)</li>
|
||||||
|
<li>✓ Express server running (port ${config.port})</li>
|
||||||
|
<li>✓ Database initialized (10 collections)</li>
|
||||||
|
<li>✓ Core models implemented</li>
|
||||||
|
<li>✓ API routes complete (auth, documents, blog, admin)</li>
|
||||||
|
<li>✓ Governance services active (6 core services)</li>
|
||||||
|
<li>⏳ Frontend (pending)</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Available Endpoints</h2>
|
||||||
|
<ul>
|
||||||
|
<li><code>GET /health</code> - Health check</li>
|
||||||
|
<li><code>GET /api</code> - API documentation</li>
|
||||||
|
<li><code>POST /api/auth/login</code> - Admin login</li>
|
||||||
|
<li><code>GET /api/documents</code> - List framework documents</li>
|
||||||
|
<li><code>GET /api/blog</code> - List published blog posts</li>
|
||||||
|
<li><code>GET /api/admin/stats</code> - System statistics (auth required)</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p><em>Phase 1 Development - Not for public use</em></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// ERROR HANDLING
|
// ERROR HANDLING (Quick Wins)
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
// 404 handler
|
// 404 handler
|
||||||
app.use(notFound);
|
app.use(notFound);
|
||||||
|
|
||||||
// Error handler
|
// Enhanced error handler (sanitizes responses, hides stack traces)
|
||||||
|
app.use(sanitizeErrorResponse);
|
||||||
|
|
||||||
|
// Fallback to original error handler if needed
|
||||||
app.use(errorHandler);
|
app.use(errorHandler);
|
||||||
|
|
||||||
// ============================================================
|
// Server startup
|
||||||
// SERVER STARTUP
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
async function start() {
|
async function start() {
|
||||||
try {
|
try {
|
||||||
// Connect to MongoDB (native driver)
|
// Connect to MongoDB (native driver)
|
||||||
await connectDb();
|
await connectDb();
|
||||||
logger.info('✅ MongoDB (native) connected');
|
|
||||||
|
|
||||||
// Connect Mongoose (for ODM models)
|
// Connect Mongoose (for ODM models)
|
||||||
await connectMongoose();
|
await connectMongoose();
|
||||||
logger.info('✅ Mongoose connected');
|
|
||||||
|
// Sync instructions from file to database
|
||||||
|
try {
|
||||||
|
const { syncInstructions } = require('../scripts/sync-instructions-to-db.js');
|
||||||
|
const syncResult = await syncInstructions({ silent: true });
|
||||||
|
if (syncResult && syncResult.success) {
|
||||||
|
logger.info(`✅ Instructions synced to database: ${syncResult.finalCount} active rules`);
|
||||||
|
if (syncResult.added > 0 || syncResult.deactivated > 0) {
|
||||||
|
logger.info(` Added: ${syncResult.added}, Updated: ${syncResult.updated}, Deactivated: ${syncResult.deactivated}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn(`⚠️ Instruction sync failed: ${err.message}`);
|
||||||
|
logger.warn(' Admin UI may show outdated rule counts');
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize governance services
|
// Initialize governance services
|
||||||
const BoundaryEnforcer = require('./services/BoundaryEnforcer.service');
|
const BoundaryEnforcer = require('./services/BoundaryEnforcer.service');
|
||||||
|
|
@ -113,12 +232,14 @@ async function start() {
|
||||||
|
|
||||||
// Start server
|
// Start server
|
||||||
const server = app.listen(config.port, () => {
|
const server = app.listen(config.port, () => {
|
||||||
logger.info(`🚀 Tractatus Framework server started`);
|
logger.info(`🚀 Tractatus server started`);
|
||||||
logger.info(` Environment: ${config.env}`);
|
logger.info(`✅ Environment: ${config.env}`);
|
||||||
logger.info(` Port: ${config.port}`);
|
logger.info(`✅ Port: ${config.port}`);
|
||||||
logger.info(` MongoDB: ${config.mongodb.db}`);
|
logger.info(`✅ MongoDB: ${config.mongodb.db}`);
|
||||||
logger.info(` API: http://localhost:${config.port}/api`);
|
logger.info(`🔒 Security: Quick Wins active (headers, rate limiting, input validation)`);
|
||||||
console.log(`\n🌐 Server running at http://localhost:${config.port}\n`);
|
logger.info(`📊 Security logs: ${process.env.HOME}/var/log/tractatus/security-audit.log`);
|
||||||
|
logger.info(`✨ Ready for development`);
|
||||||
|
console.log(`\n🌐 http://localhost:${config.port}\n`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Graceful shutdown
|
// Graceful shutdown
|
||||||
|
|
@ -139,7 +260,7 @@ async function shutdown(server) {
|
||||||
logger.info('HTTP server closed');
|
logger.info('HTTP server closed');
|
||||||
|
|
||||||
await closeDb();
|
await closeDb();
|
||||||
logger.info('MongoDB (native) connection closed');
|
logger.info('Native MongoDB connection closed');
|
||||||
|
|
||||||
await closeMongoose();
|
await closeMongoose();
|
||||||
logger.info('Mongoose connection closed');
|
logger.info('Mongoose connection closed');
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue