| | <!DOCTYPE html> |
| | <html lang="zh-CN"> |
| |
|
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>GCLI2API 控制面板</title> |
| | <style> |
| | body { |
| | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif; |
| | max-width: 1000px; |
| | margin: 0 auto; |
| | padding: 20px; |
| | background-color: #f8f9fa; |
| | } |
| | |
| | .container { |
| | background-color: white; |
| | padding: 30px; |
| | border-radius: 10px; |
| | box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); |
| | box-sizing: border-box; |
| | overflow-x: auto; |
| | } |
| | |
| | h1 { |
| | color: #333; |
| | text-align: center; |
| | } |
| | |
| | .form-group { |
| | margin-bottom: 20px; |
| | } |
| | |
| | label { |
| | display: block; |
| | margin-bottom: 5px; |
| | font-weight: bold; |
| | color: #555; |
| | } |
| | |
| | input[type="text"] { |
| | width: 100%; |
| | padding: 10px; |
| | border: 2px solid #ddd; |
| | border-radius: 5px; |
| | font-size: 16px; |
| | box-sizing: border-box; |
| | } |
| | |
| | input[type="text"]:focus { |
| | border-color: #4285f4; |
| | outline: none; |
| | } |
| | |
| | .btn { |
| | background-color: #4285f4; |
| | color: white; |
| | padding: 12px 30px; |
| | border: none; |
| | border-radius: 5px; |
| | font-size: 16px; |
| | cursor: pointer; |
| | width: 100%; |
| | margin-bottom: 10px; |
| | } |
| | |
| | .btn:hover { |
| | background-color: #3367d6; |
| | } |
| | |
| | .btn:disabled { |
| | background-color: #ccc; |
| | cursor: not-allowed; |
| | } |
| | |
| | .auth-url { |
| | background-color: #f8f9fa; |
| | border: 1px solid #e1e4e8; |
| | border-radius: 5px; |
| | padding: 15px; |
| | margin: 20px 0; |
| | word-break: break-all; |
| | } |
| | |
| | .auth-url a { |
| | color: #4285f4; |
| | text-decoration: none; |
| | } |
| | |
| | .auth-url a:hover { |
| | text-decoration: underline; |
| | } |
| | |
| | .credentials { |
| | background-color: #f0f8ff; |
| | border: 1px solid #b0d4ff; |
| | border-radius: 5px; |
| | padding: 15px; |
| | margin: 20px 0; |
| | font-family: monospace; |
| | font-size: 12px; |
| | white-space: pre-wrap; |
| | word-break: break-all; |
| | max-height: 400px; |
| | overflow-y: auto; |
| | } |
| | |
| | |
| | #statusSection { |
| | position: fixed; |
| | top: 20px; |
| | right: 20px; |
| | left: auto; |
| | transform: none; |
| | z-index: 9999; |
| | width: auto; |
| | max-width: 400px; |
| | min-width: 250px; |
| | } |
| | |
| | |
| | #statusSection .status { |
| | padding: 12px 20px; |
| | border-radius: 8px; |
| | margin: 0; |
| | box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); |
| | opacity: 0; |
| | transform: translateX(100%); |
| | transition: opacity 0.3s ease, transform 0.3s ease; |
| | } |
| | |
| | #statusSection .status.show { |
| | opacity: 1; |
| | transform: translateX(0); |
| | } |
| | |
| | #statusSection .status.fade-out { |
| | opacity: 0; |
| | transform: translateX(100%); |
| | } |
| | |
| | #statusSection .status.success { |
| | background-color: #28a745; |
| | border: none; |
| | color: white; |
| | } |
| | |
| | #statusSection .status.error { |
| | background-color: #dc3545; |
| | border: none; |
| | color: white; |
| | } |
| | |
| | #statusSection .status.warning { |
| | background-color: #ffc107; |
| | border: none; |
| | color: #212529; |
| | } |
| | |
| | #statusSection .status.info { |
| | background-color: #17a2b8; |
| | border: none; |
| | color: white; |
| | } |
| | |
| | |
| | .status { |
| | padding: 10px; |
| | border-radius: 5px; |
| | margin: 10px 0; |
| | } |
| | |
| | .status.success { |
| | background-color: #d4edda; |
| | border: 1px solid #c3e6cb; |
| | color: #155724; |
| | } |
| | |
| | .status.error { |
| | background-color: #f8d7da; |
| | border: 1px solid #f5c6cb; |
| | color: #721c24; |
| | } |
| | |
| | .status.info { |
| | background-color: #d1ecf1; |
| | border: 1px solid #bee5eb; |
| | color: #0c5460; |
| | } |
| | |
| | .hidden { |
| | display: none; |
| | } |
| | |
| | .loading { |
| | text-align: center; |
| | color: #666; |
| | } |
| | |
| | .login-form { |
| | text-align: center; |
| | padding: 50px 0; |
| | } |
| | |
| | .login-form input[type="password"] { |
| | width: 300px; |
| | padding: 12px; |
| | border: 2px solid #ddd; |
| | border-radius: 5px; |
| | font-size: 16px; |
| | margin-bottom: 20px; |
| | box-sizing: border-box; |
| | } |
| | |
| | .tabs { |
| | display: inline-flex; |
| | background: linear-gradient(145deg, #f5f5f7, #e8e8ed); |
| | padding: 6px; |
| | border-radius: 14px; |
| | margin-bottom: 25px; |
| | border-bottom: none; |
| | gap: 3px; |
| | overflow-x: auto; |
| | max-width: 100%; |
| | user-select: none; |
| | box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.06), |
| | 0 1px 2px rgba(255, 255, 255, 0.9); |
| | position: relative; |
| | } |
| | |
| | |
| | .tab-slider { |
| | position: absolute; |
| | top: 6px; |
| | left: 0; |
| | right: 0; |
| | height: calc(100% - 12px); |
| | background: white; |
| | border-radius: 10px; |
| | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1), |
| | 0 1px 3px rgba(0, 0, 0, 0.06), |
| | inset 0 -1px 0 rgba(0, 0, 0, 0.02); |
| | transition: left 0.3s cubic-bezier(0.4, 0, 0.2, 1), |
| | right 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
| | z-index: 0; |
| | pointer-events: none; |
| | } |
| | |
| | |
| | .tab-slider::after { |
| | content: ''; |
| | position: absolute; |
| | top: 0; |
| | left: 0; |
| | right: 0; |
| | height: 50%; |
| | background: linear-gradient(180deg, |
| | rgba(255, 255, 255, 0.8) 0%, |
| | rgba(255, 255, 255, 0) 100%); |
| | border-radius: 10px 10px 0 0; |
| | pointer-events: none; |
| | opacity: 0.5; |
| | } |
| | |
| | .tabs::-webkit-scrollbar { |
| | height: 4px; |
| | } |
| | |
| | .tabs::-webkit-scrollbar-track { |
| | background: transparent; |
| | } |
| | |
| | .tabs::-webkit-scrollbar-thumb { |
| | background: rgba(0, 0, 0, 0.15); |
| | border-radius: 2px; |
| | } |
| | |
| | .tabs::-webkit-scrollbar-thumb:hover { |
| | background: rgba(0, 0, 0, 0.25); |
| | } |
| | |
| | .tab { |
| | padding: 10px 18px; |
| | cursor: pointer; |
| | border: none; |
| | background: transparent; |
| | border-radius: 10px; |
| | color: #666; |
| | font-size: 14px; |
| | font-weight: 450; |
| | transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); |
| | white-space: nowrap; |
| | position: relative; |
| | overflow: hidden; |
| | z-index: 1; |
| | } |
| | |
| | |
| | .tab::before { |
| | content: ''; |
| | position: absolute; |
| | top: 50%; |
| | left: 50%; |
| | width: 0; |
| | height: 0; |
| | border-radius: 50%; |
| | background: rgba(66, 133, 244, 0.15); |
| | transform: translate(-50%, -50%); |
| | transition: width 0.4s ease, height 0.4s ease; |
| | z-index: -1; |
| | } |
| | |
| | .tab:active::before { |
| | width: 200%; |
| | height: 200%; |
| | } |
| | |
| | .tab.active { |
| | color: #1a1a1a; |
| | font-weight: 550; |
| | transform: translateY(0); |
| | |
| | } |
| | |
| | .tab:hover:not(.active) { |
| | background: rgba(255, 255, 255, 0.6); |
| | color: #333; |
| | transform: translateY(-1px); |
| | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04); |
| | } |
| | |
| | |
| | .tab:active { |
| | transform: scale(0.97) translateY(0); |
| | transition: transform 0.1s ease; |
| | } |
| | |
| | .tab.active:active { |
| | transform: scale(0.98); |
| | box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08), |
| | inset 0 1px 2px rgba(0, 0, 0, 0.02); |
| | } |
| | |
| | |
| | .tab:focus { |
| | outline: none; |
| | } |
| | |
| | .tab:focus-visible { |
| | outline: 2px solid rgba(66, 133, 244, 0.5); |
| | outline-offset: 2px; |
| | } |
| | |
| | .tab-content { |
| | display: none; |
| | width: 100%; |
| | box-sizing: border-box; |
| | |
| | } |
| | |
| | .tab-content.active { |
| | display: block; |
| | } |
| | |
| | .upload-area { |
| | border: 2px dashed #ddd; |
| | border-radius: 5px; |
| | padding: 40px; |
| | text-align: center; |
| | background-color: #fafafa; |
| | margin: 20px 0; |
| | cursor: pointer; |
| | transition: border-color 0.3s; |
| | } |
| | |
| | .upload-area:hover { |
| | border-color: #4285f4; |
| | } |
| | |
| | .upload-area.dragover { |
| | border-color: #4285f4; |
| | background-color: #f0f8ff; |
| | } |
| | |
| | .file-list { |
| | margin: 20px 0; |
| | } |
| | |
| | .file-item { |
| | background-color: #f8f9fa; |
| | border: 1px solid #e1e4e8; |
| | border-radius: 3px; |
| | padding: 10px; |
| | margin: 5px 0; |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: center; |
| | } |
| | |
| | .file-item .file-name { |
| | font-family: monospace; |
| | color: #333; |
| | } |
| | |
| | .file-item .file-size { |
| | color: #666; |
| | font-size: 12px; |
| | } |
| | |
| | .file-item .remove-btn { |
| | background: #dc3545; |
| | color: white; |
| | border: none; |
| | border-radius: 3px; |
| | padding: 2px 8px; |
| | cursor: pointer; |
| | font-size: 12px; |
| | } |
| | |
| | .upload-progress { |
| | background-color: #f8f9fa; |
| | border: 1px solid #e1e4e8; |
| | border-radius: 5px; |
| | padding: 15px; |
| | margin: 20px 0; |
| | } |
| | |
| | .progress-bar { |
| | width: 100%; |
| | height: 20px; |
| | background-color: #e9ecef; |
| | border-radius: 10px; |
| | overflow: hidden; |
| | margin: 10px 0; |
| | } |
| | |
| | .progress-fill { |
| | height: 100%; |
| | background-color: #28a745; |
| | transition: width 0.3s ease; |
| | } |
| | |
| | |
| | .cred-card { |
| | background-color: #f8f9fa; |
| | border: 1px solid #e1e4e8; |
| | border-radius: 8px; |
| | padding: 15px; |
| | margin: 10px 0; |
| | position: relative; |
| | width: 100%; |
| | box-sizing: border-box; |
| | } |
| | |
| | .cred-card.disabled { |
| | background-color: #f5f5f5; |
| | border-color: #ccc; |
| | opacity: 0.7; |
| | } |
| | |
| | .cred-header { |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: flex-start; |
| | margin-bottom: 10px; |
| | gap: 20px; |
| | } |
| | |
| | .cred-filename { |
| | font-family: monospace; |
| | font-weight: bold; |
| | color: #333; |
| | font-size: 14px; |
| | white-space: nowrap; |
| | overflow: hidden; |
| | text-overflow: ellipsis; |
| | min-width: 300px; |
| | } |
| | |
| | .cred-status { |
| | display: flex; |
| | gap: 5px; |
| | } |
| | |
| | .status-badge { |
| | padding: 2px 8px; |
| | border-radius: 12px; |
| | font-size: 12px; |
| | color: white; |
| | } |
| | |
| | .status-badge.enabled { |
| | background-color: #28a745; |
| | } |
| | |
| | .status-badge.disabled { |
| | background-color: #6c757d; |
| | } |
| | |
| | .error-codes { |
| | background-color: #f8d7da; |
| | color: #721c24; |
| | padding: 2px 8px; |
| | border-radius: 12px; |
| | font-size: 12px; |
| | } |
| | |
| | .cooldown-badge { |
| | background-color: #ffc107; |
| | color: #856404; |
| | padding: 2px 8px; |
| | border-radius: 12px; |
| | font-size: 12px; |
| | font-weight: bold; |
| | } |
| | |
| | .cooldown-badge.ready { |
| | background-color: #28a745; |
| | color: white; |
| | } |
| | |
| | .model-cooldown-details { |
| | background-color: #e3f2fd; |
| | border: 1px solid #90caf9; |
| | border-radius: 4px; |
| | padding: 8px; |
| | margin-top: 8px; |
| | font-size: 11px; |
| | color: #1976d2; |
| | } |
| | |
| | .model-cooldown-item { |
| | display: inline-block; |
| | background-color: #64b5f6; |
| | color: white; |
| | padding: 2px 6px; |
| | border-radius: 10px; |
| | margin: 2px; |
| | font-size: 10px; |
| | } |
| | |
| | .cred-actions { |
| | display: flex; |
| | gap: 5px; |
| | flex-wrap: wrap; |
| | } |
| | |
| | .cred-btn { |
| | padding: 4px 12px; |
| | border: none; |
| | border-radius: 4px; |
| | font-size: 12px; |
| | cursor: pointer; |
| | transition: background-color 0.2s; |
| | } |
| | |
| | .cred-btn.enable { |
| | background-color: #28a745; |
| | color: white; |
| | } |
| | |
| | .cred-btn.disable { |
| | background-color: #6c757d; |
| | color: white; |
| | } |
| | |
| | .cred-btn.delete { |
| | background-color: #dc3545; |
| | color: white; |
| | } |
| | |
| | .cred-btn.download { |
| | background-color: #007bff; |
| | color: white; |
| | } |
| | |
| | .cred-btn.view { |
| | background-color: #17a2b8; |
| | color: white; |
| | } |
| | |
| | .cred-details { |
| | margin-top: 10px; |
| | display: none; |
| | } |
| | |
| | .cred-details.show { |
| | display: block; |
| | } |
| | |
| | .cred-content { |
| | background-color: #f0f8ff; |
| | border: 1px solid #b0d4ff; |
| | border-radius: 4px; |
| | padding: 10px; |
| | font-family: monospace; |
| | font-size: 11px; |
| | white-space: pre-wrap; |
| | word-break: break-all; |
| | max-height: 200px; |
| | overflow-y: auto; |
| | } |
| | |
| | |
| | .cred-quota-details { |
| | margin-top: 10px; |
| | animation: slideDown 0.3s ease-out; |
| | } |
| | |
| | @keyframes slideDown { |
| | from { |
| | opacity: 0; |
| | transform: translateY(-10px); |
| | } |
| | |
| | to { |
| | opacity: 1; |
| | transform: translateY(0); |
| | } |
| | } |
| | |
| | .cred-quota-content { |
| | background: linear-gradient(to bottom, #ffffff, #f8f9fa); |
| | border: 2px solid #17a2b8; |
| | border-radius: 8px; |
| | padding: 10px; |
| | box-shadow: 0 2px 8px rgba(23, 162, 184, 0.15); |
| | } |
| | |
| | .manage-actions { |
| | margin-bottom: 20px; |
| | display: flex; |
| | gap: 10px; |
| | flex-wrap: wrap; |
| | } |
| | |
| | |
| | .stats-container { |
| | display: flex; |
| | gap: 15px; |
| | margin-bottom: 20px; |
| | flex-wrap: wrap; |
| | } |
| | |
| | .stat-item { |
| | background: linear-gradient(45deg, #f8f9fa, #e9ecef); |
| | border: 1px solid #dee2e6; |
| | border-radius: 8px; |
| | padding: 12px 20px; |
| | text-align: center; |
| | min-width: 120px; |
| | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); |
| | } |
| | |
| | .stat-number { |
| | font-size: 24px; |
| | font-weight: bold; |
| | color: #333; |
| | display: block; |
| | } |
| | |
| | .stat-label { |
| | font-size: 12px; |
| | color: #666; |
| | text-transform: uppercase; |
| | margin-top: 4px; |
| | } |
| | |
| | .stat-item.total { |
| | border-left: 4px solid #007bff; |
| | } |
| | |
| | .stat-item.normal { |
| | border-left: 4px solid #28a745; |
| | } |
| | |
| | .stat-item.disabled { |
| | border-left: 4px solid #6c757d; |
| | } |
| | |
| | .filter-container { |
| | display: flex; |
| | gap: 10px; |
| | margin-bottom: 20px; |
| | align-items: center; |
| | flex-wrap: wrap; |
| | } |
| | |
| | .filter-select { |
| | padding: 8px 12px; |
| | border: 2px solid #ddd; |
| | border-radius: 4px; |
| | font-size: 14px; |
| | background-color: white; |
| | } |
| | |
| | .filter-select:focus { |
| | border-color: #4285f4; |
| | outline: none; |
| | } |
| | |
| | .pagination-container { |
| | display: flex; |
| | justify-content: center; |
| | align-items: center; |
| | gap: 10px; |
| | margin: 20px 0; |
| | flex-wrap: wrap; |
| | } |
| | |
| | .pagination-info { |
| | color: #666; |
| | font-size: 14px; |
| | } |
| | |
| | .pagination-btn { |
| | padding: 8px 12px; |
| | border: 1px solid #ddd; |
| | background: white; |
| | cursor: pointer; |
| | border-radius: 4px; |
| | font-size: 14px; |
| | } |
| | |
| | .pagination-btn:hover:not(:disabled) { |
| | background: #f8f9fa; |
| | border-color: #4285f4; |
| | } |
| | |
| | .pagination-btn:disabled { |
| | background: #f5f5f5; |
| | color: #ccc; |
| | cursor: not-allowed; |
| | } |
| | |
| | .pagination-btn.active { |
| | background: #4285f4; |
| | color: white; |
| | border-color: #4285f4; |
| | } |
| | |
| | .page-size-select { |
| | padding: 6px 10px; |
| | border: 1px solid #ddd; |
| | border-radius: 4px; |
| | font-size: 14px; |
| | } |
| | |
| | .refresh-btn { |
| | background-color: #17a2b8; |
| | color: white; |
| | padding: 8px 16px; |
| | border: none; |
| | border-radius: 4px; |
| | cursor: pointer; |
| | } |
| | |
| | .download-all-btn { |
| | background-color: #28a745; |
| | color: white; |
| | padding: 8px 16px; |
| | border: none; |
| | border-radius: 4px; |
| | cursor: pointer; |
| | } |
| | |
| | |
| | .batch-controls { |
| | background-color: #f8f9fa; |
| | border: 1px solid #e1e4e8; |
| | border-radius: 8px; |
| | padding: 15px; |
| | margin-bottom: 20px; |
| | } |
| | |
| | .checkbox-container { |
| | display: flex; |
| | align-items: center; |
| | margin-right: 15px; |
| | } |
| | |
| | .batch-actions { |
| | display: flex; |
| | gap: 10px; |
| | flex-wrap: wrap; |
| | align-items: center; |
| | margin-top: 10px; |
| | } |
| | |
| | .batch-btn { |
| | padding: 6px 12px; |
| | border: none; |
| | border-radius: 4px; |
| | font-size: 12px; |
| | cursor: pointer; |
| | transition: background-color 0.2s; |
| | } |
| | |
| | .batch-btn.batch-enable { |
| | background-color: #28a745; |
| | color: white; |
| | } |
| | |
| | .batch-btn.batch-disable { |
| | background-color: #6c757d; |
| | color: white; |
| | } |
| | |
| | .batch-btn.batch-delete { |
| | background-color: #dc3545; |
| | color: white; |
| | } |
| | |
| | .batch-btn.batch-email { |
| | background-color: #17a2b8; |
| | color: white; |
| | } |
| | |
| | .batch-btn:disabled { |
| | background-color: #e9ecef; |
| | color: #6c757d; |
| | cursor: not-allowed; |
| | } |
| | |
| | .cred-btn.email { |
| | background-color: #17a2b8; |
| | color: white; |
| | } |
| | |
| | .cred-btn.email:hover { |
| | background-color: #138496; |
| | } |
| | |
| | .selected-count { |
| | font-weight: bold; |
| | color: #007bff; |
| | margin-right: 10px; |
| | } |
| | |
| | .select-all-checkbox { |
| | margin-right: 8px; |
| | transform: scale(1.2); |
| | } |
| | |
| | |
| | .error-filter-container { |
| | display: flex; |
| | gap: 10px; |
| | align-items: center; |
| | flex-wrap: wrap; |
| | } |
| | |
| | .error-code-badge { |
| | display: inline-block; |
| | background-color: #dc3545; |
| | color: white; |
| | padding: 2px 6px; |
| | border-radius: 10px; |
| | font-size: 11px; |
| | margin: 1px; |
| | cursor: pointer; |
| | transition: background-color 0.2s; |
| | } |
| | |
| | .error-code-badge:hover { |
| | background-color: #c82333; |
| | } |
| | |
| | .error-code-badge.selected { |
| | background-color: #007bff; |
| | } |
| | |
| | |
| | .config-group { |
| | background-color: #f8f9fa; |
| | border: 1px solid #e1e4e8; |
| | border-radius: 8px; |
| | padding: 20px; |
| | margin: 15px 0; |
| | } |
| | |
| | .config-group h4 { |
| | margin-top: 0; |
| | margin-bottom: 15px; |
| | color: #333; |
| | border-bottom: 1px solid #e1e4e8; |
| | padding-bottom: 8px; |
| | } |
| | |
| | .config-input { |
| | width: 100%; |
| | padding: 8px 12px; |
| | border: 2px solid #ddd; |
| | border-radius: 4px; |
| | font-size: 14px; |
| | box-sizing: border-box; |
| | margin-bottom: 5px; |
| | } |
| | |
| | .config-input:focus { |
| | border-color: #4285f4; |
| | outline: none; |
| | } |
| | |
| | .config-input:disabled { |
| | background-color: #f5f5f5; |
| | color: #666; |
| | cursor: not-allowed; |
| | } |
| | |
| | .config-checkbox { |
| | margin-right: 8px; |
| | transform: scale(1.2); |
| | } |
| | |
| | .config-note { |
| | display: block; |
| | color: #666; |
| | font-size: 12px; |
| | margin-bottom: 10px; |
| | font-style: italic; |
| | } |
| | |
| | .config-info { |
| | background-color: #e3f2fd; |
| | border: 1px solid #1976d2; |
| | border-radius: 4px; |
| | padding: 12px; |
| | margin-top: 8px; |
| | font-size: 13px; |
| | color: #1565c0; |
| | } |
| | |
| | .config-info ul { |
| | margin: 8px 0 4px 0; |
| | color: #424242; |
| | } |
| | |
| | .config-info li { |
| | margin: 3px 0; |
| | } |
| | |
| | .env-locked { |
| | position: relative; |
| | } |
| | |
| | .env-locked::after { |
| | content: "🔒 环境变量锚定"; |
| | position: absolute; |
| | right: 10px; |
| | top: 50%; |
| | transform: translateY(-50%); |
| | background-color: #ffc107; |
| | color: #212529; |
| | padding: 2px 6px; |
| | border-radius: 3px; |
| | font-size: 11px; |
| | pointer-events: none; |
| | } |
| | |
| | |
| | .usage-card { |
| | background-color: #f8f9fa; |
| | border: 1px solid #e1e4e8; |
| | border-radius: 8px; |
| | padding: 15px; |
| | margin: 10px 0; |
| | position: relative; |
| | } |
| | |
| | .usage-header { |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: center; |
| | margin-bottom: 15px; |
| | } |
| | |
| | .usage-filename { |
| | font-family: monospace; |
| | font-weight: bold; |
| | color: #333; |
| | font-size: 14px; |
| | } |
| | |
| | .usage-progress { |
| | margin: 10px 0; |
| | } |
| | |
| | .usage-progress-label { |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: center; |
| | margin-bottom: 5px; |
| | font-size: 12px; |
| | } |
| | |
| | .usage-progress-bar { |
| | width: 100%; |
| | height: 20px; |
| | background-color: #e9ecef; |
| | border-radius: 10px; |
| | overflow: hidden; |
| | position: relative; |
| | } |
| | |
| | .usage-progress-fill { |
| | height: 100%; |
| | transition: width 0.3s ease; |
| | } |
| | |
| | .usage-progress-fill.gemini { |
| | background-color: #ff6b35; |
| | } |
| | |
| | .usage-progress-fill.total { |
| | background-color: #007bff; |
| | } |
| | |
| | .usage-progress-fill.warning { |
| | background-color: #ffc107; |
| | } |
| | |
| | .usage-progress-fill.danger { |
| | background-color: #dc3545; |
| | } |
| | |
| | .usage-actions { |
| | display: flex; |
| | gap: 5px; |
| | margin-top: 10px; |
| | } |
| | |
| | .usage-btn { |
| | padding: 4px 8px; |
| | border: none; |
| | border-radius: 4px; |
| | font-size: 11px; |
| | cursor: pointer; |
| | transition: background-color 0.2s; |
| | } |
| | |
| | .usage-btn.reset { |
| | background-color: #6c757d; |
| | color: white; |
| | } |
| | |
| | .usage-btn.limits { |
| | background-color: #17a2b8; |
| | color: white; |
| | } |
| | |
| | .usage-info { |
| | display: grid; |
| | grid-template-columns: 1fr 1fr; |
| | gap: 10px; |
| | margin-top: 10px; |
| | font-size: 12px; |
| | } |
| | |
| | .usage-info-item { |
| | background-color: #ffffff; |
| | padding: 8px; |
| | border-radius: 4px; |
| | border: 1px solid #dee2e6; |
| | } |
| | |
| | .usage-info-label { |
| | font-weight: bold; |
| | color: #666; |
| | display: block; |
| | margin-bottom: 2px; |
| | } |
| | |
| | .usage-info-value { |
| | color: #333; |
| | } |
| | |
| | .reset-time { |
| | font-size: 11px; |
| | color: #666; |
| | font-style: italic; |
| | margin-top: 5px; |
| | } |
| | |
| | |
| | .modal { |
| | display: none; |
| | position: fixed; |
| | z-index: 1000; |
| | left: 0; |
| | top: 0; |
| | width: 100%; |
| | height: 100%; |
| | background-color: rgba(0, 0, 0, 0.5); |
| | } |
| | |
| | .modal-content { |
| | background-color: #fefefe; |
| | margin: 15% auto; |
| | padding: 20px; |
| | border-radius: 8px; |
| | width: 400px; |
| | max-width: 90%; |
| | } |
| | |
| | .modal-header { |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: center; |
| | margin-bottom: 15px; |
| | padding-bottom: 10px; |
| | border-bottom: 1px solid #dee2e6; |
| | } |
| | |
| | .modal-title { |
| | margin: 0; |
| | font-size: 16px; |
| | color: #333; |
| | } |
| | |
| | .modal-close { |
| | background: none; |
| | border: none; |
| | font-size: 20px; |
| | cursor: pointer; |
| | color: #999; |
| | } |
| | |
| | .modal-close:hover { |
| | color: #333; |
| | } |
| | |
| | .modal-body { |
| | margin-bottom: 15px; |
| | } |
| | |
| | .modal-footer { |
| | display: flex; |
| | gap: 10px; |
| | justify-content: flex-end; |
| | } |
| | </style> |
| | </head> |
| |
|
| | <body> |
| | <div class="container"> |
| |
|
| | |
| | <div id="loginSection" class="login-form"> |
| | <h1>GCLI2API 管理面板</h1> |
| | <p>请输入访问密码:</p> |
| | <input type="password" id="loginPassword" placeholder="输入密码" onkeypress="handlePasswordEnter(event)" /> |
| | <br> |
| | <button class="btn" onclick="login()">登录</button> |
| | </div> |
| |
|
| | |
| | <div id="mainSection" class="hidden"> |
| | <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; flex-wrap: wrap; gap: 10px;"> |
| | <div style="display: flex; align-items: center; gap: 15px; flex-wrap: wrap;"> |
| | <h1 style="margin: 0;">GCLI2API 管理面板</h1> |
| | <span id="versionInfo" style="font-size: 12px; color: #666;"> |
| | <span id="versionText">加载中...</span> |
| | </span> |
| | <button onclick="checkForUpdates()" id="checkUpdateBtn" |
| | style="padding: 4px 12px; background-color: #17a2b8; color: white; border: none; border-radius: 4px; font-size: 12px; cursor: pointer; white-space: nowrap;"> |
| | 检查更新 |
| | </button> |
| | </div> |
| | <button onclick="logout()" |
| | style="padding: 8px 20px; background-color: #dc3545; color: white; border: none; border-radius: 5px; font-size: 14px; cursor: pointer;"> |
| | 退出登录 |
| | </button> |
| | </div> |
| |
|
| | |
| | <div class="tabs"> |
| | <div class="tab-slider"></div> |
| | <button class="tab active" onclick="switchTab('oauth')">OAuth认证</button> |
| | <button class="tab" onclick="switchTab('antigravity')">Antigravity认证</button> |
| | <button class="tab" onclick="switchTab('upload')">批量上传</button> |
| | <button class="tab" onclick="switchTab('manage')">GCLI凭证管理</button> |
| | <button class="tab" onclick="switchTab('antigravity-manage')">Antigravity凭证管理</button> |
| | <button class="tab" onclick="switchTab('config')">配置管理</button> |
| | <button class="tab" onclick="switchTab('logs')">实时日志</button> |
| | <button class="tab" onclick="switchTab('about')">项目信息</button> |
| | </div> |
| |
|
| | |
| | <div id="oauthTab" class="tab-content active"> |
| | |
| | <div class="status success" style="margin-bottom: 20px;"> |
| | <strong>✨ 自动化优化:</strong> 系统现在会在认证成功后自动为您的项目启用必需的API服务 |
| | <ul style="margin: 10px 0; padding-left: 20px;"> |
| | <li><strong>Gemini Cloud Assist API</strong></li> |
| | <li><strong>Gemini for Google Cloud API</strong></li> |
| | </ul> |
| | <p style="margin: 10px 0; color: #155724;"><strong>说明:</strong>无需手动启用API,系统会自动处理这些配置步骤,让认证流程更加顺畅。 |
| | </p> |
| | </div> |
| |
|
| | |
| | <div class="form-group"> |
| | <div style="cursor: pointer; user-select: none; padding: 12px; border: 2px solid #ddd; border-radius: 5px; background: #f8f9fa; display: flex; justify-content: space-between; align-items: center;" |
| | onclick="toggleProjectIdSection()"> |
| | <span style="font-weight: bold; color: #555;">📁 高级选项:Google Cloud Project ID |
| | (不用管,直接点击获取链接即可)</span> |
| | <span id="projectIdToggleIcon" |
| | style="font-size: 14px; color: #666; transition: transform 0.3s ease;">▶</span> |
| | </div> |
| | <div id="projectIdSection" |
| | style="display: none; margin-top: 15px; padding: 15px; border: 2px solid #ddd; border-top: none; border-radius: 0 0 5px 5px; background: #ffffff;"> |
| | <label for="projectId" |
| | style="display: block; margin-bottom: 5px; font-weight: bold; color: #555;">Google Cloud |
| | Project ID (可选):</label> |
| | <input type="text" id="projectId" |
| | style="width: 100%; padding: 10px; border: 2px solid #ddd; border-radius: 5px; font-size: 16px; box-sizing: border-box;" |
| | placeholder="留空将尝试自动检测,或手动输入项目ID" /> |
| | <small style="color: #666; font-size: 12px; margin-top: 5px; display: block;"> |
| | 💡 提示:如果你不懂这是什么,可以留空此字段让系统自动检测项目ID |
| | </small> |
| | </div> |
| | </div> |
| |
|
| | <button class="btn" id="getAuthBtn" onclick="startAuth()">获取认证链接</button> |
| |
|
| | <div id="authUrlSection" class="hidden"> |
| | <h3>认证链接:</h3> |
| | <div class="auth-url"> |
| | <a id="authUrl" href="#" target="_blank">点击此链接进行认证</a> |
| | </div> |
| | <div class="status info"> |
| | <strong>重要说明:</strong> |
| | <ol style="margin: 10px 0; padding-left: 20px;"> |
| | <li>点击上方认证链接,会在新窗口中打开Google OAuth页面</li> |
| | <li>完成Google账号登录和授权</li> |
| | <li>授权成功后会跳转到localhost:11451显示成功页面</li> |
| | <li>关闭OAuth窗口,返回本页面</li> |
| | <li>点击下方"获取认证文件"按钮完成流程</li> |
| | </ol> |
| | </div> |
| |
|
| | |
| | <div class="form-group" |
| | style="margin: 20px 0; padding: 15px; border: 2px solid #e8f4fd; border-radius: 8px; background: #f8fcff;"> |
| | <div style="cursor: pointer; user-select: none; display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;" |
| | onclick="toggleCallbackUrlSection()"> |
| | <span style="font-weight: bold; color: #0066cc;">🚀 无法回源?试试快捷方式</span> |
| | <span id="callbackUrlToggleIcon" |
| | style="font-size: 14px; color: #666; transition: transform 0.3s ease;">▼</span> |
| | </div> |
| | <div id="callbackUrlSection" style="display: none;"> |
| | <div |
| | style="background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 6px; padding: 12px; margin-bottom: 12px;"> |
| | <div style="color: #856404; font-size: 14px; font-weight: bold; margin-bottom: 6px;">📚 |
| | 适用场景:</div> |
| | <ul |
| | style="color: #856404; font-size: 13px; margin: 0; padding-left: 18px; line-height: 1.5;"> |
| | <li>云服务器、VPS等非本地环境</li> |
| | <li>防火墙阻止了11451端口访问</li> |
| | <li>网络环境无法正常回源到localhost</li> |
| | <li>Docker容器内运行,端口映射问题</li> |
| | </ul> |
| | </div> |
| | <div style="color: #666; font-size: 13px; margin-bottom: 12px; line-height: 1.6;"> |
| | <strong style="color: #0066cc;">🔍 什么是回调URL?</strong><br> |
| | 完成Google OAuth授权后,浏览器地址栏显示的完整URL,通常看起来像这样:<br> |
| | <code |
| | style="background: #f1f3f4; padding: 2px 6px; border-radius: 3px; font-size: 12px; word-break: break-all;"> |
| | http://localhost:11451/?state=abc123...&code=4/0AVMBsJ...&scope=email%20profile... |
| | </code> |
| | </div> |
| | <div |
| | style="background: #e7f3ff; border: 1px solid #b3d9ff; border-radius: 6px; padding: 10px; margin-bottom: 12px;"> |
| | <div style="color: #0066cc; font-size: 13px; font-weight: bold; margin-bottom: 4px;">📋 |
| | 使用步骤:</div> |
| | <ol |
| | style="color: #0066cc; font-size: 12px; margin: 0; padding-left: 18px; line-height: 1.4;"> |
| | <li>点击上方认证链接,完成Google授权</li> |
| | <li>授权成功后,复制浏览器地址栏的<strong>完整URL</strong></li> |
| | <li>粘贴到下方输入框,点击获取凭证即可</li> |
| | </ol> |
| | </div> |
| | <div class="input-group"> |
| | <input type="url" id="callbackUrlInput" |
| | placeholder="粘贴完整的回调URL,例如:http://localhost:11451/?state=xxx&code=xxx&scope=xxx..." |
| | style="width: 100%; padding: 10px; border: 2px solid #ddd; border-radius: 4px; font-size: 13px;"> |
| | </div> |
| | <button class="btn" style="margin-top: 10px; background: #28a745; border-color: #28a745;" |
| | onclick="processCallbackUrl()"> |
| | 从回调URL获取凭证 |
| | </button> |
| | </div> |
| | </div> |
| |
|
| | <button class="btn" id="getCredsBtn" onclick="getCredentials()">获取认证文件</button> |
| | </div> |
| |
|
| | <div id="credentialsSection" class="hidden"> |
| | <h3>认证文件内容:</h3> |
| | <div class="credentials" id="credentialsContent"></div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div id="antigravityTab" class="tab-content"> |
| | <div class="status info" style="margin-bottom: 20px;"> |
| | <strong>🚀 Antigravity 认证模式</strong> |
| | <p style="margin: 10px 0;"> |
| | 获取谷歌Antigravity 凭证 |
| | </p> |
| | </div> |
| |
|
| | <button class="btn" id="getAntigravityAuthBtn">获取 Antigravity 认证链接</button> |
| |
|
| | <div id="antigravityAuthUrlSection" class="hidden"> |
| | <h3>Antigravity 认证链接:</h3> |
| | <div class="auth-url"> |
| | <a id="antigravityAuthUrl" href="#" target="_blank">点击此链接进行认证</a> |
| | </div> |
| | <div class="status info"> |
| | <strong>使用说明:</strong> |
| | <ol style="margin: 10px 0; padding-left: 20px;"> |
| | <li>点击上方认证链接,在新窗口中完成 Google 授权</li> |
| | <li>授权成功后会跳转到 localhost 显示成功页面</li> |
| | <li>关闭 OAuth 窗口,返回本页面</li> |
| | <li>点击下方"获取凭证"按钮完成流程</li> |
| | </ol> |
| | </div> |
| |
|
| | |
| | <div class="form-group" |
| | style="margin: 20px 0; padding: 15px; border: 2px solid #e8f4fd; border-radius: 8px; background: #f8fcff;"> |
| | <div style="cursor: pointer; user-select: none; display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;" |
| | onclick="toggleAntigravityCallbackUrlSection()"> |
| | <span style="font-weight: bold; color: #0066cc;">🚀 无法回源?试试快捷方式</span> |
| | <span id="antigravityCallbackUrlToggleIcon" |
| | style="font-size: 14px; color: #666; transition: transform 0.3s ease;">▼</span> |
| | </div> |
| | <div id="antigravityCallbackUrlSection" style="display: none;"> |
| | <div |
| | style="background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 6px; padding: 12px; margin-bottom: 12px;"> |
| | <div style="color: #856404; font-size: 14px; font-weight: bold; margin-bottom: 6px;">📚 |
| | 适用场景:</div> |
| | <ul |
| | style="color: #856404; font-size: 13px; margin: 0; padding-left: 18px; line-height: 1.5;"> |
| | <li>云服务器、VPS等非本地环境</li> |
| | <li>防火墙阻止了11451端口访问</li> |
| | <li>网络环境无法正常回源到localhost</li> |
| | <li>Docker容器内运行,端口映射问题</li> |
| | </ul> |
| | </div> |
| | <div style="color: #666; font-size: 13px; margin-bottom: 12px; line-height: 1.6;"> |
| | <strong style="color: #0066cc;">🔍 什么是回调URL?</strong><br> |
| | 完成Google OAuth授权后,浏览器地址栏显示的完整URL,通常看起来像这样:<br> |
| | <code |
| | style="background: #f1f3f4; padding: 2px 6px; border-radius: 3px; font-size: 12px; word-break: break-all;"> |
| | http://localhost:11451/?state=abc123...&code=4/0AVMBsJ...&scope=email%20profile... |
| | </code> |
| | </div> |
| | <div |
| | style="background: #e7f3ff; border: 1px solid #b3d9ff; border-radius: 6px; padding: 10px; margin-bottom: 12px;"> |
| | <div style="color: #0066cc; font-size: 13px; font-weight: bold; margin-bottom: 4px;">📋 |
| | 使用步骤:</div> |
| | <ol |
| | style="color: #0066cc; font-size: 12px; margin: 0; padding-left: 18px; line-height: 1.4;"> |
| | <li>点击上方认证链接,完成Google授权</li> |
| | <li>授权成功后,复制浏览器地址栏的<strong>完整URL</strong></li> |
| | <li>粘贴到下方输入框,点击获取凭证即可</li> |
| | </ol> |
| | </div> |
| | <div class="input-group"> |
| | <input type="url" id="antigravityCallbackUrlInput" |
| | placeholder="粘贴完整的回调URL,例如:http://localhost:11451/?state=xxx&code=xxx&scope=xxx..." |
| | style="width: 100%; padding: 10px; border: 2px solid #ddd; border-radius: 4px; font-size: 13px;"> |
| | </div> |
| | <button class="btn" style="margin-top: 10px; background: #28a745; border-color: #28a745;" |
| | onclick="processAntigravityCallbackUrl()"> |
| | 从回调URL获取凭证 |
| | </button> |
| | </div> |
| | </div> |
| |
|
| | <button class="btn" id="getAntigravityCredsBtn" onclick="getAntigravityCredentials()">获取 Antigravity |
| | 凭证</button> |
| |
|
| | <div id="antigravityCredsSection" class="hidden"> |
| | <h3>Antigravity 凭证内容:</h3> |
| | <div class="credentials"> |
| | <pre id="antigravityCredsContent"></pre> |
| | </div> |
| | <button class="btn" onclick="downloadAntigravityCredentials()">下载凭证文件</button> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div id="uploadTab" class="tab-content"> |
| | <h3>批量上传认证文件</h3> |
| | <p>支持批量上传 GCLI 和 Antigravity 认证文件</p> |
| |
|
| | |
| | <div |
| | style="margin-bottom: 30px; padding: 20px; border: 2px solid #e1e4e8; border-radius: 8px; background: #f8f9fa;"> |
| | <h4 style="margin-top: 0; color: #007bff; border-bottom: 2px solid #007bff; padding-bottom: 10px;"> |
| | 📤 GCLI 凭证批量上传</h4> |
| |
|
| | <div class="upload-area" id="uploadArea" onclick="document.getElementById('fileInput').click()"> |
| | <p>点击选择文件或拖拽文件到此区域</p> |
| | <p style="color: #666; font-size: 14px;">支持 .json 和 .zip 格式文件</p> |
| | <p style="color: #888; font-size: 12px;">ZIP文件会自动解压提取其中的JSON凭证</p> |
| | </div> |
| |
|
| | <input type="file" id="fileInput" multiple accept=".json,.zip" style="display: none;" |
| | onchange="handleFileSelect(event)" /> |
| |
|
| | <div id="fileListSection" class="hidden"> |
| | <h4>选择的文件:</h4> |
| | <div class="file-list" id="fileList"></div> |
| | <button class="btn" onclick="uploadFiles()">上传文件</button> |
| | <button class="btn" style="background-color: #6c757d;" onclick="clearFiles()">清空列表</button> |
| | </div> |
| |
|
| | <div id="uploadProgressSection" class="hidden"> |
| | <div class="upload-progress"> |
| | <h4>上传进度:</h4> |
| | <div class="progress-bar"> |
| | <div class="progress-fill" id="progressFill" style="width: 0%"></div> |
| | </div> |
| | <p id="progressText">0%</p> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div style="padding: 20px; border: 2px solid #e1e4e8; border-radius: 8px; background: #f8f9fa;"> |
| | <h4 style="margin-top: 0; color: #28a745; border-bottom: 2px solid #28a745; padding-bottom: 10px;"> |
| | 📤 Antigravity 凭证批量上传</h4> |
| |
|
| | <div class="upload-area" style="border-color: #28a745;" |
| | onclick="document.getElementById('antigravityFileInput').click()" |
| | ondragover="event.preventDefault(); this.style.borderColor='#28a745'; this.style.backgroundColor='#e7f9e7';" |
| | ondragleave="this.style.borderColor='#ddd'; this.style.backgroundColor='#fafafa';" |
| | ondrop="handleAntigravityFileDrop(event)"> |
| | <p>点击选择文件或拖拽文件到此区域</p> |
| | <p style="color: #666; font-size: 14px;">支持 .json 和 .zip 格式文件</p> |
| | <p style="color: #888; font-size: 12px;">ZIP文件会自动解压提取其中的JSON凭证</p> |
| | </div> |
| |
|
| | <input type="file" id="antigravityFileInput" multiple accept=".json,.zip" style="display: none;" |
| | onchange="handleAntigravityFileSelect(event)" /> |
| |
|
| | <div id="antigravityFileListSection" class="hidden"> |
| | <h4>选择的文件:</h4> |
| | <div class="file-list" id="antigravityFileList"></div> |
| | <button class="btn" style="background-color: #28a745;" |
| | onclick="uploadAntigravityFiles()">上传文件</button> |
| | <button class="btn" style="background-color: #6c757d;" |
| | onclick="clearAntigravityFiles()">清空列表</button> |
| | </div> |
| |
|
| | <div id="antigravityUploadProgressSection" class="hidden"> |
| | <div class="upload-progress"> |
| | <h4>上传进度:</h4> |
| | <div class="progress-bar"> |
| | <div class="progress-fill" id="antigravityProgressFill" style="width: 0%"></div> |
| | </div> |
| | <p id="antigravityProgressText">0%</p> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div id="manageTab" class="tab-content"> |
| | <h3>GCLI凭证文件管理</h3> |
| | <p>管理所有GCLI认证文件,查看状态和执行操作</p> |
| |
|
| | |
| | <div class="status info" style="margin-bottom: 20px;"> |
| | <strong>💡 检验功能说明:</strong> |
| | <p style="margin: 10px 0;"> |
| | 点击每个凭证的"检验"按钮可以重新获取Project ID。<br> |
| | <strong style="color: #0c5460;">✅ 检验成功可以恢复403错误</strong>,让凭证重新正常工作。<br> |
| | 建议在遇到403错误时使用此功能。 |
| | </p> |
| | </div> |
| |
|
| | |
| | <div class="stats-container" id="statsContainer"> |
| | <div class="stat-item total"> |
| | <span class="stat-number" id="statTotal">0</span> |
| | <span class="stat-label">总计</span> |
| | </div> |
| | <div class="stat-item normal"> |
| | <span class="stat-number" id="statNormal">0</span> |
| | <span class="stat-label">正常</span> |
| | </div> |
| | <div class="stat-item disabled"> |
| | <span class="stat-number" id="statDisabled">0</span> |
| | <span class="stat-label">禁用</span> |
| | </div> |
| | </div> |
| |
|
| | <div class="manage-actions"> |
| | <button class="refresh-btn" onclick="refreshCredsStatus()">刷新状态</button> |
| | <button class="download-all-btn" onclick="downloadAllCreds()">打包下载所有文件</button> |
| | </div> |
| |
|
| | |
| | <div class="batch-controls"> |
| | <h4 style="margin-top: 0; margin-bottom: 10px;">批量操作</h4> |
| | <div class="batch-actions"> |
| | <div class="checkbox-container"> |
| | <input type="checkbox" id="selectAllCheckbox" class="select-all-checkbox" |
| | onchange="toggleSelectAll()"> |
| | <label for="selectAllCheckbox">全选</label> |
| | </div> |
| | <span class="selected-count" id="selectedCount">已选择 0 项</span> |
| | <button class="batch-btn batch-enable" id="batchEnableBtn" onclick="batchAction('enable')" |
| | disabled>批量启用</button> |
| | <button class="batch-btn batch-disable" id="batchDisableBtn" onclick="batchAction('disable')" |
| | disabled>批量禁用</button> |
| | <button class="batch-btn batch-delete" id="batchDeleteBtn" onclick="batchAction('delete')" |
| | disabled>批量删除</button> |
| | <button class="batch-btn" style="background-color: #ff9800;" id="batchVerifyBtn" |
| | onclick="batchVerifyProjectIds()" disabled>批量检验</button> |
| | <button class="batch-btn batch-email" onclick="refreshAllEmails()">刷新所有邮箱</button> |
| | <button class="batch-btn" style="background-color: #e91e63;" |
| | onclick="deduplicateByEmail()">凭证一键去重</button> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="filter-container"> |
| | <label for="statusFilter">凭证状态:</label> |
| | <select id="statusFilter" class="filter-select" onchange="applyStatusFilter()"> |
| | <option value="all">全部凭证</option> |
| | <option value="enabled">仅启用</option> |
| | <option value="disabled">仅禁用</option> |
| | </select> |
| |
|
| | <label for="errorCodeFilter" style="margin-left: 20px;">错误码:</label> |
| | <select id="errorCodeFilter" class="filter-select" onchange="applyStatusFilter()"> |
| | <option value="all">全部</option> |
| | <option value="400">400</option> |
| | <option value="403">403</option> |
| | <option value="429">429</option> |
| | <option value="500">500</option> |
| | </select> |
| |
|
| | <label for="cooldownFilter" style="margin-left: 20px;">冷却状态:</label> |
| | <select id="cooldownFilter" class="filter-select" onchange="applyStatusFilter()"> |
| | <option value="all">全部</option> |
| | <option value="in_cooldown">CD中</option> |
| | <option value="no_cooldown">未CD</option> |
| | </select> |
| |
|
| | <label for="pageSizeSelect" style="margin-left: 20px;">每页显示:</label> |
| | <select id="pageSizeSelect" class="page-size-select" onchange="changePageSize()"> |
| | <option value="20">20</option> |
| | <option value="50">50</option> |
| | <option value="100">100</option> |
| | <option value="200">200</option> |
| | <option value="500">500</option> |
| | <option value="1000">1000</option> |
| | </select> |
| | </div> |
| |
|
| | <div id="credsListSection"> |
| | <div class="loading" id="credsLoading">正在加载凭证文件...</div> |
| | <div id="credsList"></div> |
| |
|
| | |
| | <div class="pagination-container" id="paginationContainer" style="display: none;"> |
| | <button class="pagination-btn" id="prevPageBtn" onclick="changePage(-1)">上一页</button> |
| | <div class="pagination-info" id="paginationInfo">第 1 页,共 1 页</div> |
| | <button class="pagination-btn" id="nextPageBtn" onclick="changePage(1)">下一页</button> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div id="antigravity-manageTab" class="tab-content"> |
| | <h3>Antigravity凭证文件管理</h3> |
| | <p>管理所有Antigravity认证文件,查看状态和执行操作</p> |
| |
|
| | |
| | <div class="status info" style="margin-bottom: 20px;"> |
| | <strong>💡 检验功能说明:</strong> |
| | <p style="margin: 10px 0;"> |
| | 点击每个凭证的"检验"按钮可以重新获取Project ID。<br> |
| | <strong style="color: #0c5460;">✅ 检验成功可以恢复403错误</strong>,让凭证重新正常工作。<br> |
| | 建议在遇到403错误时使用此功能。 |
| | </p> |
| | </div> |
| |
|
| | |
| | <div class="stats-container" id="antigravityStatsContainer"> |
| | <div class="stat-item total"> |
| | <span class="stat-number" id="antigravityStatTotal">0</span> |
| | <span class="stat-label">总计</span> |
| | </div> |
| | <div class="stat-item normal"> |
| | <span class="stat-number" id="antigravityStatNormal">0</span> |
| | <span class="stat-label">正常</span> |
| | </div> |
| | <div class="stat-item disabled"> |
| | <span class="stat-number" id="antigravityStatDisabled">0</span> |
| | <span class="stat-label">禁用</span> |
| | </div> |
| | </div> |
| |
|
| | <div class="manage-actions"> |
| | <button class="refresh-btn" onclick="refreshAntigravityCredsList()">刷新状态</button> |
| | <button class="download-all-btn" onclick="downloadAllAntigravityCreds()">打包下载所有文件</button> |
| | </div> |
| |
|
| | |
| | <div class="batch-controls"> |
| | <h4 style="margin-top: 0; margin-bottom: 10px;">批量操作</h4> |
| | <div class="batch-actions"> |
| | <div class="checkbox-container"> |
| | <input type="checkbox" id="selectAllAntigravityCheckbox" class="select-all-checkbox" |
| | onchange="toggleSelectAllAntigravity()"> |
| | <label for="selectAllAntigravityCheckbox">全选</label> |
| | </div> |
| | <span class="selected-count" id="antigravitySelectedCount">已选择 0 项</span> |
| | <button class="batch-btn batch-enable" id="antigravityBatchEnableBtn" |
| | onclick="batchAntigravityAction('enable')" disabled>批量启用</button> |
| | <button class="batch-btn batch-disable" id="antigravityBatchDisableBtn" |
| | onclick="batchAntigravityAction('disable')" disabled>批量禁用</button> |
| | <button class="batch-btn batch-delete" id="antigravityBatchDeleteBtn" |
| | onclick="batchAntigravityAction('delete')" disabled>批量删除</button> |
| | <button class="batch-btn" style="background-color: #ff9800;" id="antigravityBatchVerifyBtn" |
| | onclick="batchVerifyAntigravityProjectIds()" disabled>批量检验</button> |
| | <button class="batch-btn batch-email" onclick="refreshAllAntigravityEmails()">刷新所有邮箱</button> |
| | <button class="batch-btn" style="background-color: #e91e63;" |
| | onclick="deduplicateAntigravityByEmail()">凭证一键去重</button> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="filter-container"> |
| | <label for="antigravityStatusFilter">凭证状态:</label> |
| | <select id="antigravityStatusFilter" class="filter-select" |
| | onchange="applyAntigravityStatusFilter()"> |
| | <option value="all">全部凭证</option> |
| | <option value="enabled">仅启用</option> |
| | <option value="disabled">仅禁用</option> |
| | </select> |
| |
|
| | <label for="antigravityErrorCodeFilter" style="margin-left: 20px;">错误码:</label> |
| | <select id="antigravityErrorCodeFilter" class="filter-select" |
| | onchange="applyAntigravityStatusFilter()"> |
| | <option value="all">全部</option> |
| | <option value="400">400</option> |
| | <option value="403">403</option> |
| | <option value="429">429</option> |
| | <option value="500">500</option> |
| | </select> |
| |
|
| | <label for="antigravityCooldownFilter" style="margin-left: 20px;">冷却状态:</label> |
| | <select id="antigravityCooldownFilter" class="filter-select" |
| | onchange="applyAntigravityStatusFilter()"> |
| | <option value="all">全部</option> |
| | <option value="in_cooldown">CD中</option> |
| | <option value="no_cooldown">未CD</option> |
| | </select> |
| |
|
| | <label for="antigravityPageSizeSelect" style="margin-left: 20px;">每页显示:</label> |
| | <select id="antigravityPageSizeSelect" class="page-size-select" |
| | onchange="changeAntigravityPageSize()"> |
| | <option value="20">20</option> |
| | <option value="50">50</option> |
| | <option value="100">100</option> |
| | <option value="200">200</option> |
| | <option value="500">500</option> |
| | <option value="1000">1000</option> |
| | </select> |
| | </div> |
| |
|
| | <div id="antigravityCredsListSection"> |
| | <div class="loading" id="antigravityCredsLoading">正在加载凭证文件...</div> |
| | <div id="antigravityCredsList"></div> |
| |
|
| | |
| | <div class="pagination-container" id="antigravityPaginationContainer" style="display: none;"> |
| | <button class="pagination-btn" id="antigravityPrevPageBtn" |
| | onclick="changeAntigravityPage(-1)">上一页</button> |
| | <div class="pagination-info" id="antigravityPaginationInfo">第 1 页,共 1 页</div> |
| | <button class="pagination-btn" id="antigravityNextPageBtn" |
| | onclick="changeAntigravityPage(1)">下一页</button> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div id="configTab" class="tab-content"> |
| | <h3>配置管理</h3> |
| | <p>管理系统配置参数,修改后立即生效</p> |
| |
|
| | <div class="manage-actions"> |
| | <button class="refresh-btn" onclick="loadConfig()">刷新配置</button> |
| | <button class="btn" onclick="saveConfig()">保存配置</button> |
| | </div> |
| |
|
| | <div id="configSection"> |
| | <div class="loading" id="configLoading">正在加载配置...</div> |
| | <div id="configForm" class="hidden"> |
| | <div class="config-group"> |
| | <h4>服务器配置</h4> |
| |
|
| | <div class="form-group"> |
| | <label for="host">服务器主机地址:</label> |
| | <input type="text" id="host" class="config-input" |
| | placeholder="例如: 0.0.0.0, 127.0.0.1" /> |
| | <small class="config-note">服务器监听的主机地址,0.0.0.0表示监听所有接口</small> |
| | </div> |
| |
|
| | <div class="form-group"> |
| | <label for="port">服务器端口:</label> |
| | <input type="number" id="port" class="config-input" min="1" max="65535" |
| | placeholder="7861" /> |
| | <small class="config-note">服务器监听的端口号,修改后需要重启服务器</small> |
| | </div> |
| |
|
| | <div class="form-group"> |
| | <label for="configApiPassword">API访问密码:</label> |
| | <input type="text" id="configApiPassword" class="config-input" placeholder="pwd" /> |
| | <small class="config-note">聊天API访问密码,用于OpenAI和Gemini API端点的认证</small> |
| | </div> |
| |
|
| | <div class="form-group"> |
| | <label for="configPanelPassword">控制面板密码:</label> |
| | <input type="text" id="configPanelPassword" class="config-input" placeholder="pwd" /> |
| | <small class="config-note">控制面板访问密码,用于web界面登录认证</small> |
| | </div> |
| |
|
| | <div class="form-group"> |
| | <label for="configPassword">通用密码:</label> |
| | <input type="text" id="configPassword" class="config-input" placeholder="pwd" /> |
| | <small class="config-note">(兼容性保留)设置后将覆盖上述两个密码,留空则使用分开的密码设置</small> |
| | </div> |
| | </div> |
| |
|
| | <div class="config-group"> |
| | <h4>基础配置</h4> |
| |
|
| | <div class="form-group"> |
| | <label for="credentialsDir">凭证目录路径:</label> |
| | <input type="text" id="credentialsDir" class="config-input" /> |
| | <small class="config-note">存储认证文件的目录路径</small> |
| | </div> |
| |
|
| | <div class="form-group"> |
| | <label for="proxy">代理设置:</label> |
| | <input type="text" id="proxy" class="config-input" |
| | placeholder="例如: http://proxy:11451 或 socks5://proxy:1080" /> |
| | <small class="config-note">HTTP/HTTPS/SOCKS5Endpoint,留空表示不使用代理</small> |
| | </div> |
| | </div> |
| |
|
| | <div class="config-group"> |
| | <h4>端点配置</h4> |
| |
|
| | |
| | <div class="form-group"> |
| | <div style="display: flex; gap: 10px; margin-bottom: 15px; flex-wrap: wrap;"> |
| | <button type="button" class="btn" onclick="useMirrorUrls()" |
| | style="background-color: #28a745; font-size: 14px;"> |
| | 🚀 一键使用镜像网址 |
| | </button> |
| | <button type="button" class="btn" onclick="restoreOfficialUrls()" |
| | style="background-color: #17a2b8; font-size: 14px;"> |
| | 🔄 还原官方端点 |
| | </button> |
| | </div> |
| | <small class="config-note">镜像网址主要解决墙内无法访问官方端点的问题,部分地区可能无法使用</small> |
| | </div> |
| |
|
| | <div class="form-group"> |
| | <label for="codeAssistEndpoint">Code Assist Endpoint:</label> |
| | <input type="text" id="codeAssistEndpoint" class="config-input" /> |
| | <small class="config-note">Google Cloud Code Assist API端点地址</small> |
| | </div> |
| | <div class="form-group"> |
| | <label for="oauthProxyUrl">OAuth Endpoint:</label> |
| | <input type="text" id="oauthProxyUrl" class="config-input" |
| | placeholder="https://oauth2.googleapis.com" /> |
| | <small class="config-note">Google OAuth2 API端点地址,用于token获取和刷新</small> |
| | </div> |
| | <div class="form-group"> |
| | <label for="googleapisProxyUrl">Google APIs Endpoint:</label> |
| | <input type="text" id="googleapisProxyUrl" class="config-input" |
| | placeholder="https://www.googleapis.com" /> |
| | <small class="config-note">Google APIs API端点地址,用于API服务调用</small> |
| | </div> |
| | <div class="form-group"> |
| | <label for="resourceManagerApiUrl">Resource Manager API Endpoint:</label> |
| | <input type="text" id="resourceManagerApiUrl" class="config-input" |
| | placeholder="https://cloudresourcemanager.googleapis.com" /> |
| | <small class="config-note">Google Cloud Resource Manager API端点地址,用于项目管理</small> |
| | </div> |
| | <div class="form-group"> |
| | <label for="serviceUsageApiUrl">Service Usage API Endpoint:</label> |
| | <input type="text" id="serviceUsageApiUrl" class="config-input" |
| | placeholder="https://serviceusage.googleapis.com" /> |
| | <small class="config-note">Google Cloud Service Usage API端点地址,用于服务启用管理</small> |
| | </div> |
| | <div class="form-group"> |
| | <label for="antigravityApiUrl">Antigravity API Endpoint:</label> |
| | <input type="text" id="antigravityApiUrl" class="config-input" |
| | placeholder="https://daily-cloudcode-pa.sandbox.googleapis.com" /> |
| | <small class="config-note">Google Antigravity API端点地址,用于反重力模式</small> |
| | </div> |
| | </div> |
| |
|
| | <div class="config-group"> |
| | <h4>自动封禁配置</h4> |
| |
|
| | <div class="form-group"> |
| | <label> |
| | <input type="checkbox" id="autoBanEnabled" class="config-checkbox" /> |
| | 启用自动封禁 |
| | </label> |
| | <small class="config-note">遇到指定错误码时自动禁用凭证</small> |
| | </div> |
| |
|
| | <div class="form-group"> |
| | <label for="autoBanErrorCodes">自动封禁错误码:</label> |
| | <input type="text" id="autoBanErrorCodes" class="config-input" |
| | placeholder="例如: 400,403" /> |
| | <small class="config-note">用逗号分隔的错误码列表</small> |
| | </div> |
| | </div> |
| |
|
| | <div class="config-group"> |
| | <h4>429重试配置</h4> |
| |
|
| | <div class="form-group"> |
| | <label> |
| | <input type="checkbox" id="retry429Enabled" class="config-checkbox" /> |
| | 启用429重试 |
| | </label> |
| | <small class="config-note">遇到429错误时自动重试</small> |
| | </div> |
| |
|
| | <div class="form-group"> |
| | <label for="retry429MaxRetries">429重试次数:</label> |
| | <input type="number" id="retry429MaxRetries" class="config-input" min="1" max="50" /> |
| | <small class="config-note">遇到429错误时的最大重试次数</small> |
| | </div> |
| |
|
| | <div class="form-group"> |
| | <label for="retry429Interval">429重试间隔(秒):</label> |
| | <input type="number" id="retry429Interval" class="config-input" min="0.01" max="10" |
| | step="0.01" /> |
| | <small class="config-note">遇到429错误时每两次重试间的等待时间</small> |
| | </div> |
| | </div> |
| |
|
| |
|
| | <div class="config-group"> |
| | <h4>兼容性配置</h4> |
| |
|
| | <div class="form-group"> |
| | <label> |
| | <input type="checkbox" id="compatibilityModeEnabled" class="config-checkbox" /> |
| | 启用兼容性模式 |
| | </label> |
| | <small class="config-note">启用后所有system消息全部转换成user,停用system_instructions <span |
| | style="color: #28a745;">✓ 支持热更新</span></small> |
| | <div class="config-info" |
| | style="background-color: #fff3cd; border: 1px solid #ffc107; color: #856404;"> |
| | <strong>⚠️ 注意:</strong>该选项可能会降低模型理解能力,但是能避免流式空回的情况。 |
| | <br><strong>适用场景:</strong>当遇到流式传输时模型不返回内容或返回空响应时启用此选项。 |
| | </div> |
| | </div> |
| |
|
| | <div class="form-group"> |
| | <label> |
| | <input type="checkbox" id="returnThoughtsToFrontend" class="config-checkbox" /> |
| | 返回思维链到前端 |
| | </label> |
| | <small class="config-note">启用后,模型的思维链会在响应中返回;禁用后,思维链会被过滤掉 <span |
| | style="color: #28a745;">✓ 支持热更新</span></small> |
| | <div class="config-info" |
| | style="background-color: #e3f2fd; border: 1px solid #2196f3; color: #0d47a1;"> |
| | <strong>💭 说明:</strong>某些模型(如Gemini 2.0 |
| | Pro)支持thinking模式,会在生成回答前先输出思考过程。启用后可以看到模型的思考过程;禁用后只显示最终回答,让输出更简洁。 |
| | </div> |
| | </div> |
| |
|
| | <div class="form-group"> |
| | <label> |
| | <input type="checkbox" id="antigravityStream2nostream" class="config-checkbox" /> |
| | Antigravity流式转非流式 |
| | </label> |
| | <small class="config-note">启用后,非流式请求将使用流式API并收集为完整响应 <span |
| | style="color: #28a745;">✓ 支持热更新</span></small> |
| | <div class="config-info" |
| | style="background-color: #f3e5f5; border: 1px solid #9c27b0; color: #4a148c;"> |
| | <strong>🔄 说明:</strong>针对Antigravity模式的优化选项。启用后,即使客户端请求非流式响应,后端也会使用流式API获取数据并收集完整后再返回。 |
| | <br><strong>适用场景:</strong>某些情况下流式API比非流式API更稳定,启用此选项可以提高响应质量。 |
| | <br><strong>默认:</strong>已启用 |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | <div class="config-group"> |
| | <h4>抗截断配置</h4> |
| |
|
| | <div class="form-group"> |
| | <label for="antiTruncationMaxAttempts">抗截断最大重试次数:</label> |
| | <input type="number" id="antiTruncationMaxAttempts" class="config-input" min="1" |
| | max="10" /> |
| | <small class="config-note">当检测到输出截断时的最大续传尝试次数</small> |
| | </div> |
| |
|
| | <div class="form-group"> |
| | <div class="config-info"> |
| | <strong>注意:</strong>抗截断功能现在通过模型名控制: |
| | <ul style="margin: 5px 0; padding-left: 20px;"> |
| | <li>选择带有 "-流式抗截断" 后缀的模型即可启用</li> |
| | <li>该功能仅在流式传输时生效</li> |
| | <li>例如: "gemini-2.5-pro-流式抗截断"</li> |
| | </ul> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | <div class="config-group"> |
| | <h4>配置热更新说明</h4> |
| |
|
| | <div class="form-group"> |
| | <div class="config-info" |
| | style="background-color: #d4edda; border: 1px solid #c3e6cb; color: #155724;"> |
| | <strong>🔥 热更新配置(立即生效):</strong> |
| | <ul style="margin: 8px 0; padding-left: 20px; color: #212529;"> |
| | <li><strong>网络配置:</strong>代理设置、端点配置、HTTP超时时间、最大连接数</li> |
| | <li><strong>API配置:</strong>凭证轮换次数、429重试设置、自动封禁配置</li> |
| | <li><strong>密码配置:</strong>API密码、控制面板密码、通用密码</li> |
| | <li><strong>功能配置:</strong>抗截断最大重试次数</li> |
| | </ul> |
| | </div> |
| | </div> |
| |
|
| | <div class="form-group"> |
| | <div class="config-info" |
| | style="background-color: #fff3cd; border: 1px solid #ffc107; color: #856404;"> |
| | <strong>🔄 需要重启的配置:</strong> |
| | <ul style="margin: 8px 0; padding-left: 20px; color: #212529;"> |
| | <li><strong>服务器配置:</strong>主机地址、端口号</li> |
| | <li><strong>目录配置:</strong>凭证目录路径、Code Assist端点</li> |
| | </ul> |
| | </div> |
| | </div> |
| |
|
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div id="logsTab" class="tab-content"> |
| | <h3>实时日志</h3> |
| | <p>查看系统实时日志输出,支持日志筛选和自动滚动</p> |
| |
|
| | <div class="manage-actions"> |
| | <button class="refresh-btn" onclick="connectWebSocket()">连接日志流</button> |
| | <button class="btn" style="background-color: #dc3545;" onclick="disconnectWebSocket()">断开连接</button> |
| | <button class="btn" style="background-color: #28a745;" onclick="downloadLogs()">下载日志</button> |
| | <button class="btn" style="background-color: #6c757d;" onclick="clearLogs()">清空日志</button> |
| | </div> |
| |
|
| | <div class="filter-container"> |
| | <label for="logLevelFilter">日志级别筛选:</label> |
| | <select id="logLevelFilter" class="filter-select" onchange="filterLogs()"> |
| | <option value="all">全部</option> |
| | <option value="ERROR">错误</option> |
| | <option value="WARNING">警告</option> |
| | <option value="INFO">信息</option> |
| | <option value="DEBUG">调试</option> |
| | </select> |
| |
|
| | <label> |
| | <input type="checkbox" id="autoScroll" checked> 自动滚动到底部 |
| | </label> |
| | </div> |
| |
|
| | <div id="logConnectionStatus" class="status info"> |
| | <strong>连接状态:</strong> <span id="connectionStatusText">未连接</span> |
| | </div> |
| |
|
| | <div id="logContainer" |
| | style="background-color: #1e1e1e; color: #ffffff; font-family: 'Courier New', monospace; font-size: 12px; height: 600px; overflow-y: auto; border: 1px solid #333; border-radius: 5px; padding: 15px; white-space: pre-wrap; word-break: break-all;"> |
| | <div id="logContent">等待连接日志流...</div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div id="aboutTab" class="tab-content"> |
| | <h3>项目信息</h3> |
| | <p>关于GCLI2API项目的详细信息和支持方式</p> |
| |
|
| | |
| | <div |
| | style="background-color: #f8f9fa; padding: 20px; border-radius: 10px; margin: 20px 0; border-left: 4px solid #007bff;"> |
| | <h4 style="margin-top: 0; color: #007bff;">📋 项目简介</h4> |
| | <p style="margin: 10px 0; line-height: 1.6; color: #495057;"> |
| | GCLI2API是一个将Google Gemini API转换为OpenAI 和GEMINI API格式的代理工具,支持多账户管理、自动轮换、实时日志监控等功能。 |
| | </p> |
| | <div style="margin: 15px 0;"> |
| | <p style="margin: 5px 0;"><strong>🔗 项目地址:</strong> <a |
| | href="https://github.com/su-kaka/gcli2api" target="_blank" |
| | style="color: #007bff; text-decoration: none;">GitHub - su-kaka/gcli2api</a></p> |
| | <p style="margin: 5px 0;"><strong>⚠️ 使用声明:</strong> <span |
| | style="color: #dc3545; font-weight: 500;">禁止商业用途和倒卖 - 仅供学习使用</span></p> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div |
| | style="background-color: #e7f3ff; padding: 20px; border-radius: 10px; margin: 20px 0; border-left: 4px solid #17a2b8;"> |
| | <h4 style="margin-top: 0; color: #17a2b8;">✨ 主要功能</h4> |
| | <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 15px;"> |
| | <div> |
| | <p><strong>🔄 多账户管理:</strong> 支持批量上传和管理多个Google账户</p> |
| | <p><strong>⚡ 自动轮换:</strong> 智能轮换账户,避免单账户限额</p> |
| | <p><strong>📊 实时监控:</strong> 使用统计、错误监控、实时日志</p> |
| | </div> |
| | <div> |
| | <p><strong>🛡️ 安全可靠:</strong> OAuth2认证、自动封禁异常账户</p> |
| | <p><strong>🎛️ 配置灵活:</strong> 支持热更新配置、代理设置</p> |
| | <p><strong>📱 界面友好:</strong> 响应式设计、移动端适配</p> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div |
| | style="background-color: #e7f3ff; padding: 20px; border-radius: 10px; margin: 20px 0; border-left: 4px solid #4285f4; text-align: center;"> |
| | <h4 style="margin-top: 0; color: #1976d2;">💬 交流群</h4> |
| | <div style="color: #1565c0; line-height: 1.6;"> |
| | <p>欢迎加入 QQ 群交流讨论!</p> |
| | <p style="font-size: 18px; font-weight: bold; color: #4285f4;">QQ 群号:937681997</p> |
| | </div> |
| | <div |
| | style="display: inline-block; background: white; padding: 15px; border-radius: 12px; box-shadow: 0 2px 15px rgba(0,0,0,0.1); margin-top: 10px;"> |
| | <img src="docs/qq群.jpg" alt="QQ群二维码" |
| | style="width: 200px; height: 200px; border-radius: 8px; display: block;"> |
| | <p |
| | style="color: #666; margin: 10px 0 0 0; font-size: 13px; font-weight: 600; text-align: center;"> |
| | 扫码加入QQ群</p> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div |
| | style="background-color: #d1ecf1; padding: 20px; border-radius: 10px; margin: 20px 0; border-left: 4px solid #bee5eb;"> |
| | <h4 style="margin-top: 0; color: #0c5460;">📞 联系我们</h4> |
| | <div style="color: #0c5460; line-height: 1.6;"> |
| | <p>• <strong>问题反馈:</strong> 通过GitHub Issues提交问题和建议</p> |
| | <p>• <strong>功能请求:</strong> 在GitHub Discussions中讨论新功能</p> |
| | <p>• <strong>代码贡献:</strong> 欢迎提交Pull Request改进项目</p> |
| | <p>• <strong>文档完善:</strong> 帮助改进项目文档和使用指南</p> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | <div id="statusSection"></div> |
| |
|
| | |
| | <div |
| | style="background-color: #f8f9fa; border: 1px solid #dee2e6; border-radius: 8px; padding: 12px; margin-top: 30px; text-align: center; border-left: 4px solid #007bff;"> |
| | <p style="margin: 5px 0; font-size: 14px; color: #495057;">GitHub: <a |
| | href="https://github.com/su-kaka/gcli2api" target="_blank" |
| | style="color: #007bff; text-decoration: none;">https://github.com/su-kaka/gcli2api</a></p> |
| | <p style="margin: 5px 0; font-size: 14px; color: #dc3545; font-weight: 500;">⚠️ 禁止商业用途和倒卖 - 仅供学习使用 ⚠️ |
| | </p> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <script src="./front/common.js"></script> |
| |
|
| | </body> |
| |
|
| | </html> |