auto-save 2026-04-19 21:01 (+3, ~2)
This commit is contained in:
@@ -132,6 +132,13 @@
|
||||
"message": "auto-save 2026-04-19 18:14 (~1)",
|
||||
"hash": "867ca6f",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-19T20:27:26+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-19 20:15 (~1)",
|
||||
"hash": "eea4230",
|
||||
"files_changed": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -54,8 +54,8 @@ incus exec "$BUILDER" --project "$PROJECT" -- bash -c '
|
||||
|
||||
log "Installing uv"
|
||||
incus exec "$BUILDER" --project "$PROJECT" -- bash -c '
|
||||
curl -LsSf https://astral.sh/uv/install.sh | env INSTALL_DIR=/usr/local/bin UV_UNMANAGED_INSTALL=1 sh
|
||||
ls -la /usr/local/bin/uv
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh -s -- --no-modify-path --install-dir /usr/local/bin
|
||||
ls -la /usr/local/bin/uv /usr/local/bin/uvx
|
||||
'
|
||||
|
||||
log "Installing bun"
|
||||
|
||||
53
images/base/patch-uv.sh
Normal file
53
images/base/patch-uv.sh
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
# Patch existing lobe-sandbox-base image with correctly-installed uv.
|
||||
# One-shot fix for the UV_INSTALL_DIR mistake in initial build.
|
||||
set -euo pipefail
|
||||
|
||||
PROJECT="lobe-sandbox"
|
||||
BUILDER="sb-patch"
|
||||
IMAGE="lobe-sandbox-base"
|
||||
log() { echo "==> [$(date +%H:%M:%S)] $*"; }
|
||||
|
||||
incus info "$BUILDER" --project "$PROJECT" >/dev/null 2>&1 && \
|
||||
incus delete "$BUILDER" --project "$PROJECT" --force
|
||||
|
||||
log "Launch from base"
|
||||
incus init "$IMAGE" "$BUILDER" --project "$PROJECT" -p sandbox-default
|
||||
incus start "$BUILDER" --project "$PROJECT"
|
||||
sleep 3
|
||||
|
||||
log "Install uv via UV_INSTALL_DIR (correct env var)"
|
||||
incus exec "$BUILDER" --project "$PROJECT" -- bash -c '
|
||||
rm -f /usr/local/bin/uv /usr/local/bin/uvx /root/.local/bin/uv /root/.local/bin/uvx 2>/dev/null || true
|
||||
curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/usr/local/bin UV_NO_MODIFY_PATH=1 sh
|
||||
ls -la /usr/local/bin/uv /usr/local/bin/uvx
|
||||
'
|
||||
|
||||
log "Verify uv as root AND as sandbox user"
|
||||
incus exec "$BUILDER" --project "$PROJECT" -- uv --version
|
||||
incus exec "$BUILDER" --project "$PROJECT" --user 1000 -- uv --version
|
||||
|
||||
log "Realistic LLM flow: create venv, install requests"
|
||||
incus exec "$BUILDER" --project "$PROJECT" --user 1000 --cwd /workspace -- bash -c '
|
||||
uv venv .venv --seed 2>&1 | tail -3
|
||||
uv pip install --python .venv/bin/python requests 2>&1 | tail -3
|
||||
.venv/bin/python -c "import requests; print(\"requests\", requests.__version__)"
|
||||
'
|
||||
|
||||
log "Cleanup cache (shrink image)"
|
||||
incus exec "$BUILDER" --project "$PROJECT" --user 1000 -- rm -rf /home/sandbox/.cache /workspace/.venv
|
||||
incus exec "$BUILDER" --project "$PROJECT" -- bash -c "rm -rf /root/.cache /tmp/* /var/tmp/*"
|
||||
|
||||
log "Stop builder"
|
||||
incus stop "$BUILDER" --project "$PROJECT"
|
||||
|
||||
log "Replace image alias"
|
||||
incus image delete "$IMAGE" --project "$PROJECT" || true
|
||||
incus publish "$BUILDER" --project "$PROJECT" --alias "$IMAGE"
|
||||
|
||||
log "Cleanup patch container"
|
||||
incus delete "$BUILDER" --project "$PROJECT"
|
||||
|
||||
log "Final"
|
||||
incus image list --project "$PROJECT"
|
||||
log "DONE"
|
||||
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