idconvert/backend/core/storage.py

80 lines
2.3 KiB
Python

"""MinIO / S3-compatible object storage.
Provides upload and presigned URL generation for converted DOCX files.
"""
from __future__ import annotations
import boto3
import structlog
from botocore.exceptions import ClientError
from config import settings
from core.exceptions import StorageError
log = structlog.get_logger()
# Pack credit values — used by payment service to resolve pack → credit count
PACK_CREDITS: dict[str, int] = {
'starter': 5,
'studio': 20,
'agency': 60,
}
def _s3_client():
return boto3.client(
's3',
endpoint_url=settings.s3_endpoint_url,
region_name=settings.s3_region,
aws_access_key_id=settings.aws_access_key_id,
aws_secret_access_key=settings.aws_secret_access_key,
)
def upload_docx(object_key: str, docx_bytes: bytes) -> str:
"""Upload a DOCX file to MinIO and return a presigned download URL.
Args:
object_key: S3 object key, e.g. 'conversions/<session_id>.docx'
docx_bytes: Raw DOCX bytes.
Returns:
Presigned URL valid for settings.s3_presign_expiry_seconds.
Raises:
StorageError: If upload or presign fails.
"""
client = _s3_client()
try:
client.put_object(
Bucket=settings.s3_bucket,
Key=object_key,
Body=docx_bytes,
ContentType='application/vnd.openxmlformats-officedocument.wordprocessingml.document',
)
log.info('docx_uploaded', key=object_key, size_bytes=len(docx_bytes))
except ClientError as e:
log.error('docx_upload_failed', key=object_key, error=str(e))
raise StorageError(f'Upload failed: {e}')
try:
url = client.generate_presigned_url(
'get_object',
Params={'Bucket': settings.s3_bucket, 'Key': object_key},
ExpiresIn=settings.s3_presign_expiry_seconds,
)
return url
except ClientError as e:
log.error('docx_presign_failed', key=object_key, error=str(e))
raise StorageError(f'Presign failed: {e}')
def storage_available() -> bool:
"""Return True if MinIO/S3 is reachable and the bucket exists."""
try:
client = _s3_client()
client.head_bucket(Bucket=settings.s3_bucket)
return True
except Exception:
return False