""" Fixup: call make_public() on every .bsm binary blob under melodies/ in Firebase Storage. Run this if blobs were uploaded but are returning AccessDenied. docker exec -it bellsystems-backend python scripts/fix_storage_acl.py --dry-run docker exec -it bellsystems-backend python scripts/fix_storage_acl.py """ import argparse import os import sys from pathlib import Path # --------------------------------------------------------------------------- # .env loader # --------------------------------------------------------------------------- def _load_env() -> dict: search = Path(__file__).resolve().parent for _ in range(4): env_file = search / ".env" if env_file.exists(): result = {} for line in env_file.read_text(encoding="utf-8").splitlines(): line = line.strip() if not line or line.startswith("#") or "=" not in line: continue key, _, val = line.partition("=") result[key.strip()] = val.strip().strip('"').strip("'") print(f"[INFO] Loaded config from {env_file}") return result search = search.parent return {} _env = _load_env() def _cfg(key: str, default: str = "") -> str: return _env.get(key) or os.environ.get(key) or default # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- def run(dry_run: bool = False): label = "[DRY-RUN]" if dry_run else "[LIVE]" sa_path = _cfg("FIREBASE_SERVICE_ACCOUNT_PATH", "./firebase-service-account.json") bucket_name = _cfg("FIREBASE_STORAGE_BUCKET") if not bucket_name: print("ERROR: FIREBASE_STORAGE_BUCKET not set in .env") sys.exit(1) import firebase_admin from firebase_admin import credentials, storage as fb_storage cred = credentials.Certificate(sa_path) firebase_admin.initialize_app(cred, {"storageBucket": bucket_name}) bucket = fb_storage.bucket() print(f"\n{label} Scanning melodies/ in bucket: {bucket_name}\n") blobs = list(bucket.list_blobs(prefix="melodies/")) bsm_blobs = [b for b in blobs if b.name.lower().endswith(".bsm")] print(f"Found {len(bsm_blobs)} .bsm blob(s)\n") fixed = 0 for blob in bsm_blobs: print(f" {'[skip] ' if dry_run else '[fix] '}{blob.name}") if not dry_run: try: blob.make_public() fixed += 1 except Exception as e: print(f" ERROR: {e}") print(f"\n{label} Done. {fixed if not dry_run else len(bsm_blobs)} blob(s) {'would be ' if dry_run else ''}made public.") if dry_run: print("Run without --dry-run to apply.") if __name__ == "__main__": parser = argparse.ArgumentParser(description="Make all melody .bsm blobs public in Firebase Storage") parser.add_argument("--dry-run", action="store_true") args = parser.parse_args() run(dry_run=args.dry_run)