LogicGoInfotechSpaces commited on
Commit
87f9058
·
1 Parent(s): a0088ec

Add MongoDB integration for API call logging with timestamps

Browse files
Files changed (6) hide show
  1. app/config.py +5 -0
  2. app/database.py +209 -0
  3. app/main.py +168 -11
  4. app/main_fastai.py +93 -6
  5. app/main_sdxl.py +207 -14
  6. requirements.txt +2 -1
app/config.py CHANGED
@@ -54,6 +54,11 @@ class Settings(BaseSettings):
54
  UPLOAD_DIR: str = os.getenv("UPLOAD_DIR", "uploads")
55
  RESULT_DIR: str = os.getenv("RESULT_DIR", "results")
56
 
 
 
 
 
 
57
  class Config:
58
  env_file = ".env"
59
  case_sensitive = False
 
54
  UPLOAD_DIR: str = os.getenv("UPLOAD_DIR", "uploads")
55
  RESULT_DIR: str = os.getenv("RESULT_DIR", "results")
56
 
57
+ # MongoDB settings
58
+ # MONGODB_URI should be set in Hugging Face Space secrets
59
+ MONGODB_URI: str = os.getenv("MONGODB_URI", "")
60
+ MONGODB_DB_NAME: str = os.getenv("MONGODB_DB_NAME", "colorization_db")
61
+
62
  class Config:
63
  env_file = ".env"
64
  case_sensitive = False
