fix: recognize structured oauth accounts in routing (#369)

* fix: recognize structured oauth accounts in routing

* fix: use structured oauth accounts in account list
This commit is contained in:
Cita
2026-04-01 17:15:22 +08:00
committed by GitHub
parent e2d9481b95
commit 21d92e1887
8 changed files with 209 additions and 30 deletions
@@ -276,6 +276,68 @@ describe('accounts credential mode', { timeout: 15_000 }, () => {
});
});
it('uses structured oauth columns when listing oauth account capabilities and runtime health', async () => {
const site = await db.insert(schema.sites).values({
name: 'Structured Codex Site',
url: 'https://chatgpt.com/backend-api/codex',
platform: 'codex',
}).returning().get();
const account = await db.insert(schema.accounts).values({
siteId: site.id,
username: 'structured-oauth@example.com',
accessToken: 'oauth-access-token',
apiToken: null,
status: 'active',
checkinEnabled: false,
oauthProvider: 'codex',
oauthAccountKey: 'chatgpt-account-structured-123',
extraConfig: JSON.stringify({
credentialMode: 'session',
oauth: {
email: 'structured-oauth@example.com',
planType: 'team',
},
}),
}).returning().get();
await db.insert(schema.modelAvailability).values({
accountId: account.id,
modelName: 'gpt-5.2-codex',
available: true,
checkedAt: '2026-04-01T12:00:00.000Z',
}).run();
const listResponse = await app.inject({
method: 'GET',
url: '/api/accounts',
});
expect(listResponse.statusCode).toBe(200);
const list = listResponse.json() as Array<{
id: number;
capabilities?: {
canCheckin?: boolean;
canRefreshBalance?: boolean;
proxyOnly?: boolean;
};
runtimeHealth?: {
state?: string;
reason?: string;
};
}>;
const item = list.find((entry) => entry.id === account.id);
expect(item?.capabilities).toMatchObject({
canCheckin: false,
canRefreshBalance: false,
proxyOnly: true,
});
expect(item?.runtimeHealth).toMatchObject({
state: 'healthy',
reason: '模型探测成功',
});
});
it('stores managed refresh token for sub2api session account', async () => {
verifyTokenMock.mockResolvedValueOnce({
tokenType: 'session',
+6 -13
View File
@@ -105,9 +105,9 @@ function resolveStoredCredentialMode(account: typeof schema.accounts.$inferSelec
function buildCapabilitiesFromCredentialMode(
credentialMode: AccountCredentialMode,
hasSessionToken: boolean,
extraConfig?: string | null,
oauthIdentity?: string | null | Pick<typeof schema.accounts.$inferSelect, 'extraConfig' | 'oauthProvider'>,
): AccountCapabilities {
if (hasOauthProvider(extraConfig)) {
if (hasOauthProvider(oauthIdentity)) {
return {
canCheckin: false,
canRefreshBalance: false,
@@ -126,7 +126,7 @@ function buildCapabilitiesFromCredentialMode(
function buildCapabilitiesForAccount(account: typeof schema.accounts.$inferSelect): AccountCapabilities {
const credentialMode = resolveStoredCredentialMode(account);
return buildCapabilitiesFromCredentialMode(credentialMode, hasSessionTokenValue(account.accessToken), account.extraConfig);
return buildCapabilitiesFromCredentialMode(credentialMode, hasSessionTokenValue(account.accessToken), account);
}
function normalizeBatchIds(input: unknown): number[] {
@@ -498,15 +498,12 @@ export async function accountsRoutes(app: FastifyInstance) {
return rows.map((r) => {
const credentialMode = resolveStoredCredentialMode(r.accounts);
const capabilities = buildCapabilitiesForAccount(r.accounts);
return {
...r.accounts,
site: r.sites,
credentialMode,
capabilities: buildCapabilitiesFromCredentialMode(
credentialMode,
hasSessionTokenValue(r.accounts.accessToken),
r.accounts.extraConfig,
),
capabilities,
todaySpend: Math.round((spendByAccount[r.accounts.id] || 0) * 1_000_000) / 1_000_000,
todayReward: Math.round(estimateRewardWithTodayIncomeFallback({
day: localDay,
@@ -519,11 +516,7 @@ export async function accountsRoutes(app: FastifyInstance) {
accountStatus: r.accounts.status,
siteStatus: r.sites.status,
extraConfig: r.accounts.extraConfig,
sessionCapable: buildCapabilitiesFromCredentialMode(
credentialMode,
hasSessionTokenValue(r.accounts.accessToken),
r.accounts.extraConfig,
).canRefreshBalance,
sessionCapable: capabilities.canRefreshBalance,
hasDiscoveredModels: (modelCountByAccount[r.accounts.id] || 0) > 0,
}),
};