Zum Inhalt springen
CADCAMFUN
Doku

Dokumentation

Datenbank

SQLite-Persistenz mit begrenztem Verbindungseigentum, typisierten Abfragefehlern, Migrationssicherungen und lokaler Dokumentenspeicherung.

Automatisch aus dem Englischen übersetzt. Englisches Original ansehen

Persistenzmodell

@cadcamfun/db öffnet SQLite durch bun:sqlite und stellt eine Drizzle-Datenbank innerhalb des Effekts bereit Database service. Der Sidecar speichert seine Anwendungsdatenbank unter <dataDir>/cadcamfun.db. Die Verbindung ermöglicht die Write-Ahead-Protokollierung und die Durchsetzung von Fremdschlüsseln vor der Anwendung von Migrationen.

Die Datenbank enthält die Arbeitskopie von Projekten und den zugehörigen Ressourcen. Projektversionskontrollobjekte, Pull-Requests und Workflow-Ausführungen werden ebenfalls lokal gespeichert. Anmeldeinformationen für KI-Anbieter verwenden eine separate, vom Eigentümer lesbare JSON-Datei. Das Anwendungsträgertoken wird beim Start bereitgestellt und nicht als Voreinstellung beibehalten.

Schema und Einstiegspunkte

packages/db/src/schema.ts definiert Tabellen, Indizes, Beziehungen, Standardwerte und abgeleitete Auswahl-/Einfügetypen. src/index.ts exporte Database, DatabaseError, der Nieselregen Db typ und Schema-Namespace. src/upgrade.ts ist für Migrationsplanung, Backups und Transaktionsanwendungen zuständig; src/migrate.ts ist der Befehlszeilen-Einstiegspunkt. Generiertes SQL und das Migrationsjournal leben darin packages/db/drizzle.

Jede Verbindung wurde geöffnet von packages/db/src/index.ts schaltet SQLite um journal_mode = WAL und schaltet sich ein foreign_keys. Das Schließen der Datenbankläufe wal_checkpoint(TRUNCATE) daher ist die Hauptdatei vollständig, wenn der Prozess beendet wird. Wenn der Prüfpunkt ausgelastet ist oder ausfällt, wird eine Warnung protokolliert und das Herunterfahren fortgesetzt.

Gespeicherte Werte

Domänenkennungen sind standardmäßig UUIDs, sofern in der Tabelle konfiguriert. Drizzle ordnet Zeitstempelspalten JavaScript-Daten und JSON-Spalten den deklarierten Datensatztypen zu. HTTP-Repositorys konvertieren Datumsangaben in ihre Wire-Darstellung und validieren JSON an der Domänengrenze. Eine TypeScript-Typanmerkung in einer gespeicherten JSON-Spalte selbst beweist nicht, dass importierte oder ältere Daten mit dem aktuellen Domänenschema übereinstimmen.

Projiziert eigene Zeichnungen, Komponenten und Werkzeugwege. Werkzeugwege können auf eine Zeichnung, ein Material, einen Fräser und eine Maschinenkonfiguration verweisen. ihre data feld kann ein tragbares Fertigungsdokument enthalten. Separate Zeichnungs-, Komponenten- und Werkzeugweg-Versionstabellen bewahren Ressourcenversionen zusätzlich zum Git-Verlauf des gesamten Projekts auf.

Abfrage über den Dienst

Database.layer akzeptiert einen Dateipfad oder :memory:, einen optionalen Migrationsordner und ein optionales Sicherungsverzeichnis. Es erhält das SQLite-Handle für seinen Effektbereich. Anrufer nutzen query(operation, run) um synchrone oder asynchrone Drizzle-Arbeiten auszuführen; Ausländische Ausnahmen werden DatabaseError mit dem Operationsnamen und der ursprünglichen Ursache.

Eine Repository-Abfrage
import { Database, schema } from "@cadcamfun/db"
import { Effect } from "effect"

const loadProjects = Effect.gen(function* () {
  const database = yield* Database
  return yield* database.query("projects.list", (db) =>
    db.select().from(schema.projectTable).all())
})

Repositorys besitzen eigene Zugriffsregeln und Zeileninterpretation; Der gemeinsam genutzte Abfragehelfer liefert die Fehlerzuordnung. Es autorisiert nicht automatisch eine Abfrage oder erstellt eine Transaktion rund um eine willkürliche Abfolge von Serviceaufrufen.

