Landingpages als eigene Gattung, erster Typ: Mehrere Links
Die sechs noch offenen Typen haben kein fremdes Ziel, auf das man weiterleiten koennte — sie brauchen eine Seite. Dafuer bekommt der Typ-Vertrag `hasLandingPage`: /r/[slug] schickt solche Codes auf /s/[slug], statt `redirectTarget` zu befragen. Die oeffentlichen Seiten duerfen die Navigation der Anwendung nicht zeigen — wer einen Code scannt, hat mit „Verwaltung" nichts zu tun. Deshalb liegt das App-Chrome jetzt in der Routengruppe (app); das Wurzel-Layout traegt nur noch html und body. Die URLs aendern sich dadurch nicht.
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
import type { Metadata } from "next";
|
||||
import { headers } from "next/headers";
|
||||
|
||||
import { qrLandings } from "@/components/landing";
|
||||
import { LandingNotice } from "@/components/landing/shell";
|
||||
import { prisma } from "@/lib/db";
|
||||
import { ensureDefaultDomain, resolveRequestDomain } from "@/lib/domains";
|
||||
import { getQrType } from "@/lib/qr/registry";
|
||||
import { isValidSlug } from "@/lib/slug";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
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(slug: string) {
|
||||
if (!isValidSlug(slug)) return null;
|
||||
|
||||
const matched = await resolveRequestDomain(await headers());
|
||||
const domain = matched ?? { ...(await ensureDefaultDomain()), isActive: true };
|
||||
if (!domain.isActive) return null;
|
||||
|
||||
return prisma.qrCode.findUnique({
|
||||
where: { domainId_slug: { domainId: domain.id, slug } },
|
||||
});
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Landing
|
||||
data={code.data}
|
||||
label={code.label}
|
||||
slug={code.slug}
|
||||
codeId={code.id}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user