80 lines
2.3 KiB
Python
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
|