Version 2.5.0: englische Adressen entlang der Navigation, sparsameres Protokoll

This commit is contained in:
alientim committed 2026-09-11 18:57:23 +02:00
1 parent f2d5324449
commit 1ac5dc1637
33 files changed
+431 -175

No files matched your search

+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "tesm-license"
version = "2.4.1"
version = "2.5.0"
description = "TESM-Lizenzserver -- Kunden, Tickets, Lizenzen, Aktivierung"
requires-python = ">=3.11"
dependencies = [
@@ -13,7 +13,7 @@ from tesm_core.extension import core, db, transaction
from ..services import transfer
from .common import log_event
bp = Blueprint("backup", __name__, url_prefix="/sicherung")
bp = Blueprint("backup", __name__, url_prefix="/administration/backup")
MAX_IMPORT_BYTES = 64 * 1024 * 1024
PREVIEW_SESSION_KEY = "_import_token"
@@ -94,7 +94,7 @@ def export():
)
@bp.post("/import/vorschau")
@bp.post("/import/preview")
@permission_required("backup.execute")
@reauth_required
def import_preview():
@@ -131,7 +131,7 @@ def import_preview():
return redirect(url_for("backup.overview"))
@bp.post("/import/uebernehmen")
@bp.post("/import/apply")
@permission_required("backup.execute")
@reauth_required
def import_apply():
@@ -190,7 +190,7 @@ def import_apply():
return redirect(url_for("backup.overview"))
@bp.post("/import/verwerfen")
@bp.post("/import/discard")
@permission_required("backup.execute")
def import_discard():
token = session.pop(PREVIEW_SESSION_KEY, "")
@@ -9,7 +9,7 @@ from tesm_core.extension import db, transaction
from ..services import issuance, reporting
from .common import customer_facing_url, log_event
bp = Blueprint("customers", __name__, url_prefix="/kunden")
bp = Blueprint("customers", __name__, url_prefix="/customers")
FIELDS = (
"name",
@@ -61,7 +61,7 @@ def detail(customer_id: int):
)
@bp.post("/neu")
@bp.post("/new")
@permission_required("customers.create")
def create():
try:
@@ -103,7 +103,7 @@ def update(customer_id: int):
return redirect(url_for("customers.detail", customer_id=customer_id))
@bp.post("/<int:customer_id>/loeschen")
@bp.post("/<int:customer_id>/delete")
@permission_required("customers.delete")
def delete(customer_id: int):
conn = db()
@@ -16,7 +16,7 @@ from ..services import issuance, reporting
bp = Blueprint("dashboard", __name__)
@bp.get("/uebersicht")
@bp.get("/overview")
@login_required
def index():
conn = db()
@@ -38,7 +38,7 @@ from .common import (
vendor_info,
)
bp = Blueprint("licenses", __name__, url_prefix="/lizenzen")
bp = Blueprint("licenses", __name__, url_prefix="/licenses")
@bp.get("/")
@@ -49,7 +49,7 @@ def overview():
{**row, **issuance.display_state(row)}
for row in issuance.list_licenses(
conn,
customer_id=request.args.get("kunde", type=int),
customer_id=request.args.get("customer", type=int),
ticket_id=request.args.get("ticket"),
status=request.args.get("status"),
)
@@ -99,7 +99,7 @@ def detail(license_id: str):
# --------------------------------------------------------------------------- #
@bp.post("/aus-ticket/<ticket_id>")
@bp.post("/from-ticket/<ticket_id>")
@permission_required("licenses.create")
def issue(ticket_id: str):
conn = db()
@@ -140,7 +140,7 @@ def issue(ticket_id: str):
return redirect(url_for("licenses.detail", license_id=result.license_id))
@bp.get("/<license_id>/datei")
@bp.get("/<license_id>/file")
@permission_required("licenses.export")
# Die frische Bestaetigung verlangt das Recht selbst (SENSITIVE_ACTIONS).
def download(license_id: str):
@@ -181,7 +181,7 @@ def download(license_id: str):
)
@bp.post("/<license_id>/neu-signieren")
@bp.post("/<license_id>/resign")
@permission_required("licenses.edit")
@reauth_required
def resign(license_id: str):
@@ -240,7 +240,7 @@ def resign(license_id: str):
return redirect(url_for("licenses.detail", license_id=license_id))
@bp.post("/<license_id>/widerrufen")
@bp.post("/<license_id>/revoke")
@permission_required("licenses.edit")
def revoke(license_id: str):
conn = db()
@@ -265,7 +265,7 @@ def revoke(license_id: str):
return redirect(url_for("licenses.detail", license_id=license_id))
@bp.post("/<license_id>/loeschen")
@bp.post("/<license_id>/delete")
@permission_required("licenses.delete")
def delete(license_id: str):
conn = db()
@@ -286,7 +286,7 @@ def delete(license_id: str):
# --------------------------------------------------------------------------- #
@bp.post("/<license_id>/versenden")
@bp.post("/<license_id>/send")
@permission_required("licenses.export")
def send_mail(license_id: str):
conn = db()
@@ -428,7 +428,7 @@ def manual_code():
# --------------------------------------------------------------------------- #
@bp.get("/sub-lizenzserver")
@bp.get("/sub-servers")
@permission_required("reports.view")
def reports():
conn = db()
@@ -440,7 +440,7 @@ def reports():
)
@bp.get("/sub-lizenzserver/<license_id>")
@bp.get("/sub-servers/<license_id>")
@permission_required("reports.view")
def report_detail(license_id: str):
conn = db()
@@ -36,7 +36,7 @@ from .. import is_master_edition
from ..services import master_key
from .common import can_issue_license_server_licenses, log_event, signing_key
bp = Blueprint("own_license", __name__, url_prefix="/eigene-lizenz")
bp = Blueprint("own_license", __name__, url_prefix="/own-license")
MAX_UPLOAD_BYTES = 256 * 1024
@@ -68,7 +68,7 @@ def overview():
)
@bp.post("/einspielen")
@bp.post("/install")
@permission_required("settings_license.edit")
def upload():
extension = core()
@@ -129,13 +129,13 @@ def _handshake(action: str):
return redirect(url_for("own_license.overview"))
@bp.post("/aktivieren")
@bp.post("/activate")
@permission_required("settings_license.edit")
def activate():
return _handshake("activate")
@bp.post("/deaktivieren")
@bp.post("/deactivate")
@permission_required("settings_license.edit")
@reauth_required
def deactivate():
@@ -148,7 +148,7 @@ def heartbeat():
return _handshake("heartbeat")
@bp.post("/offline-antwort")
@bp.post("/offline-response")
@permission_required("settings_license.edit")
def offline_response():
manager = core().license
@@ -166,7 +166,7 @@ def offline_response():
return redirect(url_for("own_license.overview"))
@bp.get("/signaturschluessel")
@bp.get("/signing-key")
@permission_required("settings_license.view")
def signing_key_info():
"""Zeigt den oeffentlichen Signaturschluessel -- zum Pinnen beim Kunden.
@@ -31,6 +31,8 @@ from .common import (
vendor_info,
)
# Die Adresse steht in verschickten Mails und in Tickets -- sie bleibt
# deutsch, waehrend der Rest der Anwendung auf englische Pfade wechselt.
bp = Blueprint("selfservice", __name__, url_prefix="/lizenz")
RATE_BUCKET = "selfservice"
@@ -17,7 +17,7 @@ from tesm_core.extension import core, db, transaction
from ..services import mail
from .common import log_event, master_endpoint, vendor_info
bp = Blueprint("settings", __name__, url_prefix="/einstellungen")
bp = Blueprint("settings", __name__, url_prefix="/administration")
MAX_LOGO_BYTES = 200 * 1024
LOGO_MIME = {
@@ -68,6 +68,7 @@ def system():
timezones=_timezone_choices(),
nav_items=extension.nav.items,
nav_order=extension.settings.get(conn, "nav_order") or extension.nav.default_order(),
nav_child_order=extension.settings.get(conn, "nav_child_order") or {},
sysops_available=bool(sysops and sysops.available),
)
@@ -142,7 +143,7 @@ def set_hostname():
return redirect(url_for("settings.system"))
@bp.post("/system/zeitzone")
@bp.post("/system/timezone")
@permission_required("settings_system.execute")
def set_timezone():
import time
@@ -173,7 +174,7 @@ def set_timezone():
# --------------------------------------------------------------------------- #
@bp.get("/anbieter")
@bp.get("/vendor")
@permission_required("settings_vendor.view")
def vendor():
extension = core()
@@ -195,7 +196,7 @@ def vendor():
)
@bp.post("/anbieter")
@bp.post("/vendor")
@permission_required("settings_vendor.edit")
def vendor_save():
extension = core()
@@ -245,7 +246,7 @@ def vendor_save():
return redirect(url_for("settings.vendor"))
@bp.post("/anbieter/logo-entfernen")
@bp.post("/vendor/remove-logo")
@permission_required("settings_vendor.edit")
def vendor_logo_remove():
with transaction() as conn:
@@ -45,7 +45,7 @@ def overview():
)
@bp.get("/neu")
@bp.get("/new")
@permission_required("tickets.create")
def new():
conn = db()
@@ -60,11 +60,11 @@ def new():
type_labels=type_labels(),
default_valid_days=int(extension.settings.get(conn, "default_valid_days") or 365),
may_create_license_server=can_issue_license_server_licenses(),
preselected_customer=request.args.get("kunde", type=int),
preselected_customer=request.args.get("customer", type=int),
)
@bp.post("/neu")
@bp.post("/new")
@permission_required("tickets.create")
def create():
conn = db()
@@ -186,7 +186,7 @@ def update(ticket_id: str):
return redirect(url_for("tickets.detail", ticket_id=ticket_id))
@bp.post("/<ticket_id>/sperre")
@bp.post("/<ticket_id>/lock")
@permission_required("tickets.edit")
def toggle_block(ticket_id: str):
conn = db()
@@ -213,7 +213,7 @@ def toggle_block(ticket_id: str):
return redirect(url_for("tickets.detail", ticket_id=ticket_id))
@bp.post("/<ticket_id>/loeschen")
@bp.post("/<ticket_id>/delete")
@permission_required("tickets.delete")
def delete(ticket_id: str):
conn = db()
@@ -14,7 +14,7 @@
{% block topbar_extra %}
{% if current_user.has('tickets.create') %}
<a class="btn btn--primary btn--sm" href="{{ url_for('tickets.new', kunde=customer.id) }}">
<a class="btn btn--primary btn--sm" href="{{ url_for('tickets.new', customer=customer.id) }}">
{{ icon('plus', size=15) }} Ticket anlegen
</a>
{% endif %}
@@ -1,5 +1,5 @@
{% extends "tesm_core/base.html" %}
{% from "tesm_core/macros.html" import field, select_field, checkbox, alert %}
{% from "tesm_core/macros.html" import field, select_field, checkbox, alert, nav_order_card %}
{% block page_title %}Systemeinstellungen{% endblock %}
{% block page_sub %}Portal, Self-Service, Protokolle, Host{% endblock %}
@@ -104,33 +104,9 @@
</section>
{% if current_user.is_admin %}
<section class="card">
<div class="card__header">
<div class="flex-1">
<div class="card__title">{{ icon('sliders', size=17) }} Navigationsreihenfolge</div>
<div class="card__sub">Gilt für alle Benutzer</div>
</div>
</div>
<form method="post" action="{{ url_for('admin.nav_order') }}">
<input type="hidden" name="{{ csrf_field_name }}" value="{{ csrf_token() }}">
<div class="card__body">
<ol class="stack stack--tight">
{% for key in nav_order %}
{% set item = nav_items | selectattr('key', 'equalto', key) | list | first %}
{% if item %}
<li class="row row--tight">
<input type="hidden" name="order" value="{{ item.key }}">
<span class="flex-1">{{ icon(item.icon, size=15) }} {{ item.label }}</span>
</li>
{% endif %}
{% endfor %}
</ol>
</div>
<div class="card__footer">
<button class="btn btn--sm" type="submit">Reihenfolge speichern</button>
</div>
</form>
</section>
{{ nav_order_card(nav_items, nav_order, nav_child_order,
url_for('admin.nav_order'), csrf_field_name, csrf_token(),
sub='Gilt für alle Benutzer') }}
{% endif %}
</div>
</div>