chore: release version 0.5.1

This commit is contained in:
2026-04-13 11:13:27 +02:00
parent 7b53f66406
commit 96b979a878
7 changed files with 103 additions and 3 deletions

View File

@@ -5,7 +5,19 @@ DATABASE_PATH=./data/putzliga.db
UPLOAD_FOLDER=./data/uploads UPLOAD_FOLDER=./data/uploads
APP_BASE_URL=http://localhost:8000 APP_BASE_URL=http://localhost:8000
APP_TIMEZONE=Europe/Berlin APP_TIMEZONE=Europe/Berlin
# Web Push
# Lokal: APP_BASE_URL auf deine lokale URL setzen
# Cloudron: APP_BASE_URL leer lassen und automatisch CLOUDRON_APP_ORIGIN nutzen
VAPID_PUBLIC_KEY= VAPID_PUBLIC_KEY=
VAPID_PRIVATE_KEY= VAPID_PRIVATE_KEY=
VAPID_CLAIMS_SUBJECT=mailto:admin@example.com VAPID_CLAIMS_SUBJECT=mailto:admin@example.com
# Beispiel für Cloudron:
# SECRET_KEY=ein-langer-zufallswert
# APP_TIMEZONE=Europe/Berlin
# VAPID_PUBLIC_KEY=...
# VAPID_PRIVATE_KEY=...
# VAPID_CLAIMS_SUBJECT=mailto:mail@deine-domain.tld
GUNICORN_WORKERS=2 GUNICORN_WORKERS=2

View File

@@ -4,7 +4,7 @@
"author": "hnzio <mail@example.com>", "author": "hnzio <mail@example.com>",
"description": "Spielerische Haushalts-App mit Aufgaben, Punkten, Monats-Highscore, Kalender und PWA-Push.", "description": "Spielerische Haushalts-App mit Aufgaben, Punkten, Monats-Highscore, Kalender und PWA-Push.",
"tagline": "Haushalt mit Liga-Gefühl", "tagline": "Haushalt mit Liga-Gefühl",
"version": "1.0.0", "version": "0.5.1",
"manifestVersion": 2, "manifestVersion": 2,
"healthCheckPath": "/healthz", "healthCheckPath": "/healthz",
"httpPort": 8000, "httpPort": 8000,

View File

@@ -296,6 +296,35 @@ https://docs.cloudron.io/docker/
- `APP_BASE_URL` kann auf Cloudron über `CLOUDRON_APP_ORIGIN` gesetzt oder daraus abgeleitet werden - `APP_BASE_URL` kann auf Cloudron über `CLOUDRON_APP_ORIGIN` gesetzt oder daraus abgeleitet werden
- Lokale Testdaten aus `data/` werden weder committed noch in das Docker-Image gepackt - Lokale Testdaten aus `data/` werden weder committed noch in das Docker-Image gepackt
### Cloudron-ENV für Push
Für Cloudron brauchst du mindestens diese Variablen:
```dotenv
SECRET_KEY=hier-einen-langen-zufallswert-eintragen
APP_TIMEZONE=Europe/Berlin
VAPID_PUBLIC_KEY=dein_public_key
VAPID_PRIVATE_KEY=dein_private_key
VAPID_CLAIMS_SUBJECT=mailto:mail@deine-domain.tld
```
Hinweise:
- `APP_BASE_URL` musst du auf Cloudron normalerweise nicht setzen, weil `config.py` automatisch `CLOUDRON_APP_ORIGIN` verwendet
- `DATA_DIR`, `DATABASE_PATH` und `UPLOAD_FOLDER` können auf den Standardwerten bleiben, solange du die App normal auf Cloudron betreibst
- `VAPID_PRIVATE_KEY` darf als einzeiliger Wert mit escaped `\\n` gespeichert werden; Putzliga wandelt das beim Start automatisch zurück
Wenn du die Werte lokal in einer `.env` testen willst, kann das zum Beispiel so aussehen:
```dotenv
SECRET_KEY=lokal-irgendein-langer-zufallswert
APP_BASE_URL=http://127.0.0.1:5000
APP_TIMEZONE=Europe/Berlin
VAPID_PUBLIC_KEY=dein_public_key
VAPID_PRIVATE_KEY=dein_private_key
VAPID_CLAIMS_SUBJECT=mailto:mail@deine-domain.tld
```
### Beispielstart in Cloudron-/Container-Umgebungen ### Beispielstart in Cloudron-/Container-Umgebungen
```bash ```bash
@@ -312,6 +341,29 @@ python scripts/generate_vapid_keys.py
Der ausgegebene `VAPID_PRIVATE_KEY` ist bereits `.env`-freundlich mit escaped Newlines formatiert. `config.py` wandelt `\\n` beim Start automatisch in echte Zeilenumbrüche zurück. Der ausgegebene `VAPID_PRIVATE_KEY` ist bereits `.env`-freundlich mit escaped Newlines formatiert. `config.py` wandelt `\\n` beim Start automatisch in echte Zeilenumbrüche zurück.
## Release Notes
### 0.5.1
- Footer mit automatischer Versionsanzeige und Links zu Releases und hnz.io
- Login-Seite bereinigt und Demo-Login-Hinweis entfernt
- Mobile Abstände rund um die feste Bottom-Navigation weiter nachgeschärft
- Cloudron-Version auf `0.5.1` angehoben
### 0.5.0
- Mehrnutzer-Haushalts-App mit Login, Admin-Nutzermanagement und deaktivierter offener Registrierung
- Aufgaben mit sauberer Trennung aus `TaskTemplate` und `TaskInstance`
- Wiederkehrende Aufgaben für Tage, Wochen und Monate
- Monats-Highscore mit Badge-Boni, Balkenanzeige und Monatsarchiv
- Persistentes Badge-System mit eigener Admin-Seite
- Schnellaufgabe per globalem Plus-Button mit admin-konfigurierbarem Aufwand
- Kalender- und Listenansicht, inklusive mobiler Agenda-Ansicht
- Systemabhängiges Light-/Dark-Design
- PWA-Grundlage mit Manifest, Service Worker und App-Icons
- Echte Web-Push-Architektur mit VAPID und Cloudron-tauglicher ENV-Konfiguration
- Cloudron-kompatibler Start mit persistentem Storage, ohne lokale Entwicklungsdaten ins Deployment zu übernehmen
Icons neu generieren: Icons neu generieren:
```bash ```bash

