diff --git a/package.json b/package.json index 916c34b..57c386c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "owr-api", - "version": "0.0.1", + "version": "1.0.0-beta", "description": "", "author": "", "private": true, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f6ddfbb..14db354 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,7 +61,7 @@ dependencies: version: 7.8.1 sharp: specifier: ^0.33.3 - version: 0.33.3 + version: 0.33.4 swagger-themes: specifier: ^1.4.3 version: 1.4.3 @@ -666,8 +666,8 @@ packages: resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} dev: true - /@img/sharp-darwin-arm64@0.33.3: - resolution: {integrity: sha512-FaNiGX1MrOuJ3hxuNzWgsT/mg5OHG/Izh59WW2mk1UwYHUwtfbhk5QNKYZgxf0pLOhx9ctGiGa2OykD71vOnSw==} + /@img/sharp-darwin-arm64@0.33.4: + resolution: {integrity: sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA==} engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [arm64] os: [darwin] @@ -677,8 +677,8 @@ packages: dev: false optional: true - /@img/sharp-darwin-x64@0.33.3: - resolution: {integrity: sha512-2QeSl7QDK9ru//YBT4sQkoq7L0EAJZA3rtV+v9p8xTKl4U1bUqTIaCnoC7Ctx2kCjQgwFXDasOtPTCT8eCTXvw==} + /@img/sharp-darwin-x64@0.33.4: + resolution: {integrity: sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw==} engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [x64] os: [darwin] @@ -760,8 +760,8 @@ packages: dev: false optional: true - /@img/sharp-linux-arm64@0.33.3: - resolution: {integrity: sha512-Zf+sF1jHZJKA6Gor9hoYG2ljr4wo9cY4twaxgFDvlG0Xz9V7sinsPp8pFd1XtlhTzYo0IhDbl3rK7P6MzHpnYA==} + /@img/sharp-linux-arm64@0.33.4: + resolution: {integrity: sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q==} engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [arm64] os: [linux] @@ -771,8 +771,8 @@ packages: dev: false optional: true - /@img/sharp-linux-arm@0.33.3: - resolution: {integrity: sha512-Q7Ee3fFSC9P7vUSqVEF0zccJsZ8GiiCJYGWDdhEjdlOeS9/jdkyJ6sUSPj+bL8VuOYFSbofrW0t/86ceVhx32w==} + /@img/sharp-linux-arm@0.33.4: + resolution: {integrity: sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ==} engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [arm] os: [linux] @@ -782,9 +782,9 @@ packages: dev: false optional: true - /@img/sharp-linux-s390x@0.33.3: - resolution: {integrity: sha512-vFk441DKRFepjhTEH20oBlFrHcLjPfI8B0pMIxGm3+yilKyYeHEVvrZhYFdqIseSclIqbQ3SnZMwEMWonY5XFA==} - engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + /@img/sharp-linux-s390x@0.33.4: + resolution: {integrity: sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ==} + engines: {glibc: '>=2.31', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [s390x] os: [linux] requiresBuild: true @@ -793,8 +793,8 @@ packages: dev: false optional: true - /@img/sharp-linux-x64@0.33.3: - resolution: {integrity: sha512-Q4I++herIJxJi+qmbySd072oDPRkCg/SClLEIDh5IL9h1zjhqjv82H0Seupd+q2m0yOfD+/fJnjSoDFtKiHu2g==} + /@img/sharp-linux-x64@0.33.4: + resolution: {integrity: sha512-GoR++s0XW9DGVi8SUGQ/U4AeIzLdNjHka6jidVwapQ/JebGVQIpi52OdyxCNVRE++n1FCLzjDovJNozif7w/Aw==} engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [x64] os: [linux] @@ -804,8 +804,8 @@ packages: dev: false optional: true - /@img/sharp-linuxmusl-arm64@0.33.3: - resolution: {integrity: sha512-qnDccehRDXadhM9PM5hLvcPRYqyFCBN31kq+ErBSZtZlsAc1U4Z85xf/RXv1qolkdu+ibw64fUDaRdktxTNP9A==} + /@img/sharp-linuxmusl-arm64@0.33.4: + resolution: {integrity: sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ==} engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [arm64] os: [linux] @@ -815,8 +815,8 @@ packages: dev: false optional: true - /@img/sharp-linuxmusl-x64@0.33.3: - resolution: {integrity: sha512-Jhchim8kHWIU/GZ+9poHMWRcefeaxFIs9EBqf9KtcC14Ojk6qua7ghKiPs0sbeLbLj/2IGBtDcxHyjCdYWkk2w==} + /@img/sharp-linuxmusl-x64@0.33.4: + resolution: {integrity: sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw==} engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [x64] os: [linux] @@ -826,8 +826,8 @@ packages: dev: false optional: true - /@img/sharp-wasm32@0.33.3: - resolution: {integrity: sha512-68zivsdJ0koE96stdUfM+gmyaK/NcoSZK5dV5CAjES0FUXS9lchYt8LAB5rTbM7nlWtxaU/2GON0HVN6/ZYJAQ==} + /@img/sharp-wasm32@0.33.4: + resolution: {integrity: sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [wasm32] requiresBuild: true @@ -836,8 +836,8 @@ packages: dev: false optional: true - /@img/sharp-win32-ia32@0.33.3: - resolution: {integrity: sha512-CyimAduT2whQD8ER4Ux7exKrtfoaUiVr7HG0zZvO0XTFn2idUWljjxv58GxNTkFb8/J9Ub9AqITGkJD6ZginxQ==} + /@img/sharp-win32-ia32@0.33.4: + resolution: {integrity: sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [ia32] os: [win32] @@ -845,8 +845,8 @@ packages: dev: false optional: true - /@img/sharp-win32-x64@0.33.3: - resolution: {integrity: sha512-viT4fUIDKnli3IfOephGnolMzhz5VaTvDRkYqtZxOMIoMQ4MrAziO7pT1nVnOt2FAm7qW5aa+CCc13aEY6Le0g==} + /@img/sharp-win32-x64@0.33.4: + resolution: {integrity: sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [x64] os: [win32] @@ -2287,8 +2287,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001618 - electron-to-chromium: 1.4.769 + caniuse-lite: 1.0.30001620 + electron-to-chromium: 1.4.774 node-releases: 2.0.14 update-browserslist-db: 1.0.16(browserslist@4.23.0) dev: true @@ -2350,8 +2350,8 @@ packages: engines: {node: '>=10'} dev: true - /caniuse-lite@1.0.30001618: - resolution: {integrity: sha512-p407+D1tIkDvsEAPS22lJxLQQaG8OTBEqo0KhzfABGk0TU4juBNDSfH0hyAp/HRyx+M8L17z/ltyhxh27FTfQg==} + /caniuse-lite@1.0.30001620: + resolution: {integrity: sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==} dev: true /chalk@2.4.2: @@ -2744,8 +2744,8 @@ packages: /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - /electron-to-chromium@1.4.769: - resolution: {integrity: sha512-bZu7p623NEA2rHTc9K1vykl57ektSPQYFFqQir8BOYf6EKOB+yIsbFB9Kpm7Cgt6tsLr9sRkqfqSZUw7LP1XxQ==} + /electron-to-chromium@1.4.774: + resolution: {integrity: sha512-132O1XCd7zcTkzS3FgkAzKmnBuNJjK8WjcTtNuoylj7MYbqw5eXehjQ5OK91g0zm7OTKIPeaAG4CPoRfD9M1Mg==} dev: true /emittery@0.13.1: @@ -2789,8 +2789,8 @@ packages: engines: {node: '>= 0.4'} dev: true - /es-module-lexer@1.5.2: - resolution: {integrity: sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA==} + /es-module-lexer@1.5.3: + resolution: {integrity: sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==} dev: true /escalade@3.1.2: @@ -5188,8 +5188,8 @@ packages: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} dev: false - /sharp@0.33.3: - resolution: {integrity: sha512-vHUeXJU1UvlO/BNwTpT0x/r53WkLUVxrmb5JTgW92fdFCFk0ispLMAeu/jPO2vjkXM1fYUi3K7/qcLF47pwM1A==} + /sharp@0.33.4: + resolution: {integrity: sha512-7i/dt5kGl7qR4gwPRD2biwD2/SvBn3O04J77XKFgL2OnZtQw+AG9wnuS/csmu80nPRHLYE9E41fyEiG8nhH6/Q==} engines: {libvips: '>=8.15.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0} requiresBuild: true dependencies: @@ -5197,8 +5197,8 @@ packages: detect-libc: 2.0.3 semver: 7.6.2 optionalDependencies: - '@img/sharp-darwin-arm64': 0.33.3 - '@img/sharp-darwin-x64': 0.33.3 + '@img/sharp-darwin-arm64': 0.33.4 + '@img/sharp-darwin-x64': 0.33.4 '@img/sharp-libvips-darwin-arm64': 1.0.2 '@img/sharp-libvips-darwin-x64': 1.0.2 '@img/sharp-libvips-linux-arm': 1.0.2 @@ -5207,15 +5207,15 @@ packages: '@img/sharp-libvips-linux-x64': 1.0.2 '@img/sharp-libvips-linuxmusl-arm64': 1.0.2 '@img/sharp-libvips-linuxmusl-x64': 1.0.2 - '@img/sharp-linux-arm': 0.33.3 - '@img/sharp-linux-arm64': 0.33.3 - '@img/sharp-linux-s390x': 0.33.3 - '@img/sharp-linux-x64': 0.33.3 - '@img/sharp-linuxmusl-arm64': 0.33.3 - '@img/sharp-linuxmusl-x64': 0.33.3 - '@img/sharp-wasm32': 0.33.3 - '@img/sharp-win32-ia32': 0.33.3 - '@img/sharp-win32-x64': 0.33.3 + '@img/sharp-linux-arm': 0.33.4 + '@img/sharp-linux-arm64': 0.33.4 + '@img/sharp-linux-s390x': 0.33.4 + '@img/sharp-linux-x64': 0.33.4 + '@img/sharp-linuxmusl-arm64': 0.33.4 + '@img/sharp-linuxmusl-x64': 0.33.4 + '@img/sharp-wasm32': 0.33.4 + '@img/sharp-win32-ia32': 0.33.4 + '@img/sharp-win32-x64': 0.33.4 dev: false /shebang-command@2.0.0: @@ -5901,7 +5901,7 @@ packages: browserslist: 4.23.0 chrome-trace-event: 1.0.3 enhanced-resolve: 5.16.1 - es-module-lexer: 1.5.2 + es-module-lexer: 1.5.3 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 @@ -5941,7 +5941,7 @@ packages: browserslist: 4.23.0 chrome-trace-event: 1.0.3 enhanced-resolve: 5.16.1 - es-module-lexer: 1.5.2 + es-module-lexer: 1.5.3 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 diff --git a/prisma/migrations/20240518080728_delete_cascade/migration.sql b/prisma/migrations/20240518080728_delete_cascade/migration.sql new file mode 100644 index 0000000..a5d9018 --- /dev/null +++ b/prisma/migrations/20240518080728_delete_cascade/migration.sql @@ -0,0 +1,42 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_webtoon_genres" ( + "webtoon_id" INTEGER NOT NULL, + "genre_id" INTEGER NOT NULL, + + PRIMARY KEY ("webtoon_id", "genre_id"), + CONSTRAINT "webtoon_genres_webtoon_id_fkey" FOREIGN KEY ("webtoon_id") REFERENCES "webtoons" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "webtoon_genres_genre_id_fkey" FOREIGN KEY ("genre_id") REFERENCES "genres" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_webtoon_genres" ("genre_id", "webtoon_id") SELECT "genre_id", "webtoon_id" FROM "webtoon_genres"; +DROP TABLE "webtoon_genres"; +ALTER TABLE "new_webtoon_genres" RENAME TO "webtoon_genres"; +CREATE TABLE "new_episode_images" ( + "number" INTEGER NOT NULL, + "episode_id" INTEGER NOT NULL, + "image_id" INTEGER NOT NULL, + + PRIMARY KEY ("episode_id", "number"), + CONSTRAINT "episode_images_episode_id_fkey" FOREIGN KEY ("episode_id") REFERENCES "episodes" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "episode_images_image_id_fkey" FOREIGN KEY ("image_id") REFERENCES "images" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_episode_images" ("episode_id", "image_id", "number") SELECT "episode_id", "image_id", "number" FROM "episode_images"; +DROP TABLE "episode_images"; +ALTER TABLE "new_episode_images" RENAME TO "episode_images"; +CREATE TABLE "new_episodes" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "title" TEXT NOT NULL, + "number" INTEGER NOT NULL, + "webtoon_id" INTEGER NOT NULL, + "thumbnail_id" INTEGER NOT NULL, + CONSTRAINT "episodes_webtoon_id_fkey" FOREIGN KEY ("webtoon_id") REFERENCES "webtoons" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "episodes_thumbnail_id_fkey" FOREIGN KEY ("thumbnail_id") REFERENCES "images" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_episodes" ("id", "number", "thumbnail_id", "title", "webtoon_id") SELECT "id", "number", "thumbnail_id", "title", "webtoon_id" FROM "episodes"; +DROP TABLE "episodes"; +ALTER TABLE "new_episodes" RENAME TO "episodes"; +CREATE UNIQUE INDEX "episodes_webtoon_id_number_key" ON "episodes"("webtoon_id", "number"); +PRAGMA foreign_key_check("webtoon_genres"); +PRAGMA foreign_key_check("episode_images"); +PRAGMA foreign_key_check("episodes"); +PRAGMA foreign_keys=ON; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c5c6760..9af2a44 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -47,7 +47,7 @@ model Genres { model WebtoonGenres { webtoon_id Int genre_id Int - webtoon Webtoons @relation(fields: [webtoon_id], references: [id]) + webtoon Webtoons @relation(fields: [webtoon_id], references: [id], onDelete: Cascade) genre Genres @relation(fields: [genre_id], references: [id]) @@id([webtoon_id, genre_id]) @@ -79,7 +79,7 @@ model Episodes { title String number Int webtoon_id Int - webtoon Webtoons @relation(fields: [webtoon_id], references: [id]) + webtoon Webtoons @relation(fields: [webtoon_id], references: [id], onDelete: Cascade) thumbnail_id Int thumbnail Images @relation(fields: [thumbnail_id], references: [id]) episode_images EpisodeImages[] @@ -91,7 +91,7 @@ model Episodes { model EpisodeImages { number Int episode_id Int - episode Episodes @relation(fields: [episode_id], references: [id]) + episode Episodes @relation(fields: [episode_id], references: [id], onDelete: Cascade) image_id Int image Images @relation(fields: [image_id], references: [id]) diff --git a/src/modules/webtoon/webtoon/models/responses/episode.response.ts b/src/modules/webtoon/webtoon/models/responses/episode.response.ts index c321fc9..5322735 100644 --- a/src/modules/webtoon/webtoon/models/responses/episode.response.ts +++ b/src/modules/webtoon/webtoon/models/responses/episode.response.ts @@ -3,14 +3,10 @@ import {ApiProperty} from "@nestjs/swagger"; export default class EpisodeResponse{ @ApiProperty() title: string; - @ApiProperty() - images: string[]; constructor( title: string, - images: string[] ){ this.title = title; - this.images = images; } } diff --git a/src/modules/webtoon/webtoon/models/responses/webtoon.response.ts b/src/modules/webtoon/webtoon/models/responses/light-webtoon-response.ts similarity index 93% rename from src/modules/webtoon/webtoon/models/responses/webtoon.response.ts rename to src/modules/webtoon/webtoon/models/responses/light-webtoon-response.ts index c3acf61..20538e8 100644 --- a/src/modules/webtoon/webtoon/models/responses/webtoon.response.ts +++ b/src/modules/webtoon/webtoon/models/responses/light-webtoon-response.ts @@ -1,6 +1,6 @@ import {ApiProperty} from "@nestjs/swagger"; -export default class WebtoonResponse{ +export default class LightWebtoonResponse{ @ApiProperty() id: number; @ApiProperty() diff --git a/src/modules/webtoon/webtoon/models/responses/episodes.response.ts b/src/modules/webtoon/webtoon/models/responses/webtoon-response.ts similarity index 54% rename from src/modules/webtoon/webtoon/models/responses/episodes.response.ts rename to src/modules/webtoon/webtoon/models/responses/webtoon-response.ts index a7b3963..b10efb5 100644 --- a/src/modules/webtoon/webtoon/models/responses/episodes.response.ts +++ b/src/modules/webtoon/webtoon/models/responses/webtoon-response.ts @@ -1,34 +1,27 @@ -import EpisodeLineModel from "../models/episode-line.model"; import {ApiProperty} from "@nestjs/swagger"; +import LightWebtoonResponse from "./light-webtoon-response"; -export default class EpisodesResponse{ - @ApiProperty() - episodes: EpisodeLineModel[]; +export default class WebtoonResponse extends LightWebtoonResponse{ @ApiProperty() backgroundBanner: string; @ApiProperty() topBanner: string; @ApiProperty() mobileBanner: string; - @ApiProperty() - title: string; - @ApiProperty() - author: string; constructor( - episodes: EpisodeLineModel[], + id: number, + title: string, + language: string, + thumbnail: string, + author: string, backgroundBanner: string, topBanner: string, mobileBanner: string, - title: string, - author: string ){ - this.episodes = episodes; + super(id, title, language, thumbnail, author); this.backgroundBanner = backgroundBanner; this.topBanner = topBanner; this.mobileBanner = mobileBanner; - this.title = title; - this.author = author; } - } diff --git a/src/modules/webtoon/webtoon/webtoon-database.service.ts b/src/modules/webtoon/webtoon/webtoon-database.service.ts index 8cd1836..6329815 100644 --- a/src/modules/webtoon/webtoon/webtoon-database.service.ts +++ b/src/modules/webtoon/webtoon/webtoon-database.service.ts @@ -3,20 +3,19 @@ import CachedWebtoonModel from "./models/models/cached-webtoon.model"; import EpisodeModel from "./models/models/episode.model"; import EpisodeDataModel from "./models/models/episode-data.model"; import EpisodeResponse from "./models/responses/episode.response"; -import EpisodesResponse from "./models/responses/episodes.response"; import EpisodeLineModel from "./models/models/episode-line.model"; -import WebtoonResponse from "./models/responses/webtoon.response"; +import LightWebtoonResponse from "./models/responses/light-webtoon-response"; import WebtoonModel from "./models/models/webtoon.model"; import WebtoonDataModel from "./models/models/webtoon-data.model"; import {Injectable, NotFoundException} from "@nestjs/common"; import {PrismaService} from "../../misc/prisma.service"; import {MiscService} from "../../misc/misc.service"; import ImageTypes from "./models/enums/image-types"; +import WebtoonResponse from "./models/responses/webtoon-response"; @Injectable() export class WebtoonDatabaseService{ - constructor( private readonly prismaService: PrismaService, private readonly miscService: MiscService @@ -224,22 +223,22 @@ export class WebtoonDatabaseService{ }); } - async getWebtoons(): Promise{ - const webtoons = await this.prismaService.webtoons.findMany({ + async getWebtoons(): Promise{ + const webtoons: any = await this.prismaService.webtoons.findMany({ include: { thumbnail: true } }); - const response: WebtoonResponse[] = []; + const response: LightWebtoonResponse[] = []; for(const webtoon of webtoons){ const thumbnail: string = this.miscService.bufferToDataURL(this.loadImage(webtoon.thumbnail.sum)); - response.push(new WebtoonResponse(webtoon.id, webtoon.title, webtoon.language, thumbnail, webtoon.author)); + response.push(new LightWebtoonResponse(webtoon.id, webtoon.title, webtoon.language, thumbnail, webtoon.author)); } return response; } - async getEpisodeInfos(webtoonId: number): Promise{ - const dbWebtoon = await this.prismaService.webtoons.findFirst({ + async getWebtoon(webtoonId: number){ + const webtoon: any = await this.prismaService.webtoons.findFirst({ where: { id: webtoonId }, @@ -247,12 +246,36 @@ export class WebtoonDatabaseService{ thumbnail: true, background_banner: true, top_banner: true, - mobile_banner: true, + mobile_banner: true + } + }); + if(!webtoon) + throw new NotFoundException(`Webtoon with id ${webtoonId} not found in database.`); + const thumbnail: string = this.miscService.bufferToDataURL(this.loadImage(webtoon.thumbnail.sum)); + const backgroundBanner: string = this.miscService.bufferToDataURL(this.loadImage(webtoon.background_banner.sum)); + const topBanner: string = this.miscService.bufferToDataURL(this.loadImage(webtoon.top_banner.sum)); + const mobileBanner: string = this.miscService.bufferToDataURL(this.loadImage(webtoon.mobile_banner.sum)); + return new WebtoonResponse( + webtoon.id, + webtoon.title, + webtoon.language, + thumbnail, + webtoon.author, + backgroundBanner, + topBanner, + mobileBanner + ); + } + + async getEpisodes(webtoonId: number): Promise{ + const dbWebtoon: any = await this.prismaService.webtoons.findFirst({ + where: { + id: webtoonId } }); if(!dbWebtoon) throw new NotFoundException(`Webtoon with id ${webtoonId} not found in database.`); - const episodes = await this.prismaService.episodes.findMany({ + const episodes: any[] = await this.prismaService.episodes.findMany({ where: { webtoon_id: webtoonId }, @@ -263,40 +286,36 @@ export class WebtoonDatabaseService{ number: "asc" } }); - const episodeLines: EpisodeLineModel[] = []; - const episodeLinesPromises: Promise[] = []; - for(const episode of episodes) - episodeLinesPromises.push(this.loadEpisodeLine(episode)); - for(const promise of episodeLinesPromises) - episodeLines.push(await promise); - return { - episodes: episodeLines, - backgroundBanner: this.miscService.bufferToDataURL(this.loadImage(dbWebtoon.background_banner.sum)), - topBanner: this.miscService.bufferToDataURL(this.loadImage(dbWebtoon.top_banner.sum)), - mobileBanner: this.miscService.bufferToDataURL(this.loadImage(dbWebtoon.mobile_banner.sum)), - title: dbWebtoon.title, - } as EpisodesResponse; + const response: EpisodeLineModel[] = []; + for(const episode of episodes){ + const thumbnail: string = this.miscService.bufferToDataURL(this.loadImage(episode.thumbnail.sum)); + response.push(new EpisodeLineModel(episode.id, episode.title, episode.number, thumbnail)); + } + return response; } - private async loadEpisodeLine(episode: any): Promise{ - const thumbnailData: Buffer | undefined = this.loadImage(episode.thumbnail.sum); - if(!thumbnailData) - throw new NotFoundException(`Thumbnail not found for episode ${episode.number}`); - const thumbnail: string = this.miscService.bufferToDataURL(thumbnailData); - return new EpisodeLineModel(episode.id, episode.title, episode.number, thumbnail); + async getEpisodeInfos(episodeId: number): Promise{ + const episode: any = await this.prismaService.episodes.findFirst({ + where: { + id: episodeId + } + }); + if(!episode) + throw new NotFoundException(`Episode with id ${episodeId} not found in database.`); + return new EpisodeResponse(episode.title); } - async getEpisodeImages(episodeId: number): Promise{ - const episode = await this.prismaService.episodes.findFirst({ + async getEpisodeImages(episodeId: number): Promise{ + const episode: any = await this.prismaService.episodes.findFirst({ where: { - id: episodeId, + id: episodeId } }); if(!episode) - throw new NotFoundException(`Episode ${episodeId} not found in database.`); - const images = await this.prismaService.episodeImages.findMany({ + throw new NotFoundException(`Episode with id ${episodeId} not found in database.`); + const images: any[] = await this.prismaService.episodeImages.findMany({ where: { - episode_id: episode.id + episode_id: episodeId }, include: { image: true @@ -305,14 +324,10 @@ export class WebtoonDatabaseService{ number: "asc" } }); - const episodeImages: string[] = []; - for(const image of images){ - const imageData: Buffer | undefined = this.loadImage(image.image.sum); - if(!imageData) - throw new NotFoundException(`Image not found for episode ${episodeId}`); - episodeImages.push(this.miscService.bufferToDataURL(imageData)); - } - return new EpisodeResponse(episode.title, episodeImages); + const response: string[] = []; + for(const image of images) + response.push(this.miscService.bufferToDataURL(this.loadImage(image.image.sum))); + return response; } private saveImage(image: Buffer): string{ diff --git a/src/modules/webtoon/webtoon/webtoon.controller.ts b/src/modules/webtoon/webtoon/webtoon.controller.ts index 8d46450..c85f437 100644 --- a/src/modules/webtoon/webtoon/webtoon.controller.ts +++ b/src/modules/webtoon/webtoon/webtoon.controller.ts @@ -3,11 +3,11 @@ import {ApiResponse, ApiTags} from "@nestjs/swagger"; import {WebtoonDatabaseService} from "./webtoon-database.service"; import {WebtoonIdDto} from "./models/dto/webtoon-id.dto"; import {EpisodeIdDto} from "./models/dto/episode-id.dto"; -import EpisodesResponse from "./models/responses/episodes.response"; import EpisodeResponse from "./models/responses/episode.response"; -import WebtoonResponse from "./models/responses/webtoon.response"; +import LightWebtoonResponse from "./models/responses/light-webtoon-response"; import {Throttle} from "@nestjs/throttler"; - +import WebtoonResponse from "./models/responses/webtoon-response"; +import EpisodeLineModel from "./models/models/episode-line.model"; @Controller("webtoons") @ApiTags("Webtoon") @@ -19,20 +19,34 @@ export class WebtoonController{ ){} @Get() - @ApiResponse({status: 200, description: "Returns a list of webtoons", type: WebtoonResponse, isArray: true}) - async getWebtoonList(): Promise{ + @ApiResponse({status: 200, description: "Returns a list of webtoons", type: LightWebtoonResponse, isArray: true}) + async getWebtoonList(): Promise{ return this.webtoonDatabaseService.getWebtoons(); } + @Get(":webtoonId") + @ApiResponse({status: 200, description: "Returns a webtoon", type: WebtoonResponse}) + @ApiResponse({status: 404, description: "Webtoon not found"}) + async getWebtoon(@Param() webtoonIdDto: WebtoonIdDto){ + return this.webtoonDatabaseService.getWebtoon(webtoonIdDto.webtoonId); + } + @Get(":webtoonId/episodes") - @ApiResponse({status: 200, description: "Returns a list of episodes for a webtoon", type: EpisodesResponse}) - async getWebtoonEpisodes(@Param() webtoonIdDto: WebtoonIdDto): Promise{ - return this.webtoonDatabaseService.getEpisodeInfos(webtoonIdDto.webtoonId); + @ApiResponse({status: 200, description: "Returns a list of episodes for a webtoon", type: EpisodeLineModel, isArray: true}) + @ApiResponse({status: 404, description: "Webtoon not found"}) + async getWebtoonEpisodes(@Param() webtoonIdDto: WebtoonIdDto): Promise{ + return this.webtoonDatabaseService.getEpisodes(webtoonIdDto.webtoonId); + } + + @Get("episodes/:episodeId") + @ApiResponse({status: 200, description: "Returns an episode", type: EpisodeResponse}) + async getEpisode(@Param() episodeIdDto: EpisodeIdDto): Promise{ + return this.webtoonDatabaseService.getEpisodeInfos(episodeIdDto.episodeId); } @Get("episodes/:episodeId/images") - @ApiResponse({status: 200, description: "Returns a list of images for an episode", type: EpisodeResponse}) - async getEpisodeImages(@Param() episodeIdDto: EpisodeIdDto): Promise{ + @ApiResponse({status: 200, description: "Returns a list of images for an episode", type: String, isArray: true}) + async getEpisodeImages(@Param() episodeIdDto: EpisodeIdDto): Promise{ return this.webtoonDatabaseService.getEpisodeImages(episodeIdDto.episodeId); } }