Upgrade-Verhalten

Beim Öffnen einer Ebene mit einem Migrationsordner werden die Migrationsdateien gelesen und ihre Namen mit verglichen __drizzle_migrations. Ein Journal aus dem älteren Format wird durch übereinstimmende Migrationszeitstempel und Hashes aufgefüllt. Bei einem nicht passenden Journal schlägt das Upgrade fehl, anstatt eine Migrationsidentität zu erraten.

Wenn eine vorhandene Datenbank auf der Festplatte aktualisiert werden muss, erstellt der Dienst vor der Migration ein SQLite-Backup mit VACUUM INTO. Es schreibt in eine Teildatei, benennt den fertigen Snapshot um und behält die letzten fünf passenden Sicherungen vor der Migration bei. Das Standard-Sicherungsverzeichnis ist backups neben der Datenbank. Für neue Datenbanken und In-Memory-Datenbanken ist diese Sicherung vor dem Upgrade nicht erforderlich.

Journaländerungen und ausstehende SQL-Anweisungen werden in einer unmittelbaren Transaktion ausgeführt. Bei einer fehlgeschlagenen Migration wird die Transaktion zurückgesetzt und der Layer-Aufbau schlägt fehl. Die Anwendung sollte keine Bereitschaft melden, wenn die Datenbankinitialisierung fehlschlägt.

Migrationsbefehle

Vom Repository-Stammverzeichnis ausführen
bun run db:generate
bun run db:migrate

Generieren Sie Migrationen nach Änderungen schema.ts, und halten Sie das generierte SQL und das Journal zusammen. Die Migrations-CLI verwendet DATABASE_URL entfernt im Lieferumfang eine Initiale file: präfix, andernfalls wird geöffnet ./data/cadcamfun.db. Der Root-Wrapper führt dieses Skript mit aus packages/db als Arbeitsverzeichnis, daher ist sein Standardziel packages/db/data/cadcamfun.db. Web Studio verwendet data/cadcamfun.db im Repository-Stammverzeichnis. Der verpackte Sidecar verwendet CADCAMFUN_DATA_DIR. Wählen Sie beim Ausführen der Wartung einen expliziten Datenbankpfad aus, damit diese Einstiegspunkte nicht unbeabsichtigt auf unterschiedliche Dateien angewendet werden.

Ohne einen Migrationsordner, Database.layer öffnet die Datenbank, erstellt aber keine Anwendungstabellen. Tests, die das vollständige Schema benötigen, stellen dem Migrationsverzeichnis eine In-Memory-Datenbank zur Verfügung.

Datendomänen

  • Identität und Zusammenarbeit: benutzer, Konten, Sitzungen, Authentifikatoren, Verifizierungstoken, Abonnements, Organisationen, Mitgliedschaft, Einladungen, Konversationen, Nachrichten, Benachrichtigungen und Push-Abonnements.
  • Design und Herstellung: projekte, Zeichnungen, Komponenten, ihre Versionen und Kommentare, Materialien, Werkzeuge, Maschinenkonfigurationen, Werkzeugwege und Produktionskostenaufzeichnungen.
  • Anwendungsdienstleistungen: dateien, Bibliothekselemente, Plugin-Registrierung/-Konfiguration, API-Schlüssel, Analyse- und Aktivitätsdatensätze, Marktplatzdaten, MCP-Konfiguration und lokale Schlüsselwerteinträge.
  • Projekthistorie: Git-Repositorys, Objekte und Refs, Pull-Requests und Reviews sowie Workflow-Ausführungen.

Das Vorhandensein einer Tabelle legt ein Persistenzmodell fest. Netzwerkintegrationen und Anwendungsfunktionen hängen von ihren tatsächlichen Handlern und Diensten ab und nicht nur von der Tabellenexistenz.

Lokaler Schlüsselwertspeicher

LocalKeyValue behält den JSON-kompatiblen Anwendungsstatus bei. Der Sidecar KvStore unterstützt das Abrufen, Upsert, Löschen und Präfix-gefilterte Auflisten. Schlüssel müssen Text enthalten, der kein Leerzeichen ist, und dürfen höchstens 512 Zeichen lang sein. Als Werte sind Null, Zeichenfolgen, boolesche Werte, endliche Zahlen, Arrays und einfache Objekte zulässig. Zyklen, Funktionen, nicht endliche Zahlen und nicht einfache Objekte werden abgelehnt.

