dan92 commited on
Commit
199c4e9
·
verified ·
1 Parent(s): 504cf28

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +83 -25
  2. requirements.txt +1 -0
  3. templates/monitor.html +112 -0
app.py CHANGED
@@ -11,13 +11,14 @@ from functools import lru_cache, wraps
11
  from typing import Dict, Any, Callable, List, Tuple
12
  import requests
13
  import tiktoken
14
- from flask import Flask, Response, jsonify, request, stream_with_context
15
  from flask_cors import CORS
16
  from requests.adapters import HTTPAdapter
17
  from urllib3.util.connection import create_connection
18
  import urllib3
19
  from cachetools import TTLCache
20
  import threading
 
21
 
22
  # 新增导入
23
  import register_bot
@@ -55,7 +56,7 @@ if not NOTDIAMOND_IP:
55
  logger.error("NOTDIAMOND_IP environment variable is not set!")
56
  raise ValueError("NOTDIAMOND_IP must be set")
57
 
58
- # API钥验证装饰器
59
  def require_api_key(f):
60
  @wraps(f)
61
  def decorated_function(*args, **kwargs):
@@ -118,6 +119,10 @@ class AuthManager:
118
  self._session: requests.Session = create_custom_session()
119
  self._logger: logging.Logger = logging.getLogger(__name__)
120
  self.model_status = {model: True for model in MODEL_INFO.keys()}
 
 
 
 
121
 
122
  def login(self) -> bool:
123
  """使用电子邮件和密码进行用户登录,并获取用户信息。"""
@@ -252,6 +257,14 @@ class AuthManager:
252
  def reset_model_status(self):
253
  self.model_status = {model: True for model in MODEL_INFO.keys()}
254
 
 
 
 
 
 
 
 
 
255
  class MultiAuthManager:
256
  def __init__(self, credentials):
257
  self.auth_managers = [AuthManager(email, password) for email, password in credentials]
@@ -477,7 +490,7 @@ def get_auth_credentials():
477
  if data.get('status') == 'success' and data.get('content'):
478
  content = data['content']
479
  credentials = []
480
- # 分割多个凭据(如果的话)
481
  for cred in content.split(';'):
482
  if '|' in cred:
483
  email, password = cred.strip().split('|')
@@ -521,29 +534,69 @@ def before_request():
521
  else:
522
  multi_auth_manager = None
523
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
524
  @app.route('/', methods=['GET'])
525
  def root():
