Download server.js from bingn/ikun2: direct link, hf CLI and curl.
- Browser
- Download file 7.22 kB
-
https://huggingface.co/spaces/bingn/ikun2/resolve/main/server.js
- Command line
-
hf download hf://spaces/bingn/ikun2/server.js
-
curl -L -o server.js https://huggingface.co/spaces/bingn/ikun2/resolve/main/server.js
7.22 kB
| /** | |
| * server.js - API ไปฃ็ๆๅกไธปๅ ฅๅฃ | |
| * | |
| * ๅฏนๅคๆด้ฒ OpenAI / Anthropic ๅ ผๅฎน็ API ็ซฏ็น | |
| * ๅฏนๅ ้่ฟ่ดฆๅทๆฑ ่ฝฎๆขไฝฟ็จ chataibot.pro ่ฏ็จ้ขๅบฆ | |
| * | |
| * ๅฏๅจ: node server.js | |
| * ็ซฏๅฃ: config.server.port (้ป่ฎค 9090) | |
| */ | |
| import http from 'http'; | |
| import config from './config.js'; | |
| import { AccountPool } from './pool.js'; | |
| import { handleChatCompletions } from './openai-adapter.js'; | |
| import { handleMessages } from './anthropic-adapter.js'; | |
| import { getModelList } from './message-convert.js'; | |
| import { getDashboardHTML } from './ui.js'; | |
| // ==================== ๅๅงๅ ==================== | |
| const pool = new AccountPool(); | |
| await pool.init(); | |
| // ==================== HTTP ๆๅกๅจ ==================== | |
| function parseBody(req, maxSize = 2 * 1024 * 1024) { // ้ป่ฎค 2MB | |
| return new Promise((resolve, reject) => { | |
| let data = ''; | |
| let size = 0; | |
| req.on('data', chunk => { | |
| size += chunk.length; | |
| if (size > maxSize) { | |
| req.destroy(); | |
| reject(new Error('Request body too large')); | |
| return; | |
| } | |
| data += chunk; | |
| }); | |
| req.on('end', () => { | |
| try { resolve(JSON.parse(data)); } | |
| catch { reject(new Error('Invalid JSON body')); } | |
| }); | |
| req.on('error', reject); | |
| }); | |
| } | |
| function sendJson(res, status, data) { | |
| res.writeHead(status, { | |
| 'Content-Type': 'application/json', | |
| 'Access-Control-Allow-Origin': '*', | |
| }); | |
| res.end(JSON.stringify(data)); | |
| } | |
| function sendError(res, status, message) { | |
| sendJson(res, status, { | |
| error: { message, type: 'invalid_request_error', code: status }, | |
| }); | |
| } | |
| function sendHtml(res, html) { | |
| res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' }); | |
| res.end(html); | |
| } | |
| function checkAuth(req) { | |
| if (!config.server.apiKey) return true; | |
| // Bearer token | |
| const auth = req.headers['authorization'] || ''; | |
| if (auth.startsWith('Bearer ') && auth.slice(7) === config.server.apiKey) return true; | |
| // x-api-key header (Anthropic ้ฃๆ ผ) | |
| if (req.headers['x-api-key'] === config.server.apiKey) return true; | |
| return false; | |
| } | |
| const server = http.createServer(async (req, res) => { | |
| const url = new URL(req.url, `http://${req.headers.host}`); | |
| const pathname = url.pathname; | |
| const method = req.method; | |
| // CORS ้ขๆฃ | |
| if (method === 'OPTIONS') { | |
| res.writeHead(204, { | |
| 'Access-Control-Allow-Origin': '*', | |
| 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', | |
| 'Access-Control-Allow-Headers': 'Content-Type, Authorization, x-api-key, anthropic-version', | |
| 'Access-Control-Max-Age': '86400', | |
| }); | |
| res.end(); | |
| return; | |
| } | |
| // ๅฅๅบทๆฃๆฅ (ไธ้่ฆ่ฎค่ฏ) | |
| if (method === 'GET' && pathname === '/health') { | |
| sendJson(res, 200, { status: 'ok', uptime: process.uptime() }); | |
| return; | |
| } | |
| // Dashboard ๆงๅถๅฐ | |
| if (method === 'GET' && (pathname === '/' || pathname === '/ui')) { | |
| sendHtml(res, getDashboardHTML()); | |
| return; | |
| } | |
| // ๆจกๅๅ่กจ (Dashboard ้่ฆๅ ่ฎค่ฏ่ฎฟ้ฎ) | |
| if (method === 'GET' && pathname === '/v1/models') { | |
| sendJson(res, 200, { object: 'list', data: getModelList() }); | |
| return; | |
| } | |
| // ่ดฆๅทๆฑ ็ถๆ (Dashboard ้่ฆๅ ่ฎค่ฏ่ฎฟ้ฎ) | |
| if (method === 'GET' && pathname === '/pool/status') { | |
| sendJson(res, 200, pool.getStatus()); | |
| return; | |
| } | |
| // ๆณจๅๆฅๅฟ (Dashboard ้่ฆๅ ่ฎค่ฏ่ฎฟ้ฎ) | |
| if (method === 'GET' && pathname === '/pool/logs') { | |
| const since = parseInt(url.searchParams.get('since')) || 0; | |
| sendJson(res, 200, { logs: pool.getLogs(since) }); | |
| return; | |
| } | |
| // ๆๅจๆณจๅ่ดฆๅท (Dashboard ่ฐ็จ) | |
| if (method === 'POST' && pathname === '/pool/register') { | |
| try { | |
| const body = await parseBody(req); | |
| const count = Math.min(Math.max(1, body.count || 5), 50); | |
| const provider = body.provider || undefined; | |
| const concurrency = body.concurrency ? Math.min(Math.max(1, body.concurrency), 20) : undefined; | |
| const result = pool.manualRegister(count, provider, concurrency); | |
| sendJson(res, 200, { ok: true, message: `ๅทฒๆไบค ${result.queued} ไธชๆณจๅไปปๅก`, ...result }); | |
| } catch (e) { | |
| sendJson(res, 200, { ok: false, message: e.message }); | |
| } | |
| return; | |
| } | |
| // ่ฎค่ฏๆ ก้ช | |
| if (!checkAuth(req)) { | |
| sendError(res, 401, 'Invalid API key'); | |
| return; | |
| } | |
| try { | |
| // ==================== ่ทฏ็ฑ ==================== | |
| // OpenAI: POST /v1/chat/completions | |
| if (method === 'POST' && pathname === '/v1/chat/completions') { | |
| const body = await parseBody(req); | |
| await handleChatCompletions(body, res, pool); | |
| return; | |
| } | |
| // Anthropic: POST /v1/messages | |
| if (method === 'POST' && pathname === '/v1/messages') { | |
| const body = await parseBody(req); | |
| await handleMessages(body, res, pool); | |
| return; | |
| } | |
| // 404 | |
| sendError(res, 404, `Not found: ${method} ${pathname}`); | |
| } catch (e) { | |
| console.error(`[Server] ่ฏทๆฑ้่ฏฏ: ${e.message}`); | |
| if (!res.headersSent) { | |
| sendError(res, 500, 'Internal server error'); | |
| } | |
| } | |
| }); | |
| // ==================== ๅฏๅจ ==================== | |
| const { port, host } = config.server; | |
| server.listen(port, host, () => { | |
| console.log(`\n โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ`); | |
| console.log(` โ ChatAIBot API Proxy โ`); | |
| console.log(` โ http://${host}:${port} โ`); | |
| console.log(` โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค`); | |
| console.log(` โ Dashboard: http://${host}:${port}/ โ`); | |
| console.log(` โ OpenAI: POST /v1/chat/completions โ`); | |
| console.log(` โ Anthropic: POST /v1/messages โ`); | |
| console.log(` โ Models: GET /v1/models โ`); | |
| console.log(` โ Pool: GET /pool/status โ`); | |
| console.log(` โ Health: GET /health โ`); | |
| console.log(` โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค`); | |
| console.log(` โ Auth: ${config.server.apiKey ? 'Bearer ' + config.server.apiKey.substring(0, 8) + '...' : 'ๆ (ๅผๆพ่ฎฟ้ฎ)'}${' '.repeat(Math.max(0, 24 - (config.server.apiKey ? 16 : 13)))}โ`); | |
| console.log(` โ Pool: ${pool.getStatus().active} active / ${pool.getStatus().total} total${' '.repeat(Math.max(0, 22 - String(pool.getStatus().active).length - String(pool.getStatus().total).length))}โ`); | |
| console.log(` โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n`); | |
| }); | |
| // ไผ้ ้ๅบ | |
| process.on('SIGINT', () => { | |
| console.log('\n[Server] ๆญฃๅจๅ ณ้ญ...'); | |
| pool.destroy(); | |
| server.close(() => process.exit(0)); | |
| }); | |
| process.on('SIGTERM', () => { | |
| pool.destroy(); | |
| server.close(() => process.exit(0)); | |
| }); | |