Merge branch 'develop'

This commit is contained in:
rene 2026-05-14 16:54:59 +02:00
commit 86c5400c2e
5 changed files with 45 additions and 13 deletions

View file

@ -406,7 +406,7 @@ async def serve_media(path: str, request: _Request):
raise _HE(404, "Nicht gefunden.")
return _media_response(filepath)
APP_VER = "948" # muss mit APP_VER in app.js übereinstimmen
APP_VER = "951" # muss mit APP_VER in app.js übereinstimmen
@app.get("/.well-known/assetlinks.json")
async def assetlinks():

View file

@ -418,20 +418,36 @@ async def cancel_subscription(user=Depends(get_current_user)):
try:
from mailer import send_email, email_html
import html as _html
tier_label = {"pro": "Ban Yaro Pro", "breeder": "Züchter"}.get(row["subscription_tier"], row["subscription_tier"])
expires_fmt = expires[:10] if expires else ""
tier_label = {"pro": "Ban Yaro Pro", "breeder": "Züchter"}.get(row["subscription_tier"], row["subscription_tier"])
expires_de = None
if expires:
from datetime import date as _date
try:
d = _date.fromisoformat(expires[:10])
monate = ["Januar","Februar","März","April","Mai","Juni",
"Juli","August","September","Oktober","November","Dezember"]
expires_de = f"{d.day}. {monate[d.month-1]} {d.year}"
except Exception:
expires_de = expires[:10]
expiry_line = (
f"<p>Dein Abo ist weiterhin aktiv bis zum <strong>{expires_de}</strong>. "
f"Ab diesem Datum wirst du automatisch auf den kostenlosen Tarif gesetzt.</p>"
if expires_de else
"<p>Dein Abo bleibt bis zum Ende des bezahlten Zeitraums aktiv.</p>"
)
body_html = f"""
<p>Hallo {_html.escape(user['name'])},</p>
<p>deine Kündigung für <strong>{tier_label}</strong> wurde bestätigt.</p>
<p>Dein Abo ist weiterhin aktiv bis zum <strong>{expires_fmt}</strong>.
Ab diesem Datum wirst du automatisch auf den kostenlosen Tarif gesetzt.</p>
{expiry_line}
<p>Deine Daten (Tagebuch, Gesundheit, Notizen) bleiben vollständig erhalten.
Wenn du mehrere Hunde hast, kannst du vor dem Ablauf einen als Haupthund festlegen.</p>
<p>Wir hoffen, dich bald wieder begrüßen zu dürfen!</p>
<p>Viele Grüße<br>René &amp; das Ban Yaro Team</p>"""
html = email_html(body_html, cta_url="https://banyaro.app", cta_label="Ban Yaro öffnen")
plain = (f"Hallo {user['name']},\n\nKündigung bestätigt für {tier_label}.\n"
f"Aktiv bis: {expires_fmt}\n\nAlle Daten bleiben erhalten.\n\nViele Grüße\nRené")
+ (f"Aktiv bis: {expires_de}\n" if expires_de else "")
+ "\nAlle Daten bleiben erhalten.\n\nViele Grüße\nRené")
await send_email(user["email"], f"Kündigung bestätigt — {tier_label}", html, plain)
except Exception:
pass

View file

@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
const APP_VER = '948'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VER = '951'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VERSION = '1.5.1'; // ← semantische Version, wird bei make release gesetzt
const IS_STAGING = location.hostname === 'staging.banyaro.app';
// Cache-Bust-Parameter nach Update-Reload sofort entfernen

View file

@ -110,7 +110,13 @@ window.Page_settings = (() => {
const isPaid = (isPro || isBreeder) && !tier.endsWith('_test') && !isAdmin;
const _expiryInfo = () => {
if (!isPaid || !expiresDate) return '';
if (!isPaid) return '';
if (cancelled && !expiresDate) {
return `<div style="font-size:var(--text-xs);color:#e65100;margin-top:var(--space-1)">
Gekündigt läuft bis Ablauf des bezahlten Zeitraums
</div>`;
}
if (!expiresDate) return '';
const color = cancelled ? '#e65100' : 'var(--c-text-secondary)';
const text = cancelled
? `Gekündigt — läuft bis ${expiresDate}`
@ -422,7 +428,17 @@ window.Page_settings = (() => {
const fresh = await API.auth.me();
Object.assign(_appState.user, fresh);
UI.modal.close();
UI.toast.success('Kündigung bestätigt. Eine Bestätigungsmail wurde gesendet.');
const tier = fresh.subscription_tier || '';
const label = { pro: 'Pro', breeder: 'Züchter' }[tier] || tier;
const exp = fresh.subscription_expires_at;
const expFmt = exp
? new Date(exp).toLocaleDateString('de-DE', {day:'numeric',month:'long',year:'numeric'})
: null;
UI.toast.success(
expFmt
? `Kündigung bestätigt — ${label} läuft noch bis ${expFmt}.`
: 'Kündigung bestätigt. Eine Bestätigungsmail wurde gesendet.'
);
_render();
} catch (e) {
btn.disabled = false;
@ -1298,10 +1314,10 @@ window.Page_settings = (() => {
document.getElementById('settings-delete-account-btn')?.addEventListener('click', async () => {
const ok = await UI.modal.confirm({
title: 'Konto unwiderruflich löschen?',
body: 'Alle deine Daten (Tagebuch, Gesundheit, Training, Fotos) werden dauerhaft gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.',
title: 'Konto unwiderruflich löschen?',
message: 'Alle deine Daten (Tagebuch, Gesundheit, Training, Fotos) werden dauerhaft gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.',
confirmText: 'Ja, Konto löschen',
danger: true,
danger: true,
});
if (!ok) return;
try {

View file

@ -3,7 +3,7 @@
Offline-Cache + Push Notifications + Tile-Cache
============================================================ */
const CACHE_VERSION = 'by-v948';
const CACHE_VERSION = 'by-v951';
const CACHE_STATIC = `${CACHE_VERSION}-static`;
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache