CoolFace
Apppublic

devin15/cursor2api-rust

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
api.html362 linesDownload Raw Back to static
1<!DOCTYPE html>2<html lang="zh">3 4<head>5  <meta charset="UTF-8">6  <link rel="icon" type="image/x-icon" href="data:image/x-icon;,">7  <meta name="viewport" content="width=device-width, initial-scale=1.0">8  <title>API 管理</title>9  <link rel="stylesheet" href="/static/shared-styles.css">10  <script src="/static/shared.js"></script>11  <style>12    .status-healthy {13      color: var(--success-color);14      animation: pulse 2s infinite;15    }16 17    .status-error {18      color: var(--error-color);19    }20 21    @keyframes pulse {22      0% {23        opacity: 1;24      }25 26      50% {27        opacity: 0.6;28      }29 30      100% {31        opacity: 1;32      }33    }34 35    .footer {36      margin-top: 2rem;37      color: var(--text-secondary);38      font-size: 0.9rem;39      text-align: center;40    }41 42    .copy-button {43      position: absolute;44      right: 0px;45      top: 0px;46      padding: 4px;47      background: transparent;48      min-height: auto;49    }50 51    .model-input-container {52      position: relative;53    }54 55    .custom-suffix {56      margin-top: 1rem;57    }58 59    .progress-container {60      margin-top: 1rem;61    }62 63    .usage-progress-container {64      width: 100%;65      height: 8px;66      background: var(--border-color);67      border-radius: 4px;68      margin: 8px 0;69      overflow: hidden;70    }71 72    .usage-progress-bar {73      height: 100%;74      transition: width 0.3s ease;75    }76 77    .progress-low {78      background: var(--success-color);79    }80 81    .progress-medium {82      background: #FFA726;83    }84 85    .progress-high {86      background: var(--error-color);87    }88 89    .usage-progress-bar.unlimited {90      background: repeating-linear-gradient(45deg,91          var(--success-color),92          var(--success-color) 10px,93          transparent 10px,94          transparent 20px);95      opacity: 0.5;96    }97 98    @media (max-width: 768px) {99      .copy-button {100        width: auto !important;101      }102    }103  </style>104</head>105 106<body>107  <div class="container">108    <div style="display: flex; justify-content: space-between; align-items: center;">109      <h1>API 管理</h1>110      <div id="serverStatus" class="status-healthy">Healthy</div>111    </div>112 113    <div class="form-group">114      <label for="authToken">AUTH Token</label>115      <input type="text" id="authToken" placeholder="请输入 AUTH Token">116    </div>117 118    <div class="button-group">119      <button onclick="calibrateToken()">校准 Token</button>120      <button onclick="getUserInfo()">获取用户信息</button>121      <button onclick="getModels()">获取模型列表</button>122    </div>123 124    <div class="form-group model-input-container">125      <label for="modelList">模型列表</label>126      <input type="text" id="modelList" readonly>127      <button class="copy-button" onclick="copyModelList()">📋</button>128    </div>129 130    <div class="form-group custom-suffix">131      <input type="checkbox" id="customSuffix" onchange="toggleCustomSuffix()">132      <label for="customSuffix">添加自定义后缀</label>133      <input type="text" id="suffixInput" placeholder="@OpenAI" style="display: none;">134    </div>135  </div>136 137  <div id="userInfoContainer" class="container" style="display: none;">138    <h2>用户信息</h2>139    <div id="userDetails"></div>140    <div id="usageProgressContainer" class="progress-container"></div>141  </div>142 143  <div id="message" class="message"></div>144 145  <footer class="footer">146    <div id="version"></div>147    <div id="uptime"></div>148  </footer>149 150  <script>151    // 全局变量152    let globalModels = [];153 154    // Token校准结果缓存155    const calibrationCache = new Map();156 157    // 服务器状态检查158    async function checkServerStatus() {159      try {160        const response = await fetch('/health');161        const data = await response.json();162 163        // 更新状态显示164        const statusElement = document.getElementById('serverStatus');165        statusElement.className = data.status === 'healthy' ? 'status-healthy' : 'status-error';166        statusElement.textContent = data.status === 'healthy' ? 'Healthy' : 'Error';167 168        // 更新版本和运行时间169        document.getElementById('version').textContent = `版本: ${data.version}`;170        document.getElementById('uptime').textContent = formatUptime(data.uptime);171 172        // 保存模型列表173        globalModels = data.models || [];174 175        return true;176      } catch (error) {177        const statusElement = document.getElementById('serverStatus');178        statusElement.className = 'status-error';179        statusElement.textContent = 'Error';180        showGlobalMessage('服务器状态检查失败', true);181        return false;182      }183    }184 185    // 定时检查服务器状态(5分钟)186    function startStatusCheck() {187      checkServerStatus();188      setInterval(checkServerStatus, 5 * 60 * 1000);189    }190 191    // 格式化运行时间192    function formatUptime(seconds) {193      const days = Math.floor(seconds / 86400);194      const hours = Math.floor((seconds % 86400) / 3600);195      const minutes = Math.floor((seconds % 3600) / 60);196      return `运行时间: ${days}天 ${hours}时 ${minutes}分`;197    }198 199    // 获取模型列表200    async function getModels() {201      const modelList = document.getElementById('modelList');202      const suffix = document.getElementById('customSuffix').checked ?203        document.getElementById('suffixInput').value : '';204 205      modelList.value = globalModels.map(model => model + suffix).join(',');206    }207 208    // 复制模型列表209    function copyModelList() {210      const modelList = document.getElementById('modelList');211      navigator.clipboard.writeText(modelList.value)212        .then(() => showGlobalMessage('已复制到剪贴板'))213        .catch(() => showGlobalMessage('复制失败', true));214    }215 216    // 切换自定义后缀输入框217    function toggleCustomSuffix() {218      const suffixInput = document.getElementById('suffixInput');219      suffixInput.style.display = document.getElementById('customSuffix').checked ? 'block' : 'none';220      if (document.getElementById('customSuffix').checked) {221        getModels();222      }223    }224 225    // Token相关请求226    async function makeTokenRequest(url, token) {227      try {228        const response = await fetch(url, {229          method: 'POST',230          headers: {231            'Content-Type': 'application/json'232          },233          body: JSON.stringify({ token })234        });235 236        if (!response.ok) {237          throw new Error(`HTTP error! status: ${response.status}`);238        }239 240        return await response.json();241      } catch (error) {242        showGlobalMessage(`请求失败: ${error.message}`, true);243        return null;244      }245    }246 247    // Token 校准248    async function calibrateToken() {249      const token = document.getElementById('authToken').value;250      if (!token) {251        showGlobalMessage('请输入 AUTH Token', true);252        return;253      }254      const result = await makeTokenRequest('/basic-calibration', token);255      if (result) {256        if (result.status === 'error') {257          showGlobalMessage(result.message, true);258        } else {259          showGlobalMessage('校准成功');260          // 缓存校准结果261          calibrationCache.set(token, {262            user_id: result.user_id,263            create_at: result.create_at,264            checksum_time: calibResult.checksum_time265          });266          updateUsageDisplay(null, calibrationCache.get(token));267        }268      }269    }270 271    // 获取用户信息272    async function getUserInfo() {273      const token = document.getElementById('authToken').value;274      if (!token) {275        showGlobalMessage('请输入 Token', true);276        return;277      }278      // 如果没有校准缓存,先进行校准279      if (!calibrationCache.has(token)) {280        const calibResult = await makeTokenRequest('/basic-calibration', token);281        if (calibResult && calibResult.status !== 'error') {282          calibrationCache.set(token, {283            user_id: calibResult.user_id,284            create_at: calibResult.create_at,285            checksum_time: calibResult.checksum_time286          });287        }288      }289 290      const result = await makeTokenRequest('/userinfo', token);291      if (result) {292        const container = document.getElementById('userInfoContainer');293        container.style.display = 'block';294        updateUsageDisplay(result, calibrationCache.get(token));295      }296    }297 298    // 更新使用情况显示299    function updateUsageDisplay(tokenInfo, calibInfo) {300      const userDetails = document.getElementById('userDetails');301      const progressContainer = document.getElementById('usageProgressContainer');302 303      // 清空现有内容304      userDetails.innerHTML = '';305      progressContainer.innerHTML = '';306 307      // 添加用户基本信息308      if (tokenInfo.user || calibInfo) {309        const user = tokenInfo.user || {};310        userDetails.innerHTML += `<p>用户ID: ${calibInfo ? calibInfo.user_id : user.id}</p><p>邮箱: ${user.email || ''}</p><p>用户名: ${user.name || ''}</p>${user.updated_at ? `<p>更新时间: ${new Date(user.updated_at).toLocaleString()}</p>` : ''}${calibInfo ? `<p>令牌创建时间: ${new Date(calibInfo.create_at).toLocaleString()}</p>` : ''}${calibInfo && calibInfo.checksum_time ? `<p>校验和时间区间: ${new Date(calibInfo.checksum_time * 1e6).toLocaleString()} - ${new Date((calibInfo.checksum_time + 1) * 1e6 - 1).toLocaleString()}</p>` : ''}`;311      }312 313      // 添加 Stripe 会员信息314      if (tokenInfo.stripe) {315        const stripe = tokenInfo.stripe;316        userDetails.innerHTML += `<p>会员类型: ${stripe.membership_type}</p>${stripe.payment_id ? `<p>付款 ID: ${stripe.payment_id}</p>` : ''}<p>试用剩余: ${stripe.days_remaining_on_trial} 天</p>`;317      }318 319      // 添加使用情况进度条320      if (tokenInfo.usage) {321        const usage = tokenInfo.usage;322        const models = {323          '高级模型': usage.premium,324          '标准模型': usage.standard,325          '未知模型': usage.unknown326        };327 328        Object.entries(models).forEach(([modelName, data]) => {329          if (data) {330            const isUnlimited = !data.max_requests;331            const percentage = isUnlimited ? 100 : (data.requests / data.max_requests * 100).toFixed(1);332            const progressClass = isUnlimited ? 'unlimited' : getProgressBarClass(parseFloat(percentage));333 334            progressContainer.innerHTML += `<div><p>${modelName}: ${data.requests}/${isUnlimited ? '∞' : data.max_requests} 请求 ${isUnlimited ? '' : `(${percentage}%)`}, ${data.tokens} tokens</p><div class="usage-progress-container"><div class="usage-progress-bar ${progressClass}" style="width: ${percentage}%"></div></div></div>`;335          }336        });337      }338    }339 340    // 获取进度条样式341    function getProgressBarClass(percentage) {342      if (percentage < 50) return 'progress-low';343      if (percentage < 80) return 'progress-medium';344      return 'progress-high';345    }346 347    // Token变更时清除缓存348    document.getElementById('authToken').addEventListener('change', (e) => {349      calibrationCache.delete(e.target.value); // 清除对应的缓存350    });351 352    // 页面加载完成后初始化353    document.addEventListener('DOMContentLoaded', () => {354      startStatusCheck();355 356      // 监听后缀输入变化357      document.getElementById('suffixInput').addEventListener('input', getModels);358    });359  </script>360</body>361 362</html>