diff --git a/.ocauto/verify b/.ocauto/verify index 0cbad7b..01cb85e 100755 --- a/.ocauto/verify +++ b/.ocauto/verify @@ -91,10 +91,13 @@ fi printf '\n== routes\n' for route in / /fotografie/ /businessfotografie/ /portrait-und-model/ /familie/ \ - /minishootings/ /video/ /musik-und-buehne/ /social-media/ /projekte/ /ueber/ /kontakt/ /impressum/ /datenschutz/ /gtin/; do + /minishootings/ /video/ /musik-und-buehne/ /social-media/ /projekte/ /ueber/ /kontakt/ /impressum/ /datenschutz/ \ + /gtin/ /gtin/impressum/ /gtin/datenschutz/; do check_status "$base$route" 200 done check_redirect "$base/gtin" "$base/gtin/" +check_redirect "$base/gtin/impressum" "$base/gtin/impressum/" +check_redirect "$base/gtin/datenschutz" "$base/gtin/datenschutz/" # The retired photo URL must keep working: it is the umbrella page now. check_redirect "$base/shootings" "$base/fotografie/" @@ -140,13 +143,19 @@ check_contains "$base/musik-und-buehne/" "Sagenbringer" check_contains "$base/impressum/" "§ 5 DDG" check_contains "$base/impressum/" "DE462149560" check_contains "$base/datenschutz/" "keine Zugriffsprotokolle" -check_contains "$base/datenschutz/" "bis zu 20" -check_contains "$base/impressum/" "GTIN-Tool" +check_not_contains "$base/datenschutz/" "GTIN" +check_not_contains "$base/impressum/" "GTIN" check_contains "$base/gtin/" 'name="robots" content="noindex, nofollow"' check_not_contains "$base/" 'href="/gtin' check_not_contains "$base/sitemap.xml" "/gtin/" -check_contains "$base/gtin/" 'href="/impressum/"' -check_contains "$base/gtin/" 'href="/datenschutz/"' +check_contains "$base/gtin/" 'href="/gtin/impressum/"' +check_contains "$base/gtin/" 'href="/gtin/datenschutz/"' +check_contains "$base/gtin/impressum/" "§ 5 DDG" +check_contains "$base/gtin/impressum/" "DE462149560" +check_contains "$base/gtin/datenschutz/" "Cache API" +check_contains "$base/gtin/datenschutz/" "nicht an einen Server übertragen" +check_contains "$base/gtin/datenschutz/" 'name="robots" content="noindex, nofollow"' +check_contains "$base/gtin/impressum/" 'name="robots" content="noindex, nofollow"' for route in / /fotografie/ /businessfotografie/ /portrait-und-model/ /familie/ \ /minishootings/ /video/ /musik-und-buehne/ /social-media/ /projekte/ /ueber/ \ /kontakt/ /impressum/ /datenschutz/; do diff --git a/AGENTS.md b/AGENTS.md index ee33f33..00f194e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -92,9 +92,10 @@ them is a defect, not a style question: - no cookies, analytics or tracking. The standalone `/gtin/` tool is the only `localStorage`/Cache API exception: it keeps its last 20 generated entries in the browser and caches only its own offline app shell; it sends no tool data - to a server. The tool stays unlisted: no site navigation or page links, no - sitemap entry, and `noindex,nofollow`; only the tool itself links out to the - required legal pages; + to a server. Its `/gtin/impressum/` and `/gtin/datenschutz/` pages are + standalone, unlisted legal pages. The tool stays unlisted: no main-site + navigation or page links, no sitemap entry, and `noindex,nofollow`; only the + tool links to its own legal pages, which never link back to the main site; - `access_log off` in `nginx.conf` — never enable request logging; - `error_log /dev/null crit;` in `nginx.conf` — never write an error-log file either; - fonts, scripts, styles and images are served from this origin only; diff --git a/README.md b/README.md index 358ca54..e1871c1 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,8 @@ Video · Social Media · Projekte · Über · Kontakt · Impressum · Datenschut Der GTIN-Generator unter `/gtin/` ist ein separates, öffentlich direkt aufrufbares Werkzeug. Er bleibt absichtlich unverlinkt, fehlt in der Sitemap und trägt `noindex,nofollow`; nur das -Werkzeug selbst verlinkt auf Impressum und Datenschutz. +Werkzeug selbst verlinkt auf seine eigenständigen Rechtstexte unter `/gtin/impressum/` und +`/gtin/datenschutz/`. Diese Seiten verwenden keine Navigation oder Rechtstexte des Hauptauftritts. ## Technische Entscheidungen @@ -66,7 +67,8 @@ Werkzeug selbst verlinkt auf Impressum und Datenschutz. - **GTIN-Generator.** Der Generator unter `/gtin/` rechnet vollständig im Browser, ohne Konto, externe API oder Serverübertragung. Er speichert bis zu 20 zuletzt erzeugte Einträge lokal im Browser; der Offline-Appcache enthält nur die - Anwendungsdateien. Beide Funktionen sind in der Datenschutzerklärung beschrieben. + Anwendungsdateien. Die eigenständige Datenschutzerklärung liegt unter + `/gtin/datenschutz/`; die Rechtstexte sind nicht mit denen des Hauptauftritts verknüpft. ## Herkunft der Inhalte diff --git a/nginx.conf b/nginx.conf index c7dc657..167af91 100644 --- a/nginx.conf +++ b/nginx.conf @@ -65,6 +65,8 @@ server { location = /datenschutz.html { return 301 /datenschutz/; } location = /kontakt.html { return 301 /kontakt/; } location = /gtin { return 301 /gtin/; } + location = /gtin/impressum { return 301 /gtin/impressum/; } + location = /gtin/datenschutz { return 301 /gtin/datenschutz/; } location = /gtin/manifest.webmanifest { default_type application/manifest+json; try_files $uri =404; diff --git a/scripts/build-site.py b/scripts/build-site.py index 1308809..d49de3e 100755 --- a/scripts/build-site.py +++ b/scripts/build-site.py @@ -40,7 +40,11 @@ from theme import CSS, TOKENS # noqa: E402 WIDTHS = (480, 768, 1200, 1600) WEBP_QUALITY = 84 -UNLISTED_HTML = ["gtin/index.html"] +UNLISTED_HTML = [ + "gtin/index.html", + "gtin/impressum/index.html", + "gtin/datenschutz/index.html", +] INK = (23, 20, 15) PAPER = (246, 242, 234) @@ -281,7 +285,7 @@ def build_html(table: dict[str, dict]) -> list[tuple[str, str]]: print(f" [html] {rel}") shutil.rmtree(SITE / "gtin", ignore_errors=True) shutil.copytree(SRC / "gtin", SITE / "gtin") - print(" [html] gtin/index.html (unlisted browser tool)") + print(" [html] gtin/ (unlisted browser tool and standalone legal pages)") not_found = pages.not_found().replace("og-desfoto.jpg", "og-desfoto.jpg") (SITE / "404.html").write_text(not_found, encoding="utf-8") print(" [html] 404.html") @@ -531,6 +535,7 @@ def link_check(routes: list[tuple[str, str]]) -> list[str]: problems: list[str] = [] built = {f"/{rel}".replace("/index.html", "/") for _p, rel in routes} + built.update(f"/{rel}".replace("/index.html", "/") for rel in UNLISTED_HTML) built.add("/404.html") for rel in ["sitemap.xml", "robots.txt", "site.webmanifest", "favicon.svg", ".well-known/security.txt", "assets/site.css", "assets/site.js", "assets/fonts.css"]: diff --git a/site/datenschutz/index.html b/site/datenschutz/index.html index e4f813e..efbbd6d 100644 --- a/site/datenschutz/index.html +++ b/site/datenschutz/index.html @@ -101,11 +101,8 @@

