diff --git a/main.py b/main.py index d9e40bb..2a0c78d 100644 --- a/main.py +++ b/main.py @@ -54,6 +54,110 @@ def ensure_default_admin(): if os.environ.get('SEED_ADMIN_DISABLED') != '1': ensure_default_admin() + +# Seed two test users (non-admin) for demos +def ensure_test_users(): + tests = [ + ('test1', 'test1@example.com', 'Пользователь 1'), + ('test2', 'test2@example.com', 'Пользователь 2'), + ] + for username, email, full_name in tests: + u = User.get_or_none(User.username == username) + if not u: + User.create( + username=username, + email=email, + full_name=full_name, + password_hash=generate_password_hash(os.environ.get('TEST_USER_PASSWORD', '1234')), + is_admin=False, + ) + +if os.environ.get('SEED_TEST_USERS_DISABLED') != '1': + ensure_test_users() + + +@app.route('/admin/users', methods=['GET', 'POST']) +@admin_required +def manage_users(): + if request.method == 'POST': + # Create user + username = request.form['username'].strip() + email = request.form['email'].strip() + full_name = request.form.get('full_name', '').strip() + password = request.form['password'] + is_admin_flag = request.form.get('is_admin') == 'on' + + if not username or not email or not password: + flash('Заполните обязательные поля', 'danger') + return redirect(url_for('manage_users')) + if User.select().where((User.username == username) | (User.email == email)).exists(): + flash('Пользователь с таким логином или email уже существует', 'danger') + return redirect(url_for('manage_users')) + + User.create( + username=username, + email=email, + full_name=full_name or None, + password_hash=generate_password_hash(password), + is_admin=is_admin_flag, + ) + flash('Пользователь создан', 'success') + return redirect(url_for('manage_users')) + + users = User.select().order_by(User.id) + return render_template('admin/users.html', users=users, title='Пользователи') + + +@app.route('/admin/users//reset_password', methods=['POST']) +@admin_required +def admin_reset_password(user_id): + user = User.get_or_none(User.id == user_id) + if not user: + flash('Пользователь не найден', 'danger') + return redirect(url_for('manage_users')) + new_password = request.form.get('new_password') + if not new_password: + flash('Укажите новый пароль', 'danger') + return redirect(url_for('manage_users')) + user.password_hash = generate_password_hash(new_password) + user.save() + flash('Пароль обновлён', 'success') + return redirect(url_for('manage_users')) + + +@app.route('/admin/users//toggle_admin', methods=['POST']) +@admin_required +def admin_toggle_admin(user_id): + if current_user.id == user_id: + flash('Нельзя менять свои собственные права', 'warning') + return redirect(url_for('manage_users')) + user = User.get_or_none(User.id == user_id) + if not user: + flash('Пользователь не найден', 'danger') + return redirect(url_for('manage_users')) + user.is_admin = not user.is_admin + user.save() + flash('Права обновлены', 'success') + return redirect(url_for('manage_users')) + + +@app.route('/admin/users//delete', methods=['POST']) +@admin_required +def admin_delete_user(user_id): + if current_user.id == user_id: + flash('Нельзя удалить самого себя', 'warning') + return redirect(url_for('manage_users')) + user = User.get_or_none(User.id == user_id) + if not user: + flash('Пользователь не найден', 'danger') + return redirect(url_for('manage_users')) + try: + user.delete_instance(recursive=True) + flash('Пользователь удалён', 'success') + except Exception: + flash('Не удалось удалить пользователя', 'danger') + return redirect(url_for('manage_users')) + login_manager = LoginManager() login_manager.init_app(app) login_manager.login_view = 'login' diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..655a441 --- /dev/null +++ b/static/style.css @@ -0,0 +1,8 @@ +/* Modern tweaks */ +:root { --brand-bg: #e9f6ff; } +body { background-color: #f6f8fb; } +.navbar { background: var(--brand-bg) !important; border-bottom: 1px solid rgba(0,0,0,0.05); } +.card { border: 1px solid rgba(0,0,0,0.05); box-shadow: 0 2px 8px rgba(0,0,0,0.04); } +.table thead th { font-weight: 600; color: #495057; } +.btn { transition: all .2s ease; } +.btn:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(0,0,0,0.08); } diff --git a/templates/admin/users.html b/templates/admin/users.html new file mode 100644 index 0000000..2e5d8f3 --- /dev/null +++ b/templates/admin/users.html @@ -0,0 +1,84 @@ +{% extends "layout.html" %} +{% block content %} +
+
+

Пользователи

+ К опросам +
+ +
+
+
Создать пользователя
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+
+ +
+
+
+
+ +
+
+
Список пользователей
+
+ + + + + + + + + + + + + {% for u in users %} + + + + + + + + + {% endfor %} + +
#ЛогинФИОEmailРольДействия
{{ u.id }}{{ u.username }}{{ u.full_name or '—' }}{{ u.email }} + {% if u.is_admin %}Админ{% else %}Пользователь{% endif %} + +
+ + +
+
+ +
+
+ +
+
+
+
+
+
+{% endblock %} + diff --git a/templates/layout.html b/templates/layout.html index 591105f..ac4f9fe 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -7,6 +7,7 @@ +