dto

package
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 29, 2026 License: Apache-2.0, MIT Imports: 7 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func AllSecretExtraConfigKeys

func AllSecretExtraConfigKeys() map[string]bool

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

func CanViewIntegrationSecrets(ctx context.Context) bool

CanViewIntegrationSecrets is true for Admin+ tenant members and for API keys with full tenant access or the manage_tenant_settings capability.

func CanViewTenantAPIKey

func CanViewTenantAPIKey(ctx context.Context) bool

CanViewTenantAPIKey is true for Owner+ only.

func HasAllSecretExtras

func HasAllSecretExtras(provider, baseURL string, extra map[string]string) bool

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

func SecretExtraConfigKeys(provider, baseURL string) map[string]bool

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

type VendorRef struct {
	Provider string
	BaseURL  string
}

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL