From 963c29084c2e11556b58ab7a3ec44ec4cfe01859 Mon Sep 17 00:00:00 2001 From: apuc Date: Wed, 16 Sep 2026 12:30:30 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D1=81=D1=82=D0=B0=D1=8F=20?= =?UTF-8?q?=D1=80=D0=B5=D0=B3=D0=B8=D1=81=D1=82=D1=80=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8F:=20/register,=20=D0=B0=D0=B2=D1=82=D0=BE=D0=B2=D1=85?= =?UTF-8?q?=D0=BE=D0=B4,=20ALLOW=5FREGISTRATION,=20=D0=BB=D0=B8=D0=BC?= =?UTF-8?q?=D0=B8=D1=82=20=D0=B4=D0=BB=D1=8F=20=D0=BD=D0=BE=D0=B2=D1=8B?= =?UTF-8?q?=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 8 ++++- DEPLOY.md | 17 +++++++++- templates/login.html | 4 +++ templates/register.html | 72 +++++++++++++++++++++++++++++++++++++++++ webui.py | 43 +++++++++++++++++++++++- 5 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 templates/register.html diff --git a/.env.example b/.env.example index 0ff258c..1fc6031 100644 --- a/.env.example +++ b/.env.example @@ -14,4 +14,10 @@ SESSION_SECRET= HH_HEADLESS=0 # На сервере без sandbox (root/контейнер): 1 -HH_NO_SANDBOX=0 \ No newline at end of file +HH_NO_SANDBOX=0 + +# Регистрация: 1 = открытая (любой может создать аккаунт), 0 = только через manage_users.py +ALLOW_REGISTRATION=1 + +# Дневной лимит генераций для самостоятельно зарегистрированных пользователей +REGISTER_LLM_LIMIT=50 \ No newline at end of file diff --git a/DEPLOY.md b/DEPLOY.md index 5f03032..6a39f82 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -69,7 +69,9 @@ - ОС: **Debian 12 / Ubuntu 22.04+**, есть **root** - Домен или статический IP; порты **22, 80, 443** открыты -- Круг пользователей небольшой (2–10 человек), пользователей создаёт владелец (без открытой регистрации) +- Круг пользователей небольшой (2–10 человек). Два варианта: + - **Открытая регистрация** (по умолчанию): любой может создать аккаунт на странице `/register` + - **Закрытая** (`ALLOW_REGISTRATION=0` в `.env`): пользователей создаёт владелец через `manage_users.py` --- @@ -154,6 +156,19 @@ systemctl enable --now xvfb ## Этап 4 — пользователи +**Вариант А — открытая регистрация** (по умолчанию, `ALLOW_REGISTRATION=1`): +любой посетитель создаёт аккаунт сам на странице `/register` (логин + пароль, +сразу входит). Лимит генераций для таких пользователей — `REGISTER_LLM_LIMIT` +(по умолчанию 50 в день). Владельца всё равно стоит создать через CLI: + +```bash +# Владелец (лимит 50 генераций в день) +/opt/hh_auto/.venv/bin/python /opt/hh_auto/manage_users.py add admin --password СЛОЖНЫЙ_ПАРОЛЬ --limit 50 +``` + +**Вариант Б — закрытая регистрация** (`ALLOW_REGISTRATION=0` в `.env`): +пользователей создаёт только владелец: + ```bash # Владелец (лимит 50 генераций в день) /opt/hh_auto/.venv/bin/python /opt/hh_auto/manage_users.py add admin --password СЛОЖНЫЙ_ПАРОЛЬ --limit 50 diff --git a/templates/login.html b/templates/login.html index 2d2762a..c8c260f 100644 --- a/templates/login.html +++ b/templates/login.html @@ -29,6 +29,9 @@ button:hover { background: var(--accent-hover); } .error { color: var(--red); font-size: 13px; text-align: center; margin-top: 10px; min-height: 20px; } + .link { text-align: center; margin-top: 14px; font-size: 13px; } + .link a { color: var(--accent); text-decoration: none; } + .link a:hover { color: var(--accent-hover); } @@ -39,6 +42,7 @@
+ + + \ No newline at end of file diff --git a/webui.py b/webui.py index 6169884..8da55e2 100644 --- a/webui.py +++ b/webui.py @@ -22,7 +22,7 @@ from functools import wraps from flask import (Flask, jsonify, render_template, request, session, redirect, url_for, send_file) -from werkzeug.security import check_password_hash +from werkzeug.security import check_password_hash, generate_password_hash from hh_search import (search_vacancies, fetch_vacancy_details, fetch_my_resumes, parse_cookies, get_proxies) @@ -208,6 +208,47 @@ def login_page(): return render_template("login.html") +@app.route("/register", methods=["GET", "POST"]) +def register_page(): + """Простая регистрация: логин + пароль. Отключается через ALLOW_REGISTRATION=0.""" + if os.environ.get("ALLOW_REGISTRATION", "1") != "1": + if request.method == "POST": + return jsonify({"error": "Регистрация отключена"}), 403 + return redirect(url_for("login_page")) + + if request.method == "POST": + data = request.get_json(force=True) + username = (data.get("username") or "").strip() + password = data.get("password") or "" + confirm = data.get("confirm") or "" + + # Проверки + if not re.match(r"^[a-zA-Z0-9_.-]{3,30}$", username): + return jsonify({"error": "Логин: 3-30 символов, буквы/цифры/точка/дефис/подчёркивание"}), 400 + if len(password) < 6: + return jsonify({"error": "Пароль: минимум 6 символов"}), 400 + if password != confirm: + return jsonify({"error": "Пароли не совпадают"}), 400 + + users = load_users() + if username in users: + return jsonify({"error": "Логин уже занят"}), 409 + + limit = int(os.environ.get("REGISTER_LLM_LIMIT", "50") or 0) + users[username] = { + "password_hash": generate_password_hash(password), + "llm_limit": limit, + "llm_usage": {}, + "created": date.today().isoformat(), + "registered": True, + } + save_users(users) + os.makedirs(os.path.join(USERS_DIR, username, "accounts"), exist_ok=True) + session["user"] = username # сразу входим + return jsonify({"ok": True, "limit": limit}) + return render_template("register.html") + + @app.route("/logout") def logout(): session.clear()