Compare commits

...

27 Commits

Author SHA1 Message Date
628ff9aeb7 📝 docs: add comment fields and relations to db diagram 2026-05-02 21:00:00 +07:00
e10c57da1c 🔒 security: fix vulnerable field in collection table diagram 2026-05-01 21:00:00 +07:00
a1e059a99b 📝 docs: add history_watch to database diagram 2026-04-30 21:00:00 +07:00
d0782710a5 📝 docs: add database diagram for schema visualization 2026-04-29 21:00:00 +07:00
1e34793d08 🗃️ db: remove old migration 2026-04-28 12:00:00 +07:00
953530a119 📝 docs: add database diagram 2026-04-27 12:00:00 +07:00
c754e7f039 💥 breaking: remove old prisma schema for new database structure 2026-04-26 12:00:00 +07:00
6556ac8354 Merge pull request 'fix-media' (#32) from fix-media into main
All checks were successful
Sync to GitHub / sync (push) Successful in 10s
Reviewed-on: #32
2026-04-24 10:31:42 +07:00
16d8c00d75 🗃️ db: fix schema drift and correct season enum
All checks were successful
Integration Tests / integration-tests (pull_request) Successful in 1m14s
2026-04-22 12:18:56 +07:00
e61686956b Merge pull request 'media-detail' (#31) from media-detail into main
All checks were successful
Sync to GitHub / sync (push) Successful in 9s
Reviewed-on: #31
2026-04-21 20:44:51 +07:00
baf7ba20b1 🗃️ db: enforce season field constraint 2026-04-07 12:00:00 +07:00
697f28de60 🗃️ db: add season field to media model 2026-04-03 12:00:00 +07:00
72f8e9e4eb 👔 feat: implement database repository for get media by slug
All checks were successful
Integration Tests / integration-tests (pull_request) Successful in 1m51s
2026-04-02 09:48:31 +07:00
59228f7d1e feat: add service for getBySlug 2026-04-02 09:34:03 +07:00
b27479cd3e 🐛 fix: resolve schema type error in getAllMedia module 2026-04-01 23:38:32 +07:00
17eb272b1d feat: add endpoint to fetch media by slug 2026-04-01 23:28:22 +07:00
697374d6cd Merge pull request '👔 feat: support user collection check on hero banner' (#30) from fix-banner into main
All checks were successful
Sync to GitHub / sync (push) Successful in 9s
Reviewed-on: #30
2026-03-30 19:21:12 +07:00
5cebd200c4 👔 feat: support user collection check on hero banner
All checks were successful
Integration Tests / integration-tests (pull_request) Successful in 56s
2026-03-30 19:03:37 +07:00
cef6da16cb Merge pull request 'feat/collection' (#29) from feat/collection into main
All checks were successful
Sync to GitHub / sync (push) Successful in 7s
Reviewed-on: #29
2026-03-29 14:18:30 +07:00
412b501c80 🚨 fix: resolve linting type error
All checks were successful
Integration Tests / integration-tests (pull_request) Successful in 31s
2026-03-29 14:17:42 +07:00
68d834ae6b feat: add delete endpoint to collection
Some checks failed
Integration Tests / integration-tests (pull_request) Failing after 1m24s
2026-03-29 12:10:50 +07:00
86fe39f5b5 🚚 chore: move collection route module 2026-03-29 11:23:27 +07:00
73b22d7f2c feat: add collection module 2026-03-29 11:17:52 +07:00
f1e79945b0 🗃️ db: redesign collection schema with manual pivot table to media 2026-03-29 10:43:20 +07:00
3768ae4c26 🚧 wip: implement collection upsert logic 2026-03-28 21:59:44 +07:00
56c921e800 👔 feat: add collection module 2026-03-27 23:42:26 +07:00
e798338107 🗃️ db: update schema for new collection 2026-03-27 22:56:18 +07:00
32 changed files with 6310 additions and 1720 deletions

View File

@ -31,8 +31,8 @@ Table medias {
bannerPromotion hero_banner [not null]
logs media_logs [not null]
episodes episodes [not null]
collections collections [not null]
reviews movie_reviews [not null]
inCollections CollectionMedia [not null]
}
Table media_logs {
@ -369,7 +369,7 @@ Table user_logs {
Table collections {
id String [pk]
name String [not null]
medias medias [not null]
slug String [not null]
owner users [not null]
ownerId String [not null]
accessStatus AccessStatus [not null, default: 'private']
@ -379,6 +379,24 @@ Table collections {
deletedAt DateTime
createdAt DateTime [default: `now()`, not null]
updatedAt DateTime [default: `now()`, not null]
media_saved CollectionMedia [not null]
indexes {
(slug, ownerId) [unique]
}
}
Table CollectionMedia {
id String [pk]
collection collections [not null]
collectionId String [not null]
media medias [not null]
mediaId String [not null]
savedAt DateTime [default: `now()`, not null]
indexes {
(collectionId, mediaId) [unique]
}
}
Table watch_histories {
@ -557,9 +575,9 @@ Table MediaCharacters {
mediasId String [ref: > medias.id]
}
Table MediaCollections {
collectionsId String [ref: > collections.id]
mediasId String [ref: > medias.id]
Table CollectionMedia {
incollectionsId String [ref: > CollectionMedia.id]
media_savedId String [ref: > CollectionMedia.id]
}
Table UserFavoriteGenres {
@ -747,6 +765,10 @@ Ref: user_logs.sessionId > user_sessions.id
Ref: collections.ownerId > users.id
Ref: CollectionMedia.collectionId > collections.id
Ref: CollectionMedia.mediaId > medias.id
Ref: watch_histories.id > episodes.id
Ref: watch_histories.userId > users.id

5812
prisma/diagram.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,894 +0,0 @@
-- CreateSchema
CREATE SCHEMA IF NOT EXISTS "public";
-- CreateEnum
CREATE TYPE "MediaType" AS ENUM ('TV', 'ONA', 'OVA', 'Movie', 'Special', 'Music');
-- CreateEnum
CREATE TYPE "Country" AS ENUM ('Japanese', 'English', 'Indonesia', 'Korea');
-- CreateEnum
CREATE TYPE "CharacterRole" AS ENUM ('Main', 'Supporting');
-- CreateEnum
CREATE TYPE "MediaOperation" AS ENUM ('create', 'update', 'delete');
-- CreateEnum
CREATE TYPE "UserGender" AS ENUM ('male', 'female');
-- CreateEnum
CREATE TYPE "AdultFiltering" AS ENUM ('hide', 'show', 'explicit');
-- CreateEnum
CREATE TYPE "AdultAlert" AS ENUM ('hide', 'show');
-- CreateEnum
CREATE TYPE "VideoQuality" AS ENUM ('Q2160', 'Q1440', 'Q1080', 'Q720', 'Q480', 'Q360', 'Q240', 'Q144');
-- CreateEnum
CREATE TYPE "UserNotificationState" AS ENUM ('info', 'warning', 'danger');
-- CreateEnum
CREATE TYPE "ReportStatus" AS ENUM ('pending', 'resolved', 'rejected');
-- CreateEnum
CREATE TYPE "ReportReason" AS ENUM ('sexualize', 'violent', 'explicit', 'hateful', 'political', 'racist', 'spam', 'other');
-- CreateEnum
CREATE TYPE "AccessStatus" AS ENUM ('private', 'selected', 'protected', 'public');
-- CreateEnum
CREATE TYPE "AccessScope" AS ENUM ('viewer', 'editor');
-- CreateEnum
CREATE TYPE "MediaReviewReaction" AS ENUM ('angry', 'sad', 'awesome', 'happy', 'sleepy', 'annoyed', 'disgusting', 'disappointed');
-- CreateEnum
CREATE TYPE "EmailPorpose" AS ENUM ('forgot_password', 'account_activation', 'account_notification', 'subscribtion');
-- CreateEnum
CREATE TYPE "TypeSystemNotification" AS ENUM ('component', 'popup', 'toast');
-- CreateTable
CREATE TABLE "medias" (
"id" UUID NOT NULL,
"title" TEXT NOT NULL,
"titleAlternative" JSONB NOT NULL,
"slug" TEXT NOT NULL,
"malId" INTEGER,
"pictureMedium" TEXT NOT NULL,
"pictureLarge" TEXT NOT NULL,
"country" "Country" NOT NULL DEFAULT 'Japanese',
"score" DECIMAL(4,2) NOT NULL DEFAULT 0.00,
"status" TEXT NOT NULL,
"startAiring" TIMESTAMP(3),
"endAiring" TIMESTAMP(3),
"synopsis" TEXT NOT NULL,
"ageRating" TEXT NOT NULL,
"mediaType" "MediaType" NOT NULL,
"source" TEXT,
"onDraft" BOOLEAN NOT NULL DEFAULT true,
"uploadedBy" UUID NOT NULL,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "medias_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "media_logs" (
"id" UUID NOT NULL,
"status" "MediaOperation" NOT NULL,
"approval" BOOLEAN NOT NULL DEFAULT false,
"proposedBy" UUID NOT NULL,
"approvedBy" UUID NOT NULL,
"mediaId" UUID NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "media_logs_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "genres" (
"id" UUID NOT NULL,
"name" VARCHAR(255) NOT NULL,
"slug" VARCHAR(255) NOT NULL,
"malId" INTEGER NOT NULL,
"malUrl" VARCHAR(255) NOT NULL,
"createdBy" UUID NOT NULL,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "genres_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "studios" (
"id" UUID NOT NULL,
"name" VARCHAR(255) NOT NULL,
"slug" VARCHAR(255) NOT NULL,
"linkAbout" TEXT NOT NULL,
"malId" INTEGER NOT NULL,
"logoUrl" TEXT,
"createdBy" UUID NOT NULL,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "studios_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "characters" (
"id" UUID NOT NULL,
"malId" INTEGER NOT NULL,
"name" TEXT NOT NULL,
"role" "CharacterRole" NOT NULL,
"favorites" INTEGER NOT NULL DEFAULT 0,
"imageUrl" TEXT,
"smallImageUrl" TEXT,
"creatorId" UUID NOT NULL,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "characters_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "voice_actors" (
"id" UUID NOT NULL,
"malId" INTEGER NOT NULL,
"name" TEXT NOT NULL,
"birthday" TIMESTAMP(3),
"description" TEXT,
"aboutUrl" TEXT,
"imageUrl" TEXT,
"websiteUrl" TEXT,
"creatorId" UUID NOT NULL,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "voice_actors_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "lang_va_char" (
"id" UUID NOT NULL,
"language" TEXT NOT NULL,
"vaId" UUID NOT NULL,
"charId" UUID NOT NULL,
"creatorId" UUID NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "lang_va_char_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "episodes" (
"id" UUID NOT NULL,
"mediaId" UUID NOT NULL,
"episode" INTEGER NOT NULL,
"name" VARCHAR(255) NOT NULL,
"score" DECIMAL(4,2) NOT NULL DEFAULT 0.00,
"pictureThumbnail" TEXT,
"viewed" BIGINT NOT NULL DEFAULT 0,
"likes" BIGINT NOT NULL DEFAULT 0,
"dislikes" BIGINT NOT NULL DEFAULT 0,
"pendingUpload" BOOLEAN NOT NULL DEFAULT true,
"uploadedBy" UUID NOT NULL,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "episodes_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "episode_likes" (
"id" UUID NOT NULL,
"userId" UUID NOT NULL,
"sessionId" UUID NOT NULL,
"episodeId" UUID NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "episode_likes_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "videos" (
"id" UUID NOT NULL,
"episodeId" UUID NOT NULL,
"serviceId" UUID NOT NULL,
"videoCode" VARCHAR(255) NOT NULL,
"thumbnailCode" TEXT,
"pendingUpload" BOOLEAN NOT NULL DEFAULT true,
"uploadedBy" UUID NOT NULL,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "videos_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "video_services" (
"id" UUID NOT NULL,
"name" VARCHAR(255) NOT NULL,
"domain" VARCHAR(255) NOT NULL,
"logo" TEXT,
"hexColor" VARCHAR(10) NOT NULL,
"endpointVideo" TEXT NOT NULL,
"endpointThumbnail" TEXT,
"endpointDownload" TEXT,
"createdBy" UUID NOT NULL,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "video_services_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "users" (
"id" UUID NOT NULL,
"name" VARCHAR(255) NOT NULL,
"username" VARCHAR(255) NOT NULL,
"email" TEXT NOT NULL,
"password" TEXT NOT NULL,
"birthDate" DATE,
"gender" "UserGender",
"phoneCC" INTEGER,
"phoneNumber" INTEGER,
"bioProfile" TEXT,
"avatar" TEXT,
"commentBackground" TEXT,
"provider" VARCHAR(255),
"providerId" VARCHAR(255),
"providerToken" TEXT,
"providerPayload" JSON,
"verifiedAt" TIMESTAMP(3),
"disabledAt" TIMESTAMP(3),
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "user_preferences" (
"id" UUID NOT NULL,
"userId" UUID NOT NULL,
"langPreference" TEXT,
"adultFiltering" "AdultFiltering" NOT NULL DEFAULT 'hide',
"adultAlert" "AdultAlert" NOT NULL DEFAULT 'show',
"videoQuality" "VideoQuality" NOT NULL DEFAULT 'Q1080',
"serviceDefaultId" UUID,
"hideContries" "Country"[],
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "user_preferences_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "user_roles" (
"id" UUID NOT NULL,
"name" VARCHAR(255) NOT NULL,
"description" TEXT,
"primaryColor" VARCHAR(10),
"secondaryColor" VARCHAR(10),
"pictureImage" TEXT,
"badgeImage" TEXT,
"isSuperadmin" BOOLEAN NOT NULL DEFAULT false,
"canEditMedia" BOOLEAN NOT NULL DEFAULT false,
"canManageMedia" BOOLEAN NOT NULL DEFAULT false,
"canEditEpisodes" BOOLEAN NOT NULL DEFAULT false,
"canManageEpisodes" BOOLEAN NOT NULL DEFAULT false,
"canEditComment" BOOLEAN NOT NULL DEFAULT false,
"canManageComment" BOOLEAN NOT NULL DEFAULT false,
"canEditUser" BOOLEAN NOT NULL DEFAULT false,
"canManageUser" BOOLEAN NOT NULL DEFAULT false,
"canEditSystem" BOOLEAN NOT NULL DEFAULT false,
"canManageSystem" BOOLEAN NOT NULL DEFAULT false,
"createdBy" UUID NOT NULL,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "user_roles_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "user_role_assignments" (
"userId" UUID NOT NULL,
"roleId" UUID NOT NULL,
"assignmentAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "user_role_assignments_pkey" PRIMARY KEY ("userId","roleId")
);
-- CreateTable
CREATE TABLE "user_notifications" (
"id" UUID NOT NULL,
"title" VARCHAR(255) NOT NULL,
"content" TEXT NOT NULL,
"picture" TEXT NOT NULL,
"state" "UserNotificationState" NOT NULL,
"ctaLink" TEXT NOT NULL,
"userId" UUID NOT NULL,
"isReaded" BOOLEAN NOT NULL DEFAULT false,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "user_notifications_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "user_sessions" (
"id" UUID NOT NULL,
"isAuthenticated" BOOLEAN NOT NULL DEFAULT false,
"userId" UUID NOT NULL,
"deviceType" VARCHAR(255) NOT NULL,
"deviceOs" VARCHAR(255) NOT NULL,
"deviceIp" VARCHAR(255) NOT NULL,
"browser" VARCHAR(255) NOT NULL,
"isOnline" BOOLEAN NOT NULL DEFAULT false,
"lastOnline" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"validUntil" TIMESTAMP(3) NOT NULL,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "user_sessions_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "user_logs" (
"id" UUID NOT NULL,
"title" VARCHAR(255) NOT NULL,
"notes" TEXT NOT NULL,
"userId" UUID NOT NULL,
"sessionId" UUID NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "user_logs_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "collections" (
"id" UUID NOT NULL,
"name" VARCHAR(255) NOT NULL,
"ownerId" UUID NOT NULL,
"accessStatus" "AccessStatus" NOT NULL DEFAULT 'private',
"password" VARCHAR(255),
"accessScope" "AccessScope" NOT NULL DEFAULT 'viewer',
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "collections_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "watch_histories" (
"id" UUID NOT NULL,
"episodeId" UUID NOT NULL,
"userId" UUID NOT NULL,
"sessionId" UUID NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "watch_histories_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "movie_reviews" (
"id" UUID NOT NULL,
"mediaId" UUID NOT NULL,
"rating" INTEGER NOT NULL,
"title" VARCHAR(255) NOT NULL,
"text" TEXT NOT NULL,
"reaction" "MediaReviewReaction" NOT NULL,
"createdBy" UUID NOT NULL,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "movie_reviews_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "comments" (
"id" UUID NOT NULL,
"episodeId" UUID NOT NULL,
"text" TEXT NOT NULL,
"isParent" BOOLEAN NOT NULL DEFAULT false,
"parentId" UUID,
"userId" UUID NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "comments_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "comment_likes" (
"id" UUID NOT NULL,
"commentId" UUID NOT NULL,
"userLiked" UUID NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "comment_likes_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "comment_reports" (
"id" UUID NOT NULL,
"userReporter" UUID NOT NULL,
"commentReported" UUID NOT NULL,
"isSupervisorReport" BOOLEAN NOT NULL DEFAULT false,
"reason" "ReportReason" NOT NULL,
"status" "ReportStatus" NOT NULL,
"description" VARCHAR(255) NOT NULL,
"approvedBy" UUID,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "comment_reports_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "languages" (
"id" UUID NOT NULL,
"name" VARCHAR(255) NOT NULL,
"code" VARCHAR(5) NOT NULL,
"countryFlag" VARCHAR(10) NOT NULL,
"fileLocation" TEXT NOT NULL,
"craetedBy" UUID NOT NULL,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "languages_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "email_system_accounts" (
"id" UUID NOT NULL,
"name" TEXT NOT NULL,
"host" VARCHAR(255) NOT NULL,
"port" INTEGER NOT NULL,
"secure" BOOLEAN NOT NULL,
"email" VARCHAR(255) NOT NULL,
"username" VARCHAR(255) NOT NULL,
"password" VARCHAR(255) NOT NULL,
"purpose" "EmailPorpose" NOT NULL,
"createdBy" UUID NOT NULL,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "email_system_accounts_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "email_system_histories" (
"id" UUID NOT NULL,
"purpose" "EmailPorpose" NOT NULL,
"fromEmail" TEXT NOT NULL,
"toEmail" TEXT NOT NULL,
"userRelated" UUID NOT NULL,
"title" VARCHAR(255) NOT NULL,
"htmlContent" TEXT NOT NULL,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "email_system_histories_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "hero_banner" (
"id" UUID NOT NULL,
"orderPriority" INTEGER,
"isClickable" BOOLEAN NOT NULL DEFAULT false,
"title" VARCHAR(225),
"tags" TEXT[],
"description" TEXT,
"buttonContent" VARCHAR(100),
"buttonLink" TEXT,
"imageUrl" TEXT,
"startDate" TIMESTAMP(3) NOT NULL,
"endDate" TIMESTAMP(3) NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"creatorId" UUID NOT NULL,
CONSTRAINT "hero_banner_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "system_preferences" (
"id" UUID NOT NULL,
"key" VARCHAR(225) NOT NULL,
"value" VARCHAR(225) NOT NULL,
"description" TEXT NOT NULL,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "system_preferences_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "system_notifications" (
"id" UUID NOT NULL,
"type" "TypeSystemNotification" NOT NULL,
"componentName" VARCHAR(255),
"popupImage" TEXT,
"titleToast" VARCHAR(255),
"contentToast" TEXT,
"createdBy" UUID NOT NULL,
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "system_notifications_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "system_logs" (
"id" UUID NOT NULL,
"title" VARCHAR(255) NOT NULL,
"notes" TEXT NOT NULL,
"relatedUser" UUID,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "system_logs_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "_MediaStudios" (
"A" UUID NOT NULL,
"B" UUID NOT NULL,
CONSTRAINT "_MediaStudios_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateTable
CREATE TABLE "_MediaGenres" (
"A" UUID NOT NULL,
"B" UUID NOT NULL,
CONSTRAINT "_MediaGenres_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateTable
CREATE TABLE "_UserFavoriteGenres" (
"A" UUID NOT NULL,
"B" UUID NOT NULL,
CONSTRAINT "_UserFavoriteGenres_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateTable
CREATE TABLE "_MediaCharacters" (
"A" UUID NOT NULL,
"B" UUID NOT NULL,
CONSTRAINT "_MediaCharacters_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateTable
CREATE TABLE "_MediaCollections" (
"A" UUID NOT NULL,
"B" UUID NOT NULL,
CONSTRAINT "_MediaCollections_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateTable
CREATE TABLE "_UserSelectedSharingCollention" (
"A" UUID NOT NULL,
"B" UUID NOT NULL,
CONSTRAINT "_UserSelectedSharingCollention_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateIndex
CREATE UNIQUE INDEX "medias_slug_key" ON "medias"("slug");
-- CreateIndex
CREATE UNIQUE INDEX "medias_malId_key" ON "medias"("malId");
-- CreateIndex
CREATE INDEX "medias_status_onDraft_deletedAt_idx" ON "medias"("status", "onDraft", "deletedAt");
-- CreateIndex
CREATE INDEX "medias_mediaType_idx" ON "medias"("mediaType");
-- CreateIndex
CREATE INDEX "medias_uploadedBy_idx" ON "medias"("uploadedBy");
-- CreateIndex
CREATE INDEX "medias_createdAt_idx" ON "medias"("createdAt");
-- CreateIndex
CREATE UNIQUE INDEX "genres_slug_key" ON "genres"("slug");
-- CreateIndex
CREATE UNIQUE INDEX "genres_malId_key" ON "genres"("malId");
-- CreateIndex
CREATE UNIQUE INDEX "studios_slug_key" ON "studios"("slug");
-- CreateIndex
CREATE UNIQUE INDEX "studios_malId_key" ON "studios"("malId");
-- CreateIndex
CREATE UNIQUE INDEX "characters_malId_key" ON "characters"("malId");
-- CreateIndex
CREATE UNIQUE INDEX "voice_actors_malId_key" ON "voice_actors"("malId");
-- CreateIndex
CREATE UNIQUE INDEX "lang_va_char_language_vaId_charId_key" ON "lang_va_char"("language", "vaId", "charId");
-- CreateIndex
CREATE UNIQUE INDEX "episodes_mediaId_episode_key" ON "episodes"("mediaId", "episode");
-- CreateIndex
CREATE UNIQUE INDEX "videos_serviceId_videoCode_key" ON "videos"("serviceId", "videoCode");
-- CreateIndex
CREATE UNIQUE INDEX "video_services_name_key" ON "video_services"("name");
-- CreateIndex
CREATE UNIQUE INDEX "users_username_key" ON "users"("username");
-- CreateIndex
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
-- CreateIndex
CREATE UNIQUE INDEX "users_providerId_key" ON "users"("providerId");
-- CreateIndex
CREATE UNIQUE INDEX "user_preferences_userId_key" ON "user_preferences"("userId");
-- CreateIndex
CREATE UNIQUE INDEX "user_roles_name_key" ON "user_roles"("name");
-- CreateIndex
CREATE INDEX "user_sessions_userId_isAuthenticated_deletedAt_idx" ON "user_sessions"("userId", "isAuthenticated", "deletedAt");
-- CreateIndex
CREATE UNIQUE INDEX "languages_code_key" ON "languages"("code");
-- CreateIndex
CREATE UNIQUE INDEX "email_system_accounts_name_key" ON "email_system_accounts"("name");
-- CreateIndex
CREATE UNIQUE INDEX "email_system_accounts_email_key" ON "email_system_accounts"("email");
-- CreateIndex
CREATE UNIQUE INDEX "email_system_accounts_username_key" ON "email_system_accounts"("username");
-- CreateIndex
CREATE UNIQUE INDEX "hero_banner_orderPriority_key" ON "hero_banner"("orderPriority");
-- CreateIndex
CREATE UNIQUE INDEX "system_preferences_key_key" ON "system_preferences"("key");
-- CreateIndex
CREATE INDEX "_MediaStudios_B_index" ON "_MediaStudios"("B");
-- CreateIndex
CREATE INDEX "_MediaGenres_B_index" ON "_MediaGenres"("B");
-- CreateIndex
CREATE INDEX "_UserFavoriteGenres_B_index" ON "_UserFavoriteGenres"("B");
-- CreateIndex
CREATE INDEX "_MediaCharacters_B_index" ON "_MediaCharacters"("B");
-- CreateIndex
CREATE INDEX "_MediaCollections_B_index" ON "_MediaCollections"("B");
-- CreateIndex
CREATE INDEX "_UserSelectedSharingCollention_B_index" ON "_UserSelectedSharingCollention"("B");
-- AddForeignKey
ALTER TABLE "medias" ADD CONSTRAINT "medias_uploadedBy_fkey" FOREIGN KEY ("uploadedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "media_logs" ADD CONSTRAINT "media_logs_proposedBy_fkey" FOREIGN KEY ("proposedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "media_logs" ADD CONSTRAINT "media_logs_approvedBy_fkey" FOREIGN KEY ("approvedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "media_logs" ADD CONSTRAINT "media_logs_mediaId_fkey" FOREIGN KEY ("mediaId") REFERENCES "medias"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "genres" ADD CONSTRAINT "genres_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "studios" ADD CONSTRAINT "studios_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "characters" ADD CONSTRAINT "characters_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "voice_actors" ADD CONSTRAINT "voice_actors_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "lang_va_char" ADD CONSTRAINT "lang_va_char_vaId_fkey" FOREIGN KEY ("vaId") REFERENCES "voice_actors"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "lang_va_char" ADD CONSTRAINT "lang_va_char_charId_fkey" FOREIGN KEY ("charId") REFERENCES "characters"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "lang_va_char" ADD CONSTRAINT "lang_va_char_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "episodes" ADD CONSTRAINT "episodes_mediaId_fkey" FOREIGN KEY ("mediaId") REFERENCES "medias"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "episodes" ADD CONSTRAINT "episodes_uploadedBy_fkey" FOREIGN KEY ("uploadedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "episode_likes" ADD CONSTRAINT "episode_likes_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "episode_likes" ADD CONSTRAINT "episode_likes_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "user_sessions"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "episode_likes" ADD CONSTRAINT "episode_likes_episodeId_fkey" FOREIGN KEY ("episodeId") REFERENCES "episodes"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "videos" ADD CONSTRAINT "videos_episodeId_fkey" FOREIGN KEY ("episodeId") REFERENCES "episodes"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "videos" ADD CONSTRAINT "videos_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "video_services"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "videos" ADD CONSTRAINT "videos_uploadedBy_fkey" FOREIGN KEY ("uploadedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "video_services" ADD CONSTRAINT "video_services_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_preferences" ADD CONSTRAINT "user_preferences_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_preferences" ADD CONSTRAINT "user_preferences_langPreference_fkey" FOREIGN KEY ("langPreference") REFERENCES "languages"("code") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_preferences" ADD CONSTRAINT "user_preferences_serviceDefaultId_fkey" FOREIGN KEY ("serviceDefaultId") REFERENCES "video_services"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_roles" ADD CONSTRAINT "user_roles_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_role_assignments" ADD CONSTRAINT "user_role_assignments_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_role_assignments" ADD CONSTRAINT "user_role_assignments_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "user_roles"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_notifications" ADD CONSTRAINT "user_notifications_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_sessions" ADD CONSTRAINT "user_sessions_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_logs" ADD CONSTRAINT "user_logs_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_logs" ADD CONSTRAINT "user_logs_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "user_sessions"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "collections" ADD CONSTRAINT "collections_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "watch_histories" ADD CONSTRAINT "watch_histories_id_fkey" FOREIGN KEY ("id") REFERENCES "episodes"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "watch_histories" ADD CONSTRAINT "watch_histories_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "watch_histories" ADD CONSTRAINT "watch_histories_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "user_sessions"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "movie_reviews" ADD CONSTRAINT "movie_reviews_mediaId_fkey" FOREIGN KEY ("mediaId") REFERENCES "medias"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "movie_reviews" ADD CONSTRAINT "movie_reviews_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "comments" ADD CONSTRAINT "comments_episodeId_fkey" FOREIGN KEY ("episodeId") REFERENCES "episodes"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "comments" ADD CONSTRAINT "comments_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "comments"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "comments" ADD CONSTRAINT "comments_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "comment_likes" ADD CONSTRAINT "comment_likes_commentId_fkey" FOREIGN KEY ("commentId") REFERENCES "comments"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "comment_likes" ADD CONSTRAINT "comment_likes_userLiked_fkey" FOREIGN KEY ("userLiked") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "comment_reports" ADD CONSTRAINT "comment_reports_userReporter_fkey" FOREIGN KEY ("userReporter") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "comment_reports" ADD CONSTRAINT "comment_reports_approvedBy_fkey" FOREIGN KEY ("approvedBy") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "languages" ADD CONSTRAINT "languages_craetedBy_fkey" FOREIGN KEY ("craetedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "email_system_accounts" ADD CONSTRAINT "email_system_accounts_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "email_system_histories" ADD CONSTRAINT "email_system_histories_userRelated_fkey" FOREIGN KEY ("userRelated") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "hero_banner" ADD CONSTRAINT "hero_banner_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "system_notifications" ADD CONSTRAINT "system_notifications_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "system_logs" ADD CONSTRAINT "system_logs_relatedUser_fkey" FOREIGN KEY ("relatedUser") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_MediaStudios" ADD CONSTRAINT "_MediaStudios_A_fkey" FOREIGN KEY ("A") REFERENCES "medias"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_MediaStudios" ADD CONSTRAINT "_MediaStudios_B_fkey" FOREIGN KEY ("B") REFERENCES "studios"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_MediaGenres" ADD CONSTRAINT "_MediaGenres_A_fkey" FOREIGN KEY ("A") REFERENCES "genres"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_MediaGenres" ADD CONSTRAINT "_MediaGenres_B_fkey" FOREIGN KEY ("B") REFERENCES "medias"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_UserFavoriteGenres" ADD CONSTRAINT "_UserFavoriteGenres_A_fkey" FOREIGN KEY ("A") REFERENCES "genres"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_UserFavoriteGenres" ADD CONSTRAINT "_UserFavoriteGenres_B_fkey" FOREIGN KEY ("B") REFERENCES "user_preferences"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_MediaCharacters" ADD CONSTRAINT "_MediaCharacters_A_fkey" FOREIGN KEY ("A") REFERENCES "characters"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_MediaCharacters" ADD CONSTRAINT "_MediaCharacters_B_fkey" FOREIGN KEY ("B") REFERENCES "medias"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_MediaCollections" ADD CONSTRAINT "_MediaCollections_A_fkey" FOREIGN KEY ("A") REFERENCES "collections"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_MediaCollections" ADD CONSTRAINT "_MediaCollections_B_fkey" FOREIGN KEY ("B") REFERENCES "medias"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_UserSelectedSharingCollention" ADD CONSTRAINT "_UserSelectedSharingCollention_A_fkey" FOREIGN KEY ("A") REFERENCES "collections"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_UserSelectedSharingCollention" ADD CONSTRAINT "_UserSelectedSharingCollention_B_fkey" FOREIGN KEY ("B") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -1,23 +0,0 @@
/*
Warnings:
- You are about to drop the column `buttonContent` on the `hero_banner` table. All the data in the column will be lost.
- You are about to drop the column `buttonLink` on the `hero_banner` table. All the data in the column will be lost.
- You are about to drop the column `description` on the `hero_banner` table. All the data in the column will be lost.
- You are about to drop the column `isClickable` on the `hero_banner` table. All the data in the column will be lost.
- You are about to drop the column `tags` on the `hero_banner` table. All the data in the column will be lost.
- You are about to drop the column `title` on the `hero_banner` table. All the data in the column will be lost.
- Added the required column `mediaId` to the `hero_banner` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "hero_banner" DROP COLUMN "buttonContent",
DROP COLUMN "buttonLink",
DROP COLUMN "description",
DROP COLUMN "isClickable",
DROP COLUMN "tags",
DROP COLUMN "title",
ADD COLUMN "mediaId" UUID NOT NULL;
-- AddForeignKey
ALTER TABLE "hero_banner" ADD CONSTRAINT "hero_banner_mediaId_fkey" FOREIGN KEY ("mediaId") REFERENCES "medias"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -1,3 +0,0 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"

View File

@ -1,733 +1,11 @@
// 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
//// Prisma Configuration ////
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
}
generator dbml {
provider = "prisma-dbml-generator"
}
//// Prisma Model ////
model Media {
id String @id @db.Uuid
title String @db.Text
titleAlternative Json
slug String @db.Text @unique
malId Int? @unique
pictureMedium String @db.Text
pictureLarge String @db.Text
genres Genre[] @relation("MediaGenres")
country Country @default(JP)
score Decimal @db.Decimal(4, 2) @default(0.00)
status String
startAiring DateTime?
endAiring DateTime?
synopsis String @db.Text
ageRating String
mediaType MediaType
source String?
studios Studio[] @relation("MediaStudios")
characters Character[] @relation("MediaCharacters")
onDraft Boolean @default(true)
uploader User @relation("UserUploadedMedias", fields: [uploadedBy], references: [id])
uploadedBy String @db.Uuid
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
bannerPromotion HeroBanner[] @relation("MediaBannerPromotion")
logs MediaLog[] @relation("MediaLogs")
episodes Episode[] @relation("MediaEpisodes")
collections Collection[] @relation("MediaCollections")
reviews MediaReview[] @relation("MediaReviews")
@@index([status, onDraft, deletedAt])
@@index([mediaType])
@@index([uploadedBy])
@@index([createdAt])
@@map("medias")
}
model MediaLog {
id String @id @db.Uuid
status MediaOperation
approval Boolean @default(false)
proposer User @relation("UserProposedMedias", fields: [proposedBy], references: [id])
proposedBy String @db.Uuid
approver User @relation("UserApprovedMedias", fields: [approvedBy], references: [id])
approvedBy String @db.Uuid
media Media @relation("MediaLogs", fields: [mediaId], references: [id])
mediaId String @db.Uuid
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@map("media_logs")
}
model Genre {
id String @id @db.Uuid
name String @db.VarChar(255)
slug String @db.VarChar(255) @unique
malId Int @unique
malUrl String @db.VarChar(255)
creator User @relation("UserCreatedGenres", fields: [createdBy], references: [id])
createdBy String @db.Uuid
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
medias Media[] @relation("MediaGenres")
user_favourite_genres UserPreference[] @relation("UserFavoriteGenres")
@@map("genres")
}
model Studio {
id String @id @db.Uuid
name String @db.VarChar(255)
slug String @db.VarChar(255) @unique
linkAbout String @db.Text
malId Int @unique
logoUrl String? @db.Text
creator User @relation("UserCreatedStudios", fields: [createdBy], references: [id])
createdBy String @db.Uuid
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
medias Media[] @relation("MediaStudios")
@@map("studios")
}
model Character {
id String @id@db.Uuid
malId Int @unique
name String
role CharacterRole
favorites Int @default(0)
imageUrl String?
smallImageUrl String?
createdBy User @relation("UserCreatedCharacters", fields: [creatorId], references: [id])
creatorId String @db.Uuid
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
medias Media[] @relation("MediaCharacters")
voice_actors LangVAChar[] @relation("CharVALanguage")
@@map("characters")
}
model VoiceActor {
id String @id @db.Uuid
malId Int @unique
name String
birthday DateTime?
description String? @db.Text
aboutUrl String?
imageUrl String?
websiteUrl String?
createdBy User @relation("UserCreatedVoiceActors", fields: [creatorId], references: [id])
creatorId String @db.Uuid
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
characters LangVAChar[] @relation("VACharLanguage")
@@map("voice_actors")
}
model LangVAChar {
id String @id @db.Uuid
language String
voiceActor VoiceActor @relation("VACharLanguage", fields: [vaId], references: [id])
vaId String @db.Uuid
character Character @relation("CharVALanguage", fields: [charId], references: [id])
charId String @db.Uuid
createdBy User @relation("UserCreatedLangVAChar", fields: [creatorId], references: [id])
creatorId String @db.Uuid
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@unique([language, vaId, charId])
@@map("lang_va_char")
}
model Episode {
id String @id @db.Uuid
media Media @relation("MediaEpisodes", fields: [mediaId], references: [id])
mediaId String @db.Uuid
episode Int
name String @db.VarChar(255)
score Decimal @db.Decimal(4,2) @default(0.00)
pictureThumbnail String? @db.Text
viewed BigInt @default(0)
likes BigInt @default(0)
dislikes BigInt @default(0)
pendingUpload Boolean @default(true)
uploader User @relation("UserEpisodes", fields: [uploadedBy], references: [id])
uploadedBy String @db.Uuid
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
user_likes EpisodeLike[] @relation("EpisodeLikes")
videos Video[] @relation("EpisodeVideos")
user_histories WatchHistory[] @relation("EpisodeWatchHistories")
comments Comment[] @relation("EpisodeComments")
@@unique([mediaId, episode])
@@map("episodes")
}
model EpisodeLike {
id String @id @db.Uuid
user User @relation("UserEpisodeLikes", fields: [userId], references: [id])
userId String @db.Uuid
session UserSession @relation("SessionEpisodeLikes", fields: [sessionId], references: [id])
sessionId String @db.Uuid
episode Episode @relation("EpisodeLikes", fields: [episodeId], references: [id])
episodeId String @db.Uuid
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@map("episode_likes")
}
model Video {
id String @id @db.Uuid
episode Episode @relation("EpisodeVideos", fields: [episodeId], references: [id])
episodeId String @db.Uuid
service VideoService @relation("VideoServices", fields: [serviceId], references: [id])
serviceId String @db.Uuid
videoCode String @db.VarChar(255)
thumbnailCode String?
pendingUpload Boolean @default(true)
uploader User @relation("UserVideos", fields: [uploadedBy], references: [id])
uploadedBy String @db.Uuid
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@unique([serviceId, videoCode])
@@map("videos")
}
model VideoService {
id String @id @db.Uuid
name String @db.VarChar(255) @unique
domain String @db.VarChar(255)
logo String? @db.Text
hexColor String @db.VarChar(10)
endpointVideo String @db.Text
endpointThumbnail String? @db.Text
endpointDownload String?
creator User @relation("UserVideoServices", fields: [createdBy], references: [id])
createdBy String @db.Uuid
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
videos Video[] @relation("VideoServices")
user_preferences UserPreference[] @relation("UserServiceDefault")
@@map("video_services")
}
model User {
id String @id @db.Uuid
name String @db.VarChar(255)
username String @unique @db.VarChar(255)
email String @unique @db.Text
password String @db.Text
birthDate DateTime? @db.Date
gender UserGender?
phoneCC Int?
phoneNumber Int?
assignedRoles UserRoleAssignment[]
bioProfile String? @db.Text
avatar String? @db.Text
commentBackground String? @db.Text
provider String? @db.VarChar(255)
providerId String? @unique @db.VarChar(255)
providerToken String? @db.Text
providerPayload Json? @db.Json
preference UserPreference?
verifiedAt DateTime?
disabledAt DateTime?
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
medias Media[] @relation("UserUploadedMedias")
media_proposeds MediaLog[] @relation("UserProposedMedias")
media_approveds MediaLog[] @relation("UserApprovedMedias")
genres Genre[] @relation("UserCreatedGenres")
studios Studio[] @relation("UserCreatedStudios")
characters Character[] @relation("UserCreatedCharacters")
voice_actor VoiceActor[] @relation("UserCreatedVoiceActors")
lang_va_char LangVAChar[] @relation("UserCreatedLangVAChar")
episodes Episode[] @relation("UserEpisodes")
episode_likes EpisodeLike[] @relation("UserEpisodeLikes")
videos Video[] @relation("UserVideos")
video_services VideoService[] @relation("UserVideoServices")
create_roles UserRole[] @relation("UserCreateRoles")
notifications UserNotification[] @relation("UserNotifications")
sessions UserSession[] @relation("UserSession")
logs UserLog[] @relation("UserLogs")
collections Collection[] @relation("UserCollections")
allowed_collections Collection[] @relation("UserSelectedSharingCollention")
watch_histories WatchHistory[] @relation("UserWatchHistories")
media_reviews MediaReview[] @relation("UserMediaReviews")
comments Comment[] @relation("UserComments")
liked_comments CommentLike[] @relation("UserCommentLikes")
reported_comments CommentReport[] @relation("UserReportComments")
approved_comments CommentReport[] @relation("ApprovedReportComments")
create_languages Language[] @relation("UserCreateLanguages")
user_create_email EmailSystemAccount[] @relation("UserCreateSystemAccount")
user_emails EmailSystemHistory[] @relation("UserEmails")
user_hero_banner HeroBanner[] @relation("UserHeroBanner")
sys_notifications SystemNotification[] @relation("UserCreatorSystemNotifications")
sys_logs SystemLog[] @relation("UserSystemLogs")
@@map("users")
}
model UserPreference {
id String @id @db.Uuid
user User @relation(fields: [userId], references: [id])
userId String @unique @db.Uuid
lang Language? @relation("UserPreferenceLang", fields: [langPreference], references: [code])
langPreference String?
adultFiltering AdultFiltering @default(hide)
adultAlert AdultAlert @default(show)
videoQuality VideoQuality @default(Q1080)
serviceDefault VideoService? @relation("UserServiceDefault", fields: [serviceDefaultId], references: [id])
serviceDefaultId String? @db.Uuid
hideContries Country[]
favoriteGenres Genre[] @relation("UserFavoriteGenres")
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@map("user_preferences")
}
model UserRole {
id String @id @db.Uuid
name String @db.VarChar(255) @unique
description String? @db.Text
primaryColor String? @db.VarChar(10)
secondaryColor String? @db.VarChar(10)
pictureImage String? @db.Text
badgeImage String? @db.Text
isSuperadmin Boolean @default(false)
canEditMedia Boolean @default(false)
canManageMedia Boolean @default(false)
canEditEpisodes Boolean @default(false)
canManageEpisodes Boolean @default(false)
canEditComment Boolean @default(false)
canManageComment Boolean @default(false)
canEditUser Boolean @default(false)
canManageUser Boolean @default(false)
canEditSystem Boolean @default(false)
canManageSystem Boolean @default(false)
creator User @relation("UserCreateRoles", fields: [createdBy], references: [id])
createdBy String @db.Uuid
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
assignedUser UserRoleAssignment[]
@@map("user_roles")
}
model UserRoleAssignment {
user User @relation(fields: [userId], references: [id])
userId String @db.Uuid
role UserRole @relation(fields: [roleId], references: [id])
roleId String @db.Uuid
assignmentAt DateTime @default(now())
@@id([userId, roleId])
@@map("user_role_assignments")
}
model UserNotification {
id String @id @db.Uuid
title String @db.VarChar(255)
content String @db.Text
picture String @db.Text
state UserNotificationState
ctaLink String @db.Text
user User @relation("UserNotifications", fields: [userId], references: [id])
userId String @db.Uuid
isReaded Boolean @default(false)
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@map("user_notifications")
}
model UserSession {
id String @id @db.Uuid
isAuthenticated Boolean @default(false)
user User @relation("UserSession", fields: [userId], references: [id])
userId String @db.Uuid
deviceType String @db.VarChar(255)
deviceOs String @db.VarChar(255)
deviceIp String @db.VarChar(255)
browser String @db.VarChar(255)
isOnline Boolean @default(false)
lastOnline DateTime @default(now())
validUntil DateTime
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
logs UserLog[] @relation("UserSessionLogs")
episode_likes EpisodeLike[] @relation("SessionEpisodeLikes")
watch_histories WatchHistory[] @relation("SessionWatchHistories")
@@index([userId, isAuthenticated, deletedAt])
@@map("user_sessions")
}
model UserLog {
id String @id @db.Uuid
title String @db.VarChar(255)
notes String @db.Text
user User @relation("UserLogs", fields: [userId], references: [id])
userId String @db.Uuid
session UserSession @relation("UserSessionLogs", fields: [sessionId], references: [id])
sessionId String @db.Uuid
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@map("user_logs")
}
model Collection {
id String @id @db.Uuid
name String @db.VarChar(255)
medias Media[] @relation("MediaCollections")
owner User @relation("UserCollections", fields: [ownerId], references: [id])
ownerId String @db.Uuid
accessStatus AccessStatus @default(private)
password String? @db.VarChar(255)
usersAllowed User[] @relation("UserSelectedSharingCollention")
accessScope AccessScope @default(viewer)
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@map("collections")
}
model WatchHistory {
id String @id @db.Uuid
episode Episode @relation("EpisodeWatchHistories", fields: [id], references: [id])
episodeId String @db.Uuid
user User @relation("UserWatchHistories", fields: [userId], references: [id])
userId String @db.Uuid
session UserSession @relation("SessionWatchHistories", fields: [sessionId], references: [id])
sessionId String @db.Uuid
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@map("watch_histories")
}
model MediaReview {
id String @id @db.Uuid
media Media @relation("MediaReviews", fields: [mediaId], references:[id])
mediaId String @db.Uuid
rating Int
title String @db.VarChar(255)
text String @db.Text
reaction MediaReviewReaction
creator User @relation("UserMediaReviews", fields: [createdBy], references: [id])
createdBy String @db.Uuid
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@map("movie_reviews")
}
model Comment {
id String @id @db.Uuid
episode Episode @relation("EpisodeComments", fields: [episodeId], references: [id])
episodeId String @db.Uuid
text String @db.Text
isParent Boolean @default(false)
parent Comment? @relation("ParentReplyComments", fields: [parentId], references: [id])
parentId String? @db.Uuid
user User @relation("UserComments", fields: [userId], references: [id])
userId String @db.Uuid
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
replies Comment[] @relation("ParentReplyComments")
likes CommentLike[] @relation("CommentLikes")
@@map("comments")
}
model CommentLike {
id String @id @db.Uuid
comment Comment @relation("CommentLikes", fields: [commentId], references: [id])
commentId String @db.Uuid
user User @relation("UserCommentLikes", fields: [userLiked], references: [id])
userLiked String @db.Uuid
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@map("comment_likes")
}
model CommentReport {
id String @id @db.Uuid
reporter User @relation("UserReportComments", fields: [userReporter], references: [id])
userReporter String @db.Uuid
commentReported String @db.Uuid
isSupervisorReport Boolean @default(false)
reason ReportReason
status ReportStatus
description String @db.VarChar(255)
approver User? @relation("ApprovedReportComments", fields: [approvedBy], references: [id])
approvedBy String? @db.Uuid
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@map("comment_reports")
}
model Language {
id String @id @db.Uuid
name String @db.VarChar(255)
code String @db.VarChar(5) @unique
countryFlag String @db.VarChar(10)
fileLocation String @db.Text
creator User @relation("UserCreateLanguages", fields: [craetedBy], references: [id])
craetedBy String @db.Uuid
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
user_used UserPreference[] @relation("UserPreferenceLang")
@@map("languages")
}
model EmailSystemAccount {
id String @id @db.Uuid
name String @unique
host String @db.VarChar(255)
port Int
secure Boolean
email String @unique @db.VarChar(255)
username String @unique @db.VarChar(255)
password String @db.VarChar(255)
purpose EmailPorpose
creator User @relation("UserCreateSystemAccount", fields: [createdBy], references: [id])
createdBy String @db.Uuid
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@map("email_system_accounts")
}
model EmailSystemHistory {
id String @id @db.Uuid
purpose EmailPorpose
fromEmail String @db.Text
toEmail String @db.Text
user User @relation("UserEmails", fields: [userRelated], references: [id])
userRelated String @db.Uuid
title String @db.VarChar(255)
htmlContent String @db.Text
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@map("email_system_histories")
}
model HeroBanner {
id String @id @db.Uuid
orderPriority Int? @unique
mediaId String @db.Uuid
media Media @relation("MediaBannerPromotion", fields: [mediaId], references: [id])
imageUrl String? @db.Text
startDate DateTime
endDate DateTime
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
creatorId String @db.Uuid
createdBy User @relation("UserHeroBanner", fields: [creatorId], references: [id])
@@map("hero_banner")
}
model SystemPreference {
id String @id @db.Uuid
key String @db.VarChar(225) @unique
value String @db.VarChar(225)
description String @db.Text
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@map("system_preferences")
}
model SystemNotification {
id String @id @db.Uuid
type TypeSystemNotification
componentName String? @db.VarChar(255)
popupImage String? @db.Text
titleToast String? @db.VarChar(255)
contentToast String? @db.Text
creator User @relation("UserCreatorSystemNotifications", fields: [createdBy], references: [id])
createdBy String @db.Uuid
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@map("system_notifications")
}
model SystemLog {
id String @id @db.Uuid
title String @db.VarChar(255)
notes String @db.Text
user User? @relation("UserSystemLogs", fields: [relatedUser], references: [id])
relatedUser String? @db.Uuid
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@map("system_logs")
}
//// Prisma Enum Values ////
// Media Enum
enum MediaType {
TV
ONA
OVA
Movie
Special
Music
}
enum Country {
JP @map("Japanese")
EN @map("English")
ID @map("Indonesia")
KR @map("Korea")
}
// Character Enum
enum CharacterRole {
Main
Supporting
}
// MediaLog Enum
enum MediaOperation {
create
update
delete
}
// User Enum
enum UserGender {
male
female
}
// UserPreference Enum
enum AdultFiltering {
hide
show
explicit
}
enum AdultAlert {
hide
show
}
enum VideoQuality {
Q2160
Q1440
Q1080
Q720
Q480
Q360
Q240
Q144
}
// userNotification Enum
enum UserNotificationState {
info
warning
danger
}
// CommentReport Enum
enum ReportStatus {
pending
resolved
rejected
}
enum ReportReason {
sexualize
violent
explicit
hateful
political
racist
spam
other
}
// Collection Enum
enum AccessStatus {
private
selected
protected
public
}
enum AccessScope {
viewer
editor
}
// MediaReview Enum
enum MediaReviewReaction {
angry
sad
awesome
happy
sleepy
annoyed
disgusting
disappointed
}
// EmailSystemHistory Enum
enum EmailPorpose {
forgot_password
account_activation
account_notification
subscribtion
}
// systemNotification Enum
enum TypeSystemNotification {
component
popup
toast
}
generator client {
provider = "prisma-client-js"
}
generator dbml {
provider = "prisma-dbml-generator"
}
datasource db {
provider = "postgresql"
}

View File

@ -0,0 +1,22 @@
import { Context, Static } from "elysia";
import { returnWriteResponse } from "../../../helpers/callback/httpResponse";
import { addItemToCollectionBySytemSchema } from "../schemas/addItemToCollectionBySytem.schema";
import { addItemToCollectionBySystemService } from "../services/addItemToCollectionBySystem.service";
import { mainErrorHandler } from "../../../helpers/error/handler";
export const addItemToCollectionBySytemController = async (ctx: {
set: Context["set"];
headers: Static<typeof addItemToCollectionBySytemSchema.headers>;
body: Static<typeof addItemToCollectionBySytemSchema.body>;
}) => {
try {
const savedItem = await addItemToCollectionBySystemService({
cookie: ctx.headers.cookie,
collectionName: ctx.body.name,
mediaId: ctx.body.itemId,
});
return returnWriteResponse(ctx.set, 200, "Item added to collection successfully", savedItem);
} catch (error) {
return mainErrorHandler(ctx.set, error);
}
};

View File

@ -0,0 +1,22 @@
import { Context, Static } from "elysia";
import { mainErrorHandler } from "../../../helpers/error/handler";
import { returnWriteResponse } from "../../../helpers/callback/httpResponse";
import { removeItemFromCollectionBySystemService } from "../services/removeItemFromCollectionBySystem.service";
import { removeItemFromCollectionBySytemSchema } from "../schemas/removeItemFromCollectionBySytem.schema";
export const removeItemFromCollectionBySytemController = async (ctx: {
set: Context["set"];
headers: Static<typeof removeItemFromCollectionBySytemSchema.headers>;
body: Static<typeof removeItemFromCollectionBySytemSchema.body>;
}) => {
try {
const removedItem = await removeItemFromCollectionBySystemService({
cookie: ctx.headers.cookie,
collectionName: ctx.body.name,
mediaId: ctx.body.itemId,
});
return returnWriteResponse(ctx.set, 200, "Item removed from collection successfully", removedItem);
} catch (error) {
return mainErrorHandler(ctx.set, error);
}
};

View File

@ -0,0 +1,9 @@
import Elysia from "elysia";
import { addItemToCollectionBySytemController } from "./controllers/addItemToCollectionBySytem.controller";
import { addItemToCollectionBySytemSchema } from "./schemas/addItemToCollectionBySytem.schema";
import { removeItemFromCollectionBySytemController } from "./controllers/removeItemFromCollectionBySytem.controller";
import { removeItemFromCollectionBySytemSchema } from "./schemas/removeItemFromCollectionBySytem.schema";
export const collectionModule = new Elysia({ prefix: "/collections", tags: ["Collections"] })
.post("/sys", addItemToCollectionBySytemController, addItemToCollectionBySytemSchema)
.delete("/sys", removeItemFromCollectionBySytemController, removeItemFromCollectionBySytemSchema);

View File

@ -0,0 +1,31 @@
import slugify from "slugify";
import { AppError } from "../../../helpers/error/instances/app";
import { prisma } from "../../../utils/databases/prisma/connection";
export type DeleteUserCollectionBySystemPayload = {
userId: string;
collectionName: string;
itemId: string;
};
export const deleteItemInUserCollectionBySystemRepository = async (payload: DeleteUserCollectionBySystemPayload) => {
try {
return await prisma.collection.update({
where: {
slug_ownerId: {
slug: slugify(payload.collectionName, { lower: true }),
ownerId: payload.userId,
},
},
data: {
media_saved: {
deleteMany: {
mediaId: payload.itemId,
},
},
},
});
} catch (error) {
throw new AppError(500, "Failed to remove item from collection", error);
}
};

View File

@ -0,0 +1,60 @@
import slugify from "slugify";
import { AppError } from "../../../helpers/error/instances/app";
import { prisma } from "../../../utils/databases/prisma/connection";
import { generateUUIDv7 } from "../../../helpers/databases/uuidv7";
import { Prisma } from "@prisma/client";
export interface UpsertUserCollectionRepositoryPayload {
userId: string;
collectionName: string;
mediaConnectId: string;
}
export const upsertUserCollectionBySystemRepository = async (payload: UpsertUserCollectionRepositoryPayload) => {
try {
return await prisma.collection.upsert({
where: {
slug_ownerId: {
slug: slugify(payload.collectionName, { lower: true }),
ownerId: payload.userId,
},
},
update: {
media_saved: {
create: {
id: generateUUIDv7(),
media: {
connect: {
id: payload.mediaConnectId,
},
},
},
},
},
create: {
id: generateUUIDv7(),
name: payload.collectionName,
slug: slugify(payload.collectionName, { lower: true }),
owner: {
connect: {
id: payload.userId,
},
},
media_saved: {
create: {
id: generateUUIDv7(),
media: {
connect: {
id: payload.mediaConnectId,
},
},
},
},
},
});
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002")
throw new AppError(400, "Media item is already in the collection");
throw new AppError(500, "Failed to upsert user collection");
}
};

View File

@ -0,0 +1,33 @@
import { t } from "elysia";
import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema";
export const addItemToCollectionBySytemSchema = {
headers: t.Object({
cookie: t.String({ description: "Authentication token in cookie format, e.g., auth_token=your_jwt_token;" }),
}),
body: t.Object({
name: t.String({ description: "Name of the collection to which the item will be added" }),
itemId: t.String({ description: "ID of the item to be added to the collection", examples: ["12345"] }),
}),
detail: {
summary: "Add an item to a collection",
description: "Adds a specified item to a collection identified by its name.",
responses: {
200: {
description: "The item was successfully added to the collection.",
content: {
"application/json": {
schema: {
type: "object",
properties: {
success: { type: "boolean", example: true },
status: { type: "number", example: 200 },
message: { type: "string", example: "Item added to collection successfully" },
},
},
},
},
},
},
},
} satisfies AppRouteSchema;

View File

@ -0,0 +1,33 @@
import { t } from "elysia";
import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema";
export const removeItemFromCollectionBySytemSchema = {
headers: t.Object({
cookie: t.String({ description: "Authentication token in cookie format, e.g., auth_token=your_jwt_token;" }),
}),
body: t.Object({
name: t.String({ description: "Name of the collection to which the item will be added" }),
itemId: t.String({ description: "ID of the item to be added to the collection", examples: ["12345"] }),
}),
detail: {
summary: "Remove an item from a collection",
description: "Removes a specified item from a collection identified by its name.",
responses: {
200: {
description: "The item was successfully removed from the collection.",
content: {
"application/json": {
schema: {
type: "object",
properties: {
success: { type: "boolean", example: true },
status: { type: "number", example: 200 },
message: { type: "string", example: "Item removed from collection successfully" },
},
},
},
},
},
},
},
} satisfies AppRouteSchema;

View File

@ -0,0 +1,24 @@
import { parse } from "cookie";
import { tokenValidationService } from "../../auth/services/http/tokenValidation.service";
import { ErrorForwarder } from "../../../helpers/error/instances/forwarder";
import { upsertUserCollectionBySystemRepository } from "../repositories/upsertUserCollectionBySystem.repository";
export type AddItemToCollectionPayload = {
cookie: string;
collectionName: string;
mediaId: string;
};
export const addItemToCollectionBySystemService = async (payload: AddItemToCollectionPayload) => {
try {
const { auth_token } = parse(payload.cookie);
const userData = await tokenValidationService(auth_token as string);
return await upsertUserCollectionBySystemRepository({
userId: userData.user.id,
collectionName: payload.collectionName,
mediaConnectId: payload.mediaId,
});
} catch (error) {
ErrorForwarder(error);
}
};

View File

@ -0,0 +1,24 @@
import { parse } from "cookie";
import { ErrorForwarder } from "../../../helpers/error/instances/forwarder";
import { tokenValidationService } from "../../auth/services/http/tokenValidation.service";
import { deleteItemInUserCollectionBySystemRepository } from "../repositories/deleteItemInUserCollectionBySystem.repository";
export type RemoveItemFromCollectionPayload = {
cookie: string;
collectionName: string;
mediaId: string;
};
export const removeItemFromCollectionBySystemService = async (payload: RemoveItemFromCollectionPayload) => {
try {
const { auth_token } = parse(payload.cookie);
const { user } = await tokenValidationService(auth_token as string);
return await deleteItemInUserCollectionBySystemRepository({
userId: user.id,
collectionName: payload.collectionName,
itemId: payload.mediaId,
});
} catch (error) {
ErrorForwarder(error);
}
};

View File

@ -1,17 +1,14 @@
import { AppError } from "../../../../helpers/error/instances/app";
import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder";
import { getMediaIdFromSlugRepository } from "../../../media/repositories/GET/getMediaIdFromSlug.repository";
import { selectMediaIdFromSlugRepository } from "../../../media/repositories/SELECT/selectMediaIdFromSlug.repository";
import { GetEpisodeDetailsParams } from "../../controllers/getEpisodeDetails.controller";
import { getEpisodeDetailsRepository } from "../../repositories/GET/getEpisodeDetails.repository";
export const getEpisodeDetailsService = async (
params: GetEpisodeDetailsParams,
) => {
export const getEpisodeDetailsService = async (params: GetEpisodeDetailsParams) => {
try {
if (!params.mediaSlug || !params.episode)
throw new AppError(400, "Media slug and episode are required.");
if (!params.mediaSlug || !params.episode) throw new AppError(400, "Media slug and episode are required.");
const mediaId = await getMediaIdFromSlugRepository(params.mediaSlug);
const mediaId = await selectMediaIdFromSlugRepository(params.mediaSlug);
if (!mediaId?.id) throw new AppError(404, "Media not found.");
const result = await getEpisodeDetailsRepository({

View File

@ -1,17 +1,16 @@
import { Context } from "elysia";
import { Context, Static } from "elysia";
import { mainErrorHandler } from "../../../helpers/error/handler";
import { returnReadResponse } from "../../../helpers/callback/httpResponse";
import { getActiveHeroBannerService } from "../services/getActiveHeroBanner.service";
import { getActiveHeroBannerSchema } from "../schemas/getActiveHeroBanner.schema";
export const getActiveHeroBannerController = async (ctx: Context) => {
export const getActiveHeroBannerController = async (ctx: {
set: Context["set"];
header: Static<typeof getActiveHeroBannerSchema.headers>;
}) => {
try {
const response = await getActiveHeroBannerService();
return returnReadResponse(
ctx.set,
200,
"Active hero banners fetched successfully",
response,
);
const response = await getActiveHeroBannerService({ cookie: ctx.header?.cookie });
return returnReadResponse(ctx.set, 200, "Active hero banners fetched successfully", response);
} catch (error) {
return mainErrorHandler(ctx.set, error);
}

View File

@ -1,7 +1,7 @@
import { AppError } from "../../../../helpers/error/instances/app";
import { prisma } from "../../../../utils/databases/prisma/connection";
export const findAllActiveHeroBannerRepository = async () => {
export const findAllActiveHeroBannerRepository = async (userId?: string) => {
try {
return await prisma.heroBanner.findMany({
where: {
@ -36,6 +36,17 @@ export const findAllActiveHeroBannerRepository = async () => {
name: true,
},
},
_count: {
select: {
inCollections: {
where: {
collection: {
ownerId: userId,
},
},
},
},
},
},
},
},

View File

@ -0,0 +1,8 @@
import { t } from "elysia";
import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema";
export const getActiveHeroBannerSchema = {
headers: t.Object({
cookie: t.Optional(t.String()),
}),
} satisfies AppRouteSchema;

View File

@ -1,22 +1,25 @@
import { redisKey } from "../../../config/redis/key";
import { AppError } from "../../../helpers/error/instances/app";
import { ErrorForwarder } from "../../../helpers/error/instances/forwarder";
import { redis } from "../../../utils/databases/redis/connection";
import { tokenValidationService } from "../../auth/services/http/tokenValidation.service";
import { findSystemPreferenceService } from "../../systemPreference/services/internal/findSystemPreference.service";
import { findAllActiveHeroBannerRepository } from "../repositories/GET/findAllActiveHeroBanner.repository";
export const getActiveHeroBannerService = async () => {
export const getActiveHeroBannerService = async ({ cookie }: { cookie?: string }) => {
try {
const userId = cookie ? (await tokenValidationService(cookie)).user.id : undefined;
// Check if Hero Banner is enabled in system preferences
const isHeroBannerEnabled = await findSystemPreferenceService("HERO_BANNER_ENABLED", "boolean");
if (!isHeroBannerEnabled) throw new AppError(403, "Hero Banner is disabled");
// Try to get active banners from Redis cache
const cachedBanners = await redis.get(`${redisKey.filter((key) => key.name === "HERO_BANNER")[0].key}`);
if (cachedBanners) return JSON.parse(cachedBanners);
// Dont implement caching just yet; implement collection caching first, then implement banner caching.
// Please note that currently, a database query is still required to check the collections.
// // Try to get active banners from Redis cache
// const cachedBanners = await redis.get(`${redisKey.filter((key) => key.name === "HERO_BANNER")[0].key}`);
// if (cachedBanners) return JSON.parse(cachedBanners);
// If not in cache, fetch from database and cache the result
const activeBanners = await findAllActiveHeroBannerRepository();
const activeBanners = await findAllActiveHeroBannerRepository(userId);
const constructedBanners = activeBanners.map((banner) => ({
id: banner.media.id,
title: banner.media.title,
@ -27,11 +30,14 @@ export const getActiveHeroBannerService = async () => {
slug: genre.slug,
name: genre.name,
})),
isInCollection: banner.media._count.inCollections > 0,
}));
await redis.set(
`${redisKey.filter((key) => key.name === "HERO_BANNER")[0].key}`,
JSON.stringify(constructedBanners),
);
// await redis.set(
// `${redisKey.filter((key) => key.name === "HERO_BANNER")[0].key}`,
// JSON.stringify(constructedBanners),
// );
return constructedBanners;
} catch (error) {
ErrorForwarder(error);

View File

@ -1,27 +1,20 @@
import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder";
import { MediaEpisodeInfoResponse } from "../../types/mediaEpisodeInfo.type";
import { getMediaByMalIdRepository } from "../../../media/repositories/GET/getMediaByMalId.repository";
import { AppError } from "../../../../helpers/error/instances/app";
import { SystemAccountId } from "../../../../config/account/system";
import { bulkInsertEpisodesRepository } from "../../repositories/bulkInsertEpisodes.repository";
import { getEpisodeReferenceAPI } from "../../../../config/apis/jikan/episode.reference";
import { selectMediaByMalIdRepository } from "../../../media/repositories/SELECT/selectMediaByMalId.repository";
export const bulkInsertEpisodeService = async (
mal_id: number,
page: number = 1,
) => {
export const bulkInsertEpisodeService = async (mal_id: number, page: number = 1) => {
try {
const episodeAPI = getEpisodeReferenceAPI(mal_id);
const episodeData: MediaEpisodeInfoResponse = await fetch(
`${episodeAPI.baseURL}${episodeAPI.getEpisodeList}?page=${page}`,
).then((res) => res.json());
const mediaData = await getMediaByMalIdRepository(mal_id);
if (!mediaData)
throw new AppError(
404,
`Media with Mal ID ${mal_id} not found in database`,
);
const mediaData = await selectMediaByMalIdRepository(mal_id);
if (!mediaData) throw new AppError(404, `Media with Mal ID ${mal_id} not found in database`);
const insertedEpisodeData = [];
episodeData.data.forEach(async (episode) => {

View File

@ -1,19 +1,16 @@
import { Context } from "elysia";
import { Context, Static } from "elysia";
import { mainErrorHandler } from "../../../helpers/error/handler";
import { getAllMediaService } from "../services/http/getAllMedia.service";
import { returnReadResponse } from "../../../helpers/callback/httpResponse";
import { getAllMediaSchema } from "../schemas/getAllMedia.schema";
export const getAllMediaController = async (
ctx: Context & { query: { page: string } },
) => {
export const getAllMediaController = async (ctx: {
set: Context["set"];
query: Static<typeof getAllMediaSchema.query>;
}) => {
try {
const mediaData = await getAllMediaService(ctx.query.page);
return returnReadResponse(
ctx.set,
200,
"Media fetched successfully",
mediaData,
);
return returnReadResponse(ctx.set, 200, "Media fetched successfully", mediaData);
} catch (error) {
return mainErrorHandler(ctx.set, error);
}

View File

@ -0,0 +1,17 @@
import { Context, Static } from "elysia";
import { mainErrorHandler } from "../../../helpers/error/handler";
import { getMediaBySlugSchema } from "../schemas/getMediaBySlug.schema";
import { getMediaBySlugService } from "../services/http/getMediaBySlug.service";
import { returnReadResponse } from "../../../helpers/callback/httpResponse";
export const getMediaBySlugController = async (ctx: {
set: Context["set"];
params: Static<typeof getMediaBySlugSchema.params>;
}) => {
try {
const mediaData = await getMediaBySlugService(ctx.params.slug);
return returnReadResponse(ctx.set, 200, "Media fetched successfully", mediaData);
} catch (error) {
return mainErrorHandler(ctx.set, error);
}
};

View File

@ -1,7 +1,9 @@
import Elysia from "elysia";
import { getAllMediaController } from "./controllers/getAllMedia.controller";
import { getMediaBySlugController } from "./controllers/getMediaBySlug.controller";
import { getMediaBySlugSchema } from "./schemas/getMediaBySlug.schema";
import { getAllMediaSchema } from "./schemas/getAllMedia.schema";
export const mediaModule = new Elysia({ prefix: "/media" }).get(
"/",
getAllMediaController,
);
export const mediaModule = new Elysia({ prefix: "/media", tags: ["Media"] })
.get("/", getAllMediaController, getAllMediaSchema)
.get("/:slug", getMediaBySlugController, getMediaBySlugSchema);

View File

@ -1,7 +1,7 @@
import { AppError } from "../../../../helpers/error/instances/app";
import { mediaModel } from "../../model";
export const getAllMediaRepository = async (page: number) => {
export const selectAllMediaRepository = async (page: number) => {
try {
const limit = 10;
return await mediaModel.findMany({

View File

@ -1,7 +1,7 @@
import { AppError } from "../../../../helpers/error/instances/app";
import { mediaModel } from "../../model";
export const getMediaByMalIdRepository = async (mal_id: number) => {
export const selectMediaByMalIdRepository = async (mal_id: number) => {
try {
return await mediaModel.findUnique({
where: { malId: mal_id },

View File

@ -0,0 +1,12 @@
import { AppError } from "../../../../helpers/error/instances/app";
import { prisma } from "../../../../utils/databases/prisma/connection";
export const selectMediaBySlugRepository = async (slug: string) => {
try {
return await prisma.media.findUnique({
where: { slug },
});
} catch (error) {
throw new AppError(500, "Failed to fetch media by slug", error);
}
};

View File

@ -1,7 +1,7 @@
import { AppError } from "../../../../helpers/error/instances/app";
import { mediaModel } from "../../model";
export const getMediaIdFromSlugRepository = async (slug: string) => {
export const selectMediaIdFromSlugRepository = async (slug: string) => {
try {
return await mediaModel.findUnique({
where: { slug },

View File

@ -0,0 +1,58 @@
import { t } from "elysia";
import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema";
export const getAllMediaSchema = {
query: t.Object({
page: t.String({ description: "The page number for pagination", default: "1" }),
}),
detail: {
summary: "Fetch all media items with pagination",
description:
"Fetch a paginated list of all media items. The 'page' query parameter can be used to specify the page number for pagination.",
responses: {
200: {
description: "Media items fetched successfully",
content: {
"application/json": {
schema: {
type: "object",
properties: {
success: { type: "boolean", example: true },
status: { type: "number", example: 200 },
message: { type: "string", example: "Media fetched successfully" },
data: {
type: "array",
items: {
type: "object",
properties: {
status: { type: "string", example: "Finished Airing" },
id: { type: "string", example: "12345" },
title: { type: "string", example: "Example Media Title" },
slug: { type: "string", example: "example-media-title" },
malId: { type: "number", example: 67890 },
pictureMedium: { type: "string", example: "https://example.com/medium.jpg" },
pictureLarge: { type: "string", example: "https://example.com/large.jpg" },
country: { type: "string", example: "JP" },
score: { type: "number", example: 8.5 },
startAiring: { type: "string", format: "date-time", example: "2023-01-01T00:00:00Z" },
endAiring: { type: "string", format: "date-time", example: "2023-12-31T23:59:59Z" },
synopsis: { type: "string", example: "This is an example synopsis of the media item." },
ageRating: { type: "string", example: "PG-13" },
mediaType: { type: "string", example: "Anime" },
source: { type: "string", example: "Manga" },
onDraft: { type: "boolean", example: false },
uploadedBy: { type: "string", example: "admin" },
deletedAt: { type: "string", format: "date-time", nullable: true, example: null },
createdAt: { type: "string", format: "date-time", example: "2023-01-01T00:00:00Z" },
updatedAt: { type: "string", format: "date-time", example: "2023-01-02T00:00:00Z" },
},
},
},
},
},
},
},
},
},
},
} satisfies AppRouteSchema;

View File

@ -0,0 +1,29 @@
import { t } from "elysia";
import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema";
export const getMediaBySlugSchema = {
params: t.Object({
slug: t.String({ description: "The slug of the media to fetch" }),
}),
detail: {
summary: "Fetch a media item by its slug",
description: "Fetch the specified media item using its slug. This endpoint returns the media details if found.",
responses: {
200: {
description: "Media item fetched successfully",
content: {
"application/json": {
schema: {
type: "object",
properties: {
success: { type: "boolean", example: true },
status: { type: "number", example: 200 },
message: { type: "string", example: "Media fetched successfully" },
},
},
},
},
},
},
},
} satisfies AppRouteSchema;

View File

@ -1,14 +1,11 @@
import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder";
import { getAllMediaRepository } from "../../repositories/GET/getAllMedia.repository";
import { selectAllMediaRepository } from "../../repositories/SELECT/selectAllMedia.repository";
export const getAllMediaService = async (pagination: string) => {
try {
const page =
/^\d+$/.test(pagination) && Number(pagination) > 0
? Number(pagination)
: 1;
const page = /^\d+$/.test(pagination) && Number(pagination) > 0 ? Number(pagination) : 1;
return getAllMediaRepository(page);
return selectAllMediaRepository(page);
} catch (error) {
ErrorForwarder(error);
}

View File

@ -0,0 +1,14 @@
import { AppError } from "../../../../helpers/error/instances/app";
import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder";
import { selectMediaBySlugRepository } from "../../repositories/SELECT/selectMediaBySlug.repository";
export const getMediaBySlugService = async (slug: string) => {
try {
const mediaData = await selectMediaBySlugRepository(slug);
if (!mediaData) throw new AppError(404, "Media not found with the provided slug.");
return mediaData;
} catch (error) {
ErrorForwarder(error);
}
};