LogicGoInfotechSpaces commited on
Commit
7cf424d
·
1 Parent(s): 2e72deb

Add logging status endpoint and improved admin Mongo initialization logging

Browse files
Files changed (1) hide show
  1. api/main.py +155 -1
api/main.py CHANGED
@@ -2,17 +2,28 @@
2
  import os
3
  import uuid
4
  import shutil
 
5
  from datetime import datetime
6
  from typing import Dict, List, Optional
7
 
8
  import numpy as np
9
- from fastapi import FastAPI, UploadFile, File, HTTPException, Depends, Header, Request
 
 
 
 
 
 
 
 
 
10
  from fastapi.responses import FileResponse, JSONResponse
11
  from pydantic import BaseModel
12
  from PIL import Image
13
  import cv2
14
  import logging
15
 
 
16
  from pymongo import MongoClient
17
  import time
18
 
@@ -47,6 +58,54 @@ mongo_client = MongoClient(MONGO_URI)
47
  mongo_db = mongo_client["object_remover"]
48
  mongo_logs = mongo_db["api_logs"]
49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  def bearer_auth(authorization: Optional[str] = Header(default=None)) -> None:
51
  if not ENV_TOKEN:
52
  return
@@ -62,12 +121,92 @@ class InpaintRequest(BaseModel):
62
  mask_id: str
63
  invert_mask: bool = True # True => selected/painted area is removed
64
  passthrough: bool = False # If True, return the original image unchanged
 
 
65
 
66
 
67
  class SimpleRemoveRequest(BaseModel):
68
  image_id: str # Image with pink/magenta segments to remove
69
 
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  @app.get("/")
72
  def root() -> Dict[str, object]:
