Spaces:
Sleeping
Sleeping
| import os | |
| import json | |
| import uuid | |
| from datetime import datetime | |
| from flask import Flask, render_template, request, jsonify | |
| app = Flask(__name__) | |
| DATA_FILE = os.environ.get("DATA_FILE", "requests.json") | |
| DEFAULT_REQUESTS = [ | |
| { | |
| "id": "1", | |
| "title": "增加暗色模式", | |
| "description": "建议增加暗色模式支持,方便夜间使用。", | |
| "category": "界面设计 (UI/UX)", | |
| "votes": 15, | |
| "status": "planned", | |
| "created_at": datetime.now().isoformat(), | |
| "comments": [] | |
| }, | |
| { | |
| "id": "2", | |
| "title": "支持图片上传", | |
| "description": "在提交反馈时,希望可以上传截图,这样能更直观地说明问题。", | |
| "category": "功能建议 (Feature)", | |
| "votes": 8, | |
| "status": "pending", | |
| "created_at": datetime.now().isoformat(), | |
| "comments": [] | |
| }, | |
| { | |
| "id": "3", | |
| "title": "移动端适配优化", | |
| "description": "目前在手机上查看列表时,排版有些拥挤,建议优化移动端显示。", | |
| "category": "界面设计 (UI/UX)", | |
| "votes": 12, | |
| "status": "in_progress", | |
| "created_at": datetime.now().isoformat(), | |
| "comments": [] | |
| } | |
| ] | |
| def load_data(): | |
| if not os.path.exists(DATA_FILE): | |
| save_data(DEFAULT_REQUESTS) | |
| return DEFAULT_REQUESTS | |
| try: | |
| with open(DATA_FILE, "r", encoding="utf-8") as f: | |
| data = json.load(f) | |
| if not isinstance(data, list): | |
| # If data is corrupted (not a list), backup and reset | |
| print(f"Warning: {DATA_FILE} contains invalid data (not a list). Resetting.") | |
| os.rename(DATA_FILE, DATA_FILE + ".bak") | |
| save_data(DEFAULT_REQUESTS) | |
| return DEFAULT_REQUESTS | |
| return data | |
| except Exception as e: | |
| print(f"Error loading data: {e}. Resetting.") | |
| # If file is corrupted, reset | |
| if os.path.exists(DATA_FILE): | |
| os.rename(DATA_FILE, DATA_FILE + ".bak") | |
| save_data(DEFAULT_REQUESTS) | |
| return DEFAULT_REQUESTS | |
| def save_data(data): | |
| try: | |
| with open(DATA_FILE, "w", encoding="utf-8") as f: | |
| json.dump(data, f, ensure_ascii=False, indent=2) | |
| except Exception as e: | |
| print(f"Error saving data: {e}") | |
| def index(): | |
| return render_template("index.html") | |
| def get_requests(): | |
| data = load_data() | |
| # Sort by votes (desc) then date (desc) | |
| data.sort(key=lambda x: (x.get("votes", 0), x.get("created_at", "")), reverse=True) | |
| return jsonify(data) | |
| def add_request(): | |
| data = load_data() | |
| payload = request.json | |
| if not payload.get("title") or not payload.get("description"): | |
| return jsonify({"error": "Title and description are required"}), 400 | |
| new_request = { | |
| "id": str(uuid.uuid4()), | |
| "title": payload["title"], | |
| "description": payload["description"], | |
| "category": payload.get("category", "General"), | |
| "votes": 1, | |
| "status": "pending", # pending, planned, in_progress, completed, rejected | |
| "created_at": datetime.now().isoformat(), | |
| "comments": [] | |
| } | |
| data.append(new_request) | |
| save_data(data) | |
| return jsonify(new_request) | |
| def vote_request(req_id): | |
| data = load_data() | |
| for req in data: | |
| if req["id"] == req_id: | |
| req["votes"] = req.get("votes", 0) + 1 | |
| save_data(data) | |
| return jsonify({"success": True, "votes": req["votes"]}) | |
| return jsonify({"error": "Request not found"}), 404 | |
| def update_status(req_id): | |
| # Simple admin check (in a real app, use auth) | |
| # Here we just trust the client for the demo/MVP purpose | |
| payload = request.json | |
| new_status = payload.get("status") | |
| if new_status not in ["pending", "planned", "in_progress", "completed", "rejected"]: | |
| return jsonify({"error": "Invalid status"}), 400 | |
| data = load_data() | |
| for req in data: | |
| if req["id"] == req_id: | |
| req["status"] = new_status | |
| save_data(data) | |
| return jsonify({"success": True, "status": new_status}) | |
| return jsonify({"error": "Request not found"}), 404 | |
| if __name__ == "__main__": | |
| app.run(host="0.0.0.0", port=7860) | |