Spaces:
Runtime error
Runtime error
File size: 3,628 Bytes
e3b7b69 489d61b 5e0dec1 0dbdd4f 489d61b e481638 0d611fc e481638 e3b7b69 e481638 e3b7b69 d917c4e e3b7b69 d917c4e 5e0dec1 e481638 5e0dec1 b419e9c 0dbdd4f e3b7b69 0d611fc 07c37a1 e3b7b69 489d61b 666d9c1 489d61b e3b7b69 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | import 'dotenv/config'
import express from 'express'
import type { Request, Response, NextFunction } from 'express'
import path from 'path'
import { auth } from './middleware/auth.js'
import { logger } from './logger.js'
import { authRouter } from './services/auth/auth.routes'
import { chatRouter } from './services/chat/chat.routes'
import { expenseRouter } from './services/expense/router'
import { workspaceMembersRouter } from './services/expense/workspace-members/workspace-members.routes'
import { slipQueueRouter } from './services/expense/slip-queue/slip-queue.routes'
import { publicRouter } from './services/expense/public/public.routes'
import { healthRouter } from './health/health.routes'
import { runMigrations } from './services/expense/db/migrate.js'
import { seedDefaults } from './services/expense/db/seed.js'
const app = express()
app.use(express.json({ limit: '50mb' }))
app.use(express.urlencoded({ limit: '50mb', extended: true }))
app.use(express.raw({ limit: '50mb', type: ['image/*', 'application/octet-stream'] }))
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS')
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, x-internal-key')
if (req.method === 'OPTIONS') { res.sendStatus(204); return }
next()
})
app.use(authRouter) // /auth/login is public
// Serve built dashboard — no auth needed for static assets or SPA routes
const dashboardDist = path.join(__dirname, '../../web/dist')
app.use(express.static(dashboardDist))
app.get(/^\/(?!v1\/|auth\/|health)/, (_req, res) => {
res.sendFile(path.join(dashboardDist, 'index.html'))
})
app.use('/v1/public', publicRouter) // token-scoped, no auth — the token itself is the access control
app.use(auth) // all API routes require auth (internal-secret bypass, else JWT via auth-client)
app.use(healthRouter)
app.use('/v1', chatRouter)
app.use('/v1/expense', expenseRouter)
app.use('/v1/workspaces', workspaceMembersRouter)
app.use('/v1/slip-queue', slipQueueRouter)
app.use((err: unknown, req: Request, res: Response, _next: NextFunction) => {
logger.error('unhandled_route_error', {
method: req.method,
path: req.path,
error: err instanceof Error ? err.message : String(err),
cause: err instanceof Error && err.cause instanceof Error ? err.cause.message : undefined,
stack: err instanceof Error ? err.stack : undefined,
})
if (res.headersSent) return
res.status(500).json({ error: 'Internal server error' })
})
const PORT = parseInt(process.env['PORT'] ?? '7860', 10)
const FOUR_MINUTES = 4 * 60 * 1000
async function keepAlive(): Promise<void> {
const services = [
`http://localhost:${PORT}/health`,
`${process.env['OLLAMA_HOST'] ?? ''}/api/tags`,
]
for (const url of services) {
try {
const pingRes = await fetch(url, {
headers: { 'x-internal-key': process.env['INTERNAL_SECRET'] ?? '' },
signal: AbortSignal.timeout(5000),
})
console.log('keepalive_ping', { url, status: pingRes.status })
} catch (err: unknown) {
console.warn('keepalive_failed', { url, error: err instanceof Error ? err.message : String(err) })
}
}
}
setInterval(() => void keepAlive(), FOUR_MINUTES)
void keepAlive()
async function start(): Promise<void> {
await runMigrations()
await seedDefaults()
app.listen(PORT, '0.0.0.0', () => console.log(`Proxy running on port ${PORT}`))
}
start().catch((err: unknown) => {
console.error('Failed to start server:', err)
process.exit(1)
})
|