Spaces:
Sleeping
Sleeping
Upload 3 files
Browse files- core/templates.py +160 -17
core/templates.py
CHANGED
|
@@ -78,23 +78,87 @@ def generate_admin_html(request: Request, multi_account_mgr, show_hide_tip: bool
|
|
| 78 |
for account_id, account_manager in multi_account_mgr.accounts.items():
|
| 79 |
config = account_manager.config
|
| 80 |
remaining_hours = config.get_remaining_hours()
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
accounts_html += f"""
|
| 88 |
-
<div class="card account-card">
|
| 89 |
<div class="acc-header">
|
| 90 |
<div class="acc-title">
|
| 91 |
-
<span class="status-dot" style="background-color: {dot_color};"
|
| 92 |
-
{config.account_id}
|
| 93 |
-
</div>
|
| 94 |
-
<div style="display: flex; align-items: center; gap: 8px;">
|
| 95 |
-
<span class="acc-status-text" style="color: {status_color}">{status_text}</span>
|
| 96 |
-
<button onclick="deleteAccount('{config.account_id}')" class="delete-btn" title="删除账户">删除</button>
|
| 97 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
</div>
|
| 99 |
<div class="acc-body">
|
| 100 |
<div class="acc-row">
|
|
@@ -103,8 +167,13 @@ def generate_admin_html(request: Request, multi_account_mgr, show_hide_tip: bool
|
|
| 103 |
</div>
|
| 104 |
<div class="acc-row">
|
| 105 |
<span>剩余时长</span>
|
| 106 |
-
<span style="color: {status_color};
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
</div>
|
|
|
|
| 108 |
</div>
|
| 109 |
</div>
|
| 110 |
"""
|
|
@@ -242,8 +311,12 @@ def generate_admin_html(request: Request, multi_account_mgr, show_hide_tip: bool
|
|
| 242 |
.account-card .acc-header {{ display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid #f5f5f5; }}
|
| 243 |
.acc-title {{ font-weight: 600; font-size: 14px; display: flex; align-items: center; gap: 8px; }}
|
| 244 |
.status-dot {{ width: 8px; height: 8px; border-radius: 50%; }}
|
| 245 |
-
.acc-status
|
|
|
|
|
|
|
| 246 |
.acc-row {{ display: flex; justify-content: space-between; font-size: 12px; margin-top: 6px; color: var(--text-sec); }}
|
|
|
|
|
|
|
| 247 |
|
| 248 |
/* Delete Button */
|
| 249 |
.delete-btn {{
|
|
@@ -263,6 +336,42 @@ def generate_admin_html(request: Request, multi_account_mgr, show_hide_tip: bool
|
|
| 263 |
border-color: #dc2626;
|
| 264 |
}}
|
| 265 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 266 |
/* Modal */
|
| 267 |
.modal {{
|
| 268 |
display: none;
|
|
@@ -522,8 +631,8 @@ def generate_admin_html(request: Request, multi_account_mgr, show_hide_tip: bool
|
|
| 522 |
<div class="env-value">{main.ACCOUNT_FAILURE_THRESHOLD} 次</div>
|
| 523 |
</div>
|
| 524 |
<div class="env-var">
|
| 525 |
-
<div><div class="env-name">
|
| 526 |
-
<div class="env-value">{main.
|
| 527 |
</div>
|
| 528 |
</div>
|
| 529 |
</div>
|
|
@@ -799,6 +908,40 @@ def generate_admin_html(request: Request, multi_account_mgr, show_hide_tip: bool
|
|
| 799 |
}}
|
| 800 |
}}
|
| 801 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 802 |
// 点击模态框外部关闭
|
| 803 |
document.getElementById('jsonModal').addEventListener('click', function(e) {{
|
| 804 |
if (e.target === this) {{
|
|
|
|
| 78 |
for account_id, account_manager in multi_account_mgr.accounts.items():
|
| 79 |
config = account_manager.config
|
| 80 |
remaining_hours = config.get_remaining_hours()
|
| 81 |
+
expire_status_text, _, expire_display = main.format_account_expiration(remaining_hours)
|
| 82 |
+
|
| 83 |
+
# 检查账户是否过期或被手动禁用
|
| 84 |
+
is_expired = config.is_expired()
|
| 85 |
+
is_disabled = config.disabled
|
| 86 |
+
|
| 87 |
+
# 使用AccountManager的方法获取冷却信息
|
| 88 |
+
cooldown_seconds, cooldown_reason = account_manager.get_cooldown_info()
|
| 89 |
+
|
| 90 |
+
# 确定账户状态和颜色
|
| 91 |
+
if is_expired:
|
| 92 |
+
status_text = "过期禁用"
|
| 93 |
+
status_color = "#9e9e9e"
|
| 94 |
+
dot_color = "#9e9e9e"
|
| 95 |
+
card_opacity = "0.5"
|
| 96 |
+
action_buttons = f'<button onclick="deleteAccount(\'{config.account_id}\')" class="delete-btn" title="删除账户">删除</button>'
|
| 97 |
+
elif is_disabled:
|
| 98 |
+
status_text = "手动禁用"
|
| 99 |
+
status_color = "#9e9e9e"
|
| 100 |
+
dot_color = "#9e9e9e"
|
| 101 |
+
card_opacity = "0.5"
|
| 102 |
+
action_buttons = f'''
|
| 103 |
+
<button onclick="enableAccount('{config.account_id}')" class="enable-btn" title="启用账户">启用</button>
|
| 104 |
+
<button onclick="deleteAccount('{config.account_id}')" class="delete-btn" title="删除账户">删除</button>
|
| 105 |
+
'''
|
| 106 |
+
elif cooldown_seconds == -1:
|
| 107 |
+
# 错误永久禁用
|
| 108 |
+
status_text = cooldown_reason # "错误禁用"
|
| 109 |
+
status_color = "#f44336"
|
| 110 |
+
dot_color = "#f44336"
|
| 111 |
+
card_opacity = "0.5"
|
| 112 |
+
action_buttons = f'''
|
| 113 |
+
<button onclick="enableAccount('{config.account_id}')" class="enable-btn" title="启用账户">启用</button>
|
| 114 |
+
<button onclick="deleteAccount('{config.account_id}')" class="delete-btn" title="删除账户">删除</button>
|
| 115 |
+
'''
|
| 116 |
+
elif cooldown_seconds > 0:
|
| 117 |
+
# 429限流(冷却中)
|
| 118 |
+
status_text = cooldown_reason # "429限流"
|
| 119 |
+
status_color = "#ff9800"
|
| 120 |
+
dot_color = "#ff9800"
|
| 121 |
+
card_opacity = "1"
|
| 122 |
+
action_buttons = f'''
|
| 123 |
+
<button onclick="disableAccount('{config.account_id}')" class="disable-btn" title="禁用账户">禁用</button>
|
| 124 |
+
<button onclick="deleteAccount('{config.account_id}')" class="delete-btn" title="删除账户">删除</button>
|
| 125 |
+
'''
|
| 126 |
+
else:
|
| 127 |
+
# 正常状态
|
| 128 |
+
is_avail = account_manager.is_available
|
| 129 |
+
if is_avail:
|
| 130 |
+
status_text = expire_status_text # "正常", "即将过期", "紧急"
|
| 131 |
+
if expire_status_text == "正常":
|
| 132 |
+
status_color = "#4caf50"
|
| 133 |
+
dot_color = "#34c759"
|
| 134 |
+
elif expire_status_text == "即将过期":
|
| 135 |
+
status_color = "#ff9800"
|
| 136 |
+
dot_color = "#ff9800"
|
| 137 |
+
else: # 紧急
|
| 138 |
+
status_color = "#f44336"
|
| 139 |
+
dot_color = "#f44336"
|
| 140 |
+
else:
|
| 141 |
+
status_text = "不可用"
|
| 142 |
+
status_color = "#f44336"
|
| 143 |
+
dot_color = "#ff3b30"
|
| 144 |
+
card_opacity = "1"
|
| 145 |
+
action_buttons = f'''
|
| 146 |
+
<button onclick="disableAccount('{config.account_id}')" class="disable-btn" title="禁用账户">禁用</button>
|
| 147 |
+
<button onclick="deleteAccount('{config.account_id}')" class="delete-btn" title="删除账户">删除</button>
|
| 148 |
+
'''
|
| 149 |
+
|
| 150 |
+
# 构建卡片内容
|
| 151 |
accounts_html += f"""
|
| 152 |
+
<div class="card account-card" style="opacity: {card_opacity}; background: {'#f5f5f5' if float(card_opacity) < 1 else '#fafaf9'};">
|
| 153 |
<div class="acc-header">
|
| 154 |
<div class="acc-title">
|
| 155 |
+
<span class="status-dot" style="background-color: {dot_color};"></span>
|
| 156 |
+
<span>{config.account_id}</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
</div>
|
| 158 |
+
<span class="acc-status" style="color: {status_color};">{status_text}</span>
|
| 159 |
+
</div>
|
| 160 |
+
<div class="acc-actions">
|
| 161 |
+
{action_buttons}
|
| 162 |
</div>
|
| 163 |
<div class="acc-body">
|
| 164 |
<div class="acc-row">
|
|
|
|
| 167 |
</div>
|
| 168 |
<div class="acc-row">
|
| 169 |
<span>剩余时长</span>
|
| 170 |
+
<span style="color: {status_color};">{expire_display}</span>
|
| 171 |
+
</div>
|
| 172 |
+
<div class="acc-row">
|
| 173 |
+
<span>累计对话</span>
|
| 174 |
+
<span style="color: #2563eb; font-weight: 600;">{account_manager.conversation_count} 次</span>
|
| 175 |
</div>
|
| 176 |
+
{'<div class="acc-row cooldown-row"><span>冷却倒计时</span><span class="cooldown-text" style="color: ' + status_color + ';">' + str(cooldown_seconds) + '秒 (' + cooldown_reason + ')</span></div>' if cooldown_seconds > 0 else ''}
|
| 177 |
</div>
|
| 178 |
</div>
|
| 179 |
"""
|
|
|
|
| 311 |
.account-card .acc-header {{ display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid #f5f5f5; }}
|
| 312 |
.acc-title {{ font-weight: 600; font-size: 14px; display: flex; align-items: center; gap: 8px; }}
|
| 313 |
.status-dot {{ width: 8px; height: 8px; border-radius: 50%; }}
|
| 314 |
+
.acc-status {{ font-size: 12px; font-weight: 600; }}
|
| 315 |
+
.acc-actions {{ display: flex; gap: 8px; margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid #f5f5f5; }}
|
| 316 |
+
.acc-body {{ }}
|
| 317 |
.acc-row {{ display: flex; justify-content: space-between; font-size: 12px; margin-top: 6px; color: var(--text-sec); }}
|
| 318 |
+
.cooldown-row {{ background: #fff8e6; padding: 8px; border-radius: 6px; margin-top: 8px; }}
|
| 319 |
+
.cooldown-text {{ color: #f59e0b; font-weight: 600; }}
|
| 320 |
|
| 321 |
/* Delete Button */
|
| 322 |
.delete-btn {{
|
|
|
|
| 336 |
border-color: #dc2626;
|
| 337 |
}}
|
| 338 |
|
| 339 |
+
/* Disable Button */
|
| 340 |
+
.disable-btn {{
|
| 341 |
+
background: #fff;
|
| 342 |
+
color: #f59e0b;
|
| 343 |
+
border: 1px solid #fed7aa;
|
| 344 |
+
padding: 4px 12px;
|
| 345 |
+
border-radius: 6px;
|
| 346 |
+
font-size: 11px;
|
| 347 |
+
cursor: pointer;
|
| 348 |
+
font-weight: 500;
|
| 349 |
+
transition: all 0.2s;
|
| 350 |
+
}}
|
| 351 |
+
.disable-btn:hover {{
|
| 352 |
+
background: #f59e0b;
|
| 353 |
+
color: white;
|
| 354 |
+
border-color: #f59e0b;
|
| 355 |
+
}}
|
| 356 |
+
|
| 357 |
+
/* Enable Button */
|
| 358 |
+
.enable-btn {{
|
| 359 |
+
background: #fff;
|
| 360 |
+
color: #10b981;
|
| 361 |
+
border: 1px solid #a7f3d0;
|
| 362 |
+
padding: 4px 12px;
|
| 363 |
+
border-radius: 6px;
|
| 364 |
+
font-size: 11px;
|
| 365 |
+
cursor: pointer;
|
| 366 |
+
font-weight: 500;
|
| 367 |
+
transition: all 0.2s;
|
| 368 |
+
}}
|
| 369 |
+
.enable-btn:hover {{
|
| 370 |
+
background: #10b981;
|
| 371 |
+
color: white;
|
| 372 |
+
border-color: #10b981;
|
| 373 |
+
}}
|
| 374 |
+
|
| 375 |
/* Modal */
|
| 376 |
.modal {{
|
| 377 |
display: none;
|
|
|
|
| 631 |
<div class="env-value">{main.ACCOUNT_FAILURE_THRESHOLD} 次</div>
|
| 632 |
</div>
|
| 633 |
<div class="env-var">
|
| 634 |
+
<div><div class="env-name">RATE_LIMIT_COOLDOWN_SECONDS</div><div class="env-desc">429限流冷却时间</div></div>
|
| 635 |
+
<div class="env-value">{main.RATE_LIMIT_COOLDOWN_SECONDS} 秒</div>
|
| 636 |
</div>
|
| 637 |
</div>
|
| 638 |
</div>
|
|
|
|
| 908 |
}}
|
| 909 |
}}
|
| 910 |
|
| 911 |
+
async function disableAccount(accountId) {{
|
| 912 |
+
if (!confirm(`确定禁用账户 ${{accountId}}?`)) return;
|
| 913 |
+
|
| 914 |
+
try {{
|
| 915 |
+
const response = await fetch('/{main.PATH_PREFIX}/admin/accounts/' + accountId + '/disable?key={main.ADMIN_KEY}', {{
|
| 916 |
+
method: 'PUT'
|
| 917 |
+
}});
|
| 918 |
+
|
| 919 |
+
const result = await handleApiResponse(response);
|
| 920 |
+
alert(`账户已禁用!`);
|
| 921 |
+
refreshPage();
|
| 922 |
+
}} catch (error) {{
|
| 923 |
+
console.error('禁用失败:', error);
|
| 924 |
+
alert('禁用失败: ' + error.message);
|
| 925 |
+
}}
|
| 926 |
+
}}
|
| 927 |
+
|
| 928 |
+
async function enableAccount(accountId) {{
|
| 929 |
+
if (!confirm(`确定启用账户 ${{accountId}}?`)) return;
|
| 930 |
+
|
| 931 |
+
try {{
|
| 932 |
+
const response = await fetch('/{main.PATH_PREFIX}/admin/accounts/' + accountId + '/enable?key={main.ADMIN_KEY}', {{
|
| 933 |
+
method: 'PUT'
|
| 934 |
+
}});
|
| 935 |
+
|
| 936 |
+
const result = await handleApiResponse(response);
|
| 937 |
+
alert(`账户已启用!`);
|
| 938 |
+
refreshPage();
|
| 939 |
+
}} catch (error) {{
|
| 940 |
+
console.error('启用失败:', error);
|
| 941 |
+
alert('启用失败: ' + error.message);
|
| 942 |
+
}}
|
| 943 |
+
}}
|
| 944 |
+
|
| 945 |
// 点击模态框外部关闭
|
| 946 |
document.getElementById('jsonModal').addEventListener('click', function(e) {{
|
| 947 |
if (e.target === this) {{
|