huhuh / storage.py
kokokoasd's picture
Upload 21 files
36ce73b verified
"""
Shared storage layer — zone metadata and path utilities.
Single Responsibility: only handles metadata persistence and path resolution.
Dependency Inversion: routers depend on these abstractions instead of importing each other.
"""
import json
import os
from pathlib import Path
from config import DATA_DIR, ZONES_META, ZONE_NAME_PATTERN
def load_meta() -> dict:
"""Load zones metadata from JSON file."""
if ZONES_META.exists():
return json.loads(ZONES_META.read_text(encoding="utf-8"))
return {}
def save_meta(meta: dict):
"""Save zones metadata to JSON file."""
ZONES_META.write_text(json.dumps(meta, indent=2, default=str), encoding="utf-8")
def validate_zone_name(name: str):
"""Validate zone name format. Raises ValueError if invalid."""
if not ZONE_NAME_PATTERN.match(name):
raise ValueError("Tên zone chỉ chứa a-z, A-Z, 0-9, _, - (tối đa 50 ký tự)")
def get_zone_path(name: str) -> Path:
"""Get the filesystem path for a zone, validating it exists."""
validate_zone_name(name)
zone_path = DATA_DIR / name
if not zone_path.is_dir():
raise ValueError(f"Zone '{name}' không tồn tại")
return zone_path
def safe_path(zone_path: Path, rel_path: str) -> Path:
"""Resolve a relative path within a zone, preventing path traversal."""
target = (zone_path / rel_path).resolve()
zone_resolved = zone_path.resolve()
if target != zone_resolved and not str(target).startswith(str(zone_resolved) + os.sep):
raise ValueError("Truy cập ngoài zone không được phép")
return target
def check_zone_owner(zone_name: str, user_sub: str, user_role: str):
"""Check that the user owns the zone. Admins can access all zones."""
if user_role == "admin":
return
meta = load_meta()
info = meta.get(zone_name)
if not info:
raise ValueError(f"Zone '{zone_name}' không tồn tại")
if info.get("owner_id") != user_sub:
raise ValueError("Bạn không có quyền truy cập zone này")