完成llm token过期时间的配置化

This commit is contained in:
luke 2025-06-03 12:34:09 +08:00
parent 7cd51825ca
commit adf7eee7f4
11 changed files with 136 additions and 99 deletions

View File

@ -1,13 +1,13 @@
// 仅在插件安装时设置默认 popup 页面,不再在 startup 中干扰当前 UI
chrome.runtime.onInstalled.addListener(() => {
chrome.storage.local.get(["token", "loginAt"], (res) => {
const maxAge = 24 * 60 * 60 * 1000; // 1天有效期
const now = Date.now();
if (!res.token || !res.loginAt || (now - res.loginAt > maxAge)) {
chrome.action.setPopup({ popup: "./pages/login.html" });
} else {
chrome.action.setPopup({ popup: "./pages/popup.html" });
}
});
});
// // 仅在插件安装时设置默认 popup 页面,不再在 startup 中干扰当前 UI
// chrome.runtime.onInstalled.addListener(() => {
// chrome.storage.local.get(["token", "loginAt"], (res) => {
// const maxAge = 24 * 60 * 60 * 1000; // 1天有效期
// const now = Date.now();
//
// if (!res.token || !res.loginAt || (now - res.loginAt > maxAge)) {
// chrome.action.setPopup({ popup: "./pages/login.html" });
// } else {
// chrome.action.setPopup({ popup: "./pages/popup.html" });
// }
// });
// });

View File

