2026-01-12 00:53:31 +08:00
|
|
|
|
from flask import Blueprint, request, jsonify, session, current_app
|
2026-07-16 11:38:43 +08:00
|
|
|
|
from urllib.parse import unquote, urljoin, urlparse
|
2026-04-26 10:39:46 +08:00
|
|
|
|
from config import Config
|
|
|
|
|
|
from extensions import db, redis_client, s3_client
|
2026-01-21 20:43:46 +08:00
|
|
|
|
from models import User, SavedPrompt
|
2026-01-12 00:53:31 +08:00
|
|
|
|
from middlewares.auth import login_required
|
|
|
|
|
|
from services.logger import system_logger
|
2026-01-17 23:15:58 +08:00
|
|
|
|
import json
|
2026-01-12 00:53:31 +08:00
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
from services.system_service import (
|
|
|
|
|
|
get_system_config_data,
|
|
|
|
|
|
get_user_latest_notification,
|
|
|
|
|
|
mark_notification_as_read,
|
|
|
|
|
|
)
|
2026-01-17 23:15:58 +08:00
|
|
|
|
from services.history_service import get_user_history_data
|
|
|
|
|
|
from services.file_service import handle_file_uploads, get_remote_file_stream
|
|
|
|
|
|
from services.generation_service import (
|
2026-04-24 23:17:10 +08:00
|
|
|
|
validate_generation_request,
|
|
|
|
|
|
deduct_points,
|
|
|
|
|
|
handle_chat_generation_sync,
|
|
|
|
|
|
start_async_image_task,
|
|
|
|
|
|
validate_video_request,
|
|
|
|
|
|
start_async_video_task,
|
2026-01-17 23:15:58 +08:00
|
|
|
|
)
|
2026-05-16 23:11:08 +08:00
|
|
|
|
from services.aigc_detector_service import detect_aigc_image
|
2026-01-16 22:24:14 +08:00
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
api_bp = Blueprint("api", __name__)
|
|
|
|
|
|
|
2026-01-16 22:24:14 +08:00
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
@api_bp.route("/api/task_status/<task_id>")
|
2026-01-15 21:42:03 +08:00
|
|
|
|
@login_required
|
|
|
|
|
|
def get_task_status(task_id):
|
2026-01-16 22:24:14 +08:00
|
|
|
|
try:
|
|
|
|
|
|
data = redis_client.get(f"task:{task_id}")
|
|
|
|
|
|
if not data:
|
|
|
|
|
|
return jsonify({"status": "pending"})
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
2026-01-16 22:24:14 +08:00
|
|
|
|
if isinstance(data, bytes):
|
2026-04-24 23:17:10 +08:00
|
|
|
|
data = data.decode("utf-8")
|
|
|
|
|
|
|
2026-01-16 22:24:14 +08:00
|
|
|
|
return jsonify(json.loads(data))
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
system_logger.error(f"查询任务状态异常: {str(e)}")
|
|
|
|
|
|
return jsonify({"status": "error", "message": "状态查询失败"})
|
2026-01-15 21:42:03 +08:00
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
@api_bp.route("/api/config")
|
2026-01-12 00:53:31 +08:00
|
|
|
|
def get_config():
|
|
|
|
|
|
try:
|
2026-01-17 23:15:58 +08:00
|
|
|
|
return jsonify(get_system_config_data())
|
2026-01-12 00:53:31 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
@api_bp.route("/api/upload", methods=["POST"])
|
2026-01-12 00:53:31 +08:00
|
|
|
|
@login_required
|
|
|
|
|
|
def upload():
|
|
|
|
|
|
try:
|
2026-04-24 23:17:10 +08:00
|
|
|
|
files = request.files.getlist("images")
|
2026-07-16 11:47:52 +08:00
|
|
|
|
img_urls, download_urls = handle_file_uploads(files)
|
2026-04-24 23:17:10 +08:00
|
|
|
|
system_logger.info(f"用户上传文件: {len(files)} 张", user_id=session.get("user_id"))
|
2026-07-16 11:47:52 +08:00
|
|
|
|
return jsonify({"urls": img_urls, "download_urls": download_urls})
|
2026-01-12 00:53:31 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
@api_bp.route("/api/generate", methods=["POST"])
|
2026-01-12 00:53:31 +08:00
|
|
|
|
@login_required
|
|
|
|
|
|
def generate():
|
|
|
|
|
|
try:
|
2026-04-24 23:17:10 +08:00
|
|
|
|
user_id = session.get("user_id")
|
2026-01-16 22:24:14 +08:00
|
|
|
|
user = db.session.get(User, user_id)
|
2026-01-12 00:53:31 +08:00
|
|
|
|
data = request.json if request.is_json else request.form
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
2026-07-16 11:38:43 +08:00
|
|
|
|
image_urls = data.get(
|
|
|
|
|
|
"image",
|
|
|
|
|
|
data.get("image_urls", data.get("image_data", [])),
|
|
|
|
|
|
) or []
|
|
|
|
|
|
if isinstance(image_urls, str):
|
|
|
|
|
|
image_urls = [image_urls]
|
|
|
|
|
|
elif not isinstance(image_urls, (list, tuple)):
|
|
|
|
|
|
return jsonify({"error": "参考图地址格式无效"}), 400
|
|
|
|
|
|
|
|
|
|
|
|
normalized_image_urls = []
|
|
|
|
|
|
for image_url in image_urls:
|
|
|
|
|
|
if not isinstance(image_url, str) or not image_url.strip():
|
|
|
|
|
|
return jsonify({"error": "参考图地址无效"}), 400
|
|
|
|
|
|
|
|
|
|
|
|
image_url = image_url.strip()
|
|
|
|
|
|
parsed_input = urlparse(image_url)
|
|
|
|
|
|
if image_url.startswith("/"):
|
|
|
|
|
|
absolute_url = urljoin(request.url_root, image_url)
|
|
|
|
|
|
elif parsed_input.scheme in ("http", "https") and parsed_input.netloc:
|
|
|
|
|
|
absolute_url = image_url
|
|
|
|
|
|
else:
|
|
|
|
|
|
return jsonify({"error": "参考图必须使用 HTTP(S) URL,不能使用 Base64"}), 400
|
|
|
|
|
|
|
|
|
|
|
|
parsed_url = urlparse(absolute_url)
|
|
|
|
|
|
if parsed_url.scheme not in ("http", "https") or not parsed_url.netloc:
|
|
|
|
|
|
return jsonify({"error": "参考图必须使用 HTTP(S) URL,不能使用 Base64"}), 400
|
|
|
|
|
|
normalized_image_urls.append(absolute_url)
|
|
|
|
|
|
|
2026-01-17 23:15:58 +08:00
|
|
|
|
api_key, target_api, cost, use_trial, error = validate_generation_request(user, data)
|
|
|
|
|
|
if error:
|
|
|
|
|
|
return jsonify({"error": error}), 400
|
2026-01-12 00:53:31 +08:00
|
|
|
|
|
2026-01-17 23:15:58 +08:00
|
|
|
|
if use_trial:
|
2026-01-20 09:53:06 +08:00
|
|
|
|
deduct_points(user_id, cost)
|
2026-01-12 00:53:31 +08:00
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
model_value = data.get("model")
|
|
|
|
|
|
prompt = (data.get("prompt") or "").strip()
|
|
|
|
|
|
if not prompt:
|
|
|
|
|
|
return jsonify({"error": "提示词不能为空"}), 400
|
2026-02-27 10:36:08 +08:00
|
|
|
|
model_lower = model_value.lower()
|
|
|
|
|
|
is_chat_model = ("gemini" in model_lower or "gpt" in model_lower) and "image" not in model_lower
|
2026-01-12 00:53:31 +08:00
|
|
|
|
|
2026-01-17 23:15:58 +08:00
|
|
|
|
if is_chat_model:
|
2026-04-24 23:17:10 +08:00
|
|
|
|
result, status_code = handle_chat_generation_sync(
|
|
|
|
|
|
user_id, api_key, model_value, prompt, use_trial, cost
|
|
|
|
|
|
)
|
2026-01-17 23:15:58 +08:00
|
|
|
|
return jsonify(result), status_code
|
2026-01-12 00:53:31 +08:00
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
if model_value == "gpt-image-2":
|
|
|
|
|
|
payload = {
|
|
|
|
|
|
"prompt": prompt,
|
|
|
|
|
|
"model": model_value,
|
2026-05-14 12:30:10 +08:00
|
|
|
|
"response_format": "url",
|
2026-04-24 23:20:28 +08:00
|
|
|
|
"size": data.get("size") or "2048x2048",
|
2026-04-24 23:17:10 +08:00
|
|
|
|
"quality": "high",
|
|
|
|
|
|
}
|
2026-07-16 11:38:43 +08:00
|
|
|
|
if normalized_image_urls:
|
|
|
|
|
|
payload["image"] = normalized_image_urls
|
2026-04-24 23:17:10 +08:00
|
|
|
|
else:
|
|
|
|
|
|
payload = {
|
|
|
|
|
|
"prompt": prompt,
|
|
|
|
|
|
"model": model_value,
|
|
|
|
|
|
"response_format": "url",
|
|
|
|
|
|
"aspect_ratio": data.get("ratio"),
|
|
|
|
|
|
}
|
2026-07-16 11:38:43 +08:00
|
|
|
|
if normalized_image_urls:
|
|
|
|
|
|
payload["image"] = normalized_image_urls
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
if model_value in ("nano-banana-2", "gemini-3.1-flash-image-preview") and data.get("size"):
|
|
|
|
|
|
payload["image_size"] = data.get("size")
|
|
|
|
|
|
|
2026-01-15 21:42:03 +08:00
|
|
|
|
app = current_app._get_current_object()
|
2026-04-24 23:17:10 +08:00
|
|
|
|
task_id = start_async_image_task(
|
|
|
|
|
|
app,
|
|
|
|
|
|
user_id,
|
|
|
|
|
|
payload,
|
|
|
|
|
|
api_key,
|
|
|
|
|
|
target_api,
|
|
|
|
|
|
cost,
|
|
|
|
|
|
data.get("mode"),
|
|
|
|
|
|
model_value,
|
|
|
|
|
|
use_trial,
|
|
|
|
|
|
)
|
2026-01-15 21:42:03 +08:00
|
|
|
|
|
|
|
|
|
|
return jsonify({
|
|
|
|
|
|
"task_id": task_id,
|
2026-04-24 23:17:10 +08:00
|
|
|
|
"message": "已开启异步生成任务",
|
2026-01-15 21:42:03 +08:00
|
|
|
|
})
|
2026-01-12 00:53:31 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
2026-05-16 23:11:08 +08:00
|
|
|
|
@api_bp.route("/api/aigc-detect", methods=["POST"])
|
|
|
|
|
|
@login_required
|
|
|
|
|
|
def aigc_detect():
|
|
|
|
|
|
try:
|
|
|
|
|
|
user_id = session.get("user_id")
|
|
|
|
|
|
data = request.json if request.is_json else request.form
|
|
|
|
|
|
result, status_code = detect_aigc_image(
|
|
|
|
|
|
user_id,
|
|
|
|
|
|
(data.get("image_url") or data.get("url") or "").strip(),
|
|
|
|
|
|
request.url_root,
|
|
|
|
|
|
)
|
|
|
|
|
|
return jsonify(result), status_code
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
@api_bp.route("/api/video/generate", methods=["POST"])
|
2026-01-16 22:24:14 +08:00
|
|
|
|
@login_required
|
|
|
|
|
|
def video_generate():
|
|
|
|
|
|
try:
|
2026-04-24 23:17:10 +08:00
|
|
|
|
user_id = session.get("user_id")
|
2026-01-16 22:24:14 +08:00
|
|
|
|
user = db.session.get(User, user_id)
|
|
|
|
|
|
data = request.json
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
2026-01-17 23:15:58 +08:00
|
|
|
|
model_value, cost, error = validate_video_request(user, data)
|
|
|
|
|
|
if error:
|
|
|
|
|
|
return jsonify({"error": error}), 400
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
2026-01-20 09:53:06 +08:00
|
|
|
|
deduct_points(user_id, cost)
|
2026-01-16 22:24:14 +08:00
|
|
|
|
|
|
|
|
|
|
payload = {
|
|
|
|
|
|
"model": model_value,
|
2026-04-24 23:17:10 +08:00
|
|
|
|
"prompt": data.get("prompt"),
|
|
|
|
|
|
"enhance_prompt": data.get("enhance_prompt", False),
|
|
|
|
|
|
"images": data.get("images", []),
|
|
|
|
|
|
"aspect_ratio": data.get("aspect_ratio", "9:16"),
|
2026-01-16 22:24:14 +08:00
|
|
|
|
}
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
2026-02-05 20:47:14 +08:00
|
|
|
|
if payload.get("images"):
|
2026-04-24 23:17:10 +08:00
|
|
|
|
payload["images"] = [img.split(",", 1)[1] if "," in img else img for img in payload["images"]]
|
2026-01-16 22:24:14 +08:00
|
|
|
|
|
|
|
|
|
|
app = current_app._get_current_object()
|
2026-01-17 23:15:58 +08:00
|
|
|
|
task_id = start_async_video_task(app, user_id, payload, cost, model_value)
|
2026-01-16 22:24:14 +08:00
|
|
|
|
|
|
|
|
|
|
return jsonify({
|
|
|
|
|
|
"task_id": task_id,
|
2026-04-24 23:17:10 +08:00
|
|
|
|
"message": "视频生成任务已提交,系统正在处理中",
|
2026-01-16 22:24:14 +08:00
|
|
|
|
})
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
@api_bp.route("/api/notifications/latest", methods=["GET"])
|
2026-01-12 23:29:29 +08:00
|
|
|
|
@login_required
|
|
|
|
|
|
def get_latest_notification():
|
|
|
|
|
|
try:
|
2026-04-24 23:17:10 +08:00
|
|
|
|
user_id = session.get("user_id")
|
2026-01-17 23:15:58 +08:00
|
|
|
|
data = get_user_latest_notification(user_id)
|
|
|
|
|
|
return jsonify(data)
|
2026-01-12 23:29:29 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
@api_bp.route("/api/notifications/read", methods=["POST"])
|
2026-01-12 23:29:29 +08:00
|
|
|
|
@login_required
|
|
|
|
|
|
def mark_notif_read():
|
|
|
|
|
|
try:
|
2026-04-24 23:17:10 +08:00
|
|
|
|
user_id = session.get("user_id")
|
2026-01-12 23:29:29 +08:00
|
|
|
|
data = request.json
|
2026-04-24 23:17:10 +08:00
|
|
|
|
notif_id = data.get("id")
|
2026-01-12 23:29:29 +08:00
|
|
|
|
if not notif_id:
|
|
|
|
|
|
return jsonify({"error": "缺少通知 ID"}), 400
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
2026-01-17 23:15:58 +08:00
|
|
|
|
mark_notification_as_read(user_id, notif_id)
|
2026-01-12 23:29:29 +08:00
|
|
|
|
return jsonify({"status": "ok"})
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
@api_bp.route("/api/history", methods=["GET"])
|
2026-01-12 00:53:31 +08:00
|
|
|
|
@login_required
|
|
|
|
|
|
def get_history():
|
|
|
|
|
|
try:
|
2026-04-24 23:17:10 +08:00
|
|
|
|
user_id = session.get("user_id")
|
|
|
|
|
|
page = request.args.get("page", 1, type=int)
|
|
|
|
|
|
per_page = request.args.get("per_page", 10, type=int)
|
|
|
|
|
|
filter_type = request.args.get("filter_type", "all")
|
2026-01-17 23:15:58 +08:00
|
|
|
|
data = get_user_history_data(user_id, page, per_page, filter_type)
|
|
|
|
|
|
return jsonify(data)
|
2026-01-12 00:53:31 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return jsonify({"error": str(e)}), 500
|
2026-01-16 22:24:14 +08:00
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
@api_bp.route("/api/stats/points", methods=["GET"])
|
2026-01-17 23:15:58 +08:00
|
|
|
|
@login_required
|
|
|
|
|
|
def get_user_point_stats():
|
|
|
|
|
|
from services.stats_service import get_point_stats
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
user_id = session.get("user_id")
|
|
|
|
|
|
days = request.args.get("days", 7, type=int)
|
2026-01-17 23:15:58 +08:00
|
|
|
|
return jsonify(get_point_stats(user_id, days))
|
|
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
@api_bp.route("/api/stats/details", methods=["GET"])
|
2026-01-17 23:15:58 +08:00
|
|
|
|
@login_required
|
|
|
|
|
|
def get_user_point_details():
|
|
|
|
|
|
from services.stats_service import get_point_details
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
user_id = session.get("user_id")
|
|
|
|
|
|
page = request.args.get("page", 1, type=int)
|
2026-01-17 23:15:58 +08:00
|
|
|
|
return jsonify(get_point_details(user_id, page))
|
|
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
@api_bp.route("/api/download_proxy", methods=["GET"])
|
2026-01-16 22:24:14 +08:00
|
|
|
|
@login_required
|
|
|
|
|
|
def download_proxy():
|
2026-01-17 23:15:58 +08:00
|
|
|
|
import time
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
url = request.args.get("url")
|
2026-01-17 23:15:58 +08:00
|
|
|
|
default_name = f"video-{int(time.time())}.mp4"
|
2026-04-24 23:17:10 +08:00
|
|
|
|
filename = request.args.get("filename") or default_name
|
|
|
|
|
|
|
2026-01-16 22:24:14 +08:00
|
|
|
|
if not url:
|
|
|
|
|
|
return jsonify({"error": "缺少 URL 参数"}), 400
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
2026-01-16 22:24:14 +08:00
|
|
|
|
try:
|
2026-04-26 10:39:46 +08:00
|
|
|
|
parsed_url = urlparse(url)
|
|
|
|
|
|
public_prefix = Config.MINIO["public_url"].rstrip("/") + "/"
|
|
|
|
|
|
is_same_origin_file = (
|
|
|
|
|
|
parsed_url.path.startswith(public_prefix)
|
|
|
|
|
|
and (not parsed_url.scheme or parsed_url.netloc == request.host)
|
|
|
|
|
|
)
|
|
|
|
|
|
if is_same_origin_file:
|
|
|
|
|
|
object_key = unquote(parsed_url.path[len(public_prefix):])
|
|
|
|
|
|
file_obj = s3_client.get_object(Bucket=Config.MINIO["bucket"], Key=object_key)
|
|
|
|
|
|
headers = {
|
|
|
|
|
|
"Content-Type": file_obj.get("ContentType") or "application/octet-stream",
|
|
|
|
|
|
"Content-Disposition": f'attachment; filename="{filename}"',
|
|
|
|
|
|
"Cache-Control": "private, max-age=0",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def generate_minio_stream():
|
|
|
|
|
|
try:
|
|
|
|
|
|
for chunk in file_obj["Body"].iter_chunks(chunk_size=4096):
|
|
|
|
|
|
yield chunk
|
|
|
|
|
|
finally:
|
|
|
|
|
|
file_obj["Body"].close()
|
|
|
|
|
|
|
|
|
|
|
|
return current_app.response_class(generate_minio_stream(), headers=headers)
|
|
|
|
|
|
|
2026-01-17 23:15:58 +08:00
|
|
|
|
req, headers = get_remote_file_stream(url)
|
2026-04-24 23:17:10 +08:00
|
|
|
|
headers["Content-Disposition"] = f'attachment; filename="{filename}"'
|
|
|
|
|
|
|
|
|
|
|
|
def generate_stream():
|
2026-04-26 10:39:46 +08:00
|
|
|
|
try:
|
|
|
|
|
|
for chunk in req.iter_content(chunk_size=4096):
|
|
|
|
|
|
yield chunk
|
|
|
|
|
|
finally:
|
|
|
|
|
|
req.close()
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
return current_app.response_class(generate_stream(), headers=headers)
|
2026-01-16 22:24:14 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
system_logger.error(f"代理下载失败: {str(e)}")
|
|
|
|
|
|
return jsonify({"error": "下载失败"}), 500
|
2026-01-21 20:43:46 +08:00
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
@api_bp.route("/api/prompts", methods=["GET"])
|
2026-01-21 20:43:46 +08:00
|
|
|
|
@login_required
|
|
|
|
|
|
def list_prompts():
|
|
|
|
|
|
try:
|
2026-04-24 23:17:10 +08:00
|
|
|
|
user_id = session.get("user_id")
|
2026-01-21 20:43:46 +08:00
|
|
|
|
prompts = SavedPrompt.query.filter_by(user_id=user_id).order_by(SavedPrompt.created_at.desc()).all()
|
2026-04-24 23:17:10 +08:00
|
|
|
|
return jsonify([
|
|
|
|
|
|
{
|
|
|
|
|
|
"id": p.id,
|
|
|
|
|
|
"label": p.title,
|
|
|
|
|
|
"value": p.prompt,
|
|
|
|
|
|
}
|
|
|
|
|
|
for p in prompts
|
|
|
|
|
|
])
|
2026-01-21 20:43:46 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
@api_bp.route("/api/prompts", methods=["POST"])
|
2026-01-21 20:43:46 +08:00
|
|
|
|
@login_required
|
|
|
|
|
|
def save_prompt():
|
|
|
|
|
|
try:
|
2026-04-24 23:17:10 +08:00
|
|
|
|
user_id = session.get("user_id")
|
2026-01-21 20:43:46 +08:00
|
|
|
|
data = request.json
|
2026-04-24 23:17:10 +08:00
|
|
|
|
title = data.get("title")
|
|
|
|
|
|
prompt_text = data.get("prompt")
|
|
|
|
|
|
|
2026-01-21 20:43:46 +08:00
|
|
|
|
if not title or not prompt_text:
|
|
|
|
|
|
return jsonify({"error": "标题和内容不能为空"}), 400
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
2026-01-21 20:43:46 +08:00
|
|
|
|
count = SavedPrompt.query.filter_by(user_id=user_id).count()
|
|
|
|
|
|
if count >= 50:
|
|
|
|
|
|
return jsonify({"error": "收藏数量已达上限 (50)"}), 400
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
prompt = SavedPrompt(user_id=user_id, title=title, prompt=prompt_text)
|
|
|
|
|
|
db.session.add(prompt)
|
2026-01-21 20:43:46 +08:00
|
|
|
|
db.session.commit()
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
return jsonify({"message": "保存成功", "id": prompt.id})
|
2026-01-21 20:43:46 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
|
2026-04-24 23:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
@api_bp.route("/api/prompts/<int:id>", methods=["DELETE"])
|
2026-01-21 20:43:46 +08:00
|
|
|
|
@login_required
|
|
|
|
|
|
def delete_prompt(id):
|
|
|
|
|
|
try:
|
2026-04-24 23:17:10 +08:00
|
|
|
|
user_id = session.get("user_id")
|
|
|
|
|
|
prompt = SavedPrompt.query.filter_by(id=id, user_id=user_id).first()
|
|
|
|
|
|
if prompt:
|
|
|
|
|
|
db.session.delete(prompt)
|
2026-01-21 20:43:46 +08:00
|
|
|
|
db.session.commit()
|
|
|
|
|
|
return jsonify({"message": "删除成功"})
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return jsonify({"error": str(e)}), 500
|