auto-save 2026-04-19 21:01 (+3, ~2)

This commit is contained in:
2026-04-19 21:01:27 +08:00
parent eea42305fe
commit 1b8a216fbd
5 changed files with 139 additions and 2 deletions

18
orchestrator/src/auth.ts Normal file
View 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
View 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);
},
};