From 2f8fed060055d1b40cc020563c3dc4ba9d1ff809 Mon Sep 17 00:00:00 2001
From: Scarriffle
Date: Thu, 7 May 2026 19:17:26 +0200
Subject: [PATCH] feat(auth): "Angemeldet bleiben"-Checkbox auf Login-Screen
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Wenn aktiviert, bekommt der JWT-Token statt der üblichen 7 Tage eine
Lebensdauer von 180 Tagen. Der Token liegt wie bisher in localStorage,
bleibt also bis zum manuellen Löschen / Cookie-Reset gültig.
- backend/routers/auth_router.py: LoginRequest.remember_me, längere
expires_delta beim Token-Erstellen
- index.html: Checkbox unter dem 2FA-Feld
- api.js: login() reicht remember_me als 4. Parameter durch
- app.js: Wert aus #login-remember lesen und mitschicken
- Version v5 → v6
---
backend/routers/auth_router.py | 8 +++++++-
frontend/index.html | 21 ++++++++++++---------
frontend/js/api.js | 4 ++--
frontend/js/app.js | 3 ++-
frontend/js/version.js | 2 +-
frontend/sw.js | 2 +-
6 files changed, 25 insertions(+), 15 deletions(-)
diff --git a/backend/routers/auth_router.py b/backend/routers/auth_router.py
index 3f18f7e..98ede29 100644
--- a/backend/routers/auth_router.py
+++ b/backend/routers/auth_router.py
@@ -1,3 +1,4 @@
+from datetime import timedelta
from typing import Optional
import pyotp
@@ -11,6 +12,9 @@ import models
from auth import create_access_token, get_current_user, get_password_hash, verify_password
from database import get_db
+# When "Angemeldet bleiben" is ticked the token lives for half a year.
+REMEMBER_ME_EXPIRY = timedelta(days=180)
+
router = APIRouter()
@@ -24,6 +28,7 @@ class LoginRequest(BaseModel):
username: str
password: str
totp_code: Optional[str] = None
+ remember_me: Optional[bool] = False
def _user_dict(user: models.User) -> dict:
@@ -98,7 +103,8 @@ def login_json(req: LoginRequest, db: Session = Depends(get_db)):
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Ungültiger 2FA-Code",
)
- token = create_access_token({"sub": user.username})
+ expires = REMEMBER_ME_EXPIRY if req.remember_me else None
+ token = create_access_token({"sub": user.username}, expires_delta=expires)
return {"access_token": token, "token_type": "bearer", "user": _user_dict(user)}
diff --git a/frontend/index.html b/frontend/index.html
index 4a90364..6565e70 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -4,7 +4,7 @@
- Calendarr v5
+ Calendarr v6
@@ -73,11 +73,14 @@
+
-
+
@@ -196,7 +199,7 @@
-
+
@@ -232,7 +235,7 @@
@@ -250,7 +253,7 @@
@@ -308,7 +311,7 @@
@@ -862,7 +865,7 @@
scarriffleservices@gmail.com
diff --git a/frontend/js/api.js b/frontend/js/api.js
index 38b4a1e..b715277 100644
--- a/frontend/js/api.js
+++ b/frontend/js/api.js
@@ -64,8 +64,8 @@ export const api = {
delete: (path) => request('DELETE', path),
upload: (path, form) => uploadRequest(path, form),
- login: (username, password, totp_code = null) =>
- request('POST', '/auth/login', { username, password, totp_code }),
+ login: (username, password, totp_code = null, remember_me = false) =>
+ request('POST', '/auth/login', { username, password, totp_code, remember_me }),
setupRequired: () => request('GET', '/auth/setup-required'),
setup: (data) => request('POST', '/auth/setup', data),
diff --git a/frontend/js/app.js b/frontend/js/app.js
index c33fed0..cf2c277 100644
--- a/frontend/js/app.js
+++ b/frontend/js/app.js
@@ -141,11 +141,12 @@ function bindLoginForm() {
const username = document.getElementById('login-username').value.trim();
const password = document.getElementById('login-password').value;
const totpCode = document.getElementById('login-totp')?.value.trim() || null;
+ const remember = document.getElementById('login-remember')?.checked || false;
const errEl = document.getElementById('login-error');
errEl.classList.add('hidden');
try {
- const res = await api.login(username, password, totpCode);
+ const res = await api.login(username, password, totpCode, remember);
localStorage.setItem('token', res.access_token);
localStorage.setItem('user', JSON.stringify(res.user));
await launchApp();
diff --git a/frontend/js/version.js b/frontend/js/version.js
index d5ed145..bc8e7e5 100644
--- a/frontend/js/version.js
+++ b/frontend/js/version.js
@@ -1,2 +1,2 @@
// Increment APP_VERSION with every code change
-export const APP_VERSION = 'v5';
+export const APP_VERSION = 'v6';
diff --git a/frontend/sw.js b/frontend/sw.js
index 4783304..3e9316e 100644
--- a/frontend/sw.js
+++ b/frontend/sw.js
@@ -1,7 +1,7 @@
// Calendarr Service Worker
// Cache-first for static assets, network-first for /api/* (graceful offline)
-const CACHE_VERSION = 'calendarr-v5';
+const CACHE_VERSION = 'calendarr-v6';
const STATIC_ASSETS = [
'/',
'/index.html',