92 lines
3.5 KiB
Python
92 lines
3.5 KiB
Python
import os
|
|
import logging
|
|
import boto3
|
|
from botocore.client import Config
|
|
from botocore.exceptions import ClientError
|
|
import json
|
|
|
|
logger = logging.getLogger("storage")
|
|
|
|
# Read settings from environment
|
|
MINIO_ENDPOINT = os.getenv("MINIO_ENDPOINT", "minio:9000")
|
|
MINIO_ACCESS_KEY = os.getenv("MINIO_ACCESS_KEY", "minio_admin")
|
|
MINIO_SECRET_KEY = os.getenv("MINIO_SECRET_KEY", "minio_secure_password_2026")
|
|
MINIO_BUCKET = os.getenv("MINIO_BUCKET", "pragativeer-uploads")
|
|
|
|
# Initialize S3/Minio client
|
|
# Since backend runs inside Docker, endpoint_url must point to 'http://minio:9000'
|
|
# However, from the client browser, it will be reached via 'http://localhost:9000'
|
|
s3_client = boto3.client(
|
|
"s3",
|
|
endpoint_url=f"http://{MINIO_ENDPOINT}",
|
|
aws_access_key_id=MINIO_ACCESS_KEY,
|
|
aws_secret_access_key=MINIO_SECRET_KEY,
|
|
config=Config(signature_version="s3v4"),
|
|
region_name="us-east-1"
|
|
)
|
|
|
|
def ensure_bucket_exists():
|
|
try:
|
|
# Check if bucket exists
|
|
s3_client.head_bucket(Bucket=MINIO_BUCKET)
|
|
logger.info(f"Minio bucket '{MINIO_BUCKET}' already exists.")
|
|
except ClientError as e:
|
|
error_code = e.response["Error"]["Code"]
|
|
if error_code == "404":
|
|
try:
|
|
# Create bucket
|
|
s3_client.create_bucket(Bucket=MINIO_BUCKET)
|
|
logger.info(f"Successfully created Minio bucket '{MINIO_BUCKET}'.")
|
|
|
|
# Set public read policy for direct browser access
|
|
bucket_policy = {
|
|
"Version": "2012-10-17",
|
|
"Statement": [
|
|
{
|
|
"Sid": "PublicReadGetObject",
|
|
"Effect": "Allow",
|
|
"Principal": "*",
|
|
"Action": ["s3:GetObject"],
|
|
"Resource": [f"arn:aws:s3:::{MINIO_BUCKET}/*"]
|
|
}
|
|
]
|
|
}
|
|
s3_client.put_bucket_policy(
|
|
Bucket=MINIO_BUCKET,
|
|
Policy=json.dumps(bucket_policy)
|
|
)
|
|
logger.info(f"Public read policy applied to Minio bucket '{MINIO_BUCKET}'.")
|
|
except Exception as ex:
|
|
logger.error(f"Failed to create Minio bucket: {ex}")
|
|
else:
|
|
logger.error(f"Error checking Minio bucket: {e}")
|
|
|
|
def upload_file(file_content: bytes, object_name: str, content_type: str = "application/octet-stream") -> str:
|
|
"""Uploads file content to Minio and returns the direct public URL."""
|
|
try:
|
|
ensure_bucket_exists()
|
|
s3_client.put_object(
|
|
Bucket=MINIO_BUCKET,
|
|
Key=object_name,
|
|
Body=file_content,
|
|
ContentType=content_type
|
|
)
|
|
|
|
# Construct public URL (from browser's point of view, it connects to localhost:9000)
|
|
# We can resolve this dynamically by returning a path like /api/uploads/ or direct localhost:9000 link
|
|
public_url = f"/api/storage/{MINIO_BUCKET}/{object_name}"
|
|
logger.info(f"Successfully uploaded {object_name} to Minio. URL: {public_url}")
|
|
return public_url
|
|
except Exception as e:
|
|
logger.error(f"Failed to upload file to Minio: {e}")
|
|
return ""
|
|
|
|
def get_file(object_name: str) -> bytes:
|
|
"""Downloads file content from Minio."""
|
|
try:
|
|
response = s3_client.get_object(Bucket=MINIO_BUCKET, Key=object_name)
|
|
return response["Body"].read()
|
|
except Exception as e:
|
|
logger.error(f"Failed to get file from Minio: {e}")
|
|
return b""
|