from flask import Flask, request, jsonify, send_from_directory from werkzeug.utils import secure_filename from flask_cors import CORS from flask_limiter import Limiter from flask_limiter.util import get_remote_address import os import uuid from utils.constants import ( MAX_FILE_SIZE, ALLOWED_EXTENSIONS, IMAGE_MAX_DIMENSION, IMAGE_QUALITY, DEFAULT_RATE_LIMIT ) app = Flask(__name__) ALLOWED_ORIGINS = [ "http://localhost:8080", "http://127.0.0.1:8080", "http://www.coop-star.com:8081", "http://www.coop-star.com:8080" ] CORS(app, resources={ r"/*": { "origins": ALLOWED_ORIGINS, "methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"], "allow_headers": ["Content-Type", "Authorization", "Cache-Control", "Pragma", "Expires"], "supports_credentials": True } }) limiter = Limiter( get_remote_address, app=app, default_limits=[DEFAULT_RATE_LIMIT], storage_uri="memory://", ) UPLOAD_FOLDER = os.path.join(os.path.dirname(__file__), '../uploads') app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER app.config['MAX_CONTENT_LENGTH'] = MAX_FILE_SIZE app.config['ALLOWED_EXTENSIONS'] = ALLOWED_EXTENSIONS captcha_store = {} @app.before_request def log_request(): if request.path not in ['/api/health', '/api/v1/health'] and not request.path.startswith('/uploads/'): app.logger.info(f"{request.method} {request.path} from {request.remote_addr}") @app.before_request def handle_options(): if request.method == 'OPTIONS': response = app.make_default_options_response() headers = response.headers origin = request.headers.get('Origin') if origin: headers['Access-Control-Allow-Origin'] = origin headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS' headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, Cache-Control, Pragma, Expires' headers['Access-Control-Max-Age'] = '3600' return response @app.after_request def add_cors_headers(response): origin = request.headers.get('Origin') if origin and origin in ALLOWED_ORIGINS: response.headers['Access-Control-Allow-Origin'] = origin response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS' response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, Cache-Control, Pragma, Expires' response.headers['Access-Control-Allow-Credentials'] = 'true' return response @app.errorhandler(Exception) def handle_exception(e): response = jsonify({'error': '服务器内部错误'}) response.status_code = 500 app.logger.error(f'Server error: {str(e)}') origin = request.headers.get('Origin') if origin and origin in ALLOWED_ORIGINS: response.headers['Access-Control-Allow-Origin'] = origin response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS' response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, Cache-Control, Pragma, Expires' response.headers['Access-Control-Allow-Credentials'] = 'true' return response from utils import init_db from routes import register_routes register_routes(app) @app.route('/api/upload', methods=['POST']) def upload_file(): if 'file' not in request.files: return jsonify({'error': '没有选择文件'}), 400 file = request.files['file'] if file.filename == '': return jsonify({'error': '没有选择文件'}), 400 if file and allowed_file(file.filename): filename = secure_filename(file.filename) ext = filename.rsplit('.', 1)[1].lower() new_filename = f'{uuid.uuid4().hex}.{ext}' file_path = os.path.join(app.config['UPLOAD_FOLDER'], new_filename) file.save(file_path) # 压缩图片 try: from PIL import Image img = Image.open(file_path) img.thumbnail((IMAGE_MAX_DIMENSION, IMAGE_MAX_DIMENSION)) img.save(file_path, quality=IMAGE_QUALITY) app.logger.info(f'Image compressed: {new_filename}') except Exception as e: app.logger.warning(f'Failed to compress image {new_filename}: {str(e)}') return jsonify({'url': f'/uploads/{new_filename}'}) else: return jsonify({'error': '不支持的文件类型'}), 400 def allowed_file(filename): return '.' in filename and \ filename.rsplit('.', 1)[1].lower() in app.config.get('ALLOWED_EXTENSIONS', {'png', 'jpg', 'jpeg', 'gif', 'webp'}) @app.route('/uploads/') def uploaded_file(filename): return send_from_directory(app.config['UPLOAD_FOLDER'], filename) if __name__ == '__main__': init_db() from utils.config import get_config_value backend_port = int(get_config_value('backend_port', 3000)) print(f'Starting backend server on port {backend_port}') app.run(host='0.0.0.0', port=backend_port, debug=True)