import pkg from "../../package.json"; /** * Änderungsverlauf der Anwendung — die einzige Quelle dafür. * * Hieraus entsteht beides: die Seite unter `/version` und die `CHANGELOG.md` * im Wurzelverzeichnis (`npm run changelog`). Zwei gepflegte Listen liefen * unweigerlich auseinander; eine Prüfung wacht darüber, dass die erzeugte * Datei zu dieser Quelle passt. * * Die Einträge bis 1.5.0 sind aus dem Abschnitt „Gebaut“ der Roadmap und der * Git-Historie zusammengetragen: Versionsnummern gab es damals nicht, deshalb * ist je **Erscheinungstag** eine gebildet. Nachträglich vergeben, aber die * Daten selbst stimmen. Ab 1.6.0 trägt jede Fassung einen Git-Tag. */ export type ChangelogEntry = { version: string; /** ISO-Datum, an dem der Stand entstanden ist. */ date: string; /** Eine Zeile zur Einordnung — was diese Version ausmacht. */ summary: string; changes: string[]; }; export const CHANGELOG: readonly ChangelogEntry[] = [ { version: "1.7.1", date: "2026-08-21", summary: "Bilder zu den neuen Funktionen in der Bedienungsanleitung.", changes: [ "Die Bedienungsanleitung zeigt die Funktionen aus 1.7.0 jetzt auch im Bild: Logo im Code, Dublettenwarnung, Filter nach Art, Freigabe je Gruppe, Kurzhilfe und Domain-Bericht.", "Alle übrigen Bilder mit erneuert — sie stammten noch aus 1.2.0.", "Das Skript, das die Bilder erzeugt, lief seit einer Umbenennung ins Leere und tut es wieder.", ], }, { version: "1.7.0", date: "2026-08-21", summary: "Logo im Code, Freigabe je Gruppe und ein Domain-Check.", changes: [ "Ein Logo in der Mitte des Codes — das der Installation oder ein eigenes je Code, 10 bis 25 % der Kantenlänge. Die Fehlerkorrektur springt dabei auf H.", "QR-Typen lassen sich je Gruppe freigeben. Die Grundeinstellung bleibt die Grundlage; eine globale Abschaltung gewinnt immer.", "Der Knopf „Prüfen“ bei jeder Domain meldet DNS, Zertifikat, Erreichbarkeit — und ob dahinter wirklich diese Installation steht.", "Eine Kurzhilfe auf der Domain-Seite führt in sechs Schritten auf, was für eine neue Domain einzurichten ist.", "„Meine Codes“ lässt sich nach Art filtern: alle, nur dynamische, nur statische.", "Beim Ablegen eines statischen Codes wird gewarnt, wenn derselbe Inhalt schon in der Gruppe liegt.", ], }, { version: "1.6.0", date: "2026-08-21", summary: "Sichtbare Version mit Änderungsverlauf.", changes: [ "Die laufende Version steht in der Fußzeile und führt auf eine Seite mit dem vollständigen Änderungsverlauf.", "Der Verlauf wird an einer Stelle gepflegt; die CHANGELOG.md im Projekt entsteht daraus.", "Die Roadmap verweist für Vergangenes auf den Changelog und behält den Blick nach vorn.", ], }, { version: "1.5.0", date: "2026-08-20", summary: "Statische Codes lassen sich benennen und ablegen.", changes: [ "Das Feld „Name“ gilt in beiden Betriebsarten, nicht mehr nur bei dynamischen Codes.", "Der Name bestimmt den Dateinamen beim Herunterladen.", "Ein statischer Code lässt sich in „Meine Codes“ ablegen — als Schnappschuss ohne Kurz-Link und ohne Scan-Zähler.", ], }, { version: "1.4.0", date: "2026-08-17", summary: "Sprechende Kurz-Links und eine früh erkannte Fehlkonfiguration.", changes: [ "Kurz-Links selbst vergeben und nachträglich ändern, mit „/“ als Trenner.", "Die vorherige Adresse bleibt gültig und lässt sich einzeln freigeben — gedruckte Codes bleiben gültig.", "Vergeben und Ändern ist Administratoren vorbehalten und in der Verwaltung für Bearbeiter freigebbar.", "Weicht BASE_URL von der aufgerufenen Adresse ab, meldet die Anmeldung das sofort statt erst am Ende.", "Auf der Anmeldeseite steht nicht mehr, wie die Anmeldung konfiguriert ist.", ], }, { version: "1.3.0", date: "2026-08-16", summary: "Eigenes Erscheinungsbild und ein Dark Mode.", changes: [ "Name, Logo, Favicon und Akzentfarbe sind im Admin-Bereich einstellbar.", "Hell und dunkel folgen dem Betriebssystem, mit Umschalter für eine eigene Wahl.", "Das Update-Skript erklärt, warum ein Vorspulen scheitert, statt nur abzubrechen.", "Die Authentik-Anleitung sagt deutlicher, wie die Redirect URI zu bilden ist.", ], }, { version: "1.2.0", date: "2026-08-15", summary: "Single Sign-on, Dokumentation und eine Sicherheitsdurchsicht.", changes: [ "Anmeldung über Authentik und andere OIDC-Anbieter, mit PKCE und geprüftem ID-Token.", "Bedienungsanleitung mit Bildern, Funktionsübersicht und Roadmap.", "Anmeldung und Sitzungen nach OWASP durchgesehen und gehärtet: stärkere Passwort-Parameter, Längengrenze, Herkunftsprüfung, Schutzheader, absolute Sitzungsdauer.", "Auf der Startseite steht „nur dynamisch“ statt eines nichtssagenden Abzeichens an jeder Kachel.", ], }, { version: "1.1.0", date: "2026-08-14", summary: "Alle fünfzehn QR-Typen, Anmeldung, Gruppen und mehrere Domains.", changes: [ "Vierzehn weitere QR-Typen — von vCard und WLAN bis PDF, Bild und Feedback.", "Landingpages als eigene Gattung für Typen ohne fremdes Ziel.", "Datei-Upload mit Inhaltsprüfung, Größengrenze und Aufräumen.", "Anmeldung, Benutzer, Gruppen und die Rollen Administrator, Bearbeiter und Betrachter.", "Mehrere Domains für Kurz-URLs, den Gruppen zugeteilt.", "„Meine Codes“ als Liste mit Suche, Sortierung und Gruppenfilter.", "Einzelne QR-Typen lassen sich in der Verwaltung ausblenden.", ], }, { version: "1.0.0", date: "2026-08-13", summary: "Erste lauffähige Fassung.", changes: [ "Next.js-Anwendung und PostgreSQL als Docker-Compose-Stack, Migrationen beim Start.", "QR-Typ Website, statisch und dynamisch, mit Kurz-URL, Weiterleitung und Scan-Zähler.", "Gestaltung mit Farben, Kontrastwarnung, Größe, Fehlerkorrektur und Rand.", "Download als PNG und SVG, serverseitig gerendert.", "Update-Skript für Linux, macOS und Windows.", ], }, ]; /** * Die laufende Version. * * Aus der `package.json`, damit es genau eine Stelle gibt, an der sie steht. * Der Wert wird beim Bauen eingesetzt — im Container liegt kein Git, aus dem * sich sonst etwas ableiten ließe. */ export const APP_VERSION: string = pkg.version; /** Der Eintrag zur laufenden Version, falls er im Verlauf steht. */ export function currentEntry(): ChangelogEntry | undefined { return CHANGELOG.find((eintrag) => eintrag.version === APP_VERSION); } /** Datum als „21.08.2026“ — ohne Abhängigkeit von der Zeitzone des Servers. */ export function formatChangelogDate(iso: string): string { const [jahr, monat, tag] = iso.split("-"); return `${tag}.${monat}.${jahr}`; }