webscout 8.3.4__py3-none-any.whl → 8.3.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of webscout might be problematic. Click here for more details.
- webscout/AIutel.py +52 -1016
- webscout/Provider/AISEARCH/__init__.py +11 -10
- webscout/Provider/AISEARCH/felo_search.py +7 -3
- webscout/Provider/AISEARCH/scira_search.py +2 -0
- webscout/Provider/AISEARCH/stellar_search.py +53 -8
- webscout/Provider/Deepinfra.py +7 -1
- webscout/Provider/OPENAI/TogetherAI.py +57 -48
- webscout/Provider/OPENAI/TwoAI.py +94 -1
- webscout/Provider/OPENAI/__init__.py +0 -2
- webscout/Provider/OPENAI/deepinfra.py +6 -0
- webscout/Provider/OPENAI/scirachat.py +4 -0
- webscout/Provider/OPENAI/textpollinations.py +11 -7
- webscout/Provider/OPENAI/venice.py +1 -0
- webscout/Provider/Perplexitylabs.py +163 -147
- webscout/Provider/Qodo.py +30 -6
- webscout/Provider/TTI/__init__.py +1 -0
- webscout/Provider/TTI/together.py +7 -6
- webscout/Provider/TTI/venice.py +368 -0
- webscout/Provider/TextPollinationsAI.py +11 -7
- webscout/Provider/TogetherAI.py +57 -44
- webscout/Provider/TwoAI.py +96 -2
- webscout/Provider/TypliAI.py +33 -27
- webscout/Provider/UNFINISHED/PERPLEXED_search.py +254 -0
- webscout/Provider/UNFINISHED/fetch_together_models.py +6 -11
- webscout/Provider/Venice.py +1 -0
- webscout/Provider/WiseCat.py +18 -20
- webscout/Provider/__init__.py +0 -6
- webscout/Provider/scira_chat.py +4 -0
- webscout/Provider/toolbaz.py +5 -10
- webscout/Provider/typefully.py +1 -11
- webscout/__init__.py +3 -15
- webscout/auth/__init__.py +19 -4
- webscout/auth/api_key_manager.py +189 -189
- webscout/auth/auth_system.py +25 -40
- webscout/auth/config.py +105 -6
- webscout/auth/database.py +377 -22
- webscout/auth/models.py +185 -130
- webscout/auth/request_processing.py +175 -11
- webscout/auth/routes.py +99 -2
- webscout/auth/server.py +9 -2
- webscout/auth/simple_logger.py +236 -0
- webscout/sanitize.py +1074 -0
- webscout/version.py +1 -1
- {webscout-8.3.4.dist-info → webscout-8.3.5.dist-info}/METADATA +9 -149
- {webscout-8.3.4.dist-info → webscout-8.3.5.dist-info}/RECORD +49 -51
- webscout/Provider/OPENAI/README_AUTOPROXY.md +0 -238
- webscout/Provider/OPENAI/typegpt.py +0 -368
- webscout/Provider/OPENAI/uncovrAI.py +0 -477
- webscout/Provider/WritingMate.py +0 -273
- webscout/Provider/typegpt.py +0 -284
- webscout/Provider/uncovr.py +0 -333
- {webscout-8.3.4.dist-info → webscout-8.3.5.dist-info}/WHEEL +0 -0
- {webscout-8.3.4.dist-info → webscout-8.3.5.dist-info}/entry_points.txt +0 -0
- {webscout-8.3.4.dist-info → webscout-8.3.5.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.3.4.dist-info → webscout-8.3.5.dist-info}/top_level.txt +0 -0
webscout/auth/database.py
CHANGED
|
@@ -15,7 +15,13 @@ try:
|
|
|
15
15
|
except ImportError:
|
|
16
16
|
HAS_MOTOR = False
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
try:
|
|
19
|
+
from supabase import create_client, Client #type: ignore
|
|
20
|
+
HAS_SUPABASE = True
|
|
21
|
+
except ImportError:
|
|
22
|
+
HAS_SUPABASE = False
|
|
23
|
+
|
|
24
|
+
from .models import User, APIKey, RateLimitEntry, RequestLog
|
|
19
25
|
|
|
20
26
|
logger = logging.getLogger(__name__)
|
|
21
27
|
|
|
@@ -30,11 +36,12 @@ class JSONDatabase:
|
|
|
30
36
|
self.users_file = self.data_dir / "users.json"
|
|
31
37
|
self.api_keys_file = self.data_dir / "api_keys.json"
|
|
32
38
|
self.rate_limits_file = self.data_dir / "rate_limits.json"
|
|
39
|
+
self.request_logs_file = self.data_dir / "request_logs.json"
|
|
33
40
|
|
|
34
41
|
self._lock = threading.RLock()
|
|
35
42
|
|
|
36
43
|
# Initialize files if they don't exist
|
|
37
|
-
for file_path in [self.users_file, self.api_keys_file, self.rate_limits_file]:
|
|
44
|
+
for file_path in [self.users_file, self.api_keys_file, self.rate_limits_file, self.request_logs_file]:
|
|
38
45
|
if not file_path.exists():
|
|
39
46
|
self._write_json(file_path, [])
|
|
40
47
|
|
|
@@ -171,6 +178,22 @@ class JSONDatabase:
|
|
|
171
178
|
# Only for JSONDatabase
|
|
172
179
|
entries = self._read_json(self.rate_limits_file)
|
|
173
180
|
return [RateLimitEntry.from_dict(e) for e in entries]
|
|
181
|
+
|
|
182
|
+
async def create_request_log(self, request_log: RequestLog) -> RequestLog:
|
|
183
|
+
"""Create a new request log entry."""
|
|
184
|
+
request_logs = self._read_json(self.request_logs_file)
|
|
185
|
+
request_logs.append(request_log.to_dict())
|
|
186
|
+
self._write_json(self.request_logs_file, request_logs)
|
|
187
|
+
return request_log
|
|
188
|
+
|
|
189
|
+
async def get_request_logs(self, limit: int = 100, offset: int = 0) -> List[RequestLog]:
|
|
190
|
+
"""Get request logs with pagination."""
|
|
191
|
+
request_logs = self._read_json(self.request_logs_file)
|
|
192
|
+
# Sort by created_at descending (newest first)
|
|
193
|
+
request_logs.sort(key=lambda x: x.get("created_at", ""), reverse=True)
|
|
194
|
+
# Apply pagination
|
|
195
|
+
paginated_logs = request_logs[offset:offset + limit]
|
|
196
|
+
return [RequestLog.from_dict(log_data) for log_data in paginated_logs]
|
|
174
197
|
|
|
175
198
|
|
|
176
199
|
class MongoDatabase:
|
|
@@ -316,41 +339,338 @@ class MongoDatabase:
|
|
|
316
339
|
async for entry_data in cursor:
|
|
317
340
|
entries.append(RateLimitEntry.from_dict(entry_data))
|
|
318
341
|
return entries
|
|
342
|
+
|
|
343
|
+
async def create_request_log(self, request_log: RequestLog) -> RequestLog:
|
|
344
|
+
"""Create a new request log entry."""
|
|
345
|
+
if not self._connected:
|
|
346
|
+
raise RuntimeError("Database not connected")
|
|
347
|
+
|
|
348
|
+
await self.db.request_logs.insert_one(request_log.to_dict())
|
|
349
|
+
return request_log
|
|
350
|
+
|
|
351
|
+
async def get_request_logs(self, limit: int = 100, offset: int = 0) -> List[RequestLog]:
|
|
352
|
+
"""Get request logs with pagination."""
|
|
353
|
+
if not self._connected:
|
|
354
|
+
raise RuntimeError("Database not connected")
|
|
355
|
+
|
|
356
|
+
cursor = self.db.request_logs.find({}).sort("created_at", -1).skip(offset).limit(limit)
|
|
357
|
+
logs = []
|
|
358
|
+
async for log_data in cursor:
|
|
359
|
+
logs.append(RequestLog.from_dict(log_data))
|
|
360
|
+
return logs
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
class SupabaseDatabase:
|
|
364
|
+
"""Supabase database implementation."""
|
|
365
|
+
|
|
366
|
+
def __init__(self, supabase_url: str, supabase_key: str):
|
|
367
|
+
self.supabase_url = supabase_url
|
|
368
|
+
self.supabase_key = supabase_key
|
|
369
|
+
self.client: Optional[Client] = None
|
|
370
|
+
self._connected = False
|
|
371
|
+
|
|
372
|
+
async def connect(self) -> bool:
|
|
373
|
+
"""Connect to Supabase."""
|
|
374
|
+
if not HAS_SUPABASE:
|
|
375
|
+
logger.warning("supabase package not available, cannot connect to Supabase")
|
|
376
|
+
return False
|
|
377
|
+
|
|
378
|
+
try:
|
|
379
|
+
self.client = create_client(self.supabase_url, self.supabase_key)
|
|
380
|
+
# Test connection by trying to access a table
|
|
381
|
+
self.client.table('users').select('id').limit(1).execute()
|
|
382
|
+
self._connected = True
|
|
383
|
+
logger.info("Connected to Supabase successfully")
|
|
384
|
+
return True
|
|
385
|
+
except Exception as e:
|
|
386
|
+
logger.warning(f"Failed to connect to Supabase: {e}")
|
|
387
|
+
self._connected = False
|
|
388
|
+
return False
|
|
389
|
+
|
|
390
|
+
async def create_user(self, user: User) -> User:
|
|
391
|
+
"""Create a new user."""
|
|
392
|
+
if not self._connected:
|
|
393
|
+
raise RuntimeError("Database not connected")
|
|
394
|
+
|
|
395
|
+
try:
|
|
396
|
+
result = self.client.table('users').insert(user.to_dict()).execute()
|
|
397
|
+
if result.data:
|
|
398
|
+
return user
|
|
399
|
+
else:
|
|
400
|
+
raise ValueError("Failed to create user")
|
|
401
|
+
except Exception as e:
|
|
402
|
+
if "duplicate key" in str(e).lower() or "already exists" in str(e).lower():
|
|
403
|
+
raise ValueError(f"User with username '{user.username}' already exists")
|
|
404
|
+
raise e
|
|
405
|
+
|
|
406
|
+
async def get_user_by_id(self, user_id: str) -> Optional[User]:
|
|
407
|
+
"""Get user by ID."""
|
|
408
|
+
if not self._connected:
|
|
409
|
+
raise RuntimeError("Database not connected")
|
|
410
|
+
|
|
411
|
+
try:
|
|
412
|
+
result = self.client.table('users').select('*').eq('id', user_id).execute()
|
|
413
|
+
if result.data:
|
|
414
|
+
return User.from_dict(result.data[0])
|
|
415
|
+
return None
|
|
416
|
+
except Exception as e:
|
|
417
|
+
logger.error(f"Error getting user by ID: {e}")
|
|
418
|
+
return None
|
|
419
|
+
|
|
420
|
+
async def get_user_by_username(self, username: str) -> Optional[User]:
|
|
421
|
+
"""Get user by username."""
|
|
422
|
+
if not self._connected:
|
|
423
|
+
raise RuntimeError("Database not connected")
|
|
424
|
+
|
|
425
|
+
try:
|
|
426
|
+
result = self.client.table('users').select('*').eq('username', username).execute()
|
|
427
|
+
if result.data:
|
|
428
|
+
return User.from_dict(result.data[0])
|
|
429
|
+
return None
|
|
430
|
+
except Exception as e:
|
|
431
|
+
logger.error(f"Error getting user by username: {e}")
|
|
432
|
+
return None
|
|
433
|
+
|
|
434
|
+
async def get_user_by_telegram_id(self, telegram_id: str) -> Optional[User]:
|
|
435
|
+
"""Get user by Telegram ID."""
|
|
436
|
+
if not self._connected:
|
|
437
|
+
raise RuntimeError("Database not connected")
|
|
438
|
+
|
|
439
|
+
try:
|
|
440
|
+
result = self.client.table('users').select('*').eq('telegram_id', int(telegram_id)).execute()
|
|
441
|
+
if result.data:
|
|
442
|
+
return User.from_dict(result.data[0])
|
|
443
|
+
return None
|
|
444
|
+
except Exception as e:
|
|
445
|
+
logger.error(f"Error getting user by telegram_id: {e}")
|
|
446
|
+
return None
|
|
447
|
+
|
|
448
|
+
async def create_api_key(self, api_key: APIKey) -> APIKey:
|
|
449
|
+
"""Create a new API key."""
|
|
450
|
+
if not self._connected:
|
|
451
|
+
raise RuntimeError("Database not connected")
|
|
452
|
+
|
|
453
|
+
try:
|
|
454
|
+
result = self.client.table('api_keys').insert(api_key.to_dict()).execute()
|
|
455
|
+
if result.data:
|
|
456
|
+
return api_key
|
|
457
|
+
else:
|
|
458
|
+
raise ValueError("Failed to create API key")
|
|
459
|
+
except Exception as e:
|
|
460
|
+
if "duplicate key" in str(e).lower() or "already exists" in str(e).lower():
|
|
461
|
+
raise ValueError("API key already exists")
|
|
462
|
+
raise e
|
|
463
|
+
|
|
464
|
+
async def get_api_key(self, key: str) -> Optional[APIKey]:
|
|
465
|
+
"""Get API key by key value."""
|
|
466
|
+
if not self._connected:
|
|
467
|
+
raise RuntimeError("Database not connected")
|
|
468
|
+
|
|
469
|
+
try:
|
|
470
|
+
result = self.client.table('api_keys').select('*').eq('key', key).execute()
|
|
471
|
+
if result.data:
|
|
472
|
+
return APIKey.from_dict(result.data[0])
|
|
473
|
+
return None
|
|
474
|
+
except Exception as e:
|
|
475
|
+
logger.error(f"Error getting API key: {e}")
|
|
476
|
+
return None
|
|
477
|
+
|
|
478
|
+
async def update_api_key(self, api_key: APIKey) -> APIKey:
|
|
479
|
+
"""Update an existing API key."""
|
|
480
|
+
if not self._connected:
|
|
481
|
+
raise RuntimeError("Database not connected")
|
|
482
|
+
|
|
483
|
+
try:
|
|
484
|
+
result = self.client.table('api_keys').update(api_key.to_dict()).eq('id', api_key.id).execute()
|
|
485
|
+
if result.data:
|
|
486
|
+
return api_key
|
|
487
|
+
else:
|
|
488
|
+
raise ValueError(f"API key with ID '{api_key.id}' not found")
|
|
489
|
+
except Exception as e:
|
|
490
|
+
logger.error(f"Error updating API key: {e}")
|
|
491
|
+
raise e
|
|
492
|
+
|
|
493
|
+
async def get_api_keys_by_user(self, user_id: str) -> List[APIKey]:
|
|
494
|
+
"""Get all API keys for a user."""
|
|
495
|
+
if not self._connected:
|
|
496
|
+
raise RuntimeError("Database not connected")
|
|
497
|
+
|
|
498
|
+
try:
|
|
499
|
+
result = self.client.table('api_keys').select('*').eq('user_id', user_id).execute()
|
|
500
|
+
return [APIKey.from_dict(key_data) for key_data in result.data]
|
|
501
|
+
except Exception as e:
|
|
502
|
+
logger.error(f"Error getting API keys by user: {e}")
|
|
503
|
+
return []
|
|
504
|
+
|
|
505
|
+
async def get_rate_limit_entry(self, api_key_id: str) -> Optional[RateLimitEntry]:
|
|
506
|
+
"""Get rate limit entry for API key."""
|
|
507
|
+
if not self._connected:
|
|
508
|
+
raise RuntimeError("Database not connected")
|
|
509
|
+
|
|
510
|
+
try:
|
|
511
|
+
result = self.client.table('rate_limits').select('*').eq('api_key_id', api_key_id).execute()
|
|
512
|
+
if result.data:
|
|
513
|
+
return RateLimitEntry.from_dict(result.data[0])
|
|
514
|
+
return None
|
|
515
|
+
except Exception as e:
|
|
516
|
+
logger.error(f"Error getting rate limit entry: {e}")
|
|
517
|
+
return None
|
|
518
|
+
|
|
519
|
+
async def update_rate_limit_entry(self, entry: RateLimitEntry) -> RateLimitEntry:
|
|
520
|
+
"""Update rate limit entry."""
|
|
521
|
+
if not self._connected:
|
|
522
|
+
raise RuntimeError("Database not connected")
|
|
523
|
+
|
|
524
|
+
try:
|
|
525
|
+
# Try to update first
|
|
526
|
+
result = self.client.table('rate_limits').update(entry.to_dict()).eq('api_key_id', entry.api_key_id).execute()
|
|
527
|
+
if not result.data:
|
|
528
|
+
# If no rows were updated, insert new entry
|
|
529
|
+
result = self.client.table('rate_limits').insert(entry.to_dict()).execute()
|
|
530
|
+
return entry
|
|
531
|
+
except Exception as e:
|
|
532
|
+
logger.error(f"Error updating rate limit entry: {e}")
|
|
533
|
+
raise e
|
|
534
|
+
|
|
535
|
+
async def get_all_rate_limit_entries(self) -> list:
|
|
536
|
+
"""Return all rate limit entries (for maintenance/cleanup) from Supabase."""
|
|
537
|
+
if not self._connected:
|
|
538
|
+
raise RuntimeError("Database not connected")
|
|
539
|
+
|
|
540
|
+
try:
|
|
541
|
+
result = self.client.table('rate_limits').select('*').execute()
|
|
542
|
+
return [RateLimitEntry.from_dict(entry_data) for entry_data in result.data]
|
|
543
|
+
except Exception as e:
|
|
544
|
+
logger.error(f"Error getting all rate limit entries: {e}")
|
|
545
|
+
return []
|
|
546
|
+
|
|
547
|
+
async def create_request_log(self, request_log: RequestLog) -> RequestLog:
|
|
548
|
+
"""Create a new request log entry."""
|
|
549
|
+
if not self._connected:
|
|
550
|
+
raise RuntimeError("Database not connected")
|
|
551
|
+
|
|
552
|
+
try:
|
|
553
|
+
result = self.client.table('request_logs').insert(request_log.to_dict()).execute()
|
|
554
|
+
if result.data:
|
|
555
|
+
return request_log
|
|
556
|
+
else:
|
|
557
|
+
raise ValueError("Failed to create request log")
|
|
558
|
+
except Exception as e:
|
|
559
|
+
logger.error(f"Error creating request log: {e}")
|
|
560
|
+
raise e
|
|
561
|
+
|
|
562
|
+
async def get_request_logs(self, limit: int = 100, offset: int = 0) -> List[RequestLog]:
|
|
563
|
+
"""Get request logs with pagination."""
|
|
564
|
+
if not self._connected:
|
|
565
|
+
raise RuntimeError("Database not connected")
|
|
566
|
+
|
|
567
|
+
try:
|
|
568
|
+
result = self.client.table('request_logs').select('*').order('created_at', desc=True).range(offset, offset + limit - 1).execute()
|
|
569
|
+
return [RequestLog.from_dict(log_data) for log_data in result.data]
|
|
570
|
+
except Exception as e:
|
|
571
|
+
logger.error(f"Error getting request logs: {e}")
|
|
572
|
+
return []
|
|
319
573
|
|
|
320
574
|
|
|
321
575
|
class DatabaseManager:
|
|
322
|
-
"""Database manager
|
|
576
|
+
"""Database manager with flexible backend support."""
|
|
323
577
|
|
|
324
578
|
def __init__(self, mongo_connection_string: Optional[str] = None, data_dir: str = "data"):
|
|
325
|
-
self.mongo_connection_string = mongo_connection_string
|
|
579
|
+
self.mongo_connection_string = mongo_connection_string
|
|
326
580
|
self.data_dir = data_dir
|
|
581
|
+
self.supabase_url = self._get_supabase_url()
|
|
582
|
+
self.supabase_key = self._get_supabase_key()
|
|
327
583
|
|
|
584
|
+
# Database instances
|
|
585
|
+
self.supabase_db = None
|
|
328
586
|
self.mongo_db = None
|
|
329
|
-
self.json_db =
|
|
330
|
-
self.
|
|
587
|
+
self.json_db = None
|
|
588
|
+
self.active_db = None
|
|
589
|
+
|
|
590
|
+
logger.info("🔗 Database manager initialized with flexible backend support")
|
|
591
|
+
|
|
592
|
+
def _get_supabase_url(self) -> Optional[str]:
|
|
593
|
+
"""Get Supabase URL from environment variables or GitHub secrets."""
|
|
594
|
+
# Try environment variable first
|
|
595
|
+
url = os.getenv("SUPABASE_URL")
|
|
596
|
+
if url:
|
|
597
|
+
return url
|
|
331
598
|
|
|
332
|
-
|
|
599
|
+
# Try to get from GitHub secrets (if running in GitHub Actions)
|
|
600
|
+
github_url = os.getenv("GITHUB_SUPABASE_URL") # GitHub Actions secret
|
|
601
|
+
if github_url:
|
|
602
|
+
return github_url
|
|
603
|
+
|
|
604
|
+
# Don't log error during initialization - only when actually needed
|
|
605
|
+
return None
|
|
606
|
+
|
|
607
|
+
def _get_supabase_key(self) -> Optional[str]:
|
|
608
|
+
"""Get Supabase key from environment variables or GitHub secrets."""
|
|
609
|
+
# Try environment variable first
|
|
610
|
+
key = os.getenv("SUPABASE_ANON_KEY")
|
|
611
|
+
if key:
|
|
612
|
+
return key
|
|
613
|
+
|
|
614
|
+
# Try to get from GitHub secrets (if running in GitHub Actions)
|
|
615
|
+
github_key = os.getenv("GITHUB_SUPABASE_ANON_KEY") # GitHub Actions secret
|
|
616
|
+
if github_key:
|
|
617
|
+
return github_key
|
|
618
|
+
|
|
619
|
+
# Don't log error during initialization - only when actually needed
|
|
620
|
+
return None
|
|
333
621
|
|
|
334
622
|
async def initialize(self) -> None:
|
|
335
|
-
"""Initialize database connection."""
|
|
623
|
+
"""Initialize database connection with fallback support."""
|
|
624
|
+
# Try Supabase first if credentials are available
|
|
625
|
+
if self.supabase_url and self.supabase_key:
|
|
626
|
+
logger.info("🔗 Attempting to connect to Supabase...")
|
|
627
|
+
try:
|
|
628
|
+
self.supabase_db = SupabaseDatabase(self.supabase_url, self.supabase_key)
|
|
629
|
+
connected = await self.supabase_db.connect()
|
|
630
|
+
|
|
631
|
+
if connected:
|
|
632
|
+
self.active_db = self.supabase_db
|
|
633
|
+
logger.info("✅ Successfully connected to Supabase database")
|
|
634
|
+
return
|
|
635
|
+
else:
|
|
636
|
+
logger.warning("⚠️ Failed to connect to Supabase, trying fallbacks...")
|
|
637
|
+
except Exception as e:
|
|
638
|
+
logger.warning(f"⚠️ Supabase connection error: {e}, trying fallbacks...")
|
|
639
|
+
else:
|
|
640
|
+
logger.info("ℹ️ Supabase credentials not found, using fallback databases")
|
|
641
|
+
|
|
642
|
+
# Try MongoDB if connection string is provided
|
|
336
643
|
if self.mongo_connection_string:
|
|
644
|
+
logger.info("🔗 Attempting to connect to MongoDB...")
|
|
337
645
|
try:
|
|
338
646
|
self.mongo_db = MongoDatabase(self.mongo_connection_string)
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
647
|
+
connected = await self.mongo_db.connect()
|
|
648
|
+
|
|
649
|
+
if connected:
|
|
650
|
+
self.active_db = self.mongo_db
|
|
651
|
+
logger.info("✅ Successfully connected to MongoDB database")
|
|
652
|
+
return
|
|
342
653
|
else:
|
|
343
|
-
logger.
|
|
654
|
+
logger.warning("⚠️ Failed to connect to MongoDB, falling back to JSON...")
|
|
344
655
|
except Exception as e:
|
|
345
|
-
logger.warning(f"MongoDB
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
656
|
+
logger.warning(f"⚠️ MongoDB connection error: {e}, falling back to JSON...")
|
|
657
|
+
|
|
658
|
+
# Fall back to JSON database
|
|
659
|
+
logger.info("📁 Using JSON file database as fallback")
|
|
660
|
+
try:
|
|
661
|
+
self.json_db = JSONDatabase(self.data_dir)
|
|
662
|
+
self.active_db = self.json_db
|
|
663
|
+
logger.info("✅ Successfully initialized JSON database")
|
|
664
|
+
except Exception as e:
|
|
665
|
+
logger.error(f"❌ Failed to initialize JSON database: {e}")
|
|
666
|
+
raise RuntimeError(f"Failed to initialize any database backend: {e}")
|
|
349
667
|
|
|
350
668
|
@property
|
|
351
|
-
def db(self)
|
|
669
|
+
def db(self):
|
|
352
670
|
"""Get the active database instance."""
|
|
353
|
-
|
|
671
|
+
if not self.active_db:
|
|
672
|
+
raise RuntimeError("Database not initialized. Call initialize() first.")
|
|
673
|
+
return self.active_db
|
|
354
674
|
|
|
355
675
|
async def create_user(self, user: User) -> User:
|
|
356
676
|
"""Create a new user."""
|
|
@@ -392,9 +712,44 @@ class DatabaseManager:
|
|
|
392
712
|
"""Update rate limit entry."""
|
|
393
713
|
return await self.db.update_rate_limit_entry(entry)
|
|
394
714
|
|
|
715
|
+
async def create_request_log(self, request_log: RequestLog) -> RequestLog:
|
|
716
|
+
"""Create a new request log entry."""
|
|
717
|
+
return await self.db.create_request_log(request_log)
|
|
718
|
+
|
|
719
|
+
async def get_request_logs(self, limit: int = 100, offset: int = 0) -> List[RequestLog]:
|
|
720
|
+
"""Get request logs with pagination."""
|
|
721
|
+
return await self.db.get_request_logs(limit, offset)
|
|
722
|
+
|
|
395
723
|
def get_status(self) -> Dict[str, str]:
|
|
396
724
|
"""Get database status."""
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
725
|
+
if not self.active_db:
|
|
726
|
+
return {
|
|
727
|
+
"type": "None",
|
|
728
|
+
"status": "not_initialized",
|
|
729
|
+
"message": "Database not initialized"
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
if isinstance(self.active_db, SupabaseDatabase):
|
|
733
|
+
return {
|
|
734
|
+
"type": "Supabase",
|
|
735
|
+
"status": "connected" if self.active_db._connected else "disconnected",
|
|
736
|
+
"message": "Using Supabase database"
|
|
737
|
+
}
|
|
738
|
+
elif isinstance(self.active_db, MongoDatabase):
|
|
739
|
+
return {
|
|
740
|
+
"type": "MongoDB",
|
|
741
|
+
"status": "connected" if self.active_db._connected else "disconnected",
|
|
742
|
+
"message": "Using MongoDB database"
|
|
743
|
+
}
|
|
744
|
+
elif isinstance(self.active_db, JSONDatabase):
|
|
745
|
+
return {
|
|
746
|
+
"type": "JSON",
|
|
747
|
+
"status": "connected",
|
|
748
|
+
"message": "Using JSON file database"
|
|
749
|
+
}
|
|
750
|
+
else:
|
|
751
|
+
return {
|
|
752
|
+
"type": "Unknown",
|
|
753
|
+
"status": "unknown",
|
|
754
|
+
"message": "Unknown database type"
|
|
755
|
+
}
|