Initial commit
This commit is contained in:
126
backend/routes/admin.py
Normal file
126
backend/routes/admin.py
Normal file
@@ -0,0 +1,126 @@
|
||||
# backend/routes/admin.py
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from functools import wraps
|
||||
from typing import Callable
|
||||
|
||||
from flask import (
|
||||
Blueprint,
|
||||
render_template,
|
||||
redirect,
|
||||
url_for,
|
||||
flash,
|
||||
abort,
|
||||
request,
|
||||
current_app,
|
||||
)
|
||||
from flask_login import login_required, current_user
|
||||
|
||||
from backend.models.user import User, db
|
||||
|
||||
admin_bp = Blueprint("admin", __name__)
|
||||
|
||||
|
||||
# Blueprint 등록
|
||||
def register_admin_routes(app):
|
||||
app.register_blueprint(admin_bp)
|
||||
|
||||
|
||||
# 관리자 권한 데코레이터
|
||||
def admin_required(view_func: Callable):
|
||||
@wraps(view_func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if not current_user.is_authenticated:
|
||||
return redirect(url_for("auth.login"))
|
||||
if not getattr(current_user, "is_admin", False):
|
||||
flash("관리자 권한이 필요합니다.", "danger")
|
||||
return redirect(url_for("main.index"))
|
||||
return view_func(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
# 관리자 대시보드
|
||||
@admin_bp.route("/admin", methods=["GET"])
|
||||
@login_required
|
||||
@admin_required
|
||||
def admin_panel():
|
||||
users = db.session.query(User).order_by(User.id.asc()).all()
|
||||
return render_template("admin.html", users=users)
|
||||
|
||||
|
||||
# 사용자 승인
|
||||
@admin_bp.route("/admin/approve/<int:user_id>", methods=["GET"])
|
||||
@login_required
|
||||
@admin_required
|
||||
def approve_user(user_id: int):
|
||||
user = db.session.get(User, user_id)
|
||||
if not user:
|
||||
abort(404)
|
||||
user.is_active = True
|
||||
db.session.commit()
|
||||
flash("사용자가 승인되었습니다.", "success")
|
||||
logging.info("✅ 승인된 사용자: %s (id=%s)", user.username, user.id)
|
||||
return redirect(url_for("admin.admin_panel"))
|
||||
|
||||
|
||||
# 사용자 삭제
|
||||
@admin_bp.route("/admin/delete/<int:user_id>", methods=["GET"])
|
||||
@login_required
|
||||
@admin_required
|
||||
def delete_user(user_id: int):
|
||||
user = db.session.get(User, user_id)
|
||||
if not user:
|
||||
abort(404)
|
||||
username = user.username
|
||||
db.session.delete(user)
|
||||
db.session.commit()
|
||||
flash("사용자가 삭제되었습니다.", "success")
|
||||
logging.info("🗑 삭제된 사용자: %s (id=%s)", username, user_id)
|
||||
return redirect(url_for("admin.admin_panel"))
|
||||
|
||||
|
||||
# ▼▼▼ 사용자 비밀번호 변경(관리자용) ▼▼▼
|
||||
@admin_bp.route("/admin/users/<int:user_id>/reset_password", methods=["POST"])
|
||||
@login_required
|
||||
@admin_required
|
||||
def reset_password(user_id: int):
|
||||
"""
|
||||
admin.html에서 각 사용자 행 아래 폼으로부터 POST:
|
||||
- name="new_password"
|
||||
- name="confirm_password"
|
||||
CSRF는 템플릿에서 {{ csrf_token() }} 또는 {{ form.hidden_tag() }}로 포함되어야 합니다.
|
||||
"""
|
||||
new_pw = (request.form.get("new_password") or "").strip()
|
||||
confirm = (request.form.get("confirm_password") or "").strip()
|
||||
|
||||
# 서버측 검증
|
||||
if not new_pw or not confirm:
|
||||
flash("비밀번호와 확인 값을 모두 입력하세요.", "warning")
|
||||
return redirect(url_for("admin.admin_panel"))
|
||||
if new_pw != confirm:
|
||||
flash("비밀번호 확인이 일치하지 않습니다.", "warning")
|
||||
return redirect(url_for("admin.admin_panel"))
|
||||
if len(new_pw) < 8:
|
||||
flash("비밀번호는 최소 8자 이상이어야 합니다.", "warning")
|
||||
return redirect(url_for("admin.admin_panel"))
|
||||
|
||||
user = db.session.get(User, user_id)
|
||||
if not user:
|
||||
abort(404)
|
||||
|
||||
try:
|
||||
# passlib(Argon2id) 기반 set_password 사용 (models.user에 구현됨)
|
||||
user.set_password(new_pw)
|
||||
db.session.commit()
|
||||
flash(f"사용자(ID={user.id}) 비밀번호를 변경했습니다.", "success")
|
||||
current_app.logger.info(
|
||||
"ADMIN: reset password for user_id=%s by admin_id=%s",
|
||||
user.id, current_user.id
|
||||
)
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
current_app.logger.exception("ADMIN: reset password failed: %s", e)
|
||||
flash("비밀번호 변경 중 오류가 발생했습니다.", "danger")
|
||||
|
||||
return redirect(url_for("admin.admin_panel"))
|
||||
Reference in New Issue
Block a user