import { and, isNull, sql } from "drizzle-orm"; import { Elysia, t } from "elysia"; import type { NonEmptyArray } from "elysia/dist/type-system/types"; import { auth } from "~/auth"; import { prefix } from "~/base"; import { db } from "~/db"; import { shows } from "~/db/schema"; import { KError } from "~/models/error"; import { Show } from "~/models/show"; import { AcceptLanguage, createPage, Filter, Page, processLanguages, } from "~/models/utils"; import { desc } from "~/models/utils/descriptions"; import { toQueryStr } from "~/utils"; import { collectionRelations, getShows, movieRelations, serieRelations, showFilters, showRelations, showSort, } from "./logic"; export const showsH = new Elysia({ prefix: "/shows", tags: ["shows"] }) .model({ show: Show, }) .use(auth) .get( "random", async ({ status, redirect, query: { preferOriginal, with: relations }, }) => { const [show] = await db .select({ kind: shows.kind, slug: shows.slug }) .from(shows) .orderBy(sql`random()`) .limit(1); if (!show) return status(404, { status: 404, message: "No shows in the database.", }); const availableRelations = { serie: serieRelations, movie: movieRelations, collection: collectionRelations, }; return redirect( `${prefix}/${show.kind}s/${show.slug}${toQueryStr({ preferOriginal, with: relations.filter((x) => x in availableRelations[show.kind]), })}`, ); }, { detail: { description: "Get a random movie/serie/collection", }, query: t.Object({ preferOriginal: t.Optional( t.Boolean({ description: desc.preferOriginal }), ), with: t.Array( t.UnionEnum(Object.keys(showRelations) as NonEmptyArray), { default: [], description: "Include related resources in the response.", }, ), }), response: { 302: t.Void({ description: "Redirected to the appropriate get endpoint.", }), 404: { ...KError, description: "No show in the database.", }, }, }, ) .get( "", async ({ query: { limit, after, query, sort, filter, preferOriginal, ignoreInCollection, }, headers: { "accept-language": languages, ...headers }, request: { url }, jwt: { sub, settings }, }) => { const langs = processLanguages(languages); const items = await getShows({ limit, after, query, sort, filter: and( ignoreInCollection ? isNull(shows.collectionPk) : undefined, filter, ), languages: langs, preferOriginal: preferOriginal ?? settings.preferOriginal, userId: sub, }); return createPage(items, { url, sort, limit, headers }); }, { detail: { description: "Get all movies/series/collections" }, query: t.Object({ sort: showSort, filter: t.Optional(Filter({ def: showFilters })), query: t.Optional(t.String({ description: desc.query })), limit: t.Integer({ minimum: 1, maximum: 250, default: 50, description: "Max page size.", }), after: t.Optional(t.String({ description: desc.after })), preferOriginal: t.Optional( t.Boolean({ description: desc.preferOriginal, }), ), ignoreInCollection: t.Optional( t.Boolean({ description: "If a movie or serie is part of collection, don't return it.", default: true, }), ), }), headers: t.Object( { "accept-language": AcceptLanguage({ autoFallback: true }), }, { additionalProperties: true }, ), response: { 200: Page(Show), 422: KError, }, }, );