diff --git a/prisma/dbml/schema.dbml b/prisma/dbml/schema.dbml index 0619e0f..5853552 100644 --- a/prisma/dbml/schema.dbml +++ b/prisma/dbml/schema.dbml @@ -181,6 +181,10 @@ Table videos { deletedAt DateTime createdAt DateTime [default: `now()`, not null] updatedAt DateTime [default: `now()`, not null] + + indexes { + (serviceId, code) [unique] + } } Table video_services { @@ -191,6 +195,7 @@ Table video_services { hexColor String [not null] endpointVideo String [not null] endpointThumbnail String + endpointDownload String creator users [not null] createdBy String [not null] deletedAt DateTime diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 939f482..0c255ec 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -218,6 +218,8 @@ model Video { deletedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt + + @@unique([serviceId, code]) @@map("videos") } @@ -229,6 +231,7 @@ model VideoService { hexColor String @db.VarChar(10) endpointVideo String @db.Text endpointThumbnail String? @db.Text + endpointDownload String? creator User @relation("UserVideoServices", fields: [createdBy], references: [id]) createdBy String @db.Uuid deletedAt DateTime? diff --git a/src/modules/internal/controllers/bulkInsertVideo.controller.ts b/src/modules/internal/controllers/bulkInsertVideo.controller.ts new file mode 100644 index 0000000..d68450a --- /dev/null +++ b/src/modules/internal/controllers/bulkInsertVideo.controller.ts @@ -0,0 +1,26 @@ +import { Context } from "elysia"; +import { mainErrorHandler } from "../../../helpers/error/handler"; +import { bulkInsertVideoService } from "../services/http/bulkInsertVideo.service"; +import { returnWriteResponse } from "../../../helpers/callback/httpResponse"; + +export interface BulkInsertVideoBodyRequest { + media_id: string; + data: Array<{ + episode: number; + videos: Array<{ + service_id: string; + code: string; + }>; + }>; +} + +export const bulkInsertVideoController = async ( + ctx: Context & { body: BulkInsertVideoBodyRequest }, +) => { + try { + const insertedVideos = await bulkInsertVideoService(ctx.body); + return returnWriteResponse(ctx.set, 201, "Videos inserted", insertedVideos); + } catch (error) { + throw mainErrorHandler(ctx.set, error); + } +}; diff --git a/src/modules/internal/controllers/createVideoService.controller.ts b/src/modules/internal/controllers/createVideoService.controller.ts new file mode 100644 index 0000000..90f8d68 --- /dev/null +++ b/src/modules/internal/controllers/createVideoService.controller.ts @@ -0,0 +1,32 @@ +import { Context } from "elysia"; +import { mainErrorHandler } from "../../../helpers/error/handler"; +import { returnWriteResponse } from "../../../helpers/callback/httpResponse"; +import { createVideoServiceInternalService } from "../services/http/createVideoService.service"; + +export interface CreateVideoServiceBodyRequest { + name: string; + domain: string; + logo: string; + hexColor: string; + endpointVideo: string; + endpointThumbnail: string; + endpointDownload?: string; +} + +export const createVideoServiceInternalController = async ( + ctx: Context & { body: CreateVideoServiceBodyRequest }, +) => { + try { + const createdVideoService = await createVideoServiceInternalService( + ctx.body, + ); + return returnWriteResponse( + ctx.set, + 201, + "Video service created", + createdVideoService, + ); + } catch (error) { + throw mainErrorHandler(ctx.set, error); + } +}; diff --git a/src/modules/internal/index.ts b/src/modules/internal/index.ts index b7321b5..471c1c9 100644 --- a/src/modules/internal/index.ts +++ b/src/modules/internal/index.ts @@ -1,7 +1,11 @@ import Elysia from "elysia"; import { bulkInsertEpisodeController } from "./controllers/bulkInsertEpisode.controller"; import { bulkInsertMediaController } from "./controllers/bulkInsertMedia.controller"; +import { createVideoServiceInternalController } from "./controllers/createVideoService.controller"; +import { bulkInsertVideoController } from "./controllers/bulkInsertVideo.controller"; export const internalModule = new Elysia({ prefix: "/internal" }) .post("/media/bulk-insert", bulkInsertMediaController) - .post("/episode/bulk-insert", bulkInsertEpisodeController); + .post("/episode/bulk-insert", bulkInsertEpisodeController) + .post("/video/bulk-insert", bulkInsertVideoController) + .post("/video-service", createVideoServiceInternalController); diff --git a/src/modules/internal/repositories/bulkInsertVideo.repository.ts b/src/modules/internal/repositories/bulkInsertVideo.repository.ts new file mode 100644 index 0000000..21eb3cf --- /dev/null +++ b/src/modules/internal/repositories/bulkInsertVideo.repository.ts @@ -0,0 +1,26 @@ +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"; + +export const bulkInsertVideoRepository = async ( + payload: Omit, +) => { + try { + return await prisma.video.upsert({ + where: { + serviceId_code: { + serviceId: payload.serviceId, + code: payload.code, + }, + }, + create: { + id: generateUUIDv7(), + ...payload, + }, + update: payload, + }); + } catch (error) { + throw new AppError(500, "Error inserting video", error); + } +}; diff --git a/src/modules/internal/repositories/createVideoService.repository.ts b/src/modules/internal/repositories/createVideoService.repository.ts new file mode 100644 index 0000000..b73827e --- /dev/null +++ b/src/modules/internal/repositories/createVideoService.repository.ts @@ -0,0 +1,23 @@ +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"; + +export const createVideoServiceInternalRepository = async ( + payload: Omit, +) => { + try { + return await prisma.videoService.upsert({ + where: { + name: payload.name, + }, + create: { + id: generateUUIDv7(), + ...payload, + }, + update: payload, + }); + } catch (error) { + throw new AppError(500, "Failed to create video service", error); + } +}; diff --git a/src/modules/internal/repositories/findEpisodeWithMediaId.repository.ts b/src/modules/internal/repositories/findEpisodeWithMediaId.repository.ts new file mode 100644 index 0000000..0817e06 --- /dev/null +++ b/src/modules/internal/repositories/findEpisodeWithMediaId.repository.ts @@ -0,0 +1,28 @@ +import { AppError } from "../../../helpers/error/instances/app"; +import { prisma } from "../../../utils/databases/prisma/connection"; + +export const findEpisodeWithMediaIdRepository = async ({ + media, + episode, +}: { + media: string; + episode: number; +}) => { + try { + const foundEpisode = await prisma.episode.findUnique({ + where: { + mediaId_episode: { + mediaId: media, + episode: episode, + }, + }, + select: { + id: true, + }, + }); + if (!foundEpisode) throw new AppError(404, "Episode not found"); + return foundEpisode; + } catch (error) { + throw new AppError(500, "Error finding episode with media id", error); + } +}; diff --git a/src/modules/internal/services/http/bulkInsertVideo.service.ts b/src/modules/internal/services/http/bulkInsertVideo.service.ts new file mode 100644 index 0000000..d10fa5b --- /dev/null +++ b/src/modules/internal/services/http/bulkInsertVideo.service.ts @@ -0,0 +1,34 @@ +import { SystemAccountId } from "../../../../config/account/system"; +import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder"; +import { BulkInsertVideoBodyRequest } from "../../controllers/bulkInsertVideo.controller"; +import { findEpisodeWithMediaIdRepository } from "../../repositories/findEpisodeWithMediaId.repository"; +import { bulkInsertVideoRepository } from "../../repositories/bulkInsertVideo.repository"; + +export const bulkInsertVideoService = async ( + body: BulkInsertVideoBodyRequest, +) => { + try { + const insertedVideos: string[] = []; + for (const episodeData of body.data) { + const episodeId = await findEpisodeWithMediaIdRepository({ + media: body.media_id, + episode: episodeData.episode, + }); + + for (const videoData of episodeData.videos) { + const insertedVideo = await bulkInsertVideoRepository({ + episodeId: episodeId.id, + serviceId: videoData.service_id, + code: videoData.code, + uploadedBy: SystemAccountId, + }); + + insertedVideos.push(insertedVideo.id); + } + } + + return insertedVideos; + } catch (error) { + ErrorForwarder(error); + } +}; diff --git a/src/modules/internal/services/http/createVideoService.service.ts b/src/modules/internal/services/http/createVideoService.service.ts new file mode 100644 index 0000000..47791bd --- /dev/null +++ b/src/modules/internal/services/http/createVideoService.service.ts @@ -0,0 +1,23 @@ +import { SystemAccountId } from "../../../../config/account/system"; +import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder"; +import { CreateVideoServiceBodyRequest } from "../../controllers/createVideoService.controller"; +import { createVideoServiceInternalRepository } from "../../repositories/createVideoService.repository"; + +export const createVideoServiceInternalService = async ( + body: CreateVideoServiceBodyRequest, +) => { + try { + return await createVideoServiceInternalRepository({ + name: body.name, + domain: body.domain, + logo: body.logo, + hexColor: body.hexColor, + endpointVideo: body.endpointVideo, + endpointThumbnail: body.endpointThumbnail, + endpointDownload: body.endpointDownload, + createdBy: SystemAccountId, + }); + } catch (error) { + ErrorForwarder(error); + } +};