nbugs commited on
Commit
c452165
·
verified ·
1 Parent(s): 636fbda

Update public/index.html

Browse files
Files changed (1) hide show
  1. public/index.html +219 -97
public/index.html CHANGED
@@ -630,7 +630,63 @@
630
  .loading-text {
631
  font-size: 0.9375rem;
632
  }
633
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
634
  /* 动画 */
635
  @keyframes fadeIn {
636
  from { opacity: 0; transform: translateY(20px); }
@@ -752,6 +808,10 @@
752
  min-width: 0;
753
  padding: 8px 10px;
754
  }
 
 
 
 
755
  }
756
  </style>
757
  </head>
@@ -1218,17 +1278,40 @@
1218
  }
1219
 
1220
  // 获取实例列表
1221
- async function fetchInstances() {
1222
- try {
1223
- const response = await fetch('/api/proxy/spaces');
1224
- const instances = await response.json();
1225
- return instances;
1226
- } catch (error) {
1227
- console.error("获取实例列表失败:", error);
1228
- showToast('获取实例列表失败,请刷新页面重试', 'error');
1229
- return [];
1230
- }
1231
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1232
 
1233
  // 指标管理器
1234
  class MetricsManager {
@@ -1386,91 +1469,120 @@
1386
  }
1387
 
1388
  // 创建服务器卡片
1389
- function createServerCard(instance) {
1390
- const instanceId = instance.repo_id;
1391
- instanceMap.set(instanceId, instance);
1392
-
1393
- // 创建卡片元素
1394
- const card = document.createElement('div');
1395
- card.id = `instance-${instanceId}`;
1396
- card.className = 'server-card';
1397
-
1398
- // 设置状态样式
1399
- let statusClass = 'status-offline';
1400
- let statusText = '离线';
1401
-
1402
- if (instance.status.toLowerCase() === 'running') {
1403
- statusClass = 'status-online';
1404
- statusText = '在线';
1405
- } else if (instance.status.toLowerCase() === 'sleeping') {
1406
- statusClass = 'status-sleep';
1407
- statusText = '休眠';
1408
- }
1409
-
1410
- // 设置卡片内容
1411
- card.innerHTML = `
1412
- <div class="server-header">
1413
- <div class="server-name">
1414
- <i class="ri-server-line"></i>
1415
- <div>
1416
- ${instance.name}
1417
- <div class="server-id">${instance.repo_id}</div>
1418
- </div>
1419
- </div>
1420
- <div class="status-indicator">
1421
- <span class="status-dot ${statusClass}"></span>
1422
- <span>${statusText}</span>
1423
- </div>
1424
- </div>
1425
-
1426
- <div class="metric-grid">
1427
- <div class="metric-item">
1428
- <div class="metric-label">
1429
- <i class="ri-cpu-line"></i> CPU
1430
- </div>
1431
- <div class="metric-value cpu-usage">N/A</div>
1432
- </div>
1433
- <div class="metric-item">
1434
- <div class="metric-label">
1435
- <i class="ri-hard-drive-2-line"></i> 内存
1436
- </div>
1437
- <div class="metric-value memory-usage">N/A</div>
1438
- </div>
1439
- <div class="metric-item">
1440
- <div class="metric-label">
1441
- <i class="ri-upload-2-line"></i> 上传
1442
- </div>
1443
- <div class="metric-value upload">N/A</div>
1444
- </div>
1445
- <div class="metric-item">
1446
- <div class="metric-label">
1447
- <i class="ri-download-2-line"></i> 下载
1448
- </div>
1449
- <div class="metric-value download">N/A</div>
1450
- </div>
1451
- </div>
1452
-
1453
- <div class="action-buttons" style="display: ${isLoggedIn ? 'flex' : 'none'};">
1454
- <button class="btn" onclick="showConfirmDialog('restart', '${instance.repo_id}', '确认重启', '您确定要重启实例 ${instance.name} (${instance.repo_id}) 吗?')">
1455
- <i class="ri-restart-line"></i> 重启
1456
- </button>
1457
- <button class="btn" onclick="showConfirmDialog('rebuild', '${instance.repo_id}', '确认重建', '您确定要重建实例 ${instance.name} (${instance.repo_id}) 吗?这将重新构建整个实例。')">
1458
- <i class="ri-building-line"></i> 重建
1459
- </button>
1460
- </div>
1461
- `;
1462
-
1463
- // 记录服务器状态
1464
- serverStatus.set(instanceId, {
1465
- lastSeen: Date.now(),
1466
- isOnline: instance.status.toLowerCase() === 'running',
1467
- isSleep: instance.status.toLowerCase() === 'sleeping',
1468
- data: null,
1469
- status: instance.status
1470
- });
1471
-
1472
- return card;
1473
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1474
 
1475
  // 更新服务器卡片
1476
  function updateServerCard(data, instanceId, isSleep = false) {
@@ -1678,6 +1790,16 @@
1678
  document.getElementById('totalDownload').textContent = `${formatBytes(totalDownload)}/s`;
1679
  }
1680
 
 
 
 
 
 
 
 
 
 
 
1681
  // 格式化字节数
1682
  function formatBytes(bytes) {
1683
  if (bytes === 0) return '0 B';
 
630
  .loading-text {
631
  font-size: 0.9375rem;
632
  }
633
+ .instance-url {
634
+ display: flex;
635
+ align-items: center;
636
+ background: var(--network-background);
637
+ border-radius: var(--border-radius-sm);
638
+ padding: 10px 12px;
639
+ margin: 15px 0;
640
+ font-size: 0.875rem;
641
+ border: 1px solid var(--metric-border);
642
+ overflow: hidden;
643
+ }
644
+ .url-link {
645
+ color: var(--primary-color);
646
+ text-decoration: none;
647
+ white-space: nowrap;
648
+ overflow: hidden;
649
+ text-overflow: ellipsis;
650
+ flex: 1;
651
+ display: flex;
652
+ align-items: center;
653
+ gap: 6px;
654
+ }
655
+ .url-link:hover {
656
+ text-decoration: underline;
657
+ }
658
+ .btn-icon {
659
+ background: transparent;
660
+ border: none;
661
+ color: var(--secondary-text);
662
+ width: 28px;
663
+ height: 28px;
664
+ border-radius: 4px;
665
+ display: flex;
666
+ align-items: center;
667
+ justify-content: center;
668
+ cursor: pointer;
669
+ transition: all var(--transition-fast);
670
+ }
671
+ .btn-icon:hover {
672
+ background: var(--action-button-bg);
673
+ color: var(--text-color);
674
+ }
675
+ .instance-info {
676
+ margin-bottom: 15px;
677
+ font-size: 0.8125rem;
678
+ color: var(--secondary-text);
679
+ }
680
+ .info-item {
681
+ display: flex;
682
+ align-items: center;
683
+ gap: 6px;
684
+ }
685
+ .info-item i {
686
+ font-size: 0.9375rem;
687
+ opacity: 0.8;
688
+ }
689
+
690
  /* 动画 */
691
  @keyframes fadeIn {
692
  from { opacity: 0; transform: translateY(20px); }
 
808
  min-width: 0;
809
  padding: 8px 10px;
810
  }
811
+ .instance-url {
812
+ padding: 8px 10px;
813
+ font-size: 0.8125rem;
814
+ }
815
  }
816
  </style>
817
  </head>
 
1278
  }
1279
 
1280
  // 获取实例列表
1281
+ async function fetchInstances() {
1282
+ try {
1283
+ const response = await fetch('/api/proxy/spaces');
1284
+ const instances = await response.json();
1285
+
1286
+ // 过滤掉敏感信息
1287
+ return instances.map(instance => {
1288
+ // 创建一个新对象,只包含需要的字段
1289
+ const safeInstance = {
1290
+ repo_id: instance.repo_id,
1291
+ name: instance.name,
1292
+ owner: instance.owner,
1293
+ username: instance.username,
1294
+ url: instance.url,
1295
+ status: instance.status,
1296
+ last_modified: instance.last_modified,
1297
+ created_at: instance.created_at,
1298
+ sdk: instance.sdk,
1299
+ tags: instance.tags,
1300
+ private: instance.private,
1301
+ app_port: instance.app_port
1302
+ };
1303
+
1304
+ // 确保删除任何敏感字段
1305
+ delete safeInstance.token;
1306
+
1307
+ return safeInstance;
1308
+ });
1309
+ } catch (error) {
1310
+ console.error("获取实例列表失败:", error);
1311
+ showToast('获取实例列表失败,请刷新页面重试', 'error');
1312
+ return [];
1313
+ }
1314
+ }
1315
 
1316
  // 指标管理器
1317
  class MetricsManager {
 
1469
  }
1470
 
1471
  // 创建服务器卡片
1472
+ function createServerCard(instance) {
1473
+ const instanceId = instance.repo_id;
1474
+
1475
+ // 存储安全的实例信息(确保不包含token)
1476
+ const safeInstance = {...instance};
1477
+ delete safeInstance.token;
1478
+ instanceMap.set(instanceId, safeInstance);
1479
+
1480
+ // 创建卡片元素
1481
+ const card = document.createElement('div');
1482
+ card.id = `instance-${instanceId}`;
1483
+ card.className = 'server-card';
1484
+
1485
+ // 设置状态样式
1486
+ let statusClass = 'status-offline';
1487
+ let statusText = '离线';
1488
+
1489
+ if (instance.status.toLowerCase() === 'running') {
1490
+ statusClass = 'status-online';
1491
+ statusText = '在线';
1492
+ } else if (instance.status.toLowerCase() === 'sleeping') {
1493
+ statusClass = 'status-sleep';
1494
+ statusText = '休眠';
1495
+ }
1496
+
1497
+ // 格式化最后修改时间
1498
+ const lastModified = new Date(instance.last_modified).toLocaleString('zh-CN', {
1499
+ year: 'numeric',
1500
+ month: '2-digit',
1501
+ day: '2-digit',
1502
+ hour: '2-digit',
1503
+ minute: '2-digit'
1504
+ });
1505
+
1506
+ // 设置卡片内容
1507
+ card.innerHTML = `
1508
+ <div class="server-header">
1509
+ <div class="server-name">
1510
+ <i class="ri-server-line"></i>
1511
+ <div>
1512
+ ${instance.name}
1513
+ <div class="server-id">${instance.repo_id}</div>
1514
+ </div>
1515
+ </div>
1516
+ <div class="status-indicator">
1517
+ <span class="status-dot ${statusClass}"></span>
1518
+ <span>${statusText}</span>
1519
+ </div>
1520
+ </div>
1521
+
1522
+ <div class="instance-url">
1523
+ <a href="${instance.url}" target="_blank" class="url-link">
1524
+ <i class="ri-external-link-line"></i> ${instance.url}
1525
+ </a>
1526
+ <button class="btn-icon copy-url" onclick="copyToClipboard('${instance.url}')" title="复制URL">
1527
+ <i class="ri-file-copy-line"></i>
1528
+ </button>
1529
+ </div>
1530
+
1531
+ <div class="instance-info">
1532
+ <div class="info-item">
1533
+ <i class="ri-time-line"></i> 最后更新: ${lastModified}
1534
+ </div>
1535
+ </div>
1536
+
1537
+ <div class="metric-grid">
1538
+ <div class="metric-item">
1539
+ <div class="metric-label">
1540
+ <i class="ri-cpu-line"></i> CPU
1541
+ </div>
1542
+ <div class="metric-value cpu-usage">N/A</div>
1543
+ </div>
1544
+ <div class="metric-item">
1545
+ <div class="metric-label">
1546
+ <i class="ri-hard-drive-2-line"></i> 内存
1547
+ </div>
1548
+ <div class="metric-value memory-usage">N/A</div>
1549
+ </div>
1550
+ <div class="metric-item">
1551
+ <div class="metric-label">
1552
+ <i class="ri-upload-2-line"></i> 上传
1553
+ </div>
1554
+ <div class="metric-value upload">N/A</div>
1555
+ </div>
1556
+ <div class="metric-item">
1557
+ <div class="metric-label">
1558
+ <i class="ri-download-2-line"></i> 下载
1559
+ </div>
1560
+ <div class="metric-value download">N/A</div>
1561
+ </div>
1562
+ </div>
1563
+
1564
+ <div class="action-buttons" style="display: ${isLoggedIn ? 'flex' : 'none'};">
1565
+ <button class="btn" onclick="showConfirmDialog('restart', '${instance.repo_id}', '确认重启', '您确定要重启实例 ${instance.name} (${instance.repo_id}) 吗?')">
1566
+ <i class="ri-restart-line"></i> 重启
1567
+ </button>
1568
+ <button class="btn" onclick="showConfirmDialog('rebuild', '${instance.repo_id}', '确认重建', '您确定要重建实例 ${instance.name} (${instance.repo_id}) 吗?这将重新构建整个实例。')">
1569
+ <i class="ri-building-line"></i> 重建
1570
+ </button>
1571
+ </div>
1572
+ `;
1573
+
1574
+ // 记录服务器状态
1575
+ serverStatus.set(instanceId, {
1576
+ lastSeen: Date.now(),
1577
+ isOnline: instance.status.toLowerCase() === 'running',
1578
+ isSleep: instance.status.toLowerCase() === 'sleeping',
1579
+ data: null,
1580
+ status: instance.status
1581
+ });
1582
+
1583
+ return card;
1584
+ }
1585
+
1586
 
1587
  // 更新服务器卡片
1588
  function updateServerCard(data, instanceId, isSleep = false) {
 
1790
  document.getElementById('totalDownload').textContent = `${formatBytes(totalDownload)}/s`;
1791
  }
1792
 
1793
+ // 添加在 updateSummary 函数之后
1794
+ function copyToClipboard(text) {
1795
+ navigator.clipboard.writeText(text).then(() => {
1796
+ showToast('URL已复制到剪贴板', 'success');
1797
+ }).catch(err => {
1798
+ console.error('复制失败:', err);
1799
+ showToast('复制失败,请手动复制', 'error');
1800
+ });
1801
+ }
1802
+
1803
  // 格式化字节数
1804
  function formatBytes(bytes) {
1805
  if (bytes === 0) return '0 B';