Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
278 lines
12 KiB
JavaScript
278 lines
12 KiB
JavaScript
function sendMessageToBackground(message) {
|
||
return new Promise(function(resolve, reject) {
|
||
chrome.runtime.sendMessage(message, function(response) {
|
||
if (chrome.runtime.lastError) {
|
||
reject(new Error(chrome.runtime.lastError.message));
|
||
return;
|
||
}
|
||
resolve(response || null);
|
||
});
|
||
});
|
||
}
|
||
|
||
function setMessage(text, isError) {
|
||
var message = document.getElementById('message');
|
||
if (!message) return;
|
||
message.textContent = text || '';
|
||
message.style.color = isError ? '#ff8585' : '#8892a4';
|
||
}
|
||
|
||
var lastGoodCloudStats = null;
|
||
var lastGoodSyncStatus = null;
|
||
var syncProgressTimer = null;
|
||
var lastServerHealthy = null;
|
||
|
||
function wait(ms) {
|
||
return new Promise(function(resolve) {
|
||
setTimeout(resolve, ms);
|
||
});
|
||
}
|
||
|
||
function setSyncProgress(text) {
|
||
var el = document.getElementById('syncProgressText');
|
||
if (!el) return;
|
||
el.textContent = text || '';
|
||
}
|
||
|
||
function startSyncProgress(prefix) {
|
||
var startedAt = Date.now();
|
||
var dots = 0;
|
||
if (syncProgressTimer) {
|
||
clearInterval(syncProgressTimer);
|
||
}
|
||
setSyncProgress((prefix || '正在同步') + ' 0s');
|
||
syncProgressTimer = setInterval(function() {
|
||
dots = (dots + 1) % 4;
|
||
var suffix = new Array(dots + 1).join('.');
|
||
var elapsed = Math.max(0, Math.floor((Date.now() - startedAt) / 1000));
|
||
setSyncProgress((prefix || '正在同步') + suffix + ' ' + elapsed + 's');
|
||
}, 500);
|
||
}
|
||
|
||
function stopSyncProgress(text) {
|
||
if (syncProgressTimer) {
|
||
clearInterval(syncProgressTimer);
|
||
syncProgressTimer = null;
|
||
}
|
||
setSyncProgress(text || '');
|
||
}
|
||
|
||
function updateStatus(status) {
|
||
if (status && status.authenticated !== undefined) {
|
||
lastGoodSyncStatus = status;
|
||
}
|
||
var accountDot = document.getElementById('accountDot');
|
||
var accountText = document.getElementById('accountText');
|
||
var email = document.getElementById('syncEmail');
|
||
var loggedOut = document.getElementById('authLoggedOut');
|
||
var loggedIn = document.getElementById('authLoggedIn');
|
||
var loggedInEmail = document.getElementById('loggedInEmail');
|
||
var healthy = !!(status && status.color === 'green');
|
||
var authenticated = !!(status && status.authenticated);
|
||
if (accountDot) accountDot.classList.toggle('ok', healthy);
|
||
if (accountText) accountText.textContent = status && (status.accountText || status.text) ? (status.accountText || status.text) : '账号状态未知';
|
||
if (email) email.textContent = status && status.email ? ('账号:' + status.email) : '';
|
||
if (loggedOut) loggedOut.classList.toggle('hidden', authenticated);
|
||
if (loggedIn) loggedIn.classList.toggle('hidden', !authenticated);
|
||
if (loggedInEmail) loggedInEmail.textContent = status && status.email ? ('当前账号:' + status.email) : '当前账号:未知';
|
||
}
|
||
|
||
function updateServerStatus(ok, text) {
|
||
var serverDot = document.getElementById('serverDot');
|
||
var serverText = document.getElementById('serverText');
|
||
lastServerHealthy = !!ok;
|
||
if (serverDot) serverDot.classList.toggle('ok', !!ok);
|
||
if (serverText) serverText.textContent = text || (ok ? '服务器状态正常' : '服务器状态异常');
|
||
}
|
||
|
||
function formatDateTime(value) {
|
||
if (!value) return '暂无';
|
||
try {
|
||
var date = new Date(value);
|
||
if (Number.isNaN(date.getTime())) return String(value);
|
||
return date.toLocaleString('zh-CN');
|
||
} catch (error) {
|
||
return String(value);
|
||
}
|
||
}
|
||
|
||
function renderCloudStats(stats) {
|
||
var summary = document.getElementById('cloudStatsSummary');
|
||
var latest = document.getElementById('cloudStatsLatest');
|
||
var tables = document.getElementById('cloudStatsTables');
|
||
if (!summary || !latest || !tables) return;
|
||
if (!stats || !stats.ok) {
|
||
if (lastGoodCloudStats && lastGoodCloudStats.ok) {
|
||
updateServerStatus(true, '服务器状态正常(展示最近一次成功数据)');
|
||
summary.textContent = '云端状态暂时刷新失败,显示最近一次成功数据';
|
||
latest.textContent = '最近成功更新:线程 ' + formatDateTime(lastGoodCloudStats.latest.threads) + ' | 范围 ' + formatDateTime(lastGoodCloudStats.latest.coverages) + ' | 页缓存 ' + formatDateTime(lastGoodCloudStats.latest.pages);
|
||
tables.textContent = '上次空间占用:' + (Array.isArray(lastGoodCloudStats.tables) ? lastGoodCloudStats.tables.map(function(table) {
|
||
return table.tableName + ' ' + table.sizeMb + 'MB';
|
||
}).join(' | ') : '暂无');
|
||
return;
|
||
}
|
||
updateServerStatus(false, '服务器状态暂时不可读');
|
||
summary.textContent = '暂时无法读取云端状态';
|
||
latest.textContent = '';
|
||
tables.textContent = '';
|
||
return;
|
||
}
|
||
updateServerStatus(true, '服务器状态正常');
|
||
lastGoodCloudStats = stats;
|
||
summary.textContent = '帖子索引 ' + stats.counts.threads + ' 条(含磁力 ' + (stats.counts.magnetThreads || 0) + ' 条)/ 页缓存 ' + stats.counts.pages + ' 条 / 范围缓存 ' + stats.counts.coverages + ' 条 / 用户 ' + stats.counts.users + ' 个 / 保险柜 ' + stats.counts.vaultItems + ' 条';
|
||
latest.textContent = '最近更新:线程 ' + formatDateTime(stats.latest.threads) + ' | 范围 ' + formatDateTime(stats.latest.coverages) + ' | 页缓存 ' + formatDateTime(stats.latest.pages);
|
||
tables.textContent = '空间占用:' + (Array.isArray(stats.tables) ? stats.tables.map(function(table) {
|
||
return table.tableName + ' ' + table.sizeMb + 'MB';
|
||
}).join(' | ') : '暂无');
|
||
}
|
||
|
||
function formatBytes(bytes) {
|
||
var value = Number(bytes || 0);
|
||
if (!Number.isFinite(value) || value <= 0) return '0 B';
|
||
if (value < 1024) return value + ' B';
|
||
if (value < 1024 * 1024) return (value / 1024).toFixed(1) + ' KB';
|
||
if (value < 1024 * 1024 * 1024) return (value / 1024 / 1024).toFixed(2) + ' MB';
|
||
return (value / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
||
}
|
||
|
||
function renderLocalStats(stats) {
|
||
var summary = document.getElementById('localStatsSummary');
|
||
var storage = document.getElementById('localStatsStorage');
|
||
var meta = document.getElementById('localStatsMeta');
|
||
if (!summary || !storage || !meta) return;
|
||
if (!stats || !stats.ok) {
|
||
summary.textContent = '暂时无法读取本地状态';
|
||
storage.textContent = '';
|
||
meta.textContent = '';
|
||
return;
|
||
}
|
||
summary.textContent = '帖子索引 ' + stats.counts.threads + ' 条(含磁力 ' + (stats.counts.magnetThreads || 0) + ' 条)/ 页缓存 ' + stats.counts.pages + ' 条 / 范围缓存 ' + stats.counts.coverages + ' 条 / 收藏 ' + stats.counts.favorites + ' 条 / 历史 ' + stats.counts.history + ' 条';
|
||
storage.textContent = '本地存储占用:' + formatBytes(stats.storage.usage) + ' / ' + formatBytes(stats.storage.quota);
|
||
meta.textContent = '上传抑制元数据:线程 ' + stats.counts.uploadMetaThreads + ' 条 | 范围 ' + stats.counts.uploadMetaCoverages + ' 条 | 页 ' + stats.counts.uploadMetaPages + ' 条';
|
||
}
|
||
|
||
async function refreshStatus() {
|
||
try {
|
||
var response = await sendMessageToBackground({ action: 'cloudGetSyncStatus' });
|
||
if (response && response.ok !== false) {
|
||
updateStatus(response || { color: 'red', text: '状态未知', authenticated: false });
|
||
return;
|
||
}
|
||
if (lastGoodSyncStatus) {
|
||
updateStatus(lastGoodSyncStatus);
|
||
setMessage('云同步状态暂时刷新失败,显示最近一次成功状态', true);
|
||
return;
|
||
}
|
||
updateStatus({ color: 'red', text: '云同步异常', authenticated: false, email: '' });
|
||
} catch (error) {
|
||
if (lastGoodSyncStatus) {
|
||
updateStatus(lastGoodSyncStatus);
|
||
setMessage('云同步状态暂时刷新失败,显示最近一次成功状态', true);
|
||
return;
|
||
}
|
||
updateStatus({ color: 'red', text: '云同步异常', authenticated: false, email: '' });
|
||
setMessage('状态获取失败:' + error.message, true);
|
||
}
|
||
}
|
||
|
||
async function refreshCloudStats() {
|
||
var attempts = 0;
|
||
var response = null;
|
||
var error = null;
|
||
for (attempts = 0; attempts < 3; attempts++) {
|
||
try {
|
||
response = await sendMessageToBackground({ action: 'cloudGetCacheStats' });
|
||
if (response && response.ok) {
|
||
renderCloudStats(response);
|
||
return;
|
||
}
|
||
} catch (err) {
|
||
error = err;
|
||
}
|
||
await wait(500 * (attempts + 1));
|
||
}
|
||
renderCloudStats(null);
|
||
if (error) {
|
||
setMessage('云端状态暂时刷新失败,稍后会自动重试', true);
|
||
}
|
||
}
|
||
|
||
async function refreshLocalStats() {
|
||
try {
|
||
var response = await sendMessageToBackground({ action: 'localGetCacheStats' });
|
||
renderLocalStats(response);
|
||
} catch (error) {
|
||
renderLocalStats(null);
|
||
}
|
||
}
|
||
|
||
async function submitAuth(action) {
|
||
var emailInput = document.getElementById('emailInput');
|
||
var passwordInput = document.getElementById('passwordInput');
|
||
var email = emailInput ? emailInput.value.trim() : '';
|
||
var password = passwordInput ? passwordInput.value : '';
|
||
if (!email || !password) {
|
||
setMessage('请输入邮箱和密码', true);
|
||
return;
|
||
}
|
||
setMessage(action === 'cloudRegister' ? '正在注册...' : '正在登录...', false);
|
||
try {
|
||
var response = await sendMessageToBackground({ action: action, email: email, password: password });
|
||
if (!response || !response.ok) {
|
||
setMessage((action === 'cloudRegister' ? '注册失败:' : '登录失败:') + (response && response.error ? response.error : '未知错误'), true);
|
||
return;
|
||
}
|
||
if (passwordInput) passwordInput.value = '';
|
||
updateStatus(response.status || { color: 'green', text: '云同步正常', authenticated: true, email: email });
|
||
setMessage(action === 'cloudRegister' ? '注册成功,云同步已开启' : '登录成功,云同步已开启', false);
|
||
} catch (error) {
|
||
setMessage((action === 'cloudRegister' ? '注册失败:' : '登录失败:') + error.message, true);
|
||
}
|
||
}
|
||
|
||
async function syncNow() {
|
||
setMessage('', false);
|
||
startSyncProgress('正在同步本地缓存到云端');
|
||
try {
|
||
await sendMessageToBackground({ action: 'cloudBackfillLocalCache', force: true });
|
||
stopSyncProgress('已提交同步任务,正在刷新状态...');
|
||
await wait(1200);
|
||
startSyncProgress('正在刷新云端状态');
|
||
await refreshStatus();
|
||
await refreshCloudStats();
|
||
await refreshLocalStats();
|
||
stopSyncProgress('同步完成');
|
||
setMessage('已触发云端回填', false);
|
||
} catch (error) {
|
||
stopSyncProgress('同步失败');
|
||
setMessage('同步失败:' + error.message, true);
|
||
}
|
||
}
|
||
|
||
async function logout() {
|
||
try {
|
||
await sendMessageToBackground({ action: 'cloudLogout' });
|
||
await refreshStatus();
|
||
setMessage('已退出登录', false);
|
||
} catch (error) {
|
||
setMessage('退出失败:' + error.message, true);
|
||
}
|
||
}
|
||
|
||
function openExternalUrl(url) {
|
||
chrome.tabs.create({ url: url });
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
document.getElementById('loginBtn').addEventListener('click', function() { submitAuth('cloudLogin'); });
|
||
document.getElementById('registerBtn').addEventListener('click', function() { submitAuth('cloudRegister'); });
|
||
document.getElementById('syncNowBtn').addEventListener('click', syncNow);
|
||
document.getElementById('logoutBtn').addEventListener('click', logout);
|
||
document.getElementById('downloadNode2Btn').addEventListener('click', function() { openExternalUrl('http://7.haory.cn/x/x72/qBittorrent_5.1.4_x64_setup.exe'); });
|
||
document.getElementById('downloadOfficialBtn').addEventListener('click', function() { openExternalUrl('https://sourceforge.net/projects/qbittorrent/files/qbittorrent-win32/qbittorrent-5.1.4/qbittorrent_5.1.4_x64_setup.exe/download'); });
|
||
document.getElementById('downloadMotrixBtn').addEventListener('click', function() { openExternalUrl('http://7.haory.cn/x/x72/Motrix-Setup-1.8.19.exe'); });
|
||
refreshStatus();
|
||
refreshCloudStats();
|
||
refreshLocalStats();
|
||
});
|