feat: Phases 4-7 — Full Feature Expansion (26 features)
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.
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
-- 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");
|
||||
+176
-6
@@ -17,6 +17,8 @@ model User {
|
||||
role String @default("user") // admin | user
|
||||
refreshToken String?
|
||||
refreshTokenExpiresAt DateTime?
|
||||
onboardingComplete Boolean @default(false)
|
||||
trackRecentApps Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@ -26,9 +28,16 @@ model User {
|
||||
backgroundType String?
|
||||
locale String?
|
||||
|
||||
groups UserGroup[]
|
||||
createdApps App[]
|
||||
boards Board[]
|
||||
groups UserGroup[]
|
||||
createdApps App[]
|
||||
boards Board[]
|
||||
favorites UserFavorite[]
|
||||
clicks AppClick[]
|
||||
notificationChannels NotificationChannel[]
|
||||
notifications Notification[]
|
||||
apiTokens ApiToken[]
|
||||
auditLogs AuditLog[]
|
||||
boardTemplates BoardTemplate[]
|
||||
|
||||
@@index([email])
|
||||
}
|
||||
@@ -75,9 +84,14 @@ model App {
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull)
|
||||
statuses AppStatus[]
|
||||
widgets Widget[]
|
||||
createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull)
|
||||
statuses AppStatus[]
|
||||
widgets Widget[]
|
||||
appTags AppTag[]
|
||||
links AppLink[]
|
||||
clicks AppClick[]
|
||||
notifications Notification[]
|
||||
favorites UserFavorite[]
|
||||
|
||||
@@index([name])
|
||||
@@index([category])
|
||||
@@ -105,6 +119,14 @@ model Board {
|
||||
isDefault Boolean @default(false)
|
||||
isGuestAccessible Boolean @default(false)
|
||||
backgroundConfig String? // JSON stored as string for SQLite
|
||||
themeHue Int?
|
||||
themeSaturation Int?
|
||||
backgroundType String?
|
||||
cardSize String?
|
||||
wallpaperUrl String?
|
||||
wallpaperBlur Int?
|
||||
wallpaperOverlay Float?
|
||||
customCss String?
|
||||
createdById String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@@ -122,6 +144,7 @@ model Section {
|
||||
icon String?
|
||||
order Int @default(0)
|
||||
isExpandedByDefault Boolean @default(true)
|
||||
cardSize String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@ -173,6 +196,153 @@ model SystemSettings {
|
||||
defaultTheme String @default("dark")
|
||||
defaultPrimaryColor String @default("#6366f1")
|
||||
healthcheckDefaults String @default("{}") // JSON stored as string for SQLite
|
||||
customCss String?
|
||||
onboardingComplete Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
// --- New models for Phases 4-7 ---
|
||||
|
||||
model Tag {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
color String?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
appTags AppTag[]
|
||||
|
||||
@@index([name])
|
||||
}
|
||||
|
||||
model AppTag {
|
||||
id String @id @default(cuid())
|
||||
appId String
|
||||
tagId String
|
||||
|
||||
app App @relation(fields: [appId], references: [id], onDelete: Cascade)
|
||||
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([appId, tagId])
|
||||
@@index([appId])
|
||||
@@index([tagId])
|
||||
}
|
||||
|
||||
model AppLink {
|
||||
id String @id @default(cuid())
|
||||
appId String
|
||||
label String
|
||||
url String
|
||||
icon String?
|
||||
order Int @default(0)
|
||||
|
||||
app App @relation(fields: [appId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([appId])
|
||||
}
|
||||
|
||||
model UserFavorite {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
appId String
|
||||
order Int @default(0)
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
app App @relation(fields: [appId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([userId, appId])
|
||||
@@index([userId])
|
||||
@@index([appId])
|
||||
}
|
||||
|
||||
model AppClick {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
appId String
|
||||
clickedAt DateTime @default(now())
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
app App @relation(fields: [appId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([userId])
|
||||
@@index([appId])
|
||||
@@index([clickedAt])
|
||||
}
|
||||
|
||||
model NotificationChannel {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
type String // discord | slack | telegram | http
|
||||
config String @default("{}") // JSON stored as string for SQLite
|
||||
enabled Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
model Notification {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
appId String?
|
||||
event String // app_online | app_offline | app_degraded
|
||||
message String
|
||||
sentAt DateTime @default(now())
|
||||
readAt DateTime?
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
app App? @relation(fields: [appId], references: [id], onDelete: SetNull)
|
||||
|
||||
@@index([userId])
|
||||
@@index([appId])
|
||||
@@index([sentAt])
|
||||
}
|
||||
|
||||
model ApiToken {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
name String
|
||||
tokenHash String @unique
|
||||
scope String // read | write | admin
|
||||
lastUsedAt DateTime?
|
||||
expiresAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([userId])
|
||||
@@index([tokenHash])
|
||||
}
|
||||
|
||||
model AuditLog {
|
||||
id String @id @default(cuid())
|
||||
userId String?
|
||||
action String // user_created | user_deleted | etc.
|
||||
entityType String
|
||||
entityId String
|
||||
details String @default("{}") // JSON stored as string for SQLite
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
|
||||
|
||||
@@index([userId])
|
||||
@@index([action])
|
||||
@@index([entityType, entityId])
|
||||
@@index([createdAt])
|
||||
}
|
||||
|
||||
model BoardTemplate {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
description String?
|
||||
icon String?
|
||||
config String @default("{}") // JSON stored as string for SQLite
|
||||
isBuiltin Boolean @default(false)
|
||||
createdById String?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull)
|
||||
|
||||
@@index([createdById])
|
||||
}
|
||||
|
||||
+2
-1
@@ -386,7 +386,8 @@ async function main() {
|
||||
type: 'note',
|
||||
order: 2,
|
||||
config: JSON.stringify({
|
||||
content: '# Welcome\n\nThis is your **home dashboard**. Use sections to organize apps, bookmarks, notes, and more.\n\n- Drag to reorder\n- Click to launch\n- Edit to customize',
|
||||
content:
|
||||
'# Welcome\n\nThis is your **home dashboard**. Use sections to organize apps, bookmarks, notes, and more.\n\n- Drag to reorder\n- Click to launch\n- Edit to customize',
|
||||
format: 'markdown'
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user