forked from boos/BoosAPI
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:
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
}),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user