Compare commits

...

18 Commits

Author SHA1 Message Date
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
33 changed files with 1262 additions and 905 deletions

View File

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

View File

@ -7,6 +7,9 @@ CREATE TYPE "MediaType" AS ENUM ('TV', 'ONA', 'OVA', 'Movie', 'Special', 'Music'
-- CreateEnum -- CreateEnum
CREATE TYPE "Country" AS ENUM ('Japanese', 'English', 'Indonesia', 'Korea'); CREATE TYPE "Country" AS ENUM ('Japanese', 'English', 'Indonesia', 'Korea');
-- CreateEnum
CREATE TYPE "Season" AS ENUM ('Winter', 'Spring', 'Summer', 'Fall');
-- CreateEnum -- CreateEnum
CREATE TYPE "CharacterRole" AS ENUM ('Main', 'Supporting'); CREATE TYPE "CharacterRole" AS ENUM ('Main', 'Supporting');
@ -72,6 +75,8 @@ CREATE TABLE "medias" (
"deletedAt" TIMESTAMP(3), "deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"season" "Season",
"yearReleased" SMALLINT NOT NULL,
CONSTRAINT "medias_pkey" PRIMARY KEY ("id") CONSTRAINT "medias_pkey" PRIMARY KEY ("id")
); );
@ -367,7 +372,7 @@ CREATE TABLE "user_logs" (
-- CreateTable -- CreateTable
CREATE TABLE "collections" ( CREATE TABLE "collections" (
"id" UUID NOT NULL, "id" UUID NOT NULL,
"name" VARCHAR(255) NOT NULL, "name" VARCHAR(115) NOT NULL,
"ownerId" UUID NOT NULL, "ownerId" UUID NOT NULL,
"accessStatus" "AccessStatus" NOT NULL DEFAULT 'private', "accessStatus" "AccessStatus" NOT NULL DEFAULT 'private',
"password" VARCHAR(255), "password" VARCHAR(255),
@ -375,10 +380,21 @@ CREATE TABLE "collections" (
"deletedAt" TIMESTAMP(3), "deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"slug" VARCHAR(115) NOT NULL,
CONSTRAINT "collections_pkey" PRIMARY KEY ("id") CONSTRAINT "collections_pkey" PRIMARY KEY ("id")
); );
-- CreateTable
CREATE TABLE "CollectionMedia" (
"id" UUID NOT NULL,
"collectionId" UUID NOT NULL,
"mediaId" UUID NOT NULL,
"savedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "CollectionMedia_pkey" PRIMARY KEY ("id")
);
-- CreateTable -- CreateTable
CREATE TABLE "watch_histories" ( CREATE TABLE "watch_histories" (
"id" UUID NOT NULL, "id" UUID NOT NULL,
@ -502,18 +518,13 @@ CREATE TABLE "email_system_histories" (
CREATE TABLE "hero_banner" ( CREATE TABLE "hero_banner" (
"id" UUID NOT NULL, "id" UUID NOT NULL,
"orderPriority" INTEGER, "orderPriority" INTEGER,
"isClickable" BOOLEAN NOT NULL DEFAULT false,
"title" VARCHAR(225),
"tags" TEXT[],
"description" TEXT,
"buttonContent" VARCHAR(100),
"buttonLink" TEXT,
"imageUrl" TEXT, "imageUrl" TEXT,
"startDate" TIMESTAMP(3) NOT NULL, "startDate" TIMESTAMP(3) NOT NULL,
"endDate" TIMESTAMP(3) NOT NULL, "endDate" TIMESTAMP(3) NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"creatorId" UUID NOT NULL, "creatorId" UUID NOT NULL,
"mediaId" UUID NOT NULL,
CONSTRAINT "hero_banner_pkey" PRIMARY KEY ("id") CONSTRAINT "hero_banner_pkey" PRIMARY KEY ("id")
); );
@ -591,14 +602,6 @@ CREATE TABLE "_MediaCharacters" (
CONSTRAINT "_MediaCharacters_AB_pkey" PRIMARY KEY ("A","B") 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 -- CreateTable
CREATE TABLE "_UserSelectedSharingCollention" ( CREATE TABLE "_UserSelectedSharingCollention" (
"A" UUID NOT NULL, "A" UUID NOT NULL,
@ -673,6 +676,12 @@ CREATE UNIQUE INDEX "user_roles_name_key" ON "user_roles"("name");
-- CreateIndex -- CreateIndex
CREATE INDEX "user_sessions_userId_isAuthenticated_deletedAt_idx" ON "user_sessions"("userId", "isAuthenticated", "deletedAt"); CREATE INDEX "user_sessions_userId_isAuthenticated_deletedAt_idx" ON "user_sessions"("userId", "isAuthenticated", "deletedAt");
-- CreateIndex
CREATE UNIQUE INDEX "collections_slug_ownerId_key" ON "collections"("slug", "ownerId");
-- CreateIndex
CREATE UNIQUE INDEX "CollectionMedia_collectionId_mediaId_key" ON "CollectionMedia"("collectionId", "mediaId");
-- CreateIndex -- CreateIndex
CREATE UNIQUE INDEX "languages_code_key" ON "languages"("code"); CREATE UNIQUE INDEX "languages_code_key" ON "languages"("code");
@ -703,24 +712,21 @@ CREATE INDEX "_UserFavoriteGenres_B_index" ON "_UserFavoriteGenres"("B");
-- CreateIndex -- CreateIndex
CREATE INDEX "_MediaCharacters_B_index" ON "_MediaCharacters"("B"); CREATE INDEX "_MediaCharacters_B_index" ON "_MediaCharacters"("B");
-- CreateIndex
CREATE INDEX "_MediaCollections_B_index" ON "_MediaCollections"("B");
-- CreateIndex -- CreateIndex
CREATE INDEX "_UserSelectedSharingCollention_B_index" ON "_UserSelectedSharingCollention"("B"); CREATE INDEX "_UserSelectedSharingCollention_B_index" ON "_UserSelectedSharingCollention"("B");
-- AddForeignKey -- AddForeignKey
ALTER TABLE "medias" ADD CONSTRAINT "medias_uploadedBy_fkey" FOREIGN KEY ("uploadedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 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 -- AddForeignKey
ALTER TABLE "media_logs" ADD CONSTRAINT "media_logs_approvedBy_fkey" FOREIGN KEY ("approvedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "media_logs" ADD CONSTRAINT "media_logs_approvedBy_fkey" FOREIGN KEY ("approvedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "media_logs" ADD CONSTRAINT "media_logs_mediaId_fkey" FOREIGN KEY ("mediaId") REFERENCES "medias"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 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 "media_logs" ADD CONSTRAINT "media_logs_proposedBy_fkey" FOREIGN KEY ("proposedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "genres" ADD CONSTRAINT "genres_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "genres" ADD CONSTRAINT "genres_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@ -733,15 +739,15 @@ ALTER TABLE "characters" ADD CONSTRAINT "characters_creatorId_fkey" FOREIGN KEY
-- AddForeignKey -- AddForeignKey
ALTER TABLE "voice_actors" ADD CONSTRAINT "voice_actors_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 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 -- 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; 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 -- 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; 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 "lang_va_char" ADD CONSTRAINT "lang_va_char_vaId_fkey" FOREIGN KEY ("vaId") REFERENCES "voice_actors"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "episodes" ADD CONSTRAINT "episodes_mediaId_fkey" FOREIGN KEY ("mediaId") REFERENCES "medias"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "episodes" ADD CONSTRAINT "episodes_mediaId_fkey" FOREIGN KEY ("mediaId") REFERENCES "medias"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@ -749,13 +755,13 @@ ALTER TABLE "episodes" ADD CONSTRAINT "episodes_mediaId_fkey" FOREIGN KEY ("medi
ALTER TABLE "episodes" ADD CONSTRAINT "episodes_uploadedBy_fkey" FOREIGN KEY ("uploadedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "episodes" ADD CONSTRAINT "episodes_uploadedBy_fkey" FOREIGN KEY ("uploadedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "episode_likes" ADD CONSTRAINT "episode_likes_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "episode_likes" ADD CONSTRAINT "episode_likes_episodeId_fkey" FOREIGN KEY ("episodeId") REFERENCES "episodes"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "episode_likes" ADD CONSTRAINT "episode_likes_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "user_sessions"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "episode_likes" ADD CONSTRAINT "episode_likes_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "user_sessions"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "episode_likes" ADD CONSTRAINT "episode_likes_episodeId_fkey" FOREIGN KEY ("episodeId") REFERENCES "episodes"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "episode_likes" ADD CONSTRAINT "episode_likes_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "videos" ADD CONSTRAINT "videos_episodeId_fkey" FOREIGN KEY ("episodeId") REFERENCES "episodes"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "videos" ADD CONSTRAINT "videos_episodeId_fkey" FOREIGN KEY ("episodeId") REFERENCES "episodes"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@ -769,23 +775,23 @@ ALTER TABLE "videos" ADD CONSTRAINT "videos_uploadedBy_fkey" FOREIGN KEY ("uploa
-- AddForeignKey -- AddForeignKey
ALTER TABLE "video_services" ADD CONSTRAINT "video_services_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 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 -- AddForeignKey
ALTER TABLE "user_preferences" ADD CONSTRAINT "user_preferences_langPreference_fkey" FOREIGN KEY ("langPreference") REFERENCES "languages"("code") ON DELETE SET NULL ON UPDATE CASCADE; ALTER TABLE "user_preferences" ADD CONSTRAINT "user_preferences_langPreference_fkey" FOREIGN KEY ("langPreference") REFERENCES "languages"("code") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey -- 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; 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_preferences" ADD CONSTRAINT "user_preferences_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "user_roles" ADD CONSTRAINT "user_roles_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "user_roles" ADD CONSTRAINT "user_roles_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- 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; 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 -- 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; 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 -- AddForeignKey
ALTER TABLE "user_notifications" ADD CONSTRAINT "user_notifications_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "user_notifications" ADD CONSTRAINT "user_notifications_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@ -794,29 +800,35 @@ ALTER TABLE "user_notifications" ADD CONSTRAINT "user_notifications_userId_fkey"
ALTER TABLE "user_sessions" ADD CONSTRAINT "user_sessions_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "user_sessions" ADD CONSTRAINT "user_sessions_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "user_logs" ADD CONSTRAINT "user_logs_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "user_logs" ADD CONSTRAINT "user_logs_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "user_sessions"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "user_logs" ADD CONSTRAINT "user_logs_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "user_sessions"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "user_logs" ADD CONSTRAINT "user_logs_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "collections" ADD CONSTRAINT "collections_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "collections" ADD CONSTRAINT "collections_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "watch_histories" ADD CONSTRAINT "watch_histories_id_fkey" FOREIGN KEY ("id") REFERENCES "episodes"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "CollectionMedia" ADD CONSTRAINT "CollectionMedia_collectionId_fkey" FOREIGN KEY ("collectionId") REFERENCES "collections"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "watch_histories" ADD CONSTRAINT "watch_histories_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "CollectionMedia" ADD CONSTRAINT "CollectionMedia_mediaId_fkey" FOREIGN KEY ("mediaId") REFERENCES "medias"("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 -- AddForeignKey
ALTER TABLE "watch_histories" ADD CONSTRAINT "watch_histories_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "user_sessions"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "watch_histories" ADD CONSTRAINT "watch_histories_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "user_sessions"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "movie_reviews" ADD CONSTRAINT "movie_reviews_mediaId_fkey" FOREIGN KEY ("mediaId") REFERENCES "medias"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "watch_histories" ADD CONSTRAINT "watch_histories_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "movie_reviews" ADD CONSTRAINT "movie_reviews_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 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 "movie_reviews" ADD CONSTRAINT "movie_reviews_mediaId_fkey" FOREIGN KEY ("mediaId") REFERENCES "medias"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "comments" ADD CONSTRAINT "comments_episodeId_fkey" FOREIGN KEY ("episodeId") REFERENCES "episodes"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "comments" ADD CONSTRAINT "comments_episodeId_fkey" FOREIGN KEY ("episodeId") REFERENCES "episodes"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@ -833,10 +845,10 @@ ALTER TABLE "comment_likes" ADD CONSTRAINT "comment_likes_commentId_fkey" FOREIG
ALTER TABLE "comment_likes" ADD CONSTRAINT "comment_likes_userLiked_fkey" FOREIGN KEY ("userLiked") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "comment_likes" ADD CONSTRAINT "comment_likes_userLiked_fkey" FOREIGN KEY ("userLiked") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "comment_reports" ADD CONSTRAINT "comment_reports_userReporter_fkey" FOREIGN KEY ("userReporter") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "comment_reports" ADD CONSTRAINT "comment_reports_approvedBy_fkey" FOREIGN KEY ("approvedBy") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "comment_reports" ADD CONSTRAINT "comment_reports_approvedBy_fkey" FOREIGN KEY ("approvedBy") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE; ALTER TABLE "comment_reports" ADD CONSTRAINT "comment_reports_userReporter_fkey" FOREIGN KEY ("userReporter") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "languages" ADD CONSTRAINT "languages_craetedBy_fkey" FOREIGN KEY ("craetedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "languages" ADD CONSTRAINT "languages_craetedBy_fkey" FOREIGN KEY ("craetedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@ -850,6 +862,9 @@ ALTER TABLE "email_system_histories" ADD CONSTRAINT "email_system_histories_user
-- AddForeignKey -- AddForeignKey
ALTER TABLE "hero_banner" ADD CONSTRAINT "hero_banner_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 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 "hero_banner" ADD CONSTRAINT "hero_banner_mediaId_fkey" FOREIGN KEY ("mediaId") REFERENCES "medias"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey -- AddForeignKey
ALTER TABLE "system_notifications" ADD CONSTRAINT "system_notifications_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE "system_notifications" ADD CONSTRAINT "system_notifications_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@ -880,12 +895,6 @@ ALTER TABLE "_MediaCharacters" ADD CONSTRAINT "_MediaCharacters_A_fkey" FOREIGN
-- AddForeignKey -- AddForeignKey
ALTER TABLE "_MediaCharacters" ADD CONSTRAINT "_MediaCharacters_B_fkey" FOREIGN KEY ("B") REFERENCES "medias"("id") ON DELETE CASCADE ON UPDATE CASCADE; 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 -- AddForeignKey
ALTER TABLE "_UserSelectedSharingCollention" ADD CONSTRAINT "_UserSelectedSharingCollention_A_fkey" FOREIGN KEY ("A") REFERENCES "collections"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "_UserSelectedSharingCollention" ADD CONSTRAINT "_UserSelectedSharingCollention_A_fkey" FOREIGN KEY ("A") REFERENCES "collections"("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,14 +0,0 @@
/*
Warnings:
- You are about to alter the column `name` on the `collections` table. The data in that column could be lost. The data in that column will be cast from `VarChar(255)` to `VarChar(115)`.
- A unique constraint covering the columns `[slug,ownerId]` on the table `collections` will be added. If there are existing duplicate values, this will fail.
- Added the required column `slug` to the `collections` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "collections" ADD COLUMN "slug" VARCHAR(115) NOT NULL,
ALTER COLUMN "name" SET DATA TYPE VARCHAR(115);
-- CreateIndex
CREATE UNIQUE INDEX "collections_slug_ownerId_key" ON "collections"("slug", "ownerId");

View File

@ -0,0 +1,14 @@
/*
Warnings:
- The values [Winter,Spring,Summer,Fall] on the enum `Season` will be removed. If these variants are still used in the database, this will fail.
*/
-- AlterEnum
BEGIN;
CREATE TYPE "Season_new" AS ENUM ('winter', 'spring', 'summer', 'fall');
ALTER TABLE "medias" ALTER COLUMN "season" TYPE "Season_new" USING ("season"::text::"Season_new");
ALTER TYPE "Season" RENAME TO "Season_old";
ALTER TYPE "Season_new" RENAME TO "Season";
DROP TYPE "public"."Season_old";
COMMIT;

View File

@ -1,59 +1,48 @@
// 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 { generator client {
provider = "prisma-client-js" provider = "prisma-client-js"
} }
datasource db {
provider = "postgresql"
}
generator dbml { generator dbml {
provider = "prisma-dbml-generator" provider = "prisma-dbml-generator"
} }
datasource db {
provider = "postgresql"
//// Prisma Model //// }
model Media { model Media {
id String @id @db.Uuid id String @id @db.Uuid
title String @db.Text title String
titleAlternative Json titleAlternative Json
slug String @db.Text @unique slug String @unique
malId Int? @unique malId Int? @unique
pictureMedium String @db.Text pictureMedium String
pictureLarge String @db.Text pictureLarge String
genres Genre[] @relation("MediaGenres")
country Country @default(JP) country Country @default(JP)
score Decimal @db.Decimal(4, 2) @default(0.00) score Decimal @default(0.00) @db.Decimal(4, 2)
status String status String
startAiring DateTime? startAiring DateTime?
endAiring DateTime? endAiring DateTime?
synopsis String @db.Text synopsis String
ageRating String ageRating String
mediaType MediaType mediaType MediaType
source String? source String?
studios Studio[] @relation("MediaStudios")
characters Character[] @relation("MediaCharacters")
onDraft Boolean @default(true) onDraft Boolean @default(true)
uploader User @relation("UserUploadedMedias", fields: [uploadedBy], references: [id]) season Season?
yearReleased Int @db.SmallInt
uploadedBy String @db.Uuid uploadedBy String @db.Uuid
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
inCollections CollectionMedia[] @relation("CollectionMedia")
episodes Episode[] @relation("MediaEpisodes")
bannerPromotion HeroBanner[] @relation("MediaBannerPromotion") bannerPromotion HeroBanner[] @relation("MediaBannerPromotion")
logs MediaLog[] @relation("MediaLogs") logs MediaLog[] @relation("MediaLogs")
episodes Episode[] @relation("MediaEpisodes") uploader User @relation("UserUploadedMedias", fields: [uploadedBy], references: [id])
collections Collection[] @relation("MediaCollections")
reviews MediaReview[] @relation("MediaReviews") reviews MediaReview[] @relation("MediaReviews")
characters Character[] @relation("MediaCharacters")
genres Genre[] @relation("MediaGenres")
studios Studio[] @relation("MediaStudios")
@@index([status, onDraft, deletedAt]) @@index([status, onDraft, deletedAt])
@@index([mediaType]) @@index([mediaType])
@ -66,29 +55,29 @@ model MediaLog {
id String @id @db.Uuid id String @id @db.Uuid
status MediaOperation status MediaOperation
approval Boolean @default(false) approval Boolean @default(false)
proposer User @relation("UserProposedMedias", fields: [proposedBy], references: [id])
proposedBy String @db.Uuid proposedBy String @db.Uuid
approver User @relation("UserApprovedMedias", fields: [approvedBy], references: [id])
approvedBy String @db.Uuid approvedBy String @db.Uuid
media Media @relation("MediaLogs", fields: [mediaId], references: [id])
mediaId String @db.Uuid mediaId String @db.Uuid
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
approver User @relation("UserApprovedMedias", fields: [approvedBy], references: [id])
media Media @relation("MediaLogs", fields: [mediaId], references: [id])
proposer User @relation("UserProposedMedias", fields: [proposedBy], references: [id])
@@map("media_logs") @@map("media_logs")
} }
model Genre { model Genre {
id String @id @db.Uuid id String @id @db.Uuid
name String @db.VarChar(255) name String @db.VarChar(255)
slug String @db.VarChar(255) @unique slug String @unique @db.VarChar(255)
malId Int @unique malId Int @unique
malUrl String @db.VarChar(255) malUrl String @db.VarChar(255)
creator User @relation("UserCreatedGenres", fields: [createdBy], references: [id])
createdBy String @db.Uuid createdBy String @db.Uuid
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
creator User @relation("UserCreatedGenres", fields: [createdBy], references: [id])
medias Media[] @relation("MediaGenres") medias Media[] @relation("MediaGenres")
user_favourite_genres UserPreference[] @relation("UserFavoriteGenres") user_favourite_genres UserPreference[] @relation("UserFavoriteGenres")
@ -98,17 +87,17 @@ model Genre {
model Studio { model Studio {
id String @id @db.Uuid id String @id @db.Uuid
name String @db.VarChar(255) name String @db.VarChar(255)
slug String @db.VarChar(255) @unique slug String @unique @db.VarChar(255)
linkAbout String @db.Text linkAbout String
malId Int @unique malId Int @unique
logoUrl String? @db.Text logoUrl String?
creator User @relation("UserCreatedStudios", fields: [createdBy], references: [id])
createdBy String @db.Uuid createdBy String @db.Uuid
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
creator User @relation("UserCreatedStudios", fields: [createdBy], references: [id])
medias Media[] @relation("MediaStudios") medias Media[] @relation("MediaStudios")
@@map("studios") @@map("studios")
} }
@ -120,14 +109,14 @@ model Character {
favorites Int @default(0) favorites Int @default(0)
imageUrl String? imageUrl String?
smallImageUrl String? smallImageUrl String?
createdBy User @relation("UserCreatedCharacters", fields: [creatorId], references: [id])
creatorId String @db.Uuid creatorId String @db.Uuid
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
createdBy User @relation("UserCreatedCharacters", fields: [creatorId], references: [id])
medias Media[] @relation("MediaCharacters")
voice_actors LangVAChar[] @relation("CharVALanguage") voice_actors LangVAChar[] @relation("CharVALanguage")
medias Media[] @relation("MediaCharacters")
@@map("characters") @@map("characters")
} }
@ -136,31 +125,31 @@ model VoiceActor {
malId Int @unique malId Int @unique
name String name String
birthday DateTime? birthday DateTime?
description String? @db.Text description String?
aboutUrl String? aboutUrl String?
imageUrl String? imageUrl String?
websiteUrl String? websiteUrl String?
createdBy User @relation("UserCreatedVoiceActors", fields: [creatorId], references: [id])
creatorId String @db.Uuid creatorId String @db.Uuid
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
characters LangVAChar[] @relation("VACharLanguage") characters LangVAChar[] @relation("VACharLanguage")
createdBy User @relation("UserCreatedVoiceActors", fields: [creatorId], references: [id])
@@map("voice_actors") @@map("voice_actors")
} }
model LangVAChar { model LangVAChar {
id String @id @db.Uuid id String @id @db.Uuid
language String language String
voiceActor VoiceActor @relation("VACharLanguage", fields: [vaId], references: [id])
vaId String @db.Uuid vaId String @db.Uuid
character Character @relation("CharVALanguage", fields: [charId], references: [id])
charId String @db.Uuid charId String @db.Uuid
createdBy User @relation("UserCreatedLangVAChar", fields: [creatorId], references: [id])
creatorId String @db.Uuid creatorId String @db.Uuid
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
character Character @relation("CharVALanguage", fields: [charId], references: [id])
createdBy User @relation("UserCreatedLangVAChar", fields: [creatorId], references: [id])
voiceActor VoiceActor @relation("VACharLanguage", fields: [vaId], references: [id])
@@unique([language, vaId, charId]) @@unique([language, vaId, charId])
@@map("lang_va_char") @@map("lang_va_char")
@ -168,26 +157,25 @@ model LangVAChar {
model Episode { model Episode {
id String @id @db.Uuid id String @id @db.Uuid
media Media @relation("MediaEpisodes", fields: [mediaId], references: [id])
mediaId String @db.Uuid mediaId String @db.Uuid
episode Int episode Int
name String @db.VarChar(255) name String @db.VarChar(255)
score Decimal @db.Decimal(4,2) @default(0.00) score Decimal @default(0.00) @db.Decimal(4, 2)
pictureThumbnail String? @db.Text pictureThumbnail String?
viewed BigInt @default(0) viewed BigInt @default(0)
likes BigInt @default(0) likes BigInt @default(0)
dislikes BigInt @default(0) dislikes BigInt @default(0)
pendingUpload Boolean @default(true) pendingUpload Boolean @default(true)
uploader User @relation("UserEpisodes", fields: [uploadedBy], references: [id])
uploadedBy String @db.Uuid uploadedBy String @db.Uuid
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
user_likes EpisodeLike[] @relation("EpisodeLikes")
videos Video[] @relation("EpisodeVideos")
user_histories WatchHistory[] @relation("EpisodeWatchHistories")
comments Comment[] @relation("EpisodeComments") comments Comment[] @relation("EpisodeComments")
user_likes EpisodeLike[] @relation("EpisodeLikes")
media Media @relation("MediaEpisodes", fields: [mediaId], references: [id])
uploader User @relation("UserEpisodes", fields: [uploadedBy], references: [id])
videos Video[] @relation("EpisodeVideos")
user_histories WatchHistory? @relation("EpisodeWatchHistories")
@@unique([mediaId, episode]) @@unique([mediaId, episode])
@@map("episodes") @@map("episodes")
@ -195,31 +183,32 @@ model Episode {
model EpisodeLike { model EpisodeLike {
id String @id @db.Uuid id String @id @db.Uuid
user User @relation("UserEpisodeLikes", fields: [userId], references: [id])
userId String @db.Uuid userId String @db.Uuid
session UserSession @relation("SessionEpisodeLikes", fields: [sessionId], references: [id])
sessionId String @db.Uuid sessionId String @db.Uuid
episode Episode @relation("EpisodeLikes", fields: [episodeId], references: [id])
episodeId String @db.Uuid episodeId String @db.Uuid
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
episode Episode @relation("EpisodeLikes", fields: [episodeId], references: [id])
session UserSession @relation("SessionEpisodeLikes", fields: [sessionId], references: [id])
user User @relation("UserEpisodeLikes", fields: [userId], references: [id])
@@map("episode_likes") @@map("episode_likes")
} }
model Video { model Video {
id String @id @db.Uuid id String @id @db.Uuid
episode Episode @relation("EpisodeVideos", fields: [episodeId], references: [id])
episodeId String @db.Uuid episodeId String @db.Uuid
service VideoService @relation("VideoServices", fields: [serviceId], references: [id])
serviceId String @db.Uuid serviceId String @db.Uuid
videoCode String @db.VarChar(255) videoCode String @db.VarChar(255)
thumbnailCode String? thumbnailCode String?
pendingUpload Boolean @default(true) pendingUpload Boolean @default(true)
uploader User @relation("UserVideos", fields: [uploadedBy], references: [id])
uploadedBy String @db.Uuid uploadedBy String @db.Uuid
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
episode Episode @relation("EpisodeVideos", fields: [episodeId], references: [id])
service VideoService @relation("VideoServices", fields: [serviceId], references: [id])
uploader User @relation("UserVideos", fields: [uploadedBy], references: [id])
@@unique([serviceId, videoCode]) @@unique([serviceId, videoCode])
@@map("videos") @@map("videos")
@ -227,21 +216,21 @@ model Video {
model VideoService { model VideoService {
id String @id @db.Uuid id String @id @db.Uuid
name String @db.VarChar(255) @unique name String @unique @db.VarChar(255)
domain String @db.VarChar(255) domain String @db.VarChar(255)
logo String? @db.Text logo String?
hexColor String @db.VarChar(10) hexColor String @db.VarChar(10)
endpointVideo String @db.Text endpointVideo String
endpointThumbnail String? @db.Text endpointThumbnail String?
endpointDownload String? endpointDownload String?
creator User @relation("UserVideoServices", fields: [createdBy], references: [id])
createdBy String @db.Uuid createdBy String @db.Uuid
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
videos Video[] @relation("VideoServices")
user_preferences UserPreference[] @relation("UserServiceDefault") user_preferences UserPreference[] @relation("UserServiceDefault")
creator User @relation("UserVideoServices", fields: [createdBy], references: [id])
videos Video[] @relation("VideoServices")
@@map("video_services") @@map("video_services")
} }
@ -249,86 +238,87 @@ model User {
id String @id @db.Uuid id String @id @db.Uuid
name String @db.VarChar(255) name String @db.VarChar(255)
username String @unique @db.VarChar(255) username String @unique @db.VarChar(255)
email String @unique @db.Text email String @unique
password String @db.Text password String
birthDate DateTime? @db.Date birthDate DateTime? @db.Date
gender UserGender? gender UserGender?
phoneCC Int? phoneCC Int?
phoneNumber Int? phoneNumber Int?
assignedRoles UserRoleAssignment[] bioProfile String?
bioProfile String? @db.Text avatar String?
avatar String? @db.Text commentBackground String?
commentBackground String? @db.Text
provider String? @db.VarChar(255) provider String? @db.VarChar(255)
providerId String? @unique @db.VarChar(255) providerId String? @unique @db.VarChar(255)
providerToken String? @db.Text providerToken String?
providerPayload Json? @db.Json providerPayload Json? @db.Json
preference UserPreference?
verifiedAt DateTime? verifiedAt DateTime?
disabledAt DateTime? disabledAt DateTime?
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt 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") 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") 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") liked_comments CommentLike[] @relation("UserCommentLikes")
reported_comments CommentReport[] @relation("UserReportComments")
approved_comments CommentReport[] @relation("ApprovedReportComments") approved_comments CommentReport[] @relation("ApprovedReportComments")
create_languages Language[] @relation("UserCreateLanguages") reported_comments CommentReport[] @relation("UserReportComments")
comments Comment[] @relation("UserComments")
user_create_email EmailSystemAccount[] @relation("UserCreateSystemAccount") user_create_email EmailSystemAccount[] @relation("UserCreateSystemAccount")
user_emails EmailSystemHistory[] @relation("UserEmails") user_emails EmailSystemHistory[] @relation("UserEmails")
episode_likes EpisodeLike[] @relation("UserEpisodeLikes")
episodes Episode[] @relation("UserEpisodes")
genres Genre[] @relation("UserCreatedGenres")
user_hero_banner HeroBanner[] @relation("UserHeroBanner") user_hero_banner HeroBanner[] @relation("UserHeroBanner")
sys_notifications SystemNotification[] @relation("UserCreatorSystemNotifications") lang_va_char LangVAChar[] @relation("UserCreatedLangVAChar")
create_languages Language[] @relation("UserCreateLanguages")
media_approveds MediaLog[] @relation("UserApprovedMedias")
media_proposeds MediaLog[] @relation("UserProposedMedias")
medias Media[] @relation("UserUploadedMedias")
media_reviews MediaReview[] @relation("UserMediaReviews")
studios Studio[] @relation("UserCreatedStudios")
sys_logs SystemLog[] @relation("UserSystemLogs") sys_logs SystemLog[] @relation("UserSystemLogs")
sys_notifications SystemNotification[] @relation("UserCreatorSystemNotifications")
logs UserLog[] @relation("UserLogs")
notifications UserNotification[] @relation("UserNotifications")
preference UserPreference?
assignedRoles UserRoleAssignment[]
create_roles UserRole[] @relation("UserCreateRoles")
sessions UserSession[] @relation("UserSession")
video_services VideoService[] @relation("UserVideoServices")
videos Video[] @relation("UserVideos")
voice_actor VoiceActor[] @relation("UserCreatedVoiceActors")
watch_histories WatchHistory[] @relation("UserWatchHistories")
allowed_collections Collection[] @relation("UserSelectedSharingCollention")
@@map("users") @@map("users")
} }
model UserPreference { model UserPreference {
id String @id @db.Uuid id String @id @db.Uuid
user User @relation(fields: [userId], references: [id])
userId String @unique @db.Uuid userId String @unique @db.Uuid
lang Language? @relation("UserPreferenceLang", fields: [langPreference], references: [code])
langPreference String? langPreference String?
adultFiltering AdultFiltering @default(hide) adultFiltering AdultFiltering @default(hide)
adultAlert AdultAlert @default(show) adultAlert AdultAlert @default(show)
videoQuality VideoQuality @default(Q1080) videoQuality VideoQuality @default(Q1080)
serviceDefault VideoService? @relation("UserServiceDefault", fields: [serviceDefaultId], references: [id])
serviceDefaultId String? @db.Uuid serviceDefaultId String? @db.Uuid
hideContries Country[] hideContries Country[]
favoriteGenres Genre[] @relation("UserFavoriteGenres")
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
lang Language? @relation("UserPreferenceLang", fields: [langPreference], references: [code])
serviceDefault VideoService? @relation("UserServiceDefault", fields: [serviceDefaultId], references: [id])
user User @relation(fields: [userId], references: [id])
favoriteGenres Genre[] @relation("UserFavoriteGenres")
@@map("user_preferences") @@map("user_preferences")
} }
model UserRole { model UserRole {
id String @id @db.Uuid id String @id @db.Uuid
name String @db.VarChar(255) @unique name String @unique @db.VarChar(255)
description String? @db.Text description String?
primaryColor String? @db.VarChar(10) primaryColor String? @db.VarChar(10)
secondaryColor String? @db.VarChar(10) secondaryColor String? @db.VarChar(10)
pictureImage String? @db.Text pictureImage String?
badgeImage String? @db.Text badgeImage String?
isSuperadmin Boolean @default(false) isSuperadmin Boolean @default(false)
canEditMedia Boolean @default(false) canEditMedia Boolean @default(false)
canManageMedia Boolean @default(false) canManageMedia Boolean @default(false)
@ -340,24 +330,22 @@ model UserRole {
canManageUser Boolean @default(false) canManageUser Boolean @default(false)
canEditSystem Boolean @default(false) canEditSystem Boolean @default(false)
canManageSystem Boolean @default(false) canManageSystem Boolean @default(false)
creator User @relation("UserCreateRoles", fields: [createdBy], references: [id])
createdBy String @db.Uuid createdBy String @db.Uuid
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
assignedUser UserRoleAssignment[] assignedUser UserRoleAssignment[]
creator User @relation("UserCreateRoles", fields: [createdBy], references: [id])
@@map("user_roles") @@map("user_roles")
} }
model UserRoleAssignment { model UserRoleAssignment {
user User @relation(fields: [userId], references: [id])
userId String @db.Uuid userId String @db.Uuid
role UserRole @relation(fields: [roleId], references: [id])
roleId String @db.Uuid roleId String @db.Uuid
assignmentAt DateTime @default(now()) assignmentAt DateTime @default(now())
role UserRole @relation(fields: [roleId], references: [id])
user User @relation(fields: [userId], references: [id])
@@id([userId, roleId]) @@id([userId, roleId])
@@map("user_role_assignments") @@map("user_role_assignments")
@ -366,23 +354,23 @@ model UserRoleAssignment {
model UserNotification { model UserNotification {
id String @id @db.Uuid id String @id @db.Uuid
title String @db.VarChar(255) title String @db.VarChar(255)
content String @db.Text content String
picture String @db.Text picture String
state UserNotificationState state UserNotificationState
ctaLink String @db.Text ctaLink String
user User @relation("UserNotifications", fields: [userId], references: [id])
userId String @db.Uuid userId String @db.Uuid
isReaded Boolean @default(false) isReaded Boolean @default(false)
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
user User @relation("UserNotifications", fields: [userId], references: [id])
@@map("user_notifications") @@map("user_notifications")
} }
model UserSession { model UserSession {
id String @id @db.Uuid id String @id @db.Uuid
isAuthenticated Boolean @default(false) isAuthenticated Boolean @default(false)
user User @relation("UserSession", fields: [userId], references: [id])
userId String @db.Uuid userId String @db.Uuid
deviceType String @db.VarChar(255) deviceType String @db.VarChar(255)
deviceOs String @db.VarChar(255) deviceOs String @db.VarChar(255)
@ -394,9 +382,9 @@ model UserSession {
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
logs UserLog[] @relation("UserSessionLogs")
episode_likes EpisodeLike[] @relation("SessionEpisodeLikes") episode_likes EpisodeLike[] @relation("SessionEpisodeLikes")
logs UserLog[] @relation("UserSessionLogs")
user User @relation("UserSession", fields: [userId], references: [id])
watch_histories WatchHistory[] @relation("SessionWatchHistories") watch_histories WatchHistory[] @relation("SessionWatchHistories")
@@index([userId, isAuthenticated, deletedAt]) @@index([userId, isAuthenticated, deletedAt])
@ -406,122 +394,138 @@ model UserSession {
model UserLog { model UserLog {
id String @id @db.Uuid id String @id @db.Uuid
title String @db.VarChar(255) title String @db.VarChar(255)
notes String @db.Text notes String
user User @relation("UserLogs", fields: [userId], references: [id])
userId String @db.Uuid userId String @db.Uuid
session UserSession @relation("UserSessionLogs", fields: [sessionId], references: [id])
sessionId String @db.Uuid sessionId String @db.Uuid
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
session UserSession @relation("UserSessionLogs", fields: [sessionId], references: [id])
user User @relation("UserLogs", fields: [userId], references: [id])
@@map("user_logs") @@map("user_logs")
} }
model Collection { model Collection {
id String @id @db.Uuid id String @id @db.Uuid
name String @db.VarChar(115) name String @db.VarChar(115)
slug String @db.VarChar(115)
medias Media[] @relation("MediaCollections")
owner User @relation("UserCollections", fields: [ownerId], references: [id])
ownerId String @db.Uuid ownerId String @db.Uuid
accessStatus AccessStatus @default(private) accessStatus AccessStatus @default(private)
password String? @db.VarChar(255) password String? @db.VarChar(255)
usersAllowed User[] @relation("UserSelectedSharingCollention")
accessScope AccessScope @default(viewer) accessScope AccessScope @default(viewer)
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
slug String @db.VarChar(115)
media_saved CollectionMedia[] @relation("CollectionMedia")
owner User @relation("UserCollections", fields: [ownerId], references: [id])
usersAllowed User[] @relation("UserSelectedSharingCollention")
@@unique([slug, ownerId]) @@unique([slug, ownerId])
@@map("collections") @@map("collections")
} }
model CollectionMedia {
id String @id @db.Uuid
collectionId String @db.Uuid
mediaId String @db.Uuid
savedAt DateTime @default(now())
collection Collection @relation("CollectionMedia", fields: [collectionId], references: [id])
media Media @relation("CollectionMedia", fields: [mediaId], references: [id])
@@unique([collectionId, mediaId])
}
model WatchHistory { model WatchHistory {
id String @id @db.Uuid id String @id @db.Uuid
episode Episode @relation("EpisodeWatchHistories", fields: [id], references: [id])
episodeId String @db.Uuid episodeId String @db.Uuid
user User @relation("UserWatchHistories", fields: [userId], references: [id])
userId String @db.Uuid userId String @db.Uuid
session UserSession @relation("SessionWatchHistories", fields: [sessionId], references: [id])
sessionId String @db.Uuid sessionId String @db.Uuid
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
episode Episode @relation("EpisodeWatchHistories", fields: [id], references: [id])
session UserSession @relation("SessionWatchHistories", fields: [sessionId], references: [id])
user User @relation("UserWatchHistories", fields: [userId], references: [id])
@@map("watch_histories") @@map("watch_histories")
} }
model MediaReview { model MediaReview {
id String @id @db.Uuid id String @id @db.Uuid
media Media @relation("MediaReviews", fields: [mediaId], references:[id])
mediaId String @db.Uuid mediaId String @db.Uuid
rating Int rating Int
title String @db.VarChar(255) title String @db.VarChar(255)
text String @db.Text text String
reaction MediaReviewReaction reaction MediaReviewReaction
creator User @relation("UserMediaReviews", fields: [createdBy], references: [id])
createdBy String @db.Uuid createdBy String @db.Uuid
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
creator User @relation("UserMediaReviews", fields: [createdBy], references: [id])
media Media @relation("MediaReviews", fields: [mediaId], references: [id])
@@map("movie_reviews") @@map("movie_reviews")
} }
model Comment { model Comment {
id String @id @db.Uuid id String @id @db.Uuid
episode Episode @relation("EpisodeComments", fields: [episodeId], references: [id])
episodeId String @db.Uuid episodeId String @db.Uuid
text String @db.Text text String
isParent Boolean @default(false) isParent Boolean @default(false)
parent Comment? @relation("ParentReplyComments", fields: [parentId], references: [id])
parentId String? @db.Uuid parentId String? @db.Uuid
user User @relation("UserComments", fields: [userId], references: [id])
userId String @db.Uuid userId String @db.Uuid
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
replies Comment[] @relation("ParentReplyComments")
likes CommentLike[] @relation("CommentLikes") likes CommentLike[] @relation("CommentLikes")
episode Episode @relation("EpisodeComments", fields: [episodeId], references: [id])
parent Comment? @relation("ParentReplyComments", fields: [parentId], references: [id])
replies Comment[] @relation("ParentReplyComments")
user User @relation("UserComments", fields: [userId], references: [id])
@@map("comments") @@map("comments")
} }
model CommentLike { model CommentLike {
id String @id @db.Uuid id String @id @db.Uuid
comment Comment @relation("CommentLikes", fields: [commentId], references: [id])
commentId String @db.Uuid commentId String @db.Uuid
user User @relation("UserCommentLikes", fields: [userLiked], references: [id])
userLiked String @db.Uuid userLiked String @db.Uuid
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
comment Comment @relation("CommentLikes", fields: [commentId], references: [id])
user User @relation("UserCommentLikes", fields: [userLiked], references: [id])
@@map("comment_likes") @@map("comment_likes")
} }
model CommentReport { model CommentReport {
id String @id @db.Uuid id String @id @db.Uuid
reporter User @relation("UserReportComments", fields: [userReporter], references: [id])
userReporter String @db.Uuid userReporter String @db.Uuid
commentReported String @db.Uuid commentReported String @db.Uuid
isSupervisorReport Boolean @default(false) isSupervisorReport Boolean @default(false)
reason ReportReason reason ReportReason
status ReportStatus status ReportStatus
description String @db.VarChar(255) description String @db.VarChar(255)
approver User? @relation("ApprovedReportComments", fields: [approvedBy], references: [id])
approvedBy String? @db.Uuid approvedBy String? @db.Uuid
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
approver User? @relation("ApprovedReportComments", fields: [approvedBy], references: [id])
reporter User @relation("UserReportComments", fields: [userReporter], references: [id])
@@map("comment_reports") @@map("comment_reports")
} }
model Language { model Language {
id String @id @db.Uuid id String @id @db.Uuid
name String @db.VarChar(255) name String @db.VarChar(255)
code String @db.VarChar(5) @unique code String @unique @db.VarChar(5)
countryFlag String @db.VarChar(10) countryFlag String @db.VarChar(10)
fileLocation String @db.Text fileLocation String
creator User @relation("UserCreateLanguages", fields: [craetedBy], references: [id])
craetedBy String @db.Uuid craetedBy String @db.Uuid
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
creator User @relation("UserCreateLanguages", fields: [craetedBy], references: [id])
user_used UserPreference[] @relation("UserPreferenceLang") user_used UserPreference[] @relation("UserPreferenceLang")
@@map("languages") @@map("languages")
} }
@ -535,52 +539,56 @@ model EmailSystemAccount {
username String @unique @db.VarChar(255) username String @unique @db.VarChar(255)
password String @db.VarChar(255) password String @db.VarChar(255)
purpose EmailPorpose purpose EmailPorpose
creator User @relation("UserCreateSystemAccount", fields: [createdBy], references: [id])
createdBy String @db.Uuid createdBy String @db.Uuid
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
creator User @relation("UserCreateSystemAccount", fields: [createdBy], references: [id])
@@map("email_system_accounts") @@map("email_system_accounts")
} }
model EmailSystemHistory { model EmailSystemHistory {
id String @id @db.Uuid id String @id @db.Uuid
purpose EmailPorpose purpose EmailPorpose
fromEmail String @db.Text fromEmail String
toEmail String @db.Text toEmail String
user User @relation("UserEmails", fields: [userRelated], references: [id])
userRelated String @db.Uuid userRelated String @db.Uuid
title String @db.VarChar(255) title String @db.VarChar(255)
htmlContent String @db.Text htmlContent String
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
user User @relation("UserEmails", fields: [userRelated], references: [id])
@@map("email_system_histories") @@map("email_system_histories")
} }
model HeroBanner { model HeroBanner {
id String @id @db.Uuid id String @id @db.Uuid
orderPriority Int? @unique orderPriority Int? @unique
mediaId String @db.Uuid imageUrl String?
media Media @relation("MediaBannerPromotion", fields: [mediaId], references: [id])
imageUrl String? @db.Text
startDate DateTime startDate DateTime
endDate DateTime endDate DateTime
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
creatorId String @db.Uuid creatorId String @db.Uuid
mediaId String @db.Uuid
createdBy User @relation("UserHeroBanner", fields: [creatorId], references: [id]) createdBy User @relation("UserHeroBanner", fields: [creatorId], references: [id])
media Media @relation("MediaBannerPromotion", fields: [mediaId], references: [id])
@@map("hero_banner") @@map("hero_banner")
} }
model SystemPreference { model SystemPreference {
id String @id @db.Uuid id String @id @db.Uuid
key String @db.VarChar(225) @unique key String @unique @db.VarChar(225)
value String @db.VarChar(225) value String @db.VarChar(225)
description String @db.Text description String
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
@@map("system_preferences") @@map("system_preferences")
} }
@ -588,35 +596,30 @@ model SystemNotification {
id String @id @db.Uuid id String @id @db.Uuid
type TypeSystemNotification type TypeSystemNotification
componentName String? @db.VarChar(255) componentName String? @db.VarChar(255)
popupImage String? @db.Text popupImage String?
titleToast String? @db.VarChar(255) titleToast String? @db.VarChar(255)
contentToast String? @db.Text contentToast String?
creator User @relation("UserCreatorSystemNotifications", fields: [createdBy], references: [id])
createdBy String @db.Uuid createdBy String @db.Uuid
deletedAt DateTime? deletedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
creator User @relation("UserCreatorSystemNotifications", fields: [createdBy], references: [id])
@@map("system_notifications") @@map("system_notifications")
} }
model SystemLog { model SystemLog {
id String @id @db.Uuid id String @id @db.Uuid
title String @db.VarChar(255) title String @db.VarChar(255)
notes String @db.Text notes String
user User? @relation("UserSystemLogs", fields: [relatedUser], references: [id])
relatedUser String? @db.Uuid relatedUser String? @db.Uuid
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
user User? @relation("UserSystemLogs", fields: [relatedUser], references: [id])
@@map("system_logs") @@map("system_logs")
} }
//// Prisma Enum Values ////
// Media Enum
enum MediaType { enum MediaType {
TV TV
ONA ONA
@ -625,6 +628,7 @@ enum MediaType {
Special Special
Music Music
} }
enum Country { enum Country {
JP @map("Japanese") JP @map("Japanese")
EN @map("English") EN @map("English")
@ -632,35 +636,40 @@ enum Country {
KR @map("Korea") KR @map("Korea")
} }
// Character Enum enum Season {
winter
spring
summer
fall
}
enum CharacterRole { enum CharacterRole {
Main Main
Supporting Supporting
} }
// MediaLog Enum
enum MediaOperation { enum MediaOperation {
create create
update update
delete delete
} }
// User Enum
enum UserGender { enum UserGender {
male male
female female
} }
// UserPreference Enum
enum AdultFiltering { enum AdultFiltering {
hide hide
show show
explicit explicit
} }
enum AdultAlert { enum AdultAlert {
hide hide
show show
} }
enum VideoQuality { enum VideoQuality {
Q2160 Q2160
Q1440 Q1440
@ -672,19 +681,18 @@ enum VideoQuality {
Q144 Q144
} }
// userNotification Enum
enum UserNotificationState { enum UserNotificationState {
info info
warning warning
danger danger
} }
// CommentReport Enum
enum ReportStatus { enum ReportStatus {
pending pending
resolved resolved
rejected rejected
} }
enum ReportReason { enum ReportReason {
sexualize sexualize
violent violent
@ -696,19 +704,18 @@ enum ReportReason {
other other
} }
// Collection Enum
enum AccessStatus { enum AccessStatus {
private private
selected selected
protected protected
public public
} }
enum AccessScope { enum AccessScope {
viewer viewer
editor editor
} }
// MediaReview Enum
enum MediaReviewReaction { enum MediaReviewReaction {
angry angry
sad sad
@ -720,7 +727,6 @@ enum MediaReviewReaction {
disappointed disappointed
} }
// EmailSystemHistory Enum
enum EmailPorpose { enum EmailPorpose {
forgot_password forgot_password
account_activation account_activation
@ -728,7 +734,6 @@ enum EmailPorpose {
subscribtion subscribtion
} }
// systemNotification Enum
enum TypeSystemNotification { enum TypeSystemNotification {
component component
popup popup

View File

@ -1,10 +0,0 @@
import { Context, Static } from "elysia";
import { returnWriteResponse } from "../../../helpers/callback/httpResponse";
import { addItemToCollectionSchema } from "../schemas/addItemToCollection.schema";
export const addItemToCollectionController = async (ctx: {
set: Context["set"];
headers: Static<typeof addItemToCollectionSchema.headers>;
}) => {
return returnWriteResponse(ctx.set, 200, "Item added to collection successfully" + ctx.headers.cookie);
};

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

@ -1,9 +1,9 @@
import Elysia from "elysia"; import Elysia from "elysia";
import { addItemToCollectionController } from "./controllers/addItemToCollection.controller"; import { addItemToCollectionBySytemController } from "./controllers/addItemToCollectionBySytem.controller";
import { addItemToCollectionSchema } from "./schemas/addItemToCollection.schema"; 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( export const collectionModule = new Elysia({ prefix: "/collections", tags: ["Collections"] })
"/:name", .post("/sys", addItemToCollectionBySytemController, addItemToCollectionBySytemSchema)
addItemToCollectionController, .delete("/sys", removeItemFromCollectionBySytemController, removeItemFromCollectionBySytemSchema);
addItemToCollectionSchema,
);

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

@ -1,14 +1,12 @@
import { t } from "elysia"; import { t } from "elysia";
import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema"; import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema";
export const addItemToCollectionSchema = { export const addItemToCollectionBySytemSchema = {
headers: t.Object({ headers: t.Object({
cookie: t.String({ description: "Authentication token in cookie format, e.g., auth_token=your_jwt_token;" }), cookie: t.String({ description: "Authentication token in cookie format, e.g., auth_token=your_jwt_token;" }),
}), }),
params: t.Object({
name: t.String({ description: "Name of the collection to which the item will be added" }),
}),
body: t.Object({ 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"] }), itemId: t.String({ description: "ID of the item to be added to the collection", examples: ["12345"] }),
}), }),
detail: { detail: {

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 { AppError } from "../../../../helpers/error/instances/app";
import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder"; 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 { GetEpisodeDetailsParams } from "../../controllers/getEpisodeDetails.controller";
import { getEpisodeDetailsRepository } from "../../repositories/GET/getEpisodeDetails.repository"; import { getEpisodeDetailsRepository } from "../../repositories/GET/getEpisodeDetails.repository";
export const getEpisodeDetailsService = async ( export const getEpisodeDetailsService = async (params: GetEpisodeDetailsParams) => {
params: GetEpisodeDetailsParams,
) => {
try { try {
if (!params.mediaSlug || !params.episode) if (!params.mediaSlug || !params.episode) throw new AppError(400, "Media slug and episode are required.");
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."); if (!mediaId?.id) throw new AppError(404, "Media not found.");
const result = await getEpisodeDetailsRepository({ 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 { mainErrorHandler } from "../../../helpers/error/handler";
import { returnReadResponse } from "../../../helpers/callback/httpResponse"; import { returnReadResponse } from "../../../helpers/callback/httpResponse";
import { getActiveHeroBannerService } from "../services/getActiveHeroBanner.service"; 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 { try {
const response = await getActiveHeroBannerService(); const response = await getActiveHeroBannerService({ cookie: ctx.header?.cookie });
return returnReadResponse( return returnReadResponse(ctx.set, 200, "Active hero banners fetched successfully", response);
ctx.set,
200,
"Active hero banners fetched successfully",
response,
);
} catch (error) { } catch (error) {
return mainErrorHandler(ctx.set, error); return mainErrorHandler(ctx.set, error);
} }

View File

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

View File

@ -1,27 +1,20 @@
import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder"; import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder";
import { MediaEpisodeInfoResponse } from "../../types/mediaEpisodeInfo.type"; import { MediaEpisodeInfoResponse } from "../../types/mediaEpisodeInfo.type";
import { getMediaByMalIdRepository } from "../../../media/repositories/GET/getMediaByMalId.repository";
import { AppError } from "../../../../helpers/error/instances/app"; import { AppError } from "../../../../helpers/error/instances/app";
import { SystemAccountId } from "../../../../config/account/system"; import { SystemAccountId } from "../../../../config/account/system";
import { bulkInsertEpisodesRepository } from "../../repositories/bulkInsertEpisodes.repository"; import { bulkInsertEpisodesRepository } from "../../repositories/bulkInsertEpisodes.repository";
import { getEpisodeReferenceAPI } from "../../../../config/apis/jikan/episode.reference"; import { getEpisodeReferenceAPI } from "../../../../config/apis/jikan/episode.reference";
import { selectMediaByMalIdRepository } from "../../../media/repositories/SELECT/selectMediaByMalId.repository";
export const bulkInsertEpisodeService = async ( export const bulkInsertEpisodeService = async (mal_id: number, page: number = 1) => {
mal_id: number,
page: number = 1,
) => {
try { try {
const episodeAPI = getEpisodeReferenceAPI(mal_id); const episodeAPI = getEpisodeReferenceAPI(mal_id);
const episodeData: MediaEpisodeInfoResponse = await fetch( const episodeData: MediaEpisodeInfoResponse = await fetch(
`${episodeAPI.baseURL}${episodeAPI.getEpisodeList}?page=${page}`, `${episodeAPI.baseURL}${episodeAPI.getEpisodeList}?page=${page}`,
).then((res) => res.json()); ).then((res) => res.json());
const mediaData = await getMediaByMalIdRepository(mal_id); const mediaData = await selectMediaByMalIdRepository(mal_id);
if (!mediaData) if (!mediaData) throw new AppError(404, `Media with Mal ID ${mal_id} not found in database`);
throw new AppError(
404,
`Media with Mal ID ${mal_id} not found in database`,
);
const insertedEpisodeData = []; const insertedEpisodeData = [];
episodeData.data.forEach(async (episode) => { 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 { mainErrorHandler } from "../../../helpers/error/handler";
import { getAllMediaService } from "../services/http/getAllMedia.service"; import { getAllMediaService } from "../services/http/getAllMedia.service";
import { returnReadResponse } from "../../../helpers/callback/httpResponse"; import { returnReadResponse } from "../../../helpers/callback/httpResponse";
import { getAllMediaSchema } from "../schemas/getAllMedia.schema";
export const getAllMediaController = async ( export const getAllMediaController = async (ctx: {
ctx: Context & { query: { page: string } }, set: Context["set"];
) => { query: Static<typeof getAllMediaSchema.query>;
}) => {
try { try {
const mediaData = await getAllMediaService(ctx.query.page); const mediaData = await getAllMediaService(ctx.query.page);
return returnReadResponse( return returnReadResponse(ctx.set, 200, "Media fetched successfully", mediaData);
ctx.set,
200,
"Media fetched successfully",
mediaData,
);
} catch (error) { } catch (error) {
return mainErrorHandler(ctx.set, 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 Elysia from "elysia";
import { getAllMediaController } from "./controllers/getAllMedia.controller"; 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( export const mediaModule = new Elysia({ prefix: "/media", tags: ["Media"] })
"/", .get("/", getAllMediaController, getAllMediaSchema)
getAllMediaController, .get("/:slug", getMediaBySlugController, getMediaBySlugSchema);
);

View File

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

View File

@ -1,7 +1,7 @@
import { AppError } from "../../../../helpers/error/instances/app"; import { AppError } from "../../../../helpers/error/instances/app";
import { mediaModel } from "../../model"; import { mediaModel } from "../../model";
export const getMediaByMalIdRepository = async (mal_id: number) => { export const selectMediaByMalIdRepository = async (mal_id: number) => {
try { try {
return await mediaModel.findUnique({ return await mediaModel.findUnique({
where: { malId: mal_id }, 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 { AppError } from "../../../../helpers/error/instances/app";
import { mediaModel } from "../../model"; import { mediaModel } from "../../model";
export const getMediaIdFromSlugRepository = async (slug: string) => { export const selectMediaIdFromSlugRepository = async (slug: string) => {
try { try {
return await mediaModel.findUnique({ return await mediaModel.findUnique({
where: { slug }, 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 { 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) => { export const getAllMediaService = async (pagination: string) => {
try { try {
const page = const page = /^\d+$/.test(pagination) && Number(pagination) > 0 ? Number(pagination) : 1;
/^\d+$/.test(pagination) && Number(pagination) > 0
? Number(pagination)
: 1;
return getAllMediaRepository(page); return selectAllMediaRepository(page);
} catch (error) { } catch (error) {
ErrorForwarder(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);
}
};