Spaces:
Runtime error
Runtime error
Commit ·
d19344e
1
Parent(s): e0039b5
feat: process jobs; upload_pdf to sftp; status callbacks
Browse files- sftp_store.py +7 -0
- worker.py +50 -0
sftp_store.py
CHANGED
|
@@ -80,3 +80,10 @@ def upload_bytes(remote_path: str, data: bytes) -> None:
|
|
| 80 |
sftp.close()
|
| 81 |
finally:
|
| 82 |
transport.close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
sftp.close()
|
| 81 |
finally:
|
| 82 |
transport.close()
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def upload_pdf(pdf_id: str, data: bytes) -> str:
|
| 86 |
+
# Stores under: pdfs/{pdf_id}.pdf (relative to SFTP_ROOT)
|
| 87 |
+
remote_path = f"pdfs/{pdf_id}.pdf"
|
| 88 |
+
upload_bytes(remote_path, data)
|
| 89 |
+
return remote_path
|
worker.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
| 1 |
import os
|
| 2 |
import time
|
|
|
|
| 3 |
import threading
|
| 4 |
import requests
|
| 5 |
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
|
|
|
|
|
| 6 |
from sftp_store import sftp_test
|
| 7 |
|
| 8 |
import sftp_store
|
|
@@ -46,6 +49,53 @@ def serve_http():
|
|
| 46 |
HTTPServer(("0.0.0.0", PORT), Handler).serve_forever()
|
| 47 |
|
| 48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
def poll_loop():
|
| 50 |
print("Worker polling started", flush=True)
|
| 51 |
|
|
|
|
| 1 |
import os
|
| 2 |
import time
|
| 3 |
+
import base64
|
| 4 |
import threading
|
| 5 |
import requests
|
| 6 |
from http.server import BaseHTTPRequestHandler, HTTPServer
|
| 7 |
+
|
| 8 |
+
from sftp_store import upload_pdf
|
| 9 |
from sftp_store import sftp_test
|
| 10 |
|
| 11 |
import sftp_store
|
|
|
|
| 49 |
HTTPServer(("0.0.0.0", PORT), Handler).serve_forever()
|
| 50 |
|
| 51 |
|
| 52 |
+
def _api_headers() -> dict:
|
| 53 |
+
# API expects X-Worker-Token (NOT Authorization Bearer)
|
| 54 |
+
return {"X-Worker-Token": WORKER_TOKEN}
|
| 55 |
+
|
| 56 |
+
def _job_status(job_id: str, status: str, message: str = "") -> None:
|
| 57 |
+
try:
|
| 58 |
+
requests.post(
|
| 59 |
+
f"{API_BASE}/api/jobs/{job_id}/status",
|
| 60 |
+
headers=_api_headers(),
|
| 61 |
+
json={"status": status, "message": message},
|
| 62 |
+
timeout=10,
|
| 63 |
+
)
|
| 64 |
+
except Exception as e:
|
| 65 |
+
print(f"status update failed: {e}", flush=True)
|
| 66 |
+
|
| 67 |
+
def process_job(job: dict) -> None:
|
| 68 |
+
# API returns job dict (asdict(Job)) => id field is the canonical one
|
| 69 |
+
jid = job.get("id") or job.get("job_id")
|
| 70 |
+
if not jid:
|
| 71 |
+
raise RuntimeError("job missing id/job_id")
|
| 72 |
+
|
| 73 |
+
payload = job.get("payload") or {}
|
| 74 |
+
action = payload.get("action")
|
| 75 |
+
|
| 76 |
+
if action == "upload_pdf":
|
| 77 |
+
pdf_id = payload.get("pdf_id")
|
| 78 |
+
pdf_b64 = payload.get("pdf_b64")
|
| 79 |
+
if not pdf_id or not pdf_b64:
|
| 80 |
+
raise RuntimeError("upload_pdf requires payload.pdf_id and payload.pdf_b64")
|
| 81 |
+
|
| 82 |
+
# decode
|
| 83 |
+
try:
|
| 84 |
+
pdf_bytes = base64.b64decode(pdf_b64)
|
| 85 |
+
except Exception as e:
|
| 86 |
+
raise RuntimeError(f"invalid base64 in pdf_b64: {e}")
|
| 87 |
+
|
| 88 |
+
# upload
|
| 89 |
+
remote_path = upload_pdf(pdf_id, pdf_bytes)
|
| 90 |
+
_job_status(jid, "done", f"uploaded:{remote_path}")
|
| 91 |
+
print(f"job {jid} done -> {remote_path}", flush=True)
|
| 92 |
+
return
|
| 93 |
+
|
| 94 |
+
# unknown action -> fail but don't crash loop
|
| 95 |
+
_job_status(jid, "failed", f"unknown action: {action}")
|
| 96 |
+
print(f"job {jid} failed: unknown action {action}", flush=True)
|
| 97 |
+
|
| 98 |
+
|
| 99 |
def poll_loop():
|
| 100 |
print("Worker polling started", flush=True)
|
| 101 |
|