LegalMind12 / routes /admin.py
Nguyendat92929's picture
Upload 91 files
08b74f7 verified
from flask import Blueprint, request, jsonify, session, render_template
from functools import wraps
from bson import ObjectId
from datetime import datetime
import logging
from datetime import datetime, timedelta
from utils.extensions import ext
from utils.email import send_email
admin_bp = Blueprint('admin', __name__)
def admin_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if 'user_id' not in session:
return jsonify({'error': 'Vui lòng đăng nhập'}), 401
try:
user = ext.db.users.find_one({'_id': ObjectId(session['user_id'])})
if not user or not user.get('is_admin'):
return jsonify({'error': 'Quyền truy cập bị từ chối. Chỉ admin được phép.'}), 403
return f(*args, **kwargs)
except Exception as e:
logging.error(f"Error in admin_required: {e}")
return jsonify({'error': 'Lỗi hệ thống'}), 500
return decorated_function
@admin_bp.route('/admin/dashboard')
@admin_required
def admin_dashboard():
try:
users = ext.db.users.find({'is_active': True}).sort('created_at', -1)
user_name = session.get('username')
# Get statistics
total_users = ext.db.users.count_documents({})
active_users = ext.db.users.count_documents({'is_active': True})
pending_users = ext.db.users.count_documents({'is_active': False})
admin_users = ext.db.users.count_documents({'is_admin': True})
# Get recent activity
recent_conversations = ext.db.conversations.find().sort('timestamp', -1).limit(10)
users_list = []
for user in users:
users_list.append({
'id': str(user['_id']),
'username': user['username'],
'email': user['email'],
'phone': user['phone'],
'is_active': user.get('is_active', False),
'is_admin': user.get('is_admin', False),
'account_type': user.get('account_type', 'limited'),
'query_limit': user.get('query_limit', None),
'query_count': user.get('query_count', 0),
'created_at': user.get('created_at', datetime.utcnow()).isoformat(),
'last_reset': user.get('last_reset', datetime.utcnow()).isoformat() if user.get('last_reset') else None,
'last_login': user.get('last_login', None)
})
recent_conversations_list = []
for conv in recent_conversations:
user = ext.db.users.find_one({'_id': ObjectId(conv['user_id'])})
recent_conversations_list.append({
'id': str(conv['_id']),
'title': conv.get('title', 'Không có tiêu đề'),
'user': user['username'] if user else 'Unknown',
'timestamp': conv['timestamp'].isoformat() if 'timestamp' in conv else None,
'message_count': ext.db.messages.count_documents({'conversation_id': str(conv['_id'])})
})
return render_template('admin_dashboard.html',
users=users_list,
user_name=user_name,
statistics={
'total_users': total_users,
'active_users': active_users,
'pending_users': pending_users,
'admin_users': admin_users
},
recent_conversations=recent_conversations_list)
except Exception as e:
logging.error(f"Error in admin_dashboard: {e}")
return jsonify({'error': 'Lỗi hệ thống'}), 500
@admin_bp.route('/admin/pending_users', methods=['GET'])
@admin_required
def get_pending_users():
try:
pending_users = ext.db.users.find({'is_active': False}).sort('created_at', -1)
users_list = []
for user in pending_users:
users_list.append({
'id': str(user['_id']),
'username': user['username'],
'email': user['email'],
'phone': user['phone'],
'created_at': user['created_at'].isoformat() if 'created_at' in user else None,
'account_type': user.get('account_type', 'limited'),
'days_pending': (datetime.utcnow() - user['created_at']).days if 'created_at' in user else 0
})
return jsonify({
'pending_users': users_list,
'count': len(users_list)
}), 200
except Exception as e:
logging.error(f"Error in get_pending_users: {e}")
return jsonify({'error': 'Lỗi hệ thống'}), 500
@admin_bp.route('/admin/user/<user_id>/verify', methods=['POST'])
@admin_required
def verify_user(user_id):
data = request.get_json(silent=True) or {}
action = data.get('action') # 'approve' or 'reject'
if action not in ['approve', 'reject']:
return jsonify({'error': 'Hành động không hợp lệ'}), 400
try:
if not ObjectId.is_valid(user_id):
return jsonify({'error': 'ID người dùng không hợp lệ'}), 400
user = ext.db.users.find_one({'_id': ObjectId(user_id)})
if not user:
return jsonify({'error': 'Người dùng không tồn tại'}), 404
if user.get('is_active') != False:
return jsonify({'error': 'Tài khoản không ở trạng thái chờ xác thực'}), 400
if action == 'approve':
ext.db.users.update_one(
{'_id': ObjectId(user_id)},
{'$set': {
'is_active': True,
'otp': None,
'verified_at': datetime.utcnow(),
'verified_by': session.get('user_id')
}}
)
send_email(
user['email'],
'Tài khoản đã được xác thực',
f'Tài khoản của bạn ({user["username"]}) đã được admin xác thực thành công. Bạn có thể đăng nhập.'
)
logging.info(f"Admin {session.get('user_id')} approved user {user_id}")
return jsonify({
'message': 'Xác thực tài khoản thành công',
'user': {
'id': user_id,
'username': user['username'],
'email': user['email']
}
}), 200
else: # reject
# Store reason for rejection if provided
reason = data.get('reason', 'Không có lý do cụ thể')
ext.db.users.delete_one({'_id': ObjectId(user_id)})
send_email(
user['email'],
'Tài khoản bị từ chối',
f'''Tài khoản của bạn ({user["username"]}) đã bị từ chối bởi admin.
Lý do: {reason}
Vui lòng liên hệ hỗ trợ nếu cần thêm thông tin.'''
)
logging.info(f"Admin {session.get('user_id')} rejected user {user_id}")
return jsonify({
'message': 'Từ chối tài khoản thành công',
'user': {
'id': user_id,
'username': user['username'],
'email': user['email']
}
}), 200
except Exception as e:
logging.error(f"Error in verify_user: {e}")
return jsonify({'error': 'Lỗi hệ thống'}), 500
@admin_bp.route('/admin/users', methods=['GET'])
@admin_required
def get_all_users():
try:
# Get pagination parameters
page = request.args.get('page', 1, type=int)
limit = request.args.get('limit', 20, type=int)
skip = (page - 1) * limit
# Get filter parameters
search = request.args.get('search', '')
account_type = request.args.get('account_type', '')
is_active = request.args.get('is_active', '')
# Build query
query = {}
if search:
query['$or'] = [
{'username': {'$regex': search, '$options': 'i'}},
{'email': {'$regex': search, '$options': 'i'}},
{'phone': {'$regex': search, '$options': 'i'}}
]
if account_type:
query['account_type'] = account_type
if is_active.lower() == 'true':
query['is_active'] = True
elif is_active.lower() == 'false':
query['is_active'] = False
# Get total count
total_users = ext.db.users.count_documents(query)
# Get users with pagination
users_cursor = ext.db.users.find(query) \
.sort('created_at', -1) \
.skip(skip) \
.limit(limit)
users_list = []
for user in users_cursor:
# Get user statistics
conversation_count = ext.db.conversations.count_documents({'user_id': str(user['_id'])})
message_count = ext.db.messages.count_documents({'conversation_id': {'$in': [
str(conv['_id']) for conv in ext.db.conversations.find({'user_id': str(user['_id'])})
]}})
users_list.append({
'id': str(user['_id']),
'username': user['username'],
'email': user['email'],
'phone': user['phone'],
'is_active': user.get('is_active', False),
'is_admin': user.get('is_admin', False),
'account_type': user.get('account_type', 'limited'),
'query_limit': user.get('query_limit', None),
'query_count': user.get('query_count', 0),
'created_at': user.get('created_at', datetime.utcnow()).isoformat(),
'last_reset': user.get('last_reset', datetime.utcnow()).isoformat() if user.get('last_reset') else None,
'last_login': user.get('last_login', None),
'statistics': {
'conversation_count': conversation_count,
'message_count': message_count
}
})
return jsonify({
'users': users_list,
'pagination': {
'page': page,
'limit': limit,
'total': total_users,
'pages': (total_users + limit - 1) // limit
},
'filters': {
'search': search,
'account_type': account_type,
'is_active': is_active
}
}), 200
except Exception as e:
logging.error(f"Error in get_all_users: {e}")
return jsonify({'error': 'Lỗi hệ thống'}), 500
@admin_bp.route('/admin/user/<user_id>', methods=['DELETE'])
@admin_required
def delete_user(user_id):
try:
if not ObjectId.is_valid(user_id):
return jsonify({
'error': 'ID người dùng không hợp lệ',
'error_code': 'INVALID_USER_ID'
}), 400
user = ext.db.users.find_one({'_id': ObjectId(user_id)})
if not user:
return jsonify({
'error': 'Người dùng không tồn tại',
'error_code': 'USER_NOT_FOUND'
}), 404
# Prevent deleting own admin account
if str(user['_id']) == session.get('user_id'):
return jsonify({
'error': 'Không thể xóa tài khoản của chính bạn',
'error_code': 'SELF_DELETION'
}), 403
if user.get('is_admin', False):
# Check if this is the last admin
admin_count = ext.db.users.count_documents({'is_admin': True})
if admin_count <= 1:
return jsonify({
'error': 'Không thể xóa admin cuối cùng',
'error_code': 'LAST_ADMIN'
}), 403
# Delete user conversations and messages
conversations = ext.db.conversations.find({'user_id': user_id})
for conv in conversations:
ext.db.messages.delete_many({'conversation_id': str(conv['_id'])})
ext.db.conversations.delete_many({'user_id': user_id})
# Delete user
result = ext.db.users.delete_one({'_id': ObjectId(user_id)})
if result.deleted_count > 0:
# Send notification email
send_email(
user['email'],
'Tài khoản của bạn đã bị xóa',
f'''Tài khoản của bạn ({user["username"]}) đã bị admin xóa.
Nếu bạn cho rằng đây là nhầm lẫn, vui lòng liên hệ hỗ trợ để được giải đáp.'''
)
logging.info(f"Admin {session.get('user_id')} deleted user {user_id}")
return jsonify({
'message': 'Xóa tài khoản thành công',
'deleted_user': {
'id': user_id,
'username': user['username'],
'email': user['email']
}
}), 200
else:
return jsonify({
'error': 'Không thể xóa người dùng',
'error_code': 'DELETE_FAILED'
}), 500
except Exception as e:
logging.error(f"Error deleting user {user_id}: {e}")
return jsonify({
'error': 'Lỗi khi xóa tài khoản',
'error_code': 'SERVER_ERROR'
}), 500
@admin_bp.route('/admin/user/<user_id>', methods=['PUT'])
@admin_required
def update_user(user_id):
data = request.get_json(silent=True) or {}
if not ObjectId.is_valid(user_id):
return jsonify({'error': 'ID người dùng không hợp lệ'}), 400
try:
user = ext.db.users.find_one({'_id': ObjectId(user_id)})
if not user:
return jsonify({'error': 'Người dùng không tồn tại'}), 404
updates = {}
# Update account type
if 'account_type' in data and data['account_type'] in ['limited', 'unlimited']:
updates['account_type'] = data['account_type']
updates['query_limit'] = 10 if data['account_type'] == 'limited' else None
updates['query_count'] = 0
updates['last_reset'] = datetime.utcnow()
# Update admin status
if 'is_admin' in data and isinstance(data['is_admin'], bool):
# Prevent removing last admin
if user.get('is_admin') and not data['is_admin']:
admin_count = ext.db.users.count_documents({'is_admin': True})
if admin_count <= 1:
return jsonify({'error': 'Không thể xóa quyền admin của admin cuối cùng'}), 403
updates['is_admin'] = data['is_admin']
# Update query limit for limited accounts
if 'query_limit' in data and isinstance(data['query_limit'], int):
if data['query_limit'] < 1:
return jsonify({'error': 'Giới hạn truy vấn phải lớn hơn 0'}), 400
updates['query_limit'] = data['query_limit']
# Update username
if 'username' in data and data['username'].strip():
new_username = data['username'].strip()
if new_username != user.get('username'):
# Check if username is already taken
existing = ext.db.users.find_one({
'username': new_username,
'_id': {'$ne': ObjectId(user_id)}
})
if existing:
return jsonify({'error': 'Tên người dùng đã tồn tại'}), 400
updates['username'] = new_username
# Update email
if 'email' in data and data['email'].strip():
new_email = data['email'].strip()
if new_email != user.get('email'):
# Check if email is already taken
existing = ext.db.users.find_one({
'email': new_email,
'_id': {'$ne': ObjectId(user_id)}
})
if existing:
return jsonify({'error': 'Email đã tồn tại'}), 400
updates['email'] = new_email
# Update phone
if 'phone' in data and data['phone'].strip():
new_phone = data['phone'].strip()
if new_phone != user.get('phone'):
# Check if phone is already taken
existing = ext.db.users.find_one({
'phone': new_phone,
'_id': {'$ne': ObjectId(user_id)}
})
if existing:
return jsonify({'error': 'Số điện thoại đã tồn tại'}), 400
updates['phone'] = new_phone
if not updates:
return jsonify({'error': 'Không có thông tin cập nhật hợp lệ'}), 400
updates['updated_at'] = datetime.utcnow()
updates['updated_by'] = session.get('user_id')
result = ext.db.users.update_one(
{'_id': ObjectId(user_id)},
{'$set': updates}
)
if result.modified_count == 0:
return jsonify({'error': 'Không tìm thấy người dùng hoặc không có thay đổi'}), 404
logging.info(f"Admin updated user {user_id}: {updates}")
# Broadcast updated query count to the user
if user_id in ext.connected_clients:
user = ext.db.users.find_one({'_id': ObjectId(user_id)})
ext.socketio.emit('query_update', {
'query_count': user.get('query_count', 0),
'query_limit': user.get('query_limit', 10)
}, room=ext.connected_clients[user_id])
logging.info(f"Broadcasted query update to user {user_id} after admin update")
return jsonify({
'message': 'Cập nhật người dùng thành công',
'updates': updates
}), 200
except Exception as e:
logging.error(f"Error updating user: {e}")
return jsonify({'error': 'Lỗi hệ thống'}), 500
@admin_bp.route('/admin/user/<user_id>/reset_query', methods=['POST'])
@admin_required
def reset_user_query_count(user_id):
try:
if not ObjectId.is_valid(user_id):
return jsonify({'error': 'ID người dùng không hợp lệ'}), 400
user = ext.db.users.find_one({'_id': ObjectId(user_id)})
if not user:
return jsonify({'error': 'Người dùng không tồn tại'}), 404
if user.get('account_type') == 'unlimited':
return jsonify({'error': 'Tài khoản không giới hạn không cần reset!'}), 400
ext.db.users.update_one(
{'_id': ObjectId(user_id)},
{'$set': {
'query_count': 0,
'last_reset': datetime.utcnow(),
'reset_by': session.get('user_id'),
'reset_at': datetime.utcnow()
}}
)
logging.info(f"Admin {session.get('user_id')} reset query count for user {user_id}")
# Broadcast updated query count to the user
if user_id in ext.connected_clients:
user = ext.db.users.find_one({'_id': ObjectId(user_id)})
ext.socketio.emit('query_update', {
'query_count': 0,
'query_limit': user.get('query_limit', 10)
}, room=ext.connected_clients[user_id])
logging.info(f"Broadcasted query update to user {user_id} after reset")
return jsonify({
'message': 'Reset lượt hỏi đáp thành công',
'user': {
'id': user_id,
'username': user['username']
}
}), 200
except Exception as e:
logging.error(f"Error resetting user query count: {e}")
return jsonify({'error': 'Lỗi hệ thống'}), 500
@admin_bp.route('/admin/user/<user_id>/toggle_active', methods=['POST'])
@admin_required
def toggle_user_active(user_id):
try:
if not ObjectId.is_valid(user_id):
return jsonify({'error': 'ID người dùng không hợp lệ'}), 400
user = ext.db.users.find_one({'_id': ObjectId(user_id)})
if not user:
return jsonify({'error': 'Người dùng không tồn tại'}), 404
# Prevent deactivating own account
if str(user['_id']) == session.get('user_id'):
return jsonify({'error': 'Không thể vô hiệu hóa tài khoản của chính bạn'}), 403
new_status = not user.get('is_active', False)
ext.db.users.update_one(
{'_id': ObjectId(user_id)},
{'$set': {
'is_active': new_status,
'status_changed_at': datetime.utcnow(),
'status_changed_by': session.get('user_id')
}}
)
# Send notification email
status_text = "kích hoạt" if new_status else "vô hiệu hóa"
send_email(
user['email'],
f'Tài khoản đã được {status_text}',
f'''Tài khoản của bạn ({user["username"]}) đã được admin {status_text}.
Trạng thái hiện tại: {"Đang hoạt động" if new_status else "Đã vô hiệu hóa"}'''
)
logging.info(f"Admin {session.get('user_id')} set user {user_id} active status to {new_status}")
return jsonify({
'message': f'Đã {status_text} tài khoản thành công',
'is_active': new_status,
'user': {
'id': user_id,
'username': user['username']
}
}), 200
except Exception as e:
logging.error(f"Error toggling user active status: {e}")
return jsonify({'error': 'Lỗi hệ thống'}), 500
@admin_bp.route('/admin/statistics', methods=['GET'])
@admin_required
def get_statistics():
try:
# User statistics
total_users = ext.db.users.count_documents({})
active_users = ext.db.users.count_documents({'is_active': True})
new_users_today = ext.db.users.count_documents({
'created_at': {'$gte': datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)}
})
# Query statistics
total_queries = 0
users = ext.db.users.find({})
for user in users:
total_queries += user.get('query_count', 0)
# Conversation statistics
total_conversations = ext.db.conversations.count_documents({})
conversations_today = ext.db.conversations.count_documents({
'timestamp': {'$gte': datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)}
})
# Message statistics
total_messages = ext.db.messages.count_documents({})
messages_today = ext.db.messages.count_documents({
'timestamp': {'$gte': datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)}
})
# Account type distribution
limited_users = ext.db.users.count_documents({'account_type': 'limited'})
unlimited_users = ext.db.users.count_documents({'account_type': 'unlimited'})
# Daily activity (last 7 days)
daily_activity = []
for i in range(6, -1, -1):
date = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
start_date = date - timedelta(days=i)
end_date = start_date + timedelta(days=1)
daily_users = ext.db.users.count_documents({
'created_at': {'$gte': start_date, '$lt': end_date}
})
daily_conversations = ext.db.conversations.count_documents({
'timestamp': {'$gte': start_date, '$lt': end_date}
})
daily_activity.append({
'date': start_date.strftime('%Y-%m-%d'),
'new_users': daily_users,
'new_conversations': daily_conversations
})
return jsonify({
'statistics': {
'users': {
'total': total_users,
'active': active_users,
'new_today': new_users_today,
'limited': limited_users,
'unlimited': unlimited_users
},
'queries': {
'total': total_queries,
'average_per_user': total_queries / total_users if total_users > 0 else 0
},
'conversations': {
'total': total_conversations,
'today': conversations_today
},
'messages': {
'total': total_messages,
'today': messages_today
}
},
'daily_activity': daily_activity,
'timestamp': datetime.utcnow().isoformat()
}), 200
except Exception as e:
logging.error(f"Error getting statistics: {e}")
return jsonify({'error': 'Lỗi hệ thống'}), 500