View File

@@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import json
from pathlib import Path from pathlib import Path
from flask import Flask from flask import Flask
@@ -22,6 +23,12 @@ def create_app(config_class: type[Config] = Config) -> Flask:
app = Flask(__name__, static_folder="static", template_folder="templates") app = Flask(__name__, static_folder="static", template_folder="templates")
app.config.from_object(config_class) app.config.from_object(config_class)
manifest_path = Path(app.root_path).parent / "CloudronManifest.json"
try:
app.config["APP_VERSION"] = json.loads(manifest_path.read_text(encoding="utf-8")).get("version", "0.0.0")
except FileNotFoundError:
app.config["APP_VERSION"] = "0.0.0"
app.config["DATA_DIR"].mkdir(parents=True, exist_ok=True) app.config["DATA_DIR"].mkdir(parents=True, exist_ok=True)
app.config["UPLOAD_FOLDER"].mkdir(parents=True, exist_ok=True) app.config["UPLOAD_FOLDER"].mkdir(parents=True, exist_ok=True)
@@ -66,6 +73,7 @@ def create_app(config_class: type[Config] = Config) -> Flask:
return { return {
"app_name": app.config["APP_NAME"], "app_name": app.config["APP_NAME"],
"app_version": app.config["APP_VERSION"],
"nav_items": [ "nav_items": [
("tasks.my_tasks", "Meine Aufgaben", "house"), ("tasks.my_tasks", "Meine Aufgaben", "house"),
("tasks.all_tasks", "Alle", "list"), ("tasks.all_tasks", "Alle", "list"),

View File

@@ -70,6 +70,7 @@
--font-body: "InterLocal", system-ui, sans-serif; --font-body: "InterLocal", system-ui, sans-serif;
--font-heading: "SpaceGroteskLocal", "InterLocal", sans-serif; --font-heading: "SpaceGroteskLocal", "InterLocal", sans-serif;
--safe-bottom: max(24px, env(safe-area-inset-bottom)); --safe-bottom: max(24px, env(safe-area-inset-bottom));
--mobile-nav-space: 138px;
--body-radial-a: rgba(181, 210, 255, 0.85); --body-radial-a: rgba(181, 210, 255, 0.85);
--body-radial-b: rgba(255, 221, 196, 0.48); --body-radial-b: rgba(255, 221, 196, 0.48);
--body-linear-start: #f8fbff; --body-linear-start: #f8fbff;
@@ -147,7 +148,9 @@ p {
.page-shell { .page-shell {
min-height: 100vh; min-height: 100vh;
padding: 24px 18px calc(100px + var(--safe-bottom)); display: flex;
flex-direction: column;
padding: 24px 18px calc(var(--mobile-nav-space) + var(--safe-bottom));
} }
.topbar { .topbar {
@@ -179,6 +182,26 @@ p {
display: grid; display: grid;
gap: 24px; gap: 24px;
min-width: 0; min-width: 0;
flex: 1 0 auto;
}
.app-footer {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
padding: 18px 0 8px;
color: var(--muted);
font-size: 0.88rem;
text-align: center;
}
.app-footer a {
color: inherit;
}
.app-footer span {
opacity: 0.7;
} }
.panel, .panel,

View File

@@ -40,7 +40,6 @@
<label class="checkbox">{{ form.remember_me() }} <span>Angemeldet bleiben</span></label> <label class="checkbox">{{ form.remember_me() }} <span>Angemeldet bleiben</span></label>
{{ form.submit(class_="button button--wide") }} {{ form.submit(class_="button button--wide") }}
</form> </form>
<p class="inline-note">Demo-Logins nach dem Seeden: `anna@putzliga.local` / `putzliga123` und `ben@putzliga.local` / `putzliga123`.</p>
{% if registration_open %} {% if registration_open %}
<p class="inline-note">Es gibt noch keinen Nutzer. <a href="{{ url_for('auth.register') }}">Ersten Account anlegen</a></p> <p class="inline-note">Es gibt noch keinen Nutzer. <a href="{{ url_for('auth.register') }}">Ersten Account anlegen</a></p>
{% else %} {% else %}

View File

@@ -86,6 +86,12 @@
{% block content %}{% endblock %} {% block content %}{% endblock %}
</main> </main>
<footer class="app-footer">
<a href="https://git.hnz.io/hnzio/putzliga/releases" target="_blank" rel="noreferrer">Version {{ app_version }}</a>
<span>·</span>
<a href="https://hnz.io" target="_blank" rel="noreferrer">(c) 2026 @hnz.io</a>
</footer>
</div> </div>
</div> </div>