v0.0.7: SSE per-file progress — таблица обновляется по каждому файлу
Deploy drhider / validate (push) Waiting to run

This commit is contained in:
2026-07-13 10:55:43 +04:00
parent 46d8ced0dd
commit e3405556b5
3 changed files with 83 additions and 16 deletions
+56 -6
View File
@@ -1,18 +1,20 @@
"""
Blueprint: API DrHider.
Four endpoints:
- POST /api/upload — upload one file -> {session_id}
- POST /api/process/{sid} — process all session files -> {status:"done"}
- GET /api/download/{sid} — download ZIP (with timestamp name)
- GET /api/csv/{sid} — download CSV separately
Five endpoints:
- POST /api/upload — upload one file -> {session_id}
- GET /api/process_stream/<sid> — SSE: process files, per-file progress
- POST /api/process/<sid> — process all session files -> {status:"done"} (legacy)
- GET /api/download/<sid> — download ZIP (with timestamp name)
- GET /api/csv/<sid> — download CSV separately
"""
import io
import json
import zipfile
import traceback
from datetime import datetime
from flask import Blueprint, request, send_file, jsonify
from flask import Blueprint, request, send_file, jsonify, Response, stream_with_context
from drhider import obfuscate_files, LLMClient
from drhider.builder import build_zip, build_mapping_csv
@@ -40,6 +42,54 @@ def upload():
return jsonify({"ok": True, "session": sid, "count": file_count(sid)})
@api_bp.route("/process_stream/<sid>", methods=["GET"])
def process_stream(sid):
"""SSE: process all session files one by one, streaming per-file progress."""
files = get_files(sid)
if files is None:
return jsonify({"ok": False, "error": "Session not found"}), 404
if not files:
return jsonify({"ok": False, "error": "No files"}), 400
total = len(files)
def generate():
llm = LLMClient()
all_results = []
all_mapping = {}
for idx, (fname, content) in enumerate(files):
# Отправляем: начали файл
yield f"data: {json.dumps({'event': 'start', 'idx': idx, 'name': fname, 'total': total})}\n\n"
zip_data, _csv = obfuscate_files([(fname, content, "")], llm_client=llm)
with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
for name in zf.namelist():
if name == "mapping.csv":
csv_text = zf.read(name).decode("utf-8")
lines = csv_text.strip().split("\n")
if len(lines) > 1:
for line in lines[1:]:
parts = line.split(",", 2)
if len(parts) >= 3:
all_mapping[parts[1]] = parts[2]
elif not name.endswith("/"):
all_results.append((name, zf.read(name)))
# Отправляем: закончили файл
yield f"data: {json.dumps({'event': 'done', 'idx': idx, 'name': fname, 'total': total})}\n\n"
csv_str = build_mapping_csv(all_mapping) if all_mapping else ""
final_zip = build_zip(all_results)
store_result(sid, final_zip)
if csv_str:
store_csv(sid, csv_str)
yield f"data: {json.dumps({'event': 'complete', 'total': len(all_results)})}\n\n"
return Response(
stream_with_context(generate()),
content_type="text/event-stream",
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"}
)
@api_bp.route("/process/<sid>", methods=["POST"])
def process(sid):
"""Process all session files one by one -> ZIP."""