Einträge enthalten einen Aktualisierungszeitstempel. Upserts ersetzen den Wert für denselben Schlüssel. Die Präfixliste wird nach Schlüssel und Filtern im Speicher sortiert. Verwenden Sie diesen Speicher für Einstellungen und Assistentenkontext; Anmeldeinformationen gehören zu ihrem dedizierten Anmeldeinformationsdienst.

Herunterfahren und Wiederherstellen

Die Finalizer-Anforderungen für den Datenbankbereich PRAGMA wal_checkpoint(TRUNCATE) bevor Sie den Griff schließen. Ein ausgelasteter Prüfpunkt oder ein Schließfehler wird als Warnung protokolliert, damit die verbleibenden Finalizer ausgeführt werden können. Der Verbindungseigentümer und nicht einzelne HTTP-Handler schließen das Handle.

Für eine konsistente manuelle Sicherung stoppen Sie die Anwendung sauber, bevor Sie ihr Datenverzeichnis kopieren, oder nutzen Sie die Sicherungsfunktionen von SQLite, während sie ausgeführt wird. Eine Live-Datenbank kann einen Status in ihrer WAL-Datei haben. Um ein Backup vor der Migration zu überprüfen, arbeiten Sie an einer Kopie und bewahren Sie die Originaldatenbank und Backups auf, bis die Wiederherstellung bestätigt ist.

Verantwortlichkeiten des Repositorys

Behalten Sie die SQLite-Verbindungserstellungs- und Migrationslogik in diesem Paket bei. Sidecar-Repositorys nutzen den bereitgestellten Datenbankdienst und ordnen Speicherfehler den HTTP-Fehlern ihrer Ressource zu. Behalten Sie die Geometrieberechnung im Kern und Geheimnisse in den Anmeldeinformations- oder Token-Diensten. Datenbanksicherungen enthalten Projekt- und Kontodaten und erfordern dieselben Zugriffskontrollen wie das aktive Datenverzeichnis.

Untersuchen Sie eine Datenbank, ohne sie zu ändern

Verwenden Sie nach einem ordnungsgemäßen Herunterfahren die SQLite-CLI für einen expliziten Datenbankpfad. Der schreibgeschützte Modus verhindert, dass versehentlich eine neue leere Datei erstellt wird, wenn ein Pfad falsch ist. Diese Prüfungen prüfen Struktur und Konsistenz, ohne Konto- oder Anmeldeinformationszeilen offenzulegen.

Schreibgeschützte SQLite-Inspektion
sqlite3 -readonly /absolute/path/cadcamfun.db 'PRAGMA quick_check;'
sqlite3 -readonly /absolute/path/cadcamfun.db 'PRAGMA foreign_key_check;'
sqlite3 -readonly /absolute/path/cadcamfun.db "SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name;"
sqlite3 -readonly /absolute/path/cadcamfun.db 'SELECT name, applied_at FROM __drizzle_migrations ORDER BY id;'

quick_check normalerweise meldet ok; foreign_key_check gibt nur Zeilen für Verstöße zurück. Eine Zeitschrift ohne das name/applied_at spalten ist ein älteres Format, das der Anwendungs-Upgrade-Code vor der Verwendung der letzten Abfrage abgleichen muss. Überprüfen Sie Pfade und Migrationsidentität, bevor Sie Reparaturen durchführen.

Die Anwendung legt die Fremdschlüsselerzwingung für ihre eigene Verbindung fest. Eine separat geöffnete Wartungsverbindung muss diese explizit aktivieren, bevor autorisierte Schreibvorgänge ausgeführt werden. Das PRAGMA ist verbindungsspezifisch.

Quelle: packages/db/src/index.ts, upgrade.ts, und database.test.ts.

Verwenden Sie eine Einwegdatenbank im Code

Ein vollständiger In-Memory-Serviceumfang ist für Repository-Beispiele und isolierte Diagnosen nützlich. Der Bereich besitzt die Verbindung und wendet die eingecheckten Migrationen an, bevor der Effekt den Dienst liest.