5. Cookies und lokale Speicherung

Diese Website setzt keine Cookies und verwendet keine Analyse-, Marketing- oder - Tracking-Technologien. Das browserbasierte GTIN-Tool speichert die letzten bis zu 20 - Artikelnummern und erzeugten GTINs im lokalen Speicher deines Browsers, damit du sie dort erneut - aufrufen oder den Verlauf löschen kannst. Die Einträge werden nicht an einen Server übertragen. - Für den Offlinebetrieb speichert dein Browser außerdem die Anwendungsdateien im Cache. Beides - kannst du über das Tool beziehungsweise die Browser-Einstellungen löschen.

+ Tracking-Technologien. Auf den Seiten dieses Angebots werden keine Informationen dauerhaft im + Browser gespeichert.

6. Schriftarten und Bilder

Die verwendeten Schriftarten werden von diesem Server ausgeliefert und sind lokal eingebunden. diff --git a/site/gtin/datenschutz/index.html b/site/gtin/datenschutz/index.html new file mode 100644 index 0000000..9e211ae --- /dev/null +++ b/site/gtin/datenschutz/index.html @@ -0,0 +1,54 @@ + + + + + + Datenschutz + + + + + +

+

Datenschutz

+

Stand: September 2026

+ +

Verantwortlicher

