diff --git a/goldminer/backend/app.py b/goldminer/backend/app.py index d3cc5e95..a2e36726 100644 --- a/goldminer/backend/app.py +++ b/goldminer/backend/app.py @@ -8,12 +8,17 @@ import uuid from datetime import datetime from sqlalchemy.exc import OperationalError import logging +from functools import wraps logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) app = Flask(__name__, static_folder='../frontend/dist') app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev_key_for_goldminer') +# 设置会话cookie的设置 +app.config['SESSION_COOKIE_SECURE'] = False # 开发环境设为False,生产环境设为True +app.config['SESSION_COOKIE_HTTPONLY'] = True +app.config['SESSION_COOKIE_SAMESITE'] = 'Lax' # 云端MySQL数据库配置 DB_USER = os.environ.get('DB_USER', 'goldminer') @@ -29,7 +34,13 @@ app.config['SQLALCHEMY_ECHO'] = True # 开启SQL查询日志,方便调试 logger.info(f"连接到云端数据库: {DB_HOST}:{DB_PORT}/{DB_NAME}") -CORS(app, supports_credentials=True, origins=['*']) # Enable CORS with credentials for all origins +# 确保CORS配置正确处理凭证 +CORS(app, + supports_credentials=True, + origins=['*'], + methods=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], + allow_headers=['Content-Type', 'Authorization', 'X-Requested-With']) # 允许所有源的凭证请求 + db = SQLAlchemy(app) socketio = SocketIO(app, cors_allowed_origins="*", engineio_logger=True) @@ -37,6 +48,9 @@ socketio = SocketIO(app, cors_allowed_origins="*", engineio_logger=True) active_games = {} # 用户ID -> 游戏状态 sid_to_user = {} # Socket ID -> 用户ID映射 +# 添加一个简单的令牌存储 +user_tokens = {} # token -> user_id 映射 + # 用户模型 class User(db.Model): id = db.Column(db.Integer, primary_key=True) @@ -45,6 +59,7 @@ class User(db.Model): created_at = db.Column(db.DateTime, default=datetime.utcnow) last_login = db.Column(db.DateTime, nullable=True) high_score = db.Column(db.Integer, default=0) + is_admin = db.Column(db.Boolean, default=False) def set_password(self, password): self.password_hash = generate_password_hash(password) @@ -96,6 +111,9 @@ def init_db(): # 尝试创建表结构(如果不存在) db.create_all() logger.info("数据库连接成功,并且所有表都已创建(如果不存在)。") + + # 检查是否需要创建默认管理员账户 + create_default_admin() except OperationalError as e: logger.error("无法连接到云端MySQL数据库。请检查您的数据库配置和网络连接。") logger.error(f"错误详情: {e}") @@ -107,6 +125,31 @@ def init_db(): except Exception as e: logger.error(f"初始化数据库时发生未知错误: {e}") +# 创建默认管理员账户 +def create_default_admin(): + # 检查是否已有管理员账户 + admin_exists = User.query.filter_by(is_admin=True).first() + if admin_exists: + logger.info("已存在管理员账户,无需创建默认管理员。") + return + + # 检查是否已有admin用户名的账户 + admin_user = User.query.filter_by(username="admin").first() + + if admin_user: + # 将现有的admin用户升级为管理员 + admin_user.is_admin = True + db.session.commit() + logger.info("已将现有admin用户升级为管理员。") + else: + # 创建新的默认管理员账户 + default_admin = User(username="admin", is_admin=True) + default_admin.set_password("admin") + + db.session.add(default_admin) + db.session.commit() + logger.info("已创建默认管理员账户(用户名:admin,密码:admin)。在生产环境中请更改此密码!") + # 注册API @app.route('/api/register', methods=['POST']) def register(): @@ -133,9 +176,11 @@ def register(): @app.route('/api/login', methods=['POST']) def login(): data = request.get_json() + logger.info(f"收到登录请求: {data.get('username') if data else 'No data'}") # 检查必要的字段 if not data or not data.get('username') or not data.get('password'): + logger.warning("登录请求缺少用户名或密码") return jsonify({'error': '用户名和密码是必填项'}), 400 # 查找用户 @@ -143,31 +188,56 @@ def login(): # 检查密码 if user and user.check_password(data['password']): - # 生成会话ID + # 生成会话ID和令牌 session_id = str(uuid.uuid4()) + token = str(uuid.uuid4()) + + # 存储到会话和令牌映射 session['user_id'] = user.id session['session_id'] = session_id + user_tokens[token] = user.id # 更新最后登录时间 user.last_login = datetime.utcnow() db.session.commit() + logger.info(f"用户 {user.username} (ID: {user.id}) 登录成功,设置会话ID: {session_id} 和令牌: {token[:8]}...") + logger.info(f"当前会话内容: {dict(session)}") + return jsonify({ 'message': '登录成功', 'user': { 'id': user.id, 'username': user.username, - 'high_score': user.high_score + 'high_score': user.high_score, + 'is_admin': user.is_admin }, - 'session_id': session_id + 'session_id': session_id, + 'token': token }) + logger.warning(f"用户名或密码不正确: {data.get('username')}") return jsonify({'error': '用户名或密码不正确'}), 401 # 登出API @app.route('/api/logout', methods=['POST']) def logout(): + # 获取并清除令牌 + if 'Authorization' in request.headers: + token = request.headers.get('Authorization').replace('Bearer ', '') + if token in user_tokens: + logger.info(f"清除令牌: {token[:8]}...") + del user_tokens[token] + + # 获取用户ID用于日志记录 + user_id = session.get('user_id') + if user_id: + logger.info(f"用户 ID: {user_id} 登出") + + # 清除会话 session.clear() + logger.info("会话已清除") + return jsonify({'message': '已成功登出'}) # 获取当前用户信息 @@ -186,7 +256,8 @@ def get_user(): 'user': { 'id': user.id, 'username': user.username, - 'high_score': user.high_score + 'high_score': user.high_score, + 'is_admin': user.is_admin } }) @@ -573,6 +644,277 @@ def send_active_games_update(): # 广播活跃游戏状态 socketio.emit('active_games_update', {'active_games': active_games}) +# 管理员权限验证装饰器 +def admin_required(f): + @wraps(f) + def decorated_function(*args, **kwargs): + user_id = session.get('user_id') + if not user_id: + return jsonify({'error': '未登录'}), 401 + + user = User.query.get(user_id) + if not user or not user.is_admin: + return jsonify({'error': '需要管理员权限'}), 403 + + return f(*args, **kwargs) + return decorated_function + +# 获取所有用户信息 (管理员功能) +@app.route('/api/admin/users', methods=['GET']) +@admin_required +def get_all_users(): + users = User.query.all() + users_data = [] + + for user in users: + users_data.append({ + 'id': user.id, + 'username': user.username, + 'high_score': user.high_score, + 'is_admin': user.is_admin, + 'created_at': user.created_at.isoformat() if user.created_at else None, + 'last_login': user.last_login.isoformat() if user.last_login else None + }) + + return jsonify({'users': users_data}) + +# 编辑用户信息 (管理员功能) +@app.route('/api/admin/users/', methods=['PUT']) +@admin_required +def update_user(user_id): + user = User.query.get(user_id) + if not user: + return jsonify({'error': '用户不存在'}), 404 + + data = request.get_json() + + # 只允许更新特定字段 + if 'username' in data and data['username'] != user.username: + # 检查用户名是否已存在 + if User.query.filter_by(username=data['username']).first(): + return jsonify({'error': '用户名已存在'}), 400 + user.username = data['username'] + + if 'high_score' in data: + user.high_score = data['high_score'] + + if 'is_admin' in data: + # 确保至少有一个管理员账户 + if not data['is_admin'] and user.is_admin: + admin_count = User.query.filter_by(is_admin=True).count() + if admin_count <= 1: + return jsonify({'error': '系统必须至少有一个管理员账户'}), 400 + user.is_admin = data['is_admin'] + + if 'password' in data and data['password']: + user.set_password(data['password']) + + db.session.commit() + + # 如果修改了排行榜相关信息,发送更新事件 + if 'high_score' in data: + send_leaderboard_update() + + return jsonify({ + 'message': '用户信息已更新', + 'user': { + 'id': user.id, + 'username': user.username, + 'high_score': user.high_score, + 'is_admin': user.is_admin, + 'created_at': user.created_at.isoformat() if user.created_at else None, + 'last_login': user.last_login.isoformat() if user.last_login else None + } + }) + +# 删除用户 (管理员功能) +@app.route('/api/admin/users/', methods=['DELETE']) +@admin_required +def delete_user(user_id): + # 获取当前管理员 + admin_id = session.get('user_id') + admin = User.query.get(admin_id) + + # 防止删除自己 + if user_id == admin_id: + return jsonify({'error': '不能删除自己的账户'}), 400 + + user = User.query.get(user_id) + if not user: + return jsonify({'error': '用户不存在'}), 404 + + # 删除用户的所有游戏记录 + GameHistory.query.filter_by(user_id=user_id).delete() + + # 删除用户的所有聊天消息 + ChatMessage.query.filter_by(user_id=user_id).delete() + + # 删除用户创建的聊天室 + ChatRoom.query.filter_by(creator_id=user_id).delete() + + # 删除用户 + db.session.delete(user) + db.session.commit() + + # 更新排行榜 + send_leaderboard_update() + + return jsonify({'message': '用户已成功删除'}) + +# 清理排行榜记录 (管理员功能) +@app.route('/api/admin/leaderboard/reset', methods=['POST']) +@admin_required +def reset_leaderboard(): + data = request.get_json() + + # 如果指定了具体用户,只重置该用户的分数 + if data and 'user_id' in data: + user = User.query.get(data['user_id']) + if not user: + return jsonify({'error': '用户不存在'}), 404 + + user.high_score = 0 + GameHistory.query.filter_by(user_id=data['user_id']).delete() + db.session.commit() + + send_leaderboard_update() + return jsonify({'message': f'已重置用户 {user.username} 的分数'}) + + # 重置所有用户的分数 + users = User.query.all() + for user in users: + user.high_score = 0 + + # 删除所有游戏历史记录 + GameHistory.query.delete() + + db.session.commit() + + send_leaderboard_update() + return jsonify({'message': '所有排行榜记录已重置'}) + +# 修改游戏历史记录 (管理员功能) +@app.route('/api/admin/game_history', methods=['GET']) +@admin_required +def get_all_game_history(): + # 获取所有游戏历史记录,最多返回100条 + history = GameHistory.query.order_by(GameHistory.created_at.desc()).limit(100).all() + + history_data = [] + for record in history: + user = User.query.get(record.user_id) + history_data.append({ + 'id': record.id, + 'username': user.username if user else 'Unknown', + 'user_id': record.user_id, + 'score': record.score, + 'level_reached': record.level_reached, + 'duration': record.duration, + 'gold_earned': record.gold_earned, + 'created_at': record.created_at.isoformat() + }) + + return jsonify({'history': history_data}) + +# 删除指定的游戏记录 (管理员功能) +@app.route('/api/admin/game_history/', methods=['DELETE']) +@admin_required +def delete_game_history(history_id): + record = GameHistory.query.get(history_id) + if not record: + return jsonify({'error': '记录不存在'}), 404 + + db.session.delete(record) + db.session.commit() + + return jsonify({'message': '记录已成功删除'}) + +# 设置管理员 (用于初始化第一个管理员账户) +@app.route('/api/admin/setup', methods=['POST']) +def setup_admin(): + # 检查是否已有管理员账户 + admin_exists = User.query.filter_by(is_admin=True).first() + if admin_exists: + return jsonify({'error': '管理员账户已存在,不能再创建'}), 400 + + data = request.get_json() + + # 检查必要的字段 + if not data or not data.get('username') or not data.get('password') or not data.get('setup_key'): + return jsonify({'error': '用户名、密码和安装密钥是必填项'}), 400 + + # 验证安装密钥 (这应该是在环境变量中配置的一个安全密钥) + setup_key = os.environ.get('ADMIN_SETUP_KEY', 'goldminer_admin_setup_key') + if data['setup_key'] != setup_key: + return jsonify({'error': '安装密钥不正确'}), 403 + + # 检查用户名是否已存在 + existing_user = User.query.filter_by(username=data['username']).first() + + if existing_user: + # 如果用户存在,将其升级为管理员 + existing_user.is_admin = True + db.session.commit() + return jsonify({'message': '用户已成功升级为管理员', 'username': existing_user.username}), 200 + else: + # 创建新管理员用户 + admin = User(username=data['username'], is_admin=True) + admin.set_password(data['password']) + + db.session.add(admin) + db.session.commit() + + return jsonify({'message': '管理员账户创建成功', 'username': admin.username}), 201 + +# 测试管理员API +@app.route('/api/test/admin_status', methods=['GET']) +def test_admin_status(): + # 尝试从会话获取用户ID + user_id = session.get('user_id') + + # 如果会话中没有用户ID,尝试从令牌获取 + if not user_id and 'Authorization' in request.headers: + token = request.headers.get('Authorization').replace('Bearer ', '') + user_id = user_tokens.get(token) + logger.info(f"从令牌获取用户ID: {user_id}") + + logger.info(f"收到管理员状态检查请求,会话内容: {dict(session)}") + logger.info(f"请求头: {request.headers}") + + if not user_id: + logger.warning("会话和令牌中都没有用户ID,未登录状态") + return jsonify({ + 'logged_in': False, + 'message': '未登录', + 'session_data': dict(session), + 'headers': dict(request.headers) + }) + + user = User.query.get(user_id) + if not user: + logger.warning(f"用户ID {user_id} 不存在") + return jsonify({ + 'logged_in': False, + 'message': '用户不存在', + 'user_id': user_id + }) + + logger.info(f"管理员状态检查:用户 {user.username} (ID: {user.id}) 的管理员状态为 {user.is_admin}") + return jsonify({ + 'logged_in': True, + 'username': user.username, + 'is_admin': user.is_admin, + 'is_admin_type': type(user.is_admin).__name__, + 'user_data': { + 'id': user.id, + 'username': user.username, + 'high_score': user.high_score, + 'is_admin': user.is_admin, + 'created_at': user.created_at.isoformat() if user.created_at else None, + 'last_login': user.last_login.isoformat() if user.last_login else None + } + }) + if __name__ == '__main__': # 在启动应用前初始化数据库 init_db() diff --git a/goldminer/backend/run_server.py b/goldminer/backend/run_server.py index d7954bed..b6f25cc5 100644 --- a/goldminer/backend/run_server.py +++ b/goldminer/backend/run_server.py @@ -1,9 +1,23 @@ -from app import socketio, app, init_db, DB_HOST, DB_PORT, DB_NAME +from app import app, socketio, init_db +import os +import logging + +# 配置日志 +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) if __name__ == '__main__': - # 在启动应用前初始化云端数据库 - print(f"连接到云端数据库: {DB_HOST}:{DB_PORT}/{DB_NAME}") + # 在启动应用前初始化数据库 init_db() - print("启动后端服务器...") - print("监听所有网络接口 (0.0.0.0:5000)") - socketio.run(app, debug=True, host='0.0.0.0', port=5000, allow_unsafe_werkzeug=True) \ No newline at end of file + + # 从环境变量获取主机和端口,如果没有则使用默认值 + host = os.environ.get('HOST', '0.0.0.0') + port = int(os.environ.get('PORT', 5000)) + + logger.info(f"启动服务器,监听 {host}:{port}") + logger.info(f"SECRET_KEY设置: {app.config['SECRET_KEY'][:5]}...") + logger.info(f"SESSION_COOKIE_SECURE: {app.config['SESSION_COOKIE_SECURE']}") + logger.info(f"CORS supports_credentials: True") + + # 启动应用,确保支持跨域会话 + socketio.run(app, host=host, port=port, allow_unsafe_werkzeug=True) \ No newline at end of file diff --git a/goldminer/frontend/node_modules/.cache/eslint/68c8e53b.json b/goldminer/frontend/node_modules/.cache/eslint/68c8e53b.json index 97ce52dd..3048cf4b 100644 --- a/goldminer/frontend/node_modules/.cache/eslint/68c8e53b.json +++ b/goldminer/frontend/node_modules/.cache/eslint/68c8e53b.json @@ -1 +1 @@ -[{"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\main.js":"1","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\router.js":"2","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\App.vue":"3","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Register.vue":"4","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Login.vue":"5","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\UserProfile.vue":"6","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\ChatRoom.vue":"7","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Game.vue":"8","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Home.vue":"9","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Leaderboard.vue":"10","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Header.vue":"11"},{"size":420,"mtime":1750247900833,"results":"12","hashOfConfig":"13"},{"size":1555,"mtime":1750247900833,"results":"14","hashOfConfig":"13"},{"size":1004,"mtime":1750247900829,"results":"15","hashOfConfig":"13"},{"size":4492,"mtime":1750247900833,"results":"16","hashOfConfig":"13"},{"size":3472,"mtime":1750260439376,"results":"17","hashOfConfig":"13"},{"size":11652,"mtime":1750247900833,"results":"18","hashOfConfig":"13"},{"size":16278,"mtime":1750247900831,"results":"19","hashOfConfig":"13"},{"size":30598,"mtime":1750262074470,"results":"20","hashOfConfig":"13"},{"size":5735,"mtime":1750247900831,"results":"21","hashOfConfig":"13"},{"size":13225,"mtime":1750247900832,"results":"22","hashOfConfig":"13"},{"size":4152,"mtime":1750247900831,"results":"23","hashOfConfig":"13"},{"filePath":"24","messages":"25","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"26"},"1bydp2x",{"filePath":"27","messages":"28","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"26"},{"filePath":"29","messages":"30","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"31"},{"filePath":"32","messages":"33","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"31"},{"filePath":"34","messages":"35","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"36","messages":"37","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"31"},{"filePath":"38","messages":"39","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"31"},{"filePath":"40","messages":"41","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"42","messages":"43","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"31"},{"filePath":"44","messages":"45","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"31"},{"filePath":"46","messages":"47","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"31"},"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\main.js",[],[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\router.js",[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\App.vue",[],[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Register.vue",[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Login.vue",[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\UserProfile.vue",[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\ChatRoom.vue",[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Game.vue",[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Home.vue",[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Leaderboard.vue",[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Header.vue",[]] \ No newline at end of file +[{"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\main.js":"1","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\router.js":"2","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\App.vue":"3","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Register.vue":"4","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Login.vue":"5","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\UserProfile.vue":"6","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\ChatRoom.vue":"7","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Game.vue":"8","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Home.vue":"9","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Leaderboard.vue":"10","E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Header.vue":"11","E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\main.js":"12","E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\router.js":"13","E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\App.vue":"14","E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\Login.vue":"15","E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\Home.vue":"16","E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\UserProfile.vue":"17","E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\Register.vue":"18","E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\Game.vue":"19","E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\ChatRoom.vue":"20","E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\Leaderboard.vue":"21","E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\Header.vue":"22","E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\AdminSetup.vue":"23","E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\Admin.vue":"24"},{"size":420,"mtime":1750247900833,"results":"25","hashOfConfig":"26"},{"size":1555,"mtime":1750247900833,"results":"27","hashOfConfig":"26"},{"size":1004,"mtime":1750247900829,"results":"28","hashOfConfig":"26"},{"size":4492,"mtime":1750247900833,"results":"29","hashOfConfig":"26"},{"size":3472,"mtime":1750260439376,"results":"30","hashOfConfig":"26"},{"size":11652,"mtime":1750247900833,"results":"31","hashOfConfig":"26"},{"size":16278,"mtime":1750247900831,"results":"32","hashOfConfig":"26"},{"size":30598,"mtime":1750262074470,"results":"33","hashOfConfig":"26"},{"size":5735,"mtime":1750247900831,"results":"34","hashOfConfig":"26"},{"size":13225,"mtime":1750247900832,"results":"35","hashOfConfig":"26"},{"size":4152,"mtime":1750247900831,"results":"36","hashOfConfig":"26"},{"size":1538,"mtime":1750301139782,"results":"37","hashOfConfig":"38"},{"size":2525,"mtime":1750300541798,"results":"39","hashOfConfig":"38"},{"size":1004,"mtime":1750301895820,"results":"40","hashOfConfig":"38"},{"size":5227,"mtime":1750301071246,"results":"41","hashOfConfig":"38"},{"size":5735,"mtime":1750262863709,"results":"42","hashOfConfig":"38"},{"size":11652,"mtime":1750262863710,"results":"43","hashOfConfig":"38"},{"size":4492,"mtime":1750262863710,"results":"44","hashOfConfig":"38"},{"size":31688,"mtime":1750262863709,"results":"45","hashOfConfig":"38"},{"size":16278,"mtime":1750294243326,"results":"46","hashOfConfig":"38"},{"size":13225,"mtime":1750262863709,"results":"47","hashOfConfig":"38"},{"size":4675,"mtime":1750301706418,"results":"48","hashOfConfig":"38"},{"size":4836,"mtime":1750300463821,"results":"49","hashOfConfig":"38"},{"size":23162,"mtime":1750301706418,"results":"50","hashOfConfig":"38"},{"filePath":"51","messages":"52","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"53"},"1bydp2x",{"filePath":"54","messages":"55","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"53"},{"filePath":"56","messages":"57","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"58"},{"filePath":"59","messages":"60","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"58"},{"filePath":"61","messages":"62","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"63","messages":"64","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"58"},{"filePath":"65","messages":"66","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"58"},{"filePath":"67","messages":"68","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"69","messages":"70","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"58"},{"filePath":"71","messages":"72","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"58"},{"filePath":"73","messages":"74","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"58"},{"filePath":"75","messages":"76","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"77"},"1s9qeh2",{"filePath":"78","messages":"79","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"77"},{"filePath":"80","messages":"81","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"82","messages":"83","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"84"},{"filePath":"85","messages":"86","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"84"},{"filePath":"87","messages":"88","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"84"},{"filePath":"89","messages":"90","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"84"},{"filePath":"91","messages":"92","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"84"},{"filePath":"93","messages":"94","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"84"},{"filePath":"95","messages":"96","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"84"},{"filePath":"97","messages":"98","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"84"},{"filePath":"99","messages":"100","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"84"},{"filePath":"101","messages":"102","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"84"},"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\main.js",[],[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\router.js",[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\App.vue",[],[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Register.vue",[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Login.vue",[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\UserProfile.vue",[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\ChatRoom.vue",[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Game.vue",[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Home.vue",[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Leaderboard.vue",[],"E:\\学习\\网络应用开发\\Goldminer\\Goldminer_new\\goldminer\\frontend\\src\\components\\Header.vue",[],"E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\main.js",[],[],"E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\router.js",[],"E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\App.vue",[],"E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\Login.vue",[],[],"E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\Home.vue",[],"E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\UserProfile.vue",[],"E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\Register.vue",[],"E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\Game.vue",[],"E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\ChatRoom.vue",[],"E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\Leaderboard.vue",[],"E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\Header.vue",[],"E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\AdminSetup.vue",[],"E:\\学习\\网络应用开发\\GM3\\Goldminer_new\\goldminer\\frontend\\src\\components\\Admin.vue",[]] \ No newline at end of file diff --git a/goldminer/frontend/src/components/Header.vue b/goldminer/frontend/src/components/Header.vue index 70054c5b..fb528e97 100644 --- a/goldminer/frontend/src/components/Header.vue +++ b/goldminer/frontend/src/components/Header.vue @@ -9,12 +9,14 @@ 开始游戏 排行榜 聊天室 + 管理系统
@@ -35,7 +37,8 @@ export default { return { isLoggedIn: false, username: '', - highScore: 0 + highScore: 0, + isAdmin: false } }, created() { @@ -60,19 +63,27 @@ export default { this.isLoggedIn = true this.username = user.username this.highScore = user.high_score || 0 + this.isAdmin = user.is_admin === true // 明确比较为true } else { this.isLoggedIn = false this.username = '' this.highScore = 0 + this.isAdmin = false } }, async logout() { try { await axios.post('/api/logout', {}, { withCredentials: true }) + + // 清除本地存储和认证状态 localStorage.removeItem('user') + localStorage.removeItem('auth_token') + delete axios.defaults.headers.common['Authorization'] + this.isLoggedIn = false this.username = '' this.highScore = 0 + this.isAdmin = false // 重定向到登录页 if (this.$route.path !== '/login') { @@ -127,6 +138,20 @@ export default { background-color: rgba(139, 69, 19, 0.1); } +.admin-link { + color: #ff5722 !important; +} + +.admin-badge { + display: inline-block; + background-color: #ff5722; + color: white; + padding: 2px 5px; + border-radius: 3px; + font-size: 12px; + margin-left: 8px; +} + .user-menu { display: flex; align-items: center; diff --git a/goldminer/frontend/src/components/Login.vue b/goldminer/frontend/src/components/Login.vue index e8b82236..464b9645 100644 --- a/goldminer/frontend/src/components/Login.vue +++ b/goldminer/frontend/src/components/Login.vue @@ -2,6 +2,9 @@