Öffnen, migrieren, abfragen und freigeben
import { Database, schema } from "@cadcamfun/db"
import { Effect } from "effect"

const migrationsFolder = new URL("../../packages/db/drizzle", import.meta.url).pathname
const program = Effect.gen(function* () {
  const database = yield* Database
  return yield* database.query("diagnostic.projects", (db) =>
    db.select({ id: schema.projectTable.id }).from(schema.projectTable).all())
}).pipe(Effect.provide(Database.layer({ path: ":memory:", migrationsFolder })))
console.log(await Effect.runPromise(program)) // []

Speichern Sie dieses Skript unter apps/sidecar/diagnostic.ts und laufen bun apps/sidecar/diagnostic.ts aus dem Repository-Stammverzeichnis. Dieses Paket stellt die Datenbank- und Effektabhängigkeiten bereit und die Migrations-URL wird aus dieser Datei aufgelöst. Ein fehlendes oder falsches Migrationsverzeichnis ist nicht gleichbedeutend mit einem leeren Anwendungsschema. Der Nieselregen query rückruf kann eine synchrone Abfrage ausführen oder ein Versprechen zurückgeben; Der Helfer verpackt Fehler unter der bereitgestellten Vorgangsbezeichnung.

Quelle: packages/db/src/database.test.ts und index.ts.

Diagnostizieren Sie Speicherfehler

  • Keine solche Tabelle: bestätigen Sie die genaue Datenbankdatei und den Migrationsordner. Der Dienst lässt das Öffnen ohne Migrationen zu, die Abfrage von Anwendungstabellen schlägt dann jedoch fehl.
  • Fremdschlüsseleinschränkung fehlgeschlagen: überprüfen Sie den referenzierten Besitzer/das Projekt/die Ressource. Löschbeschränkungen sind schemaspezifisch: Das Löschen eines Benutzers mit einem eigenen Projekt ist eingeschränkt, während Sitzungszeilen kaskadiert werden, wenn ihr Benutzer gelöscht wird.
  • Die Sicherung ist während des Upgrades fehlgeschlagen: überprüfen Sie den freien Speicherplatz und den Schreibzugriff in den Datenbank- und Backup-Verzeichnissen. Ein Sicherungsfehler stoppt die Migration; Löschen Sie nicht die Originaldatenbank, um sie zu umgehen.
  • Das Legacy-Migrationsjournal stimmt nicht mit lokalen Migrationen überein: bewahren Sie die Datenbank und stellen Sie den passenden Migrationssatz wieder her. Durch das Umbenennen oder Entfernen von Journalzeilen würde der inkompatible Upgrade-Verlauf ausgeblendet.
  • Checkpoint beim Herunterfahren beschäftigt: überprüfen Sie andere offene Verbindungen, bevor Sie nur die Datenbankdatei kopieren. Behalten Sie WAL/SHM-Geschwister bei, bis ein sauberer Prüfpunkt oder eine konsistente SQLite-Sicherung abgeschlossen ist.

Bei Migrationssicherungen handelt es sich um Wiederherstellungspunkte vor dem Upgrade mit einer Aufbewahrungsrichtlinie für fünf Dateien und nicht um eine kontinuierliche Sicherung nachfolgender Änderungen. Sichern Sie das komplette Datenverzeichnis separat entsprechend den Wiederherstellungsanforderungen der Installation.

Quelle: packages/db/src/index.ts, upgrade.ts, schema.ts, und database.test.ts.

Migrationsgeschichte

Die eingecheckten Migrationen werden in der Reihenfolge der Ordner angewendet. Jeder Ordner enthält eine migration.sql und das Nieselregen-Set snapshot.json wird zur Berechnung des nächsten Diffs verwendet. Die Journalzeile speichert den Ordnernamen, seinen Hash und den Ordner-Zeitstempel in Millisekunden (created_at) und die ISO-Zeit, zu der es angewendet wurde (applied_at).

