WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Tencent/WeKnora/internal/types"
|
||||
)
|
||||
|
||||
// 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.
|
||||
type DataSourceResponse struct {
|
||||
ID string `json:"id"`
|
||||
TenantID uint64 `json:"tenant_id"`
|
||||
KnowledgeBaseID string `json:"knowledge_base_id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Config *DataSourceConfigDTO `json:"config,omitempty"`
|
||||
SyncSchedule string `json:"sync_schedule"`
|
||||
SyncMode string `json:"sync_mode"`
|
||||
Status string `json:"status"`
|
||||
ConflictStrategy string `json:"conflict_strategy"`
|
||||
SyncDeletions bool `json:"sync_deletions"`
|
||||
LastSyncAt *time.Time `json:"last_sync_at"`
|
||||
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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// DataSourceConfigDTO is types.DataSourceConfig with the Credentials map
|
||||
// removed by construction. Type, ResourceIDs and Settings remain visible.
|
||||
type DataSourceConfigDTO struct {
|
||||
Type string `json:"type"`
|
||||
ResourceIDs []string `json:"resource_ids,omitempty"`
|
||||
Settings map[string]interface{} `json:"settings,omitempty"`
|
||||
}
|
||||
|
||||
// NewDataSourceResponse converts a stored entity into its response shape.
|
||||
// Returns nil for nil input.
|
||||
func NewDataSourceResponse(ds *types.DataSource) *DataSourceResponse {
|
||||
if ds == nil {
|
||||
return nil
|
||||
}
|
||||
var cfgDTO *DataSourceConfigDTO
|
||||
configured := false
|
||||
if parsed, err := ds.ParseConfig(); err == nil && parsed != nil {
|
||||
cfgDTO = &DataSourceConfigDTO{
|
||||
Type: parsed.Type,
|
||||
ResourceIDs: parsed.ResourceIDs,
|
||||
Settings: parsed.Settings,
|
||||
}
|
||||
enrichRSSFeedURLsInSettings(ds.Type, parsed, cfgDTO)
|
||||
configured = parsed.HasConfiguredCredentials(ds.Type)
|
||||
}
|
||||
return &DataSourceResponse{
|
||||
ID: ds.ID,
|
||||
TenantID: ds.TenantID,
|
||||
KnowledgeBaseID: ds.KnowledgeBaseID,
|
||||
Name: ds.Name,
|
||||
Type: ds.Type,
|
||||
Config: cfgDTO,
|
||||
SyncSchedule: ds.SyncSchedule,
|
||||
SyncMode: ds.SyncMode,
|
||||
Status: ds.Status,
|
||||
ConflictStrategy: ds.ConflictStrategy,
|
||||
SyncDeletions: ds.SyncDeletions,
|
||||
LastSyncAt: ds.LastSyncAt,
|
||||
LastSyncCursor: json.RawMessage(ds.LastSyncCursor),
|
||||
LastSyncResult: json.RawMessage(ds.LastSyncResult),
|
||||
ErrorMessage: ds.ErrorMessage,
|
||||
SyncLogRetentionDays: ds.SyncLogRetentionDays,
|
||||
CreatedAt: ds.CreatedAt,
|
||||
UpdatedAt: ds.UpdatedAt,
|
||||
TotalItemsSynced: ds.TotalItemsSynced,
|
||||
LatestSyncLog: ds.LatestSyncLog,
|
||||
Credentials: map[string]CredentialFieldMetadata{
|
||||
"credentials": {Configured: configured},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// enrichRSSFeedURLsInSettings copies feed_urls from credentials into settings
|
||||
// for API responses. Feed URLs are not secrets but may still live in the
|
||||
// encrypted credentials blob on rows created before they moved to settings.
|
||||
func enrichRSSFeedURLsInSettings(dsType string, parsed *types.DataSourceConfig, cfgDTO *DataSourceConfigDTO) {
|
||||
if dsType != types.ConnectorTypeRSS || parsed == nil || cfgDTO == nil {
|
||||
return
|
||||
}
|
||||
if cfgDTO.Settings != nil {
|
||||
if v, ok := cfgDTO.Settings["feed_urls"].(string); ok && strings.TrimSpace(v) != "" {
|
||||
return
|
||||
}
|
||||
}
|
||||
raw, ok := parsed.Credentials["feed_urls"]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
feedURLs, ok := raw.(string)
|
||||
if !ok || strings.TrimSpace(feedURLs) == "" {
|
||||
return
|
||||
}
|
||||
if cfgDTO.Settings == nil {
|
||||
cfgDTO.Settings = make(map[string]interface{})
|
||||
}
|
||||
cfgDTO.Settings["feed_urls"] = feedURLs
|
||||
}
|
||||
|
||||
func NewDataSourceResponses(dss []*types.DataSource) []*DataSourceResponse {
|
||||
out := make([]*DataSourceResponse, 0, len(dss))
|
||||
for _, d := range dss {
|
||||
out = append(out, NewDataSourceResponse(d))
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user