Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b96e9ddbe5 | ||
|
|
586e1f042f |
@@ -0,0 +1,74 @@
|
||||
# Änderungen
|
||||
|
||||
Was in welcher Fassung dazugekommen ist. Dieselbe Liste steht in der
|
||||
Anwendung unter **Version und Änderungen**, erreichbar über die
|
||||
Versionsnummer in der Fußzeile.
|
||||
|
||||
> Diese Datei wird erzeugt: `npm run changelog`.
|
||||
> Gepflegt wird der Verlauf in `src/lib/changelog.ts`.
|
||||
|
||||
## 1.6.0 — 21.08.2026
|
||||
|
||||
Sichtbare Version mit Änderungsverlauf.
|
||||
|
||||
- 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.
|
||||
|
||||
## 1.5.0 — 20.08.2026
|
||||
|
||||
Statische Codes lassen sich benennen und ablegen.
|
||||
|
||||
- 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.
|
||||
|
||||
## 1.4.0 — 17.08.2026
|
||||
|
||||
Sprechende Kurz-Links und eine früh erkannte Fehlkonfiguration.
|
||||
|
||||
- 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.
|
||||
|
||||
## 1.3.0 — 16.08.2026
|
||||
|
||||
Eigenes Erscheinungsbild und ein Dark Mode.
|
||||
|
||||
- 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.
|
||||
|
||||
## 1.2.0 — 15.08.2026
|
||||
|
||||
Single Sign-on, Dokumentation und eine Sicherheitsdurchsicht.
|
||||
|
||||
- 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.
|
||||
|
||||
## 1.1.0 — 14.08.2026
|
||||
|
||||
Alle fünfzehn QR-Typen, Anmeldung, Gruppen und mehrere Domains.
|
||||
|
||||
- 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.
|
||||
|
||||
## 1.0.0 — 13.08.2026
|
||||
|
||||
Erste lauffähige Fassung.
|
||||
|
||||
- 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.
|
||||
@@ -34,6 +34,8 @@ entscheidet die Verwaltung — nicht gebrauchte lassen sich ausblenden.
|
||||
eigene Wahl.
|
||||
- **Sprechende Kurz-Links:** `qr.firma.de/r/sommeraktion` statt Zufallszeichen,
|
||||
nachträglich änderbar — die vorherige Adresse bleibt gültig.
|
||||
- **Sichtbare Version:** Die Fußzeile nennt die laufende Fassung und führt
|
||||
zum Änderungsverlauf — nach einem Update sofort erkennbar, ob es ankam.
|
||||
- **Zwei Container, kein Reverse Proxy nötig.** Migrationen laufen beim Start
|
||||
von selbst; alles Nötige steht in einer `.env`.
|
||||
|
||||
@@ -48,7 +50,8 @@ entscheidet die Verwaltung — nicht gebrauchte lassen sich ausblenden.
|
||||
- **[Anmeldung über Authentik (OIDC)](docs/authentik.md)** — Einrichtung,
|
||||
Rollen und der Weg zurück
|
||||
- **[Alle Funktionen im Detail](docs/funktionen.md)** — die lange Fassung
|
||||
- **[Roadmap](docs/roadmap.md)** — was gebaut ist und was kommen könnte
|
||||
- **[Änderungen](CHANGELOG.md)** — was in welcher Version dazugekommen ist
|
||||
- **[Roadmap](docs/roadmap.md)** — was als Nächstes kommen könnte
|
||||
- **[Anmeldung und Sitzungen nach OWASP](docs/sicherheit.md)** — Durchsicht,
|
||||
Härtungen und was bewusst offen bleibt
|
||||
|
||||
|
||||
@@ -500,6 +500,21 @@ alles zeigt und Typen aus späteren Versionen automatisch erscheinen.
|
||||
|
||||
---
|
||||
|
||||
## Version und Änderungen
|
||||
|
||||
Die Fußzeile nennt die laufende Version und führt auf eine Seite mit dem
|
||||
vollständigen Änderungsverlauf. Nach einem Update ist das die schnellste
|
||||
Antwort auf die Frage, ob der Container wirklich mit dem neuen Abbild läuft:
|
||||
Steht dort noch die alte Nummer, ist er es nicht.
|
||||
|
||||
Der Verlauf wird an **einer** Stelle gepflegt (`src/lib/changelog.ts`).
|
||||
Daraus entstehen sowohl die Seite in der Anwendung als auch die
|
||||
[CHANGELOG.md](../CHANGELOG.md) im Projekt (`npm run changelog`); eine
|
||||
Prüfung wacht darüber, dass beides zusammenpasst. Zu jeder Version gibt es
|
||||
einen Git-Tag.
|
||||
|
||||
---
|
||||
|
||||
## Schnittstelle
|
||||
|
||||
Alles, was die Oberfläche tut, geht auch über HTTP:
|
||||
|
||||
+9
-87
@@ -7,94 +7,16 @@ Aufwände sind grobe Einschätzungen, keine Zusagen.
|
||||
|
||||
## Gebaut
|
||||
|
||||
### Version 1 — Grundgerüst (13.08.2026)
|
||||
Der vollständige Verlauf mit Versionsnummern steht im
|
||||
[Changelog](../CHANGELOG.md) — und in der Anwendung selbst unter
|
||||
**Version und Änderungen**, erreichbar über die Versionsnummer in der
|
||||
Fußzeile.
|
||||
|
||||
- Next.js-Anwendung und PostgreSQL als Docker-Compose-Stack, ohne Reverse
|
||||
Proxy, Migrationen beim Start
|
||||
- QR-Typ-Registry als Dispatch-Tabelle — ein neuer Typ ist ein Modul plus ein
|
||||
Formular, ohne Eingriff in den übrigen Code
|
||||
- Typ **Website**, statisch und dynamisch, mit Kurz-URL, 302-Weiterleitung und
|
||||
Scan-Zähler
|
||||
- Gestaltung: Farben mit Kontrastwarnung, Größe, Fehlerkorrektur, Rand
|
||||
- Download als PNG und SVG, serverseitig gerendert
|
||||
- „Meine Codes“ mit Ziel ändern und Löschen
|
||||
- Deutsche Oberfläche, deutsche Fehlermeldungen, Validierung mit Zod
|
||||
- Update-Skript für Linux/macOS und Windows
|
||||
|
||||
### Typen einzeln freigeben (14.08.2026)
|
||||
|
||||
Ausblenden nicht gebrauchter Typen in der Verwaltung. Bereits erstellte Codes
|
||||
eines abgeschalteten Typs funktionieren weiter.
|
||||
|
||||
### Phase A — acht weitere Typen (14.08.2026)
|
||||
|
||||
vCard, App-Link, WhatsApp, E-Mail, SMS, Standort, Zahlung (GiroCode und
|
||||
Zahlungslink), WLAN.
|
||||
|
||||
### Phase B1 — Anmeldung, Gruppen, Rollen (14.08.2026)
|
||||
|
||||
Abschaltbare Anmeldung, Benutzerverwaltung, Gruppen mit den Rollen
|
||||
Administrator, Bearbeiter und Betrachter, Sitzungen in der Datenbank,
|
||||
scrypt-Passwörter, Sperre nach Fehlversuchen.
|
||||
|
||||
### Phase B2 — mehrere Domains (14.08.2026)
|
||||
|
||||
Domains für Kurz-URLs, den Gruppen zugeteilt. Slug je Domain eindeutig,
|
||||
Host-Header gegen die registrierten Domains geprüft.
|
||||
|
||||
### Listenansicht (14.08.2026)
|
||||
|
||||
„Meine Codes“ als Liste mit Suche, Sortierung und Gruppenfilter — gedacht für
|
||||
viele Projekte nebeneinander. Karten bleiben als zweite Ansicht.
|
||||
|
||||
### Phase C — die letzten sechs Typen (14.08.2026)
|
||||
|
||||
Mehrere Links, Social Media, Video, PDF, Bild, Feedback. Dazu:
|
||||
|
||||
- Landingpages als eigene Gattung (`/s/{slug}`), ohne App-Navigation
|
||||
- Datei-Upload mit Inhaltsprüfung, Größengrenze und Aufräumen
|
||||
- Feedback mit anonymer Erhebung und Auswertung
|
||||
|
||||
Damit sind **alle 15 vorgesehenen Typen** gebaut.
|
||||
|
||||
### Single Sign-on (15.08.2026)
|
||||
|
||||
Anmeldung über Authentik und andere OIDC-Anbieter, mit PKCE und geprüftem
|
||||
ID-Token.
|
||||
|
||||
### Erscheinungsbild (16.08.2026)
|
||||
|
||||
Name, Logo, Favicon und Akzentfarbe im Admin-Bereich einstellbar. Die Farbe
|
||||
wirkt über die Farbvariablen, die Tailwind ohnehin benutzt — deshalb ohne
|
||||
Umbau der rund 60 eingefärbten Stellen.
|
||||
|
||||
### Dark Mode (16.08.2026)
|
||||
|
||||
Folgt dem Betriebssystem, mit Umschalter für Hell, Dunkel und System.
|
||||
Umgesetzt über `light-dark()` statt über `dark:`-Klassen — deshalb ohne
|
||||
Änderung an den rund 440 Farbklassen und ohne JavaScript für die Systemwahl.
|
||||
|
||||
### Sprechende Kurz-Links (17.08.2026)
|
||||
|
||||
Kurz-Links selbst vergeben und nachträglich ändern, mit `/` als Trenner. Die
|
||||
vorherige Adresse bleibt gültig und lässt sich einzeln freigeben — sonst würde
|
||||
jedes Umbenennen gedruckte Codes entwerten. Standardmäßig Administratoren
|
||||
vorbehalten, per Einstellung für Bearbeiter freigebbar.
|
||||
|
||||
Schließt [Issue #1](https://git.wilde-server.de/florian.wilde/qrcode-generator/issues/1).
|
||||
|
||||
### Statische Codes benennen und ablegen (20.08.2026)
|
||||
|
||||
Das Namensfeld gilt in beiden Betriebsarten. Es bestimmt den Dateinamen beim
|
||||
Herunterladen und erlaubt, einen statischen Code in „Meine Codes" abzulegen —
|
||||
als Schnappschuss, dessen Inhalt sich nicht mehr ändert.
|
||||
|
||||
Schließt [Issue #2](https://git.wilde-server.de/florian.wilde/qrcode-generator/issues/2).
|
||||
|
||||
### Dokumentation (15.08.2026)
|
||||
|
||||
Bedienungsanleitung mit Bildern, Authentik-Anleitung, Funktionsübersicht.
|
||||
Die Bilder erzeugt ein Skript, damit sie nicht veralten.
|
||||
Kurz gefasst: Version 1.0.0 (13.08.2026) brachte das Grundgerüst mit dem Typ
|
||||
Website, 1.1.0 alle fünfzehn Typen samt Anmeldung, Gruppen und Domains,
|
||||
1.2.0 Single Sign-on und die Sicherheitsdurchsicht, 1.3.0 Erscheinungsbild
|
||||
und Dark Mode, 1.4.0 sprechende Kurz-Links, 1.5.0 benennbare statische Codes
|
||||
und 1.6.0 diesen Versionshinweis.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+2
-1
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "qrcode-generator",
|
||||
"version": "1.1.0",
|
||||
"version": "1.6.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"changelog": "node scripts/changelog.mjs",
|
||||
"lint": "eslint",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"db:generate": "prisma generate",
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Schreibt CHANGELOG.md aus src/lib/changelog.ts.
|
||||
*
|
||||
* node scripts/changelog.mjs erzeugen
|
||||
* node scripts/changelog.mjs --check nur prüfen, ob die Datei aktuell ist
|
||||
*
|
||||
* Warum erzeugt und nicht von Hand gepflegt: Die Anwendung zeigt denselben
|
||||
* Verlauf unter /version an. Zwei gepflegte Listen liefen unweigerlich
|
||||
* auseinander — und ein Changelog, der nicht stimmt, ist schlimmer als keiner.
|
||||
*/
|
||||
import { readFileSync, writeFileSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname, join } from "node:path";
|
||||
|
||||
const wurzel = join(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const ziel = join(wurzel, "CHANGELOG.md");
|
||||
|
||||
/**
|
||||
* Die Quelle ist TypeScript, dieses Skript aber schlichtes Node. Statt einen
|
||||
* Übersetzer dazuzunehmen, wird der Datenteil gelesen und ausgewertet — er
|
||||
* besteht ausschließlich aus Zeichenketten und Listen.
|
||||
*/
|
||||
function ladeVerlauf() {
|
||||
const quelle = readFileSync(join(wurzel, "src", "lib", "changelog.ts"), "utf8");
|
||||
const anfang = quelle.indexOf("export const CHANGELOG");
|
||||
if (anfang === -1) throw new Error("CHANGELOG-Liste nicht gefunden.");
|
||||
|
||||
const start = quelle.indexOf("[", anfang);
|
||||
const ende = quelle.indexOf("\n];", start);
|
||||
if (start === -1 || ende === -1) throw new Error("Liste nicht abgrenzbar.");
|
||||
|
||||
const literal = quelle.slice(start, ende + 2);
|
||||
return Function(`"use strict"; return (${literal});`)();
|
||||
}
|
||||
|
||||
function formatiereDatum(iso) {
|
||||
const [jahr, monat, tag] = iso.split("-");
|
||||
return `${tag}.${monat}.${jahr}`;
|
||||
}
|
||||
|
||||
function baue(verlauf) {
|
||||
const zeilen = [
|
||||
"# Änderungen",
|
||||
"",
|
||||
"Was in welcher Fassung dazugekommen ist. Dieselbe Liste steht in der",
|
||||
"Anwendung unter **Version und Änderungen**, erreichbar über die",
|
||||
"Versionsnummer in der Fußzeile.",
|
||||
"",
|
||||
"> Diese Datei wird erzeugt: `npm run changelog`.",
|
||||
"> Gepflegt wird der Verlauf in `src/lib/changelog.ts`.",
|
||||
"",
|
||||
];
|
||||
|
||||
for (const eintrag of verlauf) {
|
||||
zeilen.push(`## ${eintrag.version} — ${formatiereDatum(eintrag.date)}`, "");
|
||||
zeilen.push(eintrag.summary, "");
|
||||
for (const änderung of eintrag.changes) zeilen.push(`- ${änderung}`);
|
||||
zeilen.push("");
|
||||
}
|
||||
|
||||
return zeilen.join("\n");
|
||||
}
|
||||
|
||||
const inhalt = baue(ladeVerlauf());
|
||||
|
||||
if (process.argv.includes("--check")) {
|
||||
let vorhanden = "";
|
||||
try {
|
||||
vorhanden = readFileSync(ziel, "utf8");
|
||||
} catch {
|
||||
console.error("CHANGELOG.md fehlt. Erzeugen mit: npm run changelog");
|
||||
process.exit(1);
|
||||
}
|
||||
if (vorhanden !== inhalt) {
|
||||
console.error(
|
||||
"CHANGELOG.md passt nicht mehr zu src/lib/changelog.ts. Neu erzeugen mit: npm run changelog",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log("CHANGELOG.md ist aktuell.");
|
||||
} else {
|
||||
writeFileSync(ziel, inhalt, "utf8");
|
||||
console.log("CHANGELOG.md geschrieben.");
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { ThemeToggle } from "@/components/theme-toggle";
|
||||
import { actorLabel, getActor, isAuthEnabled } from "@/lib/auth/actor";
|
||||
import { can } from "@/lib/auth/policy";
|
||||
import { getBranding } from "@/lib/branding";
|
||||
import { APP_VERSION } from "@/lib/changelog";
|
||||
|
||||
// Der Kopfbereich hängt von der Anmeldung ab und darf nicht vorgerendert
|
||||
// werden.
|
||||
@@ -128,9 +129,20 @@ export default async function AppLayout({ children }: LayoutProps<"/">) {
|
||||
<main className="flex-1">{children}</main>
|
||||
|
||||
<footer className="border-t border-slate-200 bg-surface">
|
||||
<div className="mx-auto max-w-6xl px-4 py-6 text-sm text-slate-500 sm:px-6">
|
||||
<div className="mx-auto flex max-w-6xl flex-wrap items-baseline justify-between gap-x-4 gap-y-1 px-4 py-6 text-sm text-slate-500 sm:px-6">
|
||||
<span>
|
||||
{branding.appName}
|
||||
{branding.footerText ? ` · ${branding.footerText}` : null}
|
||||
</span>
|
||||
{/* Welche Fassung hier läuft — nach einem Update die schnellste
|
||||
Antwort auf die Frage, ob das Update auch angekommen ist. */}
|
||||
<Link
|
||||
href="/version"
|
||||
title="Version und Änderungen"
|
||||
className="rounded font-mono text-xs tabular-nums transition-colors hover:text-slate-900"
|
||||
>
|
||||
v{APP_VERSION}
|
||||
</Link>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
|
||||
import { Icon } from "@/components/icons";
|
||||
import { Badge, Card } from "@/components/ui";
|
||||
import { requirePageActor } from "@/lib/auth/guard";
|
||||
import {
|
||||
APP_VERSION,
|
||||
CHANGELOG,
|
||||
formatChangelogDate,
|
||||
} from "@/lib/changelog";
|
||||
|
||||
export const metadata: Metadata = { title: "Version und Änderungen" };
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export default async function VersionPage() {
|
||||
await requirePageActor();
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-3xl px-4 py-8 sm:px-6 sm:py-10">
|
||||
<Link
|
||||
href="/"
|
||||
className="mb-5 inline-flex items-center gap-1.5 rounded-lg text-sm font-medium text-slate-600 transition-colors hover:text-slate-900"
|
||||
>
|
||||
<Icon name="arrowLeft" className="size-4" />
|
||||
Zur Typ-Auswahl
|
||||
</Link>
|
||||
|
||||
<div className="mb-7">
|
||||
<h1 className="text-2xl font-bold tracking-tight text-slate-900 sm:text-3xl">
|
||||
Version und Änderungen
|
||||
</h1>
|
||||
<p className="mt-2 text-slate-600">
|
||||
Hier läuft Version{" "}
|
||||
<span className="font-semibold text-slate-900">{APP_VERSION}</span>.
|
||||
Darunter steht, was in welcher Fassung dazugekommen ist.
|
||||
</p>
|
||||
<p className="mt-2 text-sm text-slate-500">
|
||||
Nach einem Update lohnt ein Blick hierher: Steht oben noch die alte
|
||||
Nummer, läuft der Container noch mit dem alten Abbild.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ol className="space-y-4">
|
||||
{CHANGELOG.map((eintrag) => {
|
||||
const laeuft = eintrag.version === APP_VERSION;
|
||||
return (
|
||||
<li key={eintrag.version}>
|
||||
<Card>
|
||||
<div className="mb-3 flex flex-wrap items-baseline gap-x-3 gap-y-1">
|
||||
<h2 className="text-lg font-semibold text-slate-900">
|
||||
{eintrag.version}
|
||||
</h2>
|
||||
<span className="text-sm text-slate-500 tabular-nums">
|
||||
{formatChangelogDate(eintrag.date)}
|
||||
</span>
|
||||
{laeuft ? (
|
||||
<Badge tone="accent">läuft hier</Badge>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<p className="mb-3 text-slate-600">{eintrag.summary}</p>
|
||||
|
||||
<ul className="space-y-1.5 text-sm text-slate-700">
|
||||
{eintrag.changes.map((zeile) => (
|
||||
<li key={zeile} className="flex gap-2.5">
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="mt-2 size-1.5 shrink-0 rounded-full bg-slate-300"
|
||||
/>
|
||||
<span>{zeile}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Card>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
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 sind aus dem Abschnitt „Gebaut“ der Roadmap und der
|
||||
* Git-Historie zusammengetragen. Versionsnummern gab es dabei nie — es
|
||||
* existieren keine Tags —, deshalb ist je **Erscheinungstag** eine Version
|
||||
* gebildet. Das ist nachträglich vergeben, aber die Daten selbst stimmen.
|
||||
*/
|
||||
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.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}`;
|
||||
}
|
||||
Reference in New Issue
Block a user