fix: unify api keys for collection endpoint

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
Ettore Di Giacinto
2026-02-21 23:39:19 +00:00
parent 410ba7a467
commit 8014d22588
3 changed files with 30 additions and 44 deletions
-6
View File
@@ -38,7 +38,6 @@ var embeddingModel = os.Getenv("EMBEDDING_MODEL")
var maxChunkingSizeEnv = os.Getenv("MAX_CHUNKING_SIZE")
var chunkOverlapEnv = os.Getenv("CHUNK_OVERLAP")
var databaseURL = os.Getenv("DATABASE_URL")
var collectionAPIKeysEnv = os.Getenv("API_KEYS")
func init() {
if baseModel == "" {
@@ -94,10 +93,6 @@ func main() {
if apiKeysEnv != "" {
apiKeys = strings.Split(apiKeysEnv, ",")
}
collectionAPIKeys := []string{}
if collectionAPIKeysEnv != "" {
collectionAPIKeys = strings.Split(collectionAPIKeysEnv, ",")
}
// Skills service (optional: provides skills prompt and MCP when agents have EnableSkills)
skillsService, err := skills.NewService(stateDir)
@@ -152,7 +147,6 @@ func main() {
webui.WithMaxChunkingSize(maxChunkingSize),
webui.WithChunkOverlap(chunkOverlap),
webui.WithDatabaseURL(databaseURL),
webui.WithCollectionAPIKeys(collectionAPIKeys...),
webui.WithLocalRAGURL(localRAG),
)
+23 -24
View File
@@ -1,7 +1,6 @@
package webui
import (
"crypto/subtle"
"fmt"
"net/url"
"strings"
@@ -17,17 +16,17 @@ type collectionList map[string]*rag.PersistentKB
// collectionsState holds in-memory state for the collections API.
type collectionsState struct {
mu sync.RWMutex
collections collectionList
sourceManager *rag.SourceManager
mu sync.RWMutex
collections collectionList
sourceManager *rag.SourceManager
ensureCollection func(name string) (*rag.PersistentKB, bool) // get-or-create for internal RAG (agent name as collection)
}
// APIResponse represents a standardized API response (LocalRecall contract).
type collectionsAPIResponse struct {
Success bool `json:"success"`
Message string `json:"message,omitempty"`
Data interface{} `json:"data,omitempty"`
Success bool `json:"success"`
Message string `json:"message,omitempty"`
Data interface{} `json:"data,omitempty"`
Error *collectionsAPIError `json:"error,omitempty"`
}
@@ -64,25 +63,25 @@ func collectionsErrorResponse(code, message, details string) collectionsAPIRespo
}
}
// collectionsAPIKeyFromRequest returns the API key from the same sources as the main keyauth: Authorization, x-api-key, xi-api-key, cookie:token.
func collectionsAPIKeyFromRequest(c *fiber.Ctx) string {
if v := c.Get("Authorization"); v != "" {
return strings.TrimPrefix(strings.TrimSpace(v), "Bearer ")
}
if v := c.Get("x-api-key"); v != "" {
return strings.TrimSpace(v)
}
if v := c.Get("xi-api-key"); v != "" {
return strings.TrimSpace(v)
}
if v := c.Cookies("token"); v != "" {
return v
}
return ""
}
// RegisterCollectionRoutes mounts /api/collections* routes. backend is either from NewInProcessCollectionsBackend or NewCollectionsBackendHTTP.
func (app *App) RegisterCollectionRoutes(webapp *fiber.App, cfg *Config, backend CollectionsBackend) {
apiKeys := cfg.CollectionAPIKeys
if len(apiKeys) == 0 {
apiKeys = cfg.ApiKeys
}
if len(apiKeys) > 0 {
webapp.Use("/api/collections", func(c *fiber.Ctx) error {
apiKey := c.Get("Authorization")
apiKey = strings.TrimPrefix(apiKey, "Bearer ")
for _, validKey := range apiKeys {
if subtle.ConstantTimeCompare([]byte(apiKey), []byte(validKey)) == 1 {
return c.Next()
}
}
return c.Status(fiber.StatusUnauthorized).JSON(collectionsErrorResponse(errCodeUnauthorized, "Unauthorized", "Invalid or missing API key"))
})
}
webapp.Post("/api/collections", app.createCollection(backend))
webapp.Get("/api/collections", app.listCollections(backend))
webapp.Post("/api/collections/:name/upload", app.uploadFile(backend))
+7 -14
View File
@@ -20,14 +20,13 @@ type Config struct {
ConversationStoreDuration time.Duration
// Collections / knowledge base (LocalRecall)
CollectionDBPath string
FileAssets string
VectorEngine string
EmbeddingModel string
MaxChunkingSize int
ChunkOverlap int
CollectionAPIKeys []string
DatabaseURL string
CollectionDBPath string
FileAssets string
VectorEngine string
EmbeddingModel string
MaxChunkingSize int
ChunkOverlap int
DatabaseURL string
// LocalRAGURL when set uses HTTP backend for collections API; when empty uses in-process backend.
LocalRAGURL string
}
@@ -134,12 +133,6 @@ func WithChunkOverlap(overlap int) Option {
}
}
func WithCollectionAPIKeys(keys ...string) Option {
return func(c *Config) {
c.CollectionAPIKeys = keys
}
}
func WithDatabaseURL(url string) Option {
return func(c *Config) {
c.DatabaseURL = url