diff --git a/app/i18n.py b/app/i18n.py index 69fca59..590d121 100644 --- a/app/i18n.py +++ b/app/i18n.py @@ -12,6 +12,7 @@ translations: Dict[str, Dict[str, str]] = { "nav.users": "User", "nav.overview": "Übersicht", "nav.map": "Karte", + "nav.archive": "Archiv", "nav.new_server": "Server anlegen", "nav.logout": "Logout", "nav.login": "Login", @@ -45,6 +46,10 @@ translations: Dict[str, Dict[str, str]] = { "server_list.empty": "Noch keine Server erfasst. Leg den ersten mit „Server anlegen“ oben rechts an.", "price.month": "Monat", "price.year": "Jahr", + "archive.title": "Archivierte Server", + "archive.subtitle": "Ausgeblendete (archivierte) Server. Hier kannst du sie wiederherstellen.", + "archive.restore": "Wiederherstellen", + "archive.empty": "Keine archivierten Server vorhanden.", "server_detail.server": "Server", "server_detail.contract_end": "Vertragsende", "server_detail.days_ago": "vor {days} Tagen", @@ -169,6 +174,7 @@ translations: Dict[str, Dict[str, str]] = { "nav.users": "Users", "nav.overview": "Overview", "nav.map": "Map", + "nav.archive": "Archive", "nav.new_server": "Add server", "nav.logout": "Logout", "nav.login": "Login", @@ -202,6 +208,10 @@ translations: Dict[str, Dict[str, str]] = { "server_list.empty": "No servers yet. Create the first one via “Add server” in the top right.", "price.month": "Month", "price.year": "Year", + "archive.title": "Archived servers", + "archive.subtitle": "Hidden (archived) servers. Restore them here.", + "archive.restore": "Restore", + "archive.empty": "No archived servers found.", "server_detail.server": "Server", "server_detail.contract_end": "Contract end", "server_detail.days_ago": "{days} days ago", diff --git a/app/main.py b/app/main.py index 046c0ea..698eedb 100644 --- a/app/main.py +++ b/app/main.py @@ -624,6 +624,33 @@ def create_server( return RedirectResponse(url="/", status_code=303) +@app.get("/servers/archived", response_class=HTMLResponse) +def archived_servers( + request: Request, + session: Session = Depends(get_session), + current_user: User = Depends(require_current_user), +): + """List archived servers for the current user.""" + servers = session.exec( + select(Server) + .where(Server.archived == True) + .where(Server.owner_id == current_user.id) + .order_by(Server.provider, Server.name) + ).all() + + csrf_token = ensure_csrf_token(request) + + return templates.TemplateResponse( + "servers_archived.html", + { + "request": request, + "servers": servers, + "current_user": current_user, + "csrf_token": csrf_token, + }, + ) + + @app.get("/servers/{server_id}", response_class=HTMLResponse) def server_detail( server_id: int, @@ -801,6 +828,31 @@ def archive_server( return RedirectResponse("/", status_code=303) +@app.post("/servers/{server_id}/unarchive") +def unarchive_server( + server_id: int, + request: Request, + csrf_token: str = Form(...), + session: Session = Depends(get_session), + current_user: User = Depends(require_current_user), +): + """Restore an archived server.""" + if not validate_csrf(request, csrf_token): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid CSRF token." + ) + + server = session.get(Server, server_id) + if not server or server.owner_id != current_user.id: + return RedirectResponse("/", status_code=303) + + server.archived = False + server.updated_at = datetime.utcnow() + session.add(server) + session.commit() + return RedirectResponse("/servers/archived", status_code=303) + + # ------------- Per-user map view ------------- diff --git a/app/templates/base.html b/app/templates/base.html index 58e825b..bd09234 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -63,6 +63,9 @@ {{ t("nav.map") }} + + {{ t("nav.archive") }} + +
+
+

{{ t("archive.title") }}

+

+ {{ t("archive.subtitle") }} +

+
+
+ + {% if servers %} +
+ + + + + + + + + + + + + {% for s in servers %} + + + + + + + + + {% endfor %} + +
{{ t("table.name") }}{{ t("table.provider") }}{{ t("table.type") }}{{ t("table.location") }}{{ t("table.ipv4") }}{{ t("table.action") }}
+
+
+ {{ s.name }} + {% if s.hostname %} +
{{ s.hostname }}
+ {% endif %} +
+
+
{{ s.provider }} + + {{ s.type }} + + + {% if s.location %}{{ s.location }}{% else %}{% endif %} + + {% if s.ipv4 %}{{ s.ipv4 }}{% else %}{% endif %} + +
+ + +
+
+
+ {% else %} +
+ {{ t("archive.empty") }} +
+ {% endif %} + +{% endblock %}