f9045a8404
- 将 Nexus/Nexus/* 移到仓库根目录(消除双层嵌套) - 删除旧的多层空壳目录 (server/, web/, agent/, deploy/, docs/) - 将PHP前端 (web/) 合并进Nexus仓库 — 统一管理 - 部署时 git clone Nexus 仓库即可,不再需要两个仓库 目录结构: server/ ← Python FastAPI后端 web/ ← PHP前端 (install.php, config.php, etc) deploy/ ← Supervisor + Shell健康检查 docs/ ← 部署文档 tests/ ← 测试 .env ← 不在git中 (install.php生成) .gitignore ← 排除 .env, SECRETS.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
87 lines
2.1 KiB
JavaScript
87 lines
2.1 KiB
JavaScript
/**
|
|
* MultiSync - Frontend JavaScript
|
|
*/
|
|
|
|
/**
|
|
* Show a toast notification
|
|
*/
|
|
function showToast(message, type = 'success') {
|
|
const toast = document.createElement('div');
|
|
toast.className = `toast ${type}`;
|
|
toast.textContent = message;
|
|
document.body.appendChild(toast);
|
|
|
|
setTimeout(() => {
|
|
toast.style.animation = 'slideIn 0.3s ease reverse';
|
|
setTimeout(() => toast.remove(), 300);
|
|
}, 3000);
|
|
}
|
|
|
|
/**
|
|
* Confirm dialog with callback
|
|
*/
|
|
function confirmAction(message, callback) {
|
|
if (confirm(message)) {
|
|
callback();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Format bytes to human readable
|
|
*/
|
|
function formatBytes(bytes, decimals = 2) {
|
|
if (bytes === 0) return '0 B';
|
|
const k = 1024;
|
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i];
|
|
}
|
|
|
|
/**
|
|
* Format duration in seconds to human readable
|
|
*/
|
|
function formatDuration(seconds) {
|
|
if (seconds < 60) return `${seconds}秒`;
|
|
if (seconds < 3600) return `${Math.floor(seconds/60)}分${seconds%60}秒`;
|
|
return `${Math.floor(seconds/3600)}时${Math.floor((seconds%3600)/60)}分`;
|
|
}
|
|
|
|
/**
|
|
* API proxy for AJAX calls
|
|
*/
|
|
async function apiCall(endpoint, method = 'GET', data = null) {
|
|
const options = {
|
|
method,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
};
|
|
|
|
if (data) {
|
|
options.body = JSON.stringify(data);
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`api_proxy.php?action=${endpoint}`, options);
|
|
return await response.json();
|
|
} catch (error) {
|
|
console.error('API call failed:', error);
|
|
showToast('请求失败: ' + error.message, 'error');
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loading state management
|
|
*/
|
|
function setLoading(element, loading) {
|
|
if (loading) {
|
|
element.disabled = true;
|
|
element.dataset.originalText = element.innerHTML;
|
|
element.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 处理中...';
|
|
} else {
|
|
element.disabled = false;
|
|
element.innerHTML = element.dataset.originalText || element.innerHTML;
|
|
}
|
|
}
|