auto-save 2026-04-19 21:01 (+3, ~2)
This commit is contained in:
18
orchestrator/src/auth.ts
Normal file
18
orchestrator/src/auth.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { MiddlewareHandler } from 'hono';
|
||||
import { env } from './env.ts';
|
||||
|
||||
// 常量时间比较,防 timing attack
|
||||
const safeEqual = (a: string, b: string): boolean => {
|
||||
if (a.length !== b.length) return false;
|
||||
let diff = 0;
|
||||
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
||||
return diff === 0;
|
||||
};
|
||||
|
||||
export const authMiddleware: MiddlewareHandler = async (c, next) => {
|
||||
const header = c.req.header('X-Sandbox-Secret');
|
||||
if (!header || !safeEqual(header, env.orchSecret)) {
|
||||
return c.json({ error: 'unauthorized' }, 401);
|
||||
}
|
||||
await next();
|
||||
};
|
||||
59
orchestrator/src/state.ts
Normal file
59
orchestrator/src/state.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { Database } from 'bun:sqlite';
|
||||
import { mkdir } from 'node:fs/promises';
|
||||
import { dirname } from 'node:path';
|
||||
import { env } from './env.ts';
|
||||
|
||||
await mkdir(dirname(env.stateDbPath), { recursive: true });
|
||||
const db = new Database(env.stateDbPath, { create: true });
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
user_id TEXT PRIMARY KEY,
|
||||
created_at INTEGER NOT NULL,
|
||||
provisioned_at INTEGER,
|
||||
deleted_at INTEGER
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS activity (
|
||||
user_id TEXT PRIMARY KEY,
|
||||
last_used INTEGER NOT NULL
|
||||
);
|
||||
`);
|
||||
|
||||
const now = () => Date.now();
|
||||
|
||||
export const state = {
|
||||
recordCreate: (userId: string): void => {
|
||||
db.prepare(
|
||||
`INSERT INTO users (user_id, created_at) VALUES (?, ?)
|
||||
ON CONFLICT(user_id) DO UPDATE SET deleted_at = NULL`,
|
||||
).run(userId, now());
|
||||
},
|
||||
|
||||
markProvisioned: (userId: string): void => {
|
||||
db.prepare(`UPDATE users SET provisioned_at = ? WHERE user_id = ?`).run(now(), userId);
|
||||
},
|
||||
|
||||
markDeleted: (userId: string): void => {
|
||||
db.prepare(`UPDATE users SET deleted_at = ? WHERE user_id = ?`).run(now(), userId);
|
||||
db.prepare(`DELETE FROM activity WHERE user_id = ?`).run(userId);
|
||||
},
|
||||
|
||||
touch: (userId: string): void => {
|
||||
db.prepare(
|
||||
`INSERT INTO activity (user_id, last_used) VALUES (?, ?)
|
||||
ON CONFLICT(user_id) DO UPDATE SET last_used = excluded.last_used`,
|
||||
).run(userId, now());
|
||||
},
|
||||
|
||||
// 查找过期:最后使用时间距今 > timeoutMs
|
||||
findIdle: (timeoutMs: number): string[] => {
|
||||
const cutoff = now() - timeoutMs;
|
||||
return db
|
||||
.prepare(`SELECT user_id FROM activity WHERE last_used < ?`)
|
||||
.all(cutoff)
|
||||
.map((r: any) => r.user_id);
|
||||
},
|
||||
|
||||
clearActivity: (userId: string): void => {
|
||||
db.prepare(`DELETE FROM activity WHERE user_id = ?`).run(userId);
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user