1c0a7cb850
Phase 4 — New Widget Types: - Clock/Weather, System Stats, RSS/Feed, Calendar, Markdown, Metric/Counter, Link Group, Camera/Stream widgets - Backend services with caching for each data source - Full creation form with dynamic config fields per type Phase 5 — Visual & Styling Enhancements: - Glassmorphism card style (solid/glass/outline) - Board-level themes with per-board hue/saturation - Animated SVG status rings replacing static dots - Card size options (compact/medium/large) - Custom CSS injection (admin + per-board, sanitized) - Wallpaper backgrounds with blur/overlay/parallax Phase 6 — Functional Features: - Favorites bar with drag-and-drop reordering - Recent apps tracking with privacy toggle - Uptime dashboard page (/status, guest-accessible) - Notifications system (Discord/Slack/Telegram/HTTP webhooks) - App tags with filtering in board view - Multi-URL app cards with expandable sub-links - Personal API tokens with scoped permissions - Audit log with retention and admin viewer Phase 7 — Quality of Life: - Onboarding wizard (5-step first-launch setup) - App URL health preview with favicon/title detection - Board templates (4 built-in + custom import/export) - Keyboard shortcut overlay (j/k nav, 1-9 boards, ? help) 212 files changed, 15641 insertions, 980 deletions. Build, lint, type check, and 222 tests all pass.
244 lines
8.9 KiB
SQL
244 lines
8.9 KiB
SQL
-- AlterTable
|
|
ALTER TABLE "Board" ADD COLUMN "backgroundType" TEXT;
|
|
ALTER TABLE "Board" ADD COLUMN "cardSize" TEXT;
|
|
ALTER TABLE "Board" ADD COLUMN "customCss" TEXT;
|
|
ALTER TABLE "Board" ADD COLUMN "themeHue" INTEGER;
|
|
ALTER TABLE "Board" ADD COLUMN "themeSaturation" INTEGER;
|
|
ALTER TABLE "Board" ADD COLUMN "wallpaperBlur" INTEGER;
|
|
ALTER TABLE "Board" ADD COLUMN "wallpaperOverlay" REAL;
|
|
ALTER TABLE "Board" ADD COLUMN "wallpaperUrl" TEXT;
|
|
|
|
-- AlterTable
|
|
ALTER TABLE "Section" ADD COLUMN "cardSize" TEXT;
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Tag" (
|
|
"id" TEXT NOT NULL PRIMARY KEY,
|
|
"name" TEXT NOT NULL,
|
|
"color" TEXT,
|
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "AppTag" (
|
|
"id" TEXT NOT NULL PRIMARY KEY,
|
|
"appId" TEXT NOT NULL,
|
|
"tagId" TEXT NOT NULL,
|
|
CONSTRAINT "AppTag_appId_fkey" FOREIGN KEY ("appId") REFERENCES "App" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
|
CONSTRAINT "AppTag_tagId_fkey" FOREIGN KEY ("tagId") REFERENCES "Tag" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "AppLink" (
|
|
"id" TEXT NOT NULL PRIMARY KEY,
|
|
"appId" TEXT NOT NULL,
|
|
"label" TEXT NOT NULL,
|
|
"url" TEXT NOT NULL,
|
|
"icon" TEXT,
|
|
"order" INTEGER NOT NULL DEFAULT 0,
|
|
CONSTRAINT "AppLink_appId_fkey" FOREIGN KEY ("appId") REFERENCES "App" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "UserFavorite" (
|
|
"id" TEXT NOT NULL PRIMARY KEY,
|
|
"userId" TEXT NOT NULL,
|
|
"appId" TEXT NOT NULL,
|
|
"order" INTEGER NOT NULL DEFAULT 0,
|
|
CONSTRAINT "UserFavorite_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
|
CONSTRAINT "UserFavorite_appId_fkey" FOREIGN KEY ("appId") REFERENCES "App" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "AppClick" (
|
|
"id" TEXT NOT NULL PRIMARY KEY,
|
|
"userId" TEXT NOT NULL,
|
|
"appId" TEXT NOT NULL,
|
|
"clickedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT "AppClick_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
|
CONSTRAINT "AppClick_appId_fkey" FOREIGN KEY ("appId") REFERENCES "App" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "NotificationChannel" (
|
|
"id" TEXT NOT NULL PRIMARY KEY,
|
|
"userId" TEXT NOT NULL,
|
|
"type" TEXT NOT NULL,
|
|
"config" TEXT NOT NULL DEFAULT '{}',
|
|
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT "NotificationChannel_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Notification" (
|
|
"id" TEXT NOT NULL PRIMARY KEY,
|
|
"userId" TEXT NOT NULL,
|
|
"appId" TEXT,
|
|
"event" TEXT NOT NULL,
|
|
"message" TEXT NOT NULL,
|
|
"sentAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"readAt" DATETIME,
|
|
CONSTRAINT "Notification_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
|
CONSTRAINT "Notification_appId_fkey" FOREIGN KEY ("appId") REFERENCES "App" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "ApiToken" (
|
|
"id" TEXT NOT NULL PRIMARY KEY,
|
|
"userId" TEXT NOT NULL,
|
|
"name" TEXT NOT NULL,
|
|
"tokenHash" TEXT NOT NULL,
|
|
"scope" TEXT NOT NULL,
|
|
"lastUsedAt" DATETIME,
|
|
"expiresAt" DATETIME,
|
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT "ApiToken_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "AuditLog" (
|
|
"id" TEXT NOT NULL PRIMARY KEY,
|
|
"userId" TEXT,
|
|
"action" TEXT NOT NULL,
|
|
"entityType" TEXT NOT NULL,
|
|
"entityId" TEXT NOT NULL,
|
|
"details" TEXT NOT NULL DEFAULT '{}',
|
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT "AuditLog_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "BoardTemplate" (
|
|
"id" TEXT NOT NULL PRIMARY KEY,
|
|
"name" TEXT NOT NULL,
|
|
"description" TEXT,
|
|
"icon" TEXT,
|
|
"config" TEXT NOT NULL DEFAULT '{}',
|
|
"isBuiltin" BOOLEAN NOT NULL DEFAULT false,
|
|
"createdById" TEXT,
|
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT "BoardTemplate_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
|
);
|
|
|
|
-- RedefineTables
|
|
PRAGMA defer_foreign_keys=ON;
|
|
PRAGMA foreign_keys=OFF;
|
|
CREATE TABLE "new_SystemSettings" (
|
|
"id" TEXT NOT NULL PRIMARY KEY DEFAULT 'singleton',
|
|
"authMode" TEXT NOT NULL DEFAULT 'local',
|
|
"registrationEnabled" BOOLEAN NOT NULL DEFAULT true,
|
|
"oauthClientId" TEXT,
|
|
"oauthClientSecret" TEXT,
|
|
"oauthDiscoveryUrl" TEXT,
|
|
"defaultTheme" TEXT NOT NULL DEFAULT 'dark',
|
|
"defaultPrimaryColor" TEXT NOT NULL DEFAULT '#6366f1',
|
|
"healthcheckDefaults" TEXT NOT NULL DEFAULT '{}',
|
|
"customCss" TEXT,
|
|
"onboardingComplete" BOOLEAN NOT NULL DEFAULT false,
|
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" DATETIME NOT NULL
|
|
);
|
|
INSERT INTO "new_SystemSettings" ("authMode", "createdAt", "defaultPrimaryColor", "defaultTheme", "healthcheckDefaults", "id", "oauthClientId", "oauthClientSecret", "oauthDiscoveryUrl", "registrationEnabled", "updatedAt") SELECT "authMode", "createdAt", "defaultPrimaryColor", "defaultTheme", "healthcheckDefaults", "id", "oauthClientId", "oauthClientSecret", "oauthDiscoveryUrl", "registrationEnabled", "updatedAt" FROM "SystemSettings";
|
|
DROP TABLE "SystemSettings";
|
|
ALTER TABLE "new_SystemSettings" RENAME TO "SystemSettings";
|
|
CREATE TABLE "new_User" (
|
|
"id" TEXT NOT NULL PRIMARY KEY,
|
|
"email" TEXT NOT NULL,
|
|
"password" TEXT,
|
|
"displayName" TEXT NOT NULL,
|
|
"avatarUrl" TEXT,
|
|
"authProvider" TEXT NOT NULL DEFAULT 'local',
|
|
"role" TEXT NOT NULL DEFAULT 'user',
|
|
"refreshToken" TEXT,
|
|
"refreshTokenExpiresAt" DATETIME,
|
|
"onboardingComplete" BOOLEAN NOT NULL DEFAULT false,
|
|
"trackRecentApps" BOOLEAN NOT NULL DEFAULT true,
|
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" DATETIME NOT NULL,
|
|
"themeMode" TEXT,
|
|
"primaryHue" INTEGER,
|
|
"primarySaturation" INTEGER,
|
|
"backgroundType" TEXT,
|
|
"locale" TEXT
|
|
);
|
|
INSERT INTO "new_User" ("authProvider", "avatarUrl", "backgroundType", "createdAt", "displayName", "email", "id", "locale", "password", "primaryHue", "primarySaturation", "refreshToken", "refreshTokenExpiresAt", "role", "themeMode", "updatedAt") SELECT "authProvider", "avatarUrl", "backgroundType", "createdAt", "displayName", "email", "id", "locale", "password", "primaryHue", "primarySaturation", "refreshToken", "refreshTokenExpiresAt", "role", "themeMode", "updatedAt" FROM "User";
|
|
DROP TABLE "User";
|
|
ALTER TABLE "new_User" RENAME TO "User";
|
|
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
|
CREATE INDEX "User_email_idx" ON "User"("email");
|
|
PRAGMA foreign_keys=ON;
|
|
PRAGMA defer_foreign_keys=OFF;
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "Tag_name_key" ON "Tag"("name");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Tag_name_idx" ON "Tag"("name");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "AppTag_appId_idx" ON "AppTag"("appId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "AppTag_tagId_idx" ON "AppTag"("tagId");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "AppTag_appId_tagId_key" ON "AppTag"("appId", "tagId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "AppLink_appId_idx" ON "AppLink"("appId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "UserFavorite_userId_idx" ON "UserFavorite"("userId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "UserFavorite_appId_idx" ON "UserFavorite"("appId");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "UserFavorite_userId_appId_key" ON "UserFavorite"("userId", "appId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "AppClick_userId_idx" ON "AppClick"("userId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "AppClick_appId_idx" ON "AppClick"("appId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "AppClick_clickedAt_idx" ON "AppClick"("clickedAt");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "NotificationChannel_userId_idx" ON "NotificationChannel"("userId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Notification_userId_idx" ON "Notification"("userId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Notification_appId_idx" ON "Notification"("appId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Notification_sentAt_idx" ON "Notification"("sentAt");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "ApiToken_tokenHash_key" ON "ApiToken"("tokenHash");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ApiToken_userId_idx" ON "ApiToken"("userId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ApiToken_tokenHash_idx" ON "ApiToken"("tokenHash");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "AuditLog_userId_idx" ON "AuditLog"("userId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "AuditLog_action_idx" ON "AuditLog"("action");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "AuditLog_entityType_entityId_idx" ON "AuditLog"("entityType", "entityId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "AuditLog_createdAt_idx" ON "AuditLog"("createdAt");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "BoardTemplate_createdById_idx" ON "BoardTemplate"("createdById");
|