OrdnerGrößeWas es schafft
20261004223029_careful_monster_badoon771 ZeilenDas aus dem alten Prisma-Modell konvertierte Basisschema: Identität, Organisationen, Projekte, Zeichnungen, Komponenten, Werkzeugwege, Bibliotheken, Kosten, Marktplatz, Plugins und MCP-Konfiguration. Tabellen- und Spaltennamen bleiben unverändert, also älter dev.db daten bleiben lesbar.
20261008030518_redundant_dagger73 ZeilenProjekthistorie: GitRepo, GitObject, GitRef, PullRequest, PullRequestReview, WorkflowRun.
20261009012351_moaning_loki28 ZeilenGeometriekerndatensätze: MeshAsset und CadOperationLog.
20261009171602_brown_mad_thinker5 ZeilenLocalKeyValue (key text-Primärschlüssel, value text nicht null, updatedAt ganze Zahl nicht null).

Ordnernamen stammen von drizzle-kit: ein UTC-Zeitstempel, gefolgt von einem zufälligen Namen. Benennen oder bearbeiten Sie niemals einen Ordner, nachdem er irgendwo angewendet wurde. Der Upgrade-Planer identifiziert durchgeführte Migrationen nach Namen und bei älteren Zeitschriften nach Zeitstempel und Hash.

Entscheidungsfluss verbessern

datei öffnenWAL + Foreign_keys planUpgradetagebuch + Dateien lesen benötigt?veraltet oder ausstehend > 0 nein: Nieselregen()servicebereit backupDatabasenur wenn Datei existierte applyUpgradeeine SOFORTIGE SMS versagenDatenbankfehler, Rollback ja
SituationSicherungErgebnis
Neue Datei, Migrationsordner angegebenNein (die Datei existierte vor dem Öffnen nicht)Alle Migrationen wurden in einer Transaktion durchgeführt
Vorhandene Datei, Journal auf dem neuesten StandNEINGeöffnet wie es ist; needed ist falsch
Vorhandene Datei, ausstehende MigrationenJa, vor jeder ÄnderungAusstehende Migrationen angewendet; Journalzeilen eingefügt mit applied_at
Vorhandene Datei, Journal ohne name spalteJaSpalten name und applied_at hinzugefügt, Zeilen aufgefüllt, dann ausstehende Migrationen angewendet
Legacy-Zeile entspricht keiner lokalen MigrationEs wird kein Backup erstellt: Die Planung schlägt zunächst fehlDatabaseError mit Betrieb migrate: „Legacy-Migrationsjournal stimmt nicht mit lokalen Migrationen überein“
:memory:NiemalsMigrationen werden angewendet, wenn ein Ordner angegeben ist
Kein MigrationsordnerNiemalsGeöffnet, ohne Tabellen zu erstellen

Der Backfill-Abgleich funktioniert folgendermaßen: eine Legacy-Zeile created_at wird auf ganze Sekunden gekürzt und mit dem Zeitstempel des Ordners jeder Migration verglichen. Wenn genau eine Migration übereinstimmt, wird diese verwendet. Andernfalls löst der Hash den Gleichstand auf, und als letztes Mittel wird jede Migration mit demselben Hash verwendet.

Anatomie der Backup-Datei

Sicherungsname vor der Migration
backups/cadcamfun-20261015-034512.123-6f1c2d4e-8a90-4b7c-9e21-3f5a6b7c8d9e-pre-migrate.db
        └ prefix ┘ └ UTC timestamp ┘ └ random UUID                      ┘
  • Das Präfix ist der Name der Datenbankdatei ohne .db, gefolgt von einem Bindestrich.
  • Der Zeitstempel ist UTC mit Millisekundengenauigkeit. Wenn die neueste vorhandene Sicherung denselben oder einen späteren Zeitstempel hat, wird die neue Sicherung 1 ms später gesetzt, sodass die lexikalische Reihenfolge immer der Erstellungsreihenfolge entspricht.
  • Der Snapshot wird beschrieben <name>.partial mit VACUUM INTO, dann umbenannt. Ein Absturz hinterlässt höchstens einen .partial datei, die in a entfernt wird finally block.
  • Nach einer erfolgreichen Sicherung bleiben nur die fünf neuesten Dateien erhalten, die genau dem Muster entsprechen. Dateien, die Sie unter anderen Namen in das Verzeichnis kopieren, werden niemals gelöscht.
  • VACUUM INTO erstellt eine kompakte, eigenständige Datenbank ohne WAL-Geschwister, sodass ein Backup direkt geöffnet werden kann.