app/database.py ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ MongoDB database connection and logging utilities
3
+ """
4
+ import os
5
+ import logging
6
+ from datetime import datetime
7
+ from typing import Optional, Dict, Any
8
+ from pymongo import MongoClient
9
+ from pymongo.errors import ConnectionFailure, ServerSelectionTimeoutError
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ # MongoDB connection
14
+ _client: Optional[MongoClient] = None
15
+ _db = None
16
+
17
+ def get_mongodb_client() -> Optional[MongoClient]:
18
+ """Get or create MongoDB client"""
19
+ global _client
20
+ if _client is None:
21
+ mongodb_uri = os.getenv("MONGODB_URI")
22
+ if not mongodb_uri:
23
+ logger.warning("MONGODB_URI environment variable not set. MongoDB features will be disabled.")
24
+ return None
25
+ try:
26
+ _client = MongoClient(
27
+ mongodb_uri,
28
+ serverSelectionTimeoutMS=5000,
29
+ connectTimeoutMS=5000
30
+ )
31
+ # Test connection
32
+ _client.admin.command('ping')
33
+ logger.info("MongoDB connection established successfully")
34
+ except (ConnectionFailure, ServerSelectionTimeoutError) as e:
35
+ logger.error("Failed to connect to MongoDB: %s", str(e))
36
+ _client = None
37
+ return _client
38
+
39
+ def get_database():
40
+ """Get database instance"""
41
+ global _db
42
+ if _db is None:
43
+ client = get_mongodb_client()
44
+ if client:
45
+ db_name = os.getenv("MONGODB_DB_NAME", "colorization_db")
46
+ _db = client[db_name]
47
+ else:
48
+ logger.warning("MongoDB client not available")
49
+ return _db
50
+
51
+ def log_api_call(
52
+ endpoint: str,
53
+ method: str,
54
+ status_code: int = 200,
55
+ request_data: Optional[Dict[str, Any]] = None,
56
+ response_data: Optional[Dict[str, Any]] = None,
57
+ error: Optional[str] = None,
58
+ user_id: Optional[str] = None,
59
+ ip_address: Optional[str] = None
60
+ ) -> bool:
61
+ """
62
+ Log API call to MongoDB
63
+
64
+ Args:
65
+ endpoint: API endpoint path
66
+ method: HTTP method (GET, POST, etc.)
67
+ status_code: HTTP status code
68
+ request_data: Request data/parameters
69
+ response_data: Response data
70
+ error: Error message if any
71
+ user_id: User ID if authenticated
72
+ ip_address: Client IP address
73
+
74
+ Returns:
75
+ True if logged successfully, False otherwise
76
+ """
77
+ try:
78
+ db = get_database()
79
+ if not db:
80
+ logger.warning("MongoDB not available, skipping API log")
81
+ return False
82
+
83
+ collection = db["api_calls"]
84
+
85
+ log_entry = {
86
+ "endpoint": endpoint,
87
+ "method": method,
88
+ "status_code": status_code,
89
+ "timestamp": datetime.utcnow(),
90
+ "request_data": request_data or {},
91
+ "response_data": response_data or {},
92
+ "error": error,
93
+ "user_id": user_id,
94
+ "ip_address": ip_address
95
+ }
96
+
97
+ result = collection.insert_one(log_entry)
98
+ logger.info("API call logged to MongoDB: %s", result.inserted_id)
99
+ return True
100
+ except Exception as e:
101
+ logger.error("Failed to log API call to MongoDB: %s", str(e))
102
+ return False
103
+
104
+ def log_image_upload(
105
+ image_id: str,
106
+ filename: str,
107
+ file_size: int,
108
+ content_type: str,
109
+ user_id: Optional[str] = None,
110
+ ip_address: Optional[str] = None
111
+ ) -> bool:
112
+ """
113
+ Log image upload to MongoDB
114
+
115
+ Args:
116
+ image_id: Unique image identifier
117
+ filename: Original filename
118
+ file_size: File size in bytes
119
+ content_type: MIME type
120
+ user_id: User ID if authenticated
121
+ ip_address: Client IP address
122
+
123
+ Returns:
124
+ True if logged successfully, False otherwise
125
+ """
126
+ try:
127
+ db = get_database()
128
+ if not db:
129
+ logger.warning("MongoDB not available, skipping upload log")
130
+ return False
131
+
132
+ collection = db["image_uploads"]
133
+
134
+ log_entry = {
135
+ "image_id": image_id,
136
+ "filename": filename,
137
+ "file_size": file_size,
138
+ "content_type": content_type,
139
+ "uploaded_at": datetime.utcnow(),
140
+ "user_id": user_id,
141
+ "ip_address": ip_address
142
+ }
143
+
144
+ result = collection.insert_one(log_entry)
145
+ logger.info("Image upload logged to MongoDB: %s", result.inserted_id)
146
+ return True
147
+ except Exception as e:
148
+ logger.error("Failed to log image upload to MongoDB: %s", str(e))
149
+ return False
150
+
151
+ def log_colorization(
152
+ result_id: str,
153
+ image_id: Optional[str] = None,
154
+ prompt: Optional[str] = None,
155
+ model_type: Optional[str] = None,
156
+ processing_time: Optional[float] = None,
157
+ user_id: Optional[str] = None,
158
+ ip_address: Optional[str] = None
159
+ ) -> bool:
160
+ """
161
+ Log colorization request to MongoDB
162
+
163
+ Args:
164
+ result_id: Unique result identifier
165
+ image_id: Original image identifier
166
+ prompt: Text prompt used (if any)
167
+ model_type: Model type used (fastai, pytorch, sdxl, etc.)
168
+ processing_time: Time taken to process in seconds
169
+ user_id: User ID if authenticated
170
+ ip_address: Client IP address
171
+
172
+ Returns:
173
+ True if logged successfully, False otherwise
174
+ """
175
+ try:
176
+ db = get_database()
177
+ if not db:
178
+ logger.warning("MongoDB not available, skipping colorization log")
179
+ return False
180
+
181
+ collection = db["colorizations"]
182
+
183
+ log_entry = {
184
+ "result_id": result_id,
185
+ "image_id": image_id,
186
+ "prompt": prompt,
187
+ "model_type": model_type,
188
+ "processing_time": processing_time,
189
+ "created_at": datetime.utcnow(),
190
+ "user_id": user_id,
191
+ "ip_address": ip_address
192
+ }
193
+
194
+ result = collection.insert_one(log_entry)
195
+ logger.info("Colorization logged to MongoDB: %s", result.inserted_id)
196
+ return True
197
+ except Exception as e:
198
+ logger.error("Failed to log colorization to MongoDB: %s", str(e))
199
+ return False
200
+
201
+ def close_connection():
202
+ """Close MongoDB connection"""
203
+ global _client, _db
204
+ if _client:
205
+ _client.close()
206
+ _client = None
207
+ _db = None
208
+ logger.info("MongoDB connection closed")
209
+
app/main.py CHANGED
@@ -1,4 +1,4 @@
1
- from fastapi import FastAPI, File, UploadFile, HTTPException, Header
2
  from fastapi.responses import FileResponse
3
  from huggingface_hub import hf_hub_download
4
  import uuid
@@ -8,6 +8,7 @@ import json
8
  from PIL import Image
9
  import torch
10
  from torchvision import transforms
 
11
 
12
  # -------------------------------------------------
13
  # 🚀 FastAPI App
@@ -68,12 +69,42 @@ def colorize_image(img: Image.Image):
68
  output_img = transforms.ToPILImage()(tensor.squeeze())
69
  return output_img
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  # -------------------------------------------------
72
  # 🩺 Health Check
73
  # -------------------------------------------------
74
  @app.get("/health")
75
- def health_check():
76
- return {"status": "healthy", "model_loaded": True}
 
 
 
 
 
 
 
 
 
 
 
77
 
78
  # -------------------------------------------------
79
  # 🔐 Firebase Token Validator
@@ -88,88 +119,214 @@ def verify_app_check_token(token: str):
88
  # -------------------------------------------------
89
  @app.post("/upload")
90
  async def upload_image(
 
91
  file: UploadFile = File(...),
92
  x_firebase_appcheck: str = Header(None)
93
  ):
94
  verify_app_check_token(x_firebase_appcheck)
 
 
95
 
96
  if not file.content_type.startswith("image/"):
 
 
 
 
 
 
 
97
  raise HTTPException(status_code=400, detail="Invalid file type")
98
 
99
  image_id = f"{uuid.uuid4()}.jpg"
100
  file_path = os.path.join(UPLOAD_DIR, image_id)
101
 
 
 
 
102
  with open(file_path, "wb") as f:
103
- f.write(await file.read())
104
 
105
  base_url = "https://logicgoinfotechspaces-text-guided-image-colorization.hf.space"
106
 
107
- return {
108
  "success": True,
109
  "image_id": image_id.replace(".jpg", ""),
110
  "file_url": f"{base_url}/uploads/{image_id}"
111
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
  # -------------------------------------------------
114
  # 🎨 Colorize Image
115
  # -------------------------------------------------
116
  @app.post("/colorize")
117
  async def colorize(
 
118
  file: UploadFile = File(...),
119
  x_firebase_appcheck: str = Header(None)
120
  ):
 
 
 
121
  verify_app_check_token(x_firebase_appcheck)
 
 
122
 
123
  if not file.content_type.startswith("image/"):
 
 
 
 
 
 
 
124
  raise HTTPException(status_code=400, detail="Invalid file type")
125
 
126
  img = Image.open(io.BytesIO(await file.read()))
127
  output_img = colorize_image(img)
128
 
 
 
129
  result_id = f"{uuid.uuid4()}.jpg"
130
  output_path = os.path.join(RESULTS_DIR, result_id)
131
  output_img.save(output_path)
132
 
133
  base_url = "https://logicgoinfotechspaces-text-guided-image-colorization.hf.space"
134
 
135
- return {
 
 
136
  "success": True,
137
- "result_id": result_id.replace(".jpg", ""),
138
  "download_url": f"{base_url}/results/{result_id}",
139
- "api_download": f"{base_url}/download/{result_id.replace('.jpg','')}"
140
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
 
142
  # -------------------------------------------------
143
  # ⬇️ Download via API (Secure)
144
  # -------------------------------------------------
145
  @app.get("/download/{file_id}")
146
- def download_result(file_id: str, x_firebase_appcheck: str = Header(None)):
 
 
 
 
147
  verify_app_check_token(x_firebase_appcheck)
 
 
148
 
149
  filename = f"{file_id}.jpg"
150
  path = os.path.join(RESULTS_DIR, filename)
151
 
152
  if not os.path.exists(path):
 
 
 
 
 
 
 
153
  raise HTTPException(status_code=404, detail="Result not found")
154
 
 
 
 
 
 
 
 
 
155
  return FileResponse(path, media_type="image/jpeg")
156
 
157
  # -------------------------------------------------
158
  # 🌐 Public Result File
159
  # -------------------------------------------------
160
  @app.get("/results/{filename}")
161
- def get_result(filename: str):
 
 
162
  path = os.path.join(RESULTS_DIR, filename)
163
  if not os.path.exists(path):
 
 
 
 
 
 
 
164
  raise HTTPException(status_code=404, detail="Result not found")
 
 
 
 
 
 
 
 
 
165
  return FileResponse(path, media_type="image/jpeg")
166
 
167
  # -------------------------------------------------
168
  # 🌐 Public Uploaded File
169
  # -------------------------------------------------
170
  @app.get("/uploads/{filename}")
171
- def get_upload(filename: str):
 
 
172
  path = os.path.join(UPLOAD_DIR, filename)
173
  if not os.path.exists(path):
 
 
 
 
 
 
 
174
  raise HTTPException(status_code=404, detail="File not found")
 
 
 
 
 
 
 
 
 
175
  return FileResponse(path, media_type="image/jpeg")
 
1
+ from fastapi import FastAPI, File, UploadFile, HTTPException, Header, Request
2
  from fastapi.responses import FileResponse
3
  from huggingface_hub import hf_hub_download
4
  import uuid
 
8
  from PIL import Image
9
  import torch
10
  from torchvision import transforms
11
+ from app.database import get_database, log_api_call, log_image_upload, log_colorization, close_connection
12
 
13
  # -------------------------------------------------
14
  # 🚀 FastAPI App
 
69
  output_img = transforms.ToPILImage()(tensor.squeeze())
70
  return output_img
71
 
72
+ # -------------------------------------------------
73
+ # 🗄️ MongoDB Initialization
74
+ # -------------------------------------------------
75
+ @app.on_event("startup")
76
+ async def startup_event():
77
+ """Initialize MongoDB on startup"""
78
+ try:
79
+ db = get_database()
80
+ if db:
81
+ print("✅ MongoDB initialized successfully!")
82
+ except Exception as e:
83
+ print(f"⚠️ MongoDB initialization failed: {e}")
84
+
85
+ @app.on_event("shutdown")
86
+ async def shutdown_event():
87
+ """Cleanup on shutdown"""
88
+ close_connection()
89
+ print("Application shutdown")
90
+
91
  # -------------------------------------------------
92
  # 🩺 Health Check
93
  # -------------------------------------------------
94
  @app.get("/health")
95
+ def health_check(request: Request):
96
+ response = {"status": "healthy", "model_loaded": True}
97
+
98
+ # Log API call
99
+ log_api_call(
100
+ endpoint="/health",
101
+ method="GET",
102
+ status_code=200,
103
+ response_data=response,
104
+ ip_address=request.client.host if request.client else None
105
+ )
106
+
107
+ return response
108
 
109
  # -------------------------------------------------
110
  # 🔐 Firebase Token Validator
 
119
  # -------------------------------------------------
120
  @app.post("/upload")
121
  async def upload_image(
122
+ request: Request,
123
  file: UploadFile = File(...),
124
  x_firebase_appcheck: str = Header(None)
125
  ):
126
  verify_app_check_token(x_firebase_appcheck)
127
+
128
+ ip_address = request.client.host if request.client else None
129
 
130
  if not file.content_type.startswith("image/"):
131
+ log_api_call(
132
+ endpoint="/upload",
133
+ method="POST",
134
+ status_code=400,
135
+ error="Invalid file type",
136
+ ip_address=ip_address
137
+ )
138
  raise HTTPException(status_code=400, detail="Invalid file type")
139
 
140
  image_id = f"{uuid.uuid4()}.jpg"
141
  file_path = os.path.join(UPLOAD_DIR, image_id)
142
 
143
+ img_bytes = await file.read()
144
+ file_size = len(img_bytes)
145
+
146
  with open(file_path, "wb") as f:
147
+ f.write(img_bytes)
148
 
149
  base_url = "https://logicgoinfotechspaces-text-guided-image-colorization.hf.space"
150
 
151
+ response_data = {
152
  "success": True,
153
  "image_id": image_id.replace(".jpg", ""),
154
  "file_url": f"{base_url}/uploads/{image_id}"
155
  }
156
+
157
+ # Log to MongoDB
158
+ log_image_upload(
159
+ image_id=image_id.replace(".jpg", ""),
160
+ filename=file.filename or image_id,
161
+ file_size=file_size,
162
+ content_type=file.content_type or "image/jpeg",
163
+ ip_address=ip_address
164
+ )
165
+
166
+ log_api_call(
167
+ endpoint="/upload",
168
+ method="POST",
169
+ status_code=200,
170
+ request_data={"filename": file.filename, "content_type": file.content_type},
171
+ response_data=response_data,
172
+ ip_address=ip_address
173
+ )
174
+
175
+ return response_data
176
 
177
  # -------------------------------------------------
178
  # 🎨 Colorize Image
179
  # -------------------------------------------------
180
  @app.post("/colorize")
181
  async def colorize(
182
+ request: Request,
183
  file: UploadFile = File(...),
184
  x_firebase_appcheck: str = Header(None)
185
  ):
186
+ import time
187
+ start_time = time.time()
188
+
189
  verify_app_check_token(x_firebase_appcheck)
190
+
191
+ ip_address = request.client.host if request.client else None
192
 
193
  if not file.content_type.startswith("image/"):
194
+ log_api_call(
195
+ endpoint="/colorize",
196
+ method="POST",
197
+ status_code=400,
198
+ error="Invalid file type",
199
+ ip_address=ip_address
200
+ )
201
  raise HTTPException(status_code=400, detail="Invalid file type")
202
 
203
  img = Image.open(io.BytesIO(await file.read()))
204
  output_img = colorize_image(img)
205
 
206
+ processing_time = time.time() - start_time
207
+
208
  result_id = f"{uuid.uuid4()}.jpg"
209
  output_path = os.path.join(RESULTS_DIR, result_id)
210
  output_img.save(output_path)
211
 
212
  base_url = "https://logicgoinfotechspaces-text-guided-image-colorization.hf.space"
213
 
214
+ result_id_clean = result_id.replace(".jpg", "")
215
+
216
+ response_data = {
217
  "success": True,
218
+ "result_id": result_id_clean,
219
  "download_url": f"{base_url}/results/{result_id}",
220
+ "api_download": f"{base_url}/download/{result_id_clean}"
221
  }
222
+
223
+ # Log to MongoDB
224
+ log_colorization(
225
+ result_id=result_id_clean,
226
+ model_type="gan",
227
+ processing_time=processing_time,
228
+ ip_address=ip_address
229
+ )
230
+
231
+ log_api_call(
232
+ endpoint="/colorize",
233
+ method="POST",
234
+ status_code=200,
235
+ request_data={"filename": file.filename, "content_type": file.content_type},
236
+ response_data=response_data,
237
+ ip_address=ip_address
238
+ )
239
+
240
+ return response_data
241
 
242
  # -------------------------------------------------
243
  # ⬇️ Download via API (Secure)
244
  # -------------------------------------------------
245
  @app.get("/download/{file_id}")
246
+ def download_result(
247
+ request: Request,
248
+ file_id: str,
249
+ x_firebase_appcheck: str = Header(None)
250
+ ):
251
  verify_app_check_token(x_firebase_appcheck)
252
+
253
+ ip_address = request.client.host if request.client else None
254
 
255
  filename = f"{file_id}.jpg"
256
  path = os.path.join(RESULTS_DIR, filename)
257
 
258
  if not os.path.exists(path):
259
+ log_api_call(
260
+ endpoint=f"/download/{file_id}",
261
+ method="GET",
262
+ status_code=404,
263
+ error="Result not found",
264
+ ip_address=ip_address
265
+ )
266
  raise HTTPException(status_code=404, detail="Result not found")
267
 
268
+ log_api_call(
269
+ endpoint=f"/download/{file_id}",
270
+ method="GET",
271
+ status_code=200,
272
+ request_data={"file_id": file_id},
273
+ ip_address=ip_address
274
+ )
275
+
276
  return FileResponse(path, media_type="image/jpeg")
277
 
278
  # -------------------------------------------------
279
  # 🌐 Public Result File
280
  # -------------------------------------------------
281
  @app.get("/results/{filename}")
282
+ def get_result(request: Request, filename: str):
283
+ ip_address = request.client.host if request.client else None
284
+
285
  path = os.path.join(RESULTS_DIR, filename)
286
  if not os.path.exists(path):
287
+ log_api_call(
288
+ endpoint=f"/results/{filename}",
289
+ method="GET",
290
+ status_code=404,
291
+ error="Result not found",
292
+ ip_address=ip_address
293
+ )
294
  raise HTTPException(status_code=404, detail="Result not found")
295
+
296
+ log_api_call(
297
+ endpoint=f"/results/{filename}",
298
+ method="GET",
299
+ status_code=200,
300
+ request_data={"filename": filename},
301
+ ip_address=ip_address
302
+ )
303
+
304
  return FileResponse(path, media_type="image/jpeg")
305
 
306
  # -------------------------------------------------
307
  # 🌐 Public Uploaded File
308
  # -------------------------------------------------
309
  @app.get("/uploads/{filename}")
310
+ def get_upload(request: Request, filename: str):
311
+ ip_address = request.client.host if request.client else None
312
+
313
  path = os.path.join(UPLOAD_DIR, filename)
314
  if not os.path.exists(path):
315
+ log_api_call(
316
+ endpoint=f"/uploads/{filename}",
317
+ method="GET",
318
+ status_code=404,
319
+ error="File not found",
320
+ ip_address=ip_address
321
+ )
322
  raise HTTPException(status_code=404, detail="File not found")
323
+
324
+ log_api_call(
325
+ endpoint=f"/uploads/{filename}",
326
+ method="GET",
327
+ status_code=200,
328
+ request_data={"filename": filename},
329
+ ip_address=ip_address
330
+ )
331
+
332
  return FileResponse(path, media_type="image/jpeg")
app/main_fastai.py CHANGED
@@ -37,6 +37,7 @@ from huggingface_hub import from_pretrained_fastai
37
 
38
  from app.config import settings
39
  from app.pytorch_colorizer import PyTorchColorizer
 
40
 
41
  # Configure logging
42
  logging.basicConfig(
@@ -103,8 +104,17 @@ model_type: str = "none" # "fastai", "pytorch", or "none"
103
 
104
  @app.on_event("startup")
105
  async def startup_event():
106
- """Load FastAI or PyTorch model on startup"""
107
  global learn, pytorch_colorizer, model_load_error, model_type
 
 
 
 
 
 
 
 
 
108
  model_id = os.getenv("MODEL_ID", "Hammad712/GAN-Colorization-Model")
109
 
110
  # Try FastAI first
@@ -141,6 +151,7 @@ async def shutdown_event():
141
  del learn
142
  if pytorch_colorizer:
143
  del pytorch_colorizer
 
144
  logger.info("Application shutdown")
145
 
146
  def _extract_bearer_token(authorization_header: str | None) -> str | None:
@@ -190,18 +201,34 @@ async def verify_request(request: Request):
190
  return True
191
 
192
  @app.get("/api")
193
- async def api_info():
194
  """API info endpoint"""
195
- return {
196
  "app": "FastAI Image Colorizer API",
197
  "version": "1.0.0",
198
  "health": "/health",
199
  "colorize": "/colorize",
200
  "gradio": "/"
201
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
 
203
  @app.get("/health")
204
- async def health_check():
205
  """Health check endpoint"""
206
  model_loaded = (learn is not None) or (pytorch_colorizer is not None)
207
  response = {
@@ -218,6 +245,16 @@ async def health_check():
218
  response["message"] = "No model loaded. Using fallback colorization method."
219
  else:
220
  response["message"] = f"Model loaded successfully ({model_type})"
 
 
 
 
 
 
 
 
 
 
221
  return response
222
 
223
  def simple_colorize_fallback(image: Image.Image) -> Image.Image:
@@ -318,6 +355,7 @@ def colorize_pil(image: Image.Image) -> Image.Image:
318
 
319
  @app.post("/colorize")
320
  async def colorize_api(
 
321
  file: UploadFile = File(...),
322
  verified: bool = Depends(verify_request)
323
  ):
@@ -325,11 +363,28 @@ async def colorize_api(
325
  Upload a black & white image -> returns colorized image.
326
  Requires Firebase authentication unless DISABLE_AUTH=true
327
  """
 
 
 
 
 
 
 
 
 
