2026-04-11 21:33:04 +08:00
|
|
|
import Koa from 'koa'
|
|
|
|
|
import cors from '@koa/cors'
|
|
|
|
|
import bodyParser from '@koa/bodyparser'
|
|
|
|
|
import serve from 'koa-static'
|
|
|
|
|
import send from 'koa-send'
|
2026-04-20 20:37:32 +08:00
|
|
|
import os from 'os'
|
2026-04-11 21:33:04 +08:00
|
|
|
import { resolve } from 'path'
|
|
|
|
|
import { mkdir } from 'fs/promises'
|
|
|
|
|
import { config } from './config'
|
2026-04-16 08:38:18 +08:00
|
|
|
import { hermesRoutes, setupTerminalWebSocket, proxyMiddleware } from './routes/hermes'
|
2026-04-11 21:33:04 +08:00
|
|
|
import { uploadRoutes } from './routes/upload'
|
|
|
|
|
import { webhookRoutes } from './routes/webhook'
|
2026-04-20 20:37:32 +08:00
|
|
|
import { updateRoutes } from './routes/update'
|
|
|
|
|
import { healthRoutes, startVersionCheck } from './routes/health'
|
2026-04-14 21:48:53 +08:00
|
|
|
import { getToken, authMiddleware } from './services/auth'
|
2026-04-20 20:37:32 +08:00
|
|
|
import { initGatewayManager } from './services/gateway-bootstrap'
|
|
|
|
|
import { bindShutdown } from './services/shutdown'
|
2026-04-11 21:33:04 +08:00
|
|
|
|
2026-04-14 20:17:12 +08:00
|
|
|
let server: any = null
|
|
|
|
|
|
2026-04-11 21:33:04 +08:00
|
|
|
export async function bootstrap() {
|
|
|
|
|
await mkdir(config.uploadDir, { recursive: true })
|
|
|
|
|
await mkdir(config.dataDir, { recursive: true })
|
2026-04-14 21:48:53 +08:00
|
|
|
|
|
|
|
|
const authToken = await getToken()
|
2026-04-20 20:37:32 +08:00
|
|
|
const app = new Koa()
|
|
|
|
|
|
2026-04-14 21:48:53 +08:00
|
|
|
if (authToken) {
|
|
|
|
|
app.use(await authMiddleware(authToken))
|
|
|
|
|
console.log(`🔐 Auth enabled — token: ${authToken}`)
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-18 13:07:12 +08:00
|
|
|
await initGatewayManager()
|
2026-04-11 21:33:04 +08:00
|
|
|
app.use(cors({ origin: config.corsOrigins }))
|
|
|
|
|
app.use(bodyParser())
|
|
|
|
|
|
2026-04-20 20:37:32 +08:00
|
|
|
// Shared routes (no agent prefix)
|
2026-04-11 21:33:04 +08:00
|
|
|
app.use(webhookRoutes.routes())
|
|
|
|
|
app.use(uploadRoutes.routes())
|
2026-04-20 20:37:32 +08:00
|
|
|
app.use(updateRoutes.routes())
|
2026-04-16 13:51:42 +08:00
|
|
|
|
2026-04-20 20:37:32 +08:00
|
|
|
// Hermes routes (must be after update — proxy catch-all matches everything)
|
2026-04-16 08:38:18 +08:00
|
|
|
app.use(hermesRoutes.routes())
|
|
|
|
|
app.use(proxyMiddleware)
|
2026-04-11 21:33:04 +08:00
|
|
|
|
2026-04-20 20:37:32 +08:00
|
|
|
// Health check
|
|
|
|
|
app.use(healthRoutes.routes())
|
2026-04-13 20:08:32 +08:00
|
|
|
|
2026-04-20 20:37:32 +08:00
|
|
|
// SPA fallback
|
2026-04-16 08:38:18 +08:00
|
|
|
const distDir = resolve(__dirname, '..', 'client')
|
2026-04-11 21:33:04 +08:00
|
|
|
app.use(serve(distDir))
|
|
|
|
|
app.use(async (ctx) => {
|
2026-04-14 20:17:12 +08:00
|
|
|
if (!ctx.path.startsWith('/api') &&
|
|
|
|
|
ctx.path !== '/health' &&
|
|
|
|
|
ctx.path !== '/upload' &&
|
|
|
|
|
ctx.path !== '/webhook') {
|
2026-04-11 21:33:04 +08:00
|
|
|
await send(ctx, 'index.html', { root: distDir })
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2026-04-20 20:37:32 +08:00
|
|
|
// Start server
|
2026-04-14 20:17:12 +08:00
|
|
|
server = app.listen(config.port, '0.0.0.0')
|
|
|
|
|
|
2026-04-15 16:36:04 +08:00
|
|
|
setupTerminalWebSocket(server)
|
|
|
|
|
|
2026-04-14 20:17:12 +08:00
|
|
|
server.on('listening', () => {
|
2026-04-20 20:37:32 +08:00
|
|
|
const interfaces = os.networkInterfaces()
|
|
|
|
|
const localIp = Object.values(interfaces).flat().find(i => i?.family === 'IPv4' && !i?.internal)?.address || 'localhost'
|
|
|
|
|
console.log(`➜ Server: http://localhost:${config.port} (LAN: http://${localIp}:${config.port})`)
|
2026-04-14 20:17:12 +08:00
|
|
|
console.log(`➜ Upstream: ${config.upstream}`)
|
2026-04-11 21:33:04 +08:00
|
|
|
})
|
2026-04-14 20:17:12 +08:00
|
|
|
|
|
|
|
|
server.on('error', (err: any) => {
|
|
|
|
|
console.error('Server error:', err.message)
|
|
|
|
|
})
|
|
|
|
|
|
2026-04-20 20:37:32 +08:00
|
|
|
bindShutdown(server)
|
|
|
|
|
startVersionCheck()
|
2026-04-13 20:08:32 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-17 03:13:24 +08:00
|
|
|
bootstrap()
|