mohsin-devs commited on
Commit
8605269
·
1 Parent(s): 9d1006c

Boot safely when HF token secret is missing

Browse files
Files changed (2) hide show
  1. server/config.py +7 -2
  2. server/storage/hf.py +28 -6
server/config.py CHANGED
@@ -48,10 +48,15 @@ LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
48
  HF_STORAGE_LABEL = "HF"
49
 
50
 
51
- def _required_env(name: str, default: str | None = None) -> str:
52
  value = os.getenv(name, "").strip()
53
  if not value and default is not None:
54
  value = default.strip()
 
 
 
 
 
55
  if not value:
56
  raise RuntimeError(f"Missing required environment variable: {name}")
57
  return value
@@ -64,7 +69,7 @@ if STORAGE_MODE != HF_STORAGE_LABEL:
64
  f"DocVault is production-configured for STORAGE_MODE={HF_STORAGE_LABEL} only."
65
  )
66
 
67
- HF_TOKEN = _required_env("HF_TOKEN")
68
  HF_REPO_ID = _required_env("HF_REPO_ID")
69
  HF_REPO_TYPE = _required_env("HF_REPO_TYPE", "dataset")
70
  if HF_REPO_TYPE not in {"dataset", "model", "space"}:
 
48
  HF_STORAGE_LABEL = "HF"
49
 
50
 
51
+ def _optional_env(name: str, default: str | None = None) -> str | None:
52
  value = os.getenv(name, "").strip()
53
  if not value and default is not None:
54
  value = default.strip()
55
+ return value or None
56
+
57
+
58
+ def _required_env(name: str, default: str | None = None) -> str:
59
+ value = _optional_env(name, default)
60
  if not value:
61
  raise RuntimeError(f"Missing required environment variable: {name}")
62
  return value
 
69
  f"DocVault is production-configured for STORAGE_MODE={HF_STORAGE_LABEL} only."
70
  )
71
 
72
+ HF_TOKEN = _optional_env("HF_TOKEN") or _optional_env("HUGGING_FACE_HUB_TOKEN") or _optional_env("HF_API_TOKEN")
73
  HF_REPO_ID = _required_env("HF_REPO_ID")
74
  HF_REPO_TYPE = _required_env("HF_REPO_TYPE", "dataset")
75
  if HF_REPO_TYPE not in {"dataset", "model", "space"}:
server/storage/hf.py CHANGED
@@ -27,12 +27,22 @@ class HuggingFaceStorageManager(StorageInterface):
27
  """Stores all files and folders in a Hugging Face dataset repository."""
28
 
29
  def __init__(self) -> None:
30
- self.api = HfApi(token=config.HF_TOKEN)
31
- self._ensure_repo_exists()
 
 
 
 
 
 
 
 
32
 
33
  def _ensure_repo_exists(self) -> None:
 
34
  try:
35
  self.api.repo_info(repo_id=config.HF_REPO_ID, repo_type=config.HF_REPO_TYPE)
 
36
  except Exception:
37
  self.api.create_repo(
38
  repo_id=config.HF_REPO_ID,
@@ -40,6 +50,7 @@ class HuggingFaceStorageManager(StorageInterface):
40
  private=True,
41
  exist_ok=True,
42
  )
 
43
 
44
  def _timestamp(self) -> str:
45
  return datetime.now(timezone.utc).isoformat()
@@ -60,10 +71,13 @@ class HuggingFaceStorageManager(StorageInterface):
60
  return "/".join([repo_folder, config.FOLDER_MARKER]) if repo_folder else config.FOLDER_MARKER
61
 
62
  def _list_repo_files(self) -> List[str]:
63
- return self.api.list_repo_files(
64
- repo_id=config.HF_REPO_ID,
65
- repo_type=config.HF_REPO_TYPE,
66
- )
 
 
 
67
 
68
  def _file_exists(self, repo_path: str, repo_files: List[str] | None = None) -> bool:
69
  repo_files = repo_files if repo_files is not None else self._list_repo_files()