Stellen Sie ein Backup vor der Migration wieder her (Anwendung gestoppt)
DATA="/absolute/path/to/data"
cp "$DATA/cadcamfun.db" "$DATA/cadcamfun.db.broken"         # keep the failed state
rm -f "$DATA/cadcamfun.db-wal" "$DATA/cadcamfun.db-shm"
cp "$DATA/backups/cadcamfun-<timestamp>-<uuid>-pre-migrate.db" "$DATA/cadcamfun.db"
sqlite3 -readonly "$DATA/cadcamfun.db" 'PRAGMA quick_check;'   # expect: ok

Beim Wiederherstellen werden Schema und Daten zurückgesetzt

Ein wiederhergestelltes Backup ist der Zustand unmittelbar vor dem Upgrade. Änderungen, die nach diesem Upgrade vorgenommen wurden, sind nicht darin enthalten, und beim nächsten Start wird dieselbe Migration erneut versucht. Beheben Sie die Fehlerursache vor dem Neustart.

Kerntischformen

Dies sind die Tabellen, die der Desktop-Editor, der Verlauf und der Kernel am häufigsten lesen. Spaltennamen sind die SQL-Namen; Drizzle macht sie unter den gleichen Tasten sichtbar.

TischSpalten
Projectid, name, description, createdAt, updatedAt, ownerId, organizationId, isPublic
Drawingid, name, description, data, thumbnail, createdAt, updatedAt, projectId
Componentid, name, description, data, thumbnail, type, isPublic, createdAt, updatedAt, projectId
Toolpathid, name, description, data, type, operationType, gcode, thumbnail, isPublic, projectId, createdAt, updatedAt, createdBy, drawingId, materialId, toolId, machineConfigId
GitRepoprojectId, head, remote, createdAt
GitObjectprojectId, oid, type, body
GitRefprojectId, name, oid
PullRequestid, projectId, number, title, body, head, base, state, authorId, authorKind, mergeCommit, remoteNumber, createdAt, updatedAt
WorkflowRunid, projectId, workflow, event, ref, sha, status, events, startedAt, finishedAt
MeshAssetid, projectId, kind, inputs, stats, mesh, createdAt
CadOperationLogid, projectId, operation, meshAssetId, outcome, durationMs, inputs, error, traceId, spanId, createdAt
LocalKeyValuekey, value, updatedAt
Projekt ZeichnungKomponenteWerkzeugwegGit*-TabellenPullRequestMeshAssetWorkflowRun DrawingVersionKomp. VersionWerkzeugweg Ver.CadOpLogCadOpLog.mesh→null durchgezogene Linien: ON DELETE CASCADE

Durch das Löschen eines Projekts werden dessen Zeichnungen, Komponenten, Werkzeugwege, Verlaufsobjekte und Referenzen, Pull Requests, Workflow-Ausführungen, Mesh-Assets und Betriebsprotokolle entfernt. Versions- und Kommentarzeilen kaskadieren von ihrer übergeordneten Ressource. A CadOperationLog zeile überlebt die Löschung ihres Mesh-Assets mit meshAssetId auf null gesetzt. Werkzeugwegverknüpfungen zu Materialien, Werkzeugen und Maschinenkonfigurationen haben keine Kaskade, sodass ein referenziertes Material nicht gelöscht werden kann, während ein Werkzeugweg darauf zeigt.

Vollständiger Tischkatalog

Das Schema definiert 60 Tabellen. Die folgende Liste wurde aus den Definitionen der Drizzle-Tabelle erstellt: SQL-Name, exportierte Konstante, Spaltenanzahl, Anzahl der deklarierten Indizes und Fremdschlüssel mit ihrer Löschregel. „Keine Aktion“ ist die Standardeinstellung von SQLite: Der Löschvorgang schlägt fehl, während referenzierende Zeilen vorhanden sind (Fremdschlüssel werden für die Anwendungsverbindung erzwungen).

