2026-05-13 07:05:39 +00:00
|
|
|
// --- static/api.js ---
|
|
|
|
|
|
|
|
|
|
// ==========================================
|
|
|
|
|
// 1. 鎖定管理 (Lock Management)
|
|
|
|
|
// ==========================================
|
2026-05-19 10:25:10 +00:00
|
|
|
export async function apiAcquireLock(path, userId, username, host) {
|
|
|
|
|
const res = await fetch('/api/v1/locks/acquire', {
|
2026-05-13 07:05:39 +00:00
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
2026-05-19 10:25:10 +00:00
|
|
|
body: JSON.stringify({ path, user_id: userId, username, host })
|
2026-05-13 07:05:39 +00:00
|
|
|
});
|
2026-05-19 10:25:10 +00:00
|
|
|
const data = await res.json();
|
|
|
|
|
return { status: res.status, data };
|
2026-05-13 07:05:39 +00:00
|
|
|
}
|
|
|
|
|
|
2026-05-19 10:25:10 +00:00
|
|
|
export async function apiReleaseLock(path, userId, host) {
|
2026-05-13 07:05:39 +00:00
|
|
|
return fetch('/api/v1/locks/release', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
2026-05-19 10:25:10 +00:00
|
|
|
body: JSON.stringify({ path, user_id: userId, host })
|
2026-05-13 07:05:39 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 10:25:10 +00:00
|
|
|
export async function apiSendHeartbeat(path, userId, host) {
|
2026-05-13 07:05:39 +00:00
|
|
|
return fetch('/api/v1/locks/heartbeat', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
2026-05-19 10:25:10 +00:00
|
|
|
body: JSON.stringify({ path, user_id: userId, host })
|
2026-05-13 07:05:39 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 10:25:10 +00:00
|
|
|
export async function apiGetLockStatus(host) {
|
|
|
|
|
// 帶入 host 參數,實現 IP 隔離查詢
|
|
|
|
|
const url = host ? `/api/v1/locks/status?host=${encodeURIComponent(host)}` : '/api/v1/locks/status';
|
|
|
|
|
const response = await fetch(url);
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-13 07:05:39 +00:00
|
|
|
// ==========================================
|
|
|
|
|
// 2. 樹狀圖與選項快取 (Tree & Options)
|
|
|
|
|
// ==========================================
|
2026-05-19 10:25:10 +00:00
|
|
|
export async function apiGetFullConfig(host, username, password, skipFilter = false, configType = 'running') {
|
|
|
|
|
// 🌟 將 config_type 加入 URL 參數中
|
|
|
|
|
let url = `/api/v1/cmts-full-config?host=${encodeURIComponent(host)}&username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}&config_type=${configType}`;
|
|
|
|
|
|
2026-05-13 07:05:39 +00:00
|
|
|
if (skipFilter) url += '&skip_filter=true';
|
|
|
|
|
const response = await fetch(url);
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 06:37:16 +00:00
|
|
|
export async function apiGetLeafOptions(host) {
|
|
|
|
|
const response = await fetch(`/api/v1/cmts-leaf-options?host=${encodeURIComponent(host)}`);
|
2026-05-13 07:05:39 +00:00
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 06:37:16 +00:00
|
|
|
export async function apiSyncLeafOptions(host, paths) {
|
2026-05-13 07:05:39 +00:00
|
|
|
return fetch('/api/v1/cmts-leaf-options/sync', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
2026-06-10 06:37:16 +00:00
|
|
|
body: JSON.stringify({ host, leaf_paths: paths })
|
2026-05-13 07:05:39 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 06:37:16 +00:00
|
|
|
export async function apiClearCache(host, paths) {
|
|
|
|
|
const response = await fetch(`/api/v1/clear_cache?host=${encodeURIComponent(host)}`, {
|
2026-05-13 07:05:39 +00:00
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
body: JSON.stringify(paths)
|
|
|
|
|
});
|
|
|
|
|
if (!response.ok) throw new Error(`伺服器回應錯誤: ${response.status}`);
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-15 07:17:28 +00:00
|
|
|
export async function apiGetCmtsVersion(host, username, password) {
|
|
|
|
|
const url = `/api/v1/cmts-version?host=${encodeURIComponent(host)}&username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`;
|
|
|
|
|
const response = await fetch(url);
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 06:37:16 +00:00
|
|
|
export async function apiGetScanStatus(host) {
|
2026-05-15 07:17:28 +00:00
|
|
|
try {
|
2026-06-10 06:37:16 +00:00
|
|
|
const response = await fetch(`/api/v1/scan-status?host=${encodeURIComponent(host)}`);
|
2026-05-15 07:17:28 +00:00
|
|
|
const data = await response.json();
|
|
|
|
|
return data.is_scanning;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-13 07:05:39 +00:00
|
|
|
// ==========================================
|
|
|
|
|
// 3. 指令生成與執行 (CLI Generation & Execution)
|
|
|
|
|
// ==========================================
|
|
|
|
|
export async function apiGenerateCli(diffs, interfaces) {
|
|
|
|
|
const response = await fetch('/api/v1/cmts-generate-cli', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
body: JSON.stringify({ diffs, interfaces_with_admin_state: interfaces })
|
|
|
|
|
});
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function apiExecuteConfig(script, host, username, password) {
|
|
|
|
|
const response = await fetch('/api/v1/cmts-config', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
body: JSON.stringify({ script, host, username, password })
|
|
|
|
|
});
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ==========================================
|
|
|
|
|
// 4. 系統設定與過濾器 (System Settings)
|
|
|
|
|
// ==========================================
|
2026-05-19 10:25:10 +00:00
|
|
|
// 🌟 加上 configType 參數
|
|
|
|
|
export async function apiGetTreeFilters(configType = 'running') {
|
|
|
|
|
const response = await fetch(`/api/v1/settings/tree-filters?config_type=${configType}`);
|
2026-05-13 07:05:39 +00:00
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 10:25:10 +00:00
|
|
|
// 🌟 加上 configType 參數
|
|
|
|
|
export async function apiSaveTreeFilters(hiddenKeys, configType = 'running') {
|
|
|
|
|
const response = await fetch(`/api/v1/settings/tree-filters?config_type=${configType}`, {
|
2026-05-13 07:05:39 +00:00
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
2026-05-19 10:25:10 +00:00
|
|
|
body: JSON.stringify({ hidden_keys: hiddenKeys, config_type: configType })
|
2026-05-13 07:05:39 +00:00
|
|
|
});
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ==========================================
|
|
|
|
|
// 5. MAC Domain 與綜合查詢 (MAC Domain & Query)
|
|
|
|
|
// ==========================================
|
|
|
|
|
export async function apiGetMacDomainList(host, username, password) {
|
|
|
|
|
const url = `/api/v1/cmts-mac-domain-list?host=${encodeURIComponent(host)}&username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`;
|
|
|
|
|
const response = await fetch(url);
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function apiGetMacDomainConfig(target, host, username, password) {
|
|
|
|
|
const url = `/api/v1/cmts-mac-domain-config?target=${encodeURIComponent(target)}&host=${encodeURIComponent(host)}&username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`;
|
|
|
|
|
const response = await fetch(url);
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function apiExecuteQuery(queryType, target, host, username, password) {
|
|
|
|
|
const url = `/api/v1/cmts-query?query_type=${queryType}&target=${encodeURIComponent(target)}&host=${encodeURIComponent(host)}&username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`;
|
2026-06-05 05:45:34 +00:00
|
|
|
const response = await fetch(url);
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 🌟 新增:動態抓取設備清單 API
|
|
|
|
|
export async function apiListCms(host, username, password) {
|
|
|
|
|
const url = `/api/v1/list-cms?host=${encodeURIComponent(host)}&username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`;
|
|
|
|
|
const response = await fetch(url);
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function apiListRpds(host, username, password) {
|
|
|
|
|
const url = `/api/v1/list-rpds?host=${encodeURIComponent(host)}&username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`;
|
2026-05-13 07:05:39 +00:00
|
|
|
const response = await fetch(url);
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
2026-05-13 10:31:34 +00:00
|
|
|
|
|
|
|
|
// ==========================================
|
|
|
|
|
// 6. 專門處理串流的 API 呼叫函數 (UI 可以即時更新)
|
|
|
|
|
// ==========================================
|
2026-06-10 06:37:16 +00:00
|
|
|
export async function apiSyncLeafOptionsStream(host, paths, onProgress) {
|
2026-05-13 10:31:34 +00:00
|
|
|
const response = await fetch('/api/v1/cmts-leaf-options/sync', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
2026-06-10 06:37:16 +00:00
|
|
|
body: JSON.stringify({ host, leaf_paths: paths })
|
2026-05-13 10:31:34 +00:00
|
|
|
});
|
|
|
|
|
if (!response.body) throw new Error("瀏覽器不支援 ReadableStream");
|
|
|
|
|
const reader = response.body.getReader();
|
|
|
|
|
const decoder = new TextDecoder("utf-8");
|
|
|
|
|
while (true) {
|
|
|
|
|
const { done, value } = await reader.read();
|
2026-06-10 06:37:16 +00:00
|
|
|
if (done) break;
|
2026-05-13 10:31:34 +00:00
|
|
|
const chunk = decoder.decode(value, { stream: true });
|
|
|
|
|
const lines = chunk.split("\n").filter(line => line.trim() !== "");
|
|
|
|
|
for (const line of lines) {
|
|
|
|
|
try {
|
|
|
|
|
const data = JSON.parse(line);
|
2026-06-10 06:37:16 +00:00
|
|
|
onProgress(data);
|
|
|
|
|
} catch (e) {}
|
2026-05-13 10:31:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
2026-05-19 10:25:10 +00:00
|
|
|
}
|
2026-06-04 10:09:45 +00:00
|
|
|
|
|
|
|
|
export async function apiGetCmDiagnostics(host, username, password, mac) {
|
|
|
|
|
const url = `/api/v1/cm-diagnostics/?host=${encodeURIComponent(host)}&username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}&mac=${encodeURIComponent(mac)}`;
|
|
|
|
|
const response = await fetch(url);
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
const err = await response.json();
|
|
|
|
|
throw new Error(err.detail || "伺服器發生錯誤");
|
|
|
|
|
}
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
2026-06-05 10:29:04 +00:00
|
|
|
|
|
|
|
|
export async function apiGetLogLevels() {
|
|
|
|
|
const response = await fetch('/api/v1/settings/logs');
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function apiSetLogLevel(module, level) {
|
|
|
|
|
const response = await fetch('/api/v1/settings/logs', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
body: JSON.stringify({ module, level })
|
|
|
|
|
});
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
2026-06-08 09:01:55 +00:00
|
|
|
|
|
|
|
|
// ==========================================
|
|
|
|
|
// 7. 授權與驗證 API (Auth)
|
|
|
|
|
// ==========================================
|
|
|
|
|
export async function apiVerifyGodMode(password) {
|
|
|
|
|
const response = await fetch('/api/v1/auth/god-mode', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
body: JSON.stringify({ password })
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
const err = await response.json();
|
|
|
|
|
throw new Error(err.detail || "驗證失敗");
|
|
|
|
|
}
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
2026-06-15 06:45:54 +00:00
|
|
|
|
|
|
|
|
// ==========================================
|
|
|
|
|
// 📌 釘選防護 API (Pinning)
|
|
|
|
|
// ==========================================
|
|
|
|
|
export async function apiToggleBackupPin(backupId) {
|
|
|
|
|
const response = await fetch(`/api/v1/backups/${backupId}/toggle-pin`, {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'Content-Type': 'application/json' }
|
|
|
|
|
});
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|