[codex] rename site API request wording (#423)

* rename site api request wording

* replace real site example domains
This commit is contained in:
Cita
2026-04-05 19:16:08 +08:00
committed by GitHub
parent 5b005d8412
commit ef1962d10b
15 changed files with 84 additions and 67 deletions
+21 -21
View File
@@ -62,14 +62,14 @@ describe('accounts api endpoint host selection', { timeout: 15_000 }, () => {
const site = await db.insert(schema.sites).values({
name: 'Nihao Panel',
url: 'https://nih.cc',
url: 'https://console.example.com',
platform: 'new-api',
status: 'active',
}).returning().get();
await db.insert(schema.siteApiEndpoints).values({
siteId: site.id,
url: 'https://api.nih.cc',
url: 'https://api.example.com',
enabled: true,
sortOrder: 0,
}).run();
@@ -90,7 +90,7 @@ describe('accounts api endpoint host selection', { timeout: 15_000 }, () => {
tokenType: 'apikey',
modelCount: 1,
});
expect(getModelsMock).toHaveBeenCalledWith('https://api.nih.cc', 'sk-nihao', undefined);
expect(getModelsMock).toHaveBeenCalledWith('https://api.example.com', 'sk-nihao', undefined);
expect(verifyTokenMock).not.toHaveBeenCalled();
});
@@ -101,7 +101,7 @@ describe('accounts api endpoint host selection', { timeout: 15_000 }, () => {
const site = await db.insert(schema.sites).values({
name: 'Nihao Pool',
url: 'https://nih.cc',
url: 'https://console.example.com',
platform: 'new-api',
status: 'active',
}).returning().get();
@@ -109,13 +109,13 @@ describe('accounts api endpoint host selection', { timeout: 15_000 }, () => {
await db.insert(schema.siteApiEndpoints).values([
{
siteId: site.id,
url: 'https://api-a.nih.cc',
url: 'https://api-a.example.com',
enabled: true,
sortOrder: 0,
},
{
siteId: site.id,
url: 'https://api-b.nih.cc',
url: 'https://api-b.example.com',
enabled: true,
sortOrder: 1,
},
@@ -137,12 +137,12 @@ describe('accounts api endpoint host selection', { timeout: 15_000 }, () => {
tokenType: 'apikey',
modelCount: 1,
});
expect(getModelsMock).toHaveBeenNthCalledWith(1, 'https://api-a.nih.cc', 'sk-rotate', undefined);
expect(getModelsMock).toHaveBeenNthCalledWith(2, 'https://api-b.nih.cc', 'sk-rotate', undefined);
expect(getModelsMock).toHaveBeenNthCalledWith(1, 'https://api-a.example.com', 'sk-rotate', undefined);
expect(getModelsMock).toHaveBeenNthCalledWith(2, 'https://api-b.example.com', 'sk-rotate', undefined);
const endpoints = await db.select().from(schema.siteApiEndpoints).all();
const firstEndpoint = endpoints.find((item) => item.url === 'https://api-a.nih.cc');
const secondEndpoint = endpoints.find((item) => item.url === 'https://api-b.nih.cc');
const firstEndpoint = endpoints.find((item) => item.url === 'https://api-a.example.com');
const secondEndpoint = endpoints.find((item) => item.url === 'https://api-b.example.com');
expect(firstEndpoint?.cooldownUntil).toBeTruthy();
expect(firstEndpoint?.lastFailureReason).toContain('HTTP 502');
expect(secondEndpoint?.lastSelectedAt).toBeTruthy();
@@ -158,14 +158,14 @@ describe('accounts api endpoint host selection', { timeout: 15_000 }, () => {
const site = await db.insert(schema.sites).values({
name: 'Nihao Panel',
url: 'https://nih.cc',
url: 'https://console.example.com',
platform: 'new-api',
status: 'active',
}).returning().get();
await db.insert(schema.siteApiEndpoints).values({
siteId: site.id,
url: 'https://api.nih.cc',
url: 'https://api.example.com',
enabled: true,
sortOrder: 0,
}).run();
@@ -186,7 +186,7 @@ describe('accounts api endpoint host selection', { timeout: 15_000 }, () => {
tokenType: 'session',
apiToken: 'sk-derived',
});
expect(verifyTokenMock).toHaveBeenCalledWith('https://nih.cc', 'session-token', undefined);
expect(verifyTokenMock).toHaveBeenCalledWith('https://console.example.com', 'session-token', undefined);
expect(getModelsMock).not.toHaveBeenCalled();
});
@@ -195,14 +195,14 @@ describe('accounts api endpoint host selection', { timeout: 15_000 }, () => {
const site = await db.insert(schema.sites).values({
name: 'Nihao Panel',
url: 'https://nih.cc',
url: 'https://console.example.com',
platform: 'new-api',
status: 'active',
}).returning().get();
await db.insert(schema.siteApiEndpoints).values({
siteId: site.id,
url: 'https://api.nih.cc',
url: 'https://api.example.com',
enabled: true,
sortOrder: 0,
}).run();
@@ -221,7 +221,7 @@ describe('accounts api endpoint host selection', { timeout: 15_000 }, () => {
expect(response.json()).toMatchObject({
tokenType: 'apikey',
});
expect(getModelsMock).toHaveBeenCalledWith('https://api.nih.cc', 'sk-nihao-create', undefined);
expect(getModelsMock).toHaveBeenCalledWith('https://api.example.com', 'sk-nihao-create', undefined);
});
it('rotates API key account creation across configured ai endpoints after a retryable failure', async () => {
@@ -231,7 +231,7 @@ describe('accounts api endpoint host selection', { timeout: 15_000 }, () => {
const site = await db.insert(schema.sites).values({
name: 'Nihao Create Pool',
url: 'https://nih.cc',
url: 'https://console.example.com',
platform: 'new-api',
status: 'active',
}).returning().get();
@@ -239,13 +239,13 @@ describe('accounts api endpoint host selection', { timeout: 15_000 }, () => {
await db.insert(schema.siteApiEndpoints).values([
{
siteId: site.id,
url: 'https://api-create-a.nih.cc',
url: 'https://api-create-a.example.com',
enabled: true,
sortOrder: 0,
},
{
siteId: site.id,
url: 'https://api-create-b.nih.cc',
url: 'https://api-create-b.example.com',
enabled: true,
sortOrder: 1,
},
@@ -265,7 +265,7 @@ describe('accounts api endpoint host selection', { timeout: 15_000 }, () => {
expect(response.json()).toMatchObject({
tokenType: 'apikey',
});
expect(getModelsMock).toHaveBeenNthCalledWith(1, 'https://api-create-a.nih.cc', 'sk-nihao-create-rotate', undefined);
expect(getModelsMock).toHaveBeenNthCalledWith(2, 'https://api-create-b.nih.cc', 'sk-nihao-create-rotate', undefined);
expect(getModelsMock).toHaveBeenNthCalledWith(1, 'https://api-create-a.example.com', 'sk-nihao-create-rotate', undefined);
expect(getModelsMock).toHaveBeenNthCalledWith(2, 'https://api-create-b.example.com', 'sk-nihao-create-rotate', undefined);
});
});
@@ -150,7 +150,7 @@ describe('chat proxy site api endpoint rotation', () => {
it('rotates to the next configured ai endpoint for retryable /v1/chat/completions failures', async () => {
const site = await db.insert(schema.sites).values({
name: 'nihao-panel',
url: 'https://nih.cc',
url: 'https://console.example.com',
platform: 'openai',
status: 'active',
}).returning().get();
@@ -168,13 +168,13 @@ describe('chat proxy site api endpoint rotation', () => {
await db.insert(schema.siteApiEndpoints).values([
{
siteId: site.id,
url: 'https://api-a.nih.cc',
url: 'https://api-a.example.com',
enabled: true,
sortOrder: 0,
},
{
siteId: site.id,
url: 'https://api-b.nih.cc',
url: 'https://api-b.example.com',
enabled: true,
sortOrder: 1,
},
@@ -222,10 +222,10 @@ describe('chat proxy site api endpoint rotation', () => {
expect(response.statusCode).toBe(200);
expect(response.json()?.choices?.[0]?.message?.content).toBe('ok via api-b');
expect(fetchMock).toHaveBeenCalledTimes(4);
expect(String(fetchMock.mock.calls[0]?.[0] || '')).toBe('https://api-a.nih.cc/v1/chat/completions');
expect(String(fetchMock.mock.calls[1]?.[0] || '')).toBe('https://api-a.nih.cc/v1/responses');
expect(String(fetchMock.mock.calls[2]?.[0] || '')).toBe('https://api-a.nih.cc/v1/messages');
expect(String(fetchMock.mock.calls[3]?.[0] || '')).toBe('https://api-b.nih.cc/v1/chat/completions');
expect(String(fetchMock.mock.calls[0]?.[0] || '')).toBe('https://api-a.example.com/v1/chat/completions');
expect(String(fetchMock.mock.calls[1]?.[0] || '')).toBe('https://api-a.example.com/v1/responses');
expect(String(fetchMock.mock.calls[2]?.[0] || '')).toBe('https://api-a.example.com/v1/messages');
expect(String(fetchMock.mock.calls[3]?.[0] || '')).toBe('https://api-b.example.com/v1/chat/completions');
expect(selectNextChannelMock).not.toHaveBeenCalled();
expect(recordFailureMock).not.toHaveBeenCalled();
expect(recordSuccessMock).toHaveBeenCalledTimes(1);
@@ -235,7 +235,7 @@ describe('chat proxy site api endpoint rotation', () => {
.orderBy(asc(schema.siteApiEndpoints.sortOrder), asc(schema.siteApiEndpoints.id))
.all();
expect(storedEndpoints[0]).toMatchObject({
url: 'https://api-a.nih.cc',
url: 'https://api-a.example.com',
lastFailureReason: 'HTTP 502: [upstream:/v1/messages] Upstream returned HTTP 502: bad gateway via messages',
});
expect(storedEndpoints[0]?.cooldownUntil).toBeTruthy();
@@ -120,7 +120,7 @@ describe('/v1/completions site api endpoint rotation', () => {
it('cools down a retryable failed endpoint and retries the next endpoint within the same site', async () => {
const site = await db.insert(schema.sites).values({
name: 'nihao-panel',
url: 'https://nih.cc',
url: 'https://console.example.com',
platform: 'new-api',
status: 'active',
}).returning().get();
@@ -138,13 +138,13 @@ describe('/v1/completions site api endpoint rotation', () => {
await db.insert(schema.siteApiEndpoints).values([
{
siteId: site.id,
url: 'https://api-a.nih.cc',
url: 'https://api-a.example.com',
enabled: true,
sortOrder: 0,
},
{
siteId: site.id,
url: 'https://api-b.nih.cc',
url: 'https://api-b.example.com',
enabled: true,
sortOrder: 1,
},
@@ -194,8 +194,8 @@ describe('/v1/completions site api endpoint rotation', () => {
object: 'text_completion',
});
expect(fetchMock).toHaveBeenCalledTimes(2);
expect(String(fetchMock.mock.calls[0]?.[0] || '')).toBe('https://api-a.nih.cc/v1/completions');
expect(String(fetchMock.mock.calls[1]?.[0] || '')).toBe('https://api-b.nih.cc/v1/completions');
expect(String(fetchMock.mock.calls[0]?.[0] || '')).toBe('https://api-a.example.com/v1/completions');
expect(String(fetchMock.mock.calls[1]?.[0] || '')).toBe('https://api-b.example.com/v1/completions');
expect(selectNextChannelMock).not.toHaveBeenCalled();
expect(recordFailureMock).not.toHaveBeenCalled();
expect(recordSuccessMock).toHaveBeenCalledTimes(1);
@@ -205,12 +205,12 @@ describe('/v1/completions site api endpoint rotation', () => {
.orderBy(asc(schema.siteApiEndpoints.sortOrder), asc(schema.siteApiEndpoints.id))
.all();
expect(storedEndpoints[0]).toMatchObject({
url: 'https://api-a.nih.cc',
url: 'https://api-a.example.com',
lastFailureReason: 'HTTP 502: bad gateway',
});
expect(storedEndpoints[0]?.cooldownUntil).toBeTruthy();
expect(storedEndpoints[1]).toMatchObject({
url: 'https://api-b.nih.cc',
url: 'https://api-b.example.com',
});
expect(storedEndpoints[1]?.lastSelectedAt).toBeTruthy();
});
@@ -617,7 +617,7 @@ describe('responses websocket transport', () => {
type: 'error',
status: 408,
error: {
message: '当前站点的 AI 请求地址均不可用',
message: '当前站点的 API 请求地址均不可用',
},
});
expect(fetchMock).toHaveBeenCalledTimes(0);
@@ -138,21 +138,21 @@ describe('refreshModelsForAccount credential discovery', () => {
it('uses the configured ai endpoint for direct model discovery credentials', async () => {
getApiTokenMock.mockResolvedValue(null);
getModelsMock.mockImplementation(async (baseUrl: string, token: string) => (
baseUrl === 'https://api.nih.cc' && token === 'session-token'
baseUrl === 'https://api.example.com' && token === 'session-token'
? ['gpt-4.1']
: []
));
const site = await db.insert(schema.sites).values({
name: 'nihao-panel',
url: 'https://nih.cc',
url: 'https://console.example.com',
platform: 'new-api',
status: 'active',
}).returning().get();
await db.insert(schema.siteApiEndpoints).values({
siteId: site.id,
url: 'https://api.nih.cc',
url: 'https://api.example.com',
enabled: true,
sortOrder: 0,
}).run();
@@ -175,7 +175,7 @@ describe('refreshModelsForAccount credential discovery', () => {
modelCount: 1,
modelsPreview: ['gpt-4.1'],
});
expect(getModelsMock).toHaveBeenCalledWith('https://api.nih.cc', 'session-token', undefined);
expect(getModelsMock).toHaveBeenCalledWith('https://api.example.com', 'session-token', undefined);
});
it('deduplicates discovered model names before writing availability rows', async () => {
@@ -207,7 +207,7 @@ export async function requireSiteApiBaseUrl(
): Promise<string> {
const baseUrl = await resolveSiteApiBaseUrl(site, now);
if (baseUrl) return baseUrl;
throw new Error('当前站点的 AI 请求地址均不可用');
throw new Error('当前站点的 API 请求地址均不可用');
}
export async function recordSiteApiEndpointFailure(
@@ -258,11 +258,11 @@ export async function runWithSiteApiEndpointPool<T>(
const target = await selectSiteApiEndpointTarget(site);
if (!target) {
if (lastError) throw lastError;
throw new Error('当前站点的 AI 请求地址均不可用');
throw new Error('当前站点的 API 请求地址均不可用');
}
if (target.endpointId && attemptedEndpointIds.has(target.endpointId)) {
if (lastError) throw lastError;
throw new Error('当前站点的 AI 请求地址均不可用');
throw new Error('当前站点的 API 请求地址均不可用');
}
try {
+9 -9
View File
@@ -517,7 +517,7 @@ export default function Sites() {
}
const serializedApiEndpoints = serializeSiteApiEndpoints(form.apiEndpoints);
if (!serializedApiEndpoints.valid) {
toast.error(serializedApiEndpoints.error || 'AI 请求地址格式不正确');
toast.error(serializedApiEndpoints.error || 'API 请求地址格式不正确');
return;
}
@@ -1028,7 +1028,7 @@ export default function Sites() {
<div style={{ display: 'flex', gap: 8, flexDirection: isMobile ? 'column' : 'row' }}>
<input
data-testid="site-primary-url-input"
placeholder="站点 URL(面板/登录/签到地址,如 https://nih.cc)"
placeholder="站点 URL(面板/登录/签到地址,如 https://console.example.com)"
value={form.url}
onChange={(e) => setForm((prev) => ({ ...prev, url: e.target.value }))}
onBlur={() => {
@@ -1102,7 +1102,7 @@ export default function Sites() {
</div>
)}
<div style={{ fontSize: 12, color: 'var(--color-text-muted)', lineHeight: 1.6 }}>
主站点 URL 用于登录、签到、面板接口和系统访问令牌管理;如果 AI 请求地址和主站点不同,请在下面的 AI 请求地址池里填写。
主站点 URL 用于登录、签到、面板接口和系统访问令牌管理;如果 API 请求地址和主站点不同,请在下面的 API 请求地址池里填写。
</div>
<div
style={{
@@ -1117,7 +1117,7 @@ export default function Sites() {
>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
<div style={{ fontSize: 13, fontWeight: 600 }}>
AI 请求地址池
API 请求地址池
</div>
<button
type="button"
@@ -1125,7 +1125,7 @@ export default function Sites() {
className="btn btn-ghost"
style={{ border: '1px solid var(--color-border)' }}
>
+ 添加 AI 地址
+ 添加 API 地址
</button>
</div>
<div style={{ fontSize: 12, color: 'var(--color-text-muted)', lineHeight: 1.7 }}>
@@ -1146,7 +1146,7 @@ export default function Sites() {
>
<div style={{ display: 'flex', gap: 8, flexDirection: isMobile ? 'column' : 'row', alignItems: isMobile ? 'stretch' : 'center' }}>
<input
placeholder="AI 请求地址(如 https://api.nih.cc)"
placeholder="API 请求地址(如 https://api.example.com)"
value={endpoint.url}
onChange={(e) => updateApiEndpointRow(index, { url: e.target.value })}
style={{ ...formInputStyle, flex: 1, fontFamily: 'var(--font-mono)' }}
@@ -1405,7 +1405,7 @@ export default function Sites() {
style={formInputStyle}
/>
<div style={{ fontSize: 12, color: 'var(--color-text-muted)' }}>
这里只是 HTTP/SOCKS 代理地址,不是上游 AI API 地址。填写后优先使用站点代理;留空则使用系统代理或直连(取决于设置开关状态)。
这里只是 HTTP/SOCKS 代理地址,不是上游 API 请求地址。填写后优先使用站点代理;留空则使用系统代理或直连(取决于设置开关状态)。
</div>
</div>
<label style={{
@@ -1556,7 +1556,7 @@ export default function Sites() {
) : '-'}
/>
<MobileField
label="AI 请求地址"
label="API 请求地址"
stacked
value={(
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
@@ -1716,7 +1716,7 @@ export default function Sites() {
</span>
) : null}
<span className={`badge ${getConfiguredSiteApiEndpoints(site).length > 0 ? 'badge-warning' : 'badge-muted'}`} style={{ fontSize: 11 }}>
AI 地址: {buildSiteApiEndpointSummary(site)}
API 地址: {buildSiteApiEndpointSummary(site)}
</span>
</div>
</td>
+1 -1
View File
@@ -191,7 +191,7 @@ describe('buildSiteSaveAction', () => {
])).toEqual({
valid: false,
apiEndpoints: [],
error: 'AI 请求地址 "https://api.example.com" 重复了',
error: 'API 请求地址 "https://api.example.com" 重复了',
});
});
});
+1 -1
View File
@@ -225,7 +225,7 @@ export function serializeSiteApiEndpoints(fields: SiteApiEndpointField[]): {
return {
valid: false,
apiEndpoints: [],
error: `AI 请求地址 "${normalizedUrl}" 重复了`,
error: `API 请求地址 "${normalizedUrl}" 重复了`,
};
}
seen.add(normalizedUrl);
@@ -10,4 +10,21 @@ describe('Sites centered modal adoption', () => {
expect(source).toContain('<CenteredModal');
expect(source).not.toContain('editorPresence.shouldRender && activeEditor && (');
});
it('uses API request wording for dedicated site endpoint copy', () => {
const source = readFileSync(resolve(process.cwd(), 'src/web/pages/Sites.tsx'), 'utf8');
expect(source).toContain('API 请求地址池');
expect(source).toContain('+ 添加 API 地址');
expect(source).toContain('站点 URL(面板/登录/签到地址,如 https://console.example.com)');
expect(source).toContain('API 请求地址(如 https://api.example.com)');
expect(source).toContain('label="API 请求地址"');
expect(source).toContain('API 地址: {buildSiteApiEndpointSummary(site)}');
expect(source).not.toContain('nih.cc');
expect(source).not.toContain('api.nih.cc');
expect(source).not.toContain('AI 请求地址池');
expect(source).not.toContain('+ 添加 AI 地址');
expect(source).not.toContain('label="AI 请求地址"');
expect(source).not.toContain('AI 地址: {buildSiteApiEndpointSummary(site)}');
});
});