73
  return {
@@ -93,6 +232,12 @@ def health() -> Dict[str, str]:
93
  return {"status": "healthy"}
94
 
95
 
 
 
 
 
 
 
96
  @app.post("/upload-image")
97
  def upload_image(image: UploadFile = File(...), _: None = Depends(bearer_auth)) -> Dict[str, str]:
98
  ext = os.path.splitext(image.filename)[1] or ".png"
@@ -223,6 +368,7 @@ def inpaint(req: InpaintRequest, _: None = Depends(bearer_auth)) -> Dict[str, st
223
  output_path, "PNG", optimize=False, compress_level=1
224
  )
225
 
 
226
  return {"result": output_name}
227
 
228
  except Exception as e:
@@ -301,6 +447,7 @@ def inpaint_url(req: InpaintRequest, request: Request, _: None = Depends(bearer_
301
 
302
  url = str(request.url_for("download_file", filename=result_name))
303
  logs.append({"result": result_name, "url": url, "timestamp": datetime.utcnow().isoformat()})
 
304
  return {"result": result_name, "url": url}
305
 
306
 
@@ -312,6 +459,8 @@ def inpaint_multipart(
312
  invert_mask: bool = True,
313
  mask_is_painted: bool = False, # if True, mask file is the painted-on image (e.g., black strokes on original)
314
  passthrough: bool = False,
 
 
315
  _: None = Depends(bearer_auth),
316
  ) -> Dict[str, str]:
317
  # Load in-memory
@@ -339,6 +488,7 @@ def inpaint_multipart(
339
  resp: Dict[str, str] = {"result": result_name}
340
  if url:
341
  resp["url"] = url
 
342
  return resp
343
 
344
  if mask_is_painted:
@@ -434,6 +584,7 @@ def inpaint_multipart(
434
  resp: Dict[str, str] = {"result": result_name}
435
  if url:
436
  resp["url"] = url
 
437
  return resp
438
 
439
 
@@ -441,6 +592,8 @@ def inpaint_multipart(
441
  def remove_pink_segments(
442
  image: UploadFile = File(...),
443
  request: Request = None,
 
 
444
  _: None = Depends(bearer_auth),
445
  ) -> Dict[str, str]:
446
  """
@@ -545,6 +698,7 @@ def remove_pink_segments(
545
  resp: Dict[str, str] = {"result": result_name, "pink_segments_detected": str(nonzero)}
546
  if url:
547
  resp["url"] = url
 
548
  return resp
549
 
550
 
 
2
  import os
3
  import uuid
4
  import shutil
5
+ import re
6
  from datetime import datetime
7
  from typing import Dict, List, Optional
8
 
9
  import numpy as np
10
+ from fastapi import (
11
+ FastAPI,
12
+ UploadFile,
13
+ File,
14
+ HTTPException,
15
+ Depends,
16
+ Header,
17
+ Request,
18
+ Form,
19
+ )
20
  from fastapi.responses import FileResponse, JSONResponse
21
  from pydantic import BaseModel
22
  from PIL import Image
23
  import cv2
24
  import logging
25
 
26
+ from bson import ObjectId
27
  from pymongo import MongoClient
28
  import time
29
 
 
58
  mongo_db = mongo_client["object_remover"]
59
  mongo_logs = mongo_db["api_logs"]
60
 
61
+ ADMIN_MONGO_URI = os.environ.get("MONGODB_ADMIN")
62
+ DEFAULT_CATEGORY_ID = "69368f722e46bd68ae188984"
63
+ admin_media_clicks = None
64
+
65
+
66
+ def _init_admin_mongo() -> None:
67
+ global admin_media_clicks
68
+ if not ADMIN_MONGO_URI:
69
+ log.info("Admin Mongo URI not provided; media click logging disabled")
70
+ return
71
+ try:
72
+ admin_client = MongoClient(ADMIN_MONGO_URI)
73
+ try:
74
+ admin_db = admin_client.get_default_database() or admin_client["admin"]
75
+ except Exception:
76
+ admin_db = admin_client["admin"]
77
+ admin_media_clicks = admin_db["media_clicks"]
78
+ try:
79
+ admin_media_clicks.drop_index("user_id_1_header_1_media_id_1")
80
+ log.info("Dropped legacy index user_id_1_header_1_media_id_1")
81
+ except Exception as idx_err:
82
+ log.info("Skipping legacy index drop: %s", idx_err)
83
+ log.info(
84
+ "Admin media click logging enabled on db=%s collection=%s",
85
+ admin_media_clicks.database.name,
86
+ admin_media_clicks.name,
87
+ )
88
+ except Exception as err:
89
+ log.error("Failed to init admin Mongo client: %s", err)
90
+ admin_media_clicks = None
91
+
92
+
93
+ _init_admin_mongo()
94
+
95
+
96
+ def _admin_logging_status() -> Dict[str, object]:
97
+ if admin_media_clicks is None:
98
+ return {
99
+ "enabled": False,
100
+ "db": None,
101
+ "collection": None,
102
+ }
103
+ return {
104
+ "enabled": True,
105
+ "db": admin_media_clicks.database.name,
106
+ "collection": admin_media_clicks.name,
107
+ }
108
+
109
  def bearer_auth(authorization: Optional[str] = Header(default=None)) -> None:
110
  if not ENV_TOKEN:
111
  return
 
121
  mask_id: str
122
  invert_mask: bool = True # True => selected/painted area is removed
123
  passthrough: bool = False # If True, return the original image unchanged
124
+ user_id: Optional[str] = None
125
+ category_id: Optional[str] = None
126
 
127
 
128
  class SimpleRemoveRequest(BaseModel):
129
  image_id: str # Image with pink/magenta segments to remove
130
 
131
 
132
+ def _coerce_object_id(value: Optional[str]) -> ObjectId:
133
+ if value is None:
134
+ return ObjectId()
135
+ value_str = str(value).strip()
136
+ if re.fullmatch(r"[0-9a-fA-F]{24}", value_str):
137
+ return ObjectId(value_str)
138
+ if value_str.isdigit():
139
+ hex_str = format(int(value_str), "x")
140
+ if len(hex_str) > 24:
141
+ hex_str = hex_str[-24:]
142
+ hex_str = hex_str.rjust(24, "0")
143
+ return ObjectId(hex_str)
144
+ return ObjectId()
145
+
146
+
147
+ def _coerce_category_id(category_id: Optional[str]) -> ObjectId:
148
+ raw = category_id or DEFAULT_CATEGORY_ID
149
+ raw_str = str(raw).strip()
150
+ if re.fullmatch(r"[0-9a-fA-F]{24}", raw_str):
151
+ return ObjectId(raw_str)
152
+ return _coerce_object_id(raw_str)
153
+
154
+
155
+ def log_media_click(user_id: Optional[str], category_id: Optional[str]) -> None:
156
+ if admin_media_clicks is None:
157
+ return
158
+ try:
159
+ user_obj = _coerce_object_id(user_id)
160
+ category_obj = _coerce_category_id(category_id)
161
+ now = datetime.utcnow()
162
+
163
+ doc = admin_media_clicks.find_one({"userId": user_obj})
164
+ if doc:
165
+ categories = doc.get("categories") or []
166
+ if any(cat.get("categoryId") == category_obj for cat in categories):
167
+ admin_media_clicks.update_one(
168
+ {"_id": doc["_id"], "categories.categoryId": category_obj},
169
+ {
170
+ "$inc": {"categories.$.click_count": 1},
171
+ "$set": {
172
+ "categories.$.lastClickedAt": now,
173
+ "updatedAt": now,
174
+ },
175
+ },
176
+ )
177
+ else:
178
+ admin_media_clicks.update_one(
179
+ {"_id": doc["_id"]},
180
+ {
181
+ "$push": {
182
+ "categories": {
183
+ "categoryId": category_obj,
184
+ "click_count": 1,
185
+ "lastClickedAt": now,
186
+ }
187
+ },
188
+ "$set": {"updatedAt": now},
189
+ },
190
+ )
191
+ else:
192
+ admin_media_clicks.insert_one(
193
+ {
194
+ "userId": user_obj,
195
+ "categories": [
196
+ {
197
+ "categoryId": category_obj,
198
+ "click_count": 1,
199
+ "lastClickedAt": now,
200
+ }
201
+ ],
202
+ "createdAt": now,
203
+ "updatedAt": now,
204
+ }
205
+ )
206
+ except Exception as err:
207
+ log.warning("Admin media click logging failed: %s", err)
208
+
209
+
210
  @app.get("/")
211
  def root() -> Dict[str, object]:
212
  return {
 
232
  return {"status": "healthy"}
233
 
234
 
235
+ @app.get("/logging-status")
236
+ def logging_status(_: None = Depends(bearer_auth)) -> Dict[str, object]:
237
+ """Helper endpoint to verify admin media logging wiring (no secrets exposed)."""
238
+ return _admin_logging_status()
239
+
240
+
241
  @app.post("/upload-image")
242
  def upload_image(image: UploadFile = File(...), _: None = Depends(bearer_auth)) -> Dict[str, str]:
243
  ext = os.path.splitext(image.filename)[1] or ".png"
 
368
  output_path, "PNG", optimize=False, compress_level=1
369
  )
370
 
371
+ log_media_click(req.user_id, req.category_id)
372
  return {"result": output_name}
373
 
374
  except Exception as e:
 
447
 
448
  url = str(request.url_for("download_file", filename=result_name))
449
  logs.append({"result": result_name, "url": url, "timestamp": datetime.utcnow().isoformat()})
450
+ log_media_click(req.user_id, req.category_id)
451
  return {"result": result_name, "url": url}
452
 
453
 
 
459
  invert_mask: bool = True,
460
  mask_is_painted: bool = False, # if True, mask file is the painted-on image (e.g., black strokes on original)
461
  passthrough: bool = False,
462
+ user_id: Optional[str] = Form(None),
463
+ category_id: Optional[str] = Form(None),
464
  _: None = Depends(bearer_auth),
465
  ) -> Dict[str, str]:
466
  # Load in-memory
 
488
  resp: Dict[str, str] = {"result": result_name}
489
  if url:
490
  resp["url"] = url
491
+ log_media_click(user_id, category_id)
492
  return resp
493
 
494
  if mask_is_painted:
 
584
  resp: Dict[str, str] = {"result": result_name}
585
  if url:
586
  resp["url"] = url
587
+ log_media_click(user_id, category_id)
588
  return resp
589
 
590
 
 
592
  def remove_pink_segments(
593
  image: UploadFile = File(...),
594
  request: Request = None,
595
+ user_id: Optional[str] = Form(None),
596
+ category_id: Optional[str] = Form(None),
597
  _: None = Depends(bearer_auth),
598
  ) -> Dict[str, str]:
599
  """
 
698
  resp: Dict[str, str] = {"result": result_name, "pink_segments_detected": str(nonzero)}
699
  if url:
700
  resp["url"] = url
701
+ log_media_click(user_id, category_id)
702
  return resp
703
 
704