💥 breaking: upgrade Elysia to v1.4 and update codebase accordingly
This commit is contained in:
3
.prettierrc
Normal file
3
.prettierrc
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"parser": "typescript"
|
||||
}
|
||||
@ -31,7 +31,7 @@
|
||||
"aws-sdk": "^2.1692.0",
|
||||
"bcrypt": "^5.1.1",
|
||||
"cookie": "^1.1.1",
|
||||
"elysia": "latest",
|
||||
"elysia": "^1.4.27",
|
||||
"ioredis": "^5.6.1",
|
||||
"joi": "^17.13.3",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
@ -51,6 +51,7 @@
|
||||
"cz-emoji": "^1.3.2-canary.2",
|
||||
"eslint": "^9.29.0",
|
||||
"globals": "^16.2.0",
|
||||
"prettier": "^3.8.1",
|
||||
"prisma": "^7.2.0",
|
||||
"prisma-dbml-generator": "^0.12.0",
|
||||
"typescript-eslint": "^8.34.1"
|
||||
|
||||
18
src/config/documentation/openAPI.ts
Normal file
18
src/config/documentation/openAPI.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { ElysiaOpenAPIConfig } from "@elysiajs/openapi";
|
||||
|
||||
export const openAPIConfig: ElysiaOpenAPIConfig = {
|
||||
documentation: {
|
||||
info: {
|
||||
title: "TV Nounoz API",
|
||||
description: "API documentation for TV Nounoz backend services",
|
||||
version: "1.0.0",
|
||||
},
|
||||
tags: [
|
||||
{
|
||||
name: "Internal",
|
||||
description:
|
||||
"Endpoints for internal use, such as administrative tasks and data management operations. These endpoints may require authentication and are not intended for public use.",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
8
src/helpers/types/InferSchema.ts
Normal file
8
src/helpers/types/InferSchema.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { Static } from "elysia";
|
||||
|
||||
export type InferSchema<S> = {
|
||||
body: S extends { body: any } ? Static<S["body"]> : never;
|
||||
query: S extends { query: any } ? Static<S["query"]> : never;
|
||||
params: S extends { params: any } ? Static<S["params"]> : never;
|
||||
headers: S extends { headers: any } ? Static<S["headers"]> : never;
|
||||
};
|
||||
15
src/index.ts
15
src/index.ts
@ -3,6 +3,7 @@
|
||||
import openapi from "@elysiajs/openapi";
|
||||
import { middleware } from "./middleware";
|
||||
import { validateEnv } from "./utils/startups/validateEnv";
|
||||
import { openAPIConfig } from "./config/documentation/openAPI";
|
||||
|
||||
validateEnv();
|
||||
|
||||
@ -18,19 +19,7 @@ async function bootstrap() {
|
||||
new Elysia()
|
||||
.use(middleware)
|
||||
.use(routes)
|
||||
.use(
|
||||
openapi({
|
||||
documentation: {
|
||||
tags: [
|
||||
{
|
||||
name: "Internal",
|
||||
description:
|
||||
"Endpoints for internal use only, not exposed to public API consumers.",
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
)
|
||||
.use(openapi(openAPIConfig))
|
||||
.listen(process.env.APP_PORT || 3000);
|
||||
|
||||
console.log(
|
||||
|
||||
@ -1,47 +1,22 @@
|
||||
import { Context } from "elysia";
|
||||
import { Context, Static } from "elysia";
|
||||
import { mainErrorHandler } from "../../../helpers/error/handler";
|
||||
import { bulkInsertAnimeService } from "../services/http/bulkInsertAnime.service";
|
||||
import { returnWriteResponse } from "../../../helpers/callback/httpResponse";
|
||||
import { bulkInsertMediaSchema } from "../schemas/bulkInsertMedia.schema";
|
||||
|
||||
/**
|
||||
* @function bulkInsertMediaController
|
||||
* @description Insert new anime to the database only with mal_id. This operation including inserting related data such as genres, studios, producers, licensors, themes, demographics, and relations.
|
||||
* Insert anime and its related data into the database using MAL ID.
|
||||
*
|
||||
* @param {Context & { body: { mal_id: number } }} ctx
|
||||
* The context object containing the request body.
|
||||
* The body must include:
|
||||
* - mal_id: number - The MyAnimeList ID of the anime to be inserted.
|
||||
* This controller orchestrates the bulk insertion process including
|
||||
* genres, studios, producers, licensors, themes, voice actors, and relations.
|
||||
*
|
||||
* @example
|
||||
* Request route: POST /internal/anime/bulk-insert
|
||||
* Request body:
|
||||
* {
|
||||
* "mal_id": 12345
|
||||
* }
|
||||
*
|
||||
* @returns {Promise<Object>}
|
||||
* A response object indicating success or failure.
|
||||
* Return example:
|
||||
* {
|
||||
* success: true,
|
||||
* status: 201,
|
||||
* message: "Bulk insert anime operation completed successfully",
|
||||
* data: { ...bulkInsertResult } // Data returned only if the env run on development mode
|
||||
* }
|
||||
*
|
||||
* @throws {Object}
|
||||
* An error response object if validation fails or an error occurs during bulk insert operation.
|
||||
* Return example:
|
||||
* {
|
||||
* success: false,
|
||||
* status: <Status Code>,
|
||||
* message: "<Error Message>",
|
||||
* error: { ...errorDetails } // Additional error details if available and the env run on development mode
|
||||
* }
|
||||
* See OpenAPI documentation for request/response schema.
|
||||
*/
|
||||
export const bulkInsertMediaController = async (
|
||||
ctx: Context & { body: { mal_id: number } },
|
||||
) => {
|
||||
export const bulkInsertMediaController = async (ctx: {
|
||||
set: Context["set"];
|
||||
body: Static<typeof bulkInsertMediaSchema.body>;
|
||||
query: Static<typeof bulkInsertMediaSchema.query>;
|
||||
}) => {
|
||||
try {
|
||||
const bulkInsertResult = await bulkInsertAnimeService(ctx.body.mal_id);
|
||||
return returnWriteResponse(
|
||||
|
||||
@ -3,7 +3,7 @@ import { AppRouteSchema } from "../../../helpers/types/AppRouteSchema";
|
||||
|
||||
export const bulkInsertMediaSchema = {
|
||||
body: t.Object({
|
||||
media_mal_id: t.Number({
|
||||
mal_id: t.Number({
|
||||
description:
|
||||
"The MyAnimeList ID of the media for which episodes will be inserted",
|
||||
}),
|
||||
@ -15,35 +15,89 @@ export const bulkInsertMediaSchema = {
|
||||
}),
|
||||
),
|
||||
}),
|
||||
response: {
|
||||
201: t.Object({
|
||||
success: t.Boolean({ default: true }),
|
||||
status: t.Number(),
|
||||
message: t.String(),
|
||||
data: t.Optional(
|
||||
t.Unknown(),
|
||||
),
|
||||
}),
|
||||
404: t.Object({
|
||||
success: t.Boolean({ default: false }),
|
||||
status: t.Number(),
|
||||
message: t.String(),
|
||||
error: t.Optional(
|
||||
t.Unknown(),
|
||||
),
|
||||
}),
|
||||
500: t.Object({
|
||||
success: t.Optional(t.Boolean({ default: false })),
|
||||
status: t.Number(),
|
||||
message: t.String(),
|
||||
error: t.Optional(
|
||||
t.Unknown(),
|
||||
),
|
||||
}),
|
||||
},
|
||||
detail: {
|
||||
summary: "Bulk insert media",
|
||||
description:
|
||||
"Fetch media data from external sources and insert them into database",
|
||||
responses: {
|
||||
201: {
|
||||
description: "Bulk insert media operation completed successfully",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
success: { type: "boolean", default: true },
|
||||
status: { type: "integer", default: 201 },
|
||||
message: {
|
||||
type: "string",
|
||||
default: "Bulk insert anime operation completed successfully",
|
||||
},
|
||||
data: {
|
||||
type: "object",
|
||||
properties: {
|
||||
status: { type: "string", default: "airing" },
|
||||
id: {
|
||||
type: "string",
|
||||
default: "019cc6c9-80b2-7f9a-b1b4-c8fb612ed481",
|
||||
},
|
||||
title: { type: "string", default: "Sakamoto Days" },
|
||||
titleAlternative: { type: "object", default: {} },
|
||||
slug: { type: "string", default: "sakamoto-days" },
|
||||
malId: { type: "integer", default: 58939 },
|
||||
pictureMedium: {
|
||||
type: "string",
|
||||
default:
|
||||
"https://myanimelist.net/images/anime/1026/146459.webp",
|
||||
},
|
||||
pictureLarge: {
|
||||
type: "string",
|
||||
default:
|
||||
"https://myanimelist.net/images/anime/1026/146459.webp",
|
||||
},
|
||||
country: { type: "string", default: "JP" },
|
||||
score: { type: "string", default: "9.0" },
|
||||
startAiring: {
|
||||
type: "string",
|
||||
format: "date-time",
|
||||
default: "2022-07-01T00:00:00.000Z",
|
||||
},
|
||||
endAiring: {
|
||||
type: "string",
|
||||
format: "date-time",
|
||||
default: "2022-07-01T00:00:00.000Z",
|
||||
},
|
||||
synopsis: {
|
||||
type: "string",
|
||||
default: "No synopsis available",
|
||||
},
|
||||
ageRating: { type: "string", default: "PG-13" },
|
||||
mediaType: { type: "string", default: "ANIME" },
|
||||
source: { type: "string" },
|
||||
onDraft: { type: "boolean", default: false },
|
||||
uploadedBy: { type: "string", default: "system" },
|
||||
deletedAt: {
|
||||
type: "string",
|
||||
format: "date-time",
|
||||
default: "2022-07-01T00:00:00.000Z",
|
||||
},
|
||||
createdAt: {
|
||||
type: "string",
|
||||
format: "date-time",
|
||||
default: "2022-07-01T00:00:00.000Z",
|
||||
},
|
||||
updatedAt: {
|
||||
type: "string",
|
||||
format: "date-time",
|
||||
default: "2022-07-01T00:00:00.000Z",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} satisfies AppRouteSchema;
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { getContentReferenceAPI } from "../../../../config/apis/media.reference";
|
||||
import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder";
|
||||
import { bulkInsertGenresRepository } from "../../repositories/bulkInsertGenres.repository";
|
||||
import { InsertMediaRepository } from "../../repositories/bulkinsertMedia.repository";
|
||||
@ -9,6 +8,7 @@ import { generateSlug } from "../../../../helpers/characters/generateSlug";
|
||||
import { bulkInsertCharWithVAService } from "../internal/bulkInsertCharWithVA.service";
|
||||
import { generateUUIDv7 } from "../../../../helpers/databases/uuidv7";
|
||||
import { SystemAccountId } from "../../../../config/account/system";
|
||||
import { getContentReferenceAPI } from "../../../../config/apis/jikan/media.reference";
|
||||
|
||||
export const bulkInsertAnimeService = async (malId: number) => {
|
||||
try {
|
||||
@ -24,8 +24,8 @@ export const bulkInsertAnimeService = async (malId: number) => {
|
||||
const constructMediaPayload: Prisma.MediaUpsertArgs["create"] = {
|
||||
id: generateUUIDv7(),
|
||||
title: mediaFullInfo.data.title,
|
||||
titleAlternative: (mediaFullInfo.data
|
||||
.titles as unknown) as Prisma.InputJsonValue,
|
||||
titleAlternative: mediaFullInfo.data
|
||||
.titles as unknown as Prisma.InputJsonValue,
|
||||
slug: await generateSlug(mediaFullInfo.data.title, {
|
||||
model: "media",
|
||||
target: "slug",
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { getEpisodeReferenceAPI } from "../../../../config/apis/episode.reference";
|
||||
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";
|
||||
|
||||
export const bulkInsertEpisodeService = async (
|
||||
mal_id: number,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { SystemAccountId } from "../../../../config/account/system";
|
||||
import { getContentReferenceAPI } from "../../../../config/apis/media.reference";
|
||||
import { getContentReferenceAPI } from "../../../../config/apis/jikan/media.reference";
|
||||
import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder";
|
||||
import { bulkInsertCharactersRepository } from "../../repositories/bulkInsertCharacters.repository";
|
||||
import { bulkInsertLangVARepository } from "../../repositories/bulkInsertLangVA.repository";
|
||||
|
||||
Reference in New Issue
Block a user