Compare commits

..

1 Commits

Author SHA1 Message Date
a6200605f8 📝 docs: add schema documentation to OAuth provider routes 2026-02-25 12:00:00 +07:00
57 changed files with 1073 additions and 1827 deletions

View File

@ -28,11 +28,10 @@ Table medias {
deletedAt DateTime
createdAt DateTime [default: `now()`, not null]
updatedAt DateTime [default: `now()`, not null]
bannerPromotion hero_banner [not null]
logs media_logs [not null]
episodes episodes [not null]
collections collections [not null]
reviews movie_reviews [not null]
inCollections CollectionMedia [not null]
}
Table media_logs {
@ -369,7 +368,7 @@ Table user_logs {
Table collections {
id String [pk]
name String [not null]
slug String [not null]
medias medias [not null]
owner users [not null]
ownerId String [not null]
accessStatus AccessStatus [not null, default: 'private']
@ -379,24 +378,6 @@ Table collections {
deletedAt DateTime
createdAt DateTime [default: `now()`, not null]
updatedAt DateTime [default: `now()`, not null]
media_saved CollectionMedia [not null]
indexes {
(slug, ownerId) [unique]
}
}
Table CollectionMedia {
id String [pk]
collection collections [not null]
collectionId String [not null]
media medias [not null]
mediaId String [not null]
savedAt DateTime [default: `now()`, not null]
indexes {
(collectionId, mediaId) [unique]
}
}
Table watch_histories {
@ -515,8 +496,12 @@ Table email_system_histories {
Table hero_banner {
id String [pk]
orderPriority Int [unique]
mediaId String [not null]
media medias [not null]
isClickable Boolean [not null, default: false]
title String
tags String[] [not null]
description String
buttonContent String
buttonLink String
imageUrl String
startDate DateTime [not null]
endDate DateTime [not null]
@ -575,9 +560,9 @@ Table MediaCharacters {
mediasId String [ref: > medias.id]
}
Table CollectionMedia {
incollectionsId String [ref: > CollectionMedia.id]
media_savedId String [ref: > CollectionMedia.id]
Table MediaCollections {
collectionsId String [ref: > collections.id]
mediasId String [ref: > medias.id]
}
Table UserFavoriteGenres {
@ -765,10 +750,6 @@ Ref: user_logs.sessionId > user_sessions.id
Ref: collections.ownerId > users.id
Ref: CollectionMedia.collectionId > collections.id
Ref: CollectionMedia.mediaId > medias.id
Ref: watch_histories.id > episodes.id
Ref: watch_histories.userId > users.id
@ -799,8 +780,6 @@ Ref: email_system_accounts.createdBy > users.id
Ref: email_system_histories.userRelated > users.id
Ref: hero_banner.mediaId > medias.id
Ref: hero_banner.creatorId > users.id
Ref: system_notifications.createdBy > users.id

View File

@ -7,9 +7,6 @@ CREATE TYPE "MediaType" AS ENUM ('TV', 'ONA', 'OVA', 'Movie', 'Special', 'Music'
-- CreateEnum
CREATE TYPE "Country" AS ENUM ('Japanese', 'English', 'Indonesia', 'Korea');
-- CreateEnum
CREATE TYPE "Season" AS ENUM ('Winter', 'Spring', 'Summer', 'Fall');
-- CreateEnum
CREATE TYPE "CharacterRole" AS ENUM ('Main', 'Supporting');
@ -75,8 +72,6 @@ CREATE TABLE "medias" (
"deletedAt" TIMESTAMP(3),
"createdAt" 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")
);
@ -211,8 +206,7 @@ CREATE TABLE "videos" (
"id" UUID NOT NULL,
"episodeId" UUID NOT NULL,
"serviceId" UUID NOT NULL,
"videoCode" VARCHAR(255) NOT NULL,
"thumbnailCode" TEXT,
"code" VARCHAR(255) NOT NULL,
"pendingUpload" BOOLEAN NOT NULL DEFAULT true,
"uploadedBy" UUID NOT NULL,
"deletedAt" TIMESTAMP(3),
@ -372,7 +366,7 @@ CREATE TABLE "user_logs" (
-- CreateTable
CREATE TABLE "collections" (
"id" UUID NOT NULL,
"name" VARCHAR(115) NOT NULL,
"name" VARCHAR(255) NOT NULL,
"ownerId" UUID NOT NULL,
"accessStatus" "AccessStatus" NOT NULL DEFAULT 'private',
"password" VARCHAR(255),
@ -380,21 +374,10 @@ CREATE TABLE "collections" (
"deletedAt" TIMESTAMP(3),
"createdAt" 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")
);
-- 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
CREATE TABLE "watch_histories" (
"id" UUID NOT NULL,
@ -514,21 +497,6 @@ CREATE TABLE "email_system_histories" (
CONSTRAINT "email_system_histories_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "hero_banner" (
"id" UUID NOT NULL,
"orderPriority" INTEGER,
"imageUrl" TEXT,
"startDate" TIMESTAMP(3) NOT NULL,
"endDate" TIMESTAMP(3) NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"creatorId" UUID NOT NULL,
"mediaId" UUID NOT NULL,
CONSTRAINT "hero_banner_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "system_preferences" (
"id" UUID NOT NULL,
@ -602,6 +570,14 @@ CREATE TABLE "_MediaCharacters" (
CONSTRAINT "_MediaCharacters_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateTable
CREATE TABLE "_MediaCollections" (
"A" UUID NOT NULL,
"B" UUID NOT NULL,
CONSTRAINT "_MediaCollections_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateTable
CREATE TABLE "_UserSelectedSharingCollention" (
"A" UUID NOT NULL,
@ -653,7 +629,7 @@ CREATE UNIQUE INDEX "lang_va_char_language_vaId_charId_key" ON "lang_va_char"("l
CREATE UNIQUE INDEX "episodes_mediaId_episode_key" ON "episodes"("mediaId", "episode");
-- CreateIndex
CREATE UNIQUE INDEX "videos_serviceId_videoCode_key" ON "videos"("serviceId", "videoCode");
CREATE UNIQUE INDEX "videos_serviceId_code_key" ON "videos"("serviceId", "code");
-- CreateIndex
CREATE UNIQUE INDEX "video_services_name_key" ON "video_services"("name");
@ -676,12 +652,6 @@ CREATE UNIQUE INDEX "user_roles_name_key" ON "user_roles"("name");
-- CreateIndex
CREATE INDEX "user_sessions_userId_isAuthenticated_deletedAt_idx" ON "user_sessions"("userId", "isAuthenticated", "deletedAt");
-- CreateIndex
CREATE UNIQUE INDEX "collections_slug_ownerId_key" ON "collections"("slug", "ownerId");
-- CreateIndex
CREATE UNIQUE INDEX "CollectionMedia_collectionId_mediaId_key" ON "CollectionMedia"("collectionId", "mediaId");
-- CreateIndex
CREATE UNIQUE INDEX "languages_code_key" ON "languages"("code");
@ -694,12 +664,6 @@ CREATE UNIQUE INDEX "email_system_accounts_email_key" ON "email_system_accounts"
-- CreateIndex
CREATE UNIQUE INDEX "email_system_accounts_username_key" ON "email_system_accounts"("username");
-- CreateIndex
CREATE UNIQUE INDEX "hero_banner_orderPriority_key" ON "hero_banner"("orderPriority");
-- CreateIndex
CREATE UNIQUE INDEX "system_preferences_key_key" ON "system_preferences"("key");
-- CreateIndex
CREATE INDEX "_MediaStudios_B_index" ON "_MediaStudios"("B");
@ -712,21 +676,24 @@ CREATE INDEX "_UserFavoriteGenres_B_index" ON "_UserFavoriteGenres"("B");
-- CreateIndex
CREATE INDEX "_MediaCharacters_B_index" ON "_MediaCharacters"("B");
-- CreateIndex
CREATE INDEX "_MediaCollections_B_index" ON "_MediaCollections"("B");
-- CreateIndex
CREATE INDEX "_UserSelectedSharingCollention_B_index" ON "_UserSelectedSharingCollention"("B");
-- AddForeignKey
ALTER TABLE "medias" ADD CONSTRAINT "medias_uploadedBy_fkey" FOREIGN KEY ("uploadedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "media_logs" ADD CONSTRAINT "media_logs_proposedBy_fkey" FOREIGN KEY ("proposedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "media_logs" ADD CONSTRAINT "media_logs_approvedBy_fkey" FOREIGN KEY ("approvedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "media_logs" ADD CONSTRAINT "media_logs_mediaId_fkey" FOREIGN KEY ("mediaId") REFERENCES "medias"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "media_logs" ADD CONSTRAINT "media_logs_proposedBy_fkey" FOREIGN KEY ("proposedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "genres" ADD CONSTRAINT "genres_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@ -739,15 +706,15 @@ ALTER TABLE "characters" ADD CONSTRAINT "characters_creatorId_fkey" FOREIGN KEY
-- AddForeignKey
ALTER TABLE "voice_actors" ADD CONSTRAINT "voice_actors_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "lang_va_char" ADD CONSTRAINT "lang_va_char_vaId_fkey" FOREIGN KEY ("vaId") REFERENCES "voice_actors"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "lang_va_char" ADD CONSTRAINT "lang_va_char_charId_fkey" FOREIGN KEY ("charId") REFERENCES "characters"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "lang_va_char" ADD CONSTRAINT "lang_va_char_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "lang_va_char" ADD CONSTRAINT "lang_va_char_vaId_fkey" FOREIGN KEY ("vaId") REFERENCES "voice_actors"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "episodes" ADD CONSTRAINT "episodes_mediaId_fkey" FOREIGN KEY ("mediaId") REFERENCES "medias"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@ -755,13 +722,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;
-- 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
ALTER TABLE "episode_likes" ADD CONSTRAINT "episode_likes_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "user_sessions"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "episode_likes" ADD CONSTRAINT "episode_likes_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
ALTER TABLE "videos" ADD CONSTRAINT "videos_episodeId_fkey" FOREIGN KEY ("episodeId") REFERENCES "episodes"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@ -775,23 +742,23 @@ ALTER TABLE "videos" ADD CONSTRAINT "videos_uploadedBy_fkey" FOREIGN KEY ("uploa
-- AddForeignKey
ALTER TABLE "video_services" ADD CONSTRAINT "video_services_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_preferences" ADD CONSTRAINT "user_preferences_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_preferences" ADD CONSTRAINT "user_preferences_langPreference_fkey" FOREIGN KEY ("langPreference") REFERENCES "languages"("code") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_preferences" ADD CONSTRAINT "user_preferences_serviceDefaultId_fkey" FOREIGN KEY ("serviceDefaultId") REFERENCES "video_services"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_preferences" ADD CONSTRAINT "user_preferences_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_roles" ADD CONSTRAINT "user_roles_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_role_assignments" ADD CONSTRAINT "user_role_assignments_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
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
ALTER TABLE "user_notifications" ADD CONSTRAINT "user_notifications_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@ -800,35 +767,29 @@ 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;
-- 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
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
ALTER TABLE "collections" ADD CONSTRAINT "collections_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "CollectionMedia" ADD CONSTRAINT "CollectionMedia_collectionId_fkey" FOREIGN KEY ("collectionId") REFERENCES "collections"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
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
ALTER TABLE "watch_histories" ADD CONSTRAINT "watch_histories_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "user_sessions"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "watch_histories" ADD CONSTRAINT "watch_histories_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "movie_reviews" ADD CONSTRAINT "movie_reviews_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "watch_histories" ADD CONSTRAINT "watch_histories_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "user_sessions"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "movie_reviews" ADD CONSTRAINT "movie_reviews_mediaId_fkey" FOREIGN KEY ("mediaId") REFERENCES "medias"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "movie_reviews" ADD CONSTRAINT "movie_reviews_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "comments" ADD CONSTRAINT "comments_episodeId_fkey" FOREIGN KEY ("episodeId") REFERENCES "episodes"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@ -845,10 +806,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;
-- 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
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
ALTER TABLE "languages" ADD CONSTRAINT "languages_craetedBy_fkey" FOREIGN KEY ("craetedBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@ -859,12 +820,6 @@ ALTER TABLE "email_system_accounts" ADD CONSTRAINT "email_system_accounts_create
-- AddForeignKey
ALTER TABLE "email_system_histories" ADD CONSTRAINT "email_system_histories_userRelated_fkey" FOREIGN KEY ("userRelated") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "hero_banner" ADD CONSTRAINT "hero_banner_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "hero_banner" ADD CONSTRAINT "hero_banner_mediaId_fkey" FOREIGN KEY ("mediaId") REFERENCES "medias"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "system_notifications" ADD CONSTRAINT "system_notifications_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@ -895,6 +850,12 @@ ALTER TABLE "_MediaCharacters" ADD CONSTRAINT "_MediaCharacters_A_fkey" FOREIGN
-- AddForeignKey
ALTER TABLE "_MediaCharacters" ADD CONSTRAINT "_MediaCharacters_B_fkey" FOREIGN KEY ("B") REFERENCES "medias"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_MediaCollections" ADD CONSTRAINT "_MediaCollections_A_fkey" FOREIGN KEY ("A") REFERENCES "collections"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_MediaCollections" ADD CONSTRAINT "_MediaCollections_B_fkey" FOREIGN KEY ("B") REFERENCES "medias"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_UserSelectedSharingCollention" ADD CONSTRAINT "_UserSelectedSharingCollention_A_fkey" FOREIGN KEY ("A") REFERENCES "collections"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -0,0 +1,17 @@
/*
Warnings:
- You are about to drop the column `code` on the `videos` table. All the data in the column will be lost.
- A unique constraint covering the columns `[serviceId,videoCode]` on the table `videos` will be added. If there are existing duplicate values, this will fail.
- Added the required column `videoCode` to the `videos` table without a default value. This is not possible if the table is not empty.
*/
-- DropIndex
DROP INDEX "videos_serviceId_code_key";
-- AlterTable
ALTER TABLE "videos" RENAME COLUMN "code" TO "videoCode";
-- CreateIndex
DROP INDEX IF EXISTS "videos_serviceId_code_key";
CREATE UNIQUE INDEX "videos_serviceId_videoCode_key" ON "videos"("serviceId", "videoCode");

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "videos" ADD COLUMN "thumbnailCode" TEXT;

View File

@ -0,0 +1,20 @@
-- CreateTable
CREATE TABLE "HeroBanner" (
"id" UUID NOT NULL,
"isClickable" BOOLEAN NOT NULL DEFAULT false,
"title" VARCHAR(225),
"description" TEXT,
"buttonContent" VARCHAR(100),
"buttonLink" TEXT,
"imageUrl" TEXT,
"startDate" TIMESTAMP(3) NOT NULL,
"endDate" TIMESTAMP(3) NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"creatorId" UUID NOT NULL,
CONSTRAINT "HeroBanner_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "HeroBanner" ADD CONSTRAINT "HeroBanner_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -0,0 +1,32 @@
/*
Warnings:
- You are about to drop the `HeroBanner` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropForeignKey
ALTER TABLE "HeroBanner" DROP CONSTRAINT "HeroBanner_creatorId_fkey";
-- DropTable
DROP TABLE "HeroBanner";
-- CreateTable
CREATE TABLE "hero_banner" (
"id" UUID NOT NULL,
"isClickable" BOOLEAN NOT NULL DEFAULT false,
"title" VARCHAR(225),
"description" TEXT,
"buttonContent" VARCHAR(100),
"buttonLink" TEXT,
"imageUrl" TEXT,
"startDate" TIMESTAMP(3) NOT NULL,
"endDate" TIMESTAMP(3) NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"creatorId" UUID NOT NULL,
CONSTRAINT "hero_banner_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "hero_banner" ADD CONSTRAINT "hero_banner_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -0,0 +1,11 @@
/*
Warnings:
- A unique constraint covering the columns `[order]` on the table `hero_banner` will be added. If there are existing duplicate values, this will fail.
*/
-- AlterTable
ALTER TABLE "hero_banner" ADD COLUMN "order" INTEGER;
-- CreateIndex
CREATE UNIQUE INDEX "hero_banner_order_key" ON "hero_banner"("order");

View File

@ -0,0 +1,16 @@
/*
Warnings:
- You are about to drop the column `order` on the `hero_banner` table. All the data in the column will be lost.
- A unique constraint covering the columns `[orderPriority]` on the table `hero_banner` will be added. If there are existing duplicate values, this will fail.
*/
-- DropIndex
DROP INDEX "hero_banner_order_key";
-- AlterTable
ALTER TABLE "hero_banner" DROP COLUMN "order",
ADD COLUMN "orderPriority" INTEGER;
-- CreateIndex
CREATE UNIQUE INDEX "hero_banner_orderPriority_key" ON "hero_banner"("orderPriority");

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "hero_banner" ADD COLUMN "tags" TEXT[];

View File

@ -0,0 +1,8 @@
/*
Warnings:
- A unique constraint covering the columns `[key]` on the table `system_preferences` will be added. If there are existing duplicate values, this will fail.
*/
-- CreateIndex
CREATE UNIQUE INDEX "system_preferences_key_key" ON "system_preferences"("key");

View File

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

View File

@ -1,3 +1,4 @@
import { Context } from "elysia";
import { UserHeaderInformation } from "./types";
export interface ClientInfoHeader {
@ -9,14 +10,25 @@ export interface ClientInfoHeader {
ip: string;
}
export const getUserHeaderInformation = (clientInfo: string): UserHeaderInformation => {
const clientInfoHeader = (JSON.parse(clientInfo) as ClientInfoHeader) ?? ("unknown" as string);
export const getUserHeaderInformation = (
ctx: Context,
): UserHeaderInformation => {
const clientInfoHeader =
(JSON.parse(
ctx.request.headers.get("x-client-info") as string,
) as ClientInfoHeader) ?? ("unknown" as string);
const userHeaderInformation = {
ip: clientInfoHeader.ip ?? "unknown",
deviceType: clientInfoHeader.deviceType ?? "unknown",
deviceOS: (clientInfoHeader.os ?? "unknown") + " " + (clientInfoHeader.osVersion ?? "unknown"),
browser: (clientInfoHeader.browser ?? "unknown") + " " + (clientInfoHeader.browserVersion ?? "unknown"),
deviceOS:
(clientInfoHeader.os ?? "unknown") +
" " +
(clientInfoHeader.osVersion ?? "unknown"),
browser:
(clientInfoHeader.browser ?? "unknown") +
" " +
(clientInfoHeader.browserVersion ?? "unknown"),
};
return userHeaderInformation;

View File

@ -1,17 +1,14 @@
import { Context, Static } from "elysia";
import { Context } from "elysia";
import { returnWriteResponse } from "../../../helpers/callback/httpResponse";
import { githubCallbackService } from "../services/http/githubCallback.service";
import { mainErrorHandler } from "../../../helpers/error/handler";
import { getUserHeaderInformation } from "../../../helpers/http/userHeader/getUserHeaderInformation";
import { githubCallbackSchema } from "../schemas/githubCallback.schema";
export const githubCallbackController = async (ctx: {
set: Context["set"];
query: Static<typeof githubCallbackSchema.query>;
headers: Static<typeof githubCallbackSchema.headers>;
}) => {
export const githubCallbackController = async (
ctx: Context & { query: { code: string; callbackURI: string } }
) => {
try {
const userHeaderInfo = getUserHeaderInformation(ctx.headers["x-client-info"]);
const userHeaderInfo = getUserHeaderInformation(ctx);
const authToken = await githubCallbackService(ctx.query, userHeaderInfo);
return returnWriteResponse(ctx.set, 200, "Authenticated successfully!", {

View File

@ -1,18 +1,21 @@
import { Context, Static } from "elysia";
import { Context } from "elysia";
import { returnReadResponse } from "../../../helpers/callback/httpResponse";
import { githubRequestService } from "../services/http/githubRequest.service";
import { mainErrorHandler } from "../../../helpers/error/handler";
import { githubRequestSchema } from "../schemas/githubRequest.schema";
export const githubRequestController = async (ctx: {
set: Context["set"];
query: Static<typeof githubRequestSchema.query>;
}) => {
export const githubRequestController = async (
ctx: Context & { query: { callback?: string } },
) => {
try {
const loginUrl = await githubRequestService(ctx.query.callback);
return returnReadResponse(ctx.set, 200, "GitHub login URL created successfully.", {
return returnReadResponse(
ctx.set,
200,
"Login URL generated successfully",
{
endpointUrl: loginUrl,
});
},
);
} catch (error) {
return mainErrorHandler(ctx.set, error);
}

View File

@ -1,20 +1,17 @@
import { Context, Static } from "elysia";
import { Context } from "elysia";
import { returnReadResponse } from "../../../helpers/callback/httpResponse";
import { mainErrorHandler } from "../../../helpers/error/handler";
import { googleCallbackService } from "../services/http/googleCallback.service";
import { getUserHeaderInformation } from "../../../helpers/http/userHeader/getUserHeaderInformation";
import { googleCallbackSchema } from "../schemas/googleCallback.schema";
export const googleCallbackController = async (ctx: {
set: Context["set"];
query: Static<typeof googleCallbackSchema.query>;
headers: Static<typeof googleCallbackSchema.headers>;
}) => {
export const googleCallbackController = async (
ctx: Context & { query: { code: string; state: string; callbackURI: string } }
) => {
try {
const userHeaderInfo = getUserHeaderInformation(ctx.headers["x-client-info"]);
const userHeaderInfo = getUserHeaderInformation(ctx);
const authToken = await googleCallbackService(ctx.query, userHeaderInfo);
return returnReadResponse(ctx.set, 200, "Authentication successful!", {
return returnReadResponse(ctx.set, 200, "Authenticated successfully!", {
authToken,
});
} catch (error) {

View File

@ -1,16 +1,14 @@
import { Context, Static } from "elysia";
import { Context } from "elysia";
import { mainErrorHandler } from "../../../helpers/error/handler";
import { googleRequestService } from "../services/http/googleRequest.service";
import { returnReadResponse } from "../../../helpers/callback/httpResponse";
import { googleRequestSchema } from "../schemas/googleRequest.schema";
export const googleRequestController = async (ctx: {
set: Context["set"];
query: Static<typeof googleRequestSchema.query>;
}) => {
export const googleRequestController = async (
ctx: Context & { query: { callback?: string } }
) => {
try {
const loginUrl = await googleRequestService(ctx.query.callback);
return returnReadResponse(ctx.set, 200, "Google login URL created successfully.", {
return returnReadResponse(ctx.set, 200, "Google login url created!", {
endpointUrl: loginUrl,
});
} catch (error) {

View File

@ -9,19 +9,13 @@ import { tokenValidationController } from "./controllers/tokenValidation.control
import { logoutController } from "./controllers/logout.controller";
import { tokenValidationSchema } from "./schemas/tokenValidation.schema";
import { getOauthProvidersSchema } from "./schemas/getOauthProviders.schema";
import { getCallbackProviderUrlSchema } from "./schemas/getCallbackProviderUrl.schema";
import { googleRequestSchema } from "./schemas/googleRequest.schema";
import { googleCallbackSchema } from "./schemas/googleCallback.schema";
import { githubRequestSchema } from "./schemas/githubRequest.schema";
import { githubCallbackSchema } from "./schemas/githubCallback.schema";
import { logoutSchema } from "./schemas/logout.schema";
export const authModule = new Elysia({ prefix: "/auth", tags: ["Authentication"] })
.post("/token/validate", tokenValidationController, tokenValidationSchema)
.get("/providers", getOauthProvidersController, getOauthProvidersSchema)
.get("/providers/:name/callback", getCallbackProviderUrlController, getCallbackProviderUrlSchema)
.get("/google", googleRequestController, googleRequestSchema)
.get("/google/callback", googleCallbackController, googleCallbackSchema)
.get("/github", githubRequestController, githubRequestSchema)
.get("/github/callback", githubCallbackController, githubCallbackSchema)
.post("/logout", logoutController, logoutSchema);
.get("/providers/:name/callback", getCallbackProviderUrlController)
.get("/github", githubRequestController)
.get("/github/callback", githubCallbackController)
.get("/google", googleRequestController)
.get("/google/callback", googleCallbackController)
.post("/logout", logoutController);

View File

@ -1,45 +0,0 @@
import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema";
export const getCallbackProviderUrlSchema = {
detail: {
summary: "Get the callback URL of oauth provider",
description:
"After users have successfully completed the authentication process on the OAuth provider page, they will be redirected to the callback page on the frontend. This endpoint aims to obtain the actual endpoint for each OAuth response handler.",
responses: {
200: {
description: "The callback URL on the provider has been found.",
content: {
"application/json": {
schema: {
type: "object",
properties: {
success: {
type: "boolean",
default: true,
},
status: {
type: "number",
default: 200,
},
message: {
type: "string",
default: "The callback URL on the provider has been found.",
},
data: {
type: "object",
properties: {
callback_url: {
type: "string",
description: "The callback URL on the provider.",
example: "auth/google/callback",
},
},
},
},
},
},
},
},
},
},
} satisfies AppRouteSchema;

View File

@ -1,3 +1,4 @@
import { success } from "zod";
import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema";
export const getOauthProvidersSchema = {

View File

@ -1,57 +0,0 @@
import { t } from "elysia";
import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema";
export const githubCallbackSchema = {
headers: t.Object({
"x-client-info": t.String({
examples: [
'{"os":"Windows","osVersion":"10","browser":"Chrome","browserVersion":"89.0.4389.82","deviceType":"Desktop","ip":"192.168.1.1"}',
],
}),
}),
query: t.Object({
code: t.String({ examples: ["4/0AY0e-xxxxxxxxx"] }),
callbackURI: t.String({ examples: ["https://example.com/auth/github/callback"] }),
}),
detail: {
summary: "GitHub OAuth callback endpoint",
description:
"Handles the callback from GitHub OAuth and processes the authentication response. This endpoint also processes the account provisioning if the user is logging in for the first time.",
responses: {
200: {
description: "Authentication successful",
content: {
"application/json": {
schema: {
type: "object",
properties: {
success: {
type: "boolean",
example: true,
},
status: {
type: "number",
example: 200,
},
message: {
type: "string",
example: "Authentication successful",
},
data: {
type: "object",
properties: {
authToken: {
type: "string",
description: "JWT token for authenticated user",
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
},
},
},
},
},
},
},
},
},
},
} satisfies AppRouteSchema;

View File

@ -1,54 +0,0 @@
import { t } from "elysia";
import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema";
export const githubRequestSchema = {
query: t.Object({
callback: t.Optional(
t.String({
description: "The callback URL to redirect after GitHub authentication. It should be URL-encoded if provided.",
}),
),
}),
detail: {
summary: "Initiate GitHub OAuth flow",
description:
"This endpoint initiates the GitHub OAuth flow by redirecting the user to GitHub's authentication page.",
responses: {
200: {
description: "GitHub login URL created successfully.",
content: {
"application/json": {
schema: {
type: "object",
properties: {
success: {
type: "boolean",
default: true,
},
status: {
type: "number",
default: 200,
},
message: {
type: "string",
default: "GitHub login URL created successfully.",
},
data: {
type: "object",
properties: {
endpointUrl: {
type: "string",
description: "The URL to redirect the user for GitHub authentication.",
example:
"https://github.com/login/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&response_type=code&scope=user:email",
},
},
},
},
},
},
},
},
},
},
} satisfies AppRouteSchema;

View File

@ -1,58 +0,0 @@
import { t } from "elysia";
import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema";
export const googleCallbackSchema = {
headers: t.Object({
"x-client-info": t.String({
examples: [
'{"os":"Windows","osVersion":"10","browser":"Chrome","browserVersion":"89.0.4389.82","deviceType":"Desktop","ip":"192.168.1.1"}',
],
}),
}),
query: t.Object({
code: t.String({ examples: ["4/0AY0e-xxxxxxxxx"] }),
state: t.String({ examples: ["random_state_string"] }),
callbackURI: t.String({ examples: ["https://example.com/auth/google/callback"] }),
}),
detail: {
summary: "Google OAuth callback endpoint",
description:
"Handles the callback from Google OAuth and processes the authentication response. This endpoint also processes the account provisioning if the user is logging in for the first time.",
responses: {
200: {
description: "Authentication successful",
content: {
"application/json": {
schema: {
type: "object",
properties: {
success: {
type: "boolean",
example: true,
},
status: {
type: "number",
example: 200,
},
message: {
type: "string",
example: "Authentication successful",
},
data: {
type: "object",
properties: {
authToken: {
type: "string",
description: "JWT token for authenticated user",
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
},
},
},
},
},
},
},
},
},
},
} satisfies AppRouteSchema;

View File

@ -1,54 +0,0 @@
import { t } from "elysia";
import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema";
export const googleRequestSchema = {
query: t.Object({
callback: t.Optional(
t.String({
description: "The callback URL to redirect after Google authentication. It should be URL-encoded if provided.",
}),
),
}),
detail: {
summary: "Initiate Google OAuth flow",
description:
"This endpoint initiates the Google OAuth flow by redirecting the user to Google's authentication page.",
responses: {
200: {
description: "Google login URL created successfully.",
content: {
"application/json": {
schema: {
type: "object",
properties: {
success: {
type: "boolean",
default: true,
},
status: {
type: "number",
default: 200,
},
message: {
type: "string",
default: "Google login URL created successfully.",
},
data: {
type: "object",
properties: {
endpointUrl: {
type: "string",
description: "The URL to redirect the user for Google authentication.",
example:
"https://accounts.google.com/o/oauth2/v2/auth?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&response_type=code&scope=email%20profile",
},
},
},
},
},
},
},
},
},
},
} satisfies AppRouteSchema;

View File

@ -1,97 +0,0 @@
import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema";
export const logoutSchema = {
detail: {
summary: "Logout endpoint",
description: "Logs out the authenticated user by invalidating their session or token.",
responses: {
200: {
description: "Logout successful",
content: {
"application/json": {
schema: {
type: "object",
properties: {
success: {
type: "boolean",
example: true,
},
status: {
type: "number",
example: 200,
},
message: {
type: "string",
example: "Logout successful",
},
data: {
type: "object",
description: "Details about the logout operation. This only returned in development environment.",
properties: {
id: {
type: "string",
example: "123e4567-e89b-12d3-a456-426614174000",
},
isAuthenticated: {
type: "boolean",
example: false,
},
validUntil: {
type: "string",
format: "date-time",
example: "2024-12-31T23:59:59Z",
},
userId: {
type: "string",
example: "user_12345",
},
deletedAt: {
type: "string",
format: "date-time",
example: "2024-01-02T12:00:00Z",
},
createdAt: {
type: "string",
format: "date-time",
example: "2024-01-01T12:00:00Z",
},
updatedAt: {
type: "string",
format: "date-time",
example: "2024-01-02T12:00:00Z",
},
deviceType: {
type: "string",
example: "Desktop",
},
deviceOs: {
type: "string",
example: "Windows 10",
},
deviceIp: {
type: "string",
example: "192.168.1.1",
},
browser: {
type: "string",
example: "Chrome 89.0.4389.82",
},
isOnline: {
type: "boolean",
example: false,
},
lastOnline: {
type: "string",
format: "date-time",
example: "2024-01-02T12:00:00Z",
},
},
},
},
},
},
},
},
},
},
} satisfies AppRouteSchema;

View File

@ -1,22 +0,0 @@
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

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

View File

@ -1,31 +0,0 @@
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

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

View File

@ -1,33 +0,0 @@
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

@ -1,24 +0,0 @@
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

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

View File

@ -1,8 +0,0 @@
import { Context } from "elysia";
import { returnWriteResponse } from "../../../helpers/callback/httpResponse";
import { clearHeroBannerService } from "../services/clearHeroBanner.service";
export const clearHeroBannerController = async (ctx: { set: Context["set"] }) => {
const cacheCleared = await clearHeroBannerService();
return returnWriteResponse(ctx.set, 200, "Hero banner cache flushed successfully", cacheCleared);
};

View File

@ -1,4 +0,0 @@
import Elysia from "elysia";
import { clearHeroBannerController } from "./controllers/clearHeroBanner.controller";
export const flushCacheModule = new Elysia({ prefix: "/flush-cache" }).put("/hero-banner", clearHeroBannerController);

View File

@ -1,12 +0,0 @@
import { redisKey } from "../../../config/redis/key";
import { AppError } from "../../../helpers/error/instances/app";
import { redis } from "../../../utils/databases/redis/connection";
export const clearHeroBannerService = async () => {
try {
const cache = await redis.del(redisKey.find((key) => key.name === "HERO_BANNER")?.key || "");
return cache > 0; // Returns true if cache was cleared, false if it was not found
} catch (error) {
throw new AppError(500, "Failed to clear hero banner cache", error);
}
};

View File

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

View File

@ -1,7 +1,7 @@
import { AppError } from "../../../../helpers/error/instances/app";
import { prisma } from "../../../../utils/databases/prisma/connection";
export const findAllActiveHeroBannerRepository = async (userId?: string) => {
export const findAllActiveHeroBannerRepository = async () => {
try {
return await prisma.heroBanner.findMany({
where: {
@ -20,36 +20,6 @@ export const findAllActiveHeroBannerRepository = async (userId?: string) => {
startDate: "asc",
},
],
select: {
orderPriority: true,
imageUrl: true,
media: {
select: {
id: true,
title: true,
slug: true,
pictureLarge: true,
synopsis: true,
genres: {
select: {
slug: true,
name: true,
},
},
_count: {
select: {
inCollections: {
where: {
collection: {
ownerId: userId,
},
},
},
},
},
},
},
},
});
} catch (error) {
throw new AppError(500, "Failed to fetch active hero banners", error);

View File

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

View File

@ -1,12 +1,12 @@
import { Prisma } from "@prisma/client";
import { AppError } from "../../../helpers/error/instances/app";
import { prisma } from "../../../utils/databases/prisma/connection";
import { generateUUIDv7 } from "../../../helpers/databases/uuidv7";
import { SystemAccountId } from "../../../config/account/system";
import { Static } from "elysia";
import { createHeroBannerSchema } from "../schemas/createHeroBanner.schema";
import { Prisma } from "@prisma/client";
export const insertHeroBannerRepository = async (payload: Static<typeof createHeroBannerSchema.body>) => {
export const insertHeroBannerRepository = async (
payload: Omit<Prisma.HeroBannerCreateInput, "id" | "createdBy">,
) => {
try {
return await prisma.heroBanner.create({
data: {
@ -16,9 +16,6 @@ export const insertHeroBannerRepository = async (payload: Static<typeof createHe
},
});
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") {
throw new AppError(400, "A hero banner with the order priority already exists", error);
}
throw new AppError(500, "Failed to insert hero banner", error);
}
};

View File

@ -3,18 +3,45 @@ import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema";
export const createHeroBannerSchema = {
body: t.Object({
orderPriority: t.Optional(
t.Number({ description: "The priority order of the hero banner. Lower numbers indicate higher priority." }),
),
mediaId: t.String({ description: "The ID of the media associated with the hero banner" }),
imageUrl: t.Optional(
t.String({
description:
"The URL of the image used in the hero banner. If not provided, a thumbnail image of the media will be used.",
isClickable: t.Optional(
t.Boolean({
description: "Indicates whether the hero banner is clickable",
}),
),
startDate: t.Date({ description: "The start date for the hero banner in ISO 8601 format" }),
endDate: t.Date({ description: "The end date for the hero banner in ISO 8601 format" }),
title: t.Optional(
t.String({
description: "The title of the hero banner",
}),
),
tags: t.Array(t.String(), {
description: "An array of tags associated with the hero banner",
}),
description: t.Optional(
t.String({
description: "A brief description of the hero banner",
}),
),
buttonContent: t.Optional(
t.String({
description: "The text content of the button on the hero banner",
}),
),
buttonLink: t.Optional(
t.String({
description: "The URL that the button on the hero banner links to",
}),
),
imageUrl: t.Optional(
t.String({
description: "The URL of the image used in the hero banner",
}),
),
startDate: t.String({
description: "The start date for the hero banner in ISO 8601 format",
}),
endDate: t.String({
description: "The end date for the hero banner in ISO 8601 format",
}),
}),
detail: {
summary: "Create a new hero banner",
@ -37,16 +64,17 @@ export const createHeroBannerSchema = {
"The created hero banner object. This field is returned only if the environment is running in development mode.",
properties: {
id: { type: "string", description: "The ID of the created hero banner" },
orderPriority: {
type: "number",
description: "The priority order of the hero banner. Lower numbers indicate higher priority.",
},
mediaId: { type: "string", description: "The ID of the media associated with the hero banner" },
imageUrl: {
type: "string",
description:
"The URL of the image used in the hero banner. If not provided, a thumbnail image of the media will be used.",
isClickable: { type: "boolean", description: "Indicates whether the hero banner is clickable" },
title: { type: "string", description: "The title of the hero banner" },
tags: {
type: "array",
items: { type: "string" },
description: "An array of tags associated with the hero banner",
},
description: { type: "string", description: "A brief description of the hero banner" },
buttonContent: { type: "string", description: "The text content of the button on the hero banner" },
buttonLink: { type: "string", description: "The URL that the button on the hero banner links to" },
imageUrl: { type: "string", description: "The URL of the image used in the hero banner" },
startDate: {
type: "string",
format: "date-time",

View File

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

View File

@ -1,9 +1,10 @@
import { Static } from "elysia";
import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder";
import { CreateHeroBannerRequestBody } from "../../controllers/createHeroBanner.controller";
import { insertHeroBannerRepository } from "../../repositories/insertHeroBanner.repository";
import { createHeroBannerSchema } from "../../schemas/createHeroBanner.schema";
export const createHeroBannerService = async (payload: Static<typeof createHeroBannerSchema.body>) => {
export const createHeroBannerService = async (
payload: CreateHeroBannerRequestBody,
) => {
try {
return await insertHeroBannerRepository(payload);
} catch (error) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,12 +0,0 @@
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,58 +0,0 @@
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

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

View File

@ -1,14 +0,0 @@
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);
}
};