Files
vessel/backend/internal/api/routes.go
vikingowl 2e1a677431 feat: add backend model registry API and fix sidenav updates
Backend:
- Add local models API with filter, sort, and pagination
- Add grouped chats endpoint for date-based display
- Support search, family filtering, and multi-field sorting

Frontend:
- Add model registry store with backend-powered operations
- Add server conversations store for future backend queries
- Fix sidenav to use local state for immediate updates on
  conversation create/delete (offline-first architecture)
- Fix date display in conversation items (remove duplicate
  Date constructor call)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 02:01:18 +01:00

121 lines
4.0 KiB
Go

package api
import (
"database/sql"
"log"
"github.com/gin-gonic/gin"
)
// SetupRoutes configures all API routes
func SetupRoutes(r *gin.Engine, db *sql.DB, ollamaURL string) {
// Initialize Ollama service with official client
ollamaService, err := NewOllamaService(ollamaURL)
if err != nil {
log.Printf("Warning: Failed to initialize Ollama service: %v", err)
}
// Initialize model registry service
var modelRegistry *ModelRegistryService
if ollamaService != nil {
modelRegistry = NewModelRegistryService(db, ollamaService.Client())
} else {
modelRegistry = NewModelRegistryService(db, nil)
}
// Health check
r.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok"})
})
// API v1 routes
v1 := r.Group("/api/v1")
{
// Chat routes
chats := v1.Group("/chats")
{
chats.GET("", ListChatsHandler(db))
chats.GET("/grouped", ListGroupedChatsHandler(db))
chats.POST("", CreateChatHandler(db))
chats.GET("/:id", GetChatHandler(db))
chats.PUT("/:id", UpdateChatHandler(db))
chats.DELETE("/:id", DeleteChatHandler(db))
// Message routes (nested under chats)
chats.POST("/:id/messages", CreateMessageHandler(db))
}
// Sync routes
sync := v1.Group("/sync")
{
sync.POST("/push", PushChangesHandler(db))
sync.GET("/pull", PullChangesHandler(db))
}
// URL fetch proxy (for tools that need to fetch external URLs)
// Uses curl/wget when available, falls back to native Go HTTP client
v1.POST("/proxy/fetch", URLFetchProxyHandler())
v1.GET("/proxy/fetch-method", GetFetchMethodHandler())
// Web search proxy (for web_search tool)
v1.POST("/proxy/search", WebSearchProxyHandler())
// IP-based geolocation (fallback when browser geolocation fails)
v1.GET("/location", IPGeolocationHandler())
// Model registry routes (cached models from ollama.com)
models := v1.Group("/models")
{
// === Local Models (from Ollama instance) ===
// List local models with filtering, sorting, pagination
models.GET("/local", modelRegistry.ListLocalModelsHandler())
// Get unique model families for filter dropdowns
models.GET("/local/families", modelRegistry.GetLocalFamiliesHandler())
// Check for available updates (compares local vs remote registry)
models.GET("/local/updates", modelRegistry.CheckUpdatesHandler())
// === Remote Models (from ollama.com cache) ===
// List/search remote models (from cache)
models.GET("/remote", modelRegistry.ListRemoteModelsHandler())
// Get single model details
models.GET("/remote/:slug", modelRegistry.GetRemoteModelHandler())
// Fetch detailed info from Ollama (requires model to be pulled)
models.POST("/remote/:slug/details", modelRegistry.FetchModelDetailsHandler())
// Fetch tag sizes from ollama.com (scrapes model detail page)
models.POST("/remote/:slug/sizes", modelRegistry.FetchTagSizesHandler())
// Sync models from ollama.com
models.POST("/remote/sync", modelRegistry.SyncModelsHandler())
// Get sync status
models.GET("/remote/status", modelRegistry.SyncStatusHandler())
}
// Ollama API routes (using official client)
if ollamaService != nil {
ollama := v1.Group("/ollama")
{
// Model management
ollama.GET("/api/tags", ollamaService.ListModelsHandler())
ollama.POST("/api/show", ollamaService.ShowModelHandler())
ollama.POST("/api/pull", ollamaService.PullModelHandler())
ollama.DELETE("/api/delete", ollamaService.DeleteModelHandler())
ollama.POST("/api/copy", ollamaService.CopyModelHandler())
// Chat and generation
ollama.POST("/api/chat", ollamaService.ChatHandler())
ollama.POST("/api/generate", ollamaService.GenerateHandler())
// Embeddings
ollama.POST("/api/embed", ollamaService.EmbedHandler())
ollama.POST("/api/embeddings", ollamaService.EmbedHandler()) // Legacy endpoint
// Status
ollama.GET("/api/version", ollamaService.VersionHandler())
ollama.GET("/", ollamaService.HeartbeatHandler())
}
}
// Fallback proxy for direct Ollama access (separate path to avoid conflicts)
v1.Any("/ollama-proxy/*path", OllamaProxyHandler(ollamaURL))
}
}