-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
1 lines (1 loc) · 2.4 KB
/
server.js
File metadata and controls
1 lines (1 loc) · 2.4 KB
1
import express from "express";import Database from "better-sqlite3";import {randomBytes} from "crypto";import {join,dirname} from "path";import {fileURLToPath} from "url";import helmet from "helmet";import cors from "cors";const __filename=fileURLToPath(import.meta.url);const __dirname=dirname(__filename);const PORT=process.env.EVERCLAW_API_PORT||3000;const DB_PATH=process.env.EVERCLAW_DB_PATH||join(__dirname,"data","keys.db");const SECRET=process.env.EVERCLAW_ADMIN_SECRET;const db=new Database(DB_PATH);db.exec("CREATE TABLE IF NOT EXISTS keys(id INTEGER PRIMARY KEY,api_key TEXT UNIQUE,device_fingerprint TEXT UNIQUE,everclaw_version TEXT,created_at DATETIME DEFAULT CURRENT_TIMESTAMP,expires_at DATETIME NOT NULL,last_renewed_at DATETIME,request_count_today INTEGER DEFAULT 0,request_count_total INTEGER DEFAULT 0,last_request_at DATETIME,last_reset_at DATETIME,rate_limit_daily INTEGER DEFAULT 1000,is_revoked BOOLEAN DEFAULT 0,revoke_reason TEXT)");const app=express();app.use(helmet());app.use(cors());app.use(express.json());const genKey=()=>"evcl_"+randomBytes(16).toString("hex");const exp=()=>new Date(Date.now()+30*24*60*60*1000).toISOString();app.get("/health",(r,s)=>s.json({status:"ok"}));app.post("/api/keys/request",(r,s)=>{const{device_fingerprint:f,everclaw_version:v}=r.body;if(!f)return s.status(400).json({error:"missing fingerprint"});let k=db.prepare("SELECT*FROM keys WHERE device_fingerprint=?").get(f);if(k){if(k.is_revoked)return s.status(403).json({error:"revoked"});if(new Date(k.expires_at)<new Date()){db.prepare("UPDATE keys SET expires_at=?,last_renewed_at=CURRENT_TIMESTAMP WHERE id=?").run(exp(),k.id);k=db.prepare("SELECT*FROM keys WHERE id=?").get(k.id)}return s.json({api_key:k.api_key,expires_at:k.expires_at,rate_limit:{daily:k.rate_limit_daily,remaining:k.rate_limit_daily-k.request_count_today}})}const key=genKey();db.prepare("INSERT INTO keys(api_key,device_fingerprint,everclaw_version,expires_at)VALUES(?,?,?,?)").run(key,f,v||null,exp());console.log("[ISSUE]",key.substring(0,12));s.status(201).json({api_key:key,expires_at:exp(),rate_limit:{daily:1000,remaining:1000}})});app.get("/api/stats",(r,s)=>{if(!SECRET||r.headers["x-admin-secret"]!==SECRET)return s.status(401).json({error:"unauthorized"});const stats=db.prepare("SELECT COUNT(*)as total,SUM(CASE WHEN is_revoked=0 THEN 1 ELSE 0 END)as active FROM keys").get();s.json(stats)});app.listen(PORT,()=>console.log("EverClaw Key API on port "+PORT));