diff --git a/console-ui/public/portal/index.html b/console-ui/public/portal/index.html index c207785..2e7f114 100644 --- a/console-ui/public/portal/index.html +++ b/console-ui/public/portal/index.html @@ -186,6 +186,25 @@ + +
+
+
+
Arcrun
+
歡迎!先建立你的帳號
+
+
+ + + + +
+
+
這組帳密就是之後登入知識庫用的,只需要設定這一次。
+
+
+
@@ -548,14 +567,48 @@ function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return { // ── 認證流 ── function showAuth() { + $('v-firstsetup').classList.remove('on'); $('v-login').classList.add('on'); $('shell').classList.remove('on'); $('tabbar').classList.remove('on'); + // t49:全新實例(還沒有任何帳號)→ 換顯示「首次設定」,不讓用戶對著登入殼發呆。 + // 探測走 console/auth-status(configured 布林,console 首設同一顆;失敗就保持登入殼=誠實降級)。 + fetch(API_BASE + '/console/auth-status') + .then(function (r) { return r.json(); }) + .then(function (d) { + if (d && d.configured === false) { + $('v-login').classList.remove('on'); + $('v-firstsetup').classList.add('on'); + } + }) + .catch(function () { /* 探測不到就維持登入殼 */ }); } function showApp() { $('v-login').classList.remove('on'); + $('v-firstsetup').classList.remove('on'); $('shell').classList.add('on'); $('tabbar').classList.add('on'); + // t49 一次性歡迎引導(首次設定完成那一刻才出現;關掉就不再出現): + // 下一步只有兩件——裝同步小幫手(資料自動進來)、想問答就設金鑰。連去 installer /setup。 + try { + if (localStorage.getItem('arcrun_onboarding') === '1' && !document.getElementById('onboard-card')) { + var setupUrl = (window.ARCRUN_CONFIG && window.ARCRUN_CONFIG.installerBase + ? String(window.ARCRUN_CONFIG.installerBase).replace(/\/+$/, '') + : 'https://arcrun-installer.youlin-hsieh-dev.workers.dev') + '/setup'; + var el = document.createElement('div'); + el.id = 'onboard-card'; + el.style.cssText = 'position:fixed;left:50%;bottom:24px;transform:translateX(-50%);z-index:60;max-width:560px;width:calc(100% - 32px);padding:18px 20px;border-radius:14px;background:rgba(var(--amber-rgb),.10);border:1px solid rgba(var(--amber-rgb),.45);backdrop-filter:blur(6px);font-size:14.5px;line-height:1.7'; + el.innerHTML = '🎉 你的知識庫開張了!接下來只有一件事:' + + '下載同步小幫手設定' + + '——把你的資料夾連上來,檔案就會自動變成可搜尋的知識(想用 AI 問答,金鑰也在同一頁設定)。' + + ''; + document.body.appendChild(el); + document.getElementById('onboard-close').addEventListener('click', function () { + try { localStorage.removeItem('arcrun_onboarding'); } catch (e) { /* noop */ } + el.remove(); + }); + } + } catch (e) { /* 引導卡失敗不擋主流程 */ } var p = S.profile || {}; $('side-foot').innerHTML = esc(p.display_name || '') + '
' + esc(location.host); $('nav-workflows').classList.toggle('hide', !p.workflows_visible); @@ -623,6 +676,59 @@ function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return { dropSession(); }); + // ── t49 首次設定(leo 07-25):console/setup → portal/admin/bootstrap → 預設庫登記 → portal/login + // 四發全是既有端點,一顆按鈕做完;用戶感受=「建一組帳密就進來了」。 + $('fs-submit').addEventListener('click', doFirstSetup); + $('fs-password2').addEventListener('keydown', function (ev) { if (ev.key === 'Enter') doFirstSetup(); }); + function doFirstSetup() { + var email = $('fs-email').value.trim(); + var pw = $('fs-password').value; + var st = $('fs-status'); + if (!email || pw.length < 8) { st.textContent = '請填 Email,密碼至少 8 碼'; return; } + if (pw !== $('fs-password2').value) { st.textContent = '兩次密碼不一樣'; return; } + $('fs-submit').disabled = true; st.textContent = ''; + var post = function (path, body, hdrs) { + return fetch(API_BASE + path, { + method: 'POST', + headers: Object.assign({ 'Content-Type': 'application/json' }, hdrs || {}), + body: JSON.stringify(body) + }).then(function (r) { return r.json().catch(function () { return {}; }).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); }); + }; + post('/console/setup', { email: email, password: pw }) + .then(function (x) { + if (!x.ok || !x.d.session_token) throw new Error(x.d.error || '設定沒有成功,請再試一次'); + var ownerTok = x.d.session_token; + return post('/portal/admin/bootstrap', { email: email, password: pw, display_name: email.split('@')[0] }, { Authorization: 'Bearer ' + ownerTok }) + .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 }); + }); + }) + .then(function (l) { + 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; + try { localStorage.setItem('arcrun_onboarding', '1'); } catch (e) { /* noop */ } + boot(); + }) + .catch(function (e) { + $('fs-submit').disabled = false; + st.textContent = (e && e.message) || friendlyErr(e); + }); + } + // 任何 data 請求收到 401 → session 失效 → 回登入殼 function guard401(status) { if (status === 401) { dropSession(); return true; }