mata01 commited on
Commit
afee3ee
·
1 Parent(s): 54cc2f8

feat: add Cloudinary support as an alternative image storage mode

Browse files
Files changed (3) hide show
  1. app/core/config.py +6 -1
  2. app/services/storage.py +51 -0
  3. pyproject.toml +1 -0
app/core/config.py CHANGED
@@ -73,7 +73,7 @@ class Settings(BaseSettings):
73
  return f"redis://{self.REDIS_HOST}:{self.REDIS_PORT}/0"
74
 
75
  # Storage Configurations
76
- STORAGE_MODE: str = "local" # "local" hoặc "r2"
77
  UPLOAD_DIR: str = "uploads"
78
  SERVER_HOST_URL: str = "http://localhost:8000"
79
 
@@ -83,6 +83,11 @@ class Settings(BaseSettings):
83
  R2_SECRET_ACCESS_KEY: str = "change_me_r2_secret_key"
84
  R2_ENDPOINT_URL: str = "https://change-me-account-id.r2.cloudflarestorage.com"
85
 
 
 
 
 
 
86
 
87
  # Qdrant Vector DB
88
  QDRANT_HOST: str = "localhost"
 
73
  return f"redis://{self.REDIS_HOST}:{self.REDIS_PORT}/0"
74
 
75
  # Storage Configurations
76
+ STORAGE_MODE: str = "local" # "local", "r2", hoặc "cloudinary"
77
  UPLOAD_DIR: str = "uploads"
78
  SERVER_HOST_URL: str = "http://localhost:8000"
79
 
 
83
  R2_SECRET_ACCESS_KEY: str = "change_me_r2_secret_key"
84
  R2_ENDPOINT_URL: str = "https://change-me-account-id.r2.cloudflarestorage.com"
85
 
86
+ # Cloudinary Configs
87
+ CLOUDINARY_CLOUD_NAME: Optional[str] = None
88
+ CLOUDINARY_API_KEY: Optional[str] = None
89
+ CLOUDINARY_API_SECRET: Optional[str] = None
90
+
91
 
92
  # Qdrant Vector DB
93
  QDRANT_HOST: str = "localhost"
app/services/storage.py CHANGED
@@ -15,6 +15,16 @@ class StorageService:
15
  aws_secret_access_key=settings.R2_SECRET_ACCESS_KEY,
16
  endpoint_url=settings.R2_ENDPOINT_URL
17
  )
 
 
 
 
 
 
 
 
 
 
18
  else:
19
  self.s3_client = None
20
 
@@ -42,6 +52,25 @@ class StorageService:
42
  except Exception as e:
43
  logging.error(f"Failed to upload local file: {e}")
44
  return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  else:
46
  # Cloudflare R2 Upload
47
  try:
@@ -85,6 +114,28 @@ class StorageService:
85
  except Exception as e:
86
  logging.error(f"Failed to delete local file: {e}")
87
  return False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  else:
89
  try:
90
  if not self.s3_client:
 
15
  aws_secret_access_key=settings.R2_SECRET_ACCESS_KEY,
16
  endpoint_url=settings.R2_ENDPOINT_URL
17
  )
18
+ elif settings.STORAGE_MODE == "cloudinary":
19
+ self.s3_client = None
20
+ import cloudinary
21
+ import cloudinary.uploader
22
+ cloudinary.config(
23
+ cloud_name=settings.CLOUDINARY_CLOUD_NAME,
24
+ api_key=settings.CLOUDINARY_API_KEY,
25
+ api_secret=settings.CLOUDINARY_API_SECRET,
26
+ secure=True
27
+ )
28
  else:
29
  self.s3_client = None
30
 
 
52
  except Exception as e:
53
  logging.error(f"Failed to upload local file: {e}")
54
  return None
55
+ elif settings.STORAGE_MODE == "cloudinary":
56
+ try:
57
+ import cloudinary.uploader
58
+ # Parse folder path and filename from sanitized path
59
+ path_parts = sanitized_object_name.split("/")
60
+ folder = "wardrobe/" + "/".join(path_parts[:-1]) if len(path_parts) > 1 else "wardrobe"
61
+ filename = os.path.splitext(path_parts[-1])[0]
62
+
63
+ result = cloudinary.uploader.upload(
64
+ file_content,
65
+ folder=folder,
66
+ public_id=filename,
67
+ overwrite=True,
68
+ resource_type="auto"
69
+ )
70
+ return result.get("secure_url")
71
+ except Exception as e:
72
+ logging.error(f"Failed to upload file to Cloudinary: {e}")
73
+ return None
74
  else:
75
  # Cloudflare R2 Upload
76
  try:
 
114
  except Exception as e:
115
  logging.error(f"Failed to delete local file: {e}")
116
  return False
117
+ elif settings.STORAGE_MODE == "cloudinary":
118
+ try:
119
+ import cloudinary.uploader
120
+ import re
121
+
122
+ # Parse public_id from Cloudinary URL:
123
+ # https://res.cloudinary.com/<cloud_name>/image/upload/v<version>/<folder>/<public_id>.<ext>
124
+ if "res.cloudinary.com" in object_name_or_url:
125
+ match = re.search(r"/upload/(?:v\d+/)?(.+)$", object_name_or_url)
126
+ if match:
127
+ public_id_with_ext = match.group(1)
128
+ public_id = os.path.splitext(public_id_with_ext)[0]
129
+ else:
130
+ public_id = object_name_or_url
131
+ else:
132
+ public_id = object_name_or_url
133
+
134
+ result = cloudinary.uploader.destroy(public_id)
135
+ return result.get("result") == "ok"
136
+ except Exception as e:
137
+ logging.error(f"Failed to delete file from Cloudinary: {e}")
138
+ return False
139
  else:
140
  try:
141
  if not self.s3_client:
pyproject.toml CHANGED
@@ -42,4 +42,5 @@ dependencies = [
42
  "huggingface-hub>=0.23.0",
43
  "matplotlib>=3.9.1",
44
  "tqdm>=4.66.4",
 
45
  ]
 
42
  "huggingface-hub>=0.23.0",
43
  "matplotlib>=3.9.1",
44
  "tqdm>=4.66.4",
45
+ "cloudinary>=1.41.0",
46
  ]