diff --git a/api/src/base.ts b/api/src/base.ts index 01a7fef8..ad33b439 100644 --- a/api/src/base.ts +++ b/api/src/base.ts @@ -52,8 +52,7 @@ export const base = new Elysia({ name: "base" }) console.error(code, error); return { status: 500, - message: "message" in error ? (error?.message ?? code) : code, - details: error, + message: "Internal server error", } as KError; }) .get("/health", () => ({ status: "healthy" }) as const, { diff --git a/api/src/controllers/seed/insert/staff.ts b/api/src/controllers/seed/insert/staff.ts index 837bf74b..a85b4b23 100644 --- a/api/src/controllers/seed/insert/staff.ts +++ b/api/src/controllers/seed/insert/staff.ts @@ -4,6 +4,7 @@ import { roles, staff } from "~/db/schema"; import { conflictUpdateAllExcept, unnestValues } from "~/db/utils"; import type { SeedStaff } from "~/models/staff"; import { record } from "~/otel"; +import { uniqBy } from "~/utils"; import { enqueueOptImage, flushImageQueue, type ImageTask } from "../images"; export const insertStaff = record( @@ -13,13 +14,16 @@ export const insertStaff = record( return await db.transaction(async (tx) => { const imgQueue: ImageTask[] = []; - const people = seed.map((x) => ({ - ...x.staff, - image: enqueueOptImage(imgQueue, { - url: x.staff.image, - column: staff.image, - }), - })); + const people = uniqBy( + seed.map((x) => ({ + ...x.staff, + image: enqueueOptImage(imgQueue, { + url: x.staff.image, + column: staff.image, + }), + })), + (x) => x.slug, + ); const ret = await tx .insert(staff) .select(unnestValues(people, staff)) @@ -36,7 +40,7 @@ export const insertStaff = record( const rval = seed.map((x, i) => ({ showPk, - staffPk: ret[i].pk, + staffPk: ret.find(y => y.slug === x.staff.slug)!.pk, kind: x.kind, order: i, character: { diff --git a/api/src/controllers/videos.ts b/api/src/controllers/videos.ts index 87d35954..db528571 100644 --- a/api/src/controllers/videos.ts +++ b/api/src/controllers/videos.ts @@ -831,6 +831,9 @@ export const videosWriteH = new Elysia({ prefix: "/videos", tags: ["videos"] }) .post( "", async ({ body, status }) => { + if (body.length === 0) { + return status(422, { status: 422, message: "No videos" }); + } return await db.transaction(async (tx) => { let vids: { pk: number; id: string; path: string; guess: Guess }[] = []; try { @@ -925,6 +928,7 @@ export const videosWriteH = new Elysia({ prefix: "/videos", tags: ["videos"] }) description: "Invalid rendering specified. (conflicts with an existing video)", }, + 422: KError, }, }, ) diff --git a/api/src/db/schema/seasons.ts b/api/src/db/schema/seasons.ts index e309eb94..a196b355 100644 --- a/api/src/db/schema/seasons.ts +++ b/api/src/db/schema/seasons.ts @@ -91,7 +91,7 @@ export const seasonRelations = relations(seasons, ({ one, many }) => ({ export const seasonTrRelations = relations(seasonTranslations, ({ one }) => ({ season: one(seasons, { - relationName: "season_translation", + relationName: "season_translations", fields: [seasonTranslations.pk], references: [seasons.pk], }), diff --git a/api/src/utils.ts b/api/src/utils.ts index 5c251e67..da16412f 100644 --- a/api/src/utils.ts +++ b/api/src/utils.ts @@ -28,3 +28,13 @@ export function getFile(path: string): BunFile | S3File { return Bun.file(path); } + +export function uniqBy(a: T[], key: (val: T) => string) { + const seen: Record = {}; + return a.filter((item) => { + const k = key(item); + if (seen[k]) return false; + seen[k] = true; + return true; + }); +} diff --git a/api/tests/series/seed-serie.test.ts b/api/tests/series/seed-serie.test.ts index 44c73f9d..523d5c61 100644 --- a/api/tests/series/seed-serie.test.ts +++ b/api/tests/series/seed-serie.test.ts @@ -104,4 +104,60 @@ describe("Serie seeding", () => { ], }); }); + + it("Can create a serie with quotes", async () => { + const [resp, body] = await createSerie({ + ...madeInAbyss, + slug: "quote-test", + seasons: [ + { + ...madeInAbyss.seasons[0], + translations: { + en: { + ...madeInAbyss.seasons[0].translations.en, + name: "Season'1", + }, + }, + }, + { + ...madeInAbyss.seasons[1], + translations: { + en: { + ...madeInAbyss.seasons[0].translations.en, + name: 'Season"2', + description: `This's """""quote, idk'''''`, + }, + }, + }, + ], + }); + + expectStatus(resp, body).toBe(201); + expect(body.id).toBeString(); + expect(body.slug).toBe("quote-test"); + + const ret = await db.query.shows.findFirst({ + where: eq(shows.id, body.id), + with: { + seasons: { + orderBy: seasons.seasonNumber, + with: { translations: true }, + }, + entries: { + with: { + translations: true, + evj: { with: { video: true } }, + }, + }, + }, + }); + + expect(ret).not.toBeNull(); + expect(ret!.seasons).toBeArrayOfSize(2); + expect(ret!.seasons[0].translations[0].name).toBe("Season'1"); + expect(ret!.seasons[1].translations[0].name).toBe('Season"2'); + expect(ret!.entries).toBeArrayOfSize( + madeInAbyss.entries.length + madeInAbyss.extras.length, + ); + }); }); diff --git a/api/tsconfig.json b/api/tsconfig.json index 6926c9d1..67912b95 100644 --- a/api/tsconfig.json +++ b/api/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ES2021", + "target": "ES2022", "module": "ES2022", "moduleResolution": "node", "esModuleInterop": true,