Files
qrcode-generator/src/app/s/[...slug]/page.tsx
T
Florian Wilde eeaf3820b7 Statische Codes benennen und auf Wunsch ablegen
Das Feld „Name (optional)" stand nur im Dynamisch-Block. Bei einem statischen
Code gab es damit keine Moeglichkeit, ihm einen Namen zum Wiederfinden zu
geben.

Jetzt gilt es in beiden Betriebsarten und tut zweierlei: Es bestimmt den
Dateinamen beim Herunterladen — aus „Plakat Eingang" wird
qr-plakat-eingang.png statt qr-example-com-plakat.png — und es laesst sich
damit ein statischer Code in „Meine Codes" ablegen.

Ein abgelegter statischer Code ist ein Schnappschuss, kein steuerbares Ziel.
Aenderbar ist nur der Name; Inhalt und Gestaltung bleiben, wie sie beim
Ablegen waren, denn genau das steckt im gedruckten Bild. Er bekommt keinen
Kurz-Link, ist unter /r nicht erreichbar und zaehlt keine Scans — in der
Liste steht dort „statisch" und ein Strich. Wer nach dem Ablegen den Inhalt
aendert, bekommt das Ablegen erneut angeboten; der vorherige Eintrag bleibt
als eigener Schnappschuss.

Dafuer traegt QrCode jetzt `mode`, und `slug` darf leer sein. In PostgreSQL
schliessen sich mehrere NULL in einem eindeutigen Index nicht aus, die
Bedingung „je Domain eindeutig" gilt fuer dynamische Codes also unveraendert.

Refs #2
2026-08-20 19:33:49 +02:00

114 lines
3.6 KiB
TypeScript

import type { Metadata } from "next";
import { headers } from "next/headers";
import { qrLandings } from "@/components/landing";
import { LandingNotice } from "@/components/landing/shell";
import { ensureDefaultDomain, resolveRequestDomain } from "@/lib/domains";
import { getQrType } from "@/lib/qr/registry";
import { isValidSlug } from "@/lib/slug";
import { findCodeBySlug } from "@/lib/slug-resolve";
export const dynamic = "force-dynamic";
// Sammelroute, weil ein Kurz-Link „/" enthalten darf.
type PageProps = { params: Promise<{ slug: string[] }> };
/**
* Öffentliche Landingpage eines dynamischen Codes.
*
* Hierher schickt `/r/[slug]` alle Typen, die keine fremde Adresse haben,
* sondern eine Seite: Linksammlungen, hinterlegte Dateien, Feedback.
*
* Der Scan-Zähler wird bewusst **nicht** hier erhöht, sondern in `/r/[slug]`.
* Sonst zählte jedes Neuladen dieser Seite als weiterer Scan.
*/
async function loadCode(abschnitte: string[] | undefined) {
const slug = (abschnitte ?? []).join("/");
if (!isValidSlug(slug)) return null;
const matched = await resolveRequestDomain(await headers());
const domain = matched ?? { ...(await ensureDefaultDomain()), isActive: true };
if (!domain.isActive) return null;
return (await findCodeBySlug(domain.id, slug))?.code ?? null;
}
export async function generateMetadata({
params,
}: PageProps): Promise<Metadata> {
const { slug } = await params;
const code = await loadCode(slug);
const qrType = code ? getQrType(code.type) : undefined;
if (!code || !qrType?.hasLandingPage) {
return { title: "Nicht gefunden" };
}
let title = qrType.title;
try {
title = qrType.summary(code.data);
} catch {
// Daten passen nicht mehr zum Schema — der Typname tut es auch.
}
// Absolut, damit der Titel nicht mit dem Anwendungsnamen ergänzt wird:
// Wer hier landet, hat mit dem Generator nichts zu tun.
return { title: { absolute: title } };
}
export default async function LandingPage({ params }: PageProps) {
const { slug } = await params;
const code = await loadCode(slug);
if (!code) {
return (
<LandingNotice title="Diese Seite gibt es nicht">
Der Code wurde gelöscht oder hat nie existiert. Bitte wende dich an
denjenigen, der ihn erstellt hat.
</LandingNotice>
);
}
const qrType = getQrType(code.type);
const Landing = qrType?.hasLandingPage ? qrLandings[qrType.id] : undefined;
if (!qrType || !Landing) {
return (
<LandingNotice title="Diese Seite gibt es nicht">
Dieser Code führt nicht auf eine Seite. Bitte wende dich an denjenigen,
der ihn erstellt hat.
</LandingNotice>
);
}
// Alte Datensätze können nach einer Schema-Änderung unbrauchbar sein. Das
// ist kein Grund, den Besuchern einen Absturz zu zeigen.
if (!qrType.validate(code.data).ok) {
return (
<LandingNotice title="Diese Seite ist gerade nicht verfügbar">
Die hinterlegten Inhalte sind unvollständig. Bitte wende dich an
denjenigen, der den Code erstellt hat.
</LandingNotice>
);
}
// Gefunden wird ein Code nur ueber seinen Kurz-Link; statische haben keinen
// und koennen hier deshalb gar nicht ankommen. Der Zweig ist reine Vorsorge.
if (!code.slug) {
return (
<LandingNotice title="Diese Seite gibt es nicht">
Dieser Code führt nicht auf eine Seite. Bitte wende dich an denjenigen,
der ihn erstellt hat.
</LandingNotice>
);
}
return (
<Landing
data={code.data}
label={code.label}
slug={code.slug}
codeId={code.id}
/>
);
}