76 lines
2.3 KiB
Python
76 lines
2.3 KiB
Python
from __future__ import annotations
|
|
|
|
import secrets
|
|
from datetime import datetime, timezone
|
|
|
|
from sqlalchemy import delete, select, update
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.models import Wrap, WrapStatus
|
|
from app.services.client_ip import client_ip, request_meta
|
|
from app.services.storage import storage
|
|
|
|
|
|
def new_wrap_id() -> str:
|
|
return secrets.token_urlsafe(18).replace("-", "").replace("_", "")[:24]
|
|
|
|
|
|
async def expire_due_wraps(db: AsyncSession) -> int:
|
|
now = datetime.now(timezone.utc)
|
|
result = await db.execute(
|
|
select(Wrap).where(Wrap.status == WrapStatus.pending, Wrap.expires_at <= now)
|
|
)
|
|
rows = list(result.scalars().all())
|
|
for wrap in rows:
|
|
try:
|
|
await storage.delete(wrap.object_key)
|
|
except Exception:
|
|
pass
|
|
wrap.status = WrapStatus.expired
|
|
if rows:
|
|
await db.commit()
|
|
return len(rows)
|
|
|
|
|
|
async def delete_wrap_object(db: AsyncSession, wrap: Wrap) -> None:
|
|
try:
|
|
await storage.delete(wrap.object_key)
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
async def cleanup_expired_meta(db: AsyncSession) -> int:
|
|
"""Remove long-expired wrap rows (objects already deleted)."""
|
|
now = datetime.now(timezone.utc)
|
|
result = await db.execute(
|
|
delete(Wrap).where(
|
|
Wrap.status.in_([WrapStatus.expired, WrapStatus.consumed]),
|
|
Wrap.expires_at < now,
|
|
)
|
|
)
|
|
await db.commit()
|
|
return result.rowcount or 0
|
|
|
|
|
|
async def mark_consumed(db: AsyncSession, wrap_id: str) -> Wrap | None:
|
|
now = datetime.now(timezone.utc)
|
|
result = await db.execute(
|
|
update(Wrap)
|
|
.where(Wrap.id == wrap_id, Wrap.status == WrapStatus.pending, Wrap.expires_at > now)
|
|
.values(status=WrapStatus.consumed, consumed_at=now)
|
|
.returning(Wrap)
|
|
)
|
|
row = result.scalar_one_or_none()
|
|
await db.commit()
|
|
return row
|
|
|
|
|
|
async def purge_all_wraps(db: AsyncSession) -> dict[str, int]:
|
|
"""Force-delete all wrap DB rows and all objects under wraps/ in MinIO."""
|
|
count_result = await db.execute(select(Wrap.id))
|
|
db_count = len(list(count_result.scalars().all()))
|
|
await db.execute(delete(Wrap))
|
|
await db.commit()
|
|
objects_deleted = await storage.delete_prefix("wraps/")
|
|
return {"wraps_deleted": db_count, "objects_deleted": objects_deleted}
|