+

Denny Schulz Fotografie, Inhaber Denny Schulz
+ Flensburger Str. 16, 24537 Neumünster, Deutschland
+ info@dennyschulz.de

+ +

Aufruf und Nutzung

+

Beim Aufruf verarbeitet der selbst betriebene Server deine IP-Adresse kurzzeitig, um die + angeforderte Seite auszuliefern. Zugriffsprotokolle werden nicht gespeichert. Rechtsgrundlage + ist Art. 6 Abs. 1 lit. f DSGVO (sicherer und funktionsfähiger Betrieb).

+

Deine Eingaben und erzeugten GTINs werden ausschließlich in deinem Browser verarbeitet und + nicht an einen Server übertragen. Der Browser speichert bis zu 20 Verlaufseinträge im + lokalen Speicher und Anwendungsdateien für den Offlinebetrieb in der Cache API. Den Verlauf + kannst du im Werkzeug löschen; den Offline-Cache kannst du in den Browser-Einstellungen + entfernen. Es werden keine Cookies, Analyse- oder Trackingdienste eingesetzt und keine + externen Inhalte geladen.

+ +

Deine Rechte

+

Du hast im gesetzlichen Umfang das Recht auf Auskunft, Berichtigung, Löschung, + Einschränkung der Verarbeitung, Widerspruch und Datenübertragbarkeit. Außerdem kannst du + dich bei der zuständigen Datenschutzaufsicht beschweren: Unabhängiges Landeszentrum für + Datenschutz Schleswig-Holstein, Holstenstraße 98, 24103 Kiel. Für Fragen oder zur Ausübung + deiner Rechte erreichst du den Verantwortlichen unter + info@dennyschulz.de.

+
+ + + diff --git a/site/gtin/impressum/index.html b/site/gtin/impressum/index.html new file mode 100644 index 0000000..a6c8af6 --- /dev/null +++ b/site/gtin/impressum/index.html @@ -0,0 +1,43 @@ + + + + + + Impressum + + + + + +
+

Impressum

+

Angaben gemäß § 5 DDG für das GTIN-Werkzeug.

+ +

Anbieter

+

Denny Schulz Fotografie
+ Inhaber: Denny Schulz
+ Flensburger Str. 16
+ 24537 Neumünster
+ Deutschland

+ +

Kontakt

+

E-Mail: info@dennyschulz.de
+ Telefon: +49 175 4083133

+ +

Wirtschafts-Identifikationsnummer: DE462149560

+
+ + + diff --git a/site/gtin/index.html b/site/gtin/index.html index cc384ce..7b2c148 100644 --- a/site/gtin/index.html +++ b/site/gtin/index.html @@ -85,11 +85,10 @@

Eingaben und Verlauf bleiben in deinem Browser. - Datenschutz

diff --git a/site/impressum/index.html b/site/impressum/index.html index 47887b2..c46ce35 100644 --- a/site/impressum/index.html +++ b/site/impressum/index.html @@ -85,8 +85,7 @@

Marke und Angebot

desfoto ist der Markenauftritt von Denny Schulz Fotografie. Alle über diese - Website erreichbaren Leistungen werden von Denny Schulz Fotografie erbracht. Das kostenlose - GTIN-Tool ist ein browserbasiertes Zusatzwerkzeug desselben Anbieters.

+ Website erreichbaren Leistungen werden von Denny Schulz Fotografie erbracht.

Urheberrecht und Bildrechte

Alle auf dieser Website gezeigten Fotografien und Filme sind urheberrechtlich geschützt. diff --git a/src/gtin/datenschutz/index.html b/src/gtin/datenschutz/index.html new file mode 100644 index 0000000..9e211ae --- /dev/null +++ b/src/gtin/datenschutz/index.html @@ -0,0 +1,54 @@ + + + + + + Datenschutz + + + + + +

+

Datenschutz

+

Stand: September 2026

+ +

Verantwortlicher

+

