import { api } from './client' export type CredentialType = 'basic' | 'bearer' | 'api_key' | 'oauth2' | 'imap' | 'smtp' | 'mysql' | 'postgresql' export interface CredentialInfo { id: string name: string description?: string type: CredentialType createdBy: string createdAt: number // The named type this was created as, when a node registered one. The type // above is still what decides how it is stored and used. declaredType?: string updatedAt: number allowedWorkflows: string[] /** How many workflows name this credential - the way to tell two of the same name apart. */ usedByWorkflows?: number projectId?: string publicData?: Record // Non-secret fields for editing } export interface BasicAuthData { username: string password: string } export interface BearerTokenData { token: string } export interface ApiKeyData { header_name: string key_value: string } export interface OAuth2Data { grant_type: 'client_credentials' | 'authorization_code' client_id: string client_secret: string token_url: string auth_url?: string scope?: string access_token?: string refresh_token?: string expires_at?: number } export interface ImapData { host: string port: number username: string password: string use_ssl: boolean } export interface MysqlData { host: string port: number username: string password: string database: string use_ssl: boolean } export interface PostgresqlData { host: string port: number username: string password: string database: string use_ssl: boolean } export interface CreateCredentialRequest { // A node-registered type such as "sdcpp". Optional: the built-in type below // is still what the store uses. declaredType?: string name: string description?: string type: CredentialType data: BasicAuthData | BearerTokenData | ApiKeyData | OAuth2Data | ImapData | MysqlData | PostgresqlData allowedWorkflows?: string[] /** Which project it belongs to, and so who can see and use it. */ projectId?: string } export interface UpdateCredentialRequest { name?: string description?: string data?: BasicAuthData | BearerTokenData | ApiKeyData | OAuth2Data | ImapData | MysqlData | PostgresqlData allowedWorkflows?: string[] } // Transform backend credential data to frontend format function transformCredential(data: any): CredentialInfo { // Everything the server sent, then the handful of fields that need a // different name or a default. // // Listing the fields one by one is how "usedByWorkflows" arrived from the // server and never reached the screen - the same way the workflow mapper // dropped the record version. A mapper that names every field silently // discards every field added after it was written, and nothing fails. return { ...data, id: data.id || data._id, createdBy: data.createdBy || data.ownerId, // Sent by the server in milliseconds; it reads the document metadata itself. createdAt: data.createdAt, updatedAt: data.updatedAt, declaredType: data.declaredType || undefined, allowedWorkflows: data.allowedWorkflows || [], } } export const credentialsApi = { list: async (): Promise<{ credentials: CredentialInfo[] }> => { const response = await api.get('/credentials') return { ...response.data, credentials: (response.data.credentials || []).map(transformCredential), } }, get: async (id: string): Promise => { const response = await api.get(`/credentials/${id}`) return transformCredential(response.data) }, create: async (data: CreateCredentialRequest): Promise => { const response = await api.post('/credentials', data) return transformCredential(response.data) }, update: async (id: string, data: UpdateCredentialRequest): Promise => { const response = await api.put(`/credentials/${id}`, data) return transformCredential(response.data) }, delete: async (id: string): Promise => { await api.delete(`/credentials/${id}`) }, refreshOAuth2: async (id: string): Promise<{ success: boolean; expiresAt?: number }> => { const response = await api.post(`/credentials/${id}/refresh`) return response.data }, }