328
  # Allow fallback colorization even if model isn't loaded
329
  # if learn is None and pytorch_colorizer is None:
330
  # raise HTTPException(status_code=503, detail="Colorization model not loaded")
331
 
332
  if not file.content_type or not file.content_type.startswith("image/"):
 
 
 
 
 
 
 
 
333
  raise HTTPException(status_code=400, detail="File must be an image")
334
 
335
  try:
@@ -339,12 +394,35 @@ async def colorize_api(
339
  logger.info("Colorizing image...")
340
  colorized = colorize_pil(image)
341
 
 
 
342
  output_filename = f"{uuid.uuid4()}.png"
343
  output_path = RESULT_DIR / output_filename
344
  colorized.save(output_path, "PNG")
345
 
346
  logger.info("Colorized image saved: %s", output_filename)
347
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348
  # Return the image file
349
  return FileResponse(
350
  output_path,
@@ -352,8 +430,17 @@ async def colorize_api(
352
  filename=f"colorized_{output_filename}"
353
  )
354
  except Exception as e:
355
- logger.error("Error colorizing image: %s", str(e))
356
- raise HTTPException(status_code=500, detail=f"Error colorizing image: {str(e)}")
 
 
 
 
 
 
 
 
 
357
 
358
  # ==========================================================
359
  # Gradio Interface (for Space UI)
 
37
 
38
  from app.config import settings
39
  from app.pytorch_colorizer import PyTorchColorizer
40
+ from app.database import get_database, log_api_call, log_image_upload, log_colorization, close_connection
41
 
42
  # Configure logging
43
  logging.basicConfig(
 
104
 
105
  @app.on_event("startup")
106
  async def startup_event():
107
+ """Load FastAI or PyTorch model on startup and initialize MongoDB"""
108
  global learn, pytorch_colorizer, model_load_error, model_type
109
+
110
+ # Initialize MongoDB
111
+ try:
112
+ db = get_database()
113
+ if db:
114
+ logger.info("✅ MongoDB initialized successfully!")
115
+ except Exception as e:
116
+ logger.warning("⚠️ MongoDB initialization failed: %s", str(e))
117
+
118
  model_id = os.getenv("MODEL_ID", "Hammad712/GAN-Colorization-Model")
119
 
120
  # Try FastAI first
 
151
  del learn
152
  if pytorch_colorizer:
153
  del pytorch_colorizer
154
+ close_connection()
155
  logger.info("Application shutdown")
156
 
157
  def _extract_bearer_token(authorization_header: str | None) -> str | None:
 
201
  return True
202
 
203
  @app.get("/api")
204
+ async def api_info(request: Request):
205
  """API info endpoint"""
206
+ response_data = {
207
  "app": "FastAI Image Colorizer API",
208
  "version": "1.0.0",
209
  "health": "/health",
210
  "colorize": "/colorize",
211
  "gradio": "/"
212
  }
213
+
214
+ # Log API call
215
+ user_id = None
216
+ if hasattr(request, 'state') and hasattr(request.state, 'user'):
217
+ user_id = request.state.user.get("uid")
218
+
219
+ log_api_call(
220
+ endpoint="/api",
221
+ method="GET",
222
+ status_code=200,
223
+ response_data=response_data,
224
+ user_id=user_id,
225
+ ip_address=request.client.host if request.client else None
226
+ )
227
+
228
+ return response_data
229
 
230
  @app.get("/health")
231
+ async def health_check(request: Request):
232
  """Health check endpoint"""
233
  model_loaded = (learn is not None) or (pytorch_colorizer is not None)
234
  response = {
 
245
  response["message"] = "No model loaded. Using fallback colorization method."
246
  else:
247
  response["message"] = f"Model loaded successfully ({model_type})"
248
+
249
+ # Log API call
250
+ log_api_call(
251
+ endpoint="/health",
252
+ method="GET",
253
+ status_code=200,
254
+ response_data=response,
255
+ ip_address=request.client.host if request.client else None
256
+ )
257
+
258
  return response
259
 
260
  def simple_colorize_fallback(image: Image.Image) -> Image.Image:
 
355
 
356
  @app.post("/colorize")
357
  async def colorize_api(
358
+ request: Request,
359
  file: UploadFile = File(...),
360
  verified: bool = Depends(verify_request)
361
  ):
 
363
  Upload a black & white image -> returns colorized image.
364
  Requires Firebase authentication unless DISABLE_AUTH=true
365
  """
366
+ import time
367
+ start_time = time.time()
368
+
369
+ user_id = None
370
+ if hasattr(request, 'state') and hasattr(request.state, 'user'):
371
+ user_id = request.state.user.get("uid")
372
+
373
+ ip_address = request.client.host if request.client else None
374
+
375
  # Allow fallback colorization even if model isn't loaded
376
  # if learn is None and pytorch_colorizer is None:
377
  # raise HTTPException(status_code=503, detail="Colorization model not loaded")
378
 
379
  if not file.content_type or not file.content_type.startswith("image/"):
380
+ log_api_call(
381
+ endpoint="/colorize",
382
+ method="POST",
383
+ status_code=400,
384
+ error="File must be an image",
385
+ user_id=user_id,
386
+ ip_address=ip_address
387
+ )
388
  raise HTTPException(status_code=400, detail="File must be an image")
389
 
390
  try:
 
394
  logger.info("Colorizing image...")
395
  colorized = colorize_pil(image)
396
 
397
+ processing_time = time.time() - start_time
398
+
399
  output_filename = f"{uuid.uuid4()}.png"
400
  output_path = RESULT_DIR / output_filename
401
  colorized.save(output_path, "PNG")
402
 
403
  logger.info("Colorized image saved: %s", output_filename)
404
 
405
+ result_id = output_filename.replace(".png", "")
406
+
407
+ # Log to MongoDB
408
+ log_colorization(
409
+ result_id=result_id,
410
+ model_type=model_type,
411
+ processing_time=processing_time,
412
+ user_id=user_id,
413
+ ip_address=ip_address
414
+ )
415
+
416
+ log_api_call(
417
+ endpoint="/colorize",
418
+ method="POST",
419
+ status_code=200,
420
+ request_data={"filename": file.filename, "content_type": file.content_type},
421
+ response_data={"result_id": result_id, "filename": output_filename},
422
+ user_id=user_id,
423
+ ip_address=ip_address
424
+ )
425
+
426
  # Return the image file
427
  return FileResponse(
428
  output_path,
 
430
  filename=f"colorized_{output_filename}"
431
  )
432
  except Exception as e:
433
+ error_msg = str(e)
434
+ logger.error("Error colorizing image: %s", error_msg)
435
+ log_api_call(
436
+ endpoint="/colorize",
437
+ method="POST",
438
+ status_code=500,
439
+ error=error_msg,
440
+ user_id=user_id,
441
+ ip_address=ip_address
442
+ )
443
+ raise HTTPException(status_code=500, detail=f"Error colorizing image: {error_msg}")
444
 
445
  # ==========================================================
446
  # Gradio Interface (for Space UI)
app/main_sdxl.py CHANGED
@@ -25,6 +25,7 @@ from pydantic import BaseModel, EmailStr
25
  from huggingface_hub import InferenceClient
26
 
27
  from app.config import settings
 
28
 
29
  # Configure logging
30
  logging.basicConfig(
@@ -181,9 +182,17 @@ def remove_unlikely_words(prompt: str) -> str:
181
 
182
  @app.on_event("startup")
183
  async def startup_event():
184
- """Initialize Hugging Face Inference API client"""
185
  global inference_client, model_load_error
186
 
 
 
 
 
 
 
 
 
187
  try:
188
  logger.info("🔄 Initializing Hugging Face Inference API client...")
189
 
@@ -214,6 +223,7 @@ async def shutdown_event():
214
  global inference_client
215
  if inference_client:
216
  inference_client = None
 
217
  logger.info("Application shutdown")
218
 
219
 
@@ -481,9 +491,9 @@ async def refresh_token(refresh_token: str = Body(..., embed=True)):
481
  # ========== API Endpoints ==========
482
 
483
  @app.get("/api")
484
- async def api_info():
485
  """API info endpoint"""
486
- return {
487
  "app": "Text-Guided Image Colorization API",
488
  "version": "1.0.0",
489
  "endpoints": {
@@ -502,10 +512,26 @@ async def api_info():
502
  "gradio": "/"
503
  }
504
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
505
 
506
 
507
  @app.get("/health")
508
- async def health_check():
509
  """Health check endpoint"""
510
  response = {
511
  "status": "healthy",
@@ -515,6 +541,16 @@ async def health_check():
515
  }
516
  if model_load_error:
517
  response["model_error"] = model_load_error
 
 
 
 
 
 
 
 
 
 
518
  return response
519
 
520
 
@@ -588,6 +624,7 @@ def colorize_image_sdxl(
588
 
589
  @app.post("/upload")
590
  async def upload_image(
 
591
  file: UploadFile = File(...),
592
  verified: bool = Depends(verify_request)
593
  ):
@@ -595,7 +632,21 @@ async def upload_image(
595
  Upload an image and get the uploaded image URL.
596
  Requires Firebase App Check authentication.
597
  """
 
 
 
 
 
 
598
  if not file.content_type or not file.content_type.startswith("image/"):
 
 
 
 
 
 
 
 
599
  raise HTTPException(status_code=400, detail="File must be an image")
600
 
601
  try:
@@ -606,6 +657,7 @@ async def upload_image(
606
 
607
  # Save uploaded file
608
  img_bytes = await file.read()
 
609
  with open(file_path, "wb") as f:
610
  f.write(img_bytes)
611
 
@@ -617,19 +669,51 @@ async def upload_image(
617
  # Try to get from request
618
  base_url = "https://logicgoinfotechspaces-text-guided-image-colorization.hf.space"
619
 
620
- return JSONResponse({
621
  "success": True,
622
  "image_id": image_id.replace(f".{file_extension}", ""),
623
  "image_url": f"{base_url}/uploads/{image_id}",
624
  "filename": image_id
625
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
626
  except Exception as e:
627
- logger.error("Error uploading image: %s", str(e))
628
- raise HTTPException(status_code=500, detail=f"Error uploading image: {str(e)}")
 
 
 
 
 
 
 
 
 
629
 
630
 
631
  @app.post("/colorize")
632
  async def colorize_api(
 
633
  file: UploadFile = File(...),
634
  positive_prompt: Optional[str] = None,
635
  negative_prompt: Optional[str] = None,
@@ -641,10 +725,35 @@ async def colorize_api(
641
  Upload a grayscale image -> returns colorized image.
642
  Uses SDXL + ControlNet with automatic captioning.
643
  """
 
 
 
 
 
 
 
 
 
644
  if inference_client is None:
 
 
 
 
 
 
 
 
645
  raise HTTPException(status_code=503, detail="Inference API client not initialized")
646
 
647
  if not file.content_type or not file.content_type.startswith("image/"):
 
 
 
 
 
 
 
 
648
  raise HTTPException(status_code=400, detail="File must be an image")
649
 
650
  try:
@@ -660,6 +769,8 @@ async def colorize_api(
660
  num_inference_steps=num_inference_steps
661
  )
662
 
 
 
663
  output_filename = f"{uuid.uuid4()}.png"
664
  output_path = RESULT_DIR / output_filename
665
  colorized.save(output_path, "PNG")
@@ -673,37 +784,119 @@ async def colorize_api(
673
 
674
  result_id = output_filename.replace(".png", "")
675
 
676
- return JSONResponse({
677
  "success": True,
678
  "result_id": result_id,
679
  "download_url": f"{base_url}/results/{output_filename}",
680
  "api_download_url": f"{base_url}/download/{result_id}",
681
  "filename": output_filename,
682
  "caption": caption
683
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
684
  except Exception as e:
685
- logger.error("Error colorizing image: %s", str(e))
686
- raise HTTPException(status_code=500, detail=f"Error colorizing image: {str(e)}")
 
 
 
 
 
 
 
 
 
687
 
688
 
689
  @app.get("/download/{file_id}")
690
- def download_result(file_id: str, verified: bool = Depends(verify_request)):
 
 
 
 
691
  """Download colorized image by file ID"""
 
 
 
 
 
 
692
  filename = f"{file_id}.png"
693
  path = RESULT_DIR / filename
694
 
695
  if not path.exists():
 
 
 
 
 
 
 
 
696
  raise HTTPException(status_code=404, detail="Result not found")
697
 
 
 
 
 
 
 
 
 
 
698
  return FileResponse(path, media_type="image/png")
699
 
700
 
701
  @app.get("/results/{filename}")
702
- def get_result(filename: str):
703
  """Public endpoint to access colorized images"""
 
 
704
  path = RESULT_DIR / filename
705
  if not path.exists():
 
 
 
 
 
 
 
706
  raise HTTPException(status_code=404, detail="Result not found")
 
 
 
 
 
 
 
 
 
707
  return FileResponse(path, media_type="image/png")
708
 
709
 
 
25
  from huggingface_hub import InferenceClient
26
 
27
  from app.config import settings
28
+ from app.database import get_database, log_api_call, log_image_upload, log_colorization, close_connection
29
 
30
  # Configure logging
31
  logging.basicConfig(
 
182
 
183
  @app.on_event("startup")
184
  async def startup_event():
185
+ """Initialize Hugging Face Inference API client and MongoDB"""
186
  global inference_client, model_load_error
187
 
188
+ # Initialize MongoDB
189
+ try:
190
+ db = get_database()
191
+ if db:
192
+ logger.info("✅ MongoDB initialized successfully!")
193
+ except Exception as e:
194
+ logger.warning("⚠️ MongoDB initialization failed: %s", str(e))
195
+
196
  try:
197
  logger.info("🔄 Initializing Hugging Face Inference API client...")
198
 
 
223
  global inference_client
224
  if inference_client:
225
  inference_client = None
226
+ close_connection()
227
  logger.info("Application shutdown")
228
 
229
 
 
491
  # ========== API Endpoints ==========
492
 
493
  @app.get("/api")
494
+ async def api_info(request: Request):
495
  """API info endpoint"""
496
+ response_data = {
497
  "app": "Text-Guided Image Colorization API",
498
  "version": "1.0.0",
499
  "endpoints": {
 
512
  "gradio": "/"
513
  }
514
  }
515
+
516
+ # Log API call
517
+ user_id = None
518
+ if hasattr(request, 'state') and hasattr(request.state, 'user'):
519
+ user_id = request.state.user.get("uid")
520
+
521
+ log_api_call(
522
+ endpoint="/api",
523
+ method="GET",
524
+ status_code=200,
525
+ response_data=response_data,
526
+ user_id=user_id,
527
+ ip_address=request.client.host if request.client else None
528
+ )
529
+
530
+ return response_data
531
 
532
 
533
  @app.get("/health")
534
+ async def health_check(request: Request):
535
  """Health check endpoint"""
536
  response = {
537
  "status": "healthy",
 
541
  }
542
  if model_load_error:
543
  response["model_error"] = model_load_error
544
+
545
+ # Log API call
546
+ log_api_call(
547
+ endpoint="/health",
548
+ method="GET",
549
+ status_code=200,
550
+ response_data=response,
551
+ ip_address=request.client.host if request.client else None
552
+ )
553
+
554
  return response
555
 
556
 
 
624
 
625
  @app.post("/upload")
626
  async def upload_image(
627
+ request: Request,
628
  file: UploadFile = File(...),
629
  verified: bool = Depends(verify_request)
630
  ):
 
632
  Upload an image and get the uploaded image URL.
633
  Requires Firebase App Check authentication.
634
  """
635
+ user_id = None
636
+ if hasattr(request, 'state') and hasattr(request.state, 'user'):
637
+ user_id = request.state.user.get("uid")
638
+
639
+ ip_address = request.client.host if request.client else None
640
+
641
  if not file.content_type or not file.content_type.startswith("image/"):
642
+ log_api_call(
643
+ endpoint="/upload",
644
+ method="POST",
645
+ status_code=400,
646
+ error="File must be an image",
647
+ user_id=user_id,
648
+ ip_address=ip_address
649
+ )
650
  raise HTTPException(status_code=400, detail="File must be an image")
651
 
652
  try:
 
657
 
658
  # Save uploaded file
659
  img_bytes = await file.read()
660
+ file_size = len(img_bytes)
661
  with open(file_path, "wb") as f:
662
  f.write(img_bytes)
663
 
 
669
  # Try to get from request
670
  base_url = "https://logicgoinfotechspaces-text-guided-image-colorization.hf.space"
671
 
672
+ response_data = {
673
  "success": True,
674
  "image_id": image_id.replace(f".{file_extension}", ""),
675
  "image_url": f"{base_url}/uploads/{image_id}",
676
  "filename": image_id
677
+ }
678
+
679
+ # Log to MongoDB
680
+ log_image_upload(
681
+ image_id=image_id.replace(f".{file_extension}", ""),
682
+ filename=file.filename or image_id,
683
+ file_size=file_size,
684
+ content_type=file.content_type or "image/jpeg",
685
+ user_id=user_id,
686
+ ip_address=ip_address
687
+ )
688
+
689
+ log_api_call(
690
+ endpoint="/upload",
691
+ method="POST",
692
+ status_code=200,
693
+ request_data={"filename": file.filename, "content_type": file.content_type},
694
+ response_data=response_data,
695
+ user_id=user_id,
696
+ ip_address=ip_address
697
+ )
698
+
699
+ return JSONResponse(response_data)
700
  except Exception as e:
701
+ error_msg = str(e)
702
+ logger.error("Error uploading image: %s", error_msg)
703
+ log_api_call(
704
+ endpoint="/upload",
705
+ method="POST",
706
+ status_code=500,
707
+ error=error_msg,
708
+ user_id=user_id,
709
+ ip_address=ip_address
710
+ )
711
+ raise HTTPException(status_code=500, detail=f"Error uploading image: {error_msg}")
712
 
713
 
714
  @app.post("/colorize")
715
  async def colorize_api(
716
+ request: Request,
717
  file: UploadFile = File(...),
718
  positive_prompt: Optional[str] = None,
719
  negative_prompt: Optional[str] = None,
 
725
  Upload a grayscale image -> returns colorized image.
726
  Uses SDXL + ControlNet with automatic captioning.
727
  """
728
+ import time
729
+ start_time = time.time()
730
+
731
+ user_id = None
732
+ if hasattr(request, 'state') and hasattr(request.state, 'user'):
733
+ user_id = request.state.user.get("uid")
734
+
735
+ ip_address = request.client.host if request.client else None
736
+
737
  if inference_client is None:
738
+ log_api_call(
739
+ endpoint="/colorize",
740
+ method="POST",
741
+ status_code=503,
742
+ error="Inference API client not initialized",
743
+ user_id=user_id,
744
+ ip_address=ip_address
745
+ )
746
  raise HTTPException(status_code=503, detail="Inference API client not initialized")
747
 
748
  if not file.content_type or not file.content_type.startswith("image/"):
749
+ log_api_call(
750
+ endpoint="/colorize",
751
+ method="POST",
752
+ status_code=400,
753
+ error="File must be an image",
754
+ user_id=user_id,
755
+ ip_address=ip_address
756
+ )
757
  raise HTTPException(status_code=400, detail="File must be an image")
758
 
759
  try:
 
769
  num_inference_steps=num_inference_steps
770
  )
771
 
772
+ processing_time = time.time() - start_time
773
+
774
  output_filename = f"{uuid.uuid4()}.png"
775
  output_path = RESULT_DIR / output_filename
776
  colorized.save(output_path, "PNG")
 
784
 
785
  result_id = output_filename.replace(".png", "")
786
 
787
+ response_data = {
788
  "success": True,
789
  "result_id": result_id,
790
  "download_url": f"{base_url}/results/{output_filename}",
791
  "api_download_url": f"{base_url}/download/{result_id}",
792
  "filename": output_filename,
793
  "caption": caption
794
+ }
795
+
796
+ # Log to MongoDB
797
+ log_colorization(
798
+ result_id=result_id,
799
+ prompt=positive_prompt,
800
+ model_type="sdxl",
801
+ processing_time=processing_time,
802
+ user_id=user_id,
803
+ ip_address=ip_address
804
+ )
805
+
806
+ log_api_call(
807
+ endpoint="/colorize",
808
+ method="POST",
809
+ status_code=200,
810
+ request_data={
811
+ "filename": file.filename,
812
+ "positive_prompt": positive_prompt,
813
+ "negative_prompt": negative_prompt,
814
+ "seed": seed,
815
+ "num_inference_steps": num_inference_steps
816
+ },
817
+ response_data=response_data,
818
+ user_id=user_id,
819
+ ip_address=ip_address
820
+ )
821
+
822
+ return JSONResponse(response_data)
823
  except Exception as e:
824
+ error_msg = str(e)
825
+ logger.error("Error colorizing image: %s", error_msg)
826
+ log_api_call(
827
+ endpoint="/colorize",
828
+ method="POST",
829
+ status_code=500,
830
+ error=error_msg,
831
+ user_id=user_id,
832
+ ip_address=ip_address
833
+ )
834
+ raise HTTPException(status_code=500, detail=f"Error colorizing image: {error_msg}")
835
 
836
 
837
  @app.get("/download/{file_id}")
838
+ def download_result(
839
+ request: Request,
840
+ file_id: str,
841
+ verified: bool = Depends(verify_request)
842
+ ):
843
  """Download colorized image by file ID"""
844
+ user_id = None
845
+ if hasattr(request, 'state') and hasattr(request.state, 'user'):
846
+ user_id = request.state.user.get("uid")
847
+
848
+ ip_address = request.client.host if request.client else None
849
+
850
  filename = f"{file_id}.png"
851
  path = RESULT_DIR / filename
852
 
853
  if not path.exists():
854
+ log_api_call(
855
+ endpoint=f"/download/{file_id}",
856
+ method="GET",
857
+ status_code=404,
858
+ error="Result not found",
859
+ user_id=user_id,
860
+ ip_address=ip_address
861
+ )
862
  raise HTTPException(status_code=404, detail="Result not found")
863
 
864
+ log_api_call(
865
+ endpoint=f"/download/{file_id}",
866
+ method="GET",
867
+ status_code=200,
868
+ request_data={"file_id": file_id},
869
+ user_id=user_id,
870
+ ip_address=ip_address
871
+ )
872
+
873
  return FileResponse(path, media_type="image/png")
874
 
875
 
876
  @app.get("/results/{filename}")
877
+ def get_result(request: Request, filename: str):
878
  """Public endpoint to access colorized images"""
879
+ ip_address = request.client.host if request.client else None
880
+
881
  path = RESULT_DIR / filename
882
  if not path.exists():
883
+ log_api_call(
884
+ endpoint=f"/results/{filename}",
885
+ method="GET",
886
+ status_code=404,
887
+ error="Result not found",
888
+ ip_address=ip_address
889
+ )
890
  raise HTTPException(status_code=404, detail="Result not found")
891
+
892
+ log_api_call(
893
+ endpoint=f"/results/{filename}",
894
+ method="GET",
895
+ status_code=200,
896
+ request_data={"filename": filename},
897
+ ip_address=ip_address
898
+ )
899
+
900
  return FileResponse(path, media_type="image/png")
901
 
902
 
requirements.txt CHANGED
@@ -16,4 +16,5 @@ diffusers
16
  safetensors
17
  ftfy
18
  httpx
19
- email-validator
 
 
16
  safetensors
17
  ftfy
18
  httpx
19
+ email-validator
20
+ pymongo