Denny Schulz Fotografie, Inhaber Denny Schulz
+ Flensburger Str. 16, 24537 Neumünster, Deutschland
+ info@dennyschulz.de

+ +

Aufruf und Nutzung

+

Beim Aufruf verarbeitet der selbst betriebene Server deine IP-Adresse kurzzeitig, um die + angeforderte Seite auszuliefern. Zugriffsprotokolle werden nicht gespeichert. Rechtsgrundlage + ist Art. 6 Abs. 1 lit. f DSGVO (sicherer und funktionsfähiger Betrieb).

+

Deine Eingaben und erzeugten GTINs werden ausschließlich in deinem Browser verarbeitet und + nicht an einen Server übertragen. Der Browser speichert bis zu 20 Verlaufseinträge im + lokalen Speicher und Anwendungsdateien für den Offlinebetrieb in der Cache API. Den Verlauf + kannst du im Werkzeug löschen; den Offline-Cache kannst du in den Browser-Einstellungen + entfernen. Es werden keine Cookies, Analyse- oder Trackingdienste eingesetzt und keine + externen Inhalte geladen.

+ +

Deine Rechte

+

Du hast im gesetzlichen Umfang das Recht auf Auskunft, Berichtigung, Löschung, + Einschränkung der Verarbeitung, Widerspruch und Datenübertragbarkeit. Außerdem kannst du + dich bei der zuständigen Datenschutzaufsicht beschweren: Unabhängiges Landeszentrum für + Datenschutz Schleswig-Holstein, Holstenstraße 98, 24103 Kiel. Für Fragen oder zur Ausübung + deiner Rechte erreichst du den Verantwortlichen unter + info@dennyschulz.de.

+
+ + + diff --git a/src/gtin/impressum/index.html b/src/gtin/impressum/index.html new file mode 100644 index 0000000..a6c8af6 --- /dev/null +++ b/src/gtin/impressum/index.html @@ -0,0 +1,43 @@ + + + + + + Impressum + + + + + +
+

Impressum

+

Angaben gemäß § 5 DDG für das GTIN-Werkzeug.

+ +

Anbieter

+

Denny Schulz Fotografie
+ Inhaber: Denny Schulz
+ Flensburger Str. 16
+ 24537 Neumünster
+ Deutschland

+ +

Kontakt

+

E-Mail: info@dennyschulz.de
+ Telefon: +49 175 4083133

+ +

Wirtschafts-Identifikationsnummer: DE462149560

+
+ + + diff --git a/src/gtin/index.html b/src/gtin/index.html index cc384ce..7b2c148 100644 --- a/src/gtin/index.html +++ b/src/gtin/index.html @@ -85,11 +85,10 @@

Eingaben und Verlauf bleiben in deinem Browser. - Datenschutz

diff --git a/src/pages.py b/src/pages.py index a3f1c66..a42c63c 100644 --- a/src/pages.py +++ b/src/pages.py @@ -1749,8 +1749,7 @@ def impressum() -> str:

Marke und Angebot

desfoto ist der Markenauftritt von {e(LEGAL['business'])}. Alle über diese - Website erreichbaren Leistungen werden von {e(LEGAL['business'])} erbracht. Das kostenlose - GTIN-Tool ist ein browserbasiertes Zusatzwerkzeug desselben Anbieters.

+ Website erreichbaren Leistungen werden von {e(LEGAL['business'])} erbracht.

Urheberrecht und Bildrechte

Alle auf dieser Website gezeigten Fotografien und Filme sind urheberrechtlich geschützt. @@ -1830,11 +1829,8 @@ def datenschutz() -> str:

5. Cookies und lokale Speicherung

Diese Website setzt keine Cookies und verwendet keine Analyse-, Marketing- oder - Tracking-Technologien. Das browserbasierte GTIN-Tool speichert die letzten bis zu 20 - Artikelnummern und erzeugten GTINs im lokalen Speicher deines Browsers, damit du sie dort erneut - aufrufen oder den Verlauf löschen kannst. Die Einträge werden nicht an einen Server übertragen. - Für den Offlinebetrieb speichert dein Browser außerdem die Anwendungsdateien im Cache. Beides - kannst du über das Tool beziehungsweise die Browser-Einstellungen löschen.

+ Tracking-Technologien. Auf den Seiten dieses Angebots werden keine Informationen dauerhaft im + Browser gespeichert.

6. Schriftarten und Bilder

