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(), });