diff --git a/.env.example b/.env.example index 769af62..c763534 100644 --- a/.env.example +++ b/.env.example @@ -5,7 +5,19 @@ DATABASE_PATH=./data/putzliga.db UPLOAD_FOLDER=./data/uploads APP_BASE_URL=http://localhost:8000 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_PRIVATE_KEY= 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 diff --git a/CloudronManifest.json b/CloudronManifest.json index 908b3d1..65378e9 100644 --- a/CloudronManifest.json +++ b/CloudronManifest.json @@ -4,7 +4,7 @@ "author": "hnzio ", "description": "Spielerische Haushalts-App mit Aufgaben, Punkten, Monats-Highscore, Kalender und PWA-Push.", "tagline": "Haushalt mit Liga-Gefühl", - "version": "1.0.0", + "version": "0.5.1", "manifestVersion": 2, "healthCheckPath": "/healthz", "httpPort": 8000, diff --git a/README.md b/README.md index c903b29..c68d18f 100644 --- a/README.md +++ b/README.md @@ -296,6 +296,35 @@ https://docs.cloudron.io/docker/ - `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 +### 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 ```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. +## 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: ```bash diff --git a/app/__init__.py b/app/__init__.py index bd3a6e2..73e4c08 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,5 +1,6 @@ from __future__ import annotations +import json from pathlib import Path 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.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["UPLOAD_FOLDER"].mkdir(parents=True, exist_ok=True) @@ -66,6 +73,7 @@ def create_app(config_class: type[Config] = Config) -> Flask: return { "app_name": app.config["APP_NAME"], + "app_version": app.config["APP_VERSION"], "nav_items": [ ("tasks.my_tasks", "Meine Aufgaben", "house"), ("tasks.all_tasks", "Alle", "list"), diff --git a/app/static/css/style.css b/app/static/css/style.css index 02ac7e3..7ba4d77 100644 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -70,6 +70,7 @@ --font-body: "InterLocal", system-ui, sans-serif; --font-heading: "SpaceGroteskLocal", "InterLocal", sans-serif; --safe-bottom: max(24px, env(safe-area-inset-bottom)); + --mobile-nav-space: 138px; --body-radial-a: rgba(181, 210, 255, 0.85); --body-radial-b: rgba(255, 221, 196, 0.48); --body-linear-start: #f8fbff; @@ -147,7 +148,9 @@ p { .page-shell { 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 { @@ -179,6 +182,26 @@ p { display: grid; gap: 24px; 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, diff --git a/app/templates/auth/login.html b/app/templates/auth/login.html index c150d0d..15dbf32 100644 --- a/app/templates/auth/login.html +++ b/app/templates/auth/login.html @@ -40,7 +40,6 @@ {{ form.submit(class_="button button--wide") }} -

Demo-Logins nach dem Seeden: `anna@putzliga.local` / `putzliga123` und `ben@putzliga.local` / `putzliga123`.

{% if registration_open %}

Es gibt noch keinen Nutzer. Ersten Account anlegen

{% else %} diff --git a/app/templates/base.html b/app/templates/base.html index 6e3615a..2edc190 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -86,6 +86,12 @@ {% block content %}{% endblock %} + +