security hardening, team invites, granular locking, view counts, board subscriptions, scheduled changelog, mentions, recovery codes, accessibility and hover states
This commit is contained in:
270
packages/api/prisma/migrations/20260319185954_init/migration.sql
Normal file
270
packages/api/prisma/migrations/20260319185954_init/migration.sql
Normal file
@@ -0,0 +1,270 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "AuthMethod" AS ENUM ('COOKIE', 'PASSKEY');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "PostType" AS ENUM ('FEATURE_REQUEST', 'BUG_REPORT');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "PostStatus" AS ENUM ('OPEN', 'UNDER_REVIEW', 'PLANNED', 'IN_PROGRESS', 'DONE', 'DECLINED');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Board" (
|
||||
"id" TEXT NOT NULL,
|
||||
"slug" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"externalUrl" TEXT,
|
||||
"isArchived" BOOLEAN NOT NULL DEFAULT false,
|
||||
"voteBudget" INTEGER NOT NULL DEFAULT 10,
|
||||
"voteBudgetReset" TEXT NOT NULL DEFAULT 'monthly',
|
||||
"lastBudgetReset" TIMESTAMP(3),
|
||||
"allowMultiVote" BOOLEAN NOT NULL DEFAULT false,
|
||||
"rssEnabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
"rssFeedCount" INTEGER NOT NULL DEFAULT 50,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Board_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" TEXT NOT NULL,
|
||||
"authMethod" "AuthMethod" NOT NULL DEFAULT 'COOKIE',
|
||||
"tokenHash" TEXT,
|
||||
"username" TEXT,
|
||||
"usernameIdx" TEXT,
|
||||
"displayName" TEXT,
|
||||
"darkMode" TEXT NOT NULL DEFAULT 'system',
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Passkey" (
|
||||
"id" TEXT NOT NULL,
|
||||
"credentialId" TEXT NOT NULL,
|
||||
"credentialIdIdx" TEXT NOT NULL,
|
||||
"credentialPublicKey" BYTEA NOT NULL,
|
||||
"counter" BIGINT NOT NULL,
|
||||
"credentialDeviceType" TEXT NOT NULL,
|
||||
"credentialBackedUp" BOOLEAN NOT NULL,
|
||||
"transports" TEXT,
|
||||
"userId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "Passkey_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Post" (
|
||||
"id" TEXT NOT NULL,
|
||||
"type" "PostType" NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"description" JSONB NOT NULL,
|
||||
"status" "PostStatus" NOT NULL DEFAULT 'OPEN',
|
||||
"category" TEXT,
|
||||
"voteCount" INTEGER NOT NULL DEFAULT 0,
|
||||
"isPinned" BOOLEAN NOT NULL DEFAULT false,
|
||||
"boardId" TEXT NOT NULL,
|
||||
"authorId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Post_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "StatusChange" (
|
||||
"id" TEXT NOT NULL,
|
||||
"postId" TEXT NOT NULL,
|
||||
"fromStatus" "PostStatus" NOT NULL,
|
||||
"toStatus" "PostStatus" NOT NULL,
|
||||
"changedBy" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "StatusChange_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Comment" (
|
||||
"id" TEXT NOT NULL,
|
||||
"body" TEXT NOT NULL,
|
||||
"postId" TEXT NOT NULL,
|
||||
"authorId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Comment_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Reaction" (
|
||||
"id" TEXT NOT NULL,
|
||||
"emoji" TEXT NOT NULL,
|
||||
"commentId" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "Reaction_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Vote" (
|
||||
"id" TEXT NOT NULL,
|
||||
"weight" INTEGER NOT NULL DEFAULT 1,
|
||||
"postId" TEXT NOT NULL,
|
||||
"voterId" TEXT NOT NULL,
|
||||
"budgetPeriod" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "Vote_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "AdminUser" (
|
||||
"id" TEXT NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"passwordHash" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "AdminUser_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "AdminResponse" (
|
||||
"id" TEXT NOT NULL,
|
||||
"body" TEXT NOT NULL,
|
||||
"postId" TEXT NOT NULL,
|
||||
"adminId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "AdminResponse_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ActivityEvent" (
|
||||
"id" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"boardId" TEXT NOT NULL,
|
||||
"postId" TEXT,
|
||||
"metadata" JSONB NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "ActivityEvent_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "PushSubscription" (
|
||||
"id" TEXT NOT NULL,
|
||||
"endpoint" TEXT NOT NULL,
|
||||
"endpointIdx" TEXT NOT NULL,
|
||||
"keysP256dh" TEXT NOT NULL,
|
||||
"keysAuth" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"boardId" TEXT,
|
||||
"postId" TEXT,
|
||||
"failureCount" INTEGER NOT NULL DEFAULT 0,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "PushSubscription_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Category" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"slug" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "Category_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Board_slug_key" ON "Board"("slug");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_tokenHash_key" ON "User"("tokenHash");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_usernameIdx_key" ON "User"("usernameIdx");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Passkey_credentialIdIdx_key" ON "Passkey"("credentialIdIdx");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Reaction_commentId_userId_emoji_key" ON "Reaction"("commentId", "userId", "emoji");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Vote_postId_voterId_key" ON "Vote"("postId", "voterId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "AdminUser_email_key" ON "AdminUser"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ActivityEvent_boardId_createdAt_idx" ON "ActivityEvent"("boardId", "createdAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ActivityEvent_createdAt_idx" ON "ActivityEvent"("createdAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "PushSubscription_endpointIdx_key" ON "PushSubscription"("endpointIdx");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Category_name_key" ON "Category"("name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Category_slug_key" ON "Category"("slug");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Passkey" ADD CONSTRAINT "Passkey_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Post" ADD CONSTRAINT "Post_boardId_fkey" FOREIGN KEY ("boardId") REFERENCES "Board"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Post" ADD CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "StatusChange" ADD CONSTRAINT "StatusChange_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Comment" ADD CONSTRAINT "Comment_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Comment" ADD CONSTRAINT "Comment_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Reaction" ADD CONSTRAINT "Reaction_commentId_fkey" FOREIGN KEY ("commentId") REFERENCES "Comment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Reaction" ADD CONSTRAINT "Reaction_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Vote" ADD CONSTRAINT "Vote_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Vote" ADD CONSTRAINT "Vote_voterId_fkey" FOREIGN KEY ("voterId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "AdminResponse" ADD CONSTRAINT "AdminResponse_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "AdminResponse" ADD CONSTRAINT "AdminResponse_adminId_fkey" FOREIGN KEY ("adminId") REFERENCES "AdminUser"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ActivityEvent" ADD CONSTRAINT "ActivityEvent_boardId_fkey" FOREIGN KEY ("boardId") REFERENCES "Board"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ActivityEvent" ADD CONSTRAINT "ActivityEvent_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "PushSubscription" ADD CONSTRAINT "PushSubscription_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "PushSubscription" ADD CONSTRAINT "PushSubscription_boardId_fkey" FOREIGN KEY ("boardId") REFERENCES "Board"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "PushSubscription" ADD CONSTRAINT "PushSubscription_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,28 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "EditHistory" (
|
||||
"id" TEXT NOT NULL,
|
||||
"postId" TEXT,
|
||||
"commentId" TEXT,
|
||||
"editedBy" TEXT NOT NULL,
|
||||
"previousTitle" TEXT,
|
||||
"previousDescription" JSONB,
|
||||
"previousBody" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "EditHistory_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "EditHistory_postId_createdAt_idx" ON "EditHistory"("postId", "createdAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "EditHistory_commentId_createdAt_idx" ON "EditHistory"("commentId", "createdAt");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "EditHistory" ADD CONSTRAINT "EditHistory_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "EditHistory" ADD CONSTRAINT "EditHistory_commentId_fkey" FOREIGN KEY ("commentId") REFERENCES "Comment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "EditHistory" ADD CONSTRAINT "EditHistory_editedBy_fkey" FOREIGN KEY ("editedBy") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Post" ADD COLUMN "isEditLocked" BOOLEAN NOT NULL DEFAULT false;
|
||||
@@ -0,0 +1,8 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "EditHistory" ALTER COLUMN "editedBy" DROP NOT NULL;
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "EditHistory" DROP CONSTRAINT "EditHistory_editedBy_fkey";
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "EditHistory" ADD CONSTRAINT "EditHistory_editedBy_fkey" FOREIGN KEY ("editedBy") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,16 @@
|
||||
CREATE TABLE "SiteSettings" (
|
||||
"id" TEXT NOT NULL DEFAULT 'default',
|
||||
"appName" TEXT NOT NULL DEFAULT 'Echoboard',
|
||||
"logoUrl" TEXT,
|
||||
"faviconUrl" TEXT,
|
||||
"accentColor" TEXT NOT NULL DEFAULT '#F59E0B',
|
||||
"headerFont" TEXT,
|
||||
"bodyFont" TEXT,
|
||||
"poweredByVisible" BOOLEAN NOT NULL DEFAULT true,
|
||||
"customCss" TEXT,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "SiteSettings_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
INSERT INTO "SiteSettings" ("id", "updatedAt") VALUES ('default', NOW());
|
||||
3
packages/api/prisma/migrations/migration_lock.toml
Normal file
3
packages/api/prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "postgresql"
|
||||
@@ -17,32 +17,46 @@ enum PostType {
|
||||
BUG_REPORT
|
||||
}
|
||||
|
||||
enum PostStatus {
|
||||
OPEN
|
||||
UNDER_REVIEW
|
||||
PLANNED
|
||||
IN_PROGRESS
|
||||
DONE
|
||||
DECLINED
|
||||
}
|
||||
|
||||
model Board {
|
||||
id String @id @default(cuid())
|
||||
slug String @unique
|
||||
name String
|
||||
description String?
|
||||
externalUrl String?
|
||||
iconName String?
|
||||
iconColor String?
|
||||
isArchived Boolean @default(false)
|
||||
voteBudget Int @default(10)
|
||||
voteBudgetReset String @default("monthly")
|
||||
lastBudgetReset DateTime?
|
||||
allowMultiVote Boolean @default(false)
|
||||
rssEnabled Boolean @default(true)
|
||||
rssFeedCount Int @default(50)
|
||||
staleDays Int @default(0)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
posts Post[]
|
||||
activityEvents ActivityEvent[]
|
||||
pushSubscriptions PushSubscription[]
|
||||
statusConfig BoardStatus[]
|
||||
templates BoardTemplate[]
|
||||
changelogEntries ChangelogEntry[]
|
||||
}
|
||||
|
||||
model BoardStatus {
|
||||
id String @id @default(cuid())
|
||||
boardId String
|
||||
status String
|
||||
label String
|
||||
color String
|
||||
position Int @default(0)
|
||||
enabled Boolean @default(true)
|
||||
|
||||
board Board @relation(fields: [boardId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([boardId, status])
|
||||
@@index([boardId, position])
|
||||
}
|
||||
|
||||
model User {
|
||||
@@ -52,16 +66,35 @@ model User {
|
||||
username String?
|
||||
usernameIdx String? @unique
|
||||
displayName String?
|
||||
avatarPath String?
|
||||
darkMode String @default("system")
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
passkeys Passkey[]
|
||||
posts Post[]
|
||||
comments Comment[]
|
||||
reactions Reaction[]
|
||||
votes Vote[]
|
||||
passkeys Passkey[]
|
||||
posts Post[]
|
||||
comments Comment[]
|
||||
reactions Reaction[]
|
||||
votes Vote[]
|
||||
notifications Notification[]
|
||||
pushSubscriptions PushSubscription[]
|
||||
attachments Attachment[]
|
||||
adminLink AdminUser?
|
||||
edits EditHistory[]
|
||||
recoveryCode RecoveryCode?
|
||||
}
|
||||
|
||||
model RecoveryCode {
|
||||
id String @id @default(cuid())
|
||||
codeHash String
|
||||
phraseIdx String @unique
|
||||
userId String @unique
|
||||
expiresAt DateTime
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([expiresAt])
|
||||
}
|
||||
|
||||
model Passkey {
|
||||
@@ -80,18 +113,26 @@ model Passkey {
|
||||
}
|
||||
|
||||
model Post {
|
||||
id String @id @default(cuid())
|
||||
type PostType
|
||||
title String
|
||||
description Json
|
||||
status PostStatus @default(OPEN)
|
||||
category String?
|
||||
voteCount Int @default(0)
|
||||
isPinned Boolean @default(false)
|
||||
boardId String
|
||||
authorId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
id String @id @default(cuid())
|
||||
type PostType
|
||||
title String
|
||||
description Json
|
||||
status String @default("OPEN")
|
||||
statusReason String?
|
||||
category String?
|
||||
templateId String?
|
||||
voteCount Int @default(0)
|
||||
viewCount Int @default(0)
|
||||
isPinned Boolean @default(false)
|
||||
isEditLocked Boolean @default(false)
|
||||
isThreadLocked Boolean @default(false)
|
||||
isVotingLocked Boolean @default(false)
|
||||
onBehalfOf String?
|
||||
boardId String
|
||||
authorId String
|
||||
lastActivityAt DateTime @default(now())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
board Board @relation(fields: [boardId], references: [id], onDelete: Cascade)
|
||||
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
|
||||
@@ -99,15 +140,23 @@ model Post {
|
||||
comments Comment[]
|
||||
votes Vote[]
|
||||
adminResponses AdminResponse[]
|
||||
adminNotes AdminNote[]
|
||||
notifications Notification[]
|
||||
activityEvents ActivityEvent[]
|
||||
pushSubscriptions PushSubscription[]
|
||||
tags PostTag[]
|
||||
attachments Attachment[]
|
||||
editHistory EditHistory[]
|
||||
|
||||
@@index([boardId, status])
|
||||
}
|
||||
|
||||
model StatusChange {
|
||||
id String @id @default(cuid())
|
||||
postId String
|
||||
fromStatus PostStatus
|
||||
toStatus PostStatus
|
||||
fromStatus String
|
||||
toStatus String
|
||||
reason String?
|
||||
changedBy String
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@ -115,16 +164,28 @@ model StatusChange {
|
||||
}
|
||||
|
||||
model Comment {
|
||||
id String @id @default(cuid())
|
||||
body String
|
||||
postId String
|
||||
authorId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
id String @id @default(cuid())
|
||||
body String
|
||||
postId String
|
||||
authorId String
|
||||
replyToId String?
|
||||
isAdmin Boolean @default(false)
|
||||
isEditLocked Boolean @default(false)
|
||||
adminUserId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
|
||||
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
|
||||
reactions Reaction[]
|
||||
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
|
||||
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
|
||||
replyTo Comment? @relation("CommentReplies", fields: [replyToId], references: [id], onDelete: SetNull)
|
||||
replies Comment[] @relation("CommentReplies")
|
||||
adminUser AdminUser? @relation(fields: [adminUserId], references: [id], onDelete: SetNull)
|
||||
reactions Reaction[]
|
||||
attachments Attachment[]
|
||||
editHistory EditHistory[]
|
||||
|
||||
@@index([replyToId])
|
||||
@@index([postId, createdAt])
|
||||
}
|
||||
|
||||
model Reaction {
|
||||
@@ -143,6 +204,7 @@ model Reaction {
|
||||
model Vote {
|
||||
id String @id @default(cuid())
|
||||
weight Int @default(1)
|
||||
importance String?
|
||||
postId String
|
||||
voterId String
|
||||
budgetPeriod String
|
||||
@@ -154,13 +216,50 @@ model Vote {
|
||||
@@unique([postId, voterId])
|
||||
}
|
||||
|
||||
model AdminUser {
|
||||
id String @id @default(cuid())
|
||||
email String @unique
|
||||
passwordHash String
|
||||
createdAt DateTime @default(now())
|
||||
enum TeamRole {
|
||||
SUPER_ADMIN
|
||||
ADMIN
|
||||
MODERATOR
|
||||
}
|
||||
|
||||
responses AdminResponse[]
|
||||
model AdminUser {
|
||||
id String @id @default(cuid())
|
||||
email String? @unique
|
||||
passwordHash String?
|
||||
role TeamRole @default(ADMIN)
|
||||
displayName String?
|
||||
teamTitle String?
|
||||
linkedUserId String? @unique
|
||||
invitedById String?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
linkedUser User? @relation(fields: [linkedUserId], references: [id], onDelete: SetNull)
|
||||
invitedBy AdminUser? @relation("TeamInviter", fields: [invitedById], references: [id], onDelete: SetNull)
|
||||
invitees AdminUser[] @relation("TeamInviter")
|
||||
responses AdminResponse[]
|
||||
notes AdminNote[]
|
||||
comments Comment[]
|
||||
createdInvites TeamInvite[] @relation("InviteCreator")
|
||||
claimedInvite TeamInvite? @relation("InviteClaimer")
|
||||
}
|
||||
|
||||
model TeamInvite {
|
||||
id String @id @default(cuid())
|
||||
tokenHash String @unique
|
||||
role TeamRole
|
||||
label String?
|
||||
expiresAt DateTime
|
||||
createdById String
|
||||
claimedById String? @unique
|
||||
claimedAt DateTime?
|
||||
recoveryHash String?
|
||||
recoveryIdx String?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
createdBy AdminUser @relation("InviteCreator", fields: [createdById], references: [id], onDelete: Cascade)
|
||||
claimedBy AdminUser? @relation("InviteClaimer", fields: [claimedById], references: [id], onDelete: SetNull)
|
||||
|
||||
@@index([expiresAt])
|
||||
}
|
||||
|
||||
model AdminResponse {
|
||||
@@ -190,15 +289,16 @@ model ActivityEvent {
|
||||
}
|
||||
|
||||
model PushSubscription {
|
||||
id String @id @default(cuid())
|
||||
endpoint String
|
||||
endpointIdx String @unique
|
||||
keysP256dh String
|
||||
keysAuth String
|
||||
userId String
|
||||
boardId String?
|
||||
postId String?
|
||||
createdAt DateTime @default(now())
|
||||
id String @id @default(cuid())
|
||||
endpoint String
|
||||
endpointIdx String @unique
|
||||
keysP256dh String
|
||||
keysAuth String
|
||||
userId String
|
||||
boardId String?
|
||||
postId String?
|
||||
failureCount Int @default(0)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
board Board? @relation(fields: [boardId], references: [id], onDelete: Cascade)
|
||||
@@ -211,3 +311,151 @@ model Category {
|
||||
slug String @unique
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
model Notification {
|
||||
id String @id @default(cuid())
|
||||
type String
|
||||
title String
|
||||
body String
|
||||
postId String?
|
||||
userId String
|
||||
read Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
post Post? @relation(fields: [postId], references: [id], onDelete: SetNull)
|
||||
|
||||
@@index([userId, read, createdAt])
|
||||
}
|
||||
|
||||
model Webhook {
|
||||
id String @id @default(cuid())
|
||||
url String
|
||||
secret String
|
||||
events String[] @default(["status_changed", "post_created", "comment_added"])
|
||||
active Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
model PostMerge {
|
||||
id String @id @default(cuid())
|
||||
sourcePostId String
|
||||
targetPostId String
|
||||
mergedBy String
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([sourcePostId])
|
||||
}
|
||||
|
||||
model ChangelogEntry {
|
||||
id String @id @default(cuid())
|
||||
title String
|
||||
body String
|
||||
boardId String?
|
||||
publishedAt DateTime @default(now())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
board Board? @relation(fields: [boardId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([boardId, publishedAt])
|
||||
}
|
||||
|
||||
model AdminNote {
|
||||
id String @id @default(cuid())
|
||||
body String
|
||||
postId String
|
||||
adminId String
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
|
||||
admin AdminUser @relation(fields: [adminId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
model Tag {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
color String @default("#6366F1")
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
posts PostTag[]
|
||||
}
|
||||
|
||||
model PostTag {
|
||||
postId String
|
||||
tagId String
|
||||
|
||||
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
|
||||
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@id([postId, tagId])
|
||||
}
|
||||
|
||||
model Attachment {
|
||||
id String @id @default(cuid())
|
||||
filename String
|
||||
path String
|
||||
size Int
|
||||
mimeType String
|
||||
postId String?
|
||||
commentId String?
|
||||
uploaderId String
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
post Post? @relation(fields: [postId], references: [id], onDelete: Cascade)
|
||||
comment Comment? @relation(fields: [commentId], references: [id], onDelete: Cascade)
|
||||
uploader User @relation(fields: [uploaderId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
model BoardTemplate {
|
||||
id String @id @default(cuid())
|
||||
boardId String
|
||||
name String
|
||||
fields Json
|
||||
isDefault Boolean @default(false)
|
||||
position Int @default(0)
|
||||
|
||||
board Board @relation(fields: [boardId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([boardId, position])
|
||||
}
|
||||
|
||||
model SiteSettings {
|
||||
id String @id @default("default")
|
||||
appName String @default("Echoboard")
|
||||
logoUrl String?
|
||||
faviconUrl String?
|
||||
accentColor String @default("#F59E0B")
|
||||
headerFont String?
|
||||
bodyFont String?
|
||||
poweredByVisible Boolean @default(true)
|
||||
customCss String?
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model BlockedToken {
|
||||
id String @id @default(cuid())
|
||||
tokenHash String @unique
|
||||
expiresAt DateTime
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([expiresAt])
|
||||
}
|
||||
|
||||
model EditHistory {
|
||||
id String @id @default(cuid())
|
||||
postId String?
|
||||
commentId String?
|
||||
editedBy String?
|
||||
previousTitle String?
|
||||
previousDescription Json?
|
||||
previousBody String?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
post Post? @relation(fields: [postId], references: [id], onDelete: Cascade)
|
||||
comment Comment? @relation(fields: [commentId], references: [id], onDelete: Cascade)
|
||||
editor User? @relation(fields: [editedBy], references: [id], onDelete: SetNull)
|
||||
|
||||
@@index([postId, createdAt])
|
||||
@@index([commentId, createdAt])
|
||||
}
|
||||
|
||||
1298
packages/api/prisma/seed.ts
Normal file
1298
packages/api/prisma/seed.ts
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user