fix(portal): 忘記密碼斷點——安裝器從沒注入 PORTAL_MAIL_RELAY_BASE(arcrun-rag#38/#69/#25)
leo 被鎖在自己的系統外面:1.4.35 已出 prod,「忘記密碼」畫面在線上,但按下去回 503「這台實例還沒有設定寄信服務」。根因在 products/arcrun-rag 的安裝器—— `grep -rn PORTAL_MAIL_RELAY installer/` 是零命中,即使 landing 那半(郵差) D62 已經寫好。這個 repo 這邊改兩件配合修: - /health 多回 `mail_relay_configured`(布林,不洩漏網址)——安裝器判斷「要不要 重推」只比 bundle_version,但這個 var 是這次才第一次被注入,跟版本號無關; 純比版本號的話,已經在最新版的實例(如 leo 那台)永遠不會因為「按更新」而 重推,這個 var 永遠補不進去。安裝器那邊會讀這個欄位決定要不要強制重推。 - portal.ts 的 503 訊息改白話(不提「實例」)+給一條使用者自己走得完的路 (重新執行安裝/更新),不再說「請管理員幫你改密碼」——單人使用者的管理員 就是他自己,那是死路(同源病灶:arcrun-rag#25)。 安裝器那半的修法(PORTAL_MAIL_RELAY_BASE 注入+stale 判斷)在 products/arcrun-rag 同批修(installer/oauth-prototype/worker.js)。 測試:cypher-executor `npx vitest run tests/portal-auth.test.ts tests/health.test.ts` 37/37 綠(35+2,既有測試無回歸;本次未新增測試——改動是訊息文案與健康檢查欄位, 行為已由既有測試覆蓋的路徑保護)。
This commit is contained in:
@@ -21,6 +21,12 @@ healthRouter.get('/health', (c) => {
|
|||||||
ok: true,
|
ok: true,
|
||||||
...(bundleVersion ? { bundle_version: bundleVersion } : {}),
|
...(bundleVersion ? { bundle_version: bundleVersion } : {}),
|
||||||
auth_store: authStoreStatus(c.env),
|
auth_store: authStoreStatus(c.env),
|
||||||
|
// arcrun-rag#38/#69/#25(2026-08-11):安裝器判斷「要不要重推」只比 bundle_version——
|
||||||
|
// 但這次要修的洞是「installer 從沒注入過 PORTAL_MAIL_RELAY_BASE」,跟 bundle 內容
|
||||||
|
// 版本無關(同一個 cypher 版本,有的實例有這個 var、有的沒有)。純比版本號的話,
|
||||||
|
// 已經在最新版的實例(如 leo 自己那台)永遠不會因為「按更新」而重推,這個 var
|
||||||
|
// 就永遠補不進去。只回布林(有沒有設,不回值本身)——不洩漏郵差網址。
|
||||||
|
mail_relay_configured: Boolean(String(c.env.PORTAL_MAIL_RELAY_BASE ?? '').trim()),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -902,8 +902,17 @@ portalRouter.get('/portal/password/reset-link', (c) => {
|
|||||||
// POST /portal/password/forgot — body {email}。**公開端點**(忘記密碼的人當然沒登入)。
|
// POST /portal/password/forgot — body {email}。**公開端點**(忘記密碼的人當然沒登入)。
|
||||||
//
|
//
|
||||||
// 不洩漏帳號存在性:帳號在不在,回的都是同一句話、同一個 200。
|
// 不洩漏帳號存在性:帳號在不在,回的都是同一句話、同一個 200。
|
||||||
// 唯一會回錯的是「這台實例根本沒設代寄服務」——那與「有沒有這個帳號」無關,講出來不洩漏任何事,
|
// 唯一會回錯的是「寄信功能根本沒接上」——那與「有沒有這個帳號」無關,講出來不洩漏任何事,
|
||||||
// 而不講就會讓人對著一封永遠不會到的信等下去(#49「把故障講成用戶的問題」的反面)。
|
// 而不講就會讓人對著一封永遠不會到的信等下去(#49「把故障講成用戶的問題」的反面)。
|
||||||
|
//
|
||||||
|
// 🔴 arcrun-rag#38/#69/#25(2026-08-11 leo 親口點出兩個問題,改字前先讀):
|
||||||
|
// ① 「實例」是行話——目標用戶是「只會叫 AI 幫忙的人」,不懂什麼叫一台實例。
|
||||||
|
// ② 「請管理員直接幫你改密碼」對單人使用者是死路——他自己就是管理員,等於叫他聯絡自己
|
||||||
|
// (`#25` 同一種病:「我忘記 portal 密碼,畫面叫我去找管理員,但管理員就是我」)。
|
||||||
|
// ⇒ 訊息改成白話(不提「實例」),而且給一條他自己走得完的路:**重新跑一次安裝/更新**
|
||||||
|
// 現在**真的有用**(不是安慰話)——arcrun-rag#38/#69/#25 同批修正讓安裝器學會「版本號一樣
|
||||||
|
// 不代表這個功能已經接上,沒接上就當作舊的重推」,所以照原本收到的安裝網址走一次,
|
||||||
|
// 選同一個 Cloudflare 帳號,這個功能就會自動接上,不需要自己改任何設定。
|
||||||
portalRouter.post('/portal/password/forgot', (c) =>
|
portalRouter.post('/portal/password/forgot', (c) =>
|
||||||
run(c, async () => {
|
run(c, async () => {
|
||||||
const body = await c.req.json().catch(() => null);
|
const body = await c.req.json().catch(() => null);
|
||||||
@@ -914,8 +923,9 @@ portalRouter.post('/portal/password/forgot', (c) =>
|
|||||||
return c.json(
|
return c.json(
|
||||||
{
|
{
|
||||||
error:
|
error:
|
||||||
'這台實例還沒有設定寄信服務,「忘記密碼」的連結寄不出去。' +
|
'寄信功能還沒接上,所以「忘記密碼」的信寄不出去。' +
|
||||||
'請重新執行安裝/更新讓它就緒,或請管理員直接幫你改密碼。',
|
'請照當初收到的安裝網址,重新執行一次安裝(選同一個 Cloudflare 帳號)——' +
|
||||||
|
'完成後這個功能就會自動接上,不需要自己設定任何東西,也不用找任何人幫忙。',
|
||||||
code: 'mail_relay_not_configured',
|
code: 'mail_relay_not_configured',
|
||||||
},
|
},
|
||||||
503,
|
503,
|
||||||
@@ -924,7 +934,7 @@ portalRouter.post('/portal/password/forgot', (c) =>
|
|||||||
|
|
||||||
const generic = {
|
const generic = {
|
||||||
success: true,
|
success: true,
|
||||||
message: '如果這個 email 在這台實例上有帳號,我們已經把「修改密碼」的連結寄過去了(連結 30 分鐘內有效、只能用一次)。',
|
message: '如果這個 email 有帳號,我們已經把「修改密碼」的連結寄過去了(連結 30 分鐘內有效、只能用一次)。',
|
||||||
};
|
};
|
||||||
|
|
||||||
// 節流:同一個 email 兩分鐘內只寄一次(擋灌信,也擋拿這支當帳號存在性探針的節奏)
|
// 節流:同一個 email 兩分鐘內只寄一次(擋灌信,也擋拿這支當帳號存在性探針的節奏)
|
||||||
|
|||||||
Reference in New Issue
Block a user