v2: чанки 30KB + MD5, /v2/chunk + /v2/finalize, Redis, worker, v2.0
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
"""v2/chunk_api.py — Приём чанков по 30KB, финализация."""
|
||||
|
||||
import sys, os, base64, hashlib, logging
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'site'))
|
||||
import db, mimeutil
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
import init as v2
|
||||
|
||||
from flask import Blueprint, request, jsonify
|
||||
|
||||
bp = Blueprint("v2", __name__, url_prefix="/v2")
|
||||
|
||||
@bp.route("/chunk", methods=["POST"])
|
||||
def receive_chunk():
|
||||
body = request.get_json(silent=True) or {}
|
||||
if not body.get("upload_id") or not body.get("data"):
|
||||
return jsonify({"ok": False, "error": "missing fields"}), 400
|
||||
try:
|
||||
v2.store_chunk(body["upload_id"], int(body.get("index", 0)), body["data"])
|
||||
return jsonify({"ok": True, "received": int(body.get("index", 0))})
|
||||
except Exception as e:
|
||||
return jsonify({"ok": False, "error": str(e)}), 500
|
||||
|
||||
@bp.route("/finalize", methods=["POST"])
|
||||
def finalize_upload():
|
||||
body = request.get_json(silent=True) or {}
|
||||
upload_id = body.get("upload_id", "")
|
||||
filename = body.get("filename", "")
|
||||
total = int(body.get("total", 0))
|
||||
checksum = body.get("checksum", "")
|
||||
|
||||
if not all([upload_id, filename, total]):
|
||||
return jsonify({"ok": False, "error": "missing fields"}), 400
|
||||
|
||||
chunks = v2.get_chunks(upload_id)
|
||||
if len(chunks) != total:
|
||||
return jsonify({"ok": False, "error": f"expected {total}, got {len(chunks)}"}), 409
|
||||
|
||||
try:
|
||||
file_bytes = b"".join(base64.b64decode(c) for c in chunks)
|
||||
except Exception as e:
|
||||
return jsonify({"ok": False, "error": f"decode: {e}"}), 422
|
||||
|
||||
if checksum and hashlib.md5(file_bytes).hexdigest() != checksum:
|
||||
return jsonify({"ok": False, "error": "checksum mismatch"}), 422
|
||||
|
||||
mime = mimeutil.guess_mime(filename) or "application/octet-stream"
|
||||
|
||||
# Сохранить в БД
|
||||
conn, err = db.connect()
|
||||
if err:
|
||||
return jsonify({"ok": False, "error": f"db: {err}"}), 500
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"INSERT INTO contracts (number, client) VALUES (%s,%s) RETURNING id",
|
||||
("б/н " + __import__("datetime").datetime.now().strftime("%Y%m%d-%H%M"), ""),
|
||||
)
|
||||
cid = cur.fetchone()[0]
|
||||
cur.execute(
|
||||
"INSERT INTO documents (filename, mime_type, original_bytes, status) VALUES (%s,%s,%s,'uploaded')",
|
||||
(filename, mime, file_bytes),
|
||||
)
|
||||
cur.execute("SELECT id FROM documents WHERE filename=%s ORDER BY created_at DESC LIMIT 1", (filename,))
|
||||
doc_id = cur.fetchone()[0]
|
||||
cur.execute("INSERT INTO supplements (contract_id, document_id, type) VALUES (%s,%s,'initial')", (str(cid), str(doc_id)))
|
||||
conn.commit()
|
||||
cur.close()
|
||||
finally:
|
||||
db.put_conn(conn)
|
||||
|
||||
v2.delete_chunks(upload_id)
|
||||
v2.push_finalize({"doc_id": str(doc_id), "filename": filename, "mime_type": mime})
|
||||
|
||||
return jsonify({"ok": True, "contract_id": str(cid), "doc_id": str(doc_id)})
|
||||
Reference in New Issue
Block a user