diff --git a/gui/app.py b/gui/app.py index 7c04688..6555a3a 100644 --- a/gui/app.py +++ b/gui/app.py @@ -250,14 +250,6 @@ def to_png_b64(text): return base64.b64encode(buf.getvalue()).decode("ascii") -def _unauthorized(): - return Response( - "Auth required", - 401, - {"WWW-Authenticate": 'Basic realm="WG Admin"'}, - ) - - def _get_csrf_token(): if "csrf_token" not in session: session["csrf_token"] = secrets.token_hex(32) @@ -266,18 +258,19 @@ def _get_csrf_token(): app.jinja_env.globals["csrf_token"] = _get_csrf_token +PUBLIC_PATHS = {"/login", "/static/"} + @app.before_request def _auth(): if request.path.startswith("/static/"): return None + if request.path == "/login": + return None if not ADMIN_PASSWORD: return None - auth = request.authorization - if not auth: - return _unauthorized() - if auth.username != ADMIN_USER or auth.password != ADMIN_PASSWORD: - return _unauthorized() + if not session.get("logged_in"): + return redirect(url_for("login", next=request.path)) return None @@ -285,12 +278,37 @@ def _auth(): def _csrf_check(): if request.method != "POST": return None + if request.path == "/login": + return None token = request.form.get("csrf_token") if not token or token != session.get("csrf_token"): return Response("CSRF token invalid", 403) return None +@app.route("/login", methods=["GET", "POST"]) +def login(): + if session.get("logged_in"): + return redirect(url_for("index")) + error = None + if request.method == "POST": + user = request.form.get("username", "").strip() + pwd = request.form.get("password", "") + if user == ADMIN_USER and pwd == ADMIN_PASSWORD: + session.clear() + session["logged_in"] = True + session.permanent = True + return redirect(request.args.get("next") or url_for("index")) + error = "Неверный логин или пароль" + return render_template("login.html", error=error) + + +@app.route("/logout") +def logout(): + session.clear() + return redirect(url_for("login")) + + @app.route("/") def index(): meta = load_meta() diff --git a/gui/static/style.css b/gui/static/style.css index 1e18837..eab8639 100644 --- a/gui/static/style.css +++ b/gui/static/style.css @@ -82,6 +82,27 @@ body { .sidebar-nav a.active { background: #eff4ff; color: var(--accent); } .sidebar-nav a.active svg { color: var(--accent); } +.sidebar-footer { + margin-top: auto; + padding: 12px 10px; + border-top: 1px solid var(--border); +} + +.logout-btn { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + border-radius: 8px; + font-size: 13px; + font-weight: 500; + color: var(--text-muted); + text-decoration: none; + transition: background .15s, color .15s; +} + +.logout-btn:hover { background: #fee2e2; color: #dc2626; } + /* ─── Layout ────────────────────────────────────────────────── */ .layout { margin-left: var(--sidebar-w); flex: 1; display: flex; flex-direction: column; } main { padding: 28px 32px; flex: 1; max-width: 1400px; width: 100%; } diff --git a/gui/templates/base.html b/gui/templates/base.html index 1518a7b..6c82966 100644 --- a/gui/templates/base.html +++ b/gui/templates/base.html @@ -26,6 +26,12 @@ Скрипты +
diff --git a/gui/templates/login.html b/gui/templates/login.html new file mode 100644 index 0000000..c6e1aa5 --- /dev/null +++ b/gui/templates/login.html @@ -0,0 +1,200 @@ + + + + + + WG Admin — Вход + + + +
+ + +

Добро пожаловать

+

Войдите в панель управления

+ + {% if error %} +
+ + + + {{ error }} +
+ {% endif %} + +
+
+ + +
+
+ + +
+ +
+ +
+ +
+ +