fix: switch blob sharding from uuid7 leading hex to random tail in attachments, project_files, and zip_service

This commit is contained in:
2026-06-13 19:12:54 +00:00
parent 1294c95bed
commit 5c199474f3
5 changed files with 9 additions and 9 deletions
+2 -1
View File
@@ -122,7 +122,8 @@ def is_extension_allowed(ext):
def _directory_for(uid):
return f"{uid[:2]}/{uid[2:4]}"
tail = uid.replace("-", "")
return f"{tail[-2:]}/{tail[-4:-2]}"
def _detect_mime(file_bytes, original_filename):
+2 -5
View File
@@ -9,6 +9,7 @@ from pathlib import Path
from devplacepy.config import BASE_DIR, ZIPS_DIR, ZIP_STAGING_DIR
from devplacepy import project_files
from devplacepy.attachments import _directory_for
from devplacepy.services.jobs.base import JobService
from devplacepy.utils import generate_uid, slugify
@@ -17,10 +18,6 @@ logger = logging.getLogger(__name__)
WORKER_MODULE = "devplacepy.services.jobs.zip_worker"
def _shard(uid: str) -> str:
return f"{uid[:2]}/{uid[2:4]}"
class ZipService(JobService):
kind = "zip"
title = "Zip"
@@ -43,7 +40,7 @@ class ZipService(JobService):
fail_meta = {"project_uid": project_uid}
try:
item_count = await asyncio.to_thread(self._materialize, source, staging)
tmp_dir = ZIPS_DIR / _shard(uid)
tmp_dir = ZIPS_DIR / _directory_for(uid)
tmp_dir.mkdir(parents=True, exist_ok=True)
tmp_zip = tmp_dir / f"{generate_uid()}.zip"
stats = await self._run_worker(staging, tmp_zip)
+1 -1
View File
@@ -25,7 +25,7 @@ An empty `path` archives the whole project; a non-empty `path` archives that fil
## Naming and storage
The final name is `{crc32}.{slug}.zip`, where the slug comes from the caller's preferred name with any trailing `.zip` stripped and the rest slugified. The archive lives under `config.DATA_DIR/zips/{uid[:2]}/{uid[2:4]}/` (outside the package, not under `/static`); it is served by the `/zips/{uid}/download` route via `FileResponse`, never the static mount. The temporary `{uuid4}.zip` is renamed into place with `os.replace`, so re-running a job overwrites the previous archive. The staging directory is then removed.
The final name is `{crc32}.{slug}.zip`, where the slug comes from the caller's preferred name with any trailing `.zip` stripped and the rest slugified. The archive lives under `config.DATA_DIR/zips/{uid[-2:]}/{uid[-4:-2]}/` (sharded on the random tail of the uuid7 via `attachments._directory_for`, outside the package, not under `/static`); it is served by the `/zips/{uid}/download` route via `FileResponse`, never the static mount. The temporary `{uuid4}.zip` is renamed into place with `os.replace`, so re-running a job overwrites the previous archive. The staging directory is then removed.
## Endpoints