()
| 505 | } |
| 506 | |
| 507 | export function startServer() { |
| 508 | const activeRequests = new Set(); |
| 509 | const bindHost = config.host || '0.0.0.0'; |
| 510 | configureBindHost(bindHost); |
| 511 | emitNoAuthWarnings(bindHost); |
| 512 | |
| 513 | const server = http.createServer(async (req, res) => { |
| 514 | activeRequests.add(res); |
| 515 | res.on('close', () => activeRequests.delete(res)); |
| 516 | try { |
| 517 | await route(req, res); |
| 518 | } catch (err) { |
| 519 | log.error('Handler error:', err); |
| 520 | if (!res.headersSent) json(res, 500, { error: { message: 'Internal error', type: 'server_error' } }); |
| 521 | } |
| 522 | }); |
| 523 | |
| 524 | server.keepAliveTimeout = 65_000; |
| 525 | server.headersTimeout = 66_000; |
| 526 | |
| 527 | let retryCount = 0; |
| 528 | const maxRetries = 10; |
| 529 | |
| 530 | server.on('error', (err) => { |
| 531 | if (err.code === 'EADDRINUSE') { |
| 532 | retryCount++; |
| 533 | if (retryCount > maxRetries) { |
| 534 | log.error(`Port ${config.port} still in use after ${maxRetries} retries. Exiting.`); |
| 535 | process.exit(1); |
| 536 | } |
| 537 | log.warn(`Port ${config.port} in use, retry ${retryCount}/${maxRetries} in 3s...`); |
| 538 | setTimeout(() => server.listen(config.port, bindHost), 3000); |
| 539 | } else { |
| 540 | log.error('Server error:', err); |
| 541 | } |
| 542 | }); |
| 543 | |
| 544 | server.getActiveRequests = () => activeRequests.size; |
| 545 | |
| 546 | server.listen({ port: config.port, host: bindHost }, () => { |
| 547 | log.info(`Server on http://${bindHost}:${config.port}`); |
| 548 | log.info(' POST /v1/chat/completions'); |
| 549 | log.info(' POST /v1/responses'); |
| 550 | log.info(' GET /v1/models'); |
| 551 | log.info(' POST /auth/login (add account)'); |
| 552 | log.info(' GET /auth/accounts (list accounts)'); |
| 553 | log.info(' DELETE /auth/accounts/:id (remove account)'); |
| 554 | }); |
| 555 | return server; |
| 556 | } |
no test coverage detected