Q169: saved cloud and custom endpoints refill their form and keep the saved key
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5cd413ffcf
commit
a8fbc08df6
13 files changed
+160
-23
No files matched your search
@@ -0,0 +1,29 @@
|
||||
import type { DatabaseSync } from "node:sqlite";
|
||||
import type { ProviderEndpoint } from "../../shared/commands";
|
||||
import type { AuthBrokerService } from "./auth-broker-service";
|
||||
import { loadCredential, type ProviderRow } from "./provider-internal";
|
||||
|
||||
function savedPayload(db: DatabaseSync, authBroker: AuthBrokerService, orgId: string, providerId: string): Record<string, unknown> | null {
|
||||
const provider = db.prepare("select * from providers where id = ? and org_id = ?").get(providerId, orgId) as ProviderRow | undefined;
|
||||
if (!provider) throw new Error("Provider was not found.");
|
||||
if (provider.auth_type !== "cloud" || !provider.credential_id) return null;
|
||||
return JSON.parse(loadCredential(authBroker, orgId, provider).secret) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
const text = (value: unknown): string => (typeof value === "string" ? value : "");
|
||||
|
||||
/** The saved address, region, project and deployment of a cloud or custom provider, so its form shows what is in use; never the key. */
|
||||
export function providerEndpoint(db: DatabaseSync, authBroker: AuthBrokerService, orgId: string, providerId: string): ProviderEndpoint | null {
|
||||
const saved = savedPayload(db, authBroker, orgId, providerId);
|
||||
return saved ? { baseUrl: text(saved.baseUrl), region: text(saved.region), projectId: text(saved.projectId), deployment: text(saved.deployment) } : null;
|
||||
}
|
||||
|
||||
/** A cloud form that left its key blank asks to keep the saved key; the saved key goes into the new payload. */
|
||||
export function withSavedSecret(db: DatabaseSync, authBroker: AuthBrokerService, orgId: string, providerId: string, secret: string): string {
|
||||
const payload = JSON.parse(secret) as Record<string, unknown>;
|
||||
if (payload.keepSavedSecret !== true) return secret;
|
||||
const saved = savedPayload(db, authBroker, orgId, providerId);
|
||||
if (!saved) throw new Error("There is no saved key to keep; enter the key.");
|
||||
delete payload.keepSavedSecret;
|
||||
return JSON.stringify({ ...payload, secret: saved.secret });
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { DatabaseSync } from "node:sqlite";
|
||||
import type { CancelProviderResponseInput, ListProviderModelsInput, RevokeProviderCredentialInput, SaveProviderCredentialInput, StreamProviderResponseInput, UpdateProviderPolicyInput, SetProviderPricesInput } from "../../shared/commands";
|
||||
import type { CancelProviderResponseInput, ListProviderModelsInput, ProviderEndpoint, RevokeProviderCredentialInput, SaveProviderCredentialInput, StreamProviderResponseInput, UpdateProviderPolicyInput, SetProviderPricesInput } from "../../shared/commands";
|
||||
import { providerEndpoint, withSavedSecret } from "./provider-endpoint";
|
||||
import type { ProviderModelEntry } from "../../shared/provider-models";
|
||||
import { PROVIDER_AUTH_TYPE, PROVIDER_DATA_POLICY, type ProviderAuthType } from "../../shared/domain";
|
||||
import type { ProviderStreamRendererEvent } from "../../shared/provider-request-domain";
|
||||
@@ -97,8 +98,9 @@ export class ProviderService {
|
||||
const provider = this.db.prepare("select * from providers where id = ? and org_id = ?")
|
||||
.get(input.providerId, input.orgId) as { provider_key: string; auth_type: ProviderAuthType } | undefined;
|
||||
if (!provider) throw new Error("Provider was not found.");
|
||||
const secret = input.secret.trim();
|
||||
if (!secret) throw new Error("Credential is required.");
|
||||
const typed = input.secret.trim();
|
||||
if (!typed) throw new Error("Credential is required.");
|
||||
const secret = provider.auth_type === "cloud" ? withSavedSecret(this.db, this.authBroker, input.orgId, input.providerId, typed) : typed;
|
||||
const adapter = requireAdapter(this.adapters, provider.provider_key, provider.auth_type);
|
||||
adapter.parseAuthProfile(secret);
|
||||
const catalogEntry = requireCatalogEntry(provider.provider_key);
|
||||
@@ -130,6 +132,10 @@ export class ProviderService {
|
||||
});
|
||||
}
|
||||
|
||||
endpoint(orgId: string, providerId: string): ProviderEndpoint | null {
|
||||
return providerEndpoint(this.db, this.authBroker, orgId, providerId);
|
||||
}
|
||||
|
||||
/** Revokes at the provider where it can, then always disconnects and erases the credential here; a remote failure is reported, not hidden. */
|
||||
async revokeCredential(input: RevokeProviderCredentialInput): Promise<void> {
|
||||
const provider = this.db.prepare(`
|
||||
|
||||
Reference in new issue
Block a user