import logging from sqladmin import ModelView, action from starlette.requests import Request from starlette.responses import RedirectResponse from app.backoffice.user_view_actions import ( handle_block_users, handle_deduct_credits, handle_grant_credits, handle_set_role, ) from app.user.models import RefreshToken, SocialAccount, User logger = logging.getLogger(__name__) class UserAdmin(ModelView, model=User): name = "사용자" name_plural = "사용자 목록" icon = "fa-solid fa-user" category = "사용자 관리" page_size = 20 column_list = [ "id", "kakao_id", "email", "nickname", "credits", "role", "is_active", "is_deleted", "created_at", ] column_details_list = [ "id", "kakao_id", "email", "nickname", "profile_image_url", "thumbnail_image_url", "phone", "name", "birth_date", "gender", "credits", "is_active", "is_admin", "role", "is_deleted", "deleted_at", "last_login_at", "created_at", "updated_at", "credit_requests", "credit_transactions", ] form_columns = [ "nickname", "email", "phone", "name", "birth_date", "gender", "credits", "is_active", "is_admin", "role", "is_deleted", ] column_searchable_list = [ User.kakao_id, User.email, User.nickname, User.phone, User.name, ] column_default_sort = (User.created_at, True) column_sortable_list = [ User.id, User.kakao_id, User.email, User.nickname, User.credits, User.role, User.is_active, User.is_deleted, User.created_at, ] column_labels = { "id": "ID", "kakao_id": "카카오 ID", "email": "이메일", "nickname": "닉네임", "profile_image_url": "프로필 이미지", "thumbnail_image_url": "썸네일 이미지", "phone": "전화번호", "name": "실명", "birth_date": "생년월일", "gender": "성별", "credits": "크레딧", "is_active": "활성화", "is_admin": "관리자", "role": "권한", "is_deleted": "삭제됨", "deleted_at": "삭제일시", "last_login_at": "마지막 로그인", "created_at": "생성일시", "updated_at": "수정일시", } @action( name="block_user", label="계정 차단", confirmation_message="선택한 사용자를 차단하시겠습니까? 로그인이 불가해집니다.", add_in_list=True, ) async def block_user_action(self, request: Request) -> RedirectResponse: return await handle_block_users(request, self.identity, block=True) @action( name="unblock_user", label="차단 해제", confirmation_message="선택한 사용자의 차단을 해제하시겠습니까?", add_in_list=True, ) async def unblock_user_action(self, request: Request) -> RedirectResponse: return await handle_block_users(request, self.identity, block=False) @action( name="set_role_admin", label="권한: admin으로 변경", confirmation_message="선택한 사용자를 admin으로 변경하시겠습니까?", add_in_list=True, ) async def set_role_admin_action(self, request: Request) -> RedirectResponse: return await handle_set_role(request, self.identity, role="admin") @action( name="set_role_user", label="권한: user로 변경", confirmation_message="선택한 사용자를 일반 user로 변경하시겠습니까?", add_in_list=True, ) async def set_role_user_action(self, request: Request) -> RedirectResponse: return await handle_set_role(request, self.identity, role="user") @action( name="grant_credits_1", label="크레딧 +1 충전", confirmation_message="선택한 사용자에게 크레딧 1개를 충전하시겠습니까?", add_in_list=True, ) async def grant_credits_1_action(self, request: Request) -> RedirectResponse: admin_id = request.session.get("admin_id") return await handle_grant_credits(request, self.identity, amount=1, admin_id=admin_id) @action( name="grant_credits_5", label="크레딧 +5 충전", confirmation_message="선택한 사용자에게 크레딧 5개를 충전하시겠습니까?", add_in_list=True, ) async def grant_credits_5_action(self, request: Request) -> RedirectResponse: admin_id = request.session.get("admin_id") return await handle_grant_credits(request, self.identity, amount=5, admin_id=admin_id) @action( name="grant_credits_10", label="크레딧 +10 충전", confirmation_message="선택한 사용자에게 크레딧 10개를 충전하시겠습니까?", add_in_list=True, ) async def grant_credits_10_action(self, request: Request) -> RedirectResponse: admin_id = request.session.get("admin_id") return await handle_grant_credits(request, self.identity, amount=10, admin_id=admin_id) @action( name="deduct_credits_1", label="크레딧 -1 차감", confirmation_message="선택한 사용자의 크레딧 1개를 차감하시겠습니까?", add_in_list=True, ) async def deduct_credits_1_action(self, request: Request) -> RedirectResponse: admin_id = request.session.get("admin_id") return await handle_deduct_credits(request, self.identity, amount=1, admin_id=admin_id) class RefreshTokenAdmin(ModelView, model=RefreshToken): name = "리프레시 토큰" name_plural = "리프레시 토큰 목록" icon = "fa-solid fa-key" category = "사용자 관리" page_size = 20 column_list = [ "id", "user_id", "is_revoked", "expires_at", "created_at", ] column_details_list = [ "id", "user_id", "token_hash", "expires_at", "is_revoked", "created_at", "revoked_at", "user_agent", "ip_address", ] form_excluded_columns = ["created_at", "user"] column_searchable_list = [ RefreshToken.user_id, RefreshToken.token_hash, RefreshToken.ip_address, ] column_default_sort = (RefreshToken.created_at, True) column_sortable_list = [ RefreshToken.id, RefreshToken.user_id, RefreshToken.is_revoked, RefreshToken.expires_at, RefreshToken.created_at, ] column_labels = { "id": "ID", "user_id": "사용자 ID", "token_hash": "토큰 해시", "expires_at": "만료일시", "is_revoked": "폐기됨", "created_at": "생성일시", "revoked_at": "폐기일시", "user_agent": "User Agent", "ip_address": "IP 주소", } class SocialAccountAdmin(ModelView, model=SocialAccount): name = "소셜 계정" name_plural = "소셜 계정 목록" icon = "fa-solid fa-share-nodes" category = "사용자 관리" page_size = 20 column_list = [ "id", "user_uuid", "platform", "platform_username", "is_active", "is_deleted", "created_at", ] column_details_list = [ "id", "user_uuid", "platform", "platform_user_id", "platform_username", "platform_data", "scope", "token_expires_at", "is_active", "is_deleted", "created_at", "updated_at", ] form_excluded_columns = ["created_at", "updated_at", "user"] column_searchable_list = [ SocialAccount.user_uuid, SocialAccount.platform, SocialAccount.platform_user_id, SocialAccount.platform_username, ] column_default_sort = (SocialAccount.created_at, True) column_sortable_list = [ SocialAccount.id, SocialAccount.user_uuid, SocialAccount.platform, SocialAccount.is_active, SocialAccount.is_deleted, SocialAccount.created_at, ] column_labels = { "id": "ID", "user_uuid": "사용자 UUID", "platform": "플랫폼", "platform_user_id": "플랫폼 사용자 ID", "platform_username": "플랫폼 사용자명", "platform_data": "플랫폼 데이터", "scope": "권한 범위", "token_expires_at": "토큰 만료일시", "is_active": "활성화", "is_deleted": "삭제됨", "created_at": "생성일시", "updated_at": "수정일시", }