@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "渠道应用开发部知识库",
"version": "2.0.0",
"version": "2.0.1",
"description": "通过关键词快速搜索内部文档",
"permissions": ["storage"],
"host_permissions": [

View File

@ -9,6 +9,8 @@
</head>
<body>
<div style="padding: 1rem; font-size: 14px;">正在加载,请稍候...</div>
<script src="../scripts/utils.js"></script>
<script src="../scripts/config.js"></script>
<script src="../scripts/index.js"></script>
</body>
</html>

View File

@ -130,10 +130,15 @@
<div class="section">
<h3>通用设置</h3>
<div class="form-row">
<label for="tokenExpireHours">Token 有效期(小时,最大 168</label>
<label for="tokenExpireHours">登录 Token 有效期(小时,最大 168</label>
<input id="tokenExpireHours" type="number" min="1" max="168" />
<div class="desc">超出有效期将要求重新登录</div>
</div>
<div class="form-row">
<label for="llmTokenExpireHours">LLM Token 有效期(小时,最大 168</label>
<input id="llmTokenExpireHours" type="number" min="1" max="168" />
<div class="desc">超出有效期将重新获取</div>
</div>
</div>
<div class="section" id="endpointSection">
@ -146,6 +151,7 @@
<button id="saveBtn">保存设置</button>
</div>
<script src="../scripts/utils.js"></script>
<script src="../scripts/config.js"></script>
<script src="../scripts/settings.js"></script>
</body>

View File

@ -2,6 +2,7 @@
## 修订记录
| 时间 | 版本 | 修订人 | 说明 |
| ---------- | ------ | ------- | ------------------ |
|------------|--------|---------|---------------------|
| 2025.06.02 | V2.0.0 | Luke.Ye | 完成插件配置化改造 |
| 2025.06.03 | V2.0.1 | Luke.Ye | 完成llm token过期时间的配置化 |

View File

@ -1,23 +1,22 @@
// scripts/auth.js
const TOKEN_EXPIRE_MS = 7 * 24 * 60 * 60 * 1000; // 7天
async function loadToken() {
return new Promise((resolve) => {
chrome.storage.local.get(["token", "loginAt", "username"], (res) => {
const now = Date.now();
if (!res.token || !res.loginAt || isNaN(res.loginAt) || (now - res.loginAt > TOKEN_EXPIRE_MS)) {
log("token 不存在或已过期");
resolve(null);
} else {
log("token 加载成功");
resolve({ token: res.token, username: res.username || "用户" });
}
});
});
}
function handleLogout() {
chrome.storage.local.remove(["token", "loginAt", "username"], () => {
window.location.href = chrome.runtime.getURL("pages/login.html");
});
}
async function loadToken() {
return new Promise((resolve) => {
chrome.storage.local.get(["token", "username", "loginAt"], async (res) => {
const mergedConfig = await loadMergedConfig();
const expireMs = mergedConfig.tokenExpireHours * 60 * 60 * 1000;
const now = Date.now();
if (res.token && res.loginAt && now - res.loginAt < expireMs) {
log("token 加载成功");
resolve({ token: res.token, username: res.username });
} else {
log("token 不存在或已过期");
resolve(null);
}
});
});
}

View File

@ -1,5 +1,6 @@
const defaultConfig = {
tokenExpireHours: 24,
llmTokenExpireHours: 24,
apiConfig: {
materialPublicUrl: {
name: "材料公开地址",
@ -79,7 +80,10 @@ function loadMergedConfig() {
chrome.storage.local.get(["customConfig"], (res) => {
const merged = JSON.parse(JSON.stringify(defaultConfig));
if (res.customConfig && typeof res.customConfig === "object") {
merged.tokenExpireHours = res.customConfig.tokenExpireHours || merged.tokenExpireHours;
merged.tokenExpireHours =
res.customConfig.tokenExpireHours != null ? res.customConfig.tokenExpireHours : merged.tokenExpireHours;
merged.llmTokenExpireHours =
res.customConfig.llmTokenExpireHours != null ? res.customConfig.llmTokenExpireHours : merged.llmTokenExpireHours;
for (const key in merged.apiConfig) {
const override = res.customConfig.apiConfig?.[key];
if (override) {

View File

@ -1,8 +1,10 @@
chrome.storage.local.get(["token", "loginAt"], (res) => {
const maxAge = 24 * 60 * 60 * 1000;
loadMergedConfig().then(config => {
chrome.storage.local.get(["token", "loginAt"], (res) => {
const expireMs = config.tokenExpireHours * 60 * 60 * 1000;
const now = Date.now();
const popup = (!res.token || !res.loginAt || (now - res.loginAt > maxAge))
const popup = (!res.token || !res.loginAt || (now - res.loginAt > expireMs))
? "pages/login.html"
: "pages/popup.html";
window.location.href = chrome.runtime.getURL(popup);
});
});

View File

@ -1,5 +1,3 @@
const LLM_TOKEN_EXPIRE_MS = 23 * 60 * 60 * 1000;
async function resolveJumpLinks(titles) {
if (!titles || titles.length === 0) return {};
@ -225,25 +223,76 @@ function insertCopyFullAnswerBtn(msgBox, answerStr) {
msgBox.appendChild(copyBtn);
}
async function getLLMPassword() {
function promptLLMPasswordModal() {
return new Promise((resolve) => {
chrome.storage.local.get(["llmPassword"], (res) => {
resolve(res.llmPassword || "");
});
// 避免重复弹窗
if (document.getElementById("llm-password-modal")) return;
// 创建遮罩层
const mask = document.createElement("div");
mask.id = "llm-password-modal-mask";
mask.style = `
position: fixed; left:0; top:0; width:100vw; height:100vh; background:rgba(0,0,0,0.19);
z-index:9998;`;
// 创建弹窗
const modal = document.createElement("div");
modal.id = "llm-password-modal";
modal.style = `
position: fixed; top: 45%; left: 50%; transform: translate(-50%, -50%);
background: #fff; border-radius: 10px; box-shadow: 0 8px 38px #0001;
padding: 24px 32px 18px 32px; z-index:9999; min-width:260px; text-align:center;`;
modal.innerHTML = `
<div style="font-size:1rem;margin-bottom:13px;">LLM Token 已过期请输入新的 LLM 密码</div>
<input id="llm-password-input" type="password" style="width:95%;padding:7px 8px;font-size:1rem;border-radius:6px;border:1px solid #bbb;margin-bottom:10px;" autofocus />
<div>
<button id="llm-password-ok" style="margin-right:10px;padding:6px 18px;">确定</button>
<button id="llm-password-cancel" style="padding:6px 18px;">取消</button>
</div>
`;
document.body.appendChild(mask);
document.body.appendChild(modal);
function closeModal(val) {
document.body.removeChild(mask);
document.body.removeChild(modal);
resolve(val);
}
document.getElementById("llm-password-ok").onclick = () => {
const pwd = document.getElementById("llm-password-input").value;
closeModal(pwd ? pwd.trim() : "");
};
document.getElementById("llm-password-cancel").onclick = () => closeModal("");
document.getElementById("llm-password-input").onkeydown = (e) => {
if (e.key === "Enter") document.getElementById("llm-password-ok").click();
};
});
}
async function getLLMToken(forceRefresh = false) {
const config = await loadMergedConfig();
const tokenCfg = config.apiConfig.llmToken;
return new Promise((resolve, reject) => {
chrome.storage.local.get(["llmToken", "llmTokenAt"], async (res) => {
const now = Date.now();
if (!forceRefresh && res.llmToken && res.llmTokenAt && now - res.llmTokenAt < LLM_TOKEN_EXPIRE_MS) {
const mergedConfig = await loadMergedConfig();
const expireMs = mergedConfig.llmTokenExpireHours * 60 * 60 * 1000;
if (!forceRefresh && res.llmToken && res.llmTokenAt && now - res.llmTokenAt < expireMs) {
log("已从缓存中获取到llmToken. expireMs: " + expireMs);
resolve(res.llmToken);
} else {
try {
const pwd = await getLLMPassword();
// 这里不再用 getLLMPassword(),而是直接弹窗
let pwd = await promptLLMPasswordModal();
if (!pwd) {
reject("未输入密码,无法获取 LLM Token");
return;
}
// 可选:此处不自动存储 llmPassword提升安全性
const body = {};
body[tokenCfg.requestParams[0].field] = pwd;
@ -257,8 +306,10 @@ async function getLLMToken(forceRefresh = false) {
const tokenField = tokenCfg.responseParams.find(p => p.label.includes("Token")).field;
if (data[tokenField]) {
chrome.storage.local.set({ llmToken: data[tokenField], llmTokenAt: now });
log("请求llmToken成功");
resolve(data[tokenField]);
} else {
log("LLM token获取失败");
reject("LLM token获取失败");
}
} catch (e) {
@ -277,7 +328,10 @@ async function getLLMWorkspaceSlug(token, forceRefresh = false) {
return new Promise((resolve, reject) => {
chrome.storage.local.get(["llmWorkspaceSlug", "llmWorkspaceAt"], async (res) => {
const now = Date.now();
if (!forceRefresh && res.llmWorkspaceSlug && res.llmWorkspaceAt && now - res.llmWorkspaceAt < LLM_TOKEN_EXPIRE_MS) {
const mergedConfig = await loadMergedConfig();
const expireMs = mergedConfig.llmTokenExpireHours * 60 * 60 * 1000;
if (!forceRefresh && res.llmWorkspaceSlug && res.llmWorkspaceAt && now - res.llmWorkspaceAt < expireMs) {
log("获取llmWorkspaceSlug. expireMs: " + expireMs)
resolve(res.llmWorkspaceSlug);
} else {
try {
@ -290,6 +344,7 @@ async function getLLMWorkspaceSlug(token, forceRefresh = false) {
const list = data[workspacesField] || [];
if (list.length > 0 && list[0].slug) {
chrome.storage.local.set({ llmWorkspaceSlug: list[0].slug, llmWorkspaceAt: now });
log("获取llmWorkspaceSlug: " + JSON.stringify(list))
resolve(list[0].slug);
} else {
reject("未获取到LLM工作区slug");

View File

@ -29,26 +29,6 @@ document.addEventListener("DOMContentLoaded", async () => {
});
}
async function loadToken() {
return new Promise((resolve) => {
chrome.storage.local.get(["token", "username", "loginAt"], async (res) => {
const mergedConfig = await loadMergedConfig();
const expireMs = mergedConfig.tokenExpireHours * 60 * 60 * 1000;
const now = Date.now();
if (res.token && res.loginAt && now - res.loginAt < expireMs) {
resolve({ token: res.token, username: res.username });
} else {
resolve(null);
}
});
});
}
async function handleLogout() {
await chrome.storage.local.remove(["token", "username", "loginAt"]);
location.href = chrome.runtime.getURL("pages/login.html");
}
const input = document.getElementById("searchInput");
const searchBtn = document.getElementById("searchBtn");
const clearBtn = document.getElementById("clearHistoryBtn");
@ -176,6 +156,8 @@ document.addEventListener("DOMContentLoaded", async () => {
},
body: JSON.stringify(requestBody)
});
log("已使用前端接口配置:" + JSON.stringify(endpoint));
log("请求后端接口参数:" + JSON.stringify(requestBody));
if (response.status === 401) return handleLogout();
const data = await response.json();

View File

@ -1,31 +1,10 @@
// scripts/settings.js
function getMergedConfig(callback) {
chrome.storage.local.get(["customConfig"], (res) => {
const merged = JSON.parse(JSON.stringify(defaultConfig));
if (res.customConfig && typeof res.customConfig === "object") {
merged.tokenExpireHours = res.customConfig.tokenExpireHours || merged.tokenExpireHours;
for (const key in merged.apiConfig) {
const override = res.customConfig.apiConfig?.[key];
if (override) {
merged.apiConfig[key].url = override.url || merged.apiConfig[key].url;
merged.apiConfig[key].requestParams.forEach((param, i) => {
if (override.requestParams?.[i]?.field) param.field = override.requestParams[i].field;
});
merged.apiConfig[key].responseParams.forEach((param, i) => {
if (override.responseParams?.[i]?.field) param.field = override.responseParams[i].field;
});
}
}
}
callback(merged);
});
loadMergedConfig().then(callback);
}
function renderConfig(config) {
document.getElementById("tokenExpireHours").value = config.tokenExpireHours;
document.getElementById("llmTokenExpireHours").value = config.llmTokenExpireHours;
const container = document.getElementById("endpointSection");
container.innerHTML = "";
@ -76,7 +55,12 @@ function renderConfig(config) {
}
function collectUserConfig() {
const tokenExpireHours = Math.min(168, parseInt(document.getElementById("tokenExpireHours").value) || 24);
let tokenExpireHours = parseInt(document.getElementById("tokenExpireHours").value);
if (isNaN(tokenExpireHours) || tokenExpireHours > 168 || tokenExpireHours < 0) tokenExpireHours = 24;
let llmTokenExpireHours = parseInt(document.getElementById("llmTokenExpireHours").value);
if (isNaN(llmTokenExpireHours) || llmTokenExpireHours > 168 || llmTokenExpireHours < 0) llmTokenExpireHours = 24;
const apiConfig = {};
const inputs = document.querySelectorAll("input[data-key]");
@ -98,12 +82,14 @@ function collectUserConfig() {
}
}
return { tokenExpireHours, apiConfig };
return { tokenExpireHours, llmTokenExpireHours, apiConfig };
}
document.getElementById("saveBtn").addEventListener("click", () => {
const newConfig = collectUserConfig();
chrome.storage.local.set({ customConfig: newConfig }, () => {
log("配置已保存:" + JSON.stringify(newConfig));
alert("设置已保存!");
});
});