fix(t97+t114): 庫目錄只顯示用戶同步進來的庫、拿掉兩段式登記
leo 07-28 原話:「用戶沒加上的庫,不要自作主張給它加上」/「掃進來的就是要進目錄… 加入目錄這件小事還要分兩段做?…這是在攻打用戶嗎?根本是 attack」。 - portal.ts:859 auto 段濾掉 general(系統未標庫桶,非用戶的庫) - index.html:bootstrap 不再預埋 kb 庫;移除「登記到目錄」按鈕與 auto/registered 視覺分岔 驗證(總管 grep 真相源):登記到目錄=0、kb 種子=0、general 濾在; os-split 10/10+safejson 8/8 綠;portal-admin 1 紅=console HTML 搬遷陳舊測試 (stash 基線同紅,與本案無關)。(實作=子 CC;驗證+commit=總管)
This commit is contained in:
@@ -943,8 +943,6 @@ function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return {
|
||||
.then(function (b) {
|
||||
// 409=已 bootstrap 過(冪等視為就緒);其餘失敗誠實丟出
|
||||
if (!b.ok && b.status !== 409) throw new Error(b.d.error || '初始化沒有成功,請再試一次');
|
||||
// 預設庫 kb 自動登記(leo:庫要自動出現,不是叫用戶去登記)——
|
||||
// 同步小幫手預設就寫進 kb 庫,這裡把登記簿先蓋好章;已存在(409/重複)不吵。
|
||||
return post('/portal/login', { email: email, password: pw });
|
||||
});
|
||||
})
|
||||
@@ -952,12 +950,6 @@ function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return {
|
||||
if (!l.ok || !l.d.session_token) throw new Error(l.d.error || '帳號建好了,但自動登入沒成功——請用剛設定的帳密登入');
|
||||
S.token = l.d.session_token;
|
||||
try { localStorage.setItem('arcrun_portal_session', S.token); } catch (e) { /* noop */ }
|
||||
// 預設庫登記(用 portal session;失敗不擋進入)
|
||||
return fetch(API_BASE + '/portal/admin/libraries', {
|
||||
method: 'POST',
|
||||
headers: Object.assign({ 'Content-Type': 'application/json' }, authHeaders()),
|
||||
body: JSON.stringify({ name: 'kb', display_name: '知識庫', description: '同步小幫手預設寫入的庫' })
|
||||
}).catch(function () { /* 已存在或無權限都不擋 */ });
|
||||
})
|
||||
.then(function () {
|
||||
$('fs-submit').disabled = false;
|
||||
@@ -1476,29 +1468,18 @@ function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return {
|
||||
}
|
||||
|
||||
function renderAdminLibs() {
|
||||
if (!adminLibs.length) { $('ad-libs').innerHTML = '<div class="muted" style="padding:16px 4px">還沒有登記任何庫。未登記時整個系統視同單一 general 庫。</div>'; return; }
|
||||
if (!adminLibs.length) { $('ad-libs').innerHTML = '<div class="muted" style="padding:16px 4px">同步小幫手還沒送來任何庫。裝好小幫手並選好資料夾後,庫會自動出現在這裡。</div>'; return; }
|
||||
$('ad-libs').innerHTML = adminLibs.map(function (l) {
|
||||
var disabled = l.status === 'disabled';
|
||||
// t52:auto 庫=資料蓋章自動出現、還沒登記進登記簿(沒有 record_id)——
|
||||
// 那兩顆按鈕會帶空 id 打 API=按了就錯,改給「登記到目錄」一顆(leo 判準:不放死按鈕)。
|
||||
if (l.auto) {
|
||||
return '<div class="card-item">' +
|
||||
'<div style="display:flex;align-items:baseline;gap:10px;flex-wrap:wrap">' +
|
||||
'<span class="serif" style="font-size:17px;font-weight:600">' + esc(l.display_name || l.name) + '</span>' +
|
||||
'<span class="dim mono" style="font-size:13px">' + esc(l.name) + '</span>' +
|
||||
'<span class="tag green">啟用中</span><span class="tag">同步自動出現</span></div>' +
|
||||
'<div style="margin-top:8px;font-size:14.5px;line-height:1.65;color:rgba(var(--ink-rgb),.65)">'
|
||||
+ '這個庫來自同步小幫手的資料夾,已經可以搜尋與設權限。登記到目錄後可以改顯示名。</div>' +
|
||||
'<div class="btnrow"><button class="btn2" data-act="lib-adopt" data-name="' + esc(l.name) + '">登記到目錄</button></div>' +
|
||||
'</div>';
|
||||
}
|
||||
return '<div class="card-item">' +
|
||||
'<div style="display:flex;align-items:baseline;gap:10px;flex-wrap:wrap">' +
|
||||
'<span class="serif" style="font-size:17px;font-weight:600">' + esc(l.display_name || l.name) + '</span>' +
|
||||
'<span class="dim mono" style="font-size:13px">' + esc(l.name) + '</span>' +
|
||||
'<span class="tag ' + (disabled ? 'dim' : 'green') + '">' + (disabled ? '已停用' : '啟用中') + '</span>' +
|
||||
(l.auto
|
||||
? '<span class="tag green">啟用中</span><span class="tag">同步自動出現</span>'
|
||||
: '<span class="tag ' + (disabled ? 'dim' : 'green') + '">' + (disabled ? '已停用' : '啟用中') + '</span>') +
|
||||
'</div>' +
|
||||
(l.description ? '<div style="margin-top:8px;font-size:14.5px;line-height:1.65;color:rgba(var(--ink-rgb),.65)">' + esc(l.description) + '</div>' : '') +
|
||||
(!l.auto && l.description ? '<div style="margin-top:8px;font-size:14.5px;line-height:1.65;color:rgba(var(--ink-rgb),.65)">' + esc(l.description) + '</div>' : '') +
|
||||
'</div>';
|
||||
}).join('');
|
||||
}
|
||||
@@ -1614,22 +1595,6 @@ function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return {
|
||||
.catch(function (e) { t.disabled = false; toast(friendlyErr(e)); });
|
||||
return;
|
||||
}
|
||||
// t52:把「自動出現的庫」正式登記進目錄(之後就能改顯示名)
|
||||
if (act === 'lib-adopt') {
|
||||
var lname = t.getAttribute('data-name') || '';
|
||||
if (!lname) return;
|
||||
t.disabled = true;
|
||||
adminApi('POST', '/portal/admin/libraries', { name: lname, display_name: lname, description: '同步小幫手看守的資料夾' })
|
||||
.then(function (x) {
|
||||
t.disabled = false;
|
||||
if (guard401(x.status)) return;
|
||||
if (!x.ok && x.status !== 409) { toast(x.d.error || ('登記失敗(HTTP ' + x.status + ')')); return; }
|
||||
toast('已登記到目錄');
|
||||
loadAdmin();
|
||||
})
|
||||
.catch(function (e) { t.disabled = false; toast(friendlyErr(e)); });
|
||||
return;
|
||||
}
|
||||
});
|
||||
// 一次性密碼關閉鈕(otpbox 動態生成,掛在容器上)
|
||||
$('ad-otp').addEventListener('click', function (ev) {
|
||||
|
||||
@@ -855,7 +855,8 @@ portalRouter.get('/portal/admin/libraries', (c) =>
|
||||
const body = (await res.json()) as { libraries?: string[] };
|
||||
for (const name of body.libraries ?? []) {
|
||||
const n = String(name ?? '').trim();
|
||||
if (!n || known.has(n)) continue;
|
||||
// general 是系統內部「未標庫」桶(未標記 entry 的 fallback),不在用戶目錄露臉
|
||||
if (!n || n === 'general' || known.has(n)) continue;
|
||||
known.add(n);
|
||||
out.push({ record_id: '', name: n, display_name: n, description: '資料同步時自動出現(可在此補顯示名)', status: 'active', graph_source: false, auto: true });
|
||||
}
|
||||
|
||||
@@ -376,12 +376,29 @@ describe('/portal/admin/libraries', () => {
|
||||
const res = await json('GET', '/portal/admin/libraries', undefined, { Authorization: 'Bearer tok-user' });
|
||||
expect(res.status).toBe(403);
|
||||
});
|
||||
|
||||
it('GET auto 庫列表過濾 general(general 是系統桶,不在用戶目錄顯示)', async () => {
|
||||
await seedAdminSession();
|
||||
mockGetRecord('rec_admin', adminValues());
|
||||
mockListByTemplate('portal_library', []);
|
||||
fetchMock
|
||||
.get(KBDB)
|
||||
.intercept({ path: (p: string) => p.startsWith('/entries/libraries'), method: 'GET' })
|
||||
.reply(200, { libraries: ['kb', 'general', 'notes'] });
|
||||
const res = await json('GET', '/portal/admin/libraries', undefined, { Authorization: 'Bearer tok-admin' });
|
||||
expect(res.status).toBe(200);
|
||||
const data = (await res.json()) as { libraries: { name: string; auto?: boolean }[] };
|
||||
const names = data.libraries.map((l) => l.name);
|
||||
expect(names).toContain('kb');
|
||||
expect(names).toContain('notes');
|
||||
expect(names).not.toContain('general');
|
||||
});
|
||||
});
|
||||
|
||||
// ═══════════════ 6. /portal HTML 殼(P4 admin 頁後紅線不回退)═══════════════
|
||||
|
||||
describe('GET /portal(P4 admin 頁 HTML 殼)', () => {
|
||||
it('admin view 存在;仍零租戶字串、零 /kbdb/、零 X-Arcrun-API-Key、零 Mira', async () => {
|
||||
it('admin view 存在;仍零租戶字串、零 /kbdb/、零 X-Arcrun-API-Key、零 Mira;無 kb 種子、無登記到目錄', async () => {
|
||||
const res = await SELF.fetch('http://localhost/portal');
|
||||
expect(res.status).toBe(200);
|
||||
const html = await res.text();
|
||||
@@ -393,5 +410,11 @@ describe('GET /portal(P4 admin 頁 HTML 殼)', () => {
|
||||
expect(html).not.toContain('/kbdb/');
|
||||
expect(html).not.toContain('X-Arcrun-API-Key');
|
||||
expect(html).not.toContain('Mira');
|
||||
// t97a:bootstrap 後不再預埋 kb 庫
|
||||
expect(html).not.toContain('"name": "kb"');
|
||||
expect(html).not.toContain("name: 'kb'");
|
||||
// t114:無「登記到目錄」按鈕
|
||||
expect(html).not.toContain('lib-adopt');
|
||||
expect(html).not.toContain('登記到目錄');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user