Avinashnalla7 commited on
Commit
f08e4d3
·
1 Parent(s): ab77655

Add SFTP storage helper for trainer outputs

Browse files
Files changed (1) hide show
  1. backend/worker/sftp_store.py +73 -0
backend/worker/sftp_store.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import base64
4
+ import os
5
+ from pathlib import Path
6
+ from typing import Optional, Tuple
7
+
8
+ import paramiko
9
+
10
+
11
+ def _sftp_client() -> Tuple[paramiko.SFTPClient, paramiko.Transport]:
12
+ host = (os.environ.get("SFTP_HOST") or "").strip()
13
+ user = (os.environ.get("SFTP_USER") or "").strip()
14
+ if not host or not user:
15
+ raise RuntimeError("Missing SFTP_HOST or SFTP_USER")
16
+
17
+ port = int((os.environ.get("SFTP_PORT") or "22").strip())
18
+ password = os.environ.get("SFTP_PASS")
19
+ key_b64 = os.environ.get("SFTP_KEY_B64")
20
+ key_pass = os.environ.get("SFTP_KEY_PASSPHRASE")
21
+
22
+ t = paramiko.Transport((host, port))
23
+ if key_b64:
24
+ key_bytes = base64.b64decode(key_b64)
25
+ pkey = paramiko.RSAKey.from_private_key_file(_write_tmp_key(key_bytes), password=key_pass)
26
+ t.connect(username=user, pkey=pkey)
27
+ else:
28
+ if not password:
29
+ raise RuntimeError("Missing SFTP_PASS (or provide SFTP_KEY_B64)")
30
+ t.connect(username=user, password=password)
31
+
32
+ return paramiko.SFTPClient.from_transport(t), t
33
+
34
+
35
+ def _write_tmp_key(key_bytes: bytes) -> str:
36
+ # HF container FS is writable for tmp; keep it simple.
37
+ p = Path("/tmp/sftp_key.pem")
38
+ p.write_bytes(key_bytes)
39
+ os.chmod(p, 0o600)
40
+ return str(p)
41
+
42
+
43
+ def _mkdir_p(sftp: paramiko.SFTPClient, remote_dir: str) -> None:
44
+ parts = remote_dir.strip("/").split("/")
45
+ cur = ""
46
+ for part in parts:
47
+ cur += "/" + part
48
+ try:
49
+ sftp.stat(cur)
50
+ except FileNotFoundError:
51
+ sftp.mkdir(cur)
52
+
53
+
54
+ def store_to_sftp(pdf_id: str, template_id: str, cfg_json_bytes: bytes, pdf_bytes: bytes, pdf_name: str) -> str:
55
+ base = (os.environ.get("SFTP_BASE_DIR") or "/").rstrip("/")
56
+ remote_dir = f"{base}/{template_id}/{pdf_id}"
57
+ remote_cfg = f"{remote_dir}/trainer_config_{pdf_id}__{template_id}.json"
58
+ remote_pdf = f"{remote_dir}/{pdf_name or (pdf_id + '.pdf')}"
59
+
60
+ sftp, transport = _sftp_client()
61
+ try:
62
+ _mkdir_p(sftp, remote_dir)
63
+ with sftp.open(remote_cfg, "wb") as f:
64
+ f.write(cfg_json_bytes)
65
+ with sftp.open(remote_pdf, "wb") as f:
66
+ f.write(pdf_bytes)
67
+ finally:
68
+ try:
69
+ sftp.close()
70
+ finally:
71
+ transport.close()
72
+
73
+ return remote_dir