Die verwendeten Schriftarten werden von diesem Server ausgeliefert und sind lokal eingebunden. diff --git a/tests/test_site.py b/tests/test_site.py index d31d70c..ad82178 100644 --- a/tests/test_site.py +++ b/tests/test_site.py @@ -28,6 +28,8 @@ from content import CONTACT, LEGAL, SITE as META # noqa: E402 ROUTE_PATHS = [route[0] for route in pages.ROUTES] HTML_FILES = [SITE / route[1] for route in pages.ROUTES] + [SITE / "404.html"] GTIN_HTML = SITE / "gtin" / "index.html" +GTIN_IMPRESSUM = SITE / "gtin" / "impressum" / "index.html" +GTIN_DATENSCHUTZ = SITE / "gtin" / "datenschutz" / "index.html" def read(path: Path) -> str: @@ -49,7 +51,12 @@ class BuildLayout(unittest.TestCase): self.assertTrue((SITE / rel).is_file(), f"missing {rel}") def test_no_stray_html_files(self): - expected = {route[1] for route in pages.ROUTES} | {"404.html", "gtin/index.html"} + expected = {route[1] for route in pages.ROUTES} | { + "404.html", + "gtin/index.html", + "gtin/impressum/index.html", + "gtin/datenschutz/index.html", + } actual = {str(f.relative_to(SITE)) for f in SITE.rglob("*.html")} self.assertEqual(expected, actual) @@ -215,9 +222,6 @@ class PrivacyAndLegal(unittest.TestCase): for needle in ( "keine Zugriffsprotokolle", "keine Cookies", - "GTIN-Tool", - "bis zu 20", - "nicht an einen Server übertragen", "youtube-nocookie.com", "Art. 6 Abs. 1 lit. f DSGVO", LEGAL["authority"], @@ -323,13 +327,52 @@ class Content(unittest.TestCase): self.assertIn("GTIN Generator", text) self.assertIn('', text) self.assertIn('', text) - self.assertIn('href="/impressum/"', text) - self.assertIn('href="/datenschutz/"', text) + self.assertIn('href="/gtin/impressum/"', text) + self.assertIn('href="/gtin/datenschutz/"', text) + self.assertEqual(len(re.findall(r'', text)), 1) + self.assertNotIn('href="/impressum/"', text) + self.assertNotIn('href="/datenschutz/"', text) self.assertNotIn('href="/"', text) self.assertNotIn("/gtin/", read(SITE / "sitemap.xml")) for file in HTML_FILES: with self.subTest(file=str(file.relative_to(SITE))): self.assertNotIn("/gtin", read(file)) + self.assertNotIn("GTIN", read(file)) + + def test_gtin_legal_pages_are_standalone_and_unlisted(self): + impressum = read(GTIN_IMPRESSUM) + datenschutz = read(GTIN_DATENSCHUTZ) + for path, text in ( + ("/gtin/impressum/", impressum), + ("/gtin/datenschutz/", datenschutz), + ): + with self.subTest(path=path): + self.assertIn(f'', text) + self.assertIn('', text) + self.assertNotIn('href="/impressum/"', text) + self.assertNotIn('href="/datenschutz/"', text) + self.assertNotIn('href="/"', text) + self.assertNotIn("assets/", text) + for value in ( + LEGAL["business"], LEGAL["owner"], LEGAL["street"], LEGAL["city"], + CONTACT["email"], CONTACT["phone"], "§ 5 DDG", LEGAL["w_id"], + ): + with self.subTest(impressum=value): + self.assertIn(value, visible_text(impressum)) + for value in ( + LEGAL["owner"], CONTACT["email"], "bis zu 20", "Cache API", + "nicht an einen Server übertragen", "keine Cookies", "Art. 6 Abs. 1 lit. f DSGVO", + LEGAL["authority"], + ): + with self.subTest(datenschutz=value): + self.assertIn(value, visible_text(datenschutz)) + self.assertIn('href="/gtin/datenschutz/"', impressum) + self.assertIn('href="/gtin/impressum/"', datenschutz) + + def test_main_legal_pages_do_not_name_the_gtin_tool(self): + for path in (SITE / "impressum" / "index.html", SITE / "datenschutz" / "index.html"): + with self.subTest(path=str(path.relative_to(SITE))): + self.assertNotIn("GTIN", visible_text(read(path))) def test_gtin_assets_and_local_data_storage_are_scoped(self): html_text = read(GTIN_HTML)