Files
LaunchPad/apps/backend/src/db/schema.ts

132 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { sqliteTable, text, integer, real } from "drizzle-orm/sqlite-core";
/**
* Ein physisches/logisches Gerät im Netzwerk (z. B. per FritzBox-Scan gefunden).
*/
export const devices = sqliteTable("devices", {
id: text("id").primaryKey(),
hostname: text("hostname").notNull(),
ip: text("ip").notNull(),
mac: text("mac"),
manufacturer: text("manufacturer"),
model: text("model"),
online: integer("online", { mode: "boolean" }).notNull().default(false),
source: text("source").notNull(), // fritzbox | dns | http | https | portscan | manual
lastScan: text("last_scan"), // ISO-8601
createdAt: text("created_at").notNull(),
updatedAt: text("updated_at").notNull(),
});
/**
* Ein Dienst, der auf einem Gerät läuft (z. B. Frigate, Portainer, Gitea ...).
* Benutzeränderungen (displayName, category, favorite, order, alias, icon,
* hostname, url, https, visible) dürfen von einem erneuten Scan eines bereits
* bekannten Dienstes NIEMALS überschrieben werden siehe README/Spezifikation.
* Nur favicon/description werden bei jedem Scan aufgefrischt.
*/
export const services = sqliteTable("services", {
id: text("id").primaryKey(),
deviceId: text("device_id")
.notNull()
.references(() => devices.id, { onDelete: "cascade" }),
// vom Benutzer gepflegt bzw. nur beim erstmaligen Anlegen durch einen Scan gesetzt
displayName: text("display_name").notNull(),
category: text("category"),
favorite: integer("favorite", { mode: "boolean" }).notNull().default(false),
order: real("order").notNull().default(0),
alias: text("alias").notNull().default("[]"), // JSON-Array als String
icon: text("icon"),
hostname: text("hostname").notNull(),
url: text("url").notNull(),
https: integer("https", { mode: "boolean" }).notNull().default(false),
// Ausgeblendete Dienste erscheinen nicht in der Suche, bleiben aber im Adminbereich.
visible: integer("visible", { mode: "boolean" }).notNull().default(true),
// Bei jedem Scan aktualisierbar
port: integer("port").notNull(),
favicon: text("favicon"),
description: text("description"),
createdAt: text("created_at").notNull(),
updatedAt: text("updated_at").notNull(),
});
/**
* Frei definierbare Kategorien (Erstellen/Löschen/Umbenennen/Sortieren per Drag & Drop).
*/
export const categories = sqliteTable("categories", {
id: text("id").primaryKey(),
name: text("name").notNull(),
order: real("order").notNull().default(0),
color: text("color"),
createdAt: text("created_at").notNull(),
updatedAt: text("updated_at").notNull(),
});
/**
* Protokoll jedes Scan-Versuchs (Geräte-Netzwerk-Scan oder FritzBox-Scan),
* angezeigt im Adminbereich unter "Logs".
*/
export const scanLogs = sqliteTable("scan_logs", {
id: text("id").primaryKey(),
type: text("type").notNull(), // "device" | "fritzbox"
targetId: text("target_id"), // Geräte-ID bei type "device", sonst null
level: text("level").notNull(), // "info" | "error"
message: text("message").notNull(),
createdAt: text("created_at").notNull(),
});
/**
* Manuell angelegtes Lesezeichen im Unterschied zu Diensten nicht an ein
* gescanntes Gerät gebunden, eigenständige Tabelle. Erscheint zusammen mit
* Diensten in der Suche (siehe packages/shared rankServices), wird aber
* separat verwaltet (Admin -> Lesezeichen).
*/
export const bookmarks = sqliteTable("bookmarks", {
id: text("id").primaryKey(),
url: text("url").notNull(),
displayName: text("display_name").notNull(),
hostname: text("hostname").notNull(), // aus der URL abgeleitet
description: text("description"),
category: text("category"),
icon: text("icon"),
favicon: text("favicon"),
favorite: integer("favorite", { mode: "boolean" }).notNull().default(false),
alias: text("alias").notNull().default("[]"),
order: real("order").notNull().default(0),
createdAt: text("created_at").notNull(),
updatedAt: text("updated_at").notNull(),
});
/**
* Zuletzt aus der Suche geöffnete Dienste/Lesezeichen, für die
* "Zuletzt besucht"-Leiste auf der Startseite.
*/
export const recentVisits = sqliteTable("recent_visits", {
id: text("id").primaryKey(),
itemType: text("item_type").notNull(), // "service" | "bookmark"
itemId: text("item_id").notNull(),
visitedAt: text("visited_at").notNull(),
});
/** Freie Schlüssel-Wert-Einstellungen, z. B. Anzahl "Zuletzt besucht". */
export const appSettings = sqliteTable("app_settings", {
key: text("key").primaryKey(),
value: text("value").notNull(),
});
/**
* "Später lesen": schnell von der Startseite abgelegte Links, unabhängig
* von Lesezeichen/Diensten. Können später zu einem Lesezeichen befördert
* oder gelöscht werden.
*/
export const readLater = sqliteTable("read_later", {
id: text("id").primaryKey(),
url: text("url").notNull(),
displayName: text("display_name").notNull(),
favicon: text("favicon"),
savedAt: text("saved_at").notNull(),
updatedAt: text("updated_at").notNull(),
});