jebin2 commited on
Commit
651af0e
·
1 Parent(s): 1e6f8bb

Add IP address, full referrer URL, and improved decryption fallback

Browse files
Files changed (5) hide show
  1. .gitignore +1 -1
  2. app.py +15 -2
  3. encryption.py +34 -15
  4. models.py +1 -0
  5. templates/index.html +14 -7
.gitignore CHANGED
@@ -45,4 +45,4 @@ build/
45
  *.egg-info/
46
 
47
  # Private keys (keep in repo but be careful)
48
- # PRIVATE_KEY.pem
 
45
  *.egg-info/
46
 
47
  # Private keys (keep in repo but be careful)
48
+ PRIVATE_KEY.pem
app.py CHANGED
@@ -122,6 +122,7 @@ async def get_data(
122
  "id": item.id,
123
  "user_id": item.user_id,
124
  "refer_url": item.refer_url,
 
125
  "json_data": item.json_data,
126
  "created_at": item.created_at.isoformat() if item.created_at else None
127
  }
@@ -188,8 +189,18 @@ async def blink(
188
  # Store with error information
189
  decrypted_results = [{"error": str(e), "raw_encrypted": encrypted_data[:100]}]
190
 
191
- # Get referer URL from headers
192
- refer_url = request.headers.get("referer") or request.headers.get("origin")
 
 
 
 
 
 
 
 
 
 
193
 
194
  # Store each decrypted result as separate records
195
  records_created = 0
@@ -197,6 +208,7 @@ async def blink(
197
  blink_record = BlinkData(
198
  user_id=user_id,
199
  refer_url=refer_url,
 
200
  json_data=json_data
201
  )
202
  db.add(blink_record)
@@ -207,6 +219,7 @@ async def blink(
207
  blink_record = BlinkData(
208
  user_id=user_id,
209
  refer_url=refer_url,
 
210
  json_data={"encrypted_length": len(encrypted_data)}
211
  )
212
  db.add(blink_record)
 
122
  "id": item.id,
123
  "user_id": item.user_id,
124
  "refer_url": item.refer_url,
125
+ "ip_address": item.ip_address,
126
  "json_data": item.json_data,
127
  "created_at": item.created_at.isoformat() if item.created_at else None
128
  }
 
189
  # Store with error information
190
  decrypted_results = [{"error": str(e), "raw_encrypted": encrypted_data[:100]}]
191
 
192
+ # Get referer URL from headers (full URL, not just origin)
193
+ refer_url = request.headers.get("referer")
194
+
195
+ # Get client IP address
196
+ # Check X-Forwarded-For header first (for proxies/load balancers)
197
+ forwarded_for = request.headers.get("x-forwarded-for")
198
+ if forwarded_for:
199
+ # X-Forwarded-For can contain multiple IPs, take the first one
200
+ ip_address = forwarded_for.split(",")[0].strip()
201
+ else:
202
+ # Fall back to direct client IP
203
+ ip_address = request.client.host if request.client else None
204
 
205
  # Store each decrypted result as separate records
206
  records_created = 0
 
208
  blink_record = BlinkData(
209
  user_id=user_id,
210
  refer_url=refer_url,
211
+ ip_address=ip_address,
212
  json_data=json_data
213
  )
214
  db.add(blink_record)
 
219
  blink_record = BlinkData(
220
  user_id=user_id,
221
  refer_url=refer_url,
222
+ ip_address=ip_address,
223
  json_data={"encrypted_length": len(encrypted_data)}
224
  )
225
  db.add(blink_record)
encryption.py CHANGED
@@ -21,21 +21,36 @@ _private_key = None
21
 
22
  def load_private_key():
23
  """
24
- Load the RSA private key from the PEM file.
25
  Caches the key for subsequent calls.
26
 
 
 
 
 
27
  Returns:
28
- RSA private key object
29
-
30
- Raises:
31
- FileNotFoundError: If private key file doesn't exist
32
- ValueError: If private key is invalid
33
  """
34
  global _private_key
35
 
36
  if _private_key is not None:
37
  return _private_key
38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  try:
40
  with open(PRIVATE_KEY_PATH, "rb") as key_file:
41
  _private_key = serialization.load_pem_private_key(
@@ -46,11 +61,12 @@ def load_private_key():
46
  logger.info(f"Successfully loaded private key from {PRIVATE_KEY_PATH}")
47
  return _private_key
48
  except FileNotFoundError:
49
- logger.error(f"Private key file not found: {PRIVATE_KEY_PATH}")
50
- raise
51
  except Exception as e:
52
- logger.error(f"Failed to load private key: {e}")
53
- raise ValueError(f"Invalid private key: {e}")
 
 
54
 
55
 
56
  def decrypt_data(encrypted_base64: str) -> Optional[Any]:
@@ -61,15 +77,17 @@ def decrypt_data(encrypted_base64: str) -> Optional[Any]:
61
  encrypted_base64: Base64 URL-safe encoded encrypted string
62
 
63
  Returns:
64
- Decrypted data parsed as JSON, or raw string if not valid JSON
65
-
66
- Raises:
67
- ValueError: If decryption fails
68
  """
69
  try:
70
  # Load the private key
71
  private_key = load_private_key()
72
 
 
 
 
 
 
73
  # Decode base64 URL-safe encoded data
74
  # Add padding if necessary
75
  padded = encrypted_base64 + '=' * (4 - len(encrypted_base64) % 4)
@@ -98,7 +116,8 @@ def decrypt_data(encrypted_base64: str) -> Optional[Any]:
98
 
99
  except Exception as e:
100
  logger.error(f"Decryption failed: {e}")
101
- raise ValueError(f"Failed to decrypt data: {e}")
 
102
 
103
 
104
  def decrypt_multiple_blocks(encrypted_data: str, block_size: int = 344) -> list[Any]:
 
21
 
22
  def load_private_key():
23
  """
24
+ Load the RSA private key from environment variable or PEM file.
25
  Caches the key for subsequent calls.
26
 
27
+ Priority:
28
+ 1. PRIVATE_KEY environment variable (PEM content)
29
+ 2. PRIVATE_KEY_PATH file
30
+
31
  Returns:
32
+ RSA private key object, or None if not available
 
 
 
 
33
  """
34
  global _private_key
35
 
36
  if _private_key is not None:
37
  return _private_key
38
 
39
+ # Try loading from environment variable first
40
+ private_key_pem = os.getenv("PRIVATE_KEY")
41
+ if private_key_pem:
42
+ try:
43
+ _private_key = serialization.load_pem_private_key(
44
+ private_key_pem.encode(),
45
+ password=None,
46
+ backend=default_backend()
47
+ )
48
+ logger.info("Successfully loaded private key from PRIVATE_KEY env variable")
49
+ return _private_key
50
+ except Exception as e:
51
+ logger.warning(f"Failed to load private key from env: {e}")
52
+
53
+ # Try loading from file
54
  try:
55
  with open(PRIVATE_KEY_PATH, "rb") as key_file:
56
  _private_key = serialization.load_pem_private_key(
 
61
  logger.info(f"Successfully loaded private key from {PRIVATE_KEY_PATH}")
62
  return _private_key
63
  except FileNotFoundError:
64
+ logger.warning(f"Private key file not found: {PRIVATE_KEY_PATH}")
 
65
  except Exception as e:
66
+ logger.warning(f"Failed to load private key from file: {e}")
67
+
68
+ logger.warning("No private key available - encrypted data will not be decrypted")
69
+ return None
70
 
71
 
72
  def decrypt_data(encrypted_base64: str) -> Optional[Any]:
 
77
  encrypted_base64: Base64 URL-safe encoded encrypted string
78
 
79
  Returns:
80
+ Decrypted data parsed as JSON, encrypted data if no key available, or None on error
 
 
 
81
  """
82
  try:
83
  # Load the private key
84
  private_key = load_private_key()
85
 
86
+ # If no private key, return the encrypted data as-is
87
+ if private_key is None:
88
+ logger.warning("No private key - returning encrypted data")
89
+ return {"encrypted_data": encrypted_base64, "decryption_status": "no_key_available"}
90
+
91
  # Decode base64 URL-safe encoded data
92
  # Add padding if necessary
93
  padded = encrypted_base64 + '=' * (4 - len(encrypted_base64) % 4)
 
116
 
117
  except Exception as e:
118
  logger.error(f"Decryption failed: {e}")
119
+ # Return encrypted data on failure
120
+ return {"encrypted_data": encrypted_base64, "decryption_error": str(e)}
121
 
122
 
123
  def decrypt_multiple_blocks(encrypted_data: str, block_size: int = 344) -> list[Any]:
models.py CHANGED
@@ -22,6 +22,7 @@ class BlinkData(Base):
22
  id = Column(Integer, primary_key=True, autoincrement=True, index=True)
23
  user_id = Column(String(20), index=True, nullable=False)
24
  refer_url = Column(Text, nullable=True)
 
25
  json_data = Column(JSON, nullable=True)
26
  created_at = Column(DateTime(timezone=True), server_default=func.now())
27
 
 
22
  id = Column(Integer, primary_key=True, autoincrement=True, index=True)
23
  user_id = Column(String(20), index=True, nullable=False)
24
  refer_url = Column(Text, nullable=True)
25
+ ip_address = Column(String(45), nullable=True) # IPv6 can be up to 45 chars
26
  json_data = Column(JSON, nullable=True)
27
  created_at = Column(DateTime(timezone=True), server_default=func.now())
28
 
templates/index.html CHANGED
@@ -1,5 +1,6 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -186,7 +187,9 @@
186
  }
187
 
188
  @keyframes spin {
189
- to { transform: rotate(360deg); }
 
 
190
  }
191
 
192
  .empty-state {
@@ -203,6 +206,7 @@
203
  }
204
  </style>
205
  </head>
 
206
  <body>
207
  <div class="container">
208
  <header>
@@ -228,13 +232,14 @@
228
  <th>ID</th>
229
  <th>User ID</th>
230
  <th>Refer URL</th>
 
231
  <th>JSON Data</th>
232
  <th>Created At</th>
233
  </tr>
234
  </thead>
235
  <tbody id="tableBody">
236
  <tr>
237
- <td colspan="5">
238
  <div class="loading">
239
  <div class="spinner"></div>
240
  Loading data...
@@ -269,11 +274,11 @@
269
 
270
  function renderTable(items) {
271
  const tbody = document.getElementById('tableBody');
272
-
273
  if (items.length === 0) {
274
  tbody.innerHTML = `
275
  <tr>
276
- <td colspan="5">
277
  <div class="empty-state">
278
  <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
279
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4" />
@@ -291,6 +296,7 @@
291
  <td>${item.id}</td>
292
  <td class="user-id">${item.user_id}</td>
293
  <td class="refer-url" title="${item.refer_url || ''}">${item.refer_url || '-'}</td>
 
294
  <td><pre class="json-data">${JSON.stringify(item.json_data, null, 2)}</pre></td>
295
  <td class="timestamp">${new Date(item.created_at).toLocaleString()}</td>
296
  </tr>
@@ -309,10 +315,10 @@
309
  currentPage = page;
310
  const data = await fetchData(page);
311
  totalRecords = data.total;
312
-
313
  document.getElementById('totalRecords').textContent = data.total.toLocaleString();
314
  document.getElementById('uniqueUsers').textContent = data.unique_users.toLocaleString();
315
-
316
  renderTable(data.items);
317
  updatePagination();
318
  }
@@ -334,4 +340,5 @@
334
  loadPage(1);
335
  </script>
336
  </body>
337
- </html>
 
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
+
4
  <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
187
  }
188
 
189
  @keyframes spin {
190
+ to {
191
+ transform: rotate(360deg);
192
+ }
193
  }
194
 
195
  .empty-state {
 
206
  }
207
  </style>
208
  </head>
209
+
210
  <body>
211
  <div class="container">
212
  <header>
 
232
  <th>ID</th>
233
  <th>User ID</th>
234
  <th>Refer URL</th>
235
+ <th>IP Address</th>
236
  <th>JSON Data</th>
237
  <th>Created At</th>
238
  </tr>
239
  </thead>
240
  <tbody id="tableBody">
241
  <tr>
242
+ <td colspan="6">
243
  <div class="loading">
244
  <div class="spinner"></div>
245
  Loading data...
 
274
 
275
  function renderTable(items) {
276
  const tbody = document.getElementById('tableBody');
277
+
278
  if (items.length === 0) {
279
  tbody.innerHTML = `
280
  <tr>
281
+ <td colspan="6">
282
  <div class="empty-state">
283
  <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
284
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4" />
 
296
  <td>${item.id}</td>
297
  <td class="user-id">${item.user_id}</td>
298
  <td class="refer-url" title="${item.refer_url || ''}">${item.refer_url || '-'}</td>
299
+ <td class="ip-address">${item.ip_address || '-'}</td>
300
  <td><pre class="json-data">${JSON.stringify(item.json_data, null, 2)}</pre></td>
301
  <td class="timestamp">${new Date(item.created_at).toLocaleString()}</td>
302
  </tr>
 
315
  currentPage = page;
316
  const data = await fetchData(page);
317
  totalRecords = data.total;
318
+
319
  document.getElementById('totalRecords').textContent = data.total.toLocaleString();
320
  document.getElementById('uniqueUsers').textContent = data.unique_users.toLocaleString();
321
+
322
  renderTable(data.items);
323
  updatePagination();
324
  }
 
340
  loadPage(1);
341
  </script>
342
  </body>
343
+
344
+ </html>