SQL-TabelleExportSpaltenIdxFremdschlüssel
AccountaccountTable131userId → Benutzer (Kaskade)
ActivityLogactivityLogTable70userId → Benutzer (keine Aktion)
AIAnalysisLogaIAnalysisLogTable70userId → Benutzer (keine Aktion)
ApiKeyapiKeyTable71userId → Benutzer (Kaskade)
authenticatorsauthenticatorTable80userId → Benutzer (Kaskade)
CadOperationLogcadOperationLogTable111projectId → Projekt (Kaskade)
meshAssetId → MeshAsset (null setzen)
ComponentcomponentTable100projectId → Projekt (Kaskade)
ComponentCommentcomponentCommentTable60componentId → Komponente (Kaskade)
userId → Benutzer (keine Aktion)
ComponentVersioncomponentVersionTable60componentId → Komponente (Kaskade)
userId → Benutzer (keine Aktion)
ConversationconversationTable60organizationId → Organisation (Kaskade)
ConversationParticipantconversationParticipantTable51userId → Benutzer (Kaskade)
conversationId → Gespräch (Kaskade)
CostSettingscostSettingsTable110ownerId → Benutzer (keine Aktion)
organizationId → Organisation (keine Aktion)
DrawingdrawingTable80projectId → Projekt (Kaskade)
DrawingVersiondrawingVersionTable50drawingId → Zeichnen (Kaskade)
FileUploadfileUploadTable120—
GitObjectgitObjectTable41projectId → Projekt (Kaskade)
GitRefgitRefTable31projectId → Projekt (Kaskade)
GitRepogitRepoTable40projectId → Projekt (Kaskade)
LibraryItemlibraryItemTable140ownerId → Benutzer (keine Aktion)
organizationId → Organisation (keine Aktion)
LocalKeyValuelocalKeyValueTable30—
MachineConfigmachineConfigTable90ownerId → Benutzer (keine Aktion)
MarketplaceCategorymarketplaceCategoryTable101parentId → MarketplaceCategory (keine Aktion)
MarketplaceCollectionmarketplaceCollectionTable101sellerId → MarketplaceSeller (keine Aktion)
MarketplaceCollectionItemmarketplaceCollectionItemTable51collectionId → MarketplaceCollection (Kaskade)
itemId → MarketplaceItem (Kaskade)
MarketplaceItemmarketplaceItemTable305sellerId → MarketplaceSeller (keine Aktion)
MarketplaceOrdermarketplaceOrderTable234buyerId → Benutzer (keine Aktion)
sellerId → MarketplaceSeller (keine Aktion)
itemId → MarketplaceItem (keine Aktion)
MarketplacePayoutmarketplacePayoutTable121sellerId → MarketplaceSeller (keine Aktion)
MarketplaceReviewmarketplaceReviewTable122itemId → MarketplaceItem (keine Aktion)
buyerId → Benutzer (keine Aktion)
orderId → MarketplaceOrder (keine Aktion)
MarketplaceSellermarketplaceSellerTable190userId → Benutzer (keine Aktion)
MarketplaceWishlistmarketplaceWishlistTable41userId → Benutzer (Kaskade)
itemId → MarketplaceItem (Kaskade)
MaterialmaterialTable90ownerId → Benutzer (keine Aktion)
organizationId → Organisation (keine Aktion)
MaterialCostmaterialCostTable100materialId → Material (Kaskade)
ownerId → Benutzer (keine Aktion)
organizationId → Organisation (keine Aktion)
MCPServerConfigmCPServerConfigTable111—
MeshAssetmeshAssetTable71projectId → Projekt (Kaskade)
MessagemessageTable81senderId → Benutzer (Kaskade)
conversationId → Gespräch (Kaskade)
fileId → FileUpload (null setzen)
NotificationnotificationTable120userId → Benutzer (Kaskade)
organizationId → Organisation (Kaskade)
projectId → Projekt (Kaskade)
componentId → Komponente (Kaskade)
messageId → Nachricht (Kaskade)
OperationCostoperationCostTable100ownerId → Benutzer (keine Aktion)
organizationId → Organisation (keine Aktion)
OrganizationorganizationTable50—
OrganizationInvitationorganizationInvitationTable70organizationId → Organisation (Kaskade)
passkeypasskeyTable110userId → Benutzer (Kaskade)
PluginConfigurationpluginConfigurationTable20pluginId → PluginRegistryEntry (Kaskade)
PluginRegistryEntrypluginRegistryEntryTable90—
ProductionCostEstimateproductionCostEstimateTable170toolpathId → Werkzeugweg (Kaskade)
operationCostId → OperationCost (keine Aktion)
ownerId → Benutzer (keine Aktion)
organizationId → Organisation (keine Aktion)
ProjectprojectTable80ownerId → Benutzer (keine Aktion)
organizationId → Organisation (keine Aktion)
PullRequestpullRequestTable141projectId → Projekt (Kaskade)
authorId → Benutzer (keine Aktion)
PullRequestReviewpullRequestReviewTable81pullRequestId → PullRequest (Kaskade)
reviewerId → Benutzer (keine Aktion)
PushSubscriptionpushSubscriptionTable60userId → Benutzer (Kaskade)
SessionsessionTable80userId → Benutzer (Kaskade)
SubscriptionsubscriptionTable243referenceId → Benutzer (Kaskade)
TokentokenTable82userId → Benutzer (Kaskade)
TooltoolTable170ownerId → Benutzer (keine Aktion)
organizationId → Organisation (keine Aktion)
ToolpathtoolpathTable170projectId → Projekt (Kaskade)
createdBy → Benutzer (keine Aktion)
drawingId → Zeichnen (keine Aktion)
materialId → Material (keine Aktion)
toolId → Werkzeug (keine Aktion)
machineConfigId → MachineConfig (keine Aktion)
ToolpathCommenttoolpathCommentTable60toolpathId → Werkzeugweg (Kaskade)
userId → Benutzer (keine Aktion)
ToolpathVersiontoolpathVersionTable70toolpathId → Werkzeugweg (Kaskade)
userId → Benutzer (keine Aktion)
ToolWearCosttoolWearCostTable100toolId → Werkzeug (Kaskade)
ownerId → Benutzer (keine Aktion)
organizationId → Organisation (keine Aktion)
twoFactortwoFactorTable40userId → Benutzer (Kaskade)
UseruserTable170—
UserOrganizationuserOrganizationTable51userId → Benutzer (Kaskade)
organizationId → Organisation (Kaskade)
verification_tokensverificationTokenTable31—
WorkflowRunworkflowRunTable101projectId → Projekt (Kaskade)