@@ -115,6 +129,7 @@ class HuggingFaceStorageManager(StorageInterface):
115
  counter += 1
116
 
117
  def create_folder(self, user_id: str, folder_path: str) -> Dict[str, Any]:
 
118
  folder_path = self._validate_relative_path(folder_path, "folder_path")
119
  if not folder_path:
120
  return {"success": False, "error": "folder_path is required", "code": "INVALID_FOLDER"}
@@ -147,6 +162,7 @@ class HuggingFaceStorageManager(StorageInterface):
147
  def upload_file(
148
  self, user_id: str, folder_path: str, filename: str, file_obj: Any
149
  ) -> Dict[str, Any]:
 
150
  folder_path = self._validate_relative_path(folder_path, "folder_path")
151
 
152
  relative_path, repo_path = self._next_available_file_path(user_id, folder_path, filename)
@@ -185,6 +201,7 @@ class HuggingFaceStorageManager(StorageInterface):
185
  }
186
 
187
  def delete_file(self, user_id: str, file_path: str) -> Dict[str, Any]:
 
188
  file_path = self._validate_relative_path(file_path, "file_path")
189
  repo_path = self._user_repo_path(user_id, file_path)
190
  if not self._file_exists(repo_path):
@@ -199,6 +216,7 @@ class HuggingFaceStorageManager(StorageInterface):
199
  return {"success": True, "message": f"Deleted file: {file_path}"}
200
 
201
  def rename_file(self, user_id: str, file_path: str, new_name: str) -> Dict[str, Any]:
 
202
  file_path = self._validate_relative_path(file_path, "file_path")
203
  new_name = sanitize_filename(new_name)
204
  if not PathValidator.is_valid_filename(new_name):
@@ -235,6 +253,7 @@ class HuggingFaceStorageManager(StorageInterface):
235
  }
236
 
237
  def delete_folder(self, user_id: str, folder_path: str) -> Dict[str, Any]:
 
238
  folder_path = self._validate_relative_path(folder_path, "folder_path")
239
  repo_folder_path = self._user_repo_path(user_id, folder_path)
240
  repo_files = self._list_repo_files()
@@ -259,6 +278,7 @@ class HuggingFaceStorageManager(StorageInterface):
259
  def rename_folder(
260
  self, user_id: str, folder_path: str, new_name: str
261
  ) -> Dict[str, Any]:
 
262
  folder_path = self._validate_relative_path(folder_path, "folder_path")
263
  new_name = sanitize_filename(new_name)
264
  if not PathValidator.is_valid_filename(new_name):
@@ -418,6 +438,7 @@ class HuggingFaceStorageManager(StorageInterface):
418
  }
419
 
420
  def get_history(self, user_id: str, path: str) -> List[Dict[str, Any]]:
 
421
  repo_path = self._user_repo_path(user_id, path)
