Files
ChatLab/electron/main/api/index.ts
T
2026-03-28 00:10:29 +08:00

149 lines
3.3 KiB
TypeScript

/**
* ChatLab API — Server manager
* Manages fastify server lifecycle
*/
import type { FastifyInstance } from 'fastify'
import { createServer } from './server'
import { loadConfig, saveConfig, ensureToken, type ApiServerConfig } from './config'
import { registerSystemRoutes } from './routes/system'
import { registerSessionRoutes } from './routes/sessions'
import { registerImportRoutes } from './routes/import'
let server: FastifyInstance | null = null
let startedAt: number | null = null
let lastError: string | null = null
export interface ApiServerStatus {
running: boolean
port: number | null
startedAt: number | null
error: string | null
}
export function getStatus(): ApiServerStatus {
return {
running: server !== null && startedAt !== null,
port: server !== null && startedAt !== null ? loadConfig().port : null,
startedAt,
error: lastError,
}
}
export async function start(): Promise<void> {
if (server) {
console.log('[ChatLab API] Server already running')
return
}
const config = loadConfig()
ensureToken(config)
lastError = null
try {
server = createServer()
registerSystemRoutes(server)
registerSessionRoutes(server)
registerImportRoutes(server)
await server.listen({ port: config.port, host: '127.0.0.1' })
startedAt = Math.floor(Date.now() / 1000)
console.log(`[ChatLab API] Server started on http://127.0.0.1:${config.port}`)
} catch (err: any) {
server = null
startedAt = null
if (err.code === 'EADDRINUSE') {
lastError = `PORT_IN_USE:${config.port}`
console.warn(`[ChatLab API] Port ${config.port} is already in use`)
} else {
lastError = err.message || 'Unknown error'
console.error('[ChatLab API] Failed to start:', err)
}
throw err
}
}
export async function stop(): Promise<void> {
if (!server) return
try {
await server.close()
} catch (err) {
console.error('[ChatLab API] Error closing server:', err)
} finally {
server = null
startedAt = null
lastError = null
console.log('[ChatLab API] Server stopped')
}
}
export async function restart(): Promise<void> {
await stop()
await start()
}
/**
* Auto-restore on app startup: attempt to start if config.enabled is true.
* Failures are silently recorded (does not affect normal app usage).
*/
export async function autoStart(): Promise<void> {
const config = loadConfig()
if (!config.enabled) return
try {
await start()
} catch {
// silent failure, lastError already recorded
}
}
/**
* Set enabled state (persisted)
*/
export async function setEnabled(enabled: boolean): Promise<ApiServerStatus> {
const config = loadConfig()
config.enabled = enabled
saveConfig(config)
if (enabled) {
ensureToken(config)
try {
await start()
} catch {
// lastError already recorded
}
} else {
await stop()
}
return getStatus()
}
/**
* Set port (persisted, requires server restart)
*/
export async function setPort(port: number): Promise<ApiServerStatus> {
const config = loadConfig()
const wasRunning = server !== null
config.port = port
saveConfig(config)
if (wasRunning) {
await stop()
try {
await start()
} catch {
// lastError already recorded
}
}
return getStatus()
}
export function getConfig(): ApiServerConfig {
return loadConfig()
}