diff --git a/.env.example b/.env.example index e61788e..5884b6b 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,7 @@ POSTGRES_DB=postgres POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres -POSTGRES_HOST=localhost +POSTGRES_HOST=db POSTGRES_PORT=5432 POSTGRES_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} URL=http://localhost:3000 \ No newline at end of file diff --git a/package.json b/package.json index da4b332..f145e56 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@mantine/hooks": "^7.15.1", "@mantine/modals": "^7.15.1", "@mantine/spotlight": "^7.15.1", + "@prisma/client": "^6.1.0", "@tabler/icons-react": "^3.24.0", "next": "15.1.0", "react": "^19.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ad28bf9..327d3b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: '@mantine/spotlight': specifier: ^7.15.1 version: 7.15.1(@mantine/core@7.15.1(@mantine/hooks@7.15.1(react@19.0.0))(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@mantine/hooks@7.15.1(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@prisma/client': + specifier: ^6.1.0 + version: 6.1.0 '@tabler/icons-react': specifier: ^3.24.0 version: 3.24.0(react@19.0.0) @@ -396,6 +399,15 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} + '@prisma/client@6.1.0': + resolution: {integrity: sha512-AbQYc5+EJKm1Ydfq3KxwcGiy7wIbm4/QbjCKWWoNROtvy7d6a3gmAGkKjK0iUCzh+rHV8xDhD5Cge8ke/kiy5Q==} + engines: {node: '>=18.18'} + peerDependencies: + prisma: '*' + peerDependenciesMeta: + prisma: + optional: true + '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} @@ -2170,6 +2182,8 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} + '@prisma/client@6.1.0': {} + '@rtsao/scc@1.1.0': {} '@rushstack/eslint-patch@1.10.4': {} diff --git a/prisma/migrations/20241229122133_init/migration.sql b/prisma/migrations/20241229122133_init/migration.sql new file mode 100644 index 0000000..89f059e --- /dev/null +++ b/prisma/migrations/20241229122133_init/migration.sql @@ -0,0 +1,275 @@ +-- CreateEnum +CREATE TYPE "UserStatus" AS ENUM ('ONLINE', 'AWAY', 'BUSY', 'OFFLINE'); + +-- CreateEnum +CREATE TYPE "ChannelType" AS ENUM ('PUBLIC', 'PRIVATE', 'DM'); + +-- CreateEnum +CREATE TYPE "ChannelMemberRole" AS ENUM ('ADMIN', 'MEMBER', 'VIEWER'); + +-- CreateEnum +CREATE TYPE "MessageType" AS ENUM ('NORMAL', 'IMPORTANT', 'PROJECT'); + +-- CreateEnum +CREATE TYPE "AssetType" AS ENUM ('IMAGE', 'VIDEO'); + +-- CreateTable +CREATE TABLE "User" ( + "id" UUID NOT NULL, + "slug" TEXT NOT NULL, + "name" TEXT NOT NULL, + "email" TEXT NOT NULL, + "password" TEXT NOT NULL, + "iconUrl" TEXT, + "status" "UserStatus" NOT NULL, + "description" TEXT, + "roleId" UUID NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "UserRole" ( + "id" UUID NOT NULL, + "name" TEXT NOT NULL, + "permissions" JSONB NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "UserRole_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Workspace" ( + "id" UUID NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Workspace_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "WorkspaceMember" ( + "id" UUID NOT NULL, + "userId" UUID NOT NULL, + "workspaceId" UUID NOT NULL, + "role" TEXT NOT NULL, + "joinDate" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "leaveDate" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "WorkspaceMember_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Channel" ( + "id" UUID NOT NULL, + "slug" TEXT NOT NULL, + "name" TEXT NOT NULL, + "workspaceId" UUID NOT NULL, + "creatorId" UUID, + "description" TEXT, + "type" "ChannelType" NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Channel_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Thread" ( + "id" UUID NOT NULL, + "parentChannelId" UUID NOT NULL, + "creatorId" UUID NOT NULL, + "messageId" UUID NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Thread_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "ThreadMember" ( + "threadId" UUID NOT NULL, + "userId" UUID NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ThreadMember_pkey" PRIMARY KEY ("threadId","userId") +); + +-- CreateTable +CREATE TABLE "ChannelMember" ( + "userId" UUID NOT NULL, + "channelId" UUID NOT NULL, + "role" "ChannelMemberRole" NOT NULL, + "joinDate" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "lastAccessedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ChannelMember_pkey" PRIMARY KEY ("channelId","userId") +); + +-- CreateTable +CREATE TABLE "Message" ( + "id" UUID NOT NULL, + "channelId" UUID NOT NULL, + "threadId" UUID, + "userId" UUID NOT NULL, + "content" TEXT NOT NULL, + "type" "MessageType" NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Message_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Asset" ( + "id" UUID NOT NULL, + "messageId" UUID NOT NULL, + "type" "AssetType" NOT NULL, + "url" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Asset_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Reaction" ( + "messageId" UUID NOT NULL, + "userId" UUID NOT NULL, + "emoji" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Reaction_pkey" PRIMARY KEY ("messageId","userId") +); + +-- CreateTable +CREATE TABLE "PinnedMessage" ( + "channelId" UUID NOT NULL, + "messageId" UUID NOT NULL, + "pinnedBy" UUID NOT NULL, + "pinnedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "PinnedMessage_pkey" PRIMARY KEY ("channelId","messageId","pinnedBy") +); + +-- CreateTable +CREATE TABLE "Bookmark" ( + "userId" UUID NOT NULL, + "messageId" UUID NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Bookmark_pkey" PRIMARY KEY ("messageId","userId") +); + +-- CreateTable +CREATE TABLE "Mention" ( + "messageId" UUID NOT NULL, + "mentionedUserId" UUID NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Mention_pkey" PRIMARY KEY ("messageId","mentionedUserId") +); + +-- CreateTable +CREATE TABLE "CustomEmoji" ( + "id" UUID NOT NULL, + "workspaceId" UUID NOT NULL, + "name" TEXT NOT NULL, + "url" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "CustomEmoji_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_slug_key" ON "User"("slug"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "Channel_slug_key" ON "Channel"("slug"); + +-- AddForeignKey +ALTER TABLE "User" ADD CONSTRAINT "User_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "UserRole"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "WorkspaceMember" ADD CONSTRAINT "WorkspaceMember_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "WorkspaceMember" ADD CONSTRAINT "WorkspaceMember_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "Workspace"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Channel" ADD CONSTRAINT "Channel_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "Workspace"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Channel" ADD CONSTRAINT "Channel_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Thread" ADD CONSTRAINT "Thread_parentChannelId_fkey" FOREIGN KEY ("parentChannelId") REFERENCES "Channel"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Thread" ADD CONSTRAINT "Thread_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Thread" ADD CONSTRAINT "Thread_messageId_fkey" FOREIGN KEY ("messageId") REFERENCES "Message"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ThreadMember" ADD CONSTRAINT "ThreadMember_threadId_fkey" FOREIGN KEY ("threadId") REFERENCES "Thread"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ThreadMember" ADD CONSTRAINT "ThreadMember_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ChannelMember" ADD CONSTRAINT "ChannelMember_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ChannelMember" ADD CONSTRAINT "ChannelMember_channelId_fkey" FOREIGN KEY ("channelId") REFERENCES "Channel"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Message" ADD CONSTRAINT "Message_channelId_fkey" FOREIGN KEY ("channelId") REFERENCES "Channel"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Message" ADD CONSTRAINT "Message_threadId_fkey" FOREIGN KEY ("threadId") REFERENCES "Thread"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Message" ADD CONSTRAINT "Message_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Asset" ADD CONSTRAINT "Asset_messageId_fkey" FOREIGN KEY ("messageId") REFERENCES "Message"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Reaction" ADD CONSTRAINT "Reaction_messageId_fkey" FOREIGN KEY ("messageId") REFERENCES "Message"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Reaction" ADD CONSTRAINT "Reaction_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PinnedMessage" ADD CONSTRAINT "PinnedMessage_channelId_fkey" FOREIGN KEY ("channelId") REFERENCES "Channel"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PinnedMessage" ADD CONSTRAINT "PinnedMessage_messageId_fkey" FOREIGN KEY ("messageId") REFERENCES "Message"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PinnedMessage" ADD CONSTRAINT "PinnedMessage_pinnedBy_fkey" FOREIGN KEY ("pinnedBy") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Bookmark" ADD CONSTRAINT "Bookmark_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Bookmark" ADD CONSTRAINT "Bookmark_messageId_fkey" FOREIGN KEY ("messageId") REFERENCES "Message"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Mention" ADD CONSTRAINT "Mention_messageId_fkey" FOREIGN KEY ("messageId") REFERENCES "Message"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Mention" ADD CONSTRAINT "Mention_mentionedUserId_fkey" FOREIGN KEY ("mentionedUserId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "CustomEmoji" ADD CONSTRAINT "CustomEmoji_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "Workspace"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20250104110708_fix_leave_date_nullable/migration.sql b/prisma/migrations/20250104110708_fix_leave_date_nullable/migration.sql new file mode 100644 index 0000000..96d31e3 --- /dev/null +++ b/prisma/migrations/20250104110708_fix_leave_date_nullable/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "ChannelMember" ALTER COLUMN "lastAccessedAt" SET DEFAULT CURRENT_TIMESTAMP; + +-- AlterTable +ALTER TABLE "WorkspaceMember" ALTER COLUMN "leaveDate" DROP NOT NULL; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..648c57f --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -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" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..52ad23b --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,237 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? +// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("POSTGRES_URL") +} + +model User { + id String @id @default(uuid()) @db.Uuid + slug String @unique + name String + email String @unique + password String + iconUrl String? + status UserStatus + description String? + role UserRole @relation(fields: [roleId], references: [id]) + roleId String @db.Uuid + workspaces WorkspaceMember[] + createdChannels Channel[] + createdThreads Thread[] + threads ThreadMember[] + channels ChannelMember[] + messages Message[] + reactions Reaction[] + pinnedMessages PinnedMessage[] + bookmarks Bookmark[] + mentions Mention[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model UserRole { + id String @id @default(uuid()) @db.Uuid + name String + permissions Json + users User[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Workspace { + id String @id @default(uuid()) @db.Uuid + name String + description String? + users WorkspaceMember[] + channels Channel[] + customEmojis CustomEmoji[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model WorkspaceMember { + id String @id @default(uuid()) @db.Uuid + userId String @db.Uuid + user User @relation(fields: [userId], references: [id]) + workspaceId String @db.Uuid + workspace Workspace @relation(fields: [workspaceId], references: [id]) + role String + joinDate DateTime @default(now()) + leaveDate DateTime? +} + +model Channel { + id String @id @default(uuid()) @db.Uuid + slug String @unique + name String + workspaceId String @db.Uuid + workspace Workspace @relation(fields: [workspaceId], references: [id]) + creatorId String? @db.Uuid + creator User? @relation(fields: [creatorId], references: [id]) + description String? + type ChannelType + threads Thread[] + members ChannelMember[] + messages Message[] + pinnedMessages PinnedMessage[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Thread { + id String @id @default(uuid()) @db.Uuid + parentChannelId String @db.Uuid + parentChannel Channel @relation(fields: [parentChannelId], references: [id]) + creatorId String @db.Uuid + creator User @relation(fields: [creatorId], references: [id]) + messageId String @db.Uuid + message Message @relation(name: "startWith", fields: [messageId], references: [id]) + title String + description String + members ThreadMember[] + messages Message[] @relation(name: "contains") + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model ThreadMember { + threadId String @db.Uuid + thread Thread @relation(fields: [threadId], references: [id]) + userId String @db.Uuid + user User @relation(fields: [userId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@id([threadId, userId]) +} + +model ChannelMember { + userId String @db.Uuid + user User @relation(fields: [userId], references: [id]) + channelId String @db.Uuid + channel Channel @relation(fields: [channelId], references: [id]) + role ChannelMemberRole + joinDate DateTime @default(now()) + lastAccessedAt DateTime @default(now()) + + @@id([channelId, userId]) +} + +model Message { + id String @id @default(uuid()) @db.Uuid + channelId String @db.Uuid + channel Channel @relation(fields: [channelId], references: [id]) + threadId String? @db.Uuid + thread Thread? @relation(name: "contains", fields: [threadId], references: [id]) + threads Thread[] @relation(name: "startWith") + userId String @db.Uuid + user User @relation(fields: [userId], references: [id]) + content String + type MessageType + assets Asset[] + reactions Reaction[] + pinnedMessages PinnedMessage[] + bookmarks Bookmark[] + mentions Mention[] + createdAt DateTime @default(now()) +} + +model Asset { + id String @id @default(uuid()) @db.Uuid + messageId String @db.Uuid + message Message @relation(fields: [messageId], references: [id]) + type AssetType + url String + createdAt DateTime @default(now()) +} + +model Reaction { + messageId String @db.Uuid + message Message @relation(fields: [messageId], references: [id]) + userId String @db.Uuid + user User @relation(fields: [userId], references: [id]) + emoji String + createdAt DateTime @default(now()) + + @@id([messageId, userId]) +} + +model PinnedMessage { + channelId String @db.Uuid + channel Channel @relation(fields: [channelId], references: [id]) + messageId String @db.Uuid + message Message @relation(fields: [messageId], references: [id]) + pinnedBy String @db.Uuid + pinnedByUser User @relation(fields: [pinnedBy], references: [id]) + pinnedAt DateTime @default(now()) + + @@id([channelId, messageId, pinnedBy]) +} + +model Bookmark { + userId String @db.Uuid + user User @relation(fields: [userId], references: [id]) + messageId String @db.Uuid + message Message @relation(fields: [messageId], references: [id]) + createdAt DateTime @default(now()) + + @@id([messageId, userId]) +} + +model Mention { + messageId String @db.Uuid + message Message @relation(fields: [messageId], references: [id]) + mentionedUserId String @db.Uuid + mentionedUser User @relation(fields: [mentionedUserId], references: [id]) + createdAt DateTime @default(now()) + + @@id([messageId, mentionedUserId]) +} + +model CustomEmoji { + id String @id @default(uuid()) @db.Uuid + workspaceId String @db.Uuid + workspace Workspace @relation(fields: [workspaceId], references: [id]) + name String + url String + createdAt DateTime @default(now()) +} + +enum UserStatus { + ONLINE + AWAY + BUSY + OFFLINE +} + +enum ChannelType { + PUBLIC + PRIVATE + DM +} + +enum ChannelMemberRole { + ADMIN + MEMBER + VIEWER +} + +enum MessageType { + NORMAL + IMPORTANT + PROJECT +} + +enum AssetType { + IMAGE + VIDEO +}