C:\Users\ASUS\.conda\envs\wys\python.exe C:\Users\ASUS\Desktop\Pro\API\app.py
Traceback (most recent call last):
File "C:\Users\ASUS\Desktop\Pro\API\app.py", line 188, in <module>
if not os.path.exists(app.config['UPLOAD_FOLDER']):
KeyError: 'UPLOAD_FOLDER'from flask import Flask, request, jsonify, send_from_directory, make_response
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
from functools import wraps # 新增
import os
import uuid
from datetime import datetime
# 初始化Flask应用
app = Flask(__name__)
# ================ CORS全局配置 ================
CORS(app, resources={
r"/*": {
"origins": "http://localhost:5173",
"methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
"allow_headers": ["Content-Type", "Authorization", "X-Requested-With"],
"supports_credentials": True,
"expose_headers": ["Set-Cookie"] # 修改
}
})
# ================ 新增认证中间件 ================
def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
# 从cookie获取用户ID
user_id = request.cookies.get('user_id')
if not user_id:
return jsonify({"message": "未认证"}), 401
# 验证用户是否存在
user = User.query.get(user_id)
if not user:
return jsonify({"message": "用户不存在"}), 401
return f(*args, **kwargs)
return decorated_function
# ================ 修改登录接口 ================
@app.route('/login', methods=['POST', 'OPTIONS'])
def login():
try:
if request.method == 'OPTIONS':
return _build_cors_preflight_response()
data = request.get_json()
username = data.get('username', '').strip()
password = data.get('password', '')
user = User.query.filter_by(username=username).first()
if not user or user.password != password:
return jsonify({"message": "认证失败"}), 401
# 创建响应并设置cookie
response = jsonify({
"message": "登录成功",
"user_id": user.id,
"profile_completed": user.profile_completed,
"avatar_url": f"/uploads/{user.avatar}"
})
# 设置HTTP-only Cookie
response.set_cookie(
key='user_id',
value=str(user.id),
httponly=True,
samesite='Lax',
max_age=86400, # 1天有效期
secure=False, # 开发环境关闭
domain='localhost'
)
return response, 200
except Exception as e:
return jsonify({"message": f"服务器错误: {str(e)}"}), 500
# ================ 修改用户资料接口 ================
@app.route('/user/<int:user_id>', methods=['GET', 'PUT', 'OPTIONS'])
@login_required # 新增认证装饰器
def user_profile(user_id):
try:
if request.method == 'OPTIONS':
return _build_cors_preflight_response()
# 从cookie获取当前用户
current_user_id = int(request.cookies.get('user_id'))
# 验证用户权限
if current_user_id != user_id:
return jsonify({"message": "无权访问"}), 403
user = User.query.get_or_404(user_id)
if request.method == 'GET':
return jsonify({
"username": user.username,
"shop_name": user.shop_name,
"phone": user.phone,
"email": user.email
}), 200
if request.method == 'PUT':
data = request.get_json()
if not data:
return jsonify({"message": "请求体不能为空"}), 400
update_fields = {
'shop_name': data.get('shop_name'),
'phone': data.get('phone'),
'email': data.get('email')
}
for field, value in update_fields.items():
if value is not None and getattr(user, field) != value:
setattr(user, field, value)
db.session.commit()
return jsonify({"message": "更新成功"}), 200
except Exception as e:
db.session.rollback()
app.logger.error(f"用户资料操作失败: {str(e)}")
return jsonify({"message": "服务器错误"}), 500
# ================ 修改密码接口 ================
@app.route('/user/<int:user_id>/password', methods=['PUT', 'OPTIONS'])
@login_required # 新增认证装饰器
def update_password(user_id):
try:
# 权限验证
current_user_id = int(request.cookies.get('user_id'))
if current_user_id != user_id:
return jsonify({"message": "无权操作"}), 403
user = User.query.get_or_404(user_id)
data = request.get_json()
if 'old_password' not in data or 'new_password' not in data:
return jsonify({"message": "需要提供原密码和新密码"}), 400
if user.password != data['old_password']:
return jsonify({"message": "原密码错误"}), 401
if len(data['new_password']) < 6:
return jsonify({"message": "密码至少6位"}), 400
user.password = data['new_password']
user.password_updated_at = datetime.utcnow()
db.session.commit()
return jsonify({"message": "密码更新成功"}), 200
except Exception as e:
db.session.rollback()
return jsonify({"message": f"服务器错误: {str(e)}"}), 500
# ================ 工具函数 ================
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png', 'jpg', 'jpeg'}
def _build_cors_preflight_response():
response = make_response()
response.headers.add("Access-Control-Allow-Origin", "http://localhost:5173")
response.headers.add("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With")
response.headers.add("Access-Control-Allow-Methods", "*")
response.headers.add("Access-Control-Max-Age", "86400")
response.headers.add("Access-Control-Allow-Credentials", "true")
response.headers['Access-Control-Allow-Origin'] = 'http://localhost:5173'
response.headers['Access-Control-Allow-Credentials'] = 'true'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return response, 204
# ================ 启动应用 ================
if __name__ == '__main__':
# 确保上传目录存在
if not os.path.exists(app.config['UPLOAD_FOLDER']):
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
# 检查数据库连接
try:
with app.app_context():
db.create_all()
db.engine.connect()
print("✅ 数据库连接成功")
except Exception as e:
print(f"❌ 数据库连接失败: {str(e)}")
exit(1)
app.run(host='0.0.0.0', port=5000, debug=True)