from flask import request, jsonify from utils.database import get_db from utils.auth import get_user_id_from_token, generate_token, admin_required from utils.helpers import error_response, build_search_conditions import bcrypt import uuid import random import datetime import secrets captcha_store = {} def register_admin_routes(app): @app.route('/api/admin/captcha', methods=['GET']) def get_captcha(): captcha_id = str(uuid.uuid4()) num1 = random.randint(1, 9) num2 = random.randint(1, 9) question = f'{num1} + {num2} = ?' answer = num1 + num2 captcha_store[captcha_id] = { 'answer': answer, 'created_at': datetime.datetime.now() } return jsonify({ 'captcha_id': captcha_id, 'question': question }) @app.route('/api/admin/login', methods=['POST']) def admin_login(): data = request.json username = data.get('username') password = data.get('password') captcha_id = data.get('captcha_id') captcha = data.get('captcha') if not captcha_id or not captcha: return error_response('请输入验证码') if captcha_id not in captcha_store: return error_response('验证码已过期,请刷新') if int(captcha) != captcha_store[captcha_id]['answer']: return error_response('验证码错误') del captcha_store[captcha_id] conn = get_db() cursor = conn.cursor() try: cursor.execute('SELECT * FROM admin_users WHERE username = ?', (username,)) admin = cursor.fetchone() if not admin: return error_response('用户名或密码错误') if bcrypt.checkpw(password.encode('utf-8'), admin['password'].encode('utf-8')): token = secrets.token_urlsafe(32) expires_at = datetime.datetime.now() + datetime.timedelta(days=7) cursor.execute( 'INSERT INTO admin_sessions (admin_id, token, expires_at) VALUES (?, ?, ?)', (admin['id'], token, expires_at.strftime('%Y-%m-%d %H:%M:%S')) ) conn.commit() return jsonify({ 'success': True, 'message': '登录成功', 'token': token, 'admin': {'username': admin['username']} }) else: return error_response('用户名或密码错误') except Exception as e: conn.rollback() raise e finally: conn.close() @app.route('/api/admin/me', methods=['GET']) @admin_required def get_admin_me(): return jsonify({'success': True, 'admin': {'username': request.admin['username']}}) @app.route('/api/admin/logout', methods=['POST']) @admin_required def admin_logout(): auth_header = request.headers.get('Authorization') if auth_header and auth_header.startswith('Bearer '): token = auth_header.split(' ')[1] conn = get_db() cursor = conn.cursor() try: cursor.execute('DELETE FROM admin_sessions WHERE token = ?', (token,)) conn.commit() except Exception as e: conn.rollback() raise e finally: conn.close() return jsonify({'success': True, 'message': '退出成功'}) @app.route('/api/admin/password', methods=['POST', 'PUT']) @admin_required def admin_change_password(): data = request.json old_password = data.get('old_password') new_password = data.get('new_password') if not old_password or not new_password: return error_response('请输入旧密码和新密码') conn = get_db() cursor = conn.cursor() try: cursor.execute('SELECT * FROM admin_users WHERE username = ?', ('admin',)) admin = cursor.fetchone() if not admin: return error_response('管理员不存在') if bcrypt.checkpw(old_password.encode('utf-8'), admin['password'].encode('utf-8')): hashed_password = bcrypt.hashpw(new_password.encode('utf-8'), bcrypt.gensalt()) cursor.execute('UPDATE admin_users SET password = ? WHERE username = ?', (hashed_password, 'admin')) conn.commit() return jsonify({'success': True, 'message': '密码修改成功'}) else: return error_response('旧密码错误') except Exception as e: conn.rollback() raise e finally: conn.close() @app.route('/api/admin/news', methods=['GET']) @admin_required def get_admin_news(): page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 10)) offset = (page - 1) * limit conn = get_db() cursor = conn.cursor() try: cursor.execute('SELECT COUNT(*) FROM news WHERE status = "published"') total = cursor.fetchone()[0] cursor.execute(''' SELECT * FROM news WHERE status = "published" ORDER BY sort_order ASC, created_at DESC LIMIT ? OFFSET ? ''', (limit, offset)) rows = cursor.fetchall() items = [dict(row) for row in rows] return jsonify({'items': items, 'total': total, 'page': page, 'limit': limit}) finally: conn.close() @app.route('/api/admin/news/', methods=['GET']) @admin_required def get_admin_news_item(news_id): conn = get_db() cursor = conn.cursor() try: cursor.execute('SELECT * FROM news WHERE id = ?', (news_id,)) row = cursor.fetchone() if not row: return error_response('资讯不存在') return jsonify(dict(row)) finally: conn.close() @app.route('/api/admin/users', methods=['GET']) @admin_required def get_admin_users(): search = request.args.get('search') page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 10)) offset = (page - 1) * limit conn = get_db() cursor = conn.cursor() try: query = 'SELECT * FROM users WHERE deleted_at IS NULL' count_query = 'SELECT COUNT(*) FROM users WHERE deleted_at IS NULL' params = [] count_params = [] if search: search_fields = ['phone', 'nickname'] search_cond, search_params = build_search_conditions(search_fields, search) if search_cond: query += search_cond count_query += search_cond params.extend(search_params) count_params.extend(search_params) cursor.execute(count_query, count_params) total = cursor.fetchone()[0] query += ' ORDER BY created_at DESC LIMIT ? OFFSET ?' params.extend([limit, offset]) cursor.execute(query, params) rows = cursor.fetchall() items = [] for row in rows: user = dict(row) user['password'] = '******' items.append(user) return jsonify({'items': items, 'total': total, 'page': page, 'limit': limit}) finally: conn.close() @app.route('/api/admin/users/', methods=['GET']) @admin_required def get_admin_user(user_id): conn = get_db() cursor = conn.cursor() try: cursor.execute('SELECT * FROM users WHERE id = ? AND deleted_at IS NULL', (user_id,)) row = cursor.fetchone() if not row: return error_response('用户不存在') user = dict(row) user['password'] = '******' return jsonify(user) finally: conn.close() @app.route('/api/admin/users/', methods=['PUT']) @admin_required def update_admin_user(user_id): data = request.json conn = get_db() cursor = conn.cursor() try: update_fields = [] update_values = [] if 'enabled' in data: update_fields.append('enabled = ?') update_values.append(data['enabled']) if 'nickname' in data: update_fields.append('nickname = ?') update_values.append(data['nickname']) if 'gender' in data: update_fields.append('gender = ?') update_values.append(data['gender']) if not update_fields: return error_response('没有需要更新的字段') update_values.append(user_id) cursor.execute(f'UPDATE users SET {", ".join(update_fields)} WHERE id = ? AND deleted_at IS NULL', update_values) conn.commit() cursor.execute('SELECT * FROM users WHERE id = ? AND deleted_at IS NULL', (user_id,)) updated_user = cursor.fetchone() user = dict(updated_user) user['password'] = '******' return jsonify(user) except Exception as e: conn.rollback() raise e finally: conn.close() @app.route('/api/admin/users/', methods=['DELETE']) @admin_required def delete_admin_user(user_id): conn = get_db() cursor = conn.cursor() try: cursor.execute('UPDATE users SET deleted_at = datetime(\'now\') WHERE id = ?', (user_id,)) conn.commit() return jsonify({'success': True, 'message': '删除成功'}) except Exception as e: conn.rollback() raise e finally: conn.close() @app.route('/api/admin/sms/config', methods=['GET']) @admin_required def get_sms_config(): from utils.sms import get_sms_config config = get_sms_config() config['aliyun_sms_access_key_secret'] = '******' return jsonify({'success': True, 'data': config}) @app.route('/api/admin/sms/config', methods=['PUT']) @admin_required def update_sms_config(): data = request.json conn = get_db() cursor = conn.cursor() try: config_keys = [ 'use_real_sms', 'aliyun_sms_access_key_id', 'aliyun_sms_access_key_secret', 'aliyun_sms_sign_name', 'aliyun_sms_template_code', 'aliyun_sms_region_id' ] for key in config_keys: if key in data: if key == 'aliyun_sms_access_key_secret' and data[key] == '******': continue cursor.execute('SELECT id FROM system_config WHERE config_key = ?', (key,)) row = cursor.fetchone() if row: cursor.execute('UPDATE system_config SET config_value = ? WHERE config_key = ?', (str(data[key]), key)) else: cursor.execute('INSERT INTO system_config (config_key, config_value) VALUES (?, ?)', (key, str(data[key]))) conn.commit() return jsonify({'success': True, 'message': '配置更新成功'}) except Exception as e: conn.rollback() raise e finally: conn.close()