feat: add Child Safety Standards page for Google Play compliance

New public page at /kinderschutz — required by Google Play's Child
Safety Standards policy for apps with user-generated content/social
features (https://support.google.com/googleplay/android-developer/answer/14747720).
Registered as public + maintenance-exempt in proxy.ts, matching the
existing /impressum, /datenschutz, /nutzungsbedingungen pattern.

Content covers only mechanisms that actually exist in this codebase:
in-app reporting (ReportSheet + admin moderation review), a named
responsible contact (same identity as /impressum), and the German/EU
legal framework (StGB §184b/c, DSA, JuSchG) for law-enforcement
cooperation on confirmed CSAE content — no invented automated-
detection claims.

Cross-linked from the other legal pages' footers, the web /pets
footer, and the mobile app's My Pets legal-links section (new
pets.legalChildSafety i18n key, DE/EN).
This commit is contained in:
admin committed 2026-09-07 18:57:49 +02:00
1 parent 554b7cacdc
commit 4758ce8d87
9 files changed
+166 -1

No files matched your search

+1
View File
@@ -213,6 +213,7 @@ function LegalSection() {
{ label: t("pets.legalImpressum"), path: "impressum" },
{ label: t("pets.legalPrivacy"), path: "datenschutz" },
{ label: t("pets.legalTerms"), path: "nutzungsbedingungen" },
{ label: t("pets.legalChildSafety"), path: "kinderschutz" },
];
return (
+1
View File
@@ -200,6 +200,7 @@
"legalImpressum": "Impressum",
"legalPrivacy": "Datenschutzerklärung",
"legalTerms": "Nutzungsbedingungen",
"legalChildSafety": "Kinderschutz",
"versionLabel": "Version {{version}}"
},
"auth": {
+1
View File
@@ -200,6 +200,7 @@
"legalImpressum": "Legal Notice",
"legalPrivacy": "Privacy Policy",
"legalTerms": "Terms of Service",
"legalChildSafety": "Child Safety Standards",
"versionLabel": "Version {{version}}"
},
"auth": {
+2
View File
@@ -45,6 +45,8 @@ export default async function PetsPage({
<span aria-hidden="true">·</span>
<Link href="/nutzungsbedingungen" className="hover:text-foreground transition-colors">AGB</Link>
<span aria-hidden="true">·</span>
<Link href="/kinderschutz" className="hover:text-foreground transition-colors">Kinderschutz</Link>
<span aria-hidden="true">·</span>
<DeleteAccountDialog
petNames={pets.map((p) => p.name)}
defaultOpen={deleteAccount === "1"}
+3
View File
@@ -247,6 +247,9 @@ export default function DatenschutzPage() {
<Link href="/nutzungsbedingungen" className="text-sm text-orange-600 dark:text-orange-400 hover:underline">
AGB
</Link>
<Link href="/kinderschutz" className="text-sm text-orange-600 dark:text-orange-400 hover:underline">
Kinderschutz
</Link>
</div>
</div>
</LegalLayout>
+3
View File
@@ -108,6 +108,9 @@ export default function ImpressumPage() {
<Link href="/nutzungsbedingungen" className="text-sm text-orange-600 dark:text-orange-400 hover:underline">
AGB
</Link>
<Link href="/kinderschutz" className="text-sm text-orange-600 dark:text-orange-400 hover:underline">
Kinderschutz
</Link>
</div>
</div>
</LegalLayout>
+150
View File
@@ -0,0 +1,150 @@
"use client";
import { useEffect, useState } from "react";
import { LegalLayout, LegalSection } from "@/components/legal/LegalLayout";
function decode(b64: string): string {
try {
return atob(b64);
} catch {
return "";
}
}
// Same operator identity as /impressum — base64-obfuscated against
// scraper harvesting, not against a determined reader.
const CONTACT_NAME = "RGFuaWVsIEJhcmFj";
const CONTACT_EMAIL = "d2VibWFzdGVyQHBhd2ZlZWQub3Jn";
/**
* Google Play "Child Safety Standards" policy page — required for apps
* with user-generated content / social features:
* https://support.google.com/googleplay/android-developer/answer/14747720
*
* Publicly accessible, no login (see proxy.ts's isPublicRoute /
* MAINTENANCE_EXEMPT_PATHS), and the URL this page lives at must be
* entered in Play Console → App content → Child Safety Standards.
*
* Describes only mechanisms that actually exist in this codebase (user
* reports via ReportSheet + admin ModerationLog review) — no invented
* automated-detection claims (no PhotoDNA/hash-matching is implemented).
*/
export default function KinderschutzPage() {
const [revealed, setRevealed] = useState(false);
useEffect(() => setRevealed(true), []);
const contactName = revealed ? decode(CONTACT_NAME) : "––";
const contactEmail = revealed ? decode(CONTACT_EMAIL) : "––";
return (
<LegalLayout
title="Kinderschutz-Standards"
subtitle="Child Safety Standards gemäß den Google Play-Richtlinien"
>
<LegalSection number="1" title="Unser Engagement">
<p>
PawFeed toleriert keine Inhalte, die Kinder sexuell ausbeuten oder
missbrauchen (Child Sexual Abuse and Exploitation, „CSAE"), und
keine Inhalte oder Verhaltensweisen, die Kinder auf andere Weise
gefährden. Das gilt für Beiträge, Storys, Profilbilder,
Kommentare und private Nachrichten gleichermaßen.
</p>
</LegalSection>
<LegalSection number="2" title="Melden von Inhalten in der App">
<p>
Jeder Beitrag und jedes Profil lässt sich direkt in der App über
die Melde-Funktion (⋮-Menü) an unser Moderationsteam melden —
unter anderem mit den Gründen „Unangemessene Inhalte" und
„Belästigung oder Mobbing". Meldungen sind sofort nach dem
Absenden für die Moderation sichtbar, unabhängig davon, ob die
gemeldete Person das mitbekommt.
</p>
<p>
Vermutest du sexualisierte Gewalt gegen Kinder oder eine akute
Gefährdung, melde den Inhalt zusätzlich direkt per E-Mail an{" "}
{revealed ? (
<a
href={`mailto:${contactEmail}?subject=Kinderschutz-Meldung`}
className="text-orange-600 dark:text-orange-400 hover:underline"
>
{contactEmail}
</a>
) : (
"––"
)}{" "}
— so erreichst du uns auch, falls die In-App-Meldung aus
technischen Gründen nicht möglich ist.
</p>
</LegalSection>
<LegalSection number="3" title="Prüfung und Maßnahmen">
<p>
Meldungen werden von unserem Moderationsteam geprüft und in einem
Prüfprotokoll dokumentiert. Bestätigt sich ein Verstoß, entfernen
wir den Inhalt umgehend und sperren das verantwortliche
Tier-/Nutzerkonto dauerhaft. Bei Verdacht auf strafbare Inhalte
nach § 184b, § 184c StGB (Kinderpornografische bzw.
jugendpornografische Inhalte) sichern wir die relevanten Daten im
gesetzlich zulässigen Rahmen für Ermittlungsbehörden, statt sie
eigenständig weiterzuverbreiten oder Dritten zugänglich zu
machen.
</p>
</LegalSection>
<LegalSection number="4" title="Zusammenarbeit mit Behörden">
<p>
Bei bestätigten oder hinreichend begründeten Verdachtsfällen von
CSAE-Inhalten erstatten wir Anzeige bei den zuständigen deutschen
Ermittlungsbehörden (z. B. beim Bundeskriminalamt) und/oder
melden den Fall an eine anerkannte Meldestelle des
INHOPE-Netzwerks (z. B. die Internet-Beschwerdestelle von eco/FSM,{" "}
<a
href="https://www.internet-beschwerdestelle.de/"
target="_blank"
rel="noopener noreferrer"
className="text-orange-600 dark:text-orange-400 hover:underline"
>
internet-beschwerdestelle.de
</a>
). Wir kommen gesetzlichen Auskunfts- und Meldepflichten nach dem
Digital Services Act (Verordnung (EU) 2022/2065) und dem
Jugendschutzgesetz (JuSchG) nach.
</p>
</LegalSection>
<LegalSection number="5" title="Verantwortliche Kontaktperson">
<address className="not-italic space-y-0.5">
<p className="font-medium text-foreground">{contactName}</p>
<p>Verantwortlich für Kinderschutz-Meldungen und -Maßnahmen</p>
<p>
E-Mail:{" "}
{revealed ? (
<a
href={`mailto:${contactEmail}`}
className="text-orange-600 dark:text-orange-400 hover:underline"
>
{contactEmail}
</a>
) : (
"––"
)}
</p>
</address>
</LegalSection>
<LegalSection number="6" title="Altersvoraussetzung">
<p>
PawFeed richtet sich an Personen, die geschäftsfähig sind bzw.
über die Einwilligung ihrer Erziehungsberechtigten verfügen
(siehe unsere{" "}
<a href="/nutzungsbedingungen" className="text-orange-600 dark:text-orange-400 hover:underline">
Nutzungsbedingungen
</a>
). Die App ist nicht gezielt für Kinder unter 13 Jahren
konzipiert und bewirbt sich nicht an diese Zielgruppe.
</p>
</LegalSection>
</LegalLayout>
);
}
+3
View File
@@ -225,6 +225,9 @@ export default function NutzungsbedingungenPage() {
<Link href="/datenschutz" className="text-sm text-orange-600 dark:text-orange-400 hover:underline">
Datenschutz
</Link>
<Link href="/kinderschutz" className="text-sm text-orange-600 dark:text-orange-400 hover:underline">
Kinderschutz
</Link>
</div>
</div>
</LegalLayout>
+2 -1
View File
@@ -3,7 +3,7 @@ import { NextResponse } from "next/server";
import { timingSafeStringEqual } from "@/lib/timing-safe-compare";
import { isMaintenanceModeEnabled } from "@/lib/maintenance";
const MAINTENANCE_EXEMPT_PATHS = new Set(["/impressum", "/datenschutz", "/nutzungsbedingungen", "/ueber-uns"]);
const MAINTENANCE_EXEMPT_PATHS = new Set(["/impressum", "/datenschutz", "/nutzungsbedingungen", "/ueber-uns", "/kinderschutz"]);
const MAINTENANCE_HTML = `<!doctype html>
<html lang="de"><head><meta charset="utf-8" />
@@ -26,6 +26,7 @@ const isPublicRoute = createRouteMatcher([
"/datenschutz",
"/nutzungsbedingungen",
"/ueber-uns",
"/kinderschutz",
"/join(.*)",
"/api/webhooks/(.*)",
"/api/auth/(.*)",