feat: REST-API unter /api/v1 mit OpenAPI-Beschreibung
Dieselben Endpunkte bedienen Skripte und das eigene Frontend: erst wird ein Bearer-Token geprüft, sonst das Sitzungscookie. Sichtungsaktionen geben immer watchIds zurück — damit lässt sich genau diese Aktion über DELETE zurücknehmen, ohne raten zu müssen, was dazugehörte. Die OpenAPI-Beschreibung ist handgeschrieben statt aus Zod erzeugt: die Schnittstelle ist überschaubar, und ein Generator brächte eine weitere Abhängigkeit für einen Nutzen, den ein Dutzend Zeilen JSON auch hergeben.
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import { apiError, withApi } from "@/lib/api";
|
||||
import { env } from "@/lib/env";
|
||||
import { searchMovies, searchMulti, searchSeries, TmdbNotConfiguredError } from "@/lib/tmdb";
|
||||
|
||||
/**
|
||||
* GET /api/v1/suche?q=…&typ=serie|film
|
||||
*
|
||||
* Durchsucht TMDB. Die Treffer tragen die TMDB-ID — damit lässt sich der
|
||||
* Titel über POST /api/v1/bibliothek direkt aufnehmen.
|
||||
*/
|
||||
export const GET = withApi(async (request) => {
|
||||
const url = new URL(request.url);
|
||||
const query = (url.searchParams.get("q") ?? "").trim();
|
||||
const type = url.searchParams.get("typ");
|
||||
|
||||
if (query.length < 2) {
|
||||
return apiError("Der Suchbegriff braucht mindestens zwei Zeichen.", 400);
|
||||
}
|
||||
if (!env.tmdb.isConfigured) {
|
||||
return apiError("Auf dieser Instanz ist kein TMDB-Schlüssel hinterlegt.", 503);
|
||||
}
|
||||
|
||||
try {
|
||||
const data =
|
||||
type === "serie"
|
||||
? await searchSeries(query)
|
||||
: type === "film"
|
||||
? await searchMovies(query)
|
||||
: await searchMulti(query);
|
||||
|
||||
return {
|
||||
query,
|
||||
total: data.total_results,
|
||||
items: data.results.map((item) => {
|
||||
const isSeries =
|
||||
item.media_type === "tv" || (type === "serie" && item.media_type === undefined);
|
||||
const date = isSeries ? item.first_air_date : item.release_date;
|
||||
return {
|
||||
tmdbId: item.id,
|
||||
type: isSeries ? "SERIES" : "MOVIE",
|
||||
title: item.name ?? item.title ?? "Ohne Titel",
|
||||
overview: item.overview ?? null,
|
||||
posterPath: item.poster_path ?? null,
|
||||
year: date ? Number.parseInt(date.slice(0, 4), 10) : null,
|
||||
voteAverage: item.vote_average ?? null,
|
||||
};
|
||||
}),
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof TmdbNotConfiguredError) return apiError(error.message, 503);
|
||||
return apiError(error instanceof Error ? error.message : "Die Suche ist fehlgeschlagen.", 502);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user