Add a files browser for every folder on the data volume

A bot's deliverables and the raw material behind them only existed as
paths in a message, so they couldn't be opened from the app at all. This
browses them read-only at /admin/files, one volume at a time: a bot's own
folder, a workspace's shared one, a computer's home (where a bot's ~/...
actually is), the media library, an MCP server's install folder and the
database.

Access is the whole gate: resolveStorageSpec checks the viewer against
the volume first (a bot through grabUserBot, a workspace through
grabUserWorkspace, a computer through grabUserComputer, media only their
own unless they manage the crew) and no path is built before it passes.
resolveStoragePath then applies the resolveBotPath rules to any root.
The picker lists folders through reachableBotsSql, so it never offers one
the resolver would refuse.

Reading is listStorageEntries (folders first, paginated, symlinks
skipped, catalog rows joined so a bot file still opens through its media
row) and grabStorageFile for bytes under a cap, both behind an ETag.
This commit is contained in:
2026-10-22 07:37:16 +01:00
parent 07042a2288
commit 956942bba9
26 changed files with 2064 additions and 1 deletions
+89
View File
@@ -0,0 +1,89 @@
import { describe, expect, test } from "bun:test";
import {
StorageBrowserPath,
isSameStorageSpec,
isStorageVolume,
parseStorageSpec,
storagePathName,
storageSpecHref,
storageVolumeFromRef,
storageVolumeLabel,
storageVolumeNeedsOwner,
} from "./storage-volumes";
describe("parseStorageSpec", () => {
test("reads a volume and its owner", () => {
expect(parseStorageSpec({ volume: "computers", owner_id: "3" })).toEqual({
volume: "computers",
owner_id: 3,
});
});
test("gives the database no owner", () => {
expect(parseStorageSpec({ volume: "db", owner_id: "999" })).toEqual({
volume: "db",
owner_id: 0,
});
});
test("refuses an unknown volume", () => {
expect(parseStorageSpec({ volume: "secrets", owner_id: 1 })).toBeNull();
expect(parseStorageSpec({ volume: "", owner_id: 1 })).toBeNull();
expect(parseStorageSpec({ volume: null, owner_id: 1 })).toBeNull();
});
test("refuses a missing or bad owner", () => {
expect(parseStorageSpec({ volume: "bots" })).toBeNull();
expect(parseStorageSpec({ volume: "bots", owner_id: 0 })).toBeNull();
expect(parseStorageSpec({ volume: "bots", owner_id: -2 })).toBeNull();
expect(parseStorageSpec({ volume: "bots", owner_id: "abc" })).toBeNull();
expect(parseStorageSpec({ volume: "bots", owner_id: "../../etc" })).toBeNull();
});
});
describe("storageSpecHref", () => {
test("builds the browser's address", () => {
expect(storageSpecHref({ spec: { volume: "bots", owner_id: 3 } })).toBe(
`${StorageBrowserPath}?volume=bots&owner=3`,
);
});
test("adds and encodes a path", () => {
expect(
storageSpecHref({ spec: { volume: "computers", owner_id: 1 }, path: "/seo/raw mes/" }),
).toBe(`${StorageBrowserPath}?volume=computers&owner=1&path=seo%2Fraw+mes`);
});
});
describe("volume names", () => {
test("maps a path ref's name to its volume", () => {
expect(storageVolumeFromRef({ name: "computer" })).toBe("computers");
expect(storageVolumeFromRef({ name: "bot" })).toBe("bots");
expect(storageVolumeFromRef({ name: "nope" })).toBeNull();
expect(storageVolumeFromRef({ name: null })).toBeNull();
});
test("knows which volumes need an owner", () => {
expect(storageVolumeNeedsOwner({ volume: "db" })).toBe(false);
expect(storageVolumeNeedsOwner({ volume: "media" })).toBe(true);
expect(isStorageVolume("mcp")).toBe(true);
expect(isStorageVolume("Media")).toBe(false);
});
test("labels a volume and a path", () => {
expect(storageVolumeLabel({ volume: "mcp" })).toBe("MCP servers");
expect(storagePathName({ volume: "computers", path: "seo/coderank/raw" })).toBe("raw");
expect(storagePathName({ volume: "computers", path: "" })).toBe("Computers");
});
});
describe("isSameStorageSpec", () => {
test("compares volume and owner", () => {
const a = { volume: "bots", owner_id: 1 } as const;
expect(isSameStorageSpec({ a, b: { volume: "bots", owner_id: 1 } })).toBe(true);
expect(isSameStorageSpec({ a, b: { volume: "bots", owner_id: 2 } })).toBe(false);
expect(isSameStorageSpec({ a, b: { volume: "workspaces", owner_id: 1 } })).toBe(false);
expect(isSameStorageSpec({ a, b: null })).toBe(false);
});
});