| |
|
| | const API_BASE = window.location.origin;
|
| |
|
| |
|
| | let authToken = localStorage.getItem('auth_token');
|
| |
|
| |
|
| | document.addEventListener('DOMContentLoaded', function() {
|
| | const currentPage = window.location.pathname;
|
| | console.log('[DEBUG] Current page:', currentPage);
|
| | console.log('[DEBUG] Auth token:', authToken ? 'exists' : 'none');
|
| |
|
| | if (currentPage.includes('dashboard')) {
|
| | console.log('[DEBUG] Loading dashboard...');
|
| | if (!authToken) {
|
| | window.location.href = '/static/index.html';
|
| | return;
|
| | }
|
| | loadDashboard();
|
| | } else {
|
| |
|
| | console.log('[DEBUG] Loading login page...');
|
| | if (authToken) {
|
| | console.log('[DEBUG] Already logged in, redirecting to dashboard...');
|
| | window.location.href = '/dashboard';
|
| | return;
|
| | }
|
| | setupLoginForm();
|
| | }
|
| | });
|
| |
|
| |
|
| |
|
| |
|
| | function setupLoginForm() {
|
| | console.log('[DEBUG] setupLoginForm called');
|
| | const loginForm = document.getElementById('loginForm');
|
| | console.log('[DEBUG] loginForm element:', loginForm);
|
| | if (loginForm) {
|
| | console.log('[DEBUG] Adding submit event listener to loginForm');
|
| | loginForm.addEventListener('submit', async (e) => {
|
| | e.preventDefault();
|
| | console.log('[DEBUG] Form submitted!');
|
| |
|
| | const username = document.getElementById('username').value;
|
| | const password = document.getElementById('password').value;
|
| | const errorDiv = document.getElementById('loginError');
|
| |
|
| | console.log('[DEBUG] Attempting login with username:', username);
|
| |
|
| | try {
|
| | console.log('[DEBUG] Sending request to:', `${API_BASE}/api/auth/login`);
|
| | const response = await fetch(`${API_BASE}/api/auth/login`, {
|
| | method: 'POST',
|
| | headers: {
|
| | 'Content-Type': 'application/json'
|
| | },
|
| | body: JSON.stringify({ username, password })
|
| | });
|
| |
|
| | const data = await response.json();
|
| |
|
| | if (response.ok) {
|
| | authToken = data.token;
|
| | localStorage.setItem('auth_token', authToken);
|
| | window.location.href = '/dashboard';
|
| | } else {
|
| | errorDiv.textContent = data.error || '登录失败';
|
| | errorDiv.style.display = 'block';
|
| | }
|
| | } catch (error) {
|
| | errorDiv.textContent = '网络错误,请稍后重试';
|
| | errorDiv.style.display = 'block';
|
| | }
|
| | });
|
| | }
|
| | }
|
| |
|
| |
|
| | function logout() {
|
| | if (confirm('确定要退出吗?')) {
|
| | localStorage.removeItem('auth_token');
|
| | authToken = null;
|
| | window.location.href = '/';
|
| | }
|
| | }
|
| |
|
| |
|
| | function showChangePasswordModal() {
|
| | document.getElementById('changePasswordModal').style.display = 'flex';
|
| | document.getElementById('changePasswordForm').reset();
|
| | document.getElementById('changePasswordError').style.display = 'none';
|
| | }
|
| |
|
| |
|
| | function closeChangePasswordModal() {
|
| | document.getElementById('changePasswordModal').style.display = 'none';
|
| | }
|
| |
|
| |
|
| | async function changePassword(oldPassword, newPassword) {
|
| | try {
|
| | const response = await apiRequest('/api/auth/password', {
|
| | method: 'PUT',
|
| | body: JSON.stringify({
|
| | old_password: oldPassword,
|
| | new_password: newPassword
|
| | })
|
| | });
|
| |
|
| | if (response.ok) {
|
| | showToast('密码修改成功', 'success');
|
| | closeChangePasswordModal();
|
| | return true;
|
| | } else {
|
| | const data = await response.json();
|
| | return { error: data.error || '密码修改失败' };
|
| | }
|
| | } catch (error) {
|
| | console.error('修改密码错误:', error);
|
| | return { error: '网络错误,请重试' };
|
| | }
|
| | }
|
| |
|
| |
|
| | function setupChangePasswordForm() {
|
| | const form = document.getElementById('changePasswordForm');
|
| | if (form) {
|
| | form.addEventListener('submit', async (e) => {
|
| | e.preventDefault();
|
| |
|
| | const oldPassword = document.getElementById('oldPassword').value;
|
| | const newPassword = document.getElementById('newPassword').value;
|
| | const confirmPassword = document.getElementById('confirmPassword').value;
|
| | const errorDiv = document.getElementById('changePasswordError');
|
| |
|
| |
|
| | if (newPassword.length < 6) {
|
| | errorDiv.textContent = '新密码至少需要6位字符';
|
| | errorDiv.style.display = 'block';
|
| | return;
|
| | }
|
| |
|
| |
|
| | if (newPassword !== confirmPassword) {
|
| | errorDiv.textContent = '两次输入的密码不一致';
|
| | errorDiv.style.display = 'block';
|
| | return;
|
| | }
|
| |
|
| | const result = await changePassword(oldPassword, newPassword);
|
| | if (result === true) {
|
| |
|
| | } else if (result && result.error) {
|
| | errorDiv.textContent = result.error;
|
| | errorDiv.style.display = 'block';
|
| | }
|
| | });
|
| | }
|
| | }
|
| |
|
| |
|
| | async function apiRequest(url, options = {}) {
|
| | const headers = {
|
| | 'Content-Type': 'application/json',
|
| | ...options.headers
|
| | };
|
| |
|
| | if (authToken) {
|
| | headers['Authorization'] = `Bearer ${authToken}`;
|
| | }
|
| |
|
| | const response = await fetch(`${API_BASE}${url}`, {
|
| | ...options,
|
| | headers
|
| | });
|
| |
|
| |
|
| | if (response.status === 401) {
|
| | localStorage.removeItem('auth_token');
|
| | authToken = null;
|
| | window.location.href = '/';
|
| | return;
|
| | }
|
| |
|
| | return response;
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| | async function loadDashboard() {
|
| | setupChangePasswordForm();
|
| | await loadUserInfo();
|
| | await loadStats();
|
| | await loadCookies();
|
| | }
|
| |
|
| |
|
| | async function loadUserInfo() {
|
| | try {
|
| | const response = await apiRequest('/api/auth/me');
|
| | if (response.ok) {
|
| | const user = await response.json();
|
| | document.getElementById('currentUser').textContent = user.username;
|
| | }
|
| | } catch (error) {
|
| | console.error('加载用户信息失败:', error);
|
| | }
|
| | }
|
| |
|
| |
|
| | async function loadStats() {
|
| | try {
|
| | const response = await apiRequest('/api/cookies/stats');
|
| | if (response.ok) {
|
| | const stats = await response.json();
|
| | document.getElementById('totalCount').textContent = stats.total_count;
|
| | document.getElementById('validCount').textContent = stats.valid_count;
|
| | document.getElementById('invalidCount').textContent = stats.invalid_count;
|
| | document.getElementById('totalUsage').textContent = stats.total_usage.toLocaleString();
|
| | }
|
| | } catch (error) {
|
| | console.error('加载统计信息失败:', error);
|
| | }
|
| | }
|
| |
|
| |
|
| | async function loadCookies() {
|
| | try {
|
| | const response = await apiRequest('/api/cookies');
|
| | if (response.ok) {
|
| | const data = await response.json();
|
| |
|
| | renderCookieTable(Array.isArray(data) ? data : (data.cookies || []));
|
| | }
|
| | } catch (error) {
|
| | console.error('加载 Cookie 列表失败:', error);
|
| | }
|
| | }
|
| |
|
| |
|
| | function renderCookieTable(cookies) {
|
| | const tbody = document.getElementById('cookieTableBody');
|
| | const emptyState = document.getElementById('emptyState');
|
| |
|
| | if (cookies.length === 0) {
|
| | tbody.innerHTML = '';
|
| | emptyState.style.display = 'block';
|
| | return;
|
| | }
|
| |
|
| | emptyState.style.display = 'none';
|
| |
|
| | tbody.innerHTML = cookies.map((cookie, index) => `
|
| | <tr>
|
| | <td>${index + 1}</td>
|
| | <td>${escapeHtml(cookie.name)}</td>
|
| | <td>
|
| | <span class="status-badge ${cookie.is_valid ? 'status-valid' : 'status-invalid'}">
|
| | ${cookie.is_valid ? '✅ 有效' : '❌ 无效'}
|
| | </span>
|
| | </td>
|
| | <td>${(cookie.usage_count || 0).toLocaleString()}</td>
|
| | <td>${cookie.priority || 0}</td>
|
| | <td>${formatTime(cookie.last_validated)}</td>
|
| | <td>
|
| | <div class="action-buttons">
|
| | <button class="btn btn-secondary btn-sm" onclick="validateCookie(${cookie.id})">🔄</button>
|
| | <button class="btn btn-secondary btn-sm" onclick="editCookie(${cookie.id})">⚙️</button>
|
| | <button class="btn btn-danger btn-sm" onclick="deleteCookie(${cookie.id})">🗑️</button>
|
| | </div>
|
| | </td>
|
| | </tr>
|
| | `).join('');
|
| | }
|
| |
|
| |
|
| | function refreshCookies() {
|
| | loadCookies();
|
| | loadStats();
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| | function showAddModal() {
|
| | document.getElementById('addModal').style.display = 'flex';
|
| | document.getElementById('addCookieForm').reset();
|
| | }
|
| |
|
| |
|
| | function closeAddModal() {
|
| | document.getElementById('addModal').style.display = 'none';
|
| | }
|
| |
|
| |
|
| | document.getElementById('addCookieForm')?.addEventListener('submit', async (e) => {
|
| | e.preventDefault();
|
| |
|
| | const formData = new FormData(e.target);
|
| | const data = {
|
| | name: formData.get('name'),
|
| | api_key: formData.get('api_key'),
|
| | session_key: formData.get('session_key') || '',
|
| | priority: parseInt(formData.get('priority') || '0')
|
| | };
|
| |
|
| | try {
|
| | const response = await apiRequest('/api/cookies', {
|
| | method: 'POST',
|
| | body: JSON.stringify(data)
|
| | });
|
| |
|
| | if (response.ok) {
|
| | showToast('Cookie 添加成功', 'success');
|
| | closeAddModal();
|
| | refreshCookies();
|
| | } else {
|
| | const error = await response.json();
|
| | showToast(error.error || '添加失败', 'error');
|
| | }
|
| | } catch (error) {
|
| | showToast('网络错误', 'error');
|
| | }
|
| | });
|
| |
|
| |
|
| | async function editCookie(id) {
|
| | try {
|
| | const response = await apiRequest(`/api/cookies/${id}`);
|
| | if (response.ok) {
|
| | const cookie = await response.json();
|
| | document.getElementById('editCookieId').value = cookie.id;
|
| | document.getElementById('editCookieName').value = cookie.name;
|
| | document.getElementById('editApiKey').value = cookie.api_key || '';
|
| | document.getElementById('editSessionKey').value = cookie.session_key || '';
|
| | document.getElementById('editCookiePriority').value = cookie.priority || 0;
|
| | document.getElementById('editModal').style.display = 'flex';
|
| | }
|
| | } catch (error) {
|
| | showToast('加载失败', 'error');
|
| | }
|
| | }
|
| |
|
| |
|
| | function closeEditModal() {
|
| | document.getElementById('editModal').style.display = 'none';
|
| | }
|
| |
|
| |
|
| | document.getElementById('editCookieForm')?.addEventListener('submit', async (e) => {
|
| | e.preventDefault();
|
| |
|
| | const id = document.getElementById('editCookieId').value;
|
| | const formData = new FormData(e.target);
|
| | const data = {
|
| | name: formData.get('name'),
|
| | api_key: formData.get('api_key'),
|
| | session_key: formData.get('session_key') || '',
|
| | priority: parseInt(formData.get('priority') || '0')
|
| | };
|
| |
|
| | try {
|
| | const response = await apiRequest(`/api/cookies/${id}`, {
|
| | method: 'PUT',
|
| | body: JSON.stringify(data)
|
| | });
|
| |
|
| | if (response.ok) {
|
| | showToast('Cookie 更新成功', 'success');
|
| | closeEditModal();
|
| | refreshCookies();
|
| | } else {
|
| | const error = await response.json();
|
| | showToast(error.error || '更新失败', 'error');
|
| | }
|
| | } catch (error) {
|
| | showToast('网络错误', 'error');
|
| | }
|
| | });
|
| |
|
| |
|
| | async function deleteCookie(id) {
|
| | if (!confirm('确定要删除这个 Cookie 吗?')) {
|
| | return;
|
| | }
|
| |
|
| | try {
|
| | const response = await apiRequest(`/api/cookies/${id}`, {
|
| | method: 'DELETE'
|
| | });
|
| |
|
| | if (response.ok) {
|
| | showToast('Cookie 删除成功', 'success');
|
| | refreshCookies();
|
| | } else {
|
| | const error = await response.json();
|
| | showToast(error.error || '删除失败', 'error');
|
| | }
|
| | } catch (error) {
|
| | showToast('网络错误', 'error');
|
| | }
|
| | }
|
| |
|
| |
|
| | async function validateCookie(id) {
|
| | try {
|
| | showToast('正在验证...', 'success');
|
| | const response = await apiRequest(`/api/cookies/${id}/validate`, {
|
| | method: 'POST'
|
| | });
|
| |
|
| | if (response.ok) {
|
| | const result = await response.json();
|
| | showToast(result.is_valid ? '✅ Cookie 有效' : '❌ Cookie 无效', result.is_valid ? 'success' : 'error');
|
| | refreshCookies();
|
| | } else {
|
| | const error = await response.json();
|
| | showToast(error.error || '验证失败', 'error');
|
| | }
|
| | } catch (error) {
|
| | showToast('网络错误', 'error');
|
| | }
|
| | }
|
| |
|
| |
|
| | async function validateAll() {
|
| | if (!confirm('确定要验证所有 Cookie 吗?这可能需要一些时间。')) {
|
| | return;
|
| | }
|
| |
|
| | try {
|
| | showToast('正在批量验证...', 'success');
|
| | const response = await apiRequest('/api/cookies/validate/all', {
|
| | method: 'POST'
|
| | });
|
| |
|
| | if (response.ok) {
|
| | const result = await response.json();
|
| | showToast(`验证完成:${result.valid_count} 个有效,${result.invalid_count} 个无效`, 'success');
|
| | refreshCookies();
|
| | } else {
|
| | const error = await response.json();
|
| | showToast(error.error || '验证失败', 'error');
|
| | }
|
| | } catch (error) {
|
| | showToast('网络错误', 'error');
|
| | }
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| | function showToast(message, type = 'success') {
|
| | const toast = document.getElementById('toast');
|
| | toast.textContent = message;
|
| | toast.className = `toast ${type}`;
|
| | toast.style.display = 'block';
|
| |
|
| | setTimeout(() => {
|
| | toast.style.display = 'none';
|
| | }, 3000);
|
| | }
|
| |
|
| |
|
| | function formatTime(timeStr) {
|
| | if (!timeStr) return '-';
|
| | const date = new Date(timeStr);
|
| | const now = new Date();
|
| | const diff = Math.floor((now - date) / 1000);
|
| |
|
| | if (diff < 60) return '刚刚';
|
| | if (diff < 3600) return `${Math.floor(diff / 60)} 分钟前`;
|
| | if (diff < 86400) return `${Math.floor(diff / 3600)} 小时前`;
|
| | return `${Math.floor(diff / 86400)} 天前`;
|
| | }
|
| |
|
| |
|
| | function escapeHtml(text) {
|
| | const div = document.createElement('div');
|
| | div.textContent = text;
|
| | return div.innerHTML;
|
| | } |