quanliang commited on
Commit
b97bd99
·
1 Parent(s): a884cdf

Debug for storage management made

Browse files
Files changed (2) hide show
  1. index.html +7 -4
  2. main.py +82 -29
index.html CHANGED
@@ -1596,7 +1596,7 @@
1596
  </label>
1597
  </div>
1598
  <div class="admin-actions">
1599
- <button type="button" id="wipeDataBtn" class="danger-btn">Delete all /data contents</button>
1600
  </div>
1601
  </div>
1602
  <div id="adminPanelStatus"></div>
@@ -3626,7 +3626,7 @@
3626
  }
3627
 
3628
  const proceed = window.confirm(
3629
- 'This will permanently delete all files/folders inside /data. Continue?'
3630
  );
3631
  if (!proceed) {
3632
  return;
@@ -3644,12 +3644,15 @@
3644
 
3645
  const data = await response.json();
3646
  if (!response.ok || !data.ok) {
3647
- setAdminStatus(data.message || 'Failed to delete /data contents.', true);
3648
  return;
3649
  }
3650
 
 
 
 
3651
  setAdminStatus(
3652
- `Deleted /data contents (files: ${data.deleted_files ?? 0}, folders: ${data.deleted_dirs ?? 0}).`
3653
  );
3654
  adminDeleteConfirmInput.value = '';
3655
  } catch (error) {
 
1596
  </label>
1597
  </div>
1598
  <div class="admin-actions">
1599
+ <button type="button" id="wipeDataBtn" class="danger-btn">Delete managed storage contents</button>
1600
  </div>
1601
  </div>
1602
  <div id="adminPanelStatus"></div>
 
3626
  }
3627
 
3628
  const proceed = window.confirm(
3629
+ 'This will permanently delete all managed storage files/folders. Continue?'
3630
  );
