andito HF Staff commited on
Commit
62b5801
·
verified ·
1 Parent(s): 1550f58

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +125 -7
app.py CHANGED
@@ -6,6 +6,7 @@ import threading
6
  import requests
7
  from datetime import datetime, timezone
8
  from typing import Dict, Optional, Tuple
 
9
 
10
  import gradio as gr
11
  from huggingface_hub import HfApi
@@ -18,6 +19,7 @@ HF_TOKEN = os.environ["HF_TOKEN"] # Space secret
18
  OPENAI_API_KEY = os.environ["OPENAI_API_KEY"] # Space secret
19
 
20
  SENT_PATH = "sent.csv"
 
21
 
22
  # Maximum keys per order = quantity * KEYS_PER_QUANTITY
23
  KEYS_PER_QUANTITY = 10
@@ -36,6 +38,10 @@ _cache = {
36
  }
37
  CACHE_TTL_SEC = 10.0
38
 
 
 
 
 
39
  # -------------------------
40
  # Helpers
41
  # -------------------------
@@ -115,6 +121,100 @@ def load_state(force: bool = False):
115
  _cache["sent"] = sent
116
  return sent
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  # -------------------------
119
  # Core API
120
  # -------------------------
@@ -127,11 +227,20 @@ def claim_c_key(
127
  and hasn't exceeded the allowed key limit (quantity * KEYS_PER_QUANTITY).
128
  Returns (key, status_message)
129
  """
130
- _ = request
 
 
 
 
 
131
 
132
  order_number = order_number.strip()
133
 
 
 
 
134
  if not order_number:
 
135
  return "", "Please provide an order number."
136
 
137
  with _lock:
@@ -139,7 +248,9 @@ def claim_c_key(
139
 
140
  # Check if order exists in sent.csv
141
  if order_number not in sent:
142
- return "", f"Order number {order_number} not found."
 
 
143
 
144
  quantity = sent[order_number]["quantity"]
145
 
@@ -151,7 +262,9 @@ def claim_c_key(
151
 
152
  # Check if limit has been reached
153
  if keys_already_sent >= max_keys:
154
- return "", f"Key limit reached for order {order_number} ({keys_already_sent}/{max_keys} keys sent)."
 
 
155
 
156
  # Check if we can reuse a recently created key
157
  last_key_sent = sent[order_number].get("last_key_sent", "")
@@ -166,7 +279,9 @@ def claim_c_key(
166
 
167
  # If the last key was created within the reuse window, reuse it
168
  if time_since_creation < KEY_REUSE_WINDOW_SECONDS:
169
- return last_key_sent, f"Reused recent key. ({keys_already_sent}/{max_keys} keys sent for this order)"
 
 
170
  except (ValueError, TypeError):
171
  # If there's an error parsing the timestamp, create a new key
172
  pass
@@ -197,7 +312,9 @@ def claim_c_key(
197
  ephemeral_data = response.json()
198
  ephemeral_key = ephemeral_data["value"]
199
  except Exception as e:
200
- return "", f"Failed to create ephemeral key: {str(e)}"
 
 
201
 
202
  # Update sent.csv - store the ephemeral key and timestamp
203
  existing_keys = sent[order_number]["keys_sent"]
@@ -223,7 +340,9 @@ def claim_c_key(
223
  _cache["ts"] = 0.0
224
 
225
  keys_sent_count = sent[order_number]["quantity_keys_sent"]
226
- return ephemeral_key, f"New ephemeral key sent successfully. ({keys_sent_count}/{max_keys} keys sent for this order)"
 
 
227
 
228
  # -------------------------
229
  # UI
@@ -245,4 +364,3 @@ with gr.Blocks(title="API") as demo:
245
 
246
  demo.queue()
247
  demo.launch()
248
-
 
6
  import requests
7
  from datetime import datetime, timezone
8
  from typing import Dict, Optional, Tuple
9
+ from collections import defaultdict
10
 
11
  import gradio as gr
12
  from huggingface_hub import HfApi
 
19
  OPENAI_API_KEY = os.environ["OPENAI_API_KEY"] # Space secret
20
 
21
  SENT_PATH = "sent.csv"
22
+ REQUEST_LOG_PATH = "request_log.csv"
23
 
24
  # Maximum keys per order = quantity * KEYS_PER_QUANTITY
25
  KEYS_PER_QUANTITY = 10
 
38
  }
39
  CACHE_TTL_SEC = 10.0
40
 
41
+ # Request tracking to identify suspicious patterns
42
+ _request_tracker = defaultdict(list) # {ip: [timestamps]}
43
+ _request_log_buffer = [] # Buffer logs before writing to avoid too many uploads
44
+
45
  # -------------------------
46
  # Helpers
47
  # -------------------------
 
121
  _cache["sent"] = sent
122
  return sent
123
 
124
+ def _log_request(
125
+ order_number: str,
126
+ ip_address: str,
127
+ user_agent: str,
128
+ success: bool,
129
+ message: str,
130
+ suspicious_flags: list,
131
+ ):
132
+ """Log request details for security monitoring"""
133
+ timestamp = _utc_now_iso()
134
+
135
+ log_entry = {
136
+ "timestamp": timestamp,
137
+ "ip_address": ip_address,
138
+ "user_agent": user_agent,
139
+ "order_number": order_number,
140
+ "success": success,
141
+ "message": message,
142
+ "suspicious_flags": "|".join(suspicious_flags) if suspicious_flags else "",
143
+ }
144
+
145
+ _request_log_buffer.append(log_entry)
146
+
147
+ # Flush buffer to Hub every 10 requests or if suspicious activity detected
148
+ if len(_request_log_buffer) >= 10 or suspicious_flags:
149
+ _flush_request_log()
150
+
151
+ def _flush_request_log():
152
+ """Write buffered logs to the dataset repo"""
153
+ if not _request_log_buffer:
154
+ return
155
+
156
+ try:
157
+ # Download existing log
158
+ try:
159
+ log_csv = _download_csv(DATASET_REPO_ID, REQUEST_LOG_PATH)
160
+ except Exception:
161
+ # If log doesn't exist yet, create header
162
+ log_csv = "timestamp,ip_address,user_agent,order_number,success,message,suspicious_flags\n"
163
+
164
+ # Append new entries
165
+ out = io.StringIO()
166
+ out.write(log_csv)
167
+
168
+ fieldnames = ["timestamp", "ip_address", "user_agent", "order_number", "success", "message", "suspicious_flags"]
169
+ writer = csv.DictWriter(out, fieldnames=fieldnames)
170
+
171
+ for entry in _request_log_buffer:
172
+ writer.writerow(entry)
173
+
174
+ # Upload
175
+ _upload_csv(
176
+ DATASET_REPO_ID,
177
+ REQUEST_LOG_PATH,
178
+ out.getvalue(),
179
+ commit_message=f"Request log update at {_utc_now_iso()}",
180
+ )
181
+
182
+ _request_log_buffer.clear()
183
+ except Exception as e:
184
+ print(f"Warning: Failed to flush request log: {e}")
185
+
186
+ def _check_suspicious_activity(ip_address: str, order_number: str) -> list:
187
+ """Check for suspicious patterns and return list of flags"""
188
+ flags = []
189
+ now = time.time()
190
+
191
+ # Track requests from this IP
192
+ _request_tracker[ip_address].append(now)
193
+
194
+ # Clean old entries (older than 1 hour)
195
+ _request_tracker[ip_address] = [
196
+ ts for ts in _request_tracker[ip_address]
197
+ if now - ts < 3600
198
+ ]
199
+
200
+ recent_requests = _request_tracker[ip_address]
201
+
202
+ # Flag: More than 20 requests in the last hour
203
+ if len(recent_requests) > 20:
204
+ flags.append("HIGH_FREQUENCY")
205
+
206
+ # Flag: More than 5 requests in the last minute
207
+ last_minute = [ts for ts in recent_requests if now - ts < 60]
208
+ if len(last_minute) > 5:
209
+ flags.append("RAPID_REQUESTS")
210
+
211
+ # Flag: More than 10 requests in the last 5 minutes
212
+ last_5_min = [ts for ts in recent_requests if now - ts < 300]
213
+ if len(last_5_min) > 10:
214
+ flags.append("BURST_PATTERN")
215
+
216
+ return flags
217
+
218
  # -------------------------
219
  # Core API
220
  # -------------------------
 
227
  and hasn't exceeded the allowed key limit (quantity * KEYS_PER_QUANTITY).
228
  Returns (key, status_message)
229
  """
230
+ # Extract request metadata
231
+ ip_address = "unknown"
232
+ user_agent = "unknown"
233
+ if request:
234
+ ip_address = request.client.host if request.client else "unknown"
235
+ user_agent = request.headers.get("user-agent", "unknown")
236
 
237
  order_number = order_number.strip()
238
 
239
+ # Check for suspicious activity patterns
240
+ suspicious_flags = _check_suspicious_activity(ip_address, order_number)
241
+
242
  if not order_number:
243
+ _log_request(order_number, ip_address, user_agent, False, "Empty order number", suspicious_flags)
244
  return "", "Please provide an order number."
245
 
246
  with _lock:
 
248
 
249
  # Check if order exists in sent.csv
250
  if order_number not in sent:
251
+ msg = f"Order number {order_number} not found."
252
+ _log_request(order_number, ip_address, user_agent, False, "Order not found", suspicious_flags)
253
+ return "", msg
254
 
255
  quantity = sent[order_number]["quantity"]
256
 
 
262
 
263
  # Check if limit has been reached
264
  if keys_already_sent >= max_keys:
265
+ msg = f"Key limit reached for order {order_number} ({keys_already_sent}/{max_keys} keys sent)."
266
+ _log_request(order_number, ip_address, user_agent, False, "Key limit reached", suspicious_flags)
267
+ return "", msg
268
 
269
  # Check if we can reuse a recently created key
270
  last_key_sent = sent[order_number].get("last_key_sent", "")
 
279
 
280
  # If the last key was created within the reuse window, reuse it
281
  if time_since_creation < KEY_REUSE_WINDOW_SECONDS:
282
+ msg = f"Reused recent key. ({keys_already_sent}/{max_keys} keys sent for this order)"
283
+ _log_request(order_number, ip_address, user_agent, True, "Key reused", suspicious_flags)
284
+ return last_key_sent, msg
285
  except (ValueError, TypeError):
286
  # If there's an error parsing the timestamp, create a new key
287
  pass
 
312
  ephemeral_data = response.json()
313
  ephemeral_key = ephemeral_data["value"]
314
  except Exception as e:
315
+ msg = f"Failed to create ephemeral key: {str(e)}"
316
+ _log_request(order_number, ip_address, user_agent, False, "API error", suspicious_flags)
317
+ return "", msg
318
 
319
  # Update sent.csv - store the ephemeral key and timestamp
320
  existing_keys = sent[order_number]["keys_sent"]
 
340
  _cache["ts"] = 0.0
341
 
342
  keys_sent_count = sent[order_number]["quantity_keys_sent"]
343
+ msg = f"New ephemeral key sent successfully. ({keys_sent_count}/{max_keys} keys sent for this order)"
344
+ _log_request(order_number, ip_address, user_agent, True, "Key created", suspicious_flags)
345
+ return ephemeral_key, msg
346
 
347
  # -------------------------
348
  # UI
 
364
 
365
  demo.queue()
366
  demo.launch()