You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

80 lines
2.9 KiB

from __future__ import annotations
import json
from pathlib import Path
import anyio
from sqlalchemy.ext.asyncio import AsyncSession
from app.adapters.storage.local import LocalStorageAdapter
from app.core.config import get_settings
from app.core.ids import new_id
from app.models.entities import Backend, Blob, BlobReplica
from app.repositories.backend_repository import BackendRepository
from app.repositories.blob_replica_repository import BlobReplicaRepository
DEFAULT_LOCAL_BACKEND_ID = "bkd_local_default"
DEFAULT_LOCAL_BACKEND_NAME = "local-default"
class LocalStorageService:
def __init__(self, session: AsyncSession) -> None:
self.session = session
self.settings = get_settings()
self.backend_repository = BackendRepository(session)
self.blob_replica_repository = BlobReplicaRepository(session)
self.adapter = LocalStorageAdapter(self.settings.local_storage_dir)
async def ensure_backend_ready(self) -> Backend:
await self.adapter.ensure_base_path()
backend = await self.backend_repository.get_by_name(DEFAULT_LOCAL_BACKEND_NAME)
if backend is not None:
return backend
backend = Backend(
id=DEFAULT_LOCAL_BACKEND_ID,
name=DEFAULT_LOCAL_BACKEND_NAME,
type="local",
stability_class="local",
read_priority=100,
write_priority=100,
is_enabled=True,
config_json=json.dumps({"base_path": self.settings.local_storage_dir}),
)
await self.backend_repository.create(backend)
await self.session.commit()
return backend
async def persist_blob_from_temp(self, blob: Blob, temp_path: str) -> BlobReplica:
backend = await self.ensure_backend_ready()
existing = await self.blob_replica_repository.get_by_blob_and_backend(blob.id, backend.id)
if existing is not None:
return existing
storage_key = self._build_storage_key(blob)
stored_path = await self.adapter.put_file(temp_path, storage_key)
replica = BlobReplica(
id=new_id("rep"),
blob_id=blob.id,
backend_id=backend.id,
storage_key=storage_key,
status="ready",
checksum=blob.content_hash,
size_bytes=blob.size_bytes,
)
created = await self.blob_replica_repository.create(replica)
await self.session.commit()
await anyio.to_thread.run_sync(lambda: Path(stored_path).exists())
return created
async def resolve_replica_path(self, replica: BlobReplica) -> Path:
await self.ensure_backend_ready()
return await self.adapter.resolve_path(replica.storage_key)
@staticmethod
def _build_storage_key(blob: Blob) -> str:
content_hash = blob.content_hash
prefix = content_hash[:2] if len(content_hash) >= 2 else "xx"
return f"blobs/{prefix}/{content_hash}"