105 lines
3.0 KiB
JavaScript
105 lines
3.0 KiB
JavaScript
|
|
const AUTH_KEY = "pts_authdata";
|
|||
|
|
const statusEl = document.getElementById("status");
|
|||
|
|
const backendInput = document.getElementById("backendUrl");
|
|||
|
|
|
|||
|
|
chrome.storage.local.get(["backendUrl"], (data) => {
|
|||
|
|
if (data.backendUrl) backendInput.value = data.backendUrl;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
function isPtsUrl(url) {
|
|||
|
|
return !!url && url.includes("tw-timesheet.supermicro.com");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function getActiveTab() {
|
|||
|
|
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|||
|
|
return tab;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function readAuthViaMessage(tabId) {
|
|||
|
|
return new Promise((resolve) => {
|
|||
|
|
chrome.tabs.sendMessage(tabId, { type: "GET_PTS_AUTH" }, (response) => {
|
|||
|
|
if (chrome.runtime.lastError) {
|
|||
|
|
resolve({
|
|||
|
|
ok: false,
|
|||
|
|
error: chrome.runtime.lastError.message,
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
resolve(response || { ok: false, error: "No response from content script" });
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function readAuthViaInjection(tabId) {
|
|||
|
|
const results = await chrome.scripting.executeScript({
|
|||
|
|
target: { tabId },
|
|||
|
|
func: (key) => {
|
|||
|
|
const raw = localStorage.getItem(key);
|
|||
|
|
if (!raw) return null;
|
|||
|
|
try {
|
|||
|
|
return JSON.parse(raw);
|
|||
|
|
} catch {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
args: [AUTH_KEY],
|
|||
|
|
});
|
|||
|
|
const auth = results?.[0]?.result ?? null;
|
|||
|
|
return { ok: true, auth };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function readAuthFromPage(tab) {
|
|||
|
|
let result = await readAuthViaMessage(tab.id);
|
|||
|
|
|
|||
|
|
if (!result.ok || !result.auth) {
|
|||
|
|
result = await readAuthViaInjection(tab.id);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function syncSession(auth, backendUrl) {
|
|||
|
|
if (!auth?.accessToken) {
|
|||
|
|
throw new Error("找不到登入憑證,請確認已登入工時系統");
|
|||
|
|
}
|
|||
|
|
const res = await fetch(`${backendUrl}/api/session`, {
|
|||
|
|
method: "POST",
|
|||
|
|
headers: { "Content-Type": "application/json" },
|
|||
|
|
body: JSON.stringify({
|
|||
|
|
accessToken: auth.accessToken,
|
|||
|
|
refreshToken: auth.refreshToken,
|
|||
|
|
groupCode: auth.groupCode,
|
|||
|
|
raw: auth,
|
|||
|
|
}),
|
|||
|
|
});
|
|||
|
|
const data = await res.json().catch(() => ({}));
|
|||
|
|
if (!res.ok) throw new Error(data.detail || res.statusText);
|
|||
|
|
return data;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
document.getElementById("syncBtn").addEventListener("click", async () => {
|
|||
|
|
statusEl.textContent = "同步中...";
|
|||
|
|
const backendUrl = backendInput.value.trim().replace(/\/$/, "");
|
|||
|
|
chrome.storage.local.set({ backendUrl });
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const tab = await getActiveTab();
|
|||
|
|
if (!tab?.id || !isPtsUrl(tab.url)) {
|
|||
|
|
throw new Error("請先切到工時系統分頁(tw-timesheet.supermicro.com)再按同步");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const pageAuth = await readAuthFromPage(tab);
|
|||
|
|
if (!pageAuth.auth) {
|
|||
|
|
throw new Error(
|
|||
|
|
pageAuth.error
|
|||
|
|
? `${pageAuth.error}。請重新整理工時系統頁面後再試`
|
|||
|
|
: "無法讀取登入憑證,請重新整理工時系統頁面並確認已登入"
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const result = await syncSession(pageAuth.auth, backendUrl);
|
|||
|
|
statusEl.textContent = `成功:${result.message || "已同步"}`;
|
|||
|
|
} catch (err) {
|
|||
|
|
statusEl.textContent = `失敗:${err.message}`;
|
|||
|
|
}
|
|||
|
|
});
|