526
- return jsonify({
527
- "service": "AI Chat Completion Proxy",
528
- "usage": {
529
- "endpoint": "/ai/v1/chat/completions",
530
- "method": "POST",
531
- "headers": {
532
- "Authorization": "Bearer YOUR_API_KEY"
533
- },
534
- "body": {
535
- "model": "One of: " + ", ".join(MODEL_INFO.keys()),
536
- "messages": [
537
- {"role": "system", "content": "You are a helpful assistant."},
538
- {"role": "user", "content": "Hello, who are you?"}
539
- ],
540
- "stream": False,
541
- "temperature": 0.7
542
- }
543
- },
544
- "availableModels": list(MODEL_INFO.keys()),
545
- "note": "API key authentication is required for other endpoints."
546
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
547
 
548
  @app.route('/ai/v1/models', methods=['GET'])
549
  def proxy_models():
@@ -721,6 +774,9 @@ def make_request(payload, auth_manager, model_id):
721
  stream=True
722
  ).result()
723
 
 
 
 
724
  if response.status_code == 200 and response.headers.get('Content-Type') == 'text/event-stream':
725
  return response
726
 
@@ -743,6 +799,7 @@ def make_request(payload, auth_manager, model_id):
743
  logger.error(f"Request failed with status {response.status_code} for account {auth_manager._email}")
744
 
745
  except Exception as e:
 
746
  logger.error(f"Request attempt {attempt + 1} failed for account {auth_manager._email}: {e}")
747
  if attempt < max_retries - 1:
748
  time.sleep(retry_delay)
@@ -782,4 +839,5 @@ if __name__ == "__main__":
782
  health_check_thread.start()
783
 
784
  port = int(os.environ.get("PORT", 3000))
785
- app.run(debug=False, host='0.0.0.0', port=port, threaded=True)
 
 
11
  from typing import Dict, Any, Callable, List, Tuple
12
  import requests
13
  import tiktoken
14
+ from flask import Flask, Response, jsonify, request, stream_with_context, render_template
15
  from flask_cors import CORS
16
  from requests.adapters import HTTPAdapter
17
  from urllib3.util.connection import create_connection
18
  import urllib3
19
  from cachetools import TTLCache
20
  import threading
21
+ from datetime import datetime
22
 
23
  # 新增导入
24
  import register_bot
 
56
  logger.error("NOTDIAMOND_IP environment variable is not set!")
57
  raise ValueError("NOTDIAMOND_IP must be set")
58
 
59
+ # API钥验证装饰器
60
  def require_api_key(f):
61
  @wraps(f)
62
  def decorated_function(*args, **kwargs):
 
119
  self._session: requests.Session = create_custom_session()
120
  self._logger: logging.Logger = logging.getLogger(__name__)
121
  self.model_status = {model: True for model in MODEL_INFO.keys()}
122
+ self.total_requests = 0
123
+ self.success_requests = 0
124
+ self.failed_requests = 0
125
+ self.last_used_time = None
126
 
127
  def login(self) -> bool:
128
  """使用电子邮件和密码进行用户登录,并获取用户信息。"""
 
257
  def reset_model_status(self):
258
  self.model_status = {model: True for model in MODEL_INFO.keys()}
259
 
260
+ def record_request(self, success: bool):
261
+ self.total_requests += 1
262
+ if success:
263
+ self.success_requests += 1
264
+ else:
265
+ self.failed_requests += 1
266
+ self.last_used_time = datetime.now()
267
+
268
  class MultiAuthManager:
269
  def __init__(self, credentials):
270
  self.auth_managers = [AuthManager(email, password) for email, password in credentials]
 
490
  if data.get('status') == 'success' and data.get('content'):
491
  content = data['content']
492
  credentials = []
493
+ # 分割多个凭据(如果��的话)
494
  for cred in content.split(';'):
495
  if '|' in cred:
496
  email, password = cred.strip().split('|')
 
534
  else:
535
  multi_auth_manager = None
536
 
537
+ def get_accounts_status():
538
+ """获取所有账号的状态信息"""
539
+ if not multi_auth_manager:
540
+ return []
541
+
542
+ accounts_status = []
543
+ for auth_manager in multi_auth_manager.auth_managers:
544
+ account_info = {
545
+ "email": auth_manager._email,
546
+ "is_valid": auth_manager.is_token_valid(),
547
+ "token_expiry": datetime.fromtimestamp(auth_manager._token_expiry).strftime('%Y-%m-%d %H:%M:%S'),
548
+ "models_status": {
549
+ model: status
550
+ for model, status in auth_manager.model_status.items()
551
+ }
552
+ }
553
+ accounts_status.append(account_info)
554
+ return accounts_status
555
+
556
  @app.route('/', methods=['GET'])
557
  def root():
558
+ accounts_status = get_accounts_status()
559
+
560
+ if request.headers.get('Accept') == 'application/json':
561
+ return get_json_status(accounts_status)
562
+
563
+ return render_template('monitor.html', **get_template_data(accounts_status))
564
+
565
+ def get_template_data(accounts_status):
566
+ total_accounts = len(accounts_status)
567
+ valid_accounts = sum(1 for acc in accounts_status if acc["is_valid"])
568
+
569
+ accounts_data = []
570
+ total_requests = 0
571
+
572
+ for auth_manager in multi_auth_manager.auth_managers:
573
+ success_rate = 0
574
+ if auth_manager.total_requests > 0:
575
+ success_rate = (auth_manager.success_requests / auth_manager.total_requests) * 100
576
+
577
+ account_info = {
578
+ "email": auth_manager._email,
579
+ "is_valid": auth_manager.is_token_valid(),
580
+ "total_requests": auth_manager.total_requests,
581
+ "success_requests": auth_manager.success_requests,
582
+ "failed_requests": auth_manager.failed_requests,
583
+ "success_rate": success_rate,
584
+ "last_used_time": auth_manager.last_used_time.strftime('%m/%d/%Y, %I:%M:%S %p') if auth_manager.last_used_time else "从未使用"
585
+ }
586
+ accounts_data.append(account_info)
587
+ total_requests += auth_manager.total_requests
588
+
589
+ return {
590
+ "total_accounts": total_accounts,
591
+ "valid_accounts": valid_accounts,
592
+ "total_requests": total_requests,
593
+ "accounts": accounts_data,
594
+ "last_update": datetime.now().strftime('%Y-%m-%d %H:%M:%S')
595
+ }
596
+
597
+ def get_json_status(accounts_status):
598
+ template_data = get_template_data(accounts_status)
599
+ return jsonify(template_data)
600
 
601
  @app.route('/ai/v1/models', methods=['GET'])
602
  def proxy_models():
 
774
  stream=True
775
  ).result()
776
 
777
+ success = response.status_code == 200
778
+ auth_manager.record_request(success)
779
+
780
  if response.status_code == 200 and response.headers.get('Content-Type') == 'text/event-stream':
781
  return response
782
 
 
799
  logger.error(f"Request failed with status {response.status_code} for account {auth_manager._email}")
800
 
801
  except Exception as e:
802
+ auth_manager.record_request(False)
803
  logger.error(f"Request attempt {attempt + 1} failed for account {auth_manager._email}: {e}")
804
  if attempt < max_retries - 1:
805
  time.sleep(retry_delay)
 
