修复AGC登录网络错误与构建门禁
Project CI / Repository checks (push) Failing after 1m3s
Project CI / Frontend tests (push) Successful in 3m3s
Project CI / Backend tests (push) Successful in 4m3s
Project CI / Native shell tests (push) Failing after 3h0m0s

- 登录页归一网络错误并避免暴露底层 transport 文本

- 补充拒绝连接提示与错误信息脱敏回归测试

- master 推送门禁增加 AGC AppSurface 验证
This commit is contained in:
kdletters
2026-08-18 22:15:49 +08:00
parent fe69a49a9c
commit b8dc3fe1b1
5 changed files with 73 additions and 8 deletions
@@ -52,6 +52,27 @@ export function clearStoredAuthAccessToken() {
let clientAuthRefreshPromise: Promise<string> | null = null;
const CLIENT_AUTH_NETWORK_ERROR_MESSAGE =
'无法连接登录服务,请确认配套后端或 API 代理已启动后重试';
function getClientAuthNetworkErrorMessage(error: unknown) {
const detail =
error instanceof Error ? error.message.trim() : String(error).trim();
if (/timed? ?out|timeout|/iu.test(detail)) {
return '无法连接登录服务:连接超时,请检查服务器地址和网络后重试';
}
if (/econnrefused|connection refused|/iu.test(detail)) {
return '无法连接登录服务:服务器拒绝连接,请确认服务已启动并检查端口';
}
if (/dns|resolve|name or service not known|/iu.test(detail)) {
return '无法连接登录服务:服务器地址无法解析,请检查服务器选择';
}
if (/certificate|tls|ssl|/iu.test(detail)) {
return '无法连接登录服务:安全连接失败,请检查服务器地址和证书';
}
return CLIENT_AUTH_NETWORK_ERROR_MESSAGE;
}
class ClientAuthRequestError extends Error {
readonly status: number | null;
readonly networkError: boolean;
@@ -123,14 +144,9 @@ async function requestAuthJson<T>(
headers,
});
} catch (error) {
const detail =
error instanceof Error ? error.message.trim() : String(error).trim();
throw new ClientAuthRequestError(
detail
? `无法连接登录服务:${detail}`
: '无法连接登录服务,请确认配套后端或 API 代理已启动后重试',
{ networkError: true },
);
throw new ClientAuthRequestError(getClientAuthNetworkErrorMessage(error), {
networkError: true,
});
}
if (!response.ok) {
throw new ClientAuthRequestError(
@@ -511,6 +511,44 @@ export function registerAuthTests() {
expect(screen.queryByLabelText('已登录')).toBeNull();
});
it('explains a refused login connection without exposing transport details', async () => {
vi.spyOn(globalThis, 'fetch').mockImplementation(
async (input: RequestInfo | URL) => {
const url = String(input);
if (url === '/api/auth/refresh') {
return new Response('', { status: 401 });
}
if (url === '/api/auth/phone/login') {
throw new TypeError('connect ECONNREFUSED 127.0.0.1:8082');
}
throw new Error(`unexpected fetch ${url}`);
},
);
render(
React.createElement(AuthenticatedClient, null, () =>
React.createElement('main', { 'aria-label': '已登录' }, 'ready'),
),
);
await screen.findByRole('main', { name: '登录' });
fireEvent.change(screen.getByLabelText('手机号'), {
target: { value: '13800000000' },
});
fireEvent.change(screen.getByLabelText('验证码'), {
target: { value: '123456' },
});
fireEvent.click(screen.getByRole('button', { name: '登录' }));
expect(
await screen.findByText(
'无法连接登录服务:服务器拒绝连接,请确认服务已启动并检查端口',
),
).not.toBeNull();
expect(screen.queryByText(/ECONNREFUSED|127\.0\.0\.1:8082/u)).toBeNull();
expect(screen.queryByLabelText('已登录')).toBeNull();
});
it('keeps the stored token when startup auth check cannot reach the service', async () => {
window.localStorage.setItem(
'genarrative.auth.access-token.v1',
@@ -14256,3 +14256,9 @@
- custom 只接受纯 HTTPS origin`localhost` / loopback 的 HTTP 也允许用于本机服务,禁止把路径、查询参数、凭据或非本机明文 HTTP 地址作为服务器地址。
- Tauri release 的 HTTP capability scope 必须覆盖 release、dev、custom HTTPS 以及 loopback HTTP,否则前端选择虽能保存,plugin-http 仍会在请求层拒绝登录。
- 直连 Codex 的本机 External Editor API Key 必须按服务器 origin 独立存储。登录服务器切换后禁止复用另一 origin 的历史 Key 或 base URL;否则会出现登录走新服务器、平台资源生成仍请求旧服务器的漂移。
## 2026-08-18 AGC 登录网络错误与 Web Build 门禁对齐
- 网络 transport 的原始错误(例如浏览器 `Load failed`、URL、底层连接文本)不得直接进入登录页;客户端按超时、拒绝连接、地址解析和 TLS/证书四类可操作原因归一化,其余情况使用统一服务不可达提示。
- 本地 `master` 推送门禁 `scripts/check-repository-ci.sh` 必须在 lint 后运行 Jenkins Web Build 所覆盖的 AGC AppSurface 套件,再执行 build、content 和 diff 检查,避免登录 UI 回归只在远端构建阶段暴露;完整 Vitest 仍由 Jenkins 生产构建执行。
- 验证:`npx vitest run apps/ai-game-creator-shell/tests/appSurface.test.ts -t "shows a clear login service error|explains a refused login connection|keeps the stored token when startup auth check cannot reach the service"``npm run check:git-hooks``npm run check:encoding``git diff --check`
+1
View File
@@ -20,6 +20,7 @@ git cat-file -e "${head_ref}^{commit}" 2>/dev/null || {
echo "[repository-ci] base=${base_ref} head=$(git rev-parse "${head_ref}")"
SPACETIME_SCHEMA_BASE_REF="${base_ref}" npm run lint
npm run test -- apps/ai-game-creator-shell/tests/appSurface.test.ts
npm run build
npm run check:content
git diff --check "${base_ref}"..."${head_ref}"
+4
View File
@@ -257,6 +257,10 @@ test('Gitea Repository checks and master pre-push share the same repository comm
repositoryScript,
/SPACETIME_SCHEMA_BASE_REF="\$\{base_ref\}" npm run lint/u,
);
assert.match(
repositoryScript,
/npm run lint[\s\S]*npm run test -- apps\/ai-game-creator-shell\/tests\/appSurface\.test\.ts[\s\S]*npm run build/u,
);
assert.match(repositoryScript, /npm run build/u);
assert.match(repositoryScript, /npm run check:content/u);
assert.match(repositoryScript, /git diff --check/u);