Add first-run setup wizard, README, remove default users, add easter egg

This commit is contained in:
InviteMe Developer
2026-08-31 13:27:32 +03:00
parent 391650cf88
commit 8120ab4c3c
7 changed files with 167 additions and 112 deletions
-35
View File
@@ -36,7 +36,6 @@ def create_app(config_class=Config):
with app.app_context():
db.create_all()
_migrate_existing_data(app)
_create_default_users(app)
_load_smtp_settings(app)
return app
@@ -197,37 +196,3 @@ def _migrate_existing_data(app):
db.session.rollback()
logger.warning(f"Migration skipped or failed: {e}")
def _create_default_users(app):
"""Создание начальных пользователей, если в базе нет ни одного."""
from app.models import User
try:
if User.query.first() is not None:
return
admin = User(
username=app.config.get("ADMIN_USERNAME", "admin"),
role="admin",
is_active=True,
)
admin.set_password(app.config.get("ADMIN_PASSWORD", "admin"))
staff = User(
username=app.config.get("STAFF_USERNAME", "staff"),
role="staff",
is_active=True,
)
staff.set_password(app.config.get("STAFF_PASSWORD", "staff"))
db.session.add(admin)
db.session.add(staff)
db.session.commit()
if app.config.get("ADMIN_PASSWORD") == "admin" or app.config.get("STAFF_PASSWORD") == "staff":
logger.warning("Default users created with default passwords. Change them in production!")
else:
logger.info("Default users 'admin' and 'staff' created from environment variables.")
except Exception as e:
db.session.rollback()
logger.warning(f"Default users creation skipped or failed: {e}")
+46
View File
@@ -147,9 +147,52 @@ def _guest_matches_search(guest, query):
# Auth
# ---------------------------
def _is_first_run():
"""True, если в базе ещё нет ни одного пользователя."""
return User.query.first() is None
@main_bp.route("/setup", methods=["GET", "POST"])
@limiter.limit("10 per minute", methods=["POST"])
def setup():
"""Первоначальная настройка: создание первого администратора."""
if not _is_first_run():
return redirect(url_for("main.login"))
if request.method == "POST":
username = request.form.get("username", "").strip()
password = request.form.get("password", "").strip()
password_confirm = request.form.get("password_confirm", "").strip()
if not username or not password:
flash("Логин и пароль обязательны", "danger")
return redirect(url_for("main.setup"))
if password != password_confirm:
flash("Пароли не совпадают", "danger")
return redirect(url_for("main.setup"))
if User.query.filter_by(username=username).first():
flash("Пользователь с таким логином уже существует", "danger")
return redirect(url_for("main.setup"))
admin = User(username=username, role="admin", is_active=True)
admin.set_password(password)
db.session.add(admin)
db.session.commit()
flash("Первый администратор создан. Войдите в систему.", "success")
return redirect(url_for("main.login"))
return render_template("setup.html")
@main_bp.route("/login", methods=["GET", "POST"])
@limiter.limit("10 per minute", methods=["POST"])
def login():
if _is_first_run():
return redirect(url_for("main.setup"))
if request.method == "POST":
username = request.form.get("username", "").strip()
password = request.form.get("password", "").strip()
@@ -1341,6 +1384,9 @@ def api_guest_detail(token):
@main_bp.route("/")
def index():
if _is_first_run():
return redirect(url_for("main.setup"))
user = current_user()
if not user:
return redirect(url_for("main.login"))
+5
View File
@@ -7,6 +7,7 @@
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<link rel="icon" type="image/png" href="{{ url_for('static', filename='images/inviteme_logo.png') }}">
<!-- InviteMe — crafted for mycloud24.ru -->
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark">
@@ -79,5 +80,9 @@
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
{% block scripts %}{% endblock %}
<footer class="mt-auto py-4 text-center">
<small class="text-muted" style="opacity: 0.25; font-size: 0.7rem;">mycloud24.ru</small>
</footer>
</body>
</html>
+37
View File
@@ -0,0 +1,37 @@
{% extends "base.html" %}
{% block title %}Первоначальная настройка — InviteMe{% endblock %}
{% block content %}
<div class="row justify-content-center align-items-center" style="min-height: 70vh;">
<div class="col-md-5">
<div class="card mt-5">
<div class="card-body p-4">
<div class="text-center mb-4">
<img src="{{ url_for('static', filename='images/inviteme_logo.png') }}" alt="InviteMe" class="login-logo">
</div>
<h3 class="card-title text-center mb-3" style="color: var(--accent-color); text-shadow: 0 0 12px var(--accent-glow);">Добро пожаловать</h3>
<p class="text-muted text-center mb-4">
Создайте первого администратора, чтобы начать работу с InviteMe.
</p>
<form method="POST" action="{{ url_for('main.setup') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="mb-3">
<label for="username" class="form-label">Логин администратора</label>
<input type="text" class="form-control" id="username" name="username" required autofocus>
</div>
<div class="mb-3">
<label for="password" class="form-label">Пароль</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="mb-4">
<label for="password_confirm" class="form-label">Повторите пароль</label>
<input type="password" class="form-control" id="password_confirm" name="password_confirm" required>
</div>
<button type="submit" class="btn btn-primary w-100">Создать администратора</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}