Zum Inhalt springen

Zugriff und Identität

Der Worker verifiziert kurzlebige, signierte Workspace-Claims. Deine Anwendung implementiert den Identity-Adapter, der das angemeldete Subject und seine Workspace-Rolle auflöst. Produktion lehnt die Development-Claim-Route ab.

Jede Aktion eines Modells nimmt eine von drei Entscheidungen:

Regel Zweck
role(...) Verlangt eine Mindestrolle, bevor SQL läuft.
filter(...) Wendet eine Leseregel innerhalb von SQL-Queries und Counts an.
check(...) Bewertet eine Zeile oder einen Schreibvorgang mit Anwendungslogik.
board: model({
name: t.string(),
ownerId: t.string().index(),
}).access({
read: filter(({ auth }) => ({ ownerId: auth.sub })),
update: check(({ auth, row }) => row?.ownerId === auth.sub),
delete: role("moderator"),
}),

Defaults: Lesen member, Create/Update editor, Delete moderator.

  • filter(({ auth }) => where) ist nur für read. Es wird in das SQL jeder Query kompiliert, die das Modell berührt, nutzt also Indizes, füllt jede Seite und zählt korrekt. Ein Array ist OR; der Filter des Aufrufers ANDet damit.
  • check() funktioniert für jede Aktion. Es erhält { auth, row, data, changed } und gibt einen Boolean zurück. Schreibchecks laufen innerhalb der Transaktion neben dem Schreibvorgang, den sie bewachen. Bei read scannen sie passende Zeilen, bis die Seite voll oder die Ergebnismenge erschöpft ist. Counts wenden dieselbe Sichtbarkeitsregel an.
  • Eine Regel, die einen Fehler wirft, verweigert den Zugriff.

Bevorzuge SQL-Filter für Lesesichtbarkeit, weil ein funktionsbasierter Lesecheck nach dem Abruf läuft.

Die Engine verifiziert einen Claim und stellt nie einen aus. Implementiere zwei Methoden, dann liefert die Library die Claim-Route:

import { defineTapeDB, WORKSPACE_ROLE_LEVELS } from "tapedb/worker";
import schema from "./tapedb.schema";
import { migrations } from "./tapedb/migrations.gen";
const tapedb = defineTapeDB({
schema,
migrations,
identity: {
async getSubject(headers) {
const session = await auth.api.getSession({ headers });
return session ? { sub: session.user.id } : null;
},
async getRole(workspaceId, sub) {
const member = await memberships.get(workspaceId, sub);
return member ? WORKSPACE_ROLE_LEVELS[member.role] : null;
},
},
});
export const WorkspaceRoom = tapedb.WorkspaceRoom;
export default tapedb.worker;

POST /_tapedb/claim gibt { token, workspaceId, roleLevel, expiresAt } zurück. Der Client sendet ihn als Authorization: Bearer <token> und erneuert ihn beim Reconnect. In Development aktiviert tapedb dev eine Dev-Claim-Route, sodass der Client überhaupt keine Session braucht; ENVIRONMENT=production lehnt diese Route ab, und tapedb deploy weigert sich, das Flag auszuliefern.

Schemaänderungen und ihre SQL-Migrationen stehen in Migrationen.