An agent request, end to end
Follow one 'ask an agent' request from the Express entry point, through route registration and system-prompt selection, to the provider call.
apps/api/src/index.ts96 lines · app L20–20
1import 'dotenv/config'
2import express from 'express'
3import helmet from 'helmet'
4import { errorHandler } from './middleware/errorHandler'
5import { validateEnv } from './utils/validateEnv'
6import { apiLimiter } from './middleware/rateLimit'
7import authRoutes from './routes/auth'
8import contractRoutes from './routes/contracts'
9import matterRoutes from './routes/matters'
10import researchRoutes from './routes/research'
11import agentRoutes from './routes/agents'
12import documentRoutes from './routes/documents'
13import timeEntryRoutes from './routes/timeEntries'
14import apiKeyRoutes from './routes/apiKeys'
15import adminRoutes from './routes/admin'
16import clioRoutes from './routes/clio'
17
18validateEnv()
19
20const app = express()
21const PORT = process.env.PORT || 3001
22
23app.set('trust proxy', 1)
24app.use(helmet({ crossOriginEmbedderPolicy: false }))
25
26// Open CORS — must be before all routes
27app.use((req: any, res: any, next: any) => {
28 res.header('Access-Control-Allow-Origin', '*')
29 res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH,OPTIONS')
30 res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, x-requested-with, x-api-key, x-api-provider')
31 if (req.method === 'OPTIONS') {
32 res.sendStatus(200)
33 } else {
34 next()
35 }
36})
37app.use(express.json({ limit: '500mb' }))
38app.use(express.urlencoded({ extended: true, limit: '500mb' }))
39app.use('/api', apiLimiter)
40
41app.get('/health', (_req, res) => {
42 res.json({
43 status: 'ok',
44 timestamp: new Date().toISOString(),
45 env: process.env.NODE_ENV,
46 })
47})
48
49app.use('/api/auth', authRoutes)
50app.use('/api/contracts', contractRoutes)
51app.use('/api/matters', matterRoutes)
52app.use('/api/research', researchRoutes)
53app.use('/api/agents', agentRoutes)
54app.use('/api/documents', documentRoutes)
55app.use('/api/time-entries', timeEntryRoutes)
56app.use('/api/api-keys', apiKeyRoutes)
57app.use('/api/admin', adminRoutes)
58app.use('/api/clio', clioRoutes)
59
60app.post('/api/contact', async (req: any, res: any) => {
61 const { name, email, message } = req.body
62 if (!name || !email || !message) return res.status(400).json({ error: 'All fields required' })
63 try {
64 const { createClient } = await import('@supabase/supabase-js')
65 const sb = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_ROLE_KEY!)
66 await sb.from('contact_messages').insert({ name, email, message })
67 res.json({ sent: true })
68 } catch {
69 res.status(500).json({ error: 'Could not send' })
70 }
71})
72
73app.use(errorHandler)
74
75const server = app.listen(PORT, () => {
76 console.log(`
77 ⚖️ Law OSS API
78 ─────────────────────
79 Local: http://localhost:${PORT}
80 Health: http://localhost:${PORT}/health
81 Env: ${process.env.NODE_ENV || 'development'}
82 Build: ${new Date().toISOString().slice(0,10)}
83 `)
84})
85server.timeout = 300000
86server.keepAliveTimeout = 300000
87
88process.on('uncaughtException', (err) => {
89 console.error('UNCAUGHT EXCEPTION:', err)
90})
91process.on('unhandledRejection', (reason) => {
92 console.error('UNHANDLED REJECTION:', reason)
93})
94
95export default app
96