Documentation
¶
Overview ¶
Package dto holds response shapes for handler responses that need to differ from the persisted GORM model — most notably, response types that must NOT carry secret fields.
Why a separate package: response DTOs are deliberately distinct from the internal model so the "no secret in responses" guarantee is a compile-time invariant rather than a runtime redaction step. If a future contributor wants to expose a credential in a response, they must add it to the DTO explicitly, which makes the leak surface review-able in a single diff.
Index ¶
- func AllSecretExtraConfigKeys() map[string]bool
- func AttachMCPCatalogs(resp []*MCPServiceResponse, services []*types.MCPService, ...)
- func CanViewIntegrationSecrets(ctx context.Context) bool
- func CanViewTenantAPIKey(ctx context.Context) bool
- func HasAllSecretExtras(provider, baseURL string, extra map[string]string) bool
- func PreserveStoredSecretExtras(stored, incoming map[string]string, storedVendor, edited VendorRef) map[string]string
- func RoleCanViewTenantAPIKey(role types.TenantRole) bool
- func RoleFromContext(ctx context.Context) types.TenantRole
- func SecretExtraConfigKeys(provider, baseURL string) map[string]bool
- type AuthLoginResponse
- type AuthOIDCCallbackResponse
- type CredentialFieldMetadata
- type CredentialsResponse
- type DataSourceConfigDTO
- type DataSourceResponse
- type MCPAuthConfigResponse
- type MCPCatalogSummary
- type MCPServiceResponse
- type ModelParametersDTO
- type ModelResponse
- type TenantResponse
- func NewTenantResponse(ctx context.Context, tenant *types.Tenant) *TenantResponse
- func NewTenantResponseWithRole(tenant *types.Tenant, role types.TenantRole) *TenantResponse
- func NewTenantResponses(ctx context.Context, tenants []*types.Tenant) []*TenantResponse
- func NewTenantResponsesCrossTenant(tenants []*types.Tenant) []*TenantResponse
- type VendorRef
- type WebSearchProviderParametersDTO
- type WebSearchProviderResponse
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AllSecretExtraConfigKeys ¶
AllSecretExtraConfigKeys returns every extra_config key any registered vendor declares secret.
Redaction must not depend on the row's own provider being the one that declared the key: a row whose provider was switched, hand-edited or never stored would otherwise echo a credential in plaintext simply because its current vendor declares no secret field. Treating a name that is secret somewhere as secret everywhere is the safe direction — the worst case is withholding a value that happened to reuse the name.
func AttachMCPCatalogs ¶
func AttachMCPCatalogs( resp []*MCPServiceResponse, services []*types.MCPService, summaries map[string]*types.MCPMetadataSummary, )
AttachMCPCatalogs copies persisted directory counts onto list/detail responses.
func CanViewIntegrationSecrets ¶
CanViewIntegrationSecrets is true for Admin+ tenant members and for API keys with full tenant access or the manage_tenant_settings capability.
func CanViewTenantAPIKey ¶
CanViewTenantAPIKey is true for Owner+ only.
func HasAllSecretExtras ¶
HasAllSecretExtras reports whether extra already carries a usable value for every secret extra field the vendor declares. Callers that would otherwise skip a lookup of the stored row use it to notice that the request is missing a redacted secret.
func PreserveStoredSecretExtras ¶
func PreserveStoredSecretExtras( stored, incoming map[string]string, storedVendor, edited VendorRef, ) map[string]string
PreserveStoredSecretExtras merges the stored secret extra_config values into an incoming map.
PUT /models/{id} replaces extra_config wholesale and the frontend always sends the map for remote rows, but GET redacts the vendor's secret fields, so a plain edit-and-save round-trip carries no value for them. Without this merge the first save after any unrelated edit would erase the stored secret key and break rerank for that row. An incoming value that is absent, empty or a mask means "unchanged"; anything else replaces the stored secret.
storedVendor identifies the vendor the stored values belong to; edited is the vendor the incoming values are for. They differ when the user moves the row to another vendor, and then the stored credential must be dropped rather than carried over: it belongs to the previous integration's account, the editor no longer renders a field for it, and the new vendor may not declare it secret at all — which is how it ended up echoed in plaintext.
incoming is filled in place and returned.
func RoleCanViewTenantAPIKey ¶
func RoleCanViewTenantAPIKey(role types.TenantRole) bool
RoleCanViewTenantAPIKey is true for Owner+ only.
func RoleFromContext ¶
func RoleFromContext(ctx context.Context) types.TenantRole
RoleFromContext returns the caller's tenant role from ctx.
func SecretExtraConfigKeys ¶
SecretExtraConfigKeys returns the extra_config keys the vendor declares as secret (providers.ExtraField.Secret, or a password input). These are real credentials — LKEAP and Volcengine rerank keep an IAM/CAM secret key there — so they get the same treatment as api_key / app_secret: never echoed, only reported as present.
The field's ModelTypes restriction is deliberately ignored: a key that is secret for any model type of the vendor is redacted for every row of that vendor.
Types ¶
type AuthLoginResponse ¶
type AuthLoginResponse struct {
Success bool `json:"success"`
Message string `json:"message,omitempty"`
User *types.User `json:"user,omitempty"`
ActiveTenant *TenantResponse `json:"active_tenant,omitempty"`
Memberships []types.Membership `json:"memberships"`
Token string `json:"token,omitempty"`
RefreshToken string `json:"refresh_token,omitempty"`
}
AuthLoginResponse is the HTTP-safe login / switch-tenant response shape.
func NewAuthLoginResponse ¶
func NewAuthLoginResponse(resp *types.LoginResponse) *AuthLoginResponse
NewAuthLoginResponse converts a service-layer login response for HTTP output.
type AuthOIDCCallbackResponse ¶
type AuthOIDCCallbackResponse struct {
Success bool `json:"success"`
Message string `json:"message,omitempty"`
User *types.User `json:"user,omitempty"`
Tenant *TenantResponse `json:"tenant,omitempty"`
Memberships []types.Membership `json:"memberships"`
Token string `json:"token,omitempty"`
RefreshToken string `json:"refresh_token,omitempty"`
IsNewUser bool `json:"is_new_user,omitempty"`
}
AuthOIDCCallbackResponse is the HTTP-safe OIDC callback payload shape.
func NewAuthOIDCCallbackResponse ¶
func NewAuthOIDCCallbackResponse(resp *types.OIDCCallbackResponse) *AuthOIDCCallbackResponse
NewAuthOIDCCallbackResponse converts an OIDC callback response for HTTP output.
type CredentialFieldMetadata ¶
type CredentialFieldMetadata struct {
Configured bool `json:"configured"`
}
CredentialFieldMetadata reports whether a credential field has a value stored server-side, without exposing the value itself.
type CredentialsResponse ¶
type CredentialsResponse struct {
Fields map[string]CredentialFieldMetadata `json:"fields"`
}
CredentialsResponse is the shared shape returned by PUT /{resource}/{id}/credentials. Keyed by field name (e.g. "api_key", "token"). The frontend uses this to update its in-memory metadata after a successful save without needing to re-fetch the whole resource.
type DataSourceConfigDTO ¶
type DataSourceConfigDTO struct {
Type string `json:"type"`
ResourceIDs []string `json:"resource_ids,omitempty" extensions:"x-agent"`
Settings map[string]interface{} `json:"settings,omitempty"`
}
DataSourceConfigDTO is types.DataSourceConfig with the Credentials map removed by construction. Type, ResourceIDs and Settings remain visible.
type DataSourceResponse ¶
type DataSourceResponse struct {
ID string `json:"id" extensions:"x-agent=data_source_id"`
TenantID uint64 `json:"tenant_id"`
KnowledgeBaseID string `json:"knowledge_base_id" extensions:"x-agent"`
Name string `json:"name" extensions:"x-agent"`
Type string `json:"type" extensions:"x-agent"`
Config *DataSourceConfigDTO `json:"config,omitempty" extensions:"x-agent"`
SyncSchedule string `json:"sync_schedule" extensions:"x-agent"`
SyncMode string `json:"sync_mode" extensions:"x-agent"`
Status string `json:"status" extensions:"x-agent"`
ConflictStrategy string `json:"conflict_strategy" extensions:"x-agent"`
SyncDeletions bool `json:"sync_deletions" extensions:"x-agent"`
LastSyncAt *time.Time `json:"last_sync_at" extensions:"x-agent"`
LastSyncCursor json.RawMessage `json:"last_sync_cursor,omitempty"`
LastSyncResult json.RawMessage `json:"last_sync_result,omitempty"`
ErrorMessage string `json:"error_message,omitempty"`
SyncLogRetentionDays int `json:"sync_log_retention_days"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
TotalItemsSynced int64 `json:"total_items_synced" extensions:"x-agent"`
LatestSyncLog *types.SyncLog `json:"latest_sync_log,omitempty"`
// Single logical credential field — DataSource credentials are a
// per-connector atomic map, so "configured?" applies to the whole set.
Credentials map[string]CredentialFieldMetadata `json:"credentials,omitempty"`
}
DataSourceResponse mirrors types.DataSource for response bodies, with the connector Credentials map stripped from the Config jsonb. Credential presence is exposed via the dedicated /credentials subresource.
Unlike MCP / Model / WebSearch (which have a flat set of named credential fields), DataSource credentials are a per-connector atomic map — an OAuth token pair, a Confluence email+token bundle, etc. Splitting them at the field level would leave half-configured states that can't actually authenticate. The subresource therefore exposes only one logical field, "credentials", with PUT replacing the whole map and DELETE wiping it.
func NewDataSourceResponse ¶
func NewDataSourceResponse(ds *types.DataSource) *DataSourceResponse
NewDataSourceResponse converts a stored entity into its response shape. Returns nil for nil input.
func NewDataSourceResponses ¶
func NewDataSourceResponses(dss []*types.DataSource) []*DataSourceResponse
NewDataSourceResponses converts a list of entities for the list endpoint. The sync cursor is stripped from every element: the settings page polls the list every few seconds during a sync, cursors can grow to megabytes, and no list consumer reads them. Detail, create and update responses still carry the cursor through NewDataSourceResponse.
type MCPAuthConfigResponse ¶
type MCPAuthConfigResponse struct {
AuthType types.MCPAuthType `json:"auth_type,omitempty" extensions:"x-agent"`
APIKeyHeader string `json:"api_key_header,omitempty"`
CustomHeaders map[string]string `json:"custom_headers,omitempty"`
Scopes []string `json:"scopes,omitempty"`
AuthServerMetadataURL string `json:"auth_server_metadata_url,omitempty"`
ClientID string `json:"client_id,omitempty"`
}
MCPAuthConfigResponse intentionally has no APIKey, Token or ClientSecret fields. Their presence is signalled via MCPServiceResponse.Credentials. AuthType, Scopes, AuthServerMetadataURL and ClientID are non-secret OAuth configuration and are safe to echo back so the UI can render the current strategy.
type MCPCatalogSummary ¶
type MCPCatalogSummary struct {
ToolCount int `json:"tool_count"`
Stale bool `json:"stale"`
SyncedAt time.Time `json:"synced_at"`
}
MCPCatalogSummary is the list-card view of a saved MCP directory.
type MCPServiceResponse ¶
type MCPServiceResponse struct {
UsageInstructions string `json:"usage_instructions"`
ID string `json:"id" extensions:"x-agent=mcp_service_id"`
TenantID uint64 `json:"tenant_id"`
Name string `json:"name" extensions:"x-agent"`
Description string `json:"description" extensions:"x-agent"`
Enabled bool `json:"enabled" extensions:"x-agent"`
TransportType types.MCPTransportType `json:"transport_type" extensions:"x-agent"`
URL *string `json:"url,omitempty"`
Headers types.MCPHeaders `json:"headers,omitempty"`
AuthConfig *MCPAuthConfigResponse `json:"auth_config,omitempty" extensions:"x-agent"`
AdvancedConfig *types.MCPAdvancedConfig `json:"advanced_config,omitempty"`
StdioConfig *types.MCPStdioConfig `json:"stdio_config,omitempty"`
EnvVars types.MCPEnvVars `json:"env_vars,omitempty"`
IsBuiltin bool `json:"is_builtin" extensions:"x-agent"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Credentials is the per-field "configured?" map. Embedded on the main
// response so the credential UI doesn't need a follow-up GET. The
// frontend never sees the actual secret value — only whether one is
// stored. Omitted entirely for builtin services (they can't have
// per-tenant credentials).
Credentials map[string]CredentialFieldMetadata `json:"credentials,omitempty"`
// Catalog is the persisted tool-directory summary for list cards.
// Omitted when this principal has never synchronized the service.
Catalog *MCPCatalogSummary `json:"catalog,omitempty"`
}
MCPServiceResponse mirrors types.MCPService for response bodies, omitting every secret field (api_key, token). Credential presence/absence is exposed separately via the /credentials subresource endpoint; this shape carries only a boolean per credential field so the frontend can render a "configured / not configured" badge without an additional round-trip.
func NewMCPServiceResponse ¶
func NewMCPServiceResponse(ctx context.Context, svc *types.MCPService) *MCPServiceResponse
NewMCPServiceResponse converts a stored MCPService into its response shape.
Builtin MCP services have their tenant-specific transport details (URL, Headers, EnvVars, StdioConfig) stripped — these reveal how the tenant configured an upstream provider and must not be visible to other tenants that see the same builtin row via the cross-tenant list.
func NewMCPServiceResponses ¶
func NewMCPServiceResponses(ctx context.Context, svcs []*types.MCPService) []*MCPServiceResponse
NewMCPServiceResponses is the slice convenience wrapper used by ListMCPServices.
func NewSharedAgentMCPServiceResponses ¶
func NewSharedAgentMCPServiceResponses(svcs []*types.MCPService) []*MCPServiceResponse
NewSharedAgentMCPServiceResponses is the cross-workspace list shape: the @MCP picker of a borrowed agent needs a service's identity and how many tools it offers, and nothing else.
It fills the fields explicitly instead of reusing NewMCPServiceResponse and blanking the rest, so a field added to MCPServiceResponse later is absent here until someone decides it may cross a workspace boundary. Endpoint URLs, headers, env vars, stdio commands, auth config and credential metadata all describe how the OWNER wired the service up; the borrower never configures it and must not see it.
type ModelParametersDTO ¶
type ModelParametersDTO struct {
// BaseURL is empty for callers who may not view integration detail, and
// for builtin models; see NewModelResponse.
BaseURL string `json:"base_url" extensions:"x-agent"`
InterfaceType string `json:"interface_type"`
EmbeddingParameters types.EmbeddingParameters `json:"embedding_parameters"`
ParameterSize string `json:"parameter_size"`
Provider string `json:"provider" extensions:"x-agent"`
ExtraConfig map[string]string `json:"extra_config,omitempty" extensions:"x-agent,x-agent-detail,x-agent-keys=remote_model_name"`
CustomHeaders map[string]string `json:"custom_headers,omitempty"`
SupportsVision bool `json:"supports_vision"`
ContextWindow int `json:"context_window,omitempty"`
MaxOutputTokens int `json:"max_output_tokens,omitempty"`
MaxConcurrency int `json:"max_concurrency,omitempty"`
AppID string `json:"app_id,omitempty"`
Spec *types.ModelSpecOverride `json:"spec,omitempty"`
}
ModelParametersDTO carries every parameter field EXCEPT the two secret ones (APIKey, AppSecret). AppID is non-secret and stays — it's an account identifier the WeKnora Cloud frontend renders. CustomHeaders is also kept (structural metadata, not a credential).
type ModelResponse ¶
type ModelResponse struct {
ID string `json:"id" extensions:"x-agent=model_id"`
TenantID uint64 `json:"tenant_id"`
Name string `json:"name" extensions:"x-agent"`
DisplayName string `json:"display_name" extensions:"x-agent"`
Type types.ModelType `json:"type" extensions:"x-agent"`
Source types.ModelSource `json:"source" extensions:"x-agent"`
Description string `json:"description" extensions:"x-agent"`
Parameters ModelParametersDTO `json:"parameters" extensions:"x-agent"`
IsDefault bool `json:"is_default" extensions:"x-agent"`
DefaultFor []string `json:"default_for,omitempty" extensions:"x-agent"`
IsBuiltin bool `json:"is_builtin" extensions:"x-agent"`
Status types.ModelStatus `json:"status" extensions:"x-agent"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Per-field "configured?" map. Omitted for builtin models unless the
// caller is a system administrator. See MCPServiceResponse.Credentials.
Credentials map[string]CredentialFieldMetadata `json:"credentials,omitempty"`
// Capabilities is the catalog view of the model. Chat models get the
// protocol, thinking levels and context window; embedding and rerank
// models get input modalities, including support for image input.
Capabilities *modelruntime.Capabilities `json:"capabilities,omitempty" extensions:"x-agent,x-agent-detail"`
}
ModelResponse mirrors types.Model for response bodies, with all secret fields (APIKey, AppSecret) removed by construction. Credential presence metadata lives behind the /credentials subresource, not inlined here.
BaseURL is preserved for tenant-owned models (the frontend needs it to render which endpoint a custom model points at). For builtin models it is stripped along with every other field that could leak how a particular tenant configured the upstream provider.
func NewModelResponse ¶
func NewModelResponse(ctx context.Context, m *types.Model) *ModelResponse
NewModelResponse converts a stored Model into its response shape.
Builtin models are shared across tenants — strip BaseURL (which can leak the tenant's private endpoint) and any non-shared parameters.
func NewModelResponses ¶
func NewModelResponses(ctx context.Context, models []*types.Model) []*ModelResponse
NewModelResponses is the slice convenience wrapper.
type TenantResponse ¶
type TenantResponse struct {
ID uint64 `json:"id" extensions:"x-agent=workspace_id"`
Name string `json:"name" extensions:"x-agent"`
Description string `json:"description" extensions:"x-agent"`
Status string `json:"status" extensions:"x-agent"`
RetrieverEngines types.RetrieverEngines `json:"retriever_engines"`
Business string `json:"business"`
StorageQuota int64 `json:"storage_quota" extensions:"x-agent"`
StorageUsed int64 `json:"storage_used" extensions:"x-agent"`
ContextConfig *types.ContextConfig `json:"context_config,omitempty"`
WebSearchConfig *types.WebSearchConfig `json:"web_search_config,omitempty"`
ParserEngineConfig *types.ParserEngineConfig `json:"parser_engine_config,omitempty"`
Credentials *types.CredentialsConfig `json:"credentials,omitempty"`
StorageEngineConfig *types.StorageEngineConfig `json:"storage_engine_config,omitempty"`
ChatHistoryConfig *types.ChatHistoryConfig `json:"chat_history_config,omitempty"`
RetrievalConfig *types.RetrievalConfig `json:"retrieval_config,omitempty"`
MemoryConfig *types.MemoryConfig `json:"memory_config,omitempty"`
AssistantConfig *types.AssistantConfig `json:"assistant_config,omitempty"`
CreatedAt time.Time `json:"created_at" extensions:"x-agent"`
UpdatedAt time.Time `json:"updated_at" extensions:"x-agent"`
DeletedAt gorm.DeletedAt `json:"deleted_at"`
}
TenantResponse is the viewer-safe tenant profile shape. Secret-bearing columns are omitted or redacted unless the caller has Admin+.
func NewTenantResponse ¶
func NewTenantResponse(ctx context.Context, tenant *types.Tenant) *TenantResponse
NewTenantResponse converts a stored tenant into its HTTP response shape.
func NewTenantResponseWithRole ¶
func NewTenantResponseWithRole(tenant *types.Tenant, role types.TenantRole) *TenantResponse
NewTenantResponseWithRole converts a stored tenant using an explicit role (for auth flows where tenant role is not yet in request context).
func NewTenantResponses ¶
func NewTenantResponses(ctx context.Context, tenants []*types.Tenant) []*TenantResponse
NewTenantResponses is the slice convenience wrapper.
func NewTenantResponsesCrossTenant ¶
func NewTenantResponsesCrossTenant(tenants []*types.Tenant) []*TenantResponse
NewTenantResponsesCrossTenant redacts every tenant as Viewer regardless of the caller's active-tenant role. Used by cross-tenant list/search endpoints where the caller's home-tenant role must not unlock other tenants' secrets.
type VendorRef ¶
VendorRef identifies the vendor a set of model parameters belongs to. BaseURL covers legacy rows saved before provider was stored; the vendor is then detected from the endpoint.
type WebSearchProviderParametersDTO ¶
type WebSearchProviderParametersDTO struct {
EngineID string `json:"engine_id,omitempty"`
BaseURL string `json:"base_url,omitempty"`
ProxyURL string `json:"proxy_url,omitempty"`
ExtraConfig map[string]string `json:"extra_config,omitempty"`
}
WebSearchProviderParametersDTO holds every parameter field except APIKey. EngineID, BaseURL, ProxyURL and ExtraConfig are not secrets (they describe where to send the request, not how to authenticate) and remain visible.
type WebSearchProviderResponse ¶
type WebSearchProviderResponse struct {
ID string `json:"id" extensions:"x-agent=web_search_provider_id"`
TenantID uint64 `json:"tenant_id"`
Name string `json:"name" extensions:"x-agent"`
Provider types.WebSearchProviderType `json:"provider" extensions:"x-agent"`
Description string `json:"description" extensions:"x-agent"`
Parameters WebSearchProviderParametersDTO `json:"parameters"`
IsDefault bool `json:"is_default" extensions:"x-agent"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Per-field "configured?" map. See MCPServiceResponse.Credentials.
Credentials map[string]CredentialFieldMetadata `json:"credentials,omitempty"`
}
WebSearchProviderResponse mirrors types.WebSearchProviderEntity for response bodies, with the APIKey field removed by construction. Credential presence is exposed via the /credentials subresource.
func NewWebSearchProviderResponse ¶
func NewWebSearchProviderResponse(ctx context.Context, e *types.WebSearchProviderEntity) *WebSearchProviderResponse
NewWebSearchProviderResponse converts a stored entity into its response shape.
func NewWebSearchProviderResponses ¶
func NewWebSearchProviderResponses(ctx context.Context, es []*types.WebSearchProviderEntity) []*WebSearchProviderResponse