422
  commits = self.api.list_repo_commits(
423
  repo_id=config.HF_REPO_ID,
@@ -441,6 +462,7 @@ class HuggingFaceStorageManager(StorageInterface):
441
  def restore(
442
  self, user_id: str, path: str, revision: str, as_copy: bool = False
443
  ) -> Dict[str, Any]:
 
444
  path = self._validate_relative_path(path)
445
  source_repo_path = self._user_repo_path(user_id, path)
446
  destination_repo_path = source_repo_path
 
27
  """Stores all files and folders in a Hugging Face dataset repository."""
28
 
29
  def __init__(self) -> None:
30
+ self.api = HfApi(token=config.HF_TOKEN) if config.HF_TOKEN else HfApi()
31
+ self._repo_ready = False
32
+ if config.HF_TOKEN:
33
+ self._ensure_repo_exists()
34
+
35
+ def _ensure_token(self) -> None:
36
+ if not config.HF_TOKEN:
37
+ raise RuntimeError(
38
+ "HF_TOKEN is required for write operations. Set HF_TOKEN or HUGGING_FACE_HUB_TOKEN in the Space secrets."
39
+ )
40
 
41
  def _ensure_repo_exists(self) -> None:
42
+ self._ensure_token()
43
  try:
44
  self.api.repo_info(repo_id=config.HF_REPO_ID, repo_type=config.HF_REPO_TYPE)
45
+ self._repo_ready = True
46
  except Exception:
47
  self.api.create_repo(
48
  repo_id=config.HF_REPO_ID,
 
50
  private=True,
51
  exist_ok=True,
52
  )
53
+ self._repo_ready = True
54
 
55
  def _timestamp(self) -> str:
56
  return datetime.now(timezone.utc).isoformat()
 
71
  return "/".join([repo_folder, config.FOLDER_MARKER]) if repo_folder else config.FOLDER_MARKER
72
 
73
  def _list_repo_files(self) -> List[str]:
74
+ try:
75
+ return self.api.list_repo_files(
76
+ repo_id=config.HF_REPO_ID,
77
+ repo_type=config.HF_REPO_TYPE,
78
+ )
79
+ except Exception:
80
+ return []
81
 
82
  def _file_exists(self, repo_path: str, repo_files: List[str] | None = None) -> bool:
83
  repo_files = repo_files if repo_files is not None else self._list_repo_files()
 
129
  counter += 1
130
 
131
  def create_folder(self, user_id: str, folder_path: str) -> Dict[str, Any]:
132
+ self._ensure_token()
133
  folder_path = self._validate_relative_path(folder_path, "folder_path")
134
  if not folder_path:
135
  return {"success": False, "error": "folder_path is required", "code": "INVALID_FOLDER"}
 
162
  def upload_file(
163
  self, user_id: str, folder_path: str, filename: str, file_obj: Any
164
  ) -> Dict[str, Any]:
165
+ self._ensure_token()
166
  folder_path = self._validate_relative_path(folder_path, "folder_path")
167
 
168
  relative_path, repo_path = self._next_available_file_path(user_id, folder_path, filename)
 
201
  }
202
 
203
  def delete_file(self, user_id: str, file_path: str) -> Dict[str, Any]:
204
+ self._ensure_token()
205
  file_path = self._validate_relative_path(file_path, "file_path")
206
  repo_path = self._user_repo_path(user_id, file_path)
207
  if not self._file_exists(repo_path):
 
216
  return {"success": True, "message": f"Deleted file: {file_path}"}
217
 
218
  def rename_file(self, user_id: str, file_path: str, new_name: str) -> Dict[str, Any]:
219
+ self._ensure_token()
220
  file_path = self._validate_relative_path(file_path, "file_path")
221
  new_name = sanitize_filename(new_name)
222
  if not PathValidator.is_valid_filename(new_name):
 
253
  }
254
 
255
  def delete_folder(self, user_id: str, folder_path: str) -> Dict[str, Any]:
256
+ self._ensure_token()
257
  folder_path = self._validate_relative_path(folder_path, "folder_path")
258
  repo_folder_path = self._user_repo_path(user_id, folder_path)
259
  repo_files = self._list_repo_files()
 
278
  def rename_folder(
279
  self, user_id: str, folder_path: str, new_name: str
280
  ) -> Dict[str, Any]:
281
+ self._ensure_token()
282
  folder_path = self._validate_relative_path(folder_path, "folder_path")
283
  new_name = sanitize_filename(new_name)
284
  if not PathValidator.is_valid_filename(new_name):
 
438
  }
439
 
440
  def get_history(self, user_id: str, path: str) -> List[Dict[str, Any]]:
441
+ self._ensure_token()
442
  repo_path = self._user_repo_path(user_id, path)
443
  commits = self.api.list_repo_commits(
444
  repo_id=config.HF_REPO_ID,
 
462
  def restore(
463
  self, user_id: str, path: str, revision: str, as_copy: bool = False
464
  ) -> Dict[str, Any]:
465
+ self._ensure_token()
466
  path = self._validate_relative_path(path)
467
  source_repo_path = self._user_repo_path(user_id, path)
468
  destination_repo_path = source_repo_path