diff --git a/console-ui/public/portal/index.html b/console-ui/public/portal/index.html
index d600cee..39d2fcd 100644
--- a/console-ui/public/portal/index.html
+++ b/console-ui/public/portal/index.html
@@ -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 = '
還沒有登記任何庫。未登記時整個系統視同單一 general 庫。
'; return; }
+ if (!adminLibs.length) { $('ad-libs').innerHTML = '同步小幫手還沒送來任何庫。裝好小幫手並選好資料夾後,庫會自動出現在這裡。
'; return; }
$('ad-libs').innerHTML = adminLibs.map(function (l) {
var disabled = l.status === 'disabled';
- // t52:auto 庫=資料蓋章自動出現、還沒登記進登記簿(沒有 record_id)——
- // 那兩顆按鈕會帶空 id 打 API=按了就錯,改給「登記到目錄」一顆(leo 判準:不放死按鈕)。
- if (l.auto) {
- return '' +
- '
' +
- '' + esc(l.display_name || l.name) + '' +
- '' + esc(l.name) + '' +
- '啟用中同步自動出現
' +
- '
'
- + '這個庫來自同步小幫手的資料夾,已經可以搜尋與設權限。登記到目錄後可以改顯示名。
' +
- '
' +
- '
';
- }
return '' +
'
' +
'' + esc(l.display_name || l.name) + '' +
'' + esc(l.name) + '' +
- '' + (disabled ? '已停用' : '啟用中') + '' +
+ (l.auto
+ ? '啟用中同步自動出現'
+ : '' + (disabled ? '已停用' : '啟用中') + '') +
'
' +
- (l.description ? '
' + esc(l.description) + '
' : '') +
+ (!l.auto && l.description ? '
' + esc(l.description) + '
' : '') +
'
';
}).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) {
diff --git a/cypher-executor/src/routes/portal.ts b/cypher-executor/src/routes/portal.ts
index 2a99f49..84abaa9 100644
--- a/cypher-executor/src/routes/portal.ts
+++ b/cypher-executor/src/routes/portal.ts
@@ -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 });
}
diff --git a/cypher-executor/tests/portal-admin.test.ts b/cypher-executor/tests/portal-admin.test.ts
index 37da814..d6aaa1e 100644
--- a/cypher-executor/tests/portal-admin.test.ts
+++ b/cypher-executor/tests/portal-admin.test.ts
@@ -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('登記到目錄');
});
});