Files
brainwave/backend/src/index.ts
T
LaurensBartelds 63aa9b572a
Test / backend (push) Failing after 14s
Test / frontend (push) Failing after 19s
feat: integrate AI features for question generation and tutoring
- Added AI routes for generating questions, providing hints, and tutor chat functionality.
- Implemented AI client setup and model retrieval in aiClient.ts.
- Created aiAssist.ts for handling AI-related operations including question generation and hint retrieval.
- Updated services to utilize the new AI client and assist functions.
- Enhanced frontend components to support AI question generation and tutor chat, including UI for generating questions and displaying hints.
- Added localization strings for new AI features in English and Dutch.
- Updated exam review process to include AI-generated hints and tutor chat functionality.
2026-05-02 17:22:56 +02:00

129 lines
3.4 KiB
TypeScript

import 'dotenv/config'
import express from 'express'
import cors from 'cors'
import helmet from 'helmet'
import rateLimit from 'express-rate-limit'
import { getDb, closeDb } from './db/database.js'
import authRoutes from './routes/auth.js'
import examRoutes from './routes/exams.js'
import resultRoutes from './routes/results.js'
import adminRoutes from './routes/admin.js'
import setupRoutes from './routes/setup.js'
import gradeRoutes from './routes/grade.js'
import shareRoutes from './routes/share.js'
import collectionRoutes from './routes/collections.js'
import aiRoutes from './routes/ai.js'
import { errorHandler } from './middleware/error.js'
import { config } from './config.js'
import path from 'path'
import fs from 'fs'
import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const PORT = config.port
const app = express()
// Security headers
app.use(helmet())
// CORS
app.use(cors({ origin: config.corsOrigin }))
// Body parsing with reasonable limit
app.use(express.json({ limit: config.bodyLimit }))
// Global API rate limiting
const globalLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 200,
message: { error: 'Too many requests, please try again later' },
standardHeaders: true,
legacyHeaders: false,
})
app.use('/api/', globalLimiter)
// Request logging
app.use((req, res, next) => {
const start = Date.now()
res.on('finish', () => {
const duration = Date.now() - start
console.log(`${req.method} ${req.path} ${res.statusCode} - ${duration}ms`)
})
next()
})
// Initialize database on startup
getDb()
console.log('✓ Database initialized')
// Routes
app.use('/api/setup', setupRoutes)
app.use('/api/auth', authRoutes)
app.use('/api/exams', examRoutes)
app.use('/api/results', resultRoutes)
app.use('/api/grade', gradeRoutes)
app.use('/api/ai', aiRoutes)
app.use('/api/admin', adminRoutes)
app.use('/api/share', shareRoutes)
app.use('/api/collections', collectionRoutes)
// Health check with subsystem status
app.get('/api/health', (_req, res) => {
let dbStatus: 'connected' | 'error' = 'connected'
try {
getDb().prepare('SELECT 1').get()
} catch {
dbStatus = 'error'
}
const checks = {
database: dbStatus,
email: config.email.smtpHost ? 'configured' : 'not_configured',
}
const overall = dbStatus === 'connected' ? 'healthy' : 'unhealthy'
res.status(overall === 'healthy' ? 200 : 503).json({
status: overall,
timestamp: Date.now(),
checks,
})
})
// Error handler (must be after routes)
app.use(errorHandler)
// Serve static frontend files if they exist
const frontendDistPath = path.join(__dirname, '../../frontend/dist')
if (fs.existsSync(frontendDistPath)) {
console.log(`Serving frontend from: ${frontendDistPath}`)
app.use(express.static(frontendDistPath))
app.get('*path', (req, res) => {
if (!req.path.startsWith('/api')) {
res.sendFile(path.join(frontendDistPath, 'index.html'))
} else {
res.status(404).json({ error: 'API route not found' })
}
})
} else {
console.log('Frontend build not found, skipping static serving.')
}
const server = app.listen(PORT, () => {
console.log(`🧠 BrainWave API running on http://localhost:${PORT}`)
})
// Graceful shutdown
function shutdown() {
console.log('\nShutting down...')
server.close()
closeDb()
process.exit(0)
}
process.on('SIGINT', shutdown)
process.on('SIGTERM', shutdown)