3631
  if (!proceed) {
3632
  return;
 
3644
 
3645
  const data = await response.json();
3646
  if (!response.ok || !data.ok) {
3647
+ setAdminStatus(data.message || 'Failed to delete managed storage contents.', true);
3648
  return;
3649
  }
3650
 
3651
+ const targetInfo = Array.isArray(data.deleted_targets) && data.deleted_targets.length > 0
3652
+ ? ` Targets: ${data.deleted_targets.join(', ')}.`
3653
+ : '';
3654
  setAdminStatus(
3655
+ `Deleted storage contents (files: ${data.deleted_files ?? 0}, folders: ${data.deleted_dirs ?? 0}).${targetInfo}`
3656
  );
3657
  adminDeleteConfirmInput.value = '';
3658
  } catch (error) {
main.py CHANGED
@@ -2890,20 +2890,66 @@ def get_admin_password() -> Optional[str]:
2890
  return password.strip()
2891
  return None
2892
 
2893
- def get_admin_purge_root() -> str:
2894
- configured = (os.environ.get("ADMIN_PURGE_PATH") or DEFAULT_ADMIN_PURGE_PATH).strip()
2895
- if not configured:
2896
- configured = DEFAULT_ADMIN_PURGE_PATH
2897
- return os.path.abspath(configured)
 
 
 
 
2898
 
2899
  def is_safe_admin_purge_root(path: str) -> bool:
2900
  normalized = os.path.abspath(path)
2901
  # Must be an absolute subpath, never root.
2902
  if normalized in {"/", ""}:
2903
  return False
2904
- if not normalized.startswith("/data"):
2905
- return False
2906
- return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2907
 
2908
  def delete_directory_contents(path: str) -> Dict[str, int]:
2909
  deleted_files = 0
@@ -2962,39 +3008,46 @@ async def admin_wipe_data(
2962
  status_code=400
2963
  )
2964
 
2965
- purge_root = get_admin_purge_root()
2966
- if not is_safe_admin_purge_root(purge_root):
 
2967
  return JSONResponse(
2968
- content={"ok": False, "message": f"Unsafe ADMIN_PURGE_PATH resolved to '{purge_root}'."},
2969
  status_code=400
2970
  )
2971
 
2972
- if not os.path.exists(purge_root):
 
2973
  return JSONResponse(
2974
- content={"ok": False, "message": f"Purge path does not exist: {purge_root}"},
 
 
 
 
 
 
2975
  status_code=404
2976
  )
2977
 
2978
- if not os.path.isdir(purge_root):
2979
- return JSONResponse(
2980
- content={"ok": False, "message": f"Purge path is not a directory: {purge_root}"},
2981
- status_code=400
2982
- )
2983
-
2984
  try:
2985
- result = delete_directory_contents(purge_root)
2986
- logger.warning(
2987
- "Admin data wipe executed for %s (files=%s, dirs=%s)",
2988
- purge_root,
2989
- result["deleted_files"],
2990
- result["deleted_dirs"]
2991
- )
 
 
2992
  return JSONResponse(
2993
  content={
2994
  "ok": True,
2995
- "message": f"Deleted contents of {purge_root}.",
2996
- "deleted_files": result["deleted_files"],
2997
- "deleted_dirs": result["deleted_dirs"]
 
 
 
2998
  },
2999
  status_code=200
3000
  )
 
2890
  return password.strip()
2891
  return None
2892
 
2893
+ def get_root_database_dirs() -> List[str]:
2894
+ roots = [os.path.abspath(os.path.join(get_base_directory(), "root databases"))]
2895
+ try:
2896
+ from vectorstore import CHROMA_DB_BASE
2897
+ roots.append(os.path.abspath(CHROMA_DB_BASE))
2898
+ except Exception:
2899
+ pass
2900
+ # Preserve order while removing duplicates.
2901
+ return list(dict.fromkeys(roots))
2902
 
2903
  def is_safe_admin_purge_root(path: str) -> bool:
2904
  normalized = os.path.abspath(path)
2905
  # Must be an absolute subpath, never root.
2906
  if normalized in {"/", ""}:
2907
  return False
2908
+ if normalized.startswith("/data"):
2909
+ return True
2910
+ allowed_names = {"uploaded files", "root databases"}
2911
+ return os.path.basename(normalized) in allowed_names
2912
+
2913
+ def resolve_admin_purge_targets() -> Tuple[List[str], List[str]]:
2914
+ """
2915
+ Resolve safe directories to purge.
2916
+ Priority:
2917
+ 1) ADMIN_PURGE_PATH when configured
2918
+ 2) /data when mounted
2919
+ 3) known storage folders used by this app
2920
+ Returns (existing_targets, skipped_non_existing_targets)
2921
+ """
2922
+ configured = (os.environ.get("ADMIN_PURGE_PATH") or "").strip()
2923
+ if configured:
2924
+ configured_path = os.path.abspath(configured)
2925
+ if not is_safe_admin_purge_root(configured_path):
2926
+ raise ValueError(f"Unsafe ADMIN_PURGE_PATH resolved to '{configured_path}'.")
2927
+ if os.path.exists(configured_path):
2928
+ if not os.path.isdir(configured_path):
2929
+ raise ValueError(f"ADMIN_PURGE_PATH is not a directory: {configured_path}")
2930
+ return [configured_path], []
2931
+ return [], [configured_path]
2932
+
2933
+ data_path = os.path.abspath(DEFAULT_ADMIN_PURGE_PATH)
2934
+ if os.path.isdir(data_path):
2935
+ return [data_path], []
2936
+
2937
+ candidates: List[str] = [os.path.abspath(UPLOAD_DIR)]
2938
+ candidates.extend(get_root_database_dirs())
2939
+ # Remove duplicates while preserving order.
2940
+ candidates = list(dict.fromkeys(candidates))
2941
+
2942
+ existing_targets: List[str] = []
2943
+ skipped_targets: List[str] = []
2944
+ for candidate in candidates:
2945
+ if not is_safe_admin_purge_root(candidate):
2946
+ continue
2947
+ if os.path.exists(candidate):
2948
+ if os.path.isdir(candidate):
2949
+ existing_targets.append(candidate)
2950
+ else:
2951
+ skipped_targets.append(candidate)
2952
+ return existing_targets, skipped_targets
2953
 
2954
  def delete_directory_contents(path: str) -> Dict[str, int]:
2955
  deleted_files = 0
 
3008
  status_code=400
3009
  )
3010
 
3011
+ try:
3012
+ targets, skipped_targets = resolve_admin_purge_targets()
3013
+ except ValueError as e:
3014
  return JSONResponse(
3015
+ content={"ok": False, "message": str(e)},
3016
  status_code=400
3017
  )
3018
 
3019
+ if not targets:
3020
+ skipped_text = ", ".join(skipped_targets) if skipped_targets else "none"
3021
  return JSONResponse(
3022
+ content={
3023
+ "ok": False,
3024
+ "message": (
3025
+ "No existing managed storage directories found to purge. "
3026
+ f"Checked: {skipped_text}"
3027
+ )
3028
+ },
3029
  status_code=404
3030
  )
3031
 
 
 
 
 
 
 
3032
  try:
3033
+ total_deleted_files = 0
3034
+ total_deleted_dirs = 0
3035
+ per_target: Dict[str, Dict[str, int]] = {}
3036
+ for target in targets:
3037
+ result = delete_directory_contents(target)
3038
+ per_target[target] = result
3039
+ total_deleted_files += result["deleted_files"]
3040
+ total_deleted_dirs += result["deleted_dirs"]
3041
+
3042
  return JSONResponse(
3043
  content={
3044
  "ok": True,
3045
+ "message": f"Deleted managed storage contents from {len(targets)} target(s).",
3046
+ "deleted_files": total_deleted_files,
3047
+ "deleted_dirs": total_deleted_dirs,
3048
+ "deleted_targets": targets,
3049
+ "per_target": per_target,
3050
+ "skipped_missing_targets": skipped_targets
3051
  },
3052
  status_code=200
3053
  )