839
  health_check_thread.start()
840
 
841
  port = int(os.environ.get("PORT", 3000))
842
+ app.run(debug=False, host='0.0.0.0', port=port, threaded=True)
843
+
requirements.txt CHANGED
@@ -8,3 +8,4 @@ urllib3==1.26.9
8
  beautifulsoup4==4.11.1
9
  Pillow==9.2.0
10
  lxml==4.9.1
 
 
8
  beautifulsoup4==4.11.1
9
  Pillow==9.2.0
10
  lxml==4.9.1
11
+ jinja2>=3.0.0
templates/monitor.html ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>账号状态监控</title>
5
+ <meta charset="UTF-8">
6
+ <style>
7
+ body {
8
+ font-family: Arial, sans-serif;
9
+ margin: 20px;
10
+ background-color: #f5f5f5;
11
+ }
12
+ .header {
13
+ text-align: center;
14
+ margin-bottom: 20px;
15
+ }
16
+ .summary {
17
+ text-align: center;
18
+ margin-bottom: 30px;
19
+ font-size: 1.2em;
20
+ }
21
+ .grid {
22
+ display: grid;
23
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
24
+ gap: 20px;
25
+ padding: 20px;
26
+ }
27
+ .account-card {
28
+ background: white;
29
+ border-radius: 8px;
30
+ padding: 15px;
31
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
32
+ }
33
+ .account-card h3 {
34
+ margin-top: 0;
35
+ color: #333;
36
+ }
37
+ .status-available {
38
+ color: green;
39
+ }
40
+ .status-unavailable {
41
+ color: red;
42
+ }
43
+ .metric {
44
+ margin: 8px 0;
45
+ }
46
+ .refresh-time {
47
+ text-align: center;
48
+ margin-top: 20px;
49
+ color: #666;
50
+ }
51
+ </style>
52
+ </head>
53
+ <body>
54
+ <div class="header">
55
+ <h1>账号状态监控</h1>
56
+ </div>
57
+ <div class="summary">
58
+ <div>共 <span id="online-count">{{ valid_accounts }}/{{ total_accounts }}</span> 个账号在线</div>
59
+ <div>请求总数: <span id="total-requests">{{ total_requests }}</span></div>
60
+ </div>
61
+ <div class="grid">
62
+ {% for account in accounts %}
63
+ <div class="account-card">
64
+ <h3>账号{{ loop.index }}: {{ account.email|replace(account.email[1:-4], '*'*3) }}</h3>
65
+ <div class="metric">总请求数: {{ account.total_requests }}</div>
66
+ <div class="metric">成功请求数: {{ account.success_requests }}</div>
67
+ <div class="metric">失败请求数: {{ account.failed_requests }}</div>
68
+ <div class="metric">成功率: {{ "%.2f"|format(account.success_rate) }}%</div>
69
+ <div class="metric">最后使用时间: {{ account.last_used_time }}</div>
70
+ <div class="metric">状态:
71
+ <span class="status-{{ 'available' if account.is_valid else 'unavailable' }}">
72
+ {{ "可用" if account.is_valid else "不可用" }}
73
+ </span>
74
+ </div>
75
+ </div>
76
+ {% endfor %}
77
+ </div>
78
+ <div class="refresh-time">
79
+ 最后更新时间: <span id="last-update">{{ last_update }}</span>
80
+ </div>
81
+ <script>
82
+ function refreshData() {
83
+ fetch(window.location.href)
84
+ .then(response => response.json())
85
+ .then(data => {
86
+ document.getElementById('online-count').textContent =
87
+ `${data.valid_accounts}/${data.total_accounts}`;
88
+ document.getElementById('total-requests').textContent = data.total_requests;
89
+ document.getElementById('last-update').textContent = data.last_update;
90
+ // 更新账号卡片
91
+ const grid = document.querySelector('.grid');
92
+ grid.innerHTML = data.accounts.map((account, index) => `
93
+ <div class="account-card">
94
+ <h3>账号${index + 1}: ${account.email.replace(/(?<=.).(?=.*@)/g, '*')}</h3>
95
+ <div class="metric">总请求数: ${account.total_requests}</div>
96
+ <div class="metric">成功请求数: ${account.success_requests}</div>
97
+ <div class="metric">失败请求数: ${account.failed_requests}</div>
98
+ <div class="metric">成功率: ${account.success_rate.toFixed(2)}%</div>
99
+ <div class="metric">最后使用时间: ${account.last_used_time}</div>
100
+ <div class="metric">状态:
101
+ <span class="status-${account.is_valid ? 'available' : 'unavailable'}">
102
+ ${account.is_valid ? '可用' : '不可用'}
103
+ </span>
104
+ </div>
105
+ </div>
106
+ `).join('');
107
+ });
108
+ }
109
+ setInterval(refreshData, 60000); // 每分钟刷新一次
110
+ </script>
111
+ </body>
112
+ </html>