Avinashnalla7 commited on
Commit
5c59b0f
·
1 Parent(s): 3a934ac

api: add SFTP download helper

Browse files
Files changed (1) hide show
  1. backend/sftp_store.py +31 -105
backend/sftp_store.py CHANGED
@@ -1,113 +1,39 @@
1
  import os
2
  import posixpath
3
- from dataclasses import dataclass
4
- from typing import Optional
5
-
6
  import paramiko
7
 
8
 
9
- @dataclass
10
- class SFTPConfig:
11
- host: str
12
- port: int
13
- user: str
14
- password: str
15
- root: str
16
-
17
-
18
- def _cfg() -> SFTPConfig:
19
- host = os.getenv("SFTP_HOST", "").strip()
20
- port = int(os.getenv("SFTP_PORT", "2222"))
21
- user = os.getenv("SFTP_USER", "").strip()
22
- password = os.getenv("SFTP_PASS", "")
23
- root = os.getenv("SFTP_ROOT", "/inserio").strip() or "/inserio"
24
-
25
- if not host or not user or not password:
26
- raise RuntimeError("Missing SFTP_HOST/SFTP_USER/SFTP_PASS")
27
-
28
- # normalize root to POSIX
29
- if not root.startswith("/"):
30
- root = "/" + root
31
- return SFTPConfig(host=host, port=port, user=user, password=password, root=root)
32
-
33
-
34
- class SFTPStore:
35
- def __init__(self, cfg: Optional[SFTPConfig] = None):
36
- self.cfg = cfg or _cfg()
37
-
38
- def _connect(self):
39
- t = paramiko.Transport((self.cfg.host, self.cfg.port))
40
- t.connect(username=self.cfg.user, password=self.cfg.password)
41
- sftp = paramiko.SFTPClient.from_transport(t)
42
- return t, sftp
43
-
44
- def _abs(self, rel: str) -> str:
45
- rel = rel.lstrip("/")
46
- return posixpath.join(self.cfg.root, rel)
47
-
48
- def mkdir_p(self, remote_dir_rel: str) -> None:
49
- t, sftp = self._connect()
50
- try:
51
- path = self._abs(remote_dir_rel).rstrip("/")
52
- parts = path.split("/")
53
- cur = ""
54
- for part in parts:
55
- if not part:
56
- continue
57
- cur = cur + "/" + part
58
- try:
59
- sftp.stat(cur)
60
- except FileNotFoundError:
61
- sftp.mkdir(cur)
62
- finally:
63
- sftp.close()
64
- t.close()
65
-
66
- def put_bytes(self, remote_path_rel: str, data: bytes) -> None:
67
- remote_abs = self._abs(remote_path_rel)
68
- remote_dir = posixpath.dirname(remote_abs)
69
-
70
- t, sftp = self._connect()
71
- try:
72
- # mkdir -p
73
- parts = remote_dir.split("/")
74
- cur = ""
75
- for part in parts:
76
- if not part:
77
- continue
78
- cur = cur + "/" + part
79
- try:
80
- sftp.stat(cur)
81
- except FileNotFoundError:
82
- sftp.mkdir(cur)
83
-
84
- tmp = remote_abs + ".tmp"
85
- with sftp.file(tmp, "wb") as f:
86
- f.write(data)
87
- f.flush()
88
- sftp.rename(tmp, remote_abs)
89
- finally:
90
- sftp.close()
91
- t.close()
92
-
93
- def get_bytes(self, remote_path_rel: str) -> bytes:
94
- remote_abs = self._abs(remote_path_rel)
95
- t, sftp = self._connect()
96
  try:
97
- with sftp.file(remote_abs, "rb") as f:
98
- return f.read()
99
- finally:
100
  sftp.close()
101
- t.close()
102
-
103
- def exists(self, remote_path_rel: str) -> bool:
104
- remote_abs = self._abs(remote_path_rel)
105
- t, sftp = self._connect()
106
- try:
107
- sftp.stat(remote_abs)
108
- return True
109
- except FileNotFoundError:
110
- return False
111
  finally:
112
- sftp.close()
113
- t.close()
 
1
  import os
2
  import posixpath
 
 
 
3
  import paramiko
4
 
5
 
6
+ def _env(name: str) -> str:
7
+ v = (os.getenv(name) or "").strip()
8
+ if not v:
9
+ raise RuntimeError(f"Missing env var: {name}")
10
+ return v
11
+
12
+
13
+ def download_bytes(remote_path: str) -> bytes:
14
+ """
15
+ Downloads a file from SFTP_ROOT + remote_path.
16
+ remote_path should be relative like 'pdfs/<id>.pdf' (no leading slash).
17
+ """
18
+ host = _env("SFTP_HOST")
19
+ port = int(_env("SFTP_PORT"))
20
+ user = _env("SFTP_USER")
21
+ pw = _env("SFTP_PASS")
22
+ root = (_env("SFTP_ROOT").rstrip("/") or "/")
23
+
24
+ rp = remote_path.lstrip("/")
25
+
26
+ transport = paramiko.Transport((host, port))
27
+ transport.connect(username=user, password=pw)
28
+ sftp = paramiko.SFTPClient.from_transport(transport)
29
+
30
+ try:
31
+ sftp.chdir(root)
32
+ full = posixpath.join(".", rp)
33
+ with sftp.open(full, "rb") as f:
34
+ return f.read()
35
+ finally:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  try:
 
 
 
37
  sftp.close()
 
 
 
 
 
 
 
 
 
 
38
  finally:
39
+ transport.close()