Files
vessel/backend/internal/api/chats.go
vikingowl 35884150ee feat: rename project from ollama-webui to Vessel
- Update package.json name to "vessel"
- Update storage keys (vessel-settings, vessel IndexedDB)
- Update Go module to vessel-backend with new imports
- Update database path to vessel.db
- Add new Vessel "V" icon (favicon + app icons)
- Update all user-facing branding (titles, sidebar, settings)
- Update docker-compose files with vessel naming and network
- Change accent color from emerald to violet

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 06:53:07 +01:00

240 lines
5.9 KiB
Go

package api
import (
"database/sql"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"vessel-backend/internal/models"
)
// ListChatsHandler returns a handler for listing all chats
func ListChatsHandler(db *sql.DB) gin.HandlerFunc {
return func(c *gin.Context) {
includeArchived := c.Query("include_archived") == "true"
chats, err := models.ListChats(db, includeArchived)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if chats == nil {
chats = []models.Chat{}
}
c.JSON(http.StatusOK, gin.H{"chats": chats})
}
}
// ListGroupedChatsHandler returns a handler for listing chats grouped by date
// with search, filter, and pagination support
func ListGroupedChatsHandler(db *sql.DB) gin.HandlerFunc {
return func(c *gin.Context) {
search := c.Query("search")
includeArchived := c.Query("include_archived") == "true"
limit := 0
if limitStr := c.Query("limit"); limitStr != "" {
if l, err := strconv.Atoi(limitStr); err == nil && l > 0 {
limit = l
}
}
offset := 0
if offsetStr := c.Query("offset"); offsetStr != "" {
if o, err := strconv.Atoi(offsetStr); err == nil && o >= 0 {
offset = o
}
}
response, err := models.ListChatsGrouped(db, search, includeArchived, limit, offset)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, response)
}
}
// GetChatHandler returns a handler for getting a single chat
func GetChatHandler(db *sql.DB) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
chat, err := models.GetChat(db, id)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if chat == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "chat not found"})
return
}
c.JSON(http.StatusOK, chat)
}
}
// CreateChatRequest represents the request body for creating a chat
type CreateChatRequest struct {
Title string `json:"title"`
Model string `json:"model"`
}
// CreateChatHandler returns a handler for creating a new chat
func CreateChatHandler(db *sql.DB) gin.HandlerFunc {
return func(c *gin.Context) {
var req CreateChatRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
return
}
chat := &models.Chat{
Title: req.Title,
Model: req.Model,
}
if chat.Title == "" {
chat.Title = "New Chat"
}
if err := models.CreateChat(db, chat); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, chat)
}
}
// UpdateChatRequest represents the request body for updating a chat
type UpdateChatRequest struct {
Title *string `json:"title,omitempty"`
Model *string `json:"model,omitempty"`
Pinned *bool `json:"pinned,omitempty"`
Archived *bool `json:"archived,omitempty"`
}
// UpdateChatHandler returns a handler for updating a chat
func UpdateChatHandler(db *sql.DB) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
// Get existing chat
chat, err := models.GetChat(db, id)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if chat == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "chat not found"})
return
}
// Parse update request
var req UpdateChatRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
return
}
// Apply updates
if req.Title != nil {
chat.Title = *req.Title
}
if req.Model != nil {
chat.Model = *req.Model
}
if req.Pinned != nil {
chat.Pinned = *req.Pinned
}
if req.Archived != nil {
chat.Archived = *req.Archived
}
if err := models.UpdateChat(db, chat); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, chat)
}
}
// DeleteChatHandler returns a handler for deleting a chat
func DeleteChatHandler(db *sql.DB) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
if err := models.DeleteChat(db, id); err != nil {
if err.Error() == "chat not found" {
c.JSON(http.StatusNotFound, gin.H{"error": "chat not found"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "chat deleted"})
}
}
// CreateMessageRequest represents the request body for creating a message
type CreateMessageRequest struct {
ParentID *string `json:"parent_id,omitempty"`
Role string `json:"role" binding:"required"`
Content string `json:"content" binding:"required"`
SiblingIndex int `json:"sibling_index"`
}
// CreateMessageHandler returns a handler for creating a new message
func CreateMessageHandler(db *sql.DB) gin.HandlerFunc {
return func(c *gin.Context) {
chatID := c.Param("id")
// Verify chat exists
chat, err := models.GetChat(db, chatID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if chat == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "chat not found"})
return
}
var req CreateMessageRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
return
}
// Validate role
if req.Role != "user" && req.Role != "assistant" && req.Role != "system" {
c.JSON(http.StatusBadRequest, gin.H{"error": "role must be 'user', 'assistant', or 'system'"})
return
}
msg := &models.Message{
ChatID: chatID,
ParentID: req.ParentID,
Role: req.Role,
Content: req.Content,
SiblingIndex: req.SiblingIndex,
}
if err := models.CreateMessage(db, msg); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, msg)
}
}