Sicher schreiben

  • Fassen Sie Änderungen mit mehreren Anweisungen in einer Drizzle-Transaktion in einer zusammen query anruf: database.query("op", (db) => db.transaction((tx) => ...)). Separate query anrufe sind separate Transaktionen.
  • Geben Sie jeder Abfrage einen stabilen, gepunkteten Operationsnamen (projects.list, toolpaths.update). Es ist der einzige Kontext a DatabaseError trägt außerdem die Ursache, und es erscheint in Protokollen.
  • Generieren Sie IDs mit newId() (eine UUID) oder lassen Sie die Spalte standardmäßig dies tun; Erstellen Sie keine IDs aus Namen.
  • Validieren Sie JSON-Spalten beim Lesen. Der deklarierte TypeScript-Typ wird von SQLite nicht überprüft.
  • Verwenden LocalKeyValue über den Sidecar-Schlüsselwertdienst, anstatt Zeilen direkt zu schreiben, sodass die Schlüssel- und Wertvalidierung angewendet wird.
Ein transaktionales Schreiben
import { Database, schema } from "@cadcamfun/db"
import { eq } from "drizzle-orm"
import { Effect } from "effect"

const renameProject = (id: string, name: string) => Effect.gen(function* () {
  const database = yield* Database
  return yield* database.query("projects.rename", (db) =>
    db.transaction((tx) => {
      const updated = tx.update(schema.projectTable)
        .set({ name, updatedAt: new Date() })
        .where(eq(schema.projectTable.id, id))
        .returning().all()
      if (updated.length === 0) throw new Error(`project ${id} not found`)
      return updated[0]
    }))
})

Um Zeilen mit expliziten IDs zu erstellen, verwenden Sie schema.newId(); @cadcamfun/db exportiert das Schemamodul erneut als schema namespace, nicht als Top-Level-Namen.

In 3D

Datenbankschema in 3D Zum Orbit ziehen · zum Zoomen scrollen · zum Schwenken mit der rechten Maustaste ziehen
Alle 60 Tabellen aus den Drizzle-Migrationen in packets/db/drizzle; Jede Zeile ist ein REFERENCES-Fremdschlüssel (91 zwischen verschiedenen Tabellen). Die Hubs in der Mitte sind die Tabellen, auf die am häufigsten verwiesen wird.