Coverage for server / connection / storage_bucket.py: 89%

28 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-10-04 09:33 +0000

1import os 

2import boto3 

3from botocore.client import Config 

4from dotenv import load_dotenv 

5 

6load_dotenv() 

7 

8# MinIO Configuration (IONOS Server) 

9MINIO_ENDPOINT = os.getenv("MINIO_ENDPOINT") 

10MINIO_ACCESS_KEY = os.getenv("MINIO_ACCESS_KEY") 

11MINIO_SECRET_KEY = os.getenv("MINIO_SECRET_KEY") 

12MINIO_BUCKET = os.getenv("MINIO_BUCKET") 

13MINIO_PUBLIC_URL = os.getenv("MINIO_PUBLIC_URL", MINIO_ENDPOINT) 

14 

15s3 = boto3.client( 

16 "s3", 

17 endpoint_url=MINIO_ENDPOINT, 

18 aws_access_key_id=MINIO_ACCESS_KEY, 

19 aws_secret_access_key=MINIO_SECRET_KEY, 

20 config=Config(signature_version="s3v4", s3={"addressing_style": "path"}), 

21 region_name="us-east-1", 

22) 

23 

24# Private bucket for PII (e.g. user profile photos). Objects here are NOT public-read; 

25# they are served only via short-lived presigned URLs minted at read time. 

26MINIO_PRIVATE_BUCKET = os.getenv("MINIO_PRIVATE_BUCKET", "eruditiontx-private") 

27 

28 

29def presign_get(key: str, bucket: str | None = None, expires: int = 600) -> str | None: 

30 """Mint a short-lived presigned GET URL for a private object. 

31 

32 ``key`` is the object key (e.g. ``user-images/<id>/<file>.png``). Returns ``None`` 

33 for an empty key so callers can pass through missing values unchanged. 

34 """ 

35 if not key: 

36 return None 

37 return s3.generate_presigned_url( 

38 "get_object", 

39 Params={"Bucket": bucket or MINIO_PRIVATE_BUCKET, "Key": key}, 

40 ExpiresIn=expires, 

41 ) 

42 

43 

44_USER_IMAGE_MARKER = "user-images/" 

45 

46 

47def profile_picture_url(value: str | None, expires: int = 600) -> str | None: 

48 """Return a display URL for a stored ``profile_picture`` value. 

49 

50 Profile photos live in the private bucket (PII). If ``value`` references a 

51 ``user-images/`` object — whether stored as a bare key (``user-images/<id>/<file>``) 

52 or a legacy full public URL — return a short-lived **presigned** GET URL so it 

53 renders in ``<img>`` without exposing the object publicly. Any other value 

54 (``None``, empty, or a non-user-images URL) is passed through unchanged. 

55 """ 

56 if not value or _USER_IMAGE_MARKER not in value: 

57 return value 

58 key = value[value.index(_USER_IMAGE_MARKER):] 

59 return presign_get(key, expires=expires) 

60 

61 

62_CLASS_IMAGE_MARKER = "class-images/" 

63 

64 

65def class_photo_url(value: str | None, expires: int = 600) -> str | None: 

66 """Return a display URL for a stored ``class_photo`` value. 

67 

68 Class cover photos live in the private bucket. If ``value`` references a 

69 ``class-images/`` object, return a short-lived **presigned** GET URL so it 

70 renders in an ``<img>`` without exposing the object publicly. Any other 

71 value (``None``, empty, or a non-class-images value) is passed through 

72 unchanged. 

73 """ 

74 if not value or _CLASS_IMAGE_MARKER not in value: 

75 return value 

76 key = value[value.index(_CLASS_IMAGE_MARKER):] 

77 return presign_get(key, expires=expires)