修复AGC登录网络错误与构建门禁
- 登录页归一网络错误并避免暴露底层 transport 文本 - 补充拒绝连接提示与错误信息脱敏回归测试 - master 推送门禁增加 AGC AppSurface 验证
This commit is contained in:
@@ -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`。
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user