claude went crazy
All checks were successful
Build / test (push) Successful in 9m28s
Build / release (push) Successful in 1s
Build / build (push) Successful in 25s

This commit is contained in:
2026-03-28 02:35:00 -04:00
parent d7d4bbc099
commit 6e40413385
22 changed files with 2167 additions and 787 deletions

182
js/db.js
View File

@@ -1,159 +1,57 @@
let db = null;
// API client — replaces the sql.js database layer.
// Swap these fetch() calls for any other transport when needed.
// ── Persistence ──────────────────────────────────────────────────────────────
const BASE = '/api';
function saveToStorage() {
try {
const data = db.export(); // Uint8Array
let binary = '';
const chunk = 8192;
for (let i = 0; i < data.length; i += chunk) {
binary += String.fromCharCode(...data.subarray(i, i + chunk));
}
localStorage.setItem(STORAGE_KEY, btoa(binary));
} catch (e) {
console.warn('catalyst: failed to persist database', e);
}
async function api(path, options = {}) {
const res = await fetch(BASE + path, options);
if (res.status === 204) return null;
return res.json().then(data => ({ ok: res.ok, status: res.status, data }));
}
function loadFromStorage() {
try {
const stored = localStorage.getItem(STORAGE_KEY);
if (!stored) return null;
const binary = atob(stored);
const buf = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) buf[i] = binary.charCodeAt(i);
return buf;
} catch (e) {
console.warn('catalyst: failed to load database from storage', e);
return null;
}
// ── Queries ───────────────────────────────────────────────────────────────────
async function getInstances(filters = {}) {
const params = new URLSearchParams(
Object.entries(filters).filter(([, v]) => v)
);
const res = await fetch(`${BASE}/instances?${params}`);
return res.json();
}
// ── Init ─────────────────────────────────────────────────────────────────────
async function initDB() {
const SQL = await initSqlJs({ locateFile: f => SQL_JS_CDN + f });
const saved = loadFromStorage();
if (saved) {
db = new SQL.Database(saved);
return;
}
db = new SQL.Database();
db.run(`
CREATE TABLE instances (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
state TEXT DEFAULT 'deployed',
stack TEXT DEFAULT '',
vmid INTEGER UNIQUE NOT NULL,
atlas INTEGER DEFAULT 0,
argus INTEGER DEFAULT 0,
semaphore INTEGER DEFAULT 0,
patchmon INTEGER DEFAULT 0,
tailscale INTEGER DEFAULT 0,
andromeda INTEGER DEFAULT 0,
tailscale_ip TEXT DEFAULT '',
hardware_acceleration INTEGER DEFAULT 0,
createdAt TEXT DEFAULT (datetime('now')),
updatedAt TEXT DEFAULT (datetime('now'))
)
`);
const stmt = db.prepare(`
INSERT INTO instances
(name, state, stack, vmid, atlas, argus, semaphore, patchmon, tailscale, andromeda, tailscale_ip, hardware_acceleration)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
SEED.forEach(s => stmt.run([
s.name, s.state, s.stack, s.vmid,
+s.atlas, +s.argus, +s.semaphore, +s.patchmon,
+s.tailscale, +s.andromeda, s.tailscale_ip, +s.hardware_acceleration,
]));
stmt.free();
saveToStorage();
async function getInstance(vmid) {
const res = await fetch(`${BASE}/instances/${vmid}`);
if (res.status === 404) return null;
return res.json();
}
// ── Queries ──────────────────────────────────────────────────────────────────
function getInstances(filters = {}) {
let sql = 'SELECT * FROM instances WHERE 1=1';
const params = [];
if (filters.search) {
sql += ' AND (name LIKE ? OR CAST(vmid AS TEXT) LIKE ? OR stack LIKE ?)';
const s = `%${filters.search}%`;
params.push(s, s, s);
}
if (filters.state) { sql += ' AND state = ?'; params.push(filters.state); }
if (filters.stack) { sql += ' AND stack = ?'; params.push(filters.stack); }
sql += ' ORDER BY name ASC';
const res = db.exec(sql, params);
if (!res.length) return [];
const cols = res[0].columns;
return res[0].values.map(row => Object.fromEntries(cols.map((c, i) => [c, row[i]])));
}
function getInstance(vmid) {
const res = db.exec('SELECT * FROM instances WHERE vmid = ?', [vmid]);
if (!res.length) return null;
const cols = res[0].columns;
return Object.fromEntries(cols.map((c, i) => [c, res[0].values[0][i]]));
}
function getDistinctStacks() {
const res = db.exec(`SELECT DISTINCT stack FROM instances WHERE stack != '' ORDER BY stack`);
if (!res.length) return [];
return res[0].values.map(row => row[0]);
async function getDistinctStacks() {
const res = await fetch(`${BASE}/instances/stacks`);
return res.json();
}
// ── Mutations ─────────────────────────────────────────────────────────────────
function createInstance(data) {
try {
db.run(
`INSERT INTO instances
(name, state, stack, vmid, atlas, argus, semaphore, patchmon, tailscale, andromeda, tailscale_ip, hardware_acceleration)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[data.name, data.state, data.stack, data.vmid,
data.atlas, data.argus, data.semaphore, data.patchmon,
data.tailscale, data.andromeda, data.tailscale_ip, data.hardware_acceleration]
);
saveToStorage();
return { ok: true };
} catch (e) {
return { ok: false, error: e.message };
}
async function createInstance(data) {
const { ok, data: body } = await api('/instances', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!ok) return { ok: false, error: body.error ?? body.errors?.[0] ?? 'error creating instance' };
return { ok: true };
}
function updateInstance(id, data) {
try {
db.run(
`UPDATE instances SET
name=?, state=?, stack=?, vmid=?,
atlas=?, argus=?, semaphore=?, patchmon=?,
tailscale=?, andromeda=?, tailscale_ip=?, hardware_acceleration=?,
updatedAt=datetime('now')
WHERE id=?`,
[data.name, data.state, data.stack, data.vmid,
data.atlas, data.argus, data.semaphore, data.patchmon,
data.tailscale, data.andromeda, data.tailscale_ip, data.hardware_acceleration,
id]
);
saveToStorage();
return { ok: true };
} catch (e) {
return { ok: false, error: e.message };
}
async function updateInstance(vmid, data) {
const { ok, data: body } = await api(`/instances/${vmid}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!ok) return { ok: false, error: body.error ?? body.errors?.[0] ?? 'error updating instance' };
return { ok: true };
}
function deleteInstance(id) {
db.run('DELETE FROM instances WHERE id = ?', [id]);
saveToStorage();
async function deleteInstance(vmid) {
await api(`/instances/${vmid}`, { method: 'DELETE' });
}