Run computers with resources and health checks, add settings for bots, workspaces, computers and users
- Computers get CPUs and memory (default 1 CPU, 2 GB), set at creation and editable; new resources stop, remove and recreate the container, its home dir kept. Every computer container has a docker health check and stays "creating" until it passes; only healthy computers are reached, by hostname, else by their IP on the network (dev outside docker). - Docker is required: the app creates the cobble network on startup and exits without docker. - Production container: deploy/run-app-container.sh mounts .env, the data dir and the docker socket; the database (DB_DIR), media, computers' homes and bun-sqlite's live schema now live on the data volume. - Settings pages for bots, workspaces, computers and users, sharing list components; bots can be deleted; users are created with a password and confirmation, edited and deleted through RBAC-gated APIs. - User types trimmed to super_admin, admin, member, read_only and revoked; admins manage members. UserAvatar shows profile photos from the user row. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -1,23 +1,21 @@
|
||||
import bunext from "@moduletrace/bunext";
|
||||
import BunSQLite from "@moduletrace/bun-sqlite";
|
||||
import type { BUN_SQLITE_COBBLE_COMPUTERS, BUN_SQLITE_COBBLE_CONTAINERS } from "@/db/types/db";
|
||||
import pushComputerUpdate from "@/src/functions/backend/db/computers/push-computer-update";
|
||||
import inspectDockerContainer from "@/src/functions/backend/docker/inspect-docker-container";
|
||||
import type { BUN_SQLITE_COBBLE_CONTAINERS } from "@/db/types/db";
|
||||
import refreshComputerContainer from "@/src/functions/backend/db/computers/refresh-computer-container";
|
||||
|
||||
type Row = Pick<BUN_SQLITE_COBBLE_CONTAINERS, "id" | "docker_id" | "status"> &
|
||||
Pick<BUN_SQLITE_COBBLE_COMPUTERS, "user_id"> & { computer_id: number };
|
||||
type Row = Pick<BUN_SQLITE_COBBLE_CONTAINERS, "id" | "docker_id" | "status" | "ip_address"> &
|
||||
{ computer_id: number };
|
||||
|
||||
/**
|
||||
* Reads every computer container's state from docker and records it
|
||||
* (`containers.status`, `last_seen_at`), telling owners about changes. A
|
||||
* container docker no longer has shows as `dead`. Containers still being
|
||||
* set up, or that failed, are left alone.
|
||||
* (`refreshComputerContainer`), telling owners about changes. Containers
|
||||
* still being set up, or that failed, are left alone.
|
||||
*/
|
||||
export default async function syncComputers() {
|
||||
const rows =
|
||||
(
|
||||
await BunSQLite.sql<Row>({
|
||||
sql: `SELECT co.id, co.docker_id, co.status, c.user_id, c.id AS computer_id
|
||||
sql: `SELECT co.id, co.docker_id, co.status, co.ip_address, c.id AS computer_id
|
||||
FROM containers co JOIN computers c ON c.container_id = co.id
|
||||
WHERE co.docker_id IS NOT NULL AND co.status NOT IN ('creating', 'failed')`,
|
||||
})
|
||||
@@ -25,22 +23,10 @@ export default async function syncComputers() {
|
||||
|
||||
let changed = 0;
|
||||
|
||||
for (const row of rows) {
|
||||
const state = await inspectDockerContainer({ container: row.docker_id! }).catch(() => undefined);
|
||||
for (const { computer_id, ...container } of rows) {
|
||||
const refreshed = await refreshComputerContainer({ computer_id, container });
|
||||
|
||||
if (state === undefined) continue;
|
||||
|
||||
const status = state?.status || "dead";
|
||||
|
||||
await BunSQLite.sql({
|
||||
sql: `UPDATE containers SET status = ?, last_seen_at = ?, updated_at = ? WHERE id = ?`,
|
||||
values: [status, Date.now(), Date.now(), row.id!],
|
||||
});
|
||||
|
||||
if (status != row.status) {
|
||||
changed++;
|
||||
await pushComputerUpdate({ computer_id: row.computer_id }).catch(() => {});
|
||||
}
|
||||
if (refreshed?.changed) changed++;
|
||||
}
|
||||
|
||||
bunext.bunextLog.info(`Cron: ${rows.length} computer(s) checked, ${changed} changed`);
|
||||
|
||||
Reference in New Issue
Block a user