Spaces:
Running
Running
from flask import Blueprint, request, jsonify, make_response | |
import re | |
import logging | |
from models.user_model import User | |
from flask_jwt_extended import create_access_token, get_jwt_identity, jwt_required, get_jwt | |
import datetime | |
from functools import wraps | |
# Cấu hình logging | |
logger = logging.getLogger(__name__) | |
# Tạo blueprint | |
auth_routes = Blueprint('auth', __name__) | |
def validate_email(email): | |
"""Kiểm tra định dạng email""" | |
pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$" | |
return re.match(pattern, email) is not None | |
def validate_password(password): | |
"""Kiểm tra mật khẩu có đủ mạnh không""" | |
return len(password) >= 6 | |
# SỬA: Decorator đơn giản hơn để check admin | |
def require_admin(f): | |
"""Decorator đơn giản để kiểm tra admin""" | |
def decorated_function(*args, **kwargs): | |
try: | |
user_id = get_jwt_identity() | |
user = User.find_by_id(user_id) | |
if not user: | |
return jsonify({ | |
"success": False, | |
"error": "User không tồn tại" | |
}), 403 | |
# Kiểm tra có phải admin không | |
if not user.is_admin(): | |
return jsonify({ | |
"success": False, | |
"error": "Không có quyền truy cập admin" | |
}), 403 | |
# Thêm user vào request context | |
request.current_user = user | |
return f(*args, **kwargs) | |
except Exception as e: | |
logger.error(f"Lỗi xác thực admin: {e}") | |
return jsonify({ | |
"success": False, | |
"error": "Lỗi xác thực" | |
}), 500 | |
return decorated_function | |
def register_user(name, email, password, gender=None): | |
"""Đăng ký người dùng mới""" | |
if not name or not email or not password: | |
return False, "Vui lòng nhập đầy đủ thông tin" | |
if not validate_email(email): | |
return False, "Email không hợp lệ" | |
if not validate_password(password): | |
return False, "Mật khẩu phải có ít nhất 6 ký tự" | |
success, result = User.register(name, email, password, gender) | |
if success: | |
return True, {"user_id": result} | |
else: | |
return False, result | |
def login_user(email, password): | |
"""Đăng nhập người dùng""" | |
if not email or not password: | |
return False, "Vui lòng nhập đầy đủ thông tin đăng nhập" | |
success, result = User.login(email, password) | |
if success: | |
user = result | |
# Tạo JWT token với thời gian hết hạn 24 giờ | |
expires = datetime.timedelta(hours=24) | |
access_token = create_access_token( | |
identity=str(user.user_id), | |
expires_delta=expires, | |
additional_claims={ | |
"name": user.name, | |
"email": user.email, | |
"gender": user.gender, | |
"role": user.role, | |
"permissions": user.permissions, | |
"is_admin": user.is_admin() | |
} | |
) | |
return True, { | |
"user_id": str(user.user_id), | |
"user": { | |
"id": str(user.user_id), | |
"name": user.name, | |
"email": user.email, | |
"gender": user.gender, | |
"role": user.role, | |
"permissions": user.permissions, | |
"is_admin": user.is_admin() | |
}, | |
"access_token": access_token, | |
"expires_in": 86400 | |
} | |
else: | |
return False, result | |
def register(): | |
"""API endpoint để đăng ký người dùng mới""" | |
try: | |
data = request.json | |
name = data.get('fullName') | |
email = data.get('email') | |
password = data.get('password') | |
gender = data.get('gender') | |
success, result = register_user(name, email, password, gender) | |
if success: | |
return jsonify({ | |
"success": True, | |
"message": "Đăng ký thành công", | |
"user_id": result.get("user_id") | |
}) | |
else: | |
return jsonify({ | |
"success": False, | |
"error": result | |
}), 400 | |
except Exception as e: | |
logger.error(f"Lỗi đăng ký người dùng: {str(e)}") | |
return jsonify({ | |
"success": False, | |
"error": str(e) | |
}), 500 | |
def login(): | |
"""API endpoint để đăng nhập""" | |
try: | |
data = request.json | |
email = data.get('email') | |
password = data.get('password') | |
remember_me = data.get('rememberMe', False) | |
success, result = login_user(email, password) | |
if success: | |
access_token = result.get("access_token") | |
response = make_response(jsonify({ | |
"success": True, | |
"user_id": result.get("user_id"), | |
"user": result.get("user"), | |
"access_token": access_token, | |
"expires_in": result.get("expires_in") | |
})) | |
cookie_max_age = 86400 if remember_me else None | |
response.set_cookie( | |
'access_token_cookie', | |
access_token, | |
max_age=cookie_max_age, | |
httponly=True, | |
path='/api', | |
samesite='Lax', | |
secure=False | |
) | |
return response | |
else: | |
return jsonify({ | |
"success": False, | |
"error": result | |
}), 401 | |
except Exception as e: | |
logger.error(f"Lỗi đăng nhập: {str(e)}") | |
return jsonify({ | |
"success": False, | |
"error": str(e) | |
}), 500 | |
def logout(): | |
"""API endpoint để đăng xuất""" | |
response = make_response(jsonify({ | |
"success": True, | |
"message": "Đăng xuất thành công" | |
})) | |
response.delete_cookie('access_token_cookie', path='/api') | |
return response | |
def verify_token(): | |
"""API endpoint để kiểm tra token có hợp lệ không""" | |
try: | |
current_user_id = get_jwt_identity() | |
user = User.find_by_id(current_user_id) | |
if not user: | |
return jsonify({ | |
"success": False, | |
"error": "User không tồn tại" | |
}), 401 | |
return jsonify({ | |
"success": True, | |
"user_id": current_user_id, | |
"user": { | |
"id": str(user.user_id), | |
"name": user.name, | |
"email": user.email, | |
"gender": user.gender, | |
"role": user.role, | |
"permissions": user.permissions, | |
"is_admin": user.is_admin() | |
} | |
}) | |
except Exception as e: | |
return jsonify({ | |
"success": False, | |
"error": str(e) | |
}), 401 | |
def user_profile(): | |
"""API endpoint để lấy thông tin người dùng""" | |
try: | |
user_id = get_jwt_identity() | |
user = User.find_by_id(user_id) | |
if user: | |
profile = { | |
"id": str(user.user_id), | |
"name": user.name, | |
"email": user.email, | |
"gender": user.gender, | |
"role": user.role, | |
"permissions": user.permissions, | |
"is_admin": user.is_admin(), | |
"created_at": user.created_at.isoformat() if user.created_at else None, | |
"updated_at": user.updated_at.isoformat() if user.updated_at else None, | |
"last_login": user.last_login.isoformat() if user.last_login else None | |
} | |
return jsonify({ | |
"success": True, | |
"user": profile | |
}) | |
else: | |
return jsonify({ | |
"success": False, | |
"error": "Không tìm thấy thông tin người dùng" | |
}), 404 | |
except Exception as e: | |
logger.error(f"Lỗi lấy thông tin người dùng: {str(e)}") | |
return jsonify({ | |
"success": False, | |
"error": str(e) | |
}), 500 | |
def update_profile(): | |
"""API endpoint để cập nhật thông tin người dùng""" | |
try: | |
data = request.json | |
user_id = get_jwt_identity() | |
user = User.find_by_id(user_id) | |
if not user: | |
return jsonify({ | |
"success": False, | |
"error": "Không tìm thấy người dùng" | |
}), 404 | |
if 'name' in data: | |
user.name = data['name'] | |
if 'gender' in data: | |
user.gender = data['gender'] | |
user.save() | |
return jsonify({ | |
"success": True, | |
"message": "Cập nhật thông tin thành công" | |
}) | |
except Exception as e: | |
logger.error(f"Lỗi cập nhật thông tin người dùng: {str(e)}") | |
return jsonify({ | |
"success": False, | |
"error": str(e) | |
}), 500 | |
def update_password(): | |
"""API endpoint để đổi mật khẩu""" | |
try: | |
data = request.json | |
user_id = get_jwt_identity() | |
current_password = data.get('currentPassword') | |
new_password = data.get('newPassword') | |
user = User.find_by_id(user_id) | |
if not user: | |
return jsonify({ | |
"success": False, | |
"error": "Không tìm thấy người dùng" | |
}), 404 | |
if not User.check_password(user.password, current_password): | |
return jsonify({ | |
"success": False, | |
"error": "Mật khẩu hiện tại không chính xác" | |
}), 400 | |
if not validate_password(new_password): | |
return jsonify({ | |
"success": False, | |
"error": "Mật khẩu mới phải có ít nhất 6 ký tự" | |
}), 400 | |
user.password = User.hash_password(new_password) | |
user.save() | |
return jsonify({ | |
"success": True, | |
"message": "Đổi mật khẩu thành công" | |
}) | |
except Exception as e: | |
logger.error(f"Lỗi đổi mật khẩu: {str(e)}") | |
return jsonify({ | |
"success": False, | |
"error": str(e) | |
}), 500 | |
# === ADMIN ENDPOINTS - SỬA LẠI ĐỂ SỬ DỤNG CHUNG TOKEN === | |
def get_admin_overview_stats(): | |
"""API endpoint để lấy thống kê tổng quan cho admin""" | |
try: | |
from models.conversation_model import get_db | |
db = get_db() | |
# Đếm tổng conversations | |
total_conversations = db.conversations.count_documents({}) | |
# Conversations trong 24h qua | |
day_ago = datetime.datetime.now() - datetime.timedelta(days=1) | |
recent_conversations = db.conversations.count_documents({ | |
"created_at": {"$gte": day_ago} | |
}) | |
# Đếm tổng tin nhắn | |
pipeline = [ | |
{"$project": {"message_count": {"$size": "$messages"}}}, | |
{"$group": {"_id": None, "total_messages": {"$sum": "$message_count"}}} | |
] | |
message_result = list(db.conversations.aggregate(pipeline)) | |
total_messages = message_result[0]["total_messages"] if message_result else 0 | |
# Mock users data (có thể thay bằng query thực tế) | |
total_users = db.users.count_documents({}) if hasattr(db, 'users') else 1 | |
return jsonify({ | |
"success": True, | |
"stats": { | |
"users": { | |
"total": total_users, | |
"new_today": 0 | |
}, | |
"conversations": { | |
"total": total_conversations, | |
"recent": recent_conversations | |
}, | |
"data": { | |
"total_chunks": total_messages, | |
"total_tables": 0, | |
"total_figures": 0, | |
"total_items": total_messages, | |
"embeddings": 0 | |
}, | |
"admins": { | |
"total": 1 | |
} | |
} | |
}) | |
except Exception as e: | |
logger.error(f"Lỗi lấy thống kê admin: {str(e)}") | |
return jsonify({ | |
"success": False, | |
"error": str(e) | |
}), 500 | |
def get_admin_recent_activities(): | |
"""API endpoint để lấy hoạt động gần đây cho admin""" | |
try: | |
limit = int(request.args.get('limit', 10)) | |
from models.conversation_model import get_db | |
db = get_db() | |
# Lấy conversations gần đây | |
recent_conversations = list(db.conversations.find( | |
{}, | |
{"title": 1, "created_at": 1, "updated_at": 1} | |
).sort("updated_at", -1).limit(limit)) | |
activities = [] | |
for conv in recent_conversations: | |
activities.append({ | |
"type": "conversation_created", | |
"title": "Cuộc hội thoại mới", | |
"description": conv.get("title", "Cuộc hội thoại"), | |
"timestamp": conv.get("updated_at", datetime.datetime.now()).isoformat() | |
}) | |
return jsonify({ | |
"success": True, | |
"activities": activities | |
}) | |
except Exception as e: | |
logger.error(f"Lỗi lấy hoạt động gần đây: {str(e)}") | |
return jsonify({ | |
"success": False, | |
"error": str(e) | |
}), 500 | |
def get_admin_system_alerts(): | |
"""API endpoint để lấy cảnh báo hệ thống cho admin""" | |
try: | |
# Mock alerts - có thể thay bằng logic thực tế | |
alerts = [ | |
{ | |
"type": "info", | |
"title": "Hệ thống hoạt động bình thường", | |
"message": "Tất cả các dịch vụ đang chạy ổn định", | |
"severity": "low" | |
} | |
] | |
return jsonify({ | |
"success": True, | |
"alerts": alerts | |
}) | |
except Exception as e: | |
logger.error(f"Lỗi lấy cảnh báo hệ thống: {str(e)}") | |
return jsonify({ | |
"success": False, | |
"error": str(e) | |
}), 500 | |
def init_admin(): | |
"""API endpoint để khởi tạo admin mặc định""" | |
try: | |
success, result = User.create_default_admin() | |
if success: | |
return jsonify({ | |
"success": True, | |
"message": "Khởi tạo admin thành công", | |
"data": result | |
}) | |
else: | |
return jsonify({ | |
"success": False, | |
"error": result | |
}), 400 | |
except Exception as e: | |
logger.error(f"Lỗi khởi tạo admin: {str(e)}") | |
return jsonify({ | |
"success": False, | |
"error": str(e) | |
}), 500 |