api

package
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Oct 4, 2026 License: Apache-2.0 Imports: 106 Imported by: 0

Documentation

Overview

Package api provides HTTP handlers for the GoatFlow application.

Package api provides HTTP API handlers for GoatFlow.

Package api provides HTTP handlers for the GoatFlow application.

Package api provides HTTP handlers for the API.

Index

Constants

View Source
const (
	DFTypeText                  = "Text"
	DFTypeTextArea              = "TextArea"
	DFTypeCheckbox              = "Checkbox"
	DFTypeDropdown              = "Dropdown"
	DFTypeMultiselect           = "Multiselect"
	DFTypeDate                  = "Date"
	DFTypeDateTime              = "DateTime"
	DFTypeWebserviceDropdown    = "WebserviceDropdown"
	DFTypeWebserviceMultiselect = "WebserviceMultiselect"
)

Field type constants matching OTRS.

View Source
const (
	DFObjectTicket          = "Ticket"
	DFObjectArticle         = "Article"
	DFObjectCustomerUser    = "CustomerUser"
	DFObjectCustomerCompany = "CustomerCompany"
)

Object type constants matching OTRS.

View Source
const (
	DFScreenDisabled = 0
	DFScreenEnabled  = 1
	DFScreenRequired = 2
)

Screen config value constants.

View Source
const (
	MaxFileSize    = 10 * 1024 * 1024 // 10MB per file
	MaxTotalSize   = 50 * 1024 * 1024 // 50MB total per ticket
	MaxAttachments = 20               // Max 20 attachments per ticket
)

File upload limits.

Variables

View Source
var (
	HandleCustomerListTokens  = HandleListTokens
	HandleCustomerCreateToken = HandleCreateToken
	HandleCustomerRevokeToken = HandleRevokeToken
)

Customer handlers are aliases to the unified handlers above The handlers detect agent vs customer from context automatically

View Source
var (
	HandleAdminListUserTokens      = HandleAdminListTargetTokens
	HandleAdminCreateUserToken     = HandleAdminCreateTargetToken
	HandleAdminRevokeUserToken     = HandleAdminRevokeTargetToken
	HandleAdminListCustomerTokens  = HandleAdminListTargetTokens
	HandleAdminCreateCustomerToken = HandleAdminCreateTargetToken
	HandleAdminRevokeCustomerToken = HandleAdminRevokeTargetToken
)

Admin handler aliases - unified handlers work for both agents and customers

View Source
var (
	HandleLoginPage           = handleLoginPage
	HandleCustomerLoginPage   = handleCustomerLoginPage
	HandleLogout              = handleLogout
	HandleDashboard           = handleDashboard
	HandleRecentTickets       = handleRecentTickets
	HandleActivityStream      = handleActivityStream
	HandlePendingReminderFeed = handlePendingReminderFeed
	HandleUpdateTicketStatus  = handleUpdateTicketStatus
)

Core handlers.

View Source
var (
	HandleAdminDashboard = handleAdminDashboard
	// Users are handled by dynamic modules and admin_users_handlers.go.
	HandleAdminUserEdit           = HandleAdminUserGet // Same handler for edit form
	HandleAdminPasswordPolicy     = HandlePasswordPolicy
	HandleAdminGroups             = handleAdminGroups
	HandleGetGroup                = handleGetGroup
	HandleCreateGroup             = handleCreateGroup
	HandleUpdateGroup             = handleUpdateGroup
	HandleDeleteGroup             = handleDeleteGroup
	HandleGroupMembers            = handleGetGroupMembers
	HandleAddUserToGroup          = handleAddUserToGroup
	HandleRemoveUserFromGroup     = handleRemoveUserFromGroup
	HandleGroupPermissions        = handleGroupPermissions
	HandleSaveGroupPermissions    = handleSaveGroupPermissions
	HandleAdminQueues             = handleAdminQueues
	HandleAdminEmailIdentities    = handleAdminEmailIdentities
	HandleAdminPriorities         = handleAdminPriorities
	HandleAdminPermissions        = handleAdminPermissions // Renamed from roles
	HandleGetUserPermissionMatrix = handleGetUserPermissionMatrix
	HandleUpdateUserPermissions   = handleUpdateUserPermissions
	HandleAdminEmailQueue         = handleAdminEmailQueue
	HandleAdminEmailQueueRetry    = handleAdminEmailQueueRetry
	HandleAdminEmailQueueDelete   = handleAdminEmailQueueDelete
	HandleAdminEmailQueueRetryAll = handleAdminEmailQueueRetryAll
	HandleAdminStates             = handleAdminStates
	HandleAdminTypes              = handleAdminTypes
	HandleAdminServices           = handleAdminServices
	HandleAdminServiceCreate      = handleAdminServiceCreate
	HandleAdminServiceUpdate      = handleAdminServiceUpdate
	HandleAdminServiceDelete      = handleAdminServiceDelete
	HandleAdminSLA                = handleAdminSLA
	HandleAdminSLACreate          = handleAdminSLACreate
	HandleAdminSLAUpdate          = handleAdminSLAUpdate
	HandleAdminSLADelete          = handleAdminSLADelete
	HandleAdminLookups            = handleAdminLookups
	// Roles management.
	HandleAdminRoles                 = handleAdminRoles
	HandleAdminRoleCreate            = handleAdminRoleCreate
	HandleAdminRoleGet               = handleAdminRoleGet
	HandleAdminRoleUpdate            = handleAdminRoleUpdate
	HandleAdminRoleDelete            = handleAdminRoleDelete
	HandleAdminRoleUsers             = handleAdminRoleUsers
	HandleAdminRoleUsersSearch       = handleAdminRoleUsersSearch
	HandleAdminRoleUserAdd           = handleAdminRoleUserAdd
	HandleAdminRoleUserRemove        = handleAdminRoleUserRemove
	HandleAdminRolePermissions       = handleAdminRolePermissions
	HandleAdminRolePermissionsUpdate = handleAdminRolePermissionsUpdate
	// Customer company handlers - wrapped to get database from adapter.
	HandleAdminCustomerCompanies = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			handleAdminCustomerCompanies(nil)(c)
			return
		}
		handleAdminCustomerCompanies(dbService.GetDB())(c)
	}
	HandleAdminCustomerCompanyUsers = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminCustomerCompanyUsers(dbService.GetDB())(c)
	}
	HandleAdminCustomerCompanyTickets = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminCustomerCompanyTickets(dbService.GetDB())(c)
	}
	HandleAdminCustomerCompanyServices = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminCustomerCompanyServices(dbService.GetDB())(c)
	}
	HandleAdminCustomerPortalSettings = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminCustomerPortalSettings(dbService.GetDB())(c)
	}
	HandleAdminNewCustomerCompany = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			handleAdminNewCustomerCompany(nil)(c)
			return
		}
		handleAdminNewCustomerCompany(dbService.GetDB())(c)
	}
	HandleAdminCreateCustomerCompany = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminCreateCustomerCompany(dbService.GetDB())(c)
	}
	HandleAdminEditCustomerCompany = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminEditCustomerCompany(dbService.GetDB())(c)
	}
	HandleAdminUpdateCustomerCompany = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminUpdateCustomerCompany(dbService.GetDB())(c)
	}
	HandleAdminDeleteCustomerCompany = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminDeleteCustomerCompany(dbService.GetDB())(c)
	}
	HandleAdminUpdateCustomerCompanyServices = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminUpdateCustomerCompanyServices(dbService.GetDB())(c)
	}
	HandleAdminUpdateCustomerPortalSettings = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminUpdateCustomerPortalSettings(dbService.GetDB())(c)
	}
	HandleAdminActivateCustomerCompany = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminActivateCustomerCompany(dbService.GetDB())(c)
	}

	// Customer user ↔ services management.
	HandleAdminCustomerUserServices = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminCustomerUserServices(dbService.GetDB())(c)
	}
	HandleAdminCustomerUserServicesAllocate = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminCustomerUserServicesAllocate(dbService.GetDB())(c)
	}
	HandleAdminCustomerUserServicesUpdate = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminCustomerUserServicesUpdate(dbService.GetDB())(c)
	}
	HandleAdminServiceCustomerUsersAllocate = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminServiceCustomerUsersAllocate(dbService.GetDB())(c)
	}
	HandleAdminServiceCustomerUsersUpdate = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminServiceCustomerUsersUpdate(dbService.GetDB())(c)
	}
	HandleAdminDefaultServices = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminDefaultServices(dbService.GetDB())(c)
	}
	HandleAdminDefaultServicesUpdate = func(c *gin.Context) {
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminDefaultServicesUpdate(dbService.GetDB())(c)
	}

	// Dynamic Fields management.
	HandleAdminDynamicFields                  = handleAdminDynamicFields
	HandleAdminDynamicFieldNew                = handleAdminDynamicFieldNew
	HandleAdminDynamicFieldEdit               = handleAdminDynamicFieldEdit
	HandleAdminDynamicFieldScreenConfig       = handleAdminDynamicFieldScreenConfig
	HandleCreateDynamicField                  = handleCreateDynamicField
	HandleUpdateDynamicField                  = handleUpdateDynamicField
	HandleDeleteDynamicField                  = handleDeleteDynamicField
	HandleAdminDynamicFieldScreenConfigSave   = handleAdminDynamicFieldScreenConfigSave
	HandleAdminDynamicFieldScreenConfigSingle = handleAdminDynamicFieldScreenConfigSingle
)

Admin handlers.

View Source
var (
	HandleTicketDetail   = handleTicketDetail
	HandleQueueDetail    = handleQueueDetail
	HandleNewTicket      = handleNewTicket
	HandleNewEmailTicket = handleNewEmailTicket
	HandleNewPhoneTicket = handleNewPhoneTicket
)

Ticket handlers.

View Source
var (
	HandleGetAttachments     = handleGetAttachments
	HandleUploadAttachment   = handleUploadAttachment
	HandleDownloadAttachment = handleDownloadAttachment
	HandleDeleteAttachment   = handleDeleteAttachment
	HandleGetThumbnail       = handleGetThumbnail
	HandleViewAttachment     = handleViewAttachment
)

Attachment handlers (exported for routing).

View Source
var (
	HandleAgentTickets        = AgentHandlerExports.HandleAgentTickets
	HandleAgentTicketReply    = AgentHandlerExports.HandleAgentTicketReply
	HandleAgentTicketNote     = AgentHandlerExports.HandleAgentTicketNote
	HandleAgentTicketPhone    = AgentHandlerExports.HandleAgentTicketPhone
	HandleAgentTicketStatus   = AgentHandlerExports.HandleAgentTicketStatus
	HandleAgentTicketAssign   = AgentHandlerExports.HandleAgentTicketAssign
	HandleAgentTicketPriority = AgentHandlerExports.HandleAgentTicketPriority
	HandleAgentTicketQueue    = AgentHandlerExports.HandleAgentTicketQueue
	HandleAgentTicketMerge    = AgentHandlerExports.HandleAgentTicketMerge
)

Provide package-level handler variables for tests and direct routing.

View Source
var (
	HandleCustomerDashboard         = wrapAdapterDBHandler(handleCustomerDashboard)
	HandleCustomerTickets           = wrapAdapterDBHandler(handleCustomerTickets)
	HandleCustomerNewTicket         = wrapAdapterDBHandler(handleCustomerNewTicket)
	HandleCustomerCreateTicket      = wrapAdapterDBHandler(handleCustomerCreateTicket)
	HandleCustomerTicketView        = wrapAdapterDBHandler(handleCustomerTicketView)
	HandleCustomerTicketReply       = wrapAdapterDBHandler(handleCustomerTicketReply)
	HandleCustomerCloseTicket       = wrapAdapterDBHandler(handleCustomerCloseTicket)
	HandleCustomerProfile           = wrapAdapterDBHandler(handleCustomerProfile)
	HandleCustomerUpdateProfile     = wrapAdapterDBHandler(handleCustomerUpdateProfile)
	HandleCustomerPasswordForm      = wrapAdapterDBHandler(handleCustomerPasswordForm)
	HandleCustomerChangePassword    = wrapAdapterDBHandler(handleCustomerChangePassword)
	HandleCustomerGetLanguage       = wrapAdapterDBHandler(handleCustomerGetLanguage)
	HandleCustomerSetLanguage       = wrapAdapterDBHandler(handleCustomerSetLanguage)
	HandleCustomerGetSessionTimeout = wrapAdapterDBHandler(handleCustomerGetSessionTimeout)
	HandleCustomerSetSessionTimeout = wrapAdapterDBHandler(handleCustomerSetSessionTimeout)

	// Customer attachment handlers
	HandleCustomerGetAttachments     = wrapAdapterDBHandler(handleCustomerGetAttachments)
	HandleCustomerUploadAttachment   = wrapAdapterDBHandler(handleCustomerUploadAttachment)
	HandleCustomerDownloadAttachment = wrapAdapterDBHandler(handleCustomerDownloadAttachment)
	HandleCustomerGetThumbnail       = wrapAdapterDBHandler(handleCustomerGetThumbnail)
	HandleCustomerViewAttachment     = wrapAdapterDBHandler(handleCustomerViewAttachment)
)

Customer handler exports that get database from connection pool.

View Source
var AgentHandlerExports = struct {
	HandleAgentTickets        gin.HandlerFunc
	HandleAgentTicketReply    gin.HandlerFunc
	HandleAgentTicketNote     gin.HandlerFunc
	HandleAgentTicketPhone    gin.HandlerFunc
	HandleAgentTicketStatus   gin.HandlerFunc
	HandleAgentTicketAssign   gin.HandlerFunc
	HandleAgentTicketPriority gin.HandlerFunc
	HandleAgentTicketQueue    gin.HandlerFunc
	HandleAgentTicketMerge    gin.HandlerFunc
	HandleAgentNewTicket      gin.HandlerFunc
	HandleAgentCreateTicket   gin.HandlerFunc
	HandleAgentQueues         gin.HandlerFunc
	// Bulk ticket actions
	HandleBulkTicketStatus     gin.HandlerFunc
	HandleBulkTicketPriority   gin.HandlerFunc
	HandleBulkTicketQueue      gin.HandlerFunc
	HandleBulkTicketAssign     gin.HandlerFunc
	HandleBulkTicketLock       gin.HandlerFunc
	HandleBulkTicketMerge      gin.HandlerFunc
	HandleGetBulkActionOptions gin.HandlerFunc
	HandleGetFilteredTicketIds gin.HandlerFunc
}{
	HandleAgentTickets:        wrapDBHandler(handleAgentTickets),
	HandleAgentTicketReply:    wrapDBHandler(handleAgentTicketReply),
	HandleAgentTicketNote:     wrapDBHandler(handleAgentTicketNote),
	HandleAgentTicketPhone:    wrapDBHandler(handleAgentTicketPhone),
	HandleAgentTicketStatus:   wrapDBHandler(handleAgentTicketStatus),
	HandleAgentTicketAssign:   wrapDBHandler(handleAgentTicketAssign),
	HandleAgentTicketPriority: wrapDBHandler(handleAgentTicketPriority),
	HandleAgentTicketQueue:    wrapDBHandler(handleAgentTicketQueue),
	HandleAgentTicketMerge:    wrapDBHandler(handleAgentTicketMerge),
	HandleAgentNewTicket:      wrapDBHandler(HandleAgentNewTicket),
	HandleAgentCreateTicket:   wrapDBHandler(HandleAgentCreateTicket),
	HandleAgentQueues:         wrapDBHandler(handleAgentQueues),

	HandleBulkTicketStatus:     wrapDBHandler(handleBulkTicketStatus),
	HandleBulkTicketPriority:   wrapDBHandler(handleBulkTicketPriority),
	HandleBulkTicketQueue:      wrapDBHandler(handleBulkTicketQueue),
	HandleBulkTicketAssign:     wrapDBHandler(handleBulkTicketAssign),
	HandleBulkTicketLock:       wrapDBHandler(handleBulkTicketLock),
	HandleBulkTicketMerge:      wrapDBHandler(handleBulkTicketMerge),
	HandleGetBulkActionOptions: wrapDBHandler(handleGetBulkActionOptions),
	HandleGetFilteredTicketIds: wrapDBHandler(handleGetFilteredTicketIds),
}

AgentHandlerExports provides exported handler functions for agent routes.

View Source
var GlobalAgentHandlers = &AgentHandlerRegistry{}

GlobalAgentHandlers is the global registry for agent handlers.

View Source
var HandleAPIv1AddArticle = HandleCreateArticleAPI

Articles API handlers.

View Source
var HandleAPIv1AuthLogin = platformapi.HandleLoginAPI

Auth handlers.

View Source
var HandleAPIv1AuthLogout = platformapi.HandleLogoutAPI
View Source
var HandleAPIv1GetTicketArticle = func(c *gin.Context) {
	ticketID := c.Param("id")
	articleID := c.Param("article_id")
	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": "Get ticket article endpoint - under construction",
		"data":    gin.H{"ticket_id": ticketID, "article_id": articleID},
	})
}
View Source
var HandleAPIv1GetTicketArticles = func(c *gin.Context) {
	ticketID := c.Param("id")
	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": "Get ticket articles endpoint - under construction",
		"data":    gin.H{"ticket_id": ticketID},
	})
}
View Source
var HandleAPIv1PrioritiesList = func(c *gin.Context) {
	priorityRepo := GetPriorityRepository()
	if priorityRepo == nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "Priority repository not initialized",
		})
		return
	}

	priorities, err := priorityRepo.List()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   err.Error(),
		})
		return
	}

	priorityList := make([]gin.H, 0, len(priorities))
	for _, p := range priorities {
		priorityList = append(priorityList, gin.H{
			"id":   p.ID,
			"name": p.Name,
		})
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"data":    priorityList,
	})
}

Priorities API handlers.

View Source
var HandleAPIv1PriorityGet = func(c *gin.Context) {
	id := c.Param("id")
	priorityRepo := GetPriorityRepository()
	if priorityRepo == nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "Priority repository not initialized",
		})
		return
	}

	priorityID, err := strconv.ParseUint(id, 10, 32)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"error":   "Invalid priority ID",
		})
		return
	}

	priority, err := priorityRepo.GetByID(uint(priorityID))
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{
			"success": false,
			"error":   "Priority not found",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"data":    priority,
	})
}
View Source
var HandleAPIv1QueueCreate = func(c *gin.Context) {
	c.JSON(http.StatusCreated, gin.H{
		"success": true,
		"message": "Endpoint /api/v1/queues (POST) is under construction",
		"data":    nil,
	})
}
View Source
var HandleAPIv1QueueDelete = func(c *gin.Context) {
	c.JSON(http.StatusNoContent, gin.H{})
}
View Source
var HandleAPIv1QueueGet = func(c *gin.Context) {
	id := c.Param("id")
	queueRepo := GetQueueRepository()
	if queueRepo == nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "Queue repository not initialized",
		})
		return
	}

	queueID, err := strconv.ParseUint(id, 10, 32)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"error":   "Invalid queue ID",
		})
		return
	}

	queue, err := queueRepo.GetByID(uint(queueID))
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{
			"success": false,
			"error":   "Queue not found",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"data":    queue,
	})
}
View Source
var HandleAPIv1QueueUpdate = func(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": "Endpoint /api/v1/queues/:id (PUT) is under construction",
		"data":    nil,
	})
}
View Source
var HandleAPIv1QueuesList = func(c *gin.Context) {
	queueRepo := GetQueueRepository()
	if queueRepo == nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "Queue repository not initialized",
		})
		return
	}

	queues, err := queueRepo.List()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   err.Error(),
		})
		return
	}

	queueList := make([]gin.H, 0, len(queues))
	for _, q := range queues {
		queueList = append(queueList, gin.H{
			"id":   q.ID,
			"name": q.Name,
		})
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"data":    queueList,
	})
}

Queues API handlers.

View Source
var HandleAPIv1TicketAssign = HandleAssignTicketAPI
View Source
var HandleAPIv1TicketClose = HandleCloseTicketAPI

Ticket action handlers.

View Source
var HandleAPIv1TicketCreate = HandleCreateTicketAPI
View Source
var HandleAPIv1TicketDelete = HandleDeleteTicketAPI
View Source
var HandleAPIv1TicketGet = func(c *gin.Context) {
	id := c.Param("id")
	ticketRepo := GetTicketRepository()
	if ticketRepo == nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "Ticket repository not initialized",
		})
		return
	}

	ticketID, err := strconv.ParseUint(id, 10, 32)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"error":   "Invalid ticket ID",
		})
		return
	}

	ticket, err := ticketRepo.GetByID(uint(ticketID))
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{
			"success": false,
			"error":   "Ticket not found",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"data":    ticket,
	})
}
View Source
var HandleAPIv1TicketReopen = HandleReopenTicketAPI
View Source
var HandleAPIv1TicketUpdate = HandleUpdateTicketAPI
View Source
var HandleAPIv1TicketsList = HandleListTicketsAPI

Tickets API handlers.

View Source
var HandleAPIv1UserCreate = func(c *gin.Context) {
	c.JSON(http.StatusCreated, gin.H{
		"success": true,
		"message": "Endpoint /api/v1/users (POST) is under construction",
		"data":    nil,
	})
}
View Source
var HandleAPIv1UserDelete = func(c *gin.Context) {
	c.JSON(http.StatusNoContent, gin.H{})
}
View Source
var HandleAPIv1UserGet = func(c *gin.Context) {
	id := c.Param("id")
	userRepo := GetUserRepository()
	if userRepo == nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "User repository not initialized",
		})
		return
	}

	// Convert string ID to uint
	var userID uint
	if _, err := fmt.Sscanf(id, "%d", &userID); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"error":   "Invalid user ID",
		})
		return
	}

	user, err := userRepo.GetByID(userID)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{
			"success": false,
			"error":   "User not found",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"data":    user,
	})
}
View Source
var HandleAPIv1UserMe = platformapi.HandleUserMeAPI

Users API handlers.

View Source
var HandleAPIv1UserUpdate = func(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": "Endpoint /api/v1/users/:id (PUT) is under construction",
		"data":    nil,
	})
}
View Source
var HandleAPIv1UsersList = func(c *gin.Context) {
	userRepo := GetUserRepository()
	if userRepo == nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "User repository not initialized",
		})
		return
	}

	users, err := userRepo.List()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"data":    users,
	})
}
View Source
var HandleAuthLogin = func(c *gin.Context) {
	contentType := c.GetHeader("Content-Type")
	var username, password string
	if strings.Contains(contentType, "application/json") {
		var payload struct {
			Login    string `json:"login"`
			Username string `json:"username"`
			Email    string `json:"email"`
			Password string `json:"password"`
		}
		if err := c.ShouldBindJSON(&payload); err == nil {
			username = payload.Login
			if username == "" {
				username = payload.Username
			}
			if username == "" {
				username = payload.Email
			}
			password = payload.Password
		}
	} else {
		username = c.PostForm("username")
		password = c.PostForm("password")
		if username == "" {
			username = c.PostForm("login")
		}
		if username == "" {
			username = c.PostForm("email")
		}
		if username == "" {
			username = c.PostForm("user")
		}
	}
	provider := c.PostForm("provider")
	if provider == "" {
		provider = c.Query("provider")
	}
	provider = strings.ToLower(provider)

	if username == "" || password == "" {
		if strings.Contains(contentType, "application/json") {
			c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "username and password required"})
		} else {
			getPongo2Renderer().HTML(c, http.StatusBadRequest, "components/error.pongo2",
				pongo2.Context{"error": "Username and password are required"})
		}
		return
	}

	clientIP := c.ClientIP()
	if blocked, remaining := auth.DefaultLoginRateLimiter.IsBlocked(clientIP, username); blocked {
		switch {
		case strings.Contains(contentType, "application/json"):
			tooManyAttemptsJSON(c, remaining)
		case c.GetHeader("HX-Request") == "true":
			html := `<div class="rounded-md bg-red-50 dark:bg-red-900/20 p-4 mt-4">` +
				`<div class="text-sm text-red-800 dark:text-red-200">` +
				`Too many failed attempts. Try again later.</div></div>`
			c.Data(http.StatusTooManyRequests, "text/html; charset=utf-8", []byte(html))
		default:
			c.Redirect(http.StatusSeeOther, "/login?error=Too+many+failed+attempts.+Try+again+later")
		}
		return
	}

	authService := GetAuthService()
	if authService == nil {
		if strings.Contains(contentType, "application/json") {
			c.JSON(http.StatusUnauthorized, gin.H{"success": false, "error": "authentication unavailable"})
			return
		}
		if c.GetHeader("HX-Request") == "true" {
			html := `<div class="rounded-md bg-yellow-50 dark:bg-yellow-900/20 p-4 mt-4">` +
				`<div class="text-sm text-yellow-800 dark:text-yellow-100">` +
				`Authentication temporarily unavailable</div></div>`
			c.Data(http.StatusUnauthorized, "text/html; charset=utf-8", []byte(html))
			return
		}
		c.Redirect(http.StatusSeeOther, "/login?error=Authentication+temporarily+unavailable")
		return
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	user, err := authService.Login(ctx, username, password)
	if err != nil {
		auth.DefaultLoginRateLimiter.RecordFailure(clientIP, username)
		if strings.Contains(contentType, "application/json") {
			c.JSON(http.StatusUnauthorized, gin.H{"success": false, "error": "invalid credentials"})
			return
		}
		if c.GetHeader("HX-Request") == "true" {
			html := `<div class="rounded-md bg-red-50 dark:bg-red-900/20 p-4 mt-4">` +
				`<div class="text-sm text-red-800 dark:text-red-200">` +
				`Invalid username or password</div></div>`
			c.Data(http.StatusUnauthorized, "text/html; charset=utf-8", []byte(html))
		} else {
			c.Redirect(http.StatusSeeOther, "/login?error=Invalid+username+or+password")
		}
		return
	}
	auth.DefaultLoginRateLimiter.RecordSuccess(clientIP, username)

	mfaDB, mfaErr := database.GetDB()
	var mfa mfaStatus
	if mfaErr == nil {
		mfa, mfaErr = agentMFAStatus(mfaDB, int(user.ID))
	}
	if mfaErr != nil {
		log.Printf("login: second-factor status for user %d unavailable: %v", user.ID, mfaErr)
		if strings.Contains(contentType, "application/json") {
			c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "Login temporarily unavailable"})
		} else {
			c.Redirect(http.StatusSeeOther, "/login?error=server_error")
		}
		return
	}
	if mfa.Enabled() {

		sessionMgr := auth.GetTOTPSessionManager()
		token, err := sessionMgr.CreateAgentSession(int(user.ID), username, c.ClientIP(), c.Request.UserAgent())
		if err != nil {
			if strings.Contains(contentType, "application/json") {
				c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "Failed to create 2FA session"})
			} else {
				c.Redirect(http.StatusSeeOther, "/login?error=2FA+session+error")
			}
			return
		}

		httpcookie.SetAuth(c, "2fa_pending", token, 300)

		if c.GetHeader("HX-Request") == "true" {

			c.Redirect(http.StatusFound, "/login/2fa")
		} else if strings.Contains(contentType, "application/json") {
			c.JSON(http.StatusOK, gin.H{
				"success":      true,
				"requires_2fa": true,
				"redirect":     "/login/2fa",
			})
		} else {
			c.Redirect(http.StatusFound, "/login/2fa")
		}
		return
	}

	sessionTimeout := shared.GetSystemSessionMaxTime()
	if db, err := database.GetDB(); err == nil && db != nil {
		prefService := service.NewUserPreferencesService(db)
		if userTimeout := prefService.GetSessionTimeout(int(user.ID)); userTimeout > 0 {
			sessionTimeout = shared.ResolveSessionTimeout(userTimeout)
		}

		if preLoginLang, err := c.Cookie("goatflow_lang"); err == nil && preLoginLang != "" {
			if setErr := prefService.SetLanguage(int(user.ID), preLoginLang); setErr != nil {
				log.Printf("Failed to save language preference: %v", setErr)
			}
		}
	}
	if sessionTimeout <= 0 {
		sessionTimeout = constants.DefaultSessionTimeout
	}

	sessionID, accessToken, refreshToken, err := authService.IssueTokens(user, c.ClientIP(), c.Request.UserAgent(),
		time.Duration(sessionTimeout)*time.Second)
	if err != nil {
		log.Printf("login: issue tokens for %s: %v", username, err)
		if strings.Contains(contentType, "application/json") {
			c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "Login temporarily unavailable"})
		} else {
			c.Redirect(http.StatusSeeOther, "/login?error=server_error")
		}
		return
	}

	httpcookie.SetAuth(c, "auth_token", accessToken, sessionTimeout)
	httpcookie.SetAuth(c, "access_token", accessToken, sessionTimeout)
	httpcookie.SetAuth(c, "refresh_token", refreshToken, constants.RefreshTokenTimeout)

	httpcookie.SetAuth(c, "session_id", sessionID, sessionTimeout)

	c.Set("user", user)
	c.Set("user_id", user.ID)
	if provider != "" {
		c.Set("auth_provider", provider)
	}

	httpcookie.SetAuthState(c, "goatflow_logged_in", "1", sessionTimeout)

	redirectTarget := "/dashboard"
	if strings.EqualFold(user.Role, "customer") {
		redirectTarget = "/customer"
	}

	if lp := shared.GetLandingPage(); lp != "" && redirectTarget == "/dashboard" {
		redirectTarget = lp
	}

	if strings.Contains(contentType, "application/json") {
		c.JSON(http.StatusOK, gin.H{
			"success": true, "access_token": accessToken, "refresh_token": refreshToken,
			"token_type": "Bearer", "redirect": redirectTarget,
		})
		return
	}
	if c.GetHeader("HX-Request") == "true" {
		c.Header("HX-Redirect", redirectTarget)
		c.String(http.StatusOK, "Login successful, redirecting...")
		return
	}
	c.Redirect(http.StatusSeeOther, redirectTarget)
}

HandleAuthLogin handles the login form submission.

View Source
var HandleCustomerLogin = func(c *gin.Context) {
	handleCustomerLogin(shared.GetJWTManager())(c)
}

HandleCustomerLogin is the exported handler for customer login POST requests.

View Source
var HandleTicketCustomerUsersWrapper gin.HandlerFunc = func(c *gin.Context) {
	db, err := database.GetDB()
	if err != nil {
		c.JSON(500, gin.H{"error": "Database unavailable"})
		return
	}
	handleTicketCustomerUsers(db)(c)
}

HandleTicketCustomerUsersWrapper wraps handleTicketCustomerUsers with DB lookup.

Functions

func BroadcastTicketUpdate

func BroadcastTicketUpdate(eventType string, ticketData interface{})

BroadcastTicketUpdate sends an update to all connected clients.

func BuildDynamicFieldFilterSQL

func BuildDynamicFieldFilterSQL(filters []DynamicFieldFilter, startArgNum int) (string, []interface{}, error)

Returns the WHERE clause fragment and arguments to append.

func BuildRoutesManifest

func BuildRoutesManifest() ([]byte, error)

BuildRoutesManifest constructs the manifest JSON without registering routes (for tooling).

func BulkSetScreenConfigForField

func BulkSetScreenConfigForField(fieldID int, configs map[string]int, userID int) error

BulkSetScreenConfigForField replaces all screen configs for a field.

func CheckDynamicFieldNameExists

func CheckDynamicFieldNameExists(name string, excludeID int) (bool, error)

CheckDynamicFieldNameExists checks if a field name already exists.

func CheckSignatureNameExists

func CheckSignatureNameExists(name string, excludeID int) (bool, error)

CheckSignatureNameExists checks if a signature name already exists.

func CheckTemplateNameExists

func CheckTemplateNameExists(name string, excludeID int) (bool, error)

CheckTemplateNameExists checks if a template name already exists (excluding a specific ID).

func CombineRoutePath added in v0.8.2

func CombineRoutePath(prefix, route string) string

CombineRoutePath joins a route group prefix with a route path.

func CreateDynamicField

func CreateDynamicField(field *DynamicField, userID int) (int64, error)

CreateDynamicField creates a new dynamic field.

func CreateSignature

func CreateSignature(name, text, contentType, comments string, validID, userID int) (int, error)

CreateSignature creates a new signature.

func CreateStandardTemplate

func CreateStandardTemplate(t *StandardTemplate, userID int) (int, error)

CreateStandardTemplate creates a new template.

func CustomerOnlyGuard

func CustomerOnlyGuard(enabled bool) gin.HandlerFunc

CustomerOnlyGuard blocks non-customer paths when enabled, to keep admin/auth UIs off the customer FE.

func DeleteDynamicField

func DeleteDynamicField(id int) error

DeleteDynamicField deletes a dynamic field and its values.

func DeleteSignature

func DeleteSignature(id int) error

DeleteSignature deletes a signature by ID.

func DeleteStandardTemplate

func DeleteStandardTemplate(id int) error

DeleteStandardTemplate deletes a template and its relationships.

func EnsurePluginGroups added in v0.7.0

func EnsurePluginGroups()

EnsurePluginGroups auto-creates groups declared by plugins in their GKRegistration. Called after plugin loading to ensure the groups exist in the GoatFlow groups table. Idempotent — skips groups that already exist.

func ExportAllSignatures

func ExportAllSignatures() ([]byte, error)

ExportAllSignatures exports all signatures to YAML.

func ExportDynamicFieldsYAML

func ExportDynamicFieldsYAML(fieldNames []string, includeScreens bool) ([]byte, error)

ExportDynamicFieldsYAML exports dynamic fields to YAML format.

func ExportSignature

func ExportSignature(id int) ([]byte, error)

ExportSignature exports a single signature to YAML.

func GenerateRoutesManifest

func GenerateRoutesManifest() error

GenerateRoutesManifest creates a temporary gin engine, registers YAML routes and ensures the manifest file exists. Used by tooling (make routes-generate) without starting full server.

func GetAttachmentTemplateIDs

func GetAttachmentTemplateIDs(attachmentID int) ([]int, error)

GetAttachmentTemplateIDs returns template IDs assigned to an attachment.

func GetAuthService

func GetAuthService() *platformservice.AuthService

GetAuthService returns the singleton auth service instance.

func GetDistinctDynamicFieldValues

func GetDistinctDynamicFieldValues(fieldID int, limit int) ([]string, error)

Useful for populating filter dropdown options.

func GetDynamicFieldsGroupedByObjectType

func GetDynamicFieldsGroupedByObjectType() (map[string][]DynamicField, error)

GetDynamicFieldsGroupedByObjectType returns fields grouped by object type for admin display.

func GetDynamicHandler

func GetDynamicHandler() *dynamic.DynamicModuleHandler

GetDynamicHandler returns the initialized dynamic handler.

func GetHandler

func GetHandler(name string) (gin.HandlerFunc, bool)

GetHandler retrieves a registered handler.

func GetLookupService

func GetLookupService() *service.LookupService

GetLookupService returns the singleton lookup service instance.

func GetPluginManager

func GetPluginManager() *plugin.Manager

GetPluginManager returns the global plugin manager.

func GetPluginMenuItems

func GetPluginMenuItems(location string) []plugin.PluginMenuItem

GetPluginMenuItems returns menu items for a location.

func GetPriorityRepository

func GetPriorityRepository() *repository.PriorityRepository

GetPriorityRepository returns the singleton priority repository instance.

func GetQueueRepository

func GetQueueRepository() *repository.QueueRepository

GetQueueRepository returns the singleton queue repository instance.

func GetQueueTemplateIDs

func GetQueueTemplateIDs(queueID int) ([]int, error)

GetQueueTemplateIDs returns template IDs assigned to a queue.

func GetTemplateAttachments

func GetTemplateAttachments(templateID int) ([]int, error)

GetTemplateAttachments returns all attachment IDs assigned to a template.

func GetTemplateQueues

func GetTemplateQueues(templateID int) ([]int, error)

GetTemplateQueues returns all queues assigned to a template.

func GetTicketRepository

func GetTicketRepository() repository.ITicketRepository

GetTicketRepository returns the singleton ticket repository instance.

func GetTicketService

func GetTicketService() *service.SimpleTicketService

GetTicketService returns the singleton simple ticket service instance.

func GetTicketTemplateVariables

func GetTicketTemplateVariables(ticketID int) map[string]string

GetTicketTemplateVariables returns template variables for a ticket.

func GetUserIDFromCtx

func GetUserIDFromCtx(c *gin.Context, fallback int) int

GetUserIDFromCtx extracts the authenticated user's ID from gin context. Handles multiple types since different auth middleware may set different types. Returns the fallback value if user_id is not found or cannot be converted.

func GetUserIDFromCtxUint

func GetUserIDFromCtxUint(c *gin.Context, fallback uint) uint

GetUserIDFromCtxUint is like GetUserIDFromCtx but returns uint.

func GetUserMapForTemplate

func GetUserMapForTemplate(c *gin.Context) gin.H

GetUserMapForTemplate exposes the internal user-context builder for reuse across packages without duplicating logic.

func GetUserRepository

func GetUserRepository() *repository.UserRepository

GetUserRepository returns the singleton user repository instance.

func HandleAPIQueueDetails

func HandleAPIQueueDetails(c *gin.Context)

HandleAPIQueueDetails handles GET /api/queues/:id/details (internal UI API, outside the /api/v1 Swagger base path).

func HandleAPIQueueGet

func HandleAPIQueueGet(c *gin.Context)

HandleAPIQueueGet handles GET /api/queues/:id and, through HandleGetQueueAPI, GET /api/v1/queues/:id (documented in api/openapi.yaml).

func HandleAPIQueueStatus

func HandleAPIQueueStatus(c *gin.Context)

HandleAPIQueueStatus handles PUT /api/queues/:id/status (internal UI API, outside the /api/v1 Swagger base path).

func HandleAPISetupOnboardCustomer added in v0.9.0

func HandleAPISetupOnboardCustomer(c *gin.Context)

HandleAPISetupOnboardCustomer provisions a full customer setup (company + portal users with generated passwords) from one JSON payload. Lets an LLM or script onboard a customer end-to-end without the wizard UI. POST /api/v1/admin/setup/onboard-customer

func HandleAPISetupRecce added in v0.9.0

func HandleAPISetupRecce(c *gin.Context)

HandleAPISetupRecce returns the system snapshot (entity counts + setup status) as JSON. Lets an LLM or script understand current state. GET /api/v1/admin/setup/recce

func HandleAPISetupTask added in v0.9.0

func HandleAPISetupTask(c *gin.Context)

HandleAPISetupTask dispatches a plugin setup task, forwarding the JSON body (minus client-supplied envelope keys, plus the host envelope) to the plugin handler. Core tasks use the HTML route (handleAdminSetupTask). POST /api/v1/admin/setup/tasks/:plugin/:task_id

func HandleAPISetupTasks added in v0.9.0

func HandleAPISetupTasks(c *gin.Context)

HandleAPISetupTasks returns the combined core + plugin task catalog. GET /api/v1/admin/setup/tasks

func HandleAPISetupWizard added in v0.9.0

func HandleAPISetupWizard(c *gin.Context)

HandleAPISetupWizard accepts a full WizardRequest JSON payload and creates every entity in dependency order. Does NOT auto-mark setup complete — the caller (UI or LLM) decides by calling the mark-complete task afterwards. POST /api/v1/admin/setup/wizard

func HandleAddTicketTime

func HandleAddTicketTime(c *gin.Context)

HandleAddTicketTime is the exported wrapper for YAML routing in the routing package It delegates to handleAddTicketTime to keep the implementation in one place.

func HandleAdminCreateTargetToken

func HandleAdminCreateTargetToken(c *gin.Context)

HandleAdminCreateTargetToken creates a token for a specific user or customer (admin only) POST /api/v1/admin/users/:userId/tokens or /api/v1/admin/customer-users/:customerId/tokens

func HandleAdminCustomerUsersBulkAction

func HandleAdminCustomerUsersBulkAction(c *gin.Context)

HandleAdminCustomerUsersBulkAction handles POST /admin/customer-users/bulk-action.

func HandleAdminCustomerUsersCreate

func HandleAdminCustomerUsersCreate(c *gin.Context)

HandleAdminCustomerUsersCreate handles POST /admin/customer-users.

func HandleAdminCustomerUsersDelete

func HandleAdminCustomerUsersDelete(c *gin.Context)

HandleAdminCustomerUsersDelete handles DELETE /admin/customer-users/:id (soft delete).

func HandleAdminCustomerUsersExport

func HandleAdminCustomerUsersExport(c *gin.Context)

HandleAdminCustomerUsersExport handles GET /admin/customer-users/export.

func HandleAdminCustomerUsersGet

func HandleAdminCustomerUsersGet(c *gin.Context)

HandleAdminCustomerUsersGet handles GET /admin/customer-users/:id.

func HandleAdminCustomerUsersImport

func HandleAdminCustomerUsersImport(c *gin.Context)

HandleAdminCustomerUsersImport handles POST /admin/customer-users/import.

func HandleAdminCustomerUsersImportForm

func HandleAdminCustomerUsersImportForm(c *gin.Context)

HandleAdminCustomerUsersImportForm handles GET /admin/customer-users/import.

func HandleAdminCustomerUsersList

func HandleAdminCustomerUsersList(c *gin.Context)

HandleAdminCustomerUsersList handles GET /admin/customer-users.

func HandleAdminCustomerUsersTickets

func HandleAdminCustomerUsersTickets(c *gin.Context)

HandleAdminCustomerUsersTickets handles GET /admin/customer-users/:id/tickets.

func HandleAdminCustomerUsersUpdate

func HandleAdminCustomerUsersUpdate(c *gin.Context)

HandleAdminCustomerUsersUpdate handles PUT /admin/customer-users/:id.

func HandleAdminDynamicModule

func HandleAdminDynamicModule(c *gin.Context)

func HandleAdminDynamicModuleFor

func HandleAdminDynamicModuleFor(module string) gin.HandlerFunc

HandleAdminDynamicModuleFor returns a handler that forces the module param to a static value.

func HandleAdminExecuteSQL added in v0.8.2

func HandleAdminExecuteSQL(c *gin.Context)

HandleAdminExecuteSQL executes a read-only SQL query. Requires admin middleware. Only allowlisted statement types are permitted: SELECT, DESCRIBE, EXPLAIN, SHOW TABLES, SHOW COLUMNS.

@Summary		Execute read-only SQL query
@Description	Execute a read-only SQL query (admin only). Allowed statements: SELECT, DESCRIBE, EXPLAIN, SHOW TABLES, SHOW COLUMNS.
@Tags			Admin
@Accept			json
@Produce		json
@Param			request	body		adminSQLRequest	true	"SQL query and optional arguments"
@Success		200		{object}	map[string]interface{}	"Query results"
@Failure		400		{object}	map[string]interface{}	"Bad request"
@Failure		403		{object}	map[string]interface{}	"Forbidden"
@Security		BearerAuth
@Router			/admin/sql [post]

func HandleAdminGroupsAddUser

func HandleAdminGroupsAddUser(c *gin.Context)

HandleAdminGroupsAddUser handles POST /admin/groups/:id/users.

func HandleAdminGroupsCreate

func HandleAdminGroupsCreate(c *gin.Context)

HandleAdminGroupsCreate handles POST /admin/groups.

func HandleAdminGroupsDelete

func HandleAdminGroupsDelete(c *gin.Context)

HandleAdminGroupsDelete handles DELETE /admin/groups/:id.

func HandleAdminGroupsRemoveUser

func HandleAdminGroupsRemoveUser(c *gin.Context)

HandleAdminGroupsRemoveUser handles DELETE /admin/groups/:id/users/:userId.

func HandleAdminGroupsUpdate

func HandleAdminGroupsUpdate(c *gin.Context)

HandleAdminGroupsUpdate handles PUT /admin/groups/:id.

func HandleAdminGroupsUsers

func HandleAdminGroupsUsers(c *gin.Context)

HandleAdminGroupsUsers handles GET /admin/groups/:id/users.

func HandleAdminListAllTokens

func HandleAdminListAllTokens(c *gin.Context)

HandleAdminListAllTokens returns all tokens (admin only) GET /api/v1/admin/tokens

func HandleAdminListTargetTokens

func HandleAdminListTargetTokens(c *gin.Context)

HandleAdminListTargetTokens returns tokens for a specific user or customer (admin only) GET /api/v1/admin/users/:userId/tokens or /api/v1/admin/customer-users/:customerId/tokens

func HandleAdminMarketplace added in v0.9.0

func HandleAdminMarketplace(c *gin.Context)

HandleAdminMarketplace renders the marketplace browser page.

func HandleAdminNotificationEventEdit

func HandleAdminNotificationEventEdit(c *gin.Context)

HandleAdminNotificationEventEdit renders the notification event edit form.

func HandleAdminNotificationEventGet

func HandleAdminNotificationEventGet(c *gin.Context)

HandleAdminNotificationEventGet returns a notification event's details as JSON.

func HandleAdminNotificationEventNew

func HandleAdminNotificationEventNew(c *gin.Context)

HandleAdminNotificationEventNew renders the new notification event form.

func HandleAdminNotificationEvents

func HandleAdminNotificationEvents(c *gin.Context)

HandleAdminNotificationEvents renders the notification events management page.

func HandleAdminPluginLogs

func HandleAdminPluginLogs(c *gin.Context)

HandleAdminPluginLogs renders the plugin logs page. Data is fetched client-side from /api/v1/plugins/logs.

func HandleAdminPluginUIs added in v0.8.3

func HandleAdminPluginUIs(c *gin.Context)

HandleAdminPluginUIs renders the plugin UI management page.

func HandleAdminPlugins

func HandleAdminPlugins(c *gin.Context)

HandleAdminPlugins renders the plugin management page. Data is fetched client-side from /api/v1/plugins.

func HandleAdminPostmasterFilterEdit

func HandleAdminPostmasterFilterEdit(c *gin.Context)

handleAdminPostmasterFilterEdit renders the filter edit form.

func HandleAdminPostmasterFilterGet

func HandleAdminPostmasterFilterGet(c *gin.Context)

handleAdminPostmasterFilterGet returns a filter's details as JSON.

func HandleAdminPostmasterFilterNew

func HandleAdminPostmasterFilterNew(c *gin.Context)

handleAdminPostmasterFilterNew renders the new filter creation form.

func HandleAdminPostmasterFilters

func HandleAdminPostmasterFilters(c *gin.Context)

handleAdminPostmasterFilters renders the postmaster filters management page.

func HandleAdminRevokeTargetToken

func HandleAdminRevokeTargetToken(c *gin.Context)

HandleAdminRevokeTargetToken revokes a specific user's or customer's token (admin only) DELETE /api/v1/admin/users/:userId/tokens/:tokenId or /api/v1/admin/customer-users/:customerId/tokens/:tokenId

func HandleAdminRevokeToken

func HandleAdminRevokeToken(c *gin.Context)

HandleAdminRevokeToken revokes any token (admin only) DELETE /api/v1/admin/tokens/:id

func HandleAdminTickets

func HandleAdminTickets(c *gin.Context)

HandleAdminTickets displays all tickets for admin management.

func HandleAdminUserCreate

func HandleAdminUserCreate(c *gin.Context)

HandleAdminUserCreate handles POST /admin/users.

func HandleAdminUserDelete

func HandleAdminUserDelete(c *gin.Context)

HandleAdminUserDelete handles DELETE /admin/users/:id.

func HandleAdminUserGet

func HandleAdminUserGet(c *gin.Context)

HandleAdminUserGet handles GET /admin/users/:id.

func HandleAdminUserGroups

func HandleAdminUserGroups(c *gin.Context)

HandleAdminUserGroups handles GET /admin/users/:id/groups.

func HandleAdminUserResetPassword

func HandleAdminUserResetPassword(c *gin.Context)

HandleAdminUserResetPassword handles password reset for a user by admin.

func HandleAdminUserUpdate

func HandleAdminUserUpdate(c *gin.Context)

HandleAdminUserUpdate handles PUT /admin/users/:id.

func HandleAdminUsers

func HandleAdminUsers(c *gin.Context)

HandleAdminUsers renders the admin users management page.

func HandleAdminUsersList

func HandleAdminUsersList(c *gin.Context)

HandleAdminUsersList handles GET /admin/users (JSON API).

func HandleAdminUsersStatus

func HandleAdminUsersStatus(c *gin.Context)

HandleAdminUsersStatus handles PUT /admin/users/:id/status to toggle user valid status.

func HandleAgentChangePassword

func HandleAgentChangePassword(c *gin.Context)

HandleAgentChangePassword processes the agent password change request.

func HandleAgentCreateTicket

func HandleAgentCreateTicket(db *sql.DB) gin.HandlerFunc

HandleAgentCreateTicket creates a new ticket from the agent interface.

func HandleAgentNewTicket

func HandleAgentNewTicket(db *sql.DB) gin.HandlerFunc

HandleAgentNewTicket displays the agent ticket creation form with necessary data.

func HandleAgentPasswordForm

func HandleAgentPasswordForm(c *gin.Context)

HandleAgentPasswordForm renders the agent password change form.

func HandleAgentPerformanceAPI

func HandleAgentPerformanceAPI(c *gin.Context)

HandleAgentPerformanceAPI handles GET /api/v1/statistics/agents.

Admins get every valid agent, including agents with no activity. Other agents get only the agents with activity (assigned, closed or articles) on tickets in the queues they can read: those agents are already visible on those tickets, while the full roster is not.

@Summary		Get agent performance
@Description	Per-agent tickets assigned, tickets closed and articles created in the period (RBAC filtered). Admins see every valid agent; other agents see only agents with activity on tickets in queues they can read.
@Tags			Statistics
@Accept			json
@Produce		json
@Param			period	query		string	false	"Rolling window ending now"	Enums(24h, 7d, 30d)	default(7d)
@Success		200		{object}	map[string]interface{}	"period, agents[], top_performers[]"
@Failure		400		{object}	map[string]interface{}	"Invalid parameter"
@Failure		401		{object}	map[string]interface{}	"Unauthorized"
@Failure		403		{object}	map[string]interface{}	"Customer caller or no readable queue"
@Failure		500		{object}	map[string]interface{}	"Database error"
@Security		BearerAuth
@Router			/statistics/agents [get]

func HandleAssignQueueGroupAPI

func HandleAssignQueueGroupAPI(c *gin.Context)

HandleAssignQueueGroupAPI handles POST /api/v1/queues/:id/groups.

@Summary		Set queue group
@Description	Make the given group the queue's permission group (queue.group_id), replacing the current one. A queue has exactly one group.
@Tags			Queues
@Accept			json
@Produce		json
@Param			id		path		int		true	"Queue ID"
@Param			group	body		object	true	"Group assignment (group_id)"
@Success		200		{object}	map[string]interface{}	"Group assigned"
@Failure		400		{object}	map[string]interface{}	"Invalid request or group"
@Failure		401		{object}	map[string]interface{}	"Unauthorized"
@Failure		404		{object}	map[string]interface{}	"Queue not found"
@Security		BearerAuth
@Router			/queues/{id}/groups [post]

func HandleAssignTicketAPI

func HandleAssignTicketAPI(c *gin.Context)

HandleAssignTicketAPI handles ticket assignment via API. No production route serves it.

func HandleClearPluginLogs

func HandleClearPluginLogs(c *gin.Context)

HandleClearPluginLogs clears the plugin log buffer. DELETE /api/v1/plugins/logs

func HandleCloseTicketAPI

func HandleCloseTicketAPI(c *gin.Context)

HandleCloseTicketAPI handles ticket closure via API. No production route serves it.

func HandleCreateArticleAPI

func HandleCreateArticleAPI(c *gin.Context)

HandleCreateArticleAPI handles POST /api/v1/tickets/:ticket_id/articles.

@Summary		Create article
@Description	Add a new article (reply/note) to a ticket
@Tags			Articles
@Accept			json
@Produce		json
@Param			ticket_id	path		int		true	"Ticket ID"
@Param			article		body		object	true	"Article data (body, subject, article_type, sender_type, etc.)"
@Success		201			{object}	map[string]interface{}	"Created article"
@Failure		400			{object}	map[string]interface{}	"Invalid request"
@Failure		401			{object}	map[string]interface{}	"Unauthorized"
@Failure		404			{object}	map[string]interface{}	"Ticket not found"
@Security		BearerAuth
@Router			/tickets/{ticket_id}/articles [post]

func HandleCreateInternalNote

func HandleCreateInternalNote(c *gin.Context)

HandleCreateInternalNote creates a new internal note.

func HandleCreateNotificationEvent

func HandleCreateNotificationEvent(c *gin.Context)

HandleCreateNotificationEvent creates a new notification event.

func HandleCreatePostmasterFilter

func HandleCreatePostmasterFilter(c *gin.Context)

handleCreatePostmasterFilter creates a new postmaster filter.

func HandleCreatePriorityAPI

func HandleCreatePriorityAPI(c *gin.Context)

HandleCreatePriorityAPI handles POST /api/v1/priorities.

@Summary		Create priority
@Description	Create a new ticket priority
@Tags			Priorities
@Accept			json
@Produce		json
@Param			priority	body		object	true	"Priority data"
@Success		201			{object}	map[string]interface{}	"Created priority"
@Failure		400			{object}	map[string]interface{}	"Invalid request"
@Failure		401			{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/priorities [post]

func HandleCreateQueueAPI

func HandleCreateQueueAPI(c *gin.Context)

HandleCreateQueueAPI handles POST /api/v1/queues.

@Summary		Create queue
@Description	Create a new queue
@Tags			Queues
@Accept			json
@Produce		json
@Param			queue	body		object	true	"Queue data (name, group_id, etc.)"
@Success		201		{object}	map[string]interface{}	"Created queue"
@Failure		400		{object}	map[string]interface{}	"Invalid request"
@Failure		401		{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/queues [post]

func HandleCreateSalutationAPI

func HandleCreateSalutationAPI(c *gin.Context)

HandleCreateSalutationAPI handles POST /api/v1/salutations.

@Summary		Create salutation
@Description	Create a new email salutation
@Tags			Email Identity
@Accept			json
@Produce		json
@Param			salutation	body		object	true	"Salutation data"
@Success		201			{object}	map[string]interface{}	"Created salutation"
@Failure		400			{object}	map[string]interface{}	"Invalid request"
@Failure		401			{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/salutations [post]

func HandleCreateSavedSearchAPI added in v0.10.0

func HandleCreateSavedSearchAPI(c *gin.Context)

HandleCreateSavedSearchAPI saves a ticket search under a new name.

@Summary		Create saved search
@Tags			Search
@Accept			json
@Produce		json
@Param			search	body	object	true	"{name, type: TicketSearch (optional), parameters: {<search parameter>: string | [string]}}"
@Success		201	{object}	map[string]interface{}	"{success, data: {name, type, parameters}}"
@Failure		400	{object}	map[string]interface{}	"Invalid name or parameters"
@Failure		409	{object}	map[string]interface{}	"Name already used"
@Security		BearerAuth
@Router			/search/saved [post]

func HandleCreateSignatureAPI

func HandleCreateSignatureAPI(c *gin.Context)

HandleCreateSignatureAPI handles POST /api/v1/signatures.

@Summary		Create signature
@Description	Create a new email signature
@Tags			Email Identity
@Accept			json
@Produce		json
@Param			signature	body		object	true	"Signature data"
@Success		201			{object}	map[string]interface{}	"Created signature"
@Failure		400			{object}	map[string]interface{}	"Invalid request"
@Failure		401			{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/signatures [post]

func HandleCreateSystemAddressAPI

func HandleCreateSystemAddressAPI(c *gin.Context)

HandleCreateSystemAddressAPI handles POST /api/v1/system-addresses.

@Summary		Create system address
@Description	Create a new system email address
@Tags			Email Identity
@Accept			json
@Produce		json
@Param			address	body		object	true	"System address data"
@Success		201		{object}	map[string]interface{}	"Created address"
@Failure		400		{object}	map[string]interface{}	"Invalid request"
@Failure		401		{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/system-addresses [post]

func HandleCreateTicketAPI

func HandleCreateTicketAPI(c *gin.Context)

HandleCreateTicketAPI handles ticket creation via API.

@Summary		Create ticket
@Description	Create a new ticket with optional initial article
@Tags			Tickets
@Accept			json
@Produce		json
@Param			ticket	body		object	true	"Ticket data (title, queue_id, priority_id, customer_user_id, article)"
@Success		201		{object}	map[string]interface{}	"Created ticket"
@Failure		400		{object}	map[string]interface{}	"Invalid request"
@Failure		401		{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/tickets [post]

func HandleCreateToken

func HandleCreateToken(c *gin.Context)

HandleCreateToken creates a new API token (agent or customer) POST /api/v1/tokens or /customer/api/v1/tokens

@Summary		Create API token
@Description	Create a new API token for the authenticated user
@Tags			API Tokens
@Accept			json
@Produce		json
@Param			token	body		object	true	"Token data (name, scopes, expires_at)"
@Success		201		{object}	map[string]interface{}	"Created token (includes raw token - save it!)"
@Failure		400		{object}	map[string]interface{}	"Invalid request"
@Failure		401		{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/tokens [post]

func HandleCustomerStatisticsAPI

func HandleCustomerStatisticsAPI(c *gin.Context)

HandleCustomerStatisticsAPI handles GET /api/v1/statistics/customers.

@Summary		Get customer statistics
@Description	All-time top customers by ticket count and customer totals (RBAC filtered)
@Tags			Statistics
@Accept			json
@Produce		json
@Param			top	query		int	false	"Number of top customers returned"	minimum(1)	maximum(100)	default(10)
@Success		200	{object}	map[string]interface{}	"top_customers[], customer_metrics"
@Failure		400	{object}	map[string]interface{}	"Invalid parameter"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Failure		403	{object}	map[string]interface{}	"Customer caller or no readable queue"
@Failure		500	{object}	map[string]interface{}	"Database error"
@Security		BearerAuth
@Router			/statistics/customers [get]

func HandleDashboardStatisticsAPI

func HandleDashboardStatisticsAPI(c *gin.Context)

HandleDashboardStatisticsAPI handles GET /api/v1/statistics/dashboard.

@Summary		Get dashboard statistics
@Description	All-time ticket counts by state bucket, valid queue and valid priority, plus the 10 newest tickets (RBAC filtered)
@Tags			Statistics
@Accept			json
@Produce		json
@Success		200	{object}	map[string]interface{}	"overview, by_queue[], by_priority[], recent_activity[]"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Failure		403	{object}	map[string]interface{}	"Customer caller or no readable queue"
@Failure		500	{object}	map[string]interface{}	"Database error"
@Security		BearerAuth
@Router			/statistics/dashboard [get]

func HandleDebugConfigSources

func HandleDebugConfigSources(c *gin.Context)

HandleDebugConfigSources lists configuration settings with their source.

func HandleDebugTicketNumber

func HandleDebugTicketNumber(c *gin.Context)

HandleDebugTicketNumber returns current ticket number generator info.

func HandleDeleteArticleAPI

func HandleDeleteArticleAPI(c *gin.Context)

HandleDeleteArticleAPI handles DELETE /api/v1/tickets/:ticket_id/articles/:id.

@Summary		Delete article
@Description	Delete an article, its MIME data and attachments from a ticket
@Tags			Articles
@Accept			json
@Produce		json
@Param			ticket_id	path	int	true	"Ticket ID"
@Param			id			path	int	true	"Article ID"
@Success		200			{object}	map[string]interface{}	"Article deleted"
@Failure		401			{object}	map[string]interface{}	"Unauthorized"
@Failure		404			{object}	map[string]interface{}	"Article not found"
@Security		BearerAuth
@Router			/tickets/{ticket_id}/articles/{id} [delete]

func HandleDeleteInternalNote

func HandleDeleteInternalNote(c *gin.Context)

HandleDeleteInternalNote deletes an internal note (the underlying article).

func HandleDeleteNotificationEvent

func HandleDeleteNotificationEvent(c *gin.Context)

HandleDeleteNotificationEvent deletes a notification event.

func HandleDeletePostmasterFilter

func HandleDeletePostmasterFilter(c *gin.Context)

handleDeletePostmasterFilter deletes a postmaster filter.

func HandleDeletePriorityAPI

func HandleDeletePriorityAPI(c *gin.Context)

HandleDeletePriorityAPI handles DELETE /api/v1/priorities/:id.

@Summary		Delete priority
@Description	Delete a priority
@Tags			Priorities
@Accept			json
@Produce		json
@Param			id	path		int	true	"Priority ID"
@Success		200	{object}	map[string]interface{}	"Priority deleted"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Failure		404	{object}	map[string]interface{}	"Priority not found"
@Security		BearerAuth
@Router			/priorities/{id} [delete]

func HandleDeleteQueueAPI

func HandleDeleteQueueAPI(c *gin.Context)

HandleDeleteQueueAPI handles DELETE /api/v1/queues/:id.

@Summary		Delete queue
@Description	Delete a queue (soft delete)
@Tags			Queues
@Accept			json
@Produce		json
@Param			id	path		int	true	"Queue ID"
@Success		200	{object}	map[string]interface{}	"Queue deleted"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Failure		404	{object}	map[string]interface{}	"Queue not found"
@Security		BearerAuth
@Router			/queues/{id} [delete]

func HandleDeleteSavedSearchAPI added in v0.10.0

func HandleDeleteSavedSearchAPI(c *gin.Context)

HandleDeleteSavedSearchAPI deletes a saved search.

@Summary		Delete saved search
@Tags			Search
@Param			name	path	string	true	"Saved search name"
@Success		204
@Failure		404	{object}	map[string]interface{}	"Not found"
@Security		BearerAuth
@Router			/search/saved/{name} [delete]

func HandleDeleteTicketAPI

func HandleDeleteTicketAPI(c *gin.Context)

HandleDeleteTicketAPI handles DELETE /api/v1/tickets/:id. In OTRS, tickets are never hard deleted, only archived.

@Summary		Delete (archive) ticket
@Description	Archive a ticket (soft delete - tickets are never hard deleted)
@Tags			Tickets
@Accept			json
@Produce		json
@Param			id	path		int	true	"Ticket ID"
@Success		200	{object}	map[string]interface{}	"Ticket archived"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Failure		404	{object}	map[string]interface{}	"Ticket not found"
@Security		BearerAuth
@Router			/tickets/{id} [delete]

func HandleDetailedHealthCheck added in v0.10.0

func HandleDetailedHealthCheck(c *gin.Context)

HandleDetailedHealthCheck reports every probeable component plus uptime and build information. It is admin-gated at route level (routes/basic.yaml): an operator endpoint, not a probe target — /health covers Docker/TrueNAS/k8s probes and stays public.

func HandleDismissCoachmark

func HandleDismissCoachmark(c *gin.Context)

HandleDismissCoachmark records a coachmark as dismissed for the current user.

func HandleExecuteSavedSearchAPI added in v0.10.0

func HandleExecuteSavedSearchAPI(c *gin.Context)

HandleExecuteSavedSearchAPI runs a saved search. Paging and ordering come from the query string (page, limit, sort, order).

@Summary		Execute saved search
@Tags			Search
@Produce		json
@Param			name	path	string	true	"Saved search name"
@Success		200	{object}	map[string]interface{}	"{success, data: {name, parameters, ignored_parameters, tickets, pagination}}"
@Failure		404	{object}	map[string]interface{}	"Not found"
@Security		BearerAuth
@Router			/search/saved/{name}/execute [post]

func HandleExportStatisticsAPI

func HandleExportStatisticsAPI(c *gin.Context)

HandleExportStatisticsAPI handles GET /api/v1/statistics/export.

@Summary		Export statistics
@Description	Download a summary or the ticket list for tickets created in the period, as JSON or CSV (RBAC filtered)
@Tags			Statistics
@Accept			json
@Produce		json
@Produce		text/csv
@Param			format	query		string	false	"File format"	Enums(json, csv)	default(json)
@Param			type	query		string	false	"Export content"	Enums(summary, tickets)	default(summary)
@Param			period	query		string	false	"Rolling window ending now"	Enums(24h, 7d, 30d)	default(7d)
@Success		200		{file}		file	"statistics_<YYYYMMDD_HHMMSS>.json or .csv attachment"
@Failure		400		{object}	map[string]interface{}	"Invalid parameter"
@Failure		401		{object}	map[string]interface{}	"Unauthorized"
@Failure		403		{object}	map[string]interface{}	"Customer caller or no readable queue"
@Failure		500		{object}	map[string]interface{}	"Database error"
@Security		BearerAuth
@Router			/statistics/export [get]

func HandleGetArticleAPI

func HandleGetArticleAPI(c *gin.Context)

HandleGetArticleAPI handles GET /api/v1/tickets/:ticket_id/articles/:id.

@Summary		Get article by ID
@Description	Retrieve a single article by its ID
@Tags			Articles
@Accept			json
@Produce		json
@Param			ticket_id			path		int		true	"Ticket ID"
@Param			id					path		int		true	"Article ID"
@Param			include_attachments	query		bool	false	"Include attachment metadata"
@Success		200					{object}	map[string]interface{}	"Article details"
@Failure		401					{object}	map[string]interface{}	"Unauthorized"
@Failure		404					{object}	map[string]interface{}	"Article not found"
@Security		BearerAuth
@Router			/tickets/{ticket_id}/articles/{id} [get]

func HandleGetAvailableLanguages

func HandleGetAvailableLanguages(c *gin.Context)

HandleGetAvailableLanguages returns the list of supported languages (public, no auth required). Used on login pages to allow language selection before authentication.

func HandleGetAvailableThemes

func HandleGetAvailableThemes(c *gin.Context)

HandleGetAvailableThemes returns the list of available themes (public, no auth required). Used on login pages to allow theme selection before authentication.

func HandleGetFormData

func HandleGetFormData(c *gin.Context)

HandleGetFormData returns form data for ticket creation as JSON.

func HandleGetInternalNotes

func HandleGetInternalNotes(c *gin.Context)

HandleGetInternalNotes returns internal notes for a ticket.

func HandleGetLanguage

func HandleGetLanguage(c *gin.Context)

HandleGetLanguage retrieves the user's language preference.

func HandleGetPriorities

func HandleGetPriorities(c *gin.Context)

HandleGetPriorities returns list of priorities as JSON or HTML options for HTMX.

func HandleGetPriorityAPI

func HandleGetPriorityAPI(c *gin.Context)

HandleGetPriorityAPI handles GET /api/v1/priorities/:id.

@Summary		Get priority by ID
@Description	Retrieve a single priority
@Tags			Priorities
@Accept			json
@Produce		json
@Param			id	path		int	true	"Priority ID"
@Success		200	{object}	map[string]interface{}	"Priority details"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Failure		404	{object}	map[string]interface{}	"Priority not found"
@Security		BearerAuth
@Router			/priorities/{id} [get]

func HandleGetProfile

func HandleGetProfile(c *gin.Context)

HandleGetProfile retrieves the current user's profile information.

func HandleGetQueueAPI

func HandleGetQueueAPI(c *gin.Context)

HandleGetQueueAPI handles GET /api/v1/queues/:id.

@Summary		Get queue by ID
@Description	Retrieve a single queue by its ID
@Tags			Queues
@Accept			json
@Produce		json
@Param			id	path		int	true	"Queue ID"
@Success		200	{object}	map[string]interface{}	"Queue details"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Failure		404	{object}	map[string]interface{}	"Queue not found"
@Security		BearerAuth
@Router			/queues/{id} [get]

func HandleGetQueueAgentsAPI

func HandleGetQueueAgentsAPI(c *gin.Context)

HandleGetQueueAgentsAPI handles GET /api/v1/queues/:id/agents.

@Summary		Get queue agents
@Description	List agents assigned to a queue
@Tags			Queues
@Accept			json
@Produce		json
@Param			id	path		int	true	"Queue ID"
@Success		200	{object}	map[string]interface{}	"List of agents"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Failure		404	{object}	map[string]interface{}	"Queue not found"
@Security		BearerAuth
@Router			/queues/{id}/agents [get]

func HandleGetQueueStatsAPI

func HandleGetQueueStatsAPI(c *gin.Context)

HandleGetQueueStatsAPI handles GET /api/v1/queues/:id/stats.

@Summary		Get queue statistics
@Description	Get ticket statistics for a queue
@Tags			Queues
@Accept			json
@Produce		json
@Param			id	path		int	true	"Queue ID"
@Success		200	{object}	map[string]interface{}	"Queue statistics"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Failure		404	{object}	map[string]interface{}	"Queue not found"
@Security		BearerAuth
@Router			/queues/{id}/stats [get]

func HandleGetQueues

func HandleGetQueues(c *gin.Context)

HandleGetQueues returns list of queues as JSON or HTML options for HTMX.

func HandleGetRemindersEnabled added in v0.7.0

func HandleGetRemindersEnabled(c *gin.Context)

HandleGetRemindersEnabled retrieves the user's reminders enabled preference.

func HandleGetSavedSearchAPI added in v0.10.0

func HandleGetSavedSearchAPI(c *gin.Context)

HandleGetSavedSearchAPI returns one saved search.

@Summary		Get saved search
@Tags			Search
@Produce		json
@Param			name	path	string	true	"Saved search name"
@Success		200	{object}	map[string]interface{}	"{success, data: {name, type, parameters, ignored_parameters}}"
@Failure		404	{object}	map[string]interface{}	"Not found"
@Security		BearerAuth
@Router			/search/saved/{name} [get]

func HandleGetScopes

func HandleGetScopes(c *gin.Context)

HandleGetScopes returns available scopes for token creation GET /api/v1/tokens/scopes Scopes are filtered based on the user's role and type

@Summary		Get available scopes
@Description	List available scopes for token creation (filtered by user role)
@Tags			API Tokens
@Accept			json
@Produce		json
@Success		200	{object}	map[string]interface{}	"List of available scopes"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/tokens/scopes [get]

func HandleGetSessionTimeout

func HandleGetSessionTimeout(c *gin.Context)

HandleGetSessionTimeout retrieves the user's session timeout preference.

func HandleGetStatuses

func HandleGetStatuses(c *gin.Context)

HandleGetStatuses returns list of ticket statuses as JSON.

func HandleGetTheme

func HandleGetTheme(c *gin.Context)

HandleGetTheme retrieves the user's theme preferences. Uses UserPreferencesService for agents/admins, CustomerPreferencesService for customers.

func HandleGetTicketAPI

func HandleGetTicketAPI(c *gin.Context)

HandleGetTicketAPI handles GET /api/v1/tickets/:id.

@Summary		Get ticket by ID
@Description	Retrieve a single ticket by its ID with optional related data
@Tags			Tickets
@Accept			json
@Produce		json
@Param			id		path		int		true	"Ticket ID"
@Param			include	query		string	false	"Include related data (comma-separated: articles, customer, queue)"
@Success		200		{object}	map[string]interface{}	"Ticket details"
@Failure		401		{object}	map[string]interface{}	"Unauthorized"
@Failure		404		{object}	map[string]interface{}	"Ticket not found"
@Security		BearerAuth
@Router			/tickets/{id} [get]

func HandleGetTypes

func HandleGetTypes(c *gin.Context)

HandleGetTypes returns the valid ticket_type rows (id/value/label/order/active) as JSON, or as HTML <option> elements (value = type id) for HTMX.

func HandleHealthCheck added in v0.10.0

func HandleHealthCheck(c *gin.Context)

HandleHealthCheck is the liveness/readiness probe used by the Dockerfile HEALTHCHECK, the TrueNAS app template and Kubernetes probes. It performs a real (short-timeout) database ping so a backend with a dead DB reports 503 instead of a misleading "healthy".

func HandleInvalidateLookupCache

func HandleInvalidateLookupCache(c *gin.Context)

HandleInvalidateLookupCache forces a refresh of the lookup cache.

func HandleLegacyAgentTicketViewRedirect

func HandleLegacyAgentTicketViewRedirect(c *gin.Context)

HandleLegacyAgentTicketViewRedirect exported for YAML routing.

func HandleLegacyTicketsViewRedirect

func HandleLegacyTicketsViewRedirect(c *gin.Context)

HandleLegacyTicketsViewRedirect exported for YAML routing.

func HandleListArticlesAPI

func HandleListArticlesAPI(c *gin.Context)

HandleListArticlesAPI handles GET /api/v1/tickets/:ticket_id/articles.

@Summary		List ticket articles
@Description	Retrieve all articles for a ticket, newest first
@Tags			Articles
@Accept			json
@Produce		json
@Param			ticket_id			path		int		true	"Ticket ID"
@Param			include_attachments	query		bool	false	"Include attachment metadata"
@Success		200					{object}	map[string]interface{}	"List of articles"
@Failure		401					{object}	map[string]interface{}	"Unauthorized"
@Failure		404					{object}	map[string]interface{}	"Ticket not found"
@Security		BearerAuth
@Router			/tickets/{ticket_id}/articles [get]

func HandleListGroupsAPI

func HandleListGroupsAPI(c *gin.Context)

HandleListGroupsAPI handles GET /api/v1/groups.

@Summary		List groups
@Description	Retrieve all groups
@Tags			Groups
@Accept			json
@Produce		json
@Success		200	{object}	map[string]interface{}	"List of groups"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/groups [get]

func HandleListPrioritiesAPI

func HandleListPrioritiesAPI(c *gin.Context)

HandleListPrioritiesAPI handles GET /api/v1/priorities. Supports optional filtering via ticket attribute relations:

  • filter_attribute: The attribute to filter by (e.g., "Queue", "State")
  • filter_value: The value of that attribute (e.g., "Sales", "new")

HandleListPrioritiesAPI handles GET /api/v1/priorities.

@Summary		List priorities
@Description	Retrieve all ticket priorities
@Tags			Priorities
@Accept			json
@Produce		json
@Success		200	{object}	map[string]interface{}	"List of priorities"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/priorities [get]

func HandleListQueuesAPI

func HandleListQueuesAPI(c *gin.Context)

HandleListQueuesAPI handles GET /api/v1/queues.

@Summary		List queues
@Description	Retrieve all queues the user has access to (RBAC filtered)
@Tags			Queues
@Accept			json
@Produce		json
@Param			page		query		int		false	"Page number"		default(1)
@Param			per_page	query		int		false	"Items per page"	default(20)
@Success		200			{object}	map[string]interface{}	"List of queues"
@Failure		401			{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/queues [get]

func HandleListSalutationsAPI

func HandleListSalutationsAPI(c *gin.Context)

HandleListSalutationsAPI handles GET /api/v1/salutations.

@Summary		List salutations
@Description	List all email salutations
@Tags			Email Identity
@Accept			json
@Produce		json
@Success		200	{object}	map[string]interface{}	"List of salutations"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/salutations [get]

func HandleListSavedSearchesAPI added in v0.10.0

func HandleListSavedSearchesAPI(c *gin.Context)

HandleListSavedSearchesAPI lists the caller's saved ticket searches.

@Summary		List saved searches
@Tags			Search
@Produce		json
@Success		200	{object}	map[string]interface{}	"{success, data: {searches: [{name, type}], total}}"
@Security		BearerAuth
@Router			/search/saved [get]

func HandleListServicesAPI

func HandleListServicesAPI(c *gin.Context)

HandleListServicesAPI handles GET /api/v1/services. Supports optional filtering via ticket attribute relations:

  • filter_attribute: The attribute to filter by (e.g., "Queue", "Type")
  • filter_value: The value of that attribute (e.g., "Sales", "incident")
  • valid: Filter by valid_id (1 = valid, 2 = invalid, "all" = no filter)

HandleListServicesAPI handles GET /api/v1/services.

@Summary		List services
@Description	Retrieve all services
@Tags			Services
@Accept			json
@Produce		json
@Param			valid	query		string	false	"Filter by validity (1=valid, 2=invalid, all)"
@Success		200		{object}	map[string]interface{}	"List of services"
@Failure		401		{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/services [get]

func HandleListSignaturesAPI

func HandleListSignaturesAPI(c *gin.Context)

HandleListSignaturesAPI handles GET /api/v1/signatures.

@Summary		List signatures
@Description	List all email signatures
@Tags			Email Identity
@Accept			json
@Produce		json
@Success		200	{object}	map[string]interface{}	"List of signatures"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/signatures [get]

func HandleListStatesAPI

func HandleListStatesAPI(c *gin.Context)

HandleListStatesAPI handles GET /api/v1/states. Supports optional filtering via ticket attribute relations:

  • filter_attribute: The attribute to filter by (e.g., "Queue", "Priority")
  • filter_value: The value of that attribute (e.g., "Sales", "new")

HandleListStatesAPI handles GET /api/v1/states.

@Summary		List states
@Description	Retrieve all ticket states
@Tags			States
@Accept			json
@Produce		json
@Success		200	{object}	map[string]interface{}	"List of states"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/states [get]

func HandleListSystemAddressesAPI

func HandleListSystemAddressesAPI(c *gin.Context)

HandleListSystemAddressesAPI handles GET /api/v1/system-addresses.

@Summary		List system addresses
@Description	List all system email addresses
@Tags			Email Identity
@Accept			json
@Produce		json
@Success		200	{object}	map[string]interface{}	"List of system addresses"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/system-addresses [get]

func HandleListTicketsAPI

func HandleListTicketsAPI(c *gin.Context)

HandleListTicketsAPI handles GET /api/v1/tickets.

@Summary		List tickets
@Description	Retrieve a paginated list of tickets with optional filtering, sorting, and search
@Tags			Tickets
@Accept			json
@Produce		json
@Param			page				query		int		false	"Page number"						default(1)
@Param			per_page			query		int		false	"Items per page (max 100)"			default(20)
@Param			limit				query		int		false	"Alias for per_page (max 100)"		default(20)
@Param			offset				query		int		false	"Offset for pagination (alternative to page)"
@Param			status				query		string	false	"Filter by status"					Enums(open, closed, pending)
@Param			queue_id			query		int		false	"Filter by queue ID"
@Param			priority_id			query		int		false	"Filter by priority ID"
@Param			customer_user_id	query		string	false	"Filter by customer user ID/login"
@Param			assigned_user_id	query		int		false	"Filter by assigned agent user ID"
@Param			search				query		string	false	"Search in ticket number, title, customer"
@Param			sort				query		string	false	"Sort field"						Enums(created, updated, priority, tn)	default(created)
@Param			order				query		string	false	"Sort order"						Enums(asc, desc)						default(desc)
@Param			include				query		string	false	"Include related data (comma-separated: article_count, last_article)"
@Success		200					{object}	map[string]interface{}	"List of tickets with pagination"
@Failure		401					{object}	map[string]interface{}	"Unauthorized"
@Failure		500					{object}	map[string]interface{}	"Internal server error"
@Security		BearerAuth
@Router			/tickets [get]

func HandleListTokens

func HandleListTokens(c *gin.Context)

HandleListTokens returns all tokens for the current user (agent or customer) GET /api/v1/tokens or /customer/api/v1/tokens

@Summary		List my API tokens
@Description	List all API tokens for the authenticated user
@Tags			API Tokens
@Accept			json
@Produce		json
@Success		200	{object}	map[string]interface{}	"List of tokens"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/tokens [get]

func HandleListTypesAPI

func HandleListTypesAPI(c *gin.Context)

HandleListTypesAPI handles GET /api/v1/types. Supports optional filtering via ticket attribute relations:

  • filter_attribute: The attribute to filter by (e.g., "Queue", "Service")
  • filter_value: The value of that attribute (e.g., "Sales", "Gold Support")

The list is restricted to valid types unless ?valid=false|0 or ?valid=all is given.

@Summary		List types
@Description	Retrieve ticket types (valid ones by default)
@Tags			Types
@Accept			json
@Produce		json
@Param			valid	query		string	false	"Filter by validity (true/1 = valid (default), false/0 = invalid, all)"
@Success		200		{object}	map[string]interface{}	"List of types"
@Failure		401		{object}	map[string]interface{}	"Unauthorized"
@Failure		500		{object}	map[string]interface{}	"Lookup failed"
@Security		BearerAuth
@Router			/types [get]

func HandleMCP

func HandleMCP(c *gin.Context)

HandleMCP handles POST /api/mcp for MCP JSON-RPC messages. Requires Bearer token authentication via API token. Documented in docs/api/MCP.md (the route is outside the /api/v1 Swagger base path).

func HandleMCPInfo

func HandleMCPInfo(c *gin.Context)

HandleMCPInfo returns information about the MCP endpoint.

func HandleMCPSSE added in v0.8.2

func HandleMCPSSE(c *gin.Context)

HandleMCPSSE handles POST /api/mcp/sse for MCP Streamable HTTP transport. Creates sessions on "initialize", processes JSON-RPC messages on subsequent requests. Documented in docs/api/MCP.md (outside the /api/v1 Swagger base path).

func HandleMCPSSEDelete added in v0.8.2

func HandleMCPSSEDelete(c *gin.Context)

HandleMCPSSEDelete handles DELETE /api/mcp/sse for session termination.

func HandleMCPSSEStream added in v0.8.2

func HandleMCPSSEStream(c *gin.Context)

HandleMCPSSEStream handles GET /api/mcp/sse for server→client SSE notification stream.

func HandleMailAccountPollStatus

func HandleMailAccountPollStatus(c *gin.Context)

HandleMailAccountPollStatus returns the last poll status for a mail account from Valkey.

func HandleMarketplaceIndex added in v0.9.0

func HandleMarketplaceIndex(c *gin.Context)

HandleMarketplaceIndex returns the marketplace index annotated with install state and per-version compatibility with this GoatFlow. GET /api/v1/plugins/marketplace

func HandleMarketplaceInstall added in v0.9.0

func HandleMarketplaceInstall(c *gin.Context)

HandleMarketplaceInstall downloads, verifies, and installs a plugin from the marketplace. POST /api/v1/plugins/marketplace/install {"name": "goat-kb", "version": "1.2.0"} version is optional: without it a new install gets the newest version this GoatFlow can run and an installed plugin gets its newest compatible update.

func HandleMarketplaceSearch added in v0.9.0

func HandleMarketplaceSearch(c *gin.Context)

HandleMarketplaceSearch searches the marketplace index. GET /api/v1/plugins/marketplace/search?q=knowledge

func HandleMetrics added in v0.10.0

func HandleMetrics(c *gin.Context)

HandleMetrics serves the Prometheus exposition format from the default registerer. Cache metrics (cache_hits_total, cache_misses_total, cache_errors_total, cache_set_total, cache_delete_total, cache_latency_seconds, cache_size) are already recorded into the default registry by the Valkey cache via promauto, so they appear here with no extra wiring.

func HandlePasswordPolicy

func HandlePasswordPolicy(c *gin.Context)

HandlePasswordPolicy returns the agent password policy (PreferencesGroups###Password) that admin-set and self-chosen agent passwords are validated against.

func HandlePluginCall

func HandlePluginCall(c *gin.Context)

HandlePluginCall invokes a plugin function directly, bypassing the plugin's own route middleware, so it is mounted admin-only. POST /api/v1/plugins/:name/call/:fn

func HandlePluginDisable

func HandlePluginDisable(c *gin.Context)

HandlePluginDisable disables a plugin. POST /api/v1/plugins/:name/disable

func HandlePluginEnable

func HandlePluginEnable(c *gin.Context)

HandlePluginEnable enables a plugin. POST /api/v1/plugins/:name/enable

func HandlePluginHealth added in v0.8.3

func HandlePluginHealth(c *gin.Context)

HandlePluginHealth returns the live health snapshot for every loaded plugin. Used by the admin UI's plugin-health widget to show which plugins are healthy, in restart backoff, or abandoned. Includes the optional rich payload that plugins return from their __health_ping__ handler. GET /api/v1/plugins/health

func HandlePluginList

func HandlePluginList(c *gin.Context)

HandlePluginList returns all registered plugins. GET /api/v1/plugins

func HandlePluginLogs

func HandlePluginLogs(c *gin.Context)

HandlePluginLogs returns plugin log entries. GET /api/v1/plugins/logs?plugin=name&level=info&limit=100

func HandlePluginResetCrashLoop added in v0.8.3

func HandlePluginResetCrashLoop(c *gin.Context)

HandlePluginResetCrashLoop clears the crash-loop-abandoned flag for a plugin so auto-recovery can resume. Called by the admin UI after the operator has fixed the underlying problem (replaced a broken binary, patched a config error, etc.). POST /api/v1/plugins/:name/reset-crashloop

func HandlePluginSSEChannel added in v0.8.1

func HandlePluginSSEChannel(c *gin.Context)

HandlePluginSSEChannel serves an SSE stream scoped to a specific plugin and channel. GET /api/v1/plugins/:name/events/:channel Agents only (route group). When the plugin declares an EventAuthorizer, the plugin decides per caller and channel before the stream is opened.

func HandlePluginUIAdminList added in v0.8.3

func HandlePluginUIAdminList(c *gin.Context)

HandlePluginUIAdminList returns all registered plugin UIs for administrators.

func HandlePluginUIAdminToggle added in v0.8.3

func HandlePluginUIAdminToggle(c *gin.Context)

HandlePluginUIAdminToggle enables or disables a plugin UI.

func HandlePluginUIAdminUpdate added in v0.8.3

func HandlePluginUIAdminUpdate(c *gin.Context)

HandlePluginUIAdminUpdate updates admin-managed UI settings.

func HandlePluginUninstall added in v0.9.0

func HandlePluginUninstall(c *gin.Context)

HandlePluginUninstall removes a plugin: shuts it down, cleans up DB side-effects, removes the directory from disk, and clears in-memory state. DELETE /api/v1/plugins/:name

func HandlePluginUpload

func HandlePluginUpload(c *gin.Context)

HandlePluginUpload handles uploading a new WASM plugin. POST /api/v1/plugins/upload

func HandlePluginWidget

func HandlePluginWidget(c *gin.Context)

HandlePluginWidget returns a specific widget's HTML. GET /api/v1/plugins/:name/widgets/:id This triggers lazy loading if needed, making it HTMX-friendly.

func HandlePluginWidgetList

func HandlePluginWidgetList(c *gin.Context)

HandlePluginWidgetList returns available widgets for a location (triggers lazy load). GET /api/v1/plugins/widgets?location=dashboard

func HandleProfile

func HandleProfile(c *gin.Context)

HandleProfile is the exported version of handleProfile.

func HandleQueueMetricsAPI

func HandleQueueMetricsAPI(c *gin.Context)

HandleQueueMetricsAPI handles GET /api/v1/statistics/queues.

@Summary		Get queue metrics
@Description	All-time per-queue total, open and backlog counts for valid queues (RBAC filtered)
@Tags			Statistics
@Accept			json
@Produce		json
@Success		200	{object}	map[string]interface{}	"queues[], totals"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Failure		403	{object}	map[string]interface{}	"Customer caller or no readable queue"
@Failure		500	{object}	map[string]interface{}	"Database error"
@Security		BearerAuth
@Router			/statistics/queues [get]

func HandleRedirect

func HandleRedirect(c *gin.Context)

HandleRedirect handles redirect routes.

func HandleRedirectProfile

func HandleRedirectProfile(c *gin.Context)

HandleRedirectProfile is the exported version of redirect handler.

func HandleRedirectQueues

func HandleRedirectQueues(c *gin.Context)

HandleRedirectQueues is the exported version.

func HandleRedirectSettings

func HandleRedirectSettings(c *gin.Context)

HandleRedirectSettings is the exported version.

func HandleRedirectTickets

func HandleRedirectTickets(c *gin.Context)

HandleRedirectTickets is the exported version.

func HandleRedirectTicketsNew

func HandleRedirectTicketsNew(c *gin.Context)

HandleRedirectTicketsNew is the exported version.

func HandleReindexAPI

func HandleReindexAPI(c *gin.Context)

HandleReindexAPI handles POST /api/v1/search/reindex.

@Summary		Reindex search
@Description	Start a full rebuild of the Zinc or Elasticsearch index in the background (admin only). Progress is reported by GET /search/health. The database backend needs no index.
@Tags			Search
@Produce		json
@Success		200	{object}	map[string]interface{}	"Database backend: nothing to do"
@Success		202	{object}	map[string]interface{}	"Reindex started"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Failure		403	{object}	map[string]interface{}	"Admin access required"
@Failure		409	{object}	map[string]interface{}	"A reindex is already running"
@Failure		503	{object}	map[string]interface{}	"Search backend misconfigured"
@Security		BearerAuth
@Router			/search/reindex [post]

func HandleRemoveQueueGroupAPI

func HandleRemoveQueueGroupAPI(c *gin.Context)

HandleRemoveQueueGroupAPI handles DELETE /api/v1/queues/:id/groups/:group_id. A queue must always have a group, so removing its group is rejected; assign a different group (POST /queues/:id/groups) to move the queue instead.

@Summary		Remove group from queue (always rejected)
@Description	A queue has exactly one group, which cannot be removed. Returns 409 for the queue's group and 404 for any other group. Use POST /queues/{id}/groups to change it.
@Tags			Queues
@Accept			json
@Produce		json
@Param			id			path		int	true	"Queue ID"
@Param			group_id	path		int	true	"Group ID"
@Failure		401			{object}	map[string]interface{}	"Unauthorized"
@Failure		404			{object}	map[string]interface{}	"Queue not found or group not assigned"
@Failure		409			{object}	map[string]interface{}	"Queue must keep its group"
@Security		BearerAuth
@Router			/queues/{id}/groups/{group_id} [delete]

func HandleReopenTicketAPI

func HandleReopenTicketAPI(c *gin.Context)

HandleReopenTicketAPI handles ticket reopening via API.

@Summary		Reopen ticket
@Description	Reopen a closed ticket
@Tags			Ticket Actions
@Accept			json
@Produce		json
@Param			id	path		int	true	"Ticket ID"
@Success		200	{object}	map[string]interface{}	"Ticket reopened"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Failure		404	{object}	map[string]interface{}	"Ticket not found"
@Security		BearerAuth
@Router			/tickets/{id}/reopen [post]

func HandleRevokeToken

func HandleRevokeToken(c *gin.Context)

HandleRevokeToken revokes a token by ID (agent or customer) DELETE /api/v1/tokens/:id or /customer/api/v1/tokens/:id

@Summary		Revoke API token
@Description	Revoke an API token (user can only revoke own tokens)
@Tags			API Tokens
@Accept			json
@Produce		json
@Param			id	path		int	true	"Token ID"
@Success		200	{object}	map[string]interface{}	"Token revoked"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Failure		404	{object}	map[string]interface{}	"Token not found"
@Security		BearerAuth
@Router			/tokens/{id} [delete]

func HandleSearchAPI

func HandleSearchAPI(c *gin.Context)

HandleSearchAPI handles POST /api/v1/search.

@Summary		Search tickets
@Description	Full-text search across tickets, articles and customers. Ticket and article hits are limited to queues the caller can read.
@Tags			Search
@Accept			json
@Produce		json
@Param			query	body		object	true	"Search query"
@Success		200		{object}	map[string]interface{}	"Search results"
@Failure		400		{object}	map[string]interface{}	"Invalid request"
@Failure		401		{object}	map[string]interface{}	"Unauthorized"
@Failure		503		{object}	map[string]interface{}	"Search backend unavailable or index not built"
@Security		BearerAuth
@Router			/search [post]

func HandleSearchHealthAPI

func HandleSearchHealthAPI(c *gin.Context)

HandleSearchHealthAPI handles GET /api/v1/search/health.

@Summary		Search health
@Description	Report whether the search backend can serve searches; for Zinc and Elasticsearch also the index state, document counts and the latest reindex started by this server.
@Tags			Search
@Produce		json
@Success		200	{object}	map[string]interface{}	"Search health status"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Failure		503	{object}	map[string]interface{}	"Backend unreachable, misconfigured or index not built"
@Security		BearerAuth
@Router			/search/health [get]

func HandleServiceWorkerConfig added in v0.8.3

func HandleServiceWorkerConfig(c *gin.Context)

HandleServiceWorkerConfig returns the runtime cache configuration consumed by /sw.js.

func HandleSetLanguage

func HandleSetLanguage(c *gin.Context)

HandleSetLanguage sets the user's language preference.

func HandleSetPreLoginLanguage

func HandleSetPreLoginLanguage(c *gin.Context)

HandleSetPreLoginLanguage sets the language preference cookie (public, no auth required). This is used on login pages before the user authenticates.

func HandleSetPreLoginTheme

func HandleSetPreLoginTheme(c *gin.Context)

HandleSetPreLoginTheme sets the theme and mode preference cookies (public, no auth required). This is used on login pages before the user authenticates.

func HandleSetRemindersEnabled added in v0.7.0

func HandleSetRemindersEnabled(c *gin.Context)

HandleSetRemindersEnabled sets the user's reminders enabled preference.

func HandleSetSessionTimeout

func HandleSetSessionTimeout(c *gin.Context)

HandleSetSessionTimeout sets the user's session timeout preference.

func HandleSetTheme

func HandleSetTheme(c *gin.Context)

HandleSetTheme sets the user's theme preferences and persists to database. Uses UserPreferencesService for agents/admins, CustomerPreferencesService for customers.

func HandleSetWallpaper

func HandleSetWallpaper(c *gin.Context)

HandleSetWallpaper saves the user's wallpaper preference (on/off).

func HandleStaticFiles

func HandleStaticFiles(c *gin.Context)

HandleStaticFiles serves static files from the static directory.

func HandleTemplate

func HandleTemplate(c *gin.Context)

HandleTemplate handles template rendering routes.

func HandleTicketHistoryFragment

func HandleTicketHistoryFragment(c *gin.Context)

HandleTicketHistoryFragment is an HTMX handler for ticket history fragment in tab panel.

func HandleTicketLinksFragment

func HandleTicketLinksFragment(c *gin.Context)

HandleTicketLinksFragment is an HTMX handler for ticket links fragment in tab panel.

func HandleTicketSearchAPI added in v0.10.0

func HandleTicketSearchAPI(c *gin.Context)

HandleTicketSearchAPI searches tickets in the caller's readable queues.

@Summary		Search tickets
@Description	Agent ticket search over ticket, article and customer data. Text parameters match case-insensitively anywhere ('*' is a wildcard); ID and state_type parameters take comma-separated or repeated values; created_/changed_ take YYYY-MM-DD (whole day) or timestamps.
@Tags			Search
@Produce		json
@Param			q					query	string	false	"Words that must each match ticket number, title, customer, customer user (login, name, email), company name, or an article's subject, body, from or to"
@Param			tn					query	string	false	"Ticket number"
@Param			title				query	string	false	"Ticket title"
@Param			customer_id			query	string	false	"Customer (company) ID"
@Param			customer_user_login	query	string	false	"Customer user login"
@Param			from				query	string	false	"Article From"
@Param			to					query	string	false	"Article To or Cc"
@Param			subject				query	string	false	"Article subject"
@Param			body				query	string	false	"Article body"
@Param			state_type			query	string	false	"State types (new, open, pending, pending reminder, pending auto, closed, merged, removed)"
@Param			state_id			query	string	false	"State IDs"
@Param			queue_id			query	string	false	"Queue IDs"
@Param			priority_id			query	string	false	"Priority IDs"
@Param			type_id				query	string	false	"Ticket type IDs"
@Param			owner_id			query	string	false	"Owner agent IDs"
@Param			responsible_id		query	string	false	"Responsible agent IDs"
@Param			lock_id				query	string	false	"Lock IDs"
@Param			created_after		query	string	false	"Created at or after"
@Param			created_before		query	string	false	"Created before (date: up to the end of that day)"
@Param			changed_after		query	string	false	"Changed at or after"
@Param			changed_before		query	string	false	"Changed before (date: up to the end of that day)"
@Param			sort				query	string	false	"created, changed, priority, tn, title"	default(created)
@Param			order				query	string	false	"asc or desc"							default(desc)
@Param			page				query	int		false	"Page"									default(1)
@Param			limit				query	int		false	"Page size (max 100)"					default(25)
@Success		200	{object}	map[string]interface{}	"{success, data: {tickets, pagination: {page, limit, total}}}"
@Failure		400	{object}	map[string]interface{}	"Invalid parameter"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Failure		403	{object}	map[string]interface{}	"Customer, or agent without queue access"
@Security		BearerAuth
@Router			/search/tickets [get]

func HandleTicketStateStatisticsAPI

func HandleTicketStateStatisticsAPI(c *gin.Context)

HandleTicketStateStatisticsAPI handles GET /api/v1/ticket-states/statistics.

@Summary		Get state statistics
@Description	All-time ticket count per valid ticket state (RBAC filtered)
@Tags			States
@Accept			json
@Produce		json
@Success		200	{object}	map[string]interface{}	"statistics[], total_tickets"
@Failure		401	{object}	map[string]interface{}	"Unauthorized"
@Failure		403	{object}	map[string]interface{}	"Customer caller or no readable queue"
@Failure		500	{object}	map[string]interface{}	"Database error"
@Security		BearerAuth
@Router			/ticket-states/statistics [get]

func HandleTicketTrendsAPI

func HandleTicketTrendsAPI(c *gin.Context)

HandleTicketTrendsAPI handles GET /api/v1/statistics/trends.

@Summary		Get ticket trends
@Description	Tickets created, closed and still open per UTC day or month (RBAC filtered)
@Tags			Statistics
@Accept			json
@Produce		json
@Param			period	query		string	false	"Bucket size"	Enums(daily, monthly)	default(daily)
@Param			days	query		int		false	"Number of daily buckets (period=daily)"	minimum(1)	maximum(366)	default(7)
@Param			months	query		int		false	"Number of monthly buckets (period=monthly)"	minimum(1)	maximum(24)	default(3)
@Success		200		{object}	map[string]interface{}	"period, days|months, trends[] {date, created, closed, open}, summary"
@Failure		400		{object}	map[string]interface{}	"Invalid parameter"
@Failure		401		{object}	map[string]interface{}	"Unauthorized"
@Failure		403		{object}	map[string]interface{}	"Customer caller or no readable queue"
@Failure		500		{object}	map[string]interface{}	"Database error"
@Security		BearerAuth
@Router			/statistics/trends [get]

func HandleTimeBasedAnalyticsAPI

func HandleTimeBasedAnalyticsAPI(c *gin.Context)

HandleTimeBasedAnalyticsAPI handles GET /api/v1/statistics/analytics.

@Summary		Get time-based analytics
@Description	Tickets created and closed in the last days, by UTC hour of day or by weekday (RBAC filtered)
@Tags			Statistics
@Accept			json
@Produce		json
@Param			type	query		string	false	"Bucketing"	Enums(hourly, day_of_week)	default(hourly)
@Param			days	query		int		false	"Window in days ending now"	minimum(1)	maximum(366)	default(30)
@Success		200		{object}	map[string]interface{}	"type, days, data[], peak_hours[] (hourly) or busiest_days[] (day_of_week)"
@Failure		400		{object}	map[string]interface{}	"Invalid parameter"
@Failure		401		{object}	map[string]interface{}	"Unauthorized"
@Failure		403		{object}	map[string]interface{}	"Customer caller or no readable queue"
@Failure		500		{object}	map[string]interface{}	"Database error"
@Security		BearerAuth
@Router			/statistics/analytics [get]

func HandleUpdateArticleAPI

func HandleUpdateArticleAPI(c *gin.Context)

HandleUpdateArticleAPI handles PUT /api/v1/tickets/:ticket_id/articles/:id.

@Summary		Update article
@Description	Update the subject and/or body of an existing article
@Tags			Articles
@Accept			json
@Produce		json
@Param			ticket_id	path		int		true	"Ticket ID"
@Param			id			path		int		true	"Article ID"
@Param			article		body		object	true	"Article update data (subject, body)"
@Success		200			{object}	map[string]interface{}	"Updated article"
@Failure		400			{object}	map[string]interface{}	"Invalid request"
@Failure		401			{object}	map[string]interface{}	"Unauthorized"
@Failure		404			{object}	map[string]interface{}	"Article not found"
@Security		BearerAuth
@Router			/tickets/{ticket_id}/articles/{id} [put]

func HandleUpdateInternalNote

func HandleUpdateInternalNote(c *gin.Context)

HandleUpdateInternalNote updates the content of an existing internal note.

func HandleUpdateNotificationEvent

func HandleUpdateNotificationEvent(c *gin.Context)

HandleUpdateNotificationEvent updates an existing notification event.

func HandleUpdatePostmasterFilter

func HandleUpdatePostmasterFilter(c *gin.Context)

handleUpdatePostmasterFilter updates an existing postmaster filter.

func HandleUpdatePriorityAPI

func HandleUpdatePriorityAPI(c *gin.Context)

HandleUpdatePriorityAPI handles PUT /api/v1/priorities/:id.

@Summary		Update priority
@Description	Update an existing priority
@Tags			Priorities
@Accept			json
@Produce		json
@Param			id			path		int		true	"Priority ID"
@Param			priority	body		object	true	"Priority update data"
@Success		200			{object}	map[string]interface{}	"Updated priority"
@Failure		400			{object}	map[string]interface{}	"Invalid request"
@Failure		401			{object}	map[string]interface{}	"Unauthorized"
@Failure		404			{object}	map[string]interface{}	"Priority not found"
@Security		BearerAuth
@Router			/priorities/{id} [put]

func HandleUpdateProfile

func HandleUpdateProfile(c *gin.Context)

HandleUpdateProfile updates the current user's profile information.

func HandleUpdateQueueAPI

func HandleUpdateQueueAPI(c *gin.Context)

HandleUpdateQueueAPI handles PUT /api/v1/queues/:id.

@Summary		Update queue
@Description	Update an existing queue
@Tags			Queues
@Accept			json
@Produce		json
@Param			id		path		int		true	"Queue ID"
@Param			queue	body		object	true	"Queue update data"
@Success		200		{object}	map[string]interface{}	"Updated queue"
@Failure		400		{object}	map[string]interface{}	"Invalid request"
@Failure		401		{object}	map[string]interface{}	"Unauthorized"
@Failure		404		{object}	map[string]interface{}	"Queue not found"
@Security		BearerAuth
@Router			/queues/{id} [put]

func HandleUpdateSalutationAPI

func HandleUpdateSalutationAPI(c *gin.Context)

HandleUpdateSalutationAPI handles PUT /api/v1/salutations/:id.

@Summary		Update salutation
@Description	Update an email salutation
@Tags			Email Identity
@Accept			json
@Produce		json
@Param			id			path		int		true	"Salutation ID"
@Param			salutation	body		object	true	"Salutation update data"
@Success		200			{object}	map[string]interface{}	"Updated salutation"
@Failure		400			{object}	map[string]interface{}	"Invalid request"
@Failure		401			{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/salutations/{id} [put]

func HandleUpdateSavedSearchAPI added in v0.10.0

func HandleUpdateSavedSearchAPI(c *gin.Context)

HandleUpdateSavedSearchAPI replaces the parameters of a saved search.

@Summary		Update saved search
@Tags			Search
@Accept			json
@Produce		json
@Param			name	path	string	true	"Saved search name"
@Param			search	body	object	true	"{parameters: {<search parameter>: string | [string]}}"
@Success		200	{object}	map[string]interface{}	"{success, data: {name, type, parameters}}"
@Failure		400	{object}	map[string]interface{}	"Invalid parameters"
@Failure		404	{object}	map[string]interface{}	"Not found"
@Security		BearerAuth
@Router			/search/saved/{name} [put]

func HandleUpdateSignatureAPI

func HandleUpdateSignatureAPI(c *gin.Context)

HandleUpdateSignatureAPI handles PUT /api/v1/signatures/:id.

@Summary		Update signature
@Description	Update an email signature
@Tags			Email Identity
@Accept			json
@Produce		json
@Param			id			path		int		true	"Signature ID"
@Param			signature	body		object	true	"Signature update data"
@Success		200			{object}	map[string]interface{}	"Updated signature"
@Failure		400			{object}	map[string]interface{}	"Invalid request"
@Failure		401			{object}	map[string]interface{}	"Unauthorized"
@Security		BearerAuth
@Router			/signatures/{id} [put]

func HandleUpdateSystemAddressAPI

func HandleUpdateSystemAddressAPI(c *gin.Context)

HandleUpdateSystemAddressAPI handles PUT /api/v1/system-addresses/:id.

@Summary		Update system address
@Description	Update a system email address
@Tags			Email Identity
@Accept			json
@Produce		json
@Param			id		path		int		true	"Address ID"
@Param			address	body		object	true	"Address update data"
@Success		200		{object}	map[string]interface{}	"Updated address"
@Failure		400		{object}	map[string]interface{}	"Invalid request"
@Failure		401		{object}	map[string]interface{}	"Unauthorized"
@Failure		404		{object}	map[string]interface{}	"Address not found"
@Security		BearerAuth
@Router			/system-addresses/{id} [put]

func HandleUpdateTicketAPI

func HandleUpdateTicketAPI(c *gin.Context)

HandleUpdateTicketAPI handles PUT /api/v1/tickets/:id.

@Summary		Update ticket
@Description	Update an existing ticket's properties
@Tags			Tickets
@Accept			json
@Produce		json
@Param			id		path		int		true	"Ticket ID"
@Param			ticket	body		object	true	"Ticket update data (title, queue_id, priority_id, state_id, etc.)"
@Success		200		{object}	map[string]interface{}	"Updated ticket"
@Failure		400		{object}	map[string]interface{}	"Invalid request"
@Failure		401		{object}	map[string]interface{}	"Unauthorized"
@Failure		404		{object}	map[string]interface{}	"Ticket not found"
@Security		BearerAuth
@Router			/tickets/{id} [put]

func HasPluginAccess added in v0.8.3

func HasPluginAccess(c *gin.Context, pluginName string) bool

HasPluginAccess returns true iff the caller is entitled to use pluginName. Read-only equivalent of RequirePluginAccess, safe to call from template rendering.

func ImportSignatures

func ImportSignatures(data []byte, overwrite bool, userID int) (imported int, skipped int, err error)

ImportSignatures imports signatures from YAML data, attributing the writes to userID.

func ImportTemplates

func ImportTemplates(data []byte, overwrite bool, userID int) (imported int, skipped int, errors []string)

ImportTemplates imports templates from YAML data.

func InitAPITokenService

func InitAPITokenService(db *sql.DB)

InitAPITokenService initializes the API token service with a database connection

func InitializeServices

func InitializeServices()

InitializeServices (re)builds the singleton services for the current database handle. Without a reachable database the DB-backed services stay nil and the getters return nil, so handlers report the outage (HTTP 500) instead of serving fake data; the next call after the database returns rebuilds them.

func JoinTemplateTypes

func JoinTemplateTypes(types []string) string

JoinTemplateTypes joins template types into a sorted comma-separated string.

func ListHandlers

func ListHandlers() []string

ListHandlers returns sorted handler names (for diagnostics / tests).

func MissingCoreRoutes added in v0.10.0

func MissingCoreRoutes(r *gin.Engine) []string

MissingCoreRoutes returns the core routes that neither the main engine r nor the dynamic engine serves. YAML and plugin routes live on the dynamic engine (reached through r's NoRoute handler), not in r.Routes().

func MountDynamicEngine added in v0.7.0

func MountDynamicEngine(r *gin.Engine, routesDir string)

MountDynamicEngine installs a unified dynamic engine that serves both YAML routes and plugin routes. The engine is atomically swappable — rebuilt when YAML files change or plugins are loaded/reloaded. Static API routes registered directly on the main engine take priority (Gin matches those first, NoRoute catches the rest).

func ParseTemplateTypes

func ParseTemplateTypes(typeStr string) []string

ParseTemplateTypes splits a comma-separated type string into a slice.

func ProcessArticleDynamicFieldsFromForm

func ProcessArticleDynamicFieldsFromForm(formValues map[string][]string, articleID int, screenKey string) error

ProcessArticleDynamicFieldsFromForm processes dynamic fields with "ArticleDynamicField_" prefix.

func ProcessDynamicFieldsFromForm

func ProcessDynamicFieldsFromForm(formValues map[string][]string, objectID int, objectType, screenKey string) error

ProcessDynamicFieldsFromForm processes dynamic fields with "DynamicField_" prefix. screenKey is the screen being used (e.g., "AgentTicketPhone").

func RebuildDynamicEngine added in v0.7.0

func RebuildDynamicEngine()

RebuildDynamicEngine rebuilds the unified dynamic engine with current YAML + plugin routes. Safe to call from any goroutine.

func RefreshPluginMCPTools added in v0.8.2

func RefreshPluginMCPTools()

RefreshPluginMCPTools rebuilds the plugin tools in the MCP registry. Called from plugin enable/disable/upload handlers.

func RegisterAdminRoutes

func RegisterAdminRoutes(group *gin.RouterGroup, routes []AdminRouteDefinition)

RegisterAdminRoutes registers routes from definitions onto a router group.

func RegisterAgentHandlers

func RegisterAgentHandlers()

RegisterAgentHandlers registers agent handlers for YAML routing.

func RegisterAgentHandlersForRouting

func RegisterAgentHandlersForRouting()

RegisterAgentHandlersForRouting registers agent handlers for YAML routing.

func RegisterAgentRoutes

func RegisterAgentRoutes(r *gin.RouterGroup, db *sql.DB)

RegisterAgentRoutes registers all agent interface routes.

func RegisterCoreRoutes added in v0.10.0

func RegisterCoreRoutes(r *gin.Engine, sse http.Handler)

RegisterCoreRoutes registers the routes that live on the main engine instead of in routes/*.yaml: the i18n API, the plugin management API and the plugin SSE stream. cmd/goats and the route authorization test both use it, so the test covers exactly what production serves.

func RegisterCustomerRoutes

func RegisterCustomerRoutes(r *gin.RouterGroup, db *sql.DB)

RegisterCustomerRoutes registers all customer portal routes.

func RegisterDynamicFieldRoutes

func RegisterDynamicFieldRoutes(router *gin.RouterGroup, adminRouter *gin.RouterGroup)

RegisterDynamicFieldRoutes registers all dynamic field admin routes.

func RegisterDynamicModuleHandlersIntoRegistry

func RegisterDynamicModuleHandlersIntoRegistry(reg *routing.HandlerRegistry)

RegisterDynamicModuleHandlersIntoRegistry exposes the dynamic module handlers to the routing registry used by the main server.

func RegisterHandler

func RegisterHandler(name string, h gin.HandlerFunc)

RegisterHandler adds/overwrites a handler under a given name. Registers to both the local handlerRegistry AND routing.GlobalHandlerMap so that YAML route loader can find all handlers regardless of where they're registered.

func RegisterPluginAPIRoutes

func RegisterPluginAPIRoutes(r *gin.RouterGroup)

RegisterPluginAPIRoutes registers the plugin management API endpoints. Listing, health, widgets and per-plugin SSE channels are for agents only: their callers are the agent dashboard and admin pages, and customers' customer_user ids overlap users ids. Direct function calls skip the plugin's own route middleware, so they are admin-only alongside the management endpoints.

func RegisterWithRouting

func RegisterWithRouting(registry interface{}) error

The routing registration should be handled at the application level.

func RenderMarkdown

func RenderMarkdown(content string) string

RenderMarkdown converts markdown content to sanitized HTML with Tailwind styling. Markdown→HTML defers to the shared pkg/markdown renderer (goldmark GFM + bluemonday), so ticket notes and plugin UI render identically and any raw/unsafe HTML is stripped; addTailwindClasses layers Tailwind styling on top of the sanitized output.

func RequireAdmin

func RequireAdmin() gin.HandlerFunc

RequireAdmin middleware checks if the user is an admin. Supports both session-based auth (user_role) and JWT auth (isInAdminGroup).

func RequireGroup added in v0.7.0

func RequireGroup(groupName string) gin.HandlerFunc

RequireGroup checks that the authenticated agent belongs to a specific group. Admin users (role=Admin or isInAdminGroup) bypass group checks. Customers are refused: groups hold agents (group_user.user_id = users.id), and a customer's customer_user.id would otherwise match an unrelated agent's memberships. Used by plugins via "group:<name>" middleware, e.g. "group:myplugin-users".

func RequirePluginAccess added in v0.8.3

func RequirePluginAccess(pluginName, agentGroup string) gin.HandlerFunc

RequirePluginAccess gates a plugin route. Admins bypass; customers pass when the active org has a gk_org_plugin_access binding for pluginName and the customer is in one of those groups; agents pass when they're a member of agentGroup.

func RootRedirectTarget

func RootRedirectTarget() string

func SessionOrJWTAuth added in v0.7.0

func SessionOrJWTAuth() gin.HandlerFunc

SessionOrJWTAuth middleware accepts either session-based auth (cookie) or JWT token auth. Session auth is checked first (user_id already set by session middleware), then falls back to JWT.

func SetAPITokenService

func SetAPITokenService(svc *service.APITokenService)

SetAPITokenService sets the global API token service

func SetAttachmentTemplates

func SetAttachmentTemplates(attachmentID int, templateIDs []int, userID int) error

SetAttachmentTemplates replaces all template assignments for an attachment.

func SetDynamicFieldValue

func SetDynamicFieldValue(value *DynamicFieldValue) error

SetDynamicFieldValue sets a value for a dynamic field on an object.

func SetPluginDir

func SetPluginDir(dir string)

SetPluginDir sets the plugin directory for uploads.

func SetPluginManager

func SetPluginManager(mgr *plugin.Manager)

SetPluginManager sets the global plugin manager and wires up the lazy-load callback so MCP tools and dynamic routes are refreshed when a plugin is loaded on demand.

func SetPluginReloader added in v0.7.0

func SetPluginReloader(fn func(ctx context.Context, name string) error)

SetPluginReloader sets the callback used to load/reload a plugin after upload.

func SetPluginSSEBroker added in v0.8.1

func SetPluginSSEBroker(b *plugin.SSEBroker)

SetPluginSSEBroker sets the global SSE broker for plugin channel endpoints.

func SetPluginUnloader added in v0.8.2

func SetPluginUnloader(fn func(ctx context.Context, name string) error)

SetPluginUnloader sets the callback used to stop a running plugin before binary replacement.

func SetQueueTemplates

func SetQueueTemplates(queueID int, templateIDs []int, userID int) error

SetQueueTemplates replaces all template assignments for a queue.

func SetScreenConfig

func SetScreenConfig(fieldID int, screenKey string, configValue int, userID int) error

SetScreenConfig sets a single field-screen config.

func SetTemplateAttachments

func SetTemplateAttachments(templateID int, attachmentIDs []int, userID int) error

SetTemplateAttachments sets the attachment assignments for a template.

func SetTemplateQueues

func SetTemplateQueues(templateID int, queueIDs []int, userID int) error

SetTemplateQueues sets the queue assignments for a template.

func SetValkeyCache

func SetValkeyCache(c *cache.RedisCache)

SetValkeyCache allows main to provide the shared Redis/Valkey cache.

func SetupDynamicModules

func SetupDynamicModules(db *sql.DB) error

SetupDynamicModules initializes the dynamic module system. Route registration is handled via YAML; this ensures endpoints exist even before the handler is ready.

func SetupHTMXRoutes

func SetupHTMXRoutes(r *gin.Engine)

SetupHTMXRoutes sets up all HTMX routes on the given router.

func SetupTestRouterWithRoutes

func SetupTestRouterWithRoutes(routes []AdminRouteDefinition) *gin.Engine

SetupTestRouterWithRoutes creates a test router using canonical route definitions. Tests MUST use this instead of manually registering routes.

func SubstituteSignatureVariables

func SubstituteSignatureVariables(text string, vars map[string]string) string

Reuses the same variable substitution logic as templates.

func SubstituteTemplateVariables

func SubstituteTemplateVariables(text string, vars map[string]string) string

SubstituteTemplateVariables replaces template variables with actual values. Like OTRS, any unmatched variables are replaced with "-" rather than left as raw tags. Handles both raw tags (<OTRS_*>) and HTML-encoded tags (&lt;OTRS_*&gt;).

func SupportsAutoConfig

func SupportsAutoConfig(fieldType string) bool

SupportsAutoConfig returns true if the field type can use automatic configuration with sensible defaults, without requiring manual configuration input. Dropdown and Multiselect require PossibleValues and cannot use auto-config.

func UpdateDynamicField

func UpdateDynamicField(field *DynamicField, userID int) error

UpdateDynamicField updates an existing dynamic field.

func UpdateSignature

func UpdateSignature(id int, name, text, contentType, comments string, validID, userID int) error

UpdateSignature updates an existing signature.

func UpdateStandardTemplate

func UpdateStandardTemplate(t *StandardTemplate, userID int) error

UpdateStandardTemplate updates an existing template.

func ValidFieldTypes

func ValidFieldTypes() []string

ValidFieldTypes returns all supported field types.

func ValidObjectTypes

func ValidObjectTypes() []string

ValidObjectTypes returns all supported object types.

func ValidateAllTemplates

func ValidateAllTemplates(templatesDir string) ([]string, error)

ValidateAllTemplates walks templatesDir, parses every .pongo2 file, and returns a list of failures.

func ValidateContractHeaders

func ValidateContractHeaders(contract *EndpointContract, headers map[string]string) []string

ValidateContractHeaders checks that a request includes all required headers per the endpoint contract. Returns a list of missing headers.

func ValidateTemplatesReferencedInRoutes

func ValidateTemplatesReferencedInRoutes(routesDir, templatesDir string) ([]string, error)

ValidateTemplatesReferencedInRoutes loads YAML route groups from routesDir and attempts to parse each route's Template against templatesDir. Returns a list of failures (missing/unparsable).

func ValidateUploadedFile

func ValidateUploadedFile(header *multipart.FileHeader) error

ValidateUploadedFile is a small exported wrapper around validateFile to enable focused unit tests from an external test package without importing all api tests.

func WebhookAuth added in v0.8.1

func WebhookAuth(pluginName string) gin.HandlerFunc

WebhookAuth provides authentication for webhook endpoints. No session/JWT required — instead, verifies HMAC-SHA256 signature from the request header. The signing secret is stored in the plugin's secure config under the key "<plugin>_webhook_secret".

If no webhook secret is configured, requests are REJECTED by default. Set GOATFLOW_WEBHOOK_ALLOW_UNSIGNED=true to allow unsigned webhooks during development (NOT recommended for production).

Signature verification supports:

  • Standard HMAC: X-Signature-256, X-Hub-Signature-256, X-Webhook-Signature (format: "sha256=<hex>" or plain "<hex>")
  • Stripe: Stripe-Signature header (format: "t=<timestamp>,v1=<signature>") Stripe signatures are computed as HMAC-SHA256(secret, "<timestamp>.<body>")

func WebhookRateLimit added in v0.8.1

func WebhookRateLimit(pluginName string) gin.HandlerFunc

WebhookRateLimit applies IP-based rate limiting scoped to the plugin name. This runs BEFORE signature verification to reject floods cheaply.

Types

type ACL

type ACL struct {
	ID             int              `json:"id"`
	Name           string           `json:"name"`
	Comments       *string          `json:"comments,omitempty"`
	Description    *string          `json:"description,omitempty"`
	ValidID        int              `json:"valid_id"`
	StopAfterMatch *int             `json:"stop_after_match,omitempty"`
	ConfigMatch    *ACLConfigMatch  `json:"config_match,omitempty"`
	ConfigChange   *ACLConfigChange `json:"config_change,omitempty"`
	CreateTime     time.Time        `json:"create_time"`
	CreateBy       int              `json:"create_by"`
	ChangeTime     time.Time        `json:"change_time"`
	ChangeBy       int              `json:"change_by"`
}

ACL represents an Access Control List entry.

type ACLConfigChange

type ACLConfigChange struct {
	Possible    map[string]interface{} `json:"Possible,omitempty"`
	PossibleNot map[string]interface{} `json:"PossibleNot,omitempty"`
	PossibleAdd map[string]interface{} `json:"PossibleAdd,omitempty"`
}

ACLConfigChange defines what changes the ACL makes.

type ACLConfigMatch

type ACLConfigMatch struct {
	Properties map[string]interface{} `json:"Properties,omitempty"`
}

ACLConfigMatch defines the conditions for when an ACL applies.

type AdditionalDFStorageConfig

type AdditionalDFStorageConfig struct {
	Type         string `yaml:"Type"`         // "Backend" (store in DB) or "Frontend" (display only)
	DynamicField string `yaml:"DynamicField"` // Target field name (without DynamicField_ prefix)
	Key          string `yaml:"Key"`          // Source key from webservice response
}

AdditionalDFStorageConfig defines auto-fill behavior for webservice fields. When a webservice returns data, additional fields can be populated from the response.

type AdminRouteDefinition

type AdminRouteDefinition struct {
	Method  string
	Path    string
	Handler gin.HandlerFunc
}

AdminRouteDefinition defines a single admin route for both production and testing.

func GetAdminDynamicFieldsRoutes

func GetAdminDynamicFieldsRoutes() []AdminRouteDefinition

GetAdminDynamicFieldsRoutes returns the canonical route definitions for dynamic fields admin. This MUST be used by both htmx_routes.go and test files to prevent route divergence.

func GetAdminRolesRoutes

func GetAdminRolesRoutes() []AdminRouteDefinition

GetAdminRolesRoutes returns the canonical route definitions for admin roles. This MUST be used by both htmx_routes.go and test files to prevent route divergence.

type AgentHandlerRegistry

type AgentHandlerRegistry struct {
	NewTicket    gin.HandlerFunc
	CreateTicket gin.HandlerFunc
}

AgentHandlerRegistry holds references to agent handlers for external registration.

type ArticleInsertParams

type ArticleInsertParams struct {
	TicketID             int64
	CommunicationChannel int // constants.CommunicationChannel*
	IsVisibleForCustomer int
	CreateBy             int64
}

ArticleInsertParams holds parameters for creating an article.

type ArticleMimeParams

type ArticleMimeParams struct {
	ArticleID    int64
	From         string
	To           string // optional, empty for notes
	Subject      string
	Body         string
	ContentType  string
	IncomingTime int64
	CreateBy     int64
}

ArticleMimeParams holds parameters for article MIME data.

type AttachmentBasicInfo

type AttachmentBasicInfo struct {
	ID       int
	Name     string
	Filename string
}

AttachmentBasicInfo represents basic attachment information for the admin UI.

func GetAttachmentBasicInfo

func GetAttachmentBasicInfo(attachmentID int) (*AttachmentBasicInfo, error)

GetAttachmentBasicInfo returns basic attachment information by ID.

type AttachmentInfo

type AttachmentInfo struct {
	ID          int    `json:"id"`
	Name        string `json:"name"`
	Filename    string `json:"filename"`
	ContentType string `json:"content_type"`
	ContentSize int64  `json:"content_size"`
}

AttachmentInfo represents minimal attachment info for agent API.

func GetAttachmentsByIDs

func GetAttachmentsByIDs(ids []int) []AttachmentInfo

GetAttachmentsByIDs returns attachment details for given IDs.

type AttachmentWithTemplateCount

type AttachmentWithTemplateCount struct {
	ID            int
	Name          string
	Filename      string
	TemplateCount int
}

AttachmentWithTemplateCount represents an attachment with its template assignment count.

func GetAttachmentsWithTemplateCounts

func GetAttachmentsWithTemplateCounts() ([]AttachmentWithTemplateCount, error)

GetAttachmentsWithTemplateCounts returns all valid attachments with their template assignment counts.

type BulkActionRequest

type BulkActionRequest struct {
	TicketIDs []int `json:"ticket_ids" form:"ticket_ids"`
}

BulkActionRequest represents a request for bulk ticket operations

type BulkActionResult

type BulkActionResult struct {
	Success   bool     `json:"success"`
	Total     int      `json:"total"`
	Succeeded int      `json:"succeeded"`
	Failed    int      `json:"failed"`
	Errors    []string `json:"errors,omitempty"`
}

BulkActionResult represents the result of a bulk operation

type BulkAssignRequest

type BulkAssignRequest struct {
	BulkActionRequest
	UserID int `json:"user_id" form:"user_id"`
}

BulkAssignRequest represents a bulk assignment request

type BulkLockRequest

type BulkLockRequest struct {
	BulkActionRequest
	Lock bool `json:"lock" form:"lock"`
}

BulkLockRequest represents a bulk lock/unlock request

type BulkMergeRequest

type BulkMergeRequest struct {
	BulkActionRequest
	TargetTicketID int `json:"target_ticket_id" form:"target_ticket_id"`
}

BulkMergeRequest represents a bulk merge request

type BulkPriorityRequest

type BulkPriorityRequest struct {
	BulkActionRequest
	PriorityID int `json:"priority_id" form:"priority_id"`
}

BulkPriorityRequest represents a bulk priority change request

type BulkQueueRequest

type BulkQueueRequest struct {
	BulkActionRequest
	QueueID int `json:"queue_id" form:"queue_id"`
}

BulkQueueRequest represents a bulk queue move request

type BulkStatusRequest

type BulkStatusRequest struct {
	BulkActionRequest
	StatusID     int   `json:"status_id" form:"status_id"`
	PendingUntil int64 `json:"pending_until" form:"pending_until"`
}

BulkStatusRequest represents a bulk status change request

type CannedResponse

type CannedResponse struct {
	ID           int        `json:"id"`
	Name         string     `json:"name"`
	Category     string     `json:"category"`
	Content      string     `json:"content"`
	ContentType  string     `json:"content_type"`
	Tags         []string   `json:"tags"`
	Scope        string     `json:"scope"`
	OwnerID      int        `json:"owner_id"`
	TeamID       int        `json:"team_id,omitempty"`
	Placeholders []string   `json:"placeholders"`
	UsageCount   int        `json:"usage_count"`
	LastUsed     *time.Time `json:"last_used,omitempty"`
	CreatedAt    time.Time  `json:"created_at"`
	UpdatedAt    time.Time  `json:"updated_at"`
}

CannedResponse represents a pre-written response.

type CannedResponseDB

type CannedResponseDB struct {
	ID           int
	Name         string
	Category     sql.NullString
	Content      string
	ContentType  string
	Tags         sql.NullString
	Scope        string
	OwnerID      int
	TeamID       sql.NullInt64
	Placeholders sql.NullString
	UsageCount   int
	LastUsed     sql.NullTime
	ValidID      int
	CreateTime   time.Time
	CreateBy     int
	ChangeTime   time.Time
	ChangeBy     int
}

CannedResponseDB represents a canned response in the database.

func (*CannedResponseDB) ToCannedResponse

func (r *CannedResponseDB) ToCannedResponse() *CannedResponse

ToCannedResponse converts database model to API model.

type CannedResponseFilters

type CannedResponseFilters struct {
	Category  string
	Scope     string
	Search    string
	Tags      []string
	SortBy    string
	SortOrder string
}

CannedResponseFilters holds filter options for listing responses.

type CannedResponseRepository

type CannedResponseRepository struct {
	// contains filtered or unexported fields
}

CannedResponseRepository handles database operations for canned responses.

func NewCannedResponseRepository

func NewCannedResponseRepository() (*CannedResponseRepository, error)

NewCannedResponseRepository creates a new repository.

func (*CannedResponseRepository) CheckDuplicate

func (r *CannedResponseRepository) CheckDuplicate(name, scope string, ownerID, teamID int) (bool, error)

CheckDuplicate checks if a response with the same name exists in the same scope.

func (*CannedResponseRepository) Create

func (r *CannedResponseRepository) Create(cr *CannedResponse, userID int) (int, error)

Create inserts a new canned response.

func (*CannedResponseRepository) Delete

func (r *CannedResponseRepository) Delete(id, userID int) error

Delete soft-deletes a canned response by setting valid_id = 2.

func (*CannedResponseRepository) GetByID

func (r *CannedResponseRepository) GetByID(id int) (*CannedResponse, error)

GetByID retrieves a valid (not deleted) canned response by ID.

func (*CannedResponseRepository) IncrementUsage

func (r *CannedResponseRepository) IncrementUsage(id int) error

IncrementUsage increments usage count and updates last_used.

func (*CannedResponseRepository) ListAccessible

func (r *CannedResponseRepository) ListAccessible(userID, teamID int, filters CannedResponseFilters) ([]*CannedResponse, error)

ListAccessible returns canned responses accessible to a user.

func (*CannedResponseRepository) ListCategories

func (r *CannedResponseRepository) ListCategories() ([]string, error)

ListCategories returns all canned response categories.

func (*CannedResponseRepository) Update

func (r *CannedResponseRepository) Update(id int, cr *CannedResponse, userID int) error

Update updates a canned response.

type CustomerCompanyInfo

type CustomerCompanyInfo struct {
	CustomerID string
	Name       string
	ValidID    int
	ValidName  string
}

CustomerCompanyInfo holds customer company data for display

type CustomerGroupAssignment

type CustomerGroupAssignment struct {
	Customer    CustomerCompanyInfo
	Permissions map[int]map[string]bool // group_id -> permission_key -> enabled
}

CustomerGroupAssignment holds the permission matrix for a customer

type CustomerGroupPermission

type CustomerGroupPermission struct {
	CustomerID        string
	GroupID           int
	PermissionKey     string
	PermissionValue   int
	PermissionContext string
}

CustomerGroupPermission represents a permission assignment

type CustomerUserInfo

type CustomerUserInfo struct {
	Login     string
	FirstName string
	LastName  string
	Email     string
	ValidID   int
	ValidName string
}

CustomerUserInfo holds customer user data for display

type CustomerUserWithPermissions

type CustomerUserWithPermissions struct {
	Login       string
	FirstName   string
	LastName    string
	Email       string
	ValidID     int
	ValidName   string
	Permissions map[string]bool // permission_key -> enabled
}

CustomerUserWithPermissions holds customer user data with permission flags for templates

type CustomerWithPermissions

type CustomerWithPermissions struct {
	CustomerID  string
	Name        string
	ValidID     int
	ValidName   string
	Permissions map[string]bool // permission_key -> enabled
}

CustomerWithPermissions holds customer data with permission flags for templates

type DynamicField

type DynamicField struct {
	ID            int                 `json:"id" db:"id"`
	InternalField int                 `json:"internal_field" db:"internal_field"`
	Name          string              `json:"name" db:"name"`
	Label         string              `json:"label" db:"label"`
	FieldOrder    int                 `json:"field_order" db:"field_order"`
	FieldType     string              `json:"field_type" db:"field_type"`
	ObjectType    string              `json:"object_type" db:"object_type"`
	Config        *DynamicFieldConfig `json:"config"`
	ConfigRaw     []byte              `json:"-" db:"config"` // YAML blob from DB
	ValidID       int                 `json:"valid_id" db:"valid_id"`
	CreateTime    time.Time           `json:"create_time" db:"create_time"`
	CreateBy      int                 `json:"create_by" db:"create_by"`
	ChangeTime    time.Time           `json:"change_time" db:"change_time"`
	ChangeBy      int                 `json:"change_by" db:"change_by"`
}

DynamicField represents a field definition from dynamic_field table.

func GetDynamicField

func GetDynamicField(id int) (*DynamicField, error)

GetDynamicField retrieves a single dynamic field by ID.

func GetDynamicFieldByName

func GetDynamicFieldByName(name string) (*DynamicField, error)

GetDynamicFieldByName retrieves a single dynamic field by name.

func GetDynamicFields

func GetDynamicFields(objectType, fieldType string) ([]DynamicField, error)

GetDynamicFields retrieves all dynamic fields.

func GetDynamicFieldsForScreen

func GetDynamicFieldsForScreen(objectType, screenKey string) ([]DynamicField, error)

GetDynamicFieldsForScreen retrieves active fields for a specific screen.

func (*DynamicField) GetValueColumn

func (df *DynamicField) GetValueColumn() string

GetValueColumn returns the database column name used for storing values of this field type.

func (*DynamicField) IsActive

func (df *DynamicField) IsActive() bool

IsActive returns true if the field is valid (active).

func (*DynamicField) IsInternal

func (df *DynamicField) IsInternal() bool

IsInternal returns true if this is an internal/system field.

func (*DynamicField) ParseConfig

func (df *DynamicField) ParseConfig() error

ParseConfig deserializes YAML config blob into DynamicFieldConfig.

func (*DynamicField) SerializeConfig

func (df *DynamicField) SerializeConfig() error

SerializeConfig serializes DynamicFieldConfig to YAML for storage.

func (*DynamicField) Validate

func (df *DynamicField) Validate() error

Validate checks the dynamic field for errors.

type DynamicFieldConfig

type DynamicFieldConfig struct {
	// Common to all types
	DefaultValue string `yaml:"DefaultValue,omitempty"`

	// Text/TextArea fields
	MaxLength   int     `yaml:"MaxLength,omitempty"`
	RegExList   []RegEx `yaml:"RegExList,omitempty"` // OTRS uses RegExList
	Link        string  `yaml:"Link,omitempty"`
	LinkPreview string  `yaml:"LinkPreview,omitempty"`
	Rows        int     `yaml:"Rows,omitempty"` // TextArea only
	Cols        int     `yaml:"Cols,omitempty"` // TextArea only

	// Dropdown/Multiselect
	PossibleValues     map[string]string `yaml:"PossibleValues,omitempty"`
	PossibleNone       int               `yaml:"PossibleNone,omitempty"` // OTRS uses 0/1 not bool
	TranslatableValues int               `yaml:"TranslatableValues,omitempty"`
	TreeView           int               `yaml:"TreeView,omitempty"`

	// Date/DateTime
	YearsInPast     int    `yaml:"YearsInPast,omitempty"`
	YearsInFuture   int    `yaml:"YearsInFuture,omitempty"`
	DateRestriction string `yaml:"DateRestriction,omitempty"` // none, DisablePastDates, DisableFutureDates
	YearsPeriod     int    `yaml:"YearsPeriod,omitempty"`

	// WebserviceDropdown/WebserviceMultiselect - OTRS compatible
	Webservice               string                      `yaml:"Webservice,omitempty"`               // Webservice name
	InvokerSearch            string                      `yaml:"InvokerSearch,omitempty"`            // Invoker for search/autocomplete
	InvokerGet               string                      `yaml:"InvokerGet,omitempty"`               // Invoker for get-by-id
	Backend                  string                      `yaml:"Backend,omitempty"`                  // Backend type (DirectRequest, etc.)
	StoredValue              string                      `yaml:"StoredValue,omitempty"`              // Field key to store (e.g., "ID")
	DisplayedValues          string                      `yaml:"DisplayedValues,omitempty"`          // Fields to display (comma-sep, e.g., "Name,Code")
	DisplayedValuesSeparator string                      `yaml:"DisplayedValuesSeparator,omitempty"` // Separator for display (e.g., " - ")
	SearchKeys               string                      `yaml:"SearchKeys,omitempty"`               // Fields to search (comma-sep)
	AutocompleteMinLength    int                         `yaml:"AutocompleteMinLength,omitempty"`    // Min chars before autocomplete (default: 3)
	QueryDelay               int                         `yaml:"QueryDelay,omitempty"`               // Delay in ms before query (default: 500)
	CacheTTL                 int                         `yaml:"CacheTTL,omitempty"`                 // Cache TTL in seconds (default: 60)
	Limit                    int                         `yaml:"Limit,omitempty"`                    // Result limit (default: 20)
	AdditionalDFStorage      []AdditionalDFStorageConfig `yaml:"AdditionalDFStorage,omitempty"`      // Auto-fill other fields from response
}

DynamicFieldConfig stores type-specific configuration (YAML-serialized for OTRS compatibility).

func DefaultDynamicFieldConfig

func DefaultDynamicFieldConfig(fieldType string) *DynamicFieldConfig

DefaultDynamicFieldConfig returns sensible default configuration for each field type. Used when auto-config mode is enabled to skip manual configuration.

type DynamicFieldDisplay

type DynamicFieldDisplay struct {
	Field        DynamicField
	Value        interface{} // The raw value (string, int, time, etc.)
	DisplayValue string      // Human-readable display value
}

DynamicFieldDisplay represents a dynamic field with its value for display purposes.

func GetDynamicFieldValuesForDisplay

func GetDynamicFieldValuesForDisplay(objectID int, objectType string, screenKey string) ([]DynamicFieldDisplay, error)

screenKey is the screen (e.g., "AgentTicketZoom") - if empty, returns all enabled fields.

type DynamicFieldExport

type DynamicFieldExport struct {
	DynamicFields       map[string]DynamicFieldExportItem `yaml:"DynamicFields"`
	DynamicFieldScreens map[string]map[string]int         `yaml:"DynamicFieldScreens,omitempty"`
}

DynamicFieldExport represents the full export structure (OTRS-compatible format).

func ExportDynamicFields

func ExportDynamicFields(fieldNames []string, includeScreens bool) (*DynamicFieldExport, error)

ExportDynamicFields exports the specified dynamic fields to an OTRS-compatible format.

func ParseDynamicFieldsYAML

func ParseDynamicFieldsYAML(data []byte) (*DynamicFieldExport, error)

ParseDynamicFieldsYAML parses a YAML file into a DynamicFieldExport structure.

type DynamicFieldExportItem

type DynamicFieldExportItem struct {
	Name       string              `yaml:"Name"`
	Label      string              `yaml:"Label"`
	FieldType  string              `yaml:"FieldType"`
	ObjectType string              `yaml:"ObjectType"`
	FieldOrder int                 `yaml:"FieldOrder"`
	ValidID    int                 `yaml:"ValidID"`
	Config     *DynamicFieldConfig `yaml:"Config,omitempty"`
}

DynamicFieldExportItem represents a single field for export.

type DynamicFieldFilter

type DynamicFieldFilter struct {
	FieldID   int    // ID of the dynamic field
	FieldName string // Name of the dynamic field (alternative to ID)
	Operator  string // eq, ne, contains, gt, lt, gte, lte, in, notin, empty, notempty
	Value     string // Value to compare against (comma-separated for 'in' operator)
}

DynamicFieldFilter represents a filter condition for dynamic field values.

func ParseDynamicFieldFiltersFromQuery

func ParseDynamicFieldFiltersFromQuery(queryParams map[string][]string) []DynamicFieldFilter

Expected format: df_FieldName=value or df_FieldName_op=value (e.g., df_CustomerType=VIP, df_Amount_gt=1000).

type DynamicFieldScreenConfig

type DynamicFieldScreenConfig struct {
	ID          int       `json:"id" db:"id"`
	FieldID     int       `json:"field_id" db:"field_id"`
	ScreenKey   string    `json:"screen_key" db:"screen_key"`
	ConfigValue int       `json:"config_value" db:"config_value"` // 0=disabled, 1=enabled, 2=required
	CreateTime  time.Time `json:"create_time" db:"create_time"`
	CreateBy    int       `json:"create_by" db:"create_by"`
	ChangeTime  time.Time `json:"change_time" db:"change_time"`
	ChangeBy    int       `json:"change_by" db:"change_by"`
}

DynamicFieldScreenConfig maps fields to screens.

func GetScreenConfigForField

func GetScreenConfigForField(fieldID int) ([]DynamicFieldScreenConfig, error)

GetScreenConfigForField gets all screen configs for a single field.

func GetScreenConfigForScreen

func GetScreenConfigForScreen(screenKey string) ([]DynamicFieldScreenConfig, error)

GetScreenConfigForScreen gets all field configs for a specific screen.

type DynamicFieldValue

type DynamicFieldValue struct {
	ID        int        `json:"id" db:"id"`
	FieldID   int        `json:"field_id" db:"field_id"`
	ObjectID  int64      `json:"object_id" db:"object_id"`
	ValueText *string    `json:"value_text,omitempty" db:"value_text"`
	ValueDate *time.Time `json:"value_date,omitempty" db:"value_date"`
	ValueInt  *int64     `json:"value_int,omitempty" db:"value_int"`
}

DynamicFieldValue represents a stored value from dynamic_field_value table.

func GetDynamicFieldValues

func GetDynamicFieldValues(objectID int64) ([]DynamicFieldValue, error)

GetDynamicFieldValues retrieves all values for an object.

type EndpointContract

type EndpointContract struct {
	Method          string            // HTTP method
	Path            string            // Route path pattern
	RequiredHeaders map[string]string // Headers the client MUST send for JSON response
	ContentType     string            // Expected Content-Type for request body (if any)
	ResponseType    string            // Expected response type: "json" or "html"
	JSFunction      string            // Name of JavaScript function that calls this endpoint
}

EndpointContract documents the expected request/response contract for an endpoint. This MUST be kept in sync with both the handler logic AND the JavaScript client code. When writing tests, use these contracts to ensure test requests mirror real client requests.

func ContractForPath

func ContractForPath(contracts []EndpointContract, method, path string) *EndpointContract

ContractForPath returns the endpoint contract for a given method and path. Use this in tests to ensure requests include the required headers.

func GetAdminRolesContracts

func GetAdminRolesContracts() []EndpointContract

GetAdminRolesContracts returns the endpoint contracts for admin roles. CRITICAL: When handlers check headers to decide JSON vs HTML response, document the required headers here AND ensure JS client sends them.

type FieldWithScreenConfig

type FieldWithScreenConfig struct {
	Field       DynamicField
	ConfigValue int // 0=disabled, 1=enabled, 2=required
}

FieldWithScreenConfig pairs a field with its screen configuration value.

func GetFieldsForScreenWithConfig

func GetFieldsForScreenWithConfig(screenKey, objectType string) ([]FieldWithScreenConfig, error)

GetFieldsForScreenWithConfig gets fields enabled for a specific screen.

type GenericAgentJob

type GenericAgentJob struct {
	Name   string            `json:"name"`
	Valid  bool              `json:"valid"`
	Config map[string]string `json:"config"`
}

GenericAgentJob represents a generic agent job configuration. Generic agent jobs are stored as key-value pairs in the database.

type GroupCustomerAssignment

type GroupCustomerAssignment struct {
	Group       GroupInfo
	Permissions map[string]map[string]bool // customer_id -> permission_key -> enabled
}

GroupCustomerAssignment holds the permission matrix for a group

type GroupInfo

type GroupInfo struct {
	ID        int
	Name      string
	ValidID   int
	ValidName string
}

GroupInfo holds group data for display

type GroupWithCustomerUserPermissions

type GroupWithCustomerUserPermissions struct {
	ID          int
	Name        string
	ValidID     int
	ValidName   string
	Permissions map[string]bool // permission_key -> enabled
}

GroupWithCustomerUserPermissions holds group data with permission flags for customer user templates

type GroupWithPermissions

type GroupWithPermissions struct {
	ID          int
	Name        string
	ValidID     int
	ValidName   string
	Permissions map[string]bool // permission_key -> enabled
}

GroupWithPermissions holds group data with permission flags for templates

type ImportPreviewItem

type ImportPreviewItem struct {
	Name          string `json:"name"`
	Label         string `json:"label"`
	FieldType     string `json:"field_type"`
	ObjectType    string `json:"object_type"`
	Exists        bool   `json:"exists"`
	HasScreens    bool   `json:"has_screens"`
	ScreenCount   int    `json:"screen_count"`
	WillCreate    bool   `json:"will_create"`
	WillOverwrite bool   `json:"will_overwrite"`
}

ImportPreviewItem represents a field in the import preview.

func GetImportPreview

func GetImportPreview(export *DynamicFieldExport) ([]ImportPreviewItem, error)

GetImportPreview returns a preview of what will happen when importing.

type ImportResult

type ImportResult struct {
	Created   []string `json:"created"`
	Updated   []string `json:"updated"`
	Skipped   []string `json:"skipped"`
	Errors    []string `json:"errors"`
	ScreensOK int      `json:"screens_ok"`
}

ImportResult contains the results of an import operation.

func ImportDynamicFields

func ImportDynamicFields(
	export *DynamicFieldExport,
	selectedFields []string,
	selectedScreens []string,
	overwrite bool,
	userID int,
) (*ImportResult, error)

ImportDynamicFields imports dynamic fields from an export structure.

type InternalNote

type InternalNote struct {
	ID               int       `json:"id"`
	TicketID         int       `json:"ticket_id"`
	Content          string    `json:"content"`
	FormattedContent string    `json:"formatted_content"`
	AuthorID         int       `json:"author_id"`
	AuthorName       string    `json:"author_name"`
	Visibility       string    `json:"visibility"`       // always "internal"
	CustomerVisible  bool      `json:"customer_visible"` // always false
	Mentions         []string  `json:"mentions"`
	HasMentions      bool      `json:"has_mentions"`
	HasTeamMention   bool      `json:"has_team_mention"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
}

InternalNote is an internal (agent-only) note on a ticket. It is stored as an OTRS article on the Internal communication channel that is not visible to the customer.

type LookupItem

type LookupItem struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

LookupItem represents a simple ID/Name lookup option for templates.

type NotificationEvent

type NotificationEvent struct {
	ID         int       `json:"id"`
	Name       string    `json:"name"`
	ValidID    int       `json:"valid_id"`
	Comments   string    `json:"comments"`
	CreateTime time.Time `json:"create_time"`
	CreateBy   int       `json:"create_by"`
	ChangeTime time.Time `json:"change_time"`
	ChangeBy   int       `json:"change_by"`
}

NotificationEvent represents a notification event from the database.

type NotificationEventFull

type NotificationEventFull struct {
	NotificationEvent
	Events     []string                            `json:"events"`
	Filters    map[string][]string                 `json:"filters"`
	Recipients map[string][]string                 `json:"recipients"`
	Messages   map[string]NotificationEventMessage `json:"messages"`
}

NotificationEventFull represents a complete notification with all related data.

type NotificationEventInput

type NotificationEventInput struct {
	Name       string                              `json:"name" binding:"required"`
	ValidID    int                                 `json:"valid_id"`
	Comments   string                              `json:"comments"`
	Events     []string                            `json:"events"`
	Filters    map[string][]string                 `json:"filters"`
	Recipients map[string][]string                 `json:"recipients"`
	Messages   map[string]NotificationEventMessage `json:"messages"`
}

NotificationEventInput represents the JSON input for creating/updating notification events.

type NotificationEventItem

type NotificationEventItem struct {
	NotificationID int    `json:"notification_id"`
	EventKey       string `json:"event_key"`
	EventValue     string `json:"event_value"`
}

NotificationEventItem represents an event trigger condition.

type NotificationEventMessage

type NotificationEventMessage struct {
	ID             int    `json:"id"`
	NotificationID int    `json:"notification_id"`
	Subject        string `json:"subject"`
	Text           string `json:"text"`
	ContentType    string `json:"content_type"`
	Language       string `json:"language"`
}

NotificationEventMessage represents a notification message for a specific language.

type PaginationInfo

type PaginationInfo struct {
	Page       int  `json:"page"`
	PerPage    int  `json:"per_page"`
	Total      int  `json:"total"`
	TotalPages int  `json:"total_pages"`
	HasNext    bool `json:"has_next"`
	HasPrev    bool `json:"has_prev"`
}

PaginationInfo contains pagination metadata.

type PluginArticleAttachmentStore added in v0.10.0

type PluginArticleAttachmentStore struct{}

PluginArticleAttachmentStore serves the plugin HostAPI attachment calls from the configured article content store (internal/storage).

func (PluginArticleAttachmentStore) DeleteArticleAttachment added in v0.10.0

func (PluginArticleAttachmentStore) DeleteArticleAttachment(ctx context.Context, db *sql.DB, articleID, fileID int64) error

DeleteArticleAttachment implements plugin.ArticleAttachmentStore.

func (PluginArticleAttachmentStore) ListArticleAttachments added in v0.10.0

func (PluginArticleAttachmentStore) ListArticleAttachments(ctx context.Context, db *sql.DB, articleID int64) ([]pkgplugin.ArticleAttachment, error)

ListArticleAttachments implements plugin.ArticleAttachmentStore. HTML body parts are not attachments and are left out (OTRS ExcludeHTMLBody).

func (PluginArticleAttachmentStore) WriteArticleAttachment added in v0.10.0

func (PluginArticleAttachmentStore) WriteArticleAttachment(ctx context.Context, db *sql.DB, articleID, createdBy int64, filename, contentType string, content []byte) (int64, error)

WriteArticleAttachment implements plugin.ArticleAttachmentStore.

type PluginWidgetData

type PluginWidgetData struct {
	ID          string
	Title       string
	PluginName  string
	HTML        string
	Unavailable bool
	Size        string
	Refreshable bool
	RefreshSec  int
	GridX       int
	GridY       int
	GridW       int
	GridH       int
}

PluginWidgetData is the rendered widget data for templates. Unavailable marks a widget whose plugin call failed; it is shown with an error state instead of content.

func GetPluginWidgets

func GetPluginWidgets(ctx context.Context, location string, ginCtx ...*gin.Context) []PluginWidgetData

GetPluginWidgets returns rendered widgets for a dashboard location. Used by dashboard handlers to include plugin widgets. Pass a gin.Context so each widget call carries the caller's envelope (identity, organisation, language) for i18n, RBAC and org scoping.

type PostmasterFilterInput

type PostmasterFilterInput struct {
	Name    string                   `json:"name" binding:"required"`
	Stop    bool                     `json:"stop"`
	Matches []repository.FilterMatch `json:"matches"`
	Sets    []repository.FilterSet   `json:"sets"`
}

PostmasterFilterInput represents the JSON input for creating/updating filters.

type QueueBasic

type QueueBasic struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

QueueBasic represents minimal queue info for display.

func GetSignatureQueues

func GetSignatureQueues(signatureID int) ([]QueueBasic, error)

GetSignatureQueues returns queues that use a specific signature.

type QueueInfo

type QueueInfo struct {
	ID   int
	Name string
}

QueueInfo represents basic queue information.

func GetQueueInfo

func GetQueueInfo(queueID int) (*QueueInfo, error)

GetQueueInfo returns basic queue information by ID.

type QueueWithTemplateCount

type QueueWithTemplateCount struct {
	ID            int
	Name          string
	TemplateCount int
}

QueueWithTemplateCount represents a queue with its template assignment count.

func GetQueuesWithTemplateCounts

func GetQueuesWithTemplateCounts() ([]QueueWithTemplateCount, error)

GetQueuesWithTemplateCounts returns all valid queues with their template assignment counts.

type RegEx

type RegEx struct {
	Value        string `yaml:"Value"`
	ErrorMessage string `yaml:"ErrorMessage"`
}

RegEx represents a regex validation pattern (OTRS format).

type Role

type Role struct {
	ID          int       `json:"id"`
	Name        string    `json:"name"`
	Comments    *string   `json:"comments"`
	Description string    `json:"description"` // Computed from Comments for template
	ValidID     int       `json:"valid_id"`
	CreateTime  time.Time `json:"create_time"`
	CreateBy    int       `json:"create_by"`
	ChangeTime  time.Time `json:"change_time"`
	ChangeBy    int       `json:"change_by"`
	UserCount   int       `json:"user_count"`
	GroupCount  int       `json:"group_count"`
	IsActive    bool      `json:"is_active"`   // Computed from ValidID
	IsSystem    bool      `json:"is_system"`   // True for built-in roles
	Permissions []string  `json:"permissions"` // Simple permissions list
}

Role represents a role in the system.

type RoleGroupPermission

type RoleGroupPermission struct {
	GroupID     int             `json:"group_id"`
	GroupName   string          `json:"group_name"`
	Permissions map[string]bool `json:"permissions"`
}

RoleGroup represents group permissions for a role.

type RoleUser

type RoleUser struct {
	UserID    int    `json:"user_id"`
	Login     string `json:"login"`
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	Email     string `json:"email"`
}

RoleUser represents a user assigned to a role.

type RouteDoc added in v0.8.2

type RouteDoc = topRouteDoc

RouteDoc represents a parsed YAML route group document.

func LoadYAMLRouteGroups added in v0.8.2

func LoadYAMLRouteGroups(dir string) ([]RouteDoc, error)

LoadYAMLRouteGroups scans the routes directory and returns parsed groups. Exported for use by the MCP tool generator initialization in mcp_handler.go.

type RouteSpec added in v0.8.2

type RouteSpec = routeSpec

RouteSpec represents a single route definition from YAML.

type RoutingHandlerResolver added in v0.9.0

type RoutingHandlerResolver struct {
	// contains filtered or unexported fields
}

RoutingHandlerResolver exposes API-registered handlers through routing's resolver interface.

func NewRoutingHandlerResolver added in v0.9.0

func NewRoutingHandlerResolver() RoutingHandlerResolver

NewRoutingHandlerResolver initializes core API handlers and returns a routing resolver.

func (RoutingHandlerResolver) Get added in v0.9.0

Get resolves a registered API handler by route handler name.

func (RoutingHandlerResolver) GetMiddleware added in v0.9.0

func (r RoutingHandlerResolver) GetMiddleware(name string) (gin.HandlerFunc, error)

GetMiddleware resolves registered routing middleware by name.

func (RoutingHandlerResolver) HandlerExists added in v0.9.0

func (r RoutingHandlerResolver) HandlerExists(name string) bool

HandlerExists reports whether a route handler name is registered.

func (RoutingHandlerResolver) IsFeatureEnabled added in v0.9.0

func (r RoutingHandlerResolver) IsFeatureEnabled(name string) bool

IsFeatureEnabled reports whether a route feature flag is enabled.

func (RoutingHandlerResolver) RegisteredHandlers added in v0.9.0

func (RoutingHandlerResolver) RegisteredHandlers() map[string]gin.HandlerFunc

RegisteredHandlers returns a copy of all API handlers known to the resolver.

func (RoutingHandlerResolver) Registry added in v0.9.0

Registry returns the populated routing registry for callers that need registry-specific APIs.

type SLADefinition

type SLADefinition struct {
	ID                  int       `json:"id"`
	Name                string    `json:"name"`
	CalendarName        *string   `json:"calendar_name,omitempty"`
	FirstResponseTime   *int      `json:"first_response_time,omitempty"`
	FirstResponseNotify *int      `json:"first_response_notify,omitempty"`
	UpdateTime          *int      `json:"update_time,omitempty"`
	UpdateNotify        *int      `json:"update_notify,omitempty"`
	SolutionTime        *int      `json:"solution_time,omitempty"`
	SolutionNotify      *int      `json:"solution_notify,omitempty"`
	Comments            *string   `json:"comments,omitempty"`
	ValidID             int       `json:"valid_id"`
	CreateTime          time.Time `json:"create_time"`
	CreateBy            int       `json:"create_by"`
	ChangeTime          time.Time `json:"change_time"`
	ChangeBy            int       `json:"change_by"`
}

SLADefinition represents a Service Level Agreement configuration.

type SLAWithStats

type SLAWithStats struct {
	SLADefinition
	TicketCount int `json:"ticket_count"`
}

SLAWithStats includes additional statistics.

type ScreenConfigMatrix

type ScreenConfigMatrix struct {
	ObjectType string
	Fields     []DynamicField
	Screens    []ScreenDefinition
	ConfigMap  map[int]map[string]int // fieldID -> screenKey -> configValue
}

ScreenConfigMatrix provides a full view of field-screen mappings for admin UI.

func GetScreenConfigMatrix

func GetScreenConfigMatrix(objectType string) (*ScreenConfigMatrix, error)

GetScreenConfigMatrix builds a matrix of all fields and their screen configs.

type ScreenDefinition

type ScreenDefinition struct {
	Key              string `json:"key"`
	Name             string `json:"name"`
	ObjectType       string `json:"object_type"`
	SupportsRequired bool   `json:"supports_required"`
	IsDisplayOnly    bool   `json:"is_display_only"`
}

ScreenDefinition describes a screen that can display dynamic fields.

func GetScreenDefinitions

func GetScreenDefinitions() []ScreenDefinition

GetScreenDefinitions returns all screens that support dynamic fields.

type SearchableDynamicField

type SearchableDynamicField struct {
	DynamicField
	Options []map[string]string `json:"options"` // [{key: "value", value: "display_name"}, ...]
}

SearchableDynamicField wraps DynamicField with template-friendly Options.

func GetFieldsForSearch

func GetFieldsForSearch() ([]SearchableDynamicField, error)

Results are cached for 30 seconds to improve performance.

type Service

type Service struct {
	ID         int       `json:"id"`
	Name       string    `json:"name"`
	Comments   *string   `json:"comments,omitempty"`
	ValidID    int       `json:"valid_id"`
	CreateTime time.Time `json:"create_time"`
	CreateBy   int       `json:"create_by"`
	ChangeTime time.Time `json:"change_time"`
	ChangeBy   int       `json:"change_by"`
}

type ServiceWithStats

type ServiceWithStats struct {
	Service
	TicketCount int `json:"ticket_count"`
	SLACount    int `json:"sla_count"`
}

ServiceWithStats includes additional statistics.

type Signature

type Signature struct {
	ID          int       `json:"id"`
	Name        string    `json:"name"`
	Text        string    `json:"text"`
	ContentType string    `json:"content_type"`
	Comments    string    `json:"comments"`
	ValidID     int       `json:"valid_id"`
	CreateTime  time.Time `json:"create_time"`
	CreateBy    int       `json:"create_by"`
	ChangeTime  time.Time `json:"change_time"`
	ChangeBy    int       `json:"change_by"`
}

Signature represents an email signature in the system.

func GetAllSignatures

func GetAllSignatures() ([]Signature, error)

GetAllSignatures returns all valid signatures for dropdowns.

func GetSignature

func GetSignature(id int) (*Signature, error)

GetSignature returns a single signature by ID.

type SignatureExportData

type SignatureExportData struct {
	Name        string   `yaml:"name"`
	Text        string   `yaml:"text"`
	ContentType string   `yaml:"content_type,omitempty"`
	Comments    string   `yaml:"comments,omitempty"`
	Valid       bool     `yaml:"valid"`
	Queues      []string `yaml:"queues,omitempty"`
}

SignatureExportData represents a signature for YAML export.

type SignatureForAgent

type SignatureForAgent struct {
	ID          int    `json:"id"`
	Name        string `json:"name"`
	Text        string `json:"text"`
	ContentType string `json:"content_type"`
}

SignatureForAgent represents a signature available to agents.

func GetQueueSignature

func GetQueueSignature(queueID int) (*SignatureForAgent, error)

GetQueueSignature returns the signature assigned to a specific queue.

type SignatureWithStats

type SignatureWithStats struct {
	Signature
	QueueCount int `json:"queue_count"`
}

SignatureWithStats includes queue usage statistics.

func ListSignatures

func ListSignatures(search string, validFilter string, sortBy string, sortOrder string) ([]SignatureWithStats, error)

ListSignatures returns all signatures with optional filters.

type StandardAttachment

type StandardAttachment struct {
	ID                   int       `json:"id"`
	Name                 string    `json:"name"`
	Filename             string    `json:"filename"`
	ContentType          string    `json:"content_type"`
	Content              []byte    `json:"-"` // Don't send raw content in JSON
	ContentSize          int64     `json:"content_size"`
	ContentSizeFormatted string    `json:"content_size_formatted"`
	Comments             *string   `json:"comments"`
	ValidID              int       `json:"valid_id"`
	CreateTime           time.Time `json:"create_time"`
	CreateBy             int       `json:"create_by"`
	ChangeTime           time.Time `json:"change_time"`
	ChangeBy             int       `json:"change_by"`
}

StandardAttachment represents a standard attachment in OTRS.

func ListStandardAttachments

func ListStandardAttachments() ([]StandardAttachment, error)

ListStandardAttachments returns all valid standard attachments.

type StandardTemplate

type StandardTemplate struct {
	ID           int       `json:"id"`
	Name         string    `json:"name"`
	Text         string    `json:"text"`
	ContentType  string    `json:"content_type"`
	TemplateType string    `json:"template_type"` // comma-separated: Answer,Forward,Note
	Comments     string    `json:"comments"`
	ValidID      int       `json:"valid_id"`
	CreateTime   time.Time `json:"create_time"`
	CreateBy     int       `json:"create_by"`
	ChangeTime   time.Time `json:"change_time"`
	ChangeBy     int       `json:"change_by"`
}

StandardTemplate represents a response template in the system.

func GetStandardTemplate

func GetStandardTemplate(id int) (*StandardTemplate, error)

GetStandardTemplate retrieves a single template by ID.

type StandardTemplateWithStats

type StandardTemplateWithStats struct {
	StandardTemplate
	QueueCount int `json:"queue_count"`
}

StandardTemplateWithStats includes usage statistics.

func ListStandardTemplates

func ListStandardTemplates(
	search string, validFilter string, typeFilter string, sortBy string, sortOrder string,
) ([]StandardTemplateWithStats, error)

ListStandardTemplates returns all templates with optional filters.

type State

type State struct {
	ID       int     `json:"id"`
	Name     string  `json:"name"`
	TypeID   *int    `json:"type_id,omitempty"`
	Comments *string `json:"comments,omitempty"`
	ValidID  *int    `json:"valid_id,omitempty"`
}

State represents a ticket state.

type StateType

type StateType struct {
	ID       int     `json:"id"`
	Name     string  `json:"name"`
	Comments *string `json:"comments,omitempty"`
}

StateType represents a ticket state type.

type StateWithType

type StateWithType struct {
	ID          int     `json:"id"`
	Name        string  `json:"name"`
	TypeID      int     `json:"type_id"`
	TypeName    string  `json:"type_name"`
	Comments    *string `json:"comments,omitempty"`
	ValidID     int     `json:"valid_id"`
	TicketCount int     `json:"ticket_count"`
}

StateWithType includes type information.

type TemplateExport

type TemplateExport struct {
	Name         string   `yaml:"name"`
	Text         string   `yaml:"text,omitempty"`
	ContentType  string   `yaml:"content_type,omitempty"`
	TemplateType string   `yaml:"template_type,omitempty"`
	Comments     string   `yaml:"comments,omitempty"`
	Valid        bool     `yaml:"valid"`
	Queues       []string `yaml:"queues,omitempty"`
	Attachments  []string `yaml:"attachments,omitempty"`
}

TemplateExport represents a single template for YAML export.

func ExportTemplate

func ExportTemplate(id int) (*TemplateExport, error)

ExportTemplate exports a single template with its relationships.

type TemplateExportFile

type TemplateExportFile struct {
	Version    string           `yaml:"version"`
	ExportedAt string           `yaml:"exported_at"`
	Templates  []TemplateExport `yaml:"templates"`
}

TemplateExportFile represents the full export file structure.

func ExportAllTemplates

func ExportAllTemplates() (*TemplateExportFile, error)

ExportAllTemplates exports all templates.

type TemplateForAgent

type TemplateForAgent struct {
	ID           int    `json:"id"`
	Name         string `json:"name"`
	Text         string `json:"text"`
	ContentType  string `json:"content_type"`
	TemplateType string `json:"template_type"`
}

TemplateForAgent represents a template available to agents.

func GetTemplatesForQueue

func GetTemplatesForQueue(queueID int, templateType string) ([]TemplateForAgent, error)

GetTemplatesForQueue returns templates available for a specific queue and optional type filter.

type TemplateOption

type TemplateOption struct {
	ID           int
	Name         string
	TemplateType string
	Selected     bool
}

TemplateOption represents a template option for checkbox selection.

type TemplateTypeOption

type TemplateTypeOption struct {
	Key   string `json:"key"`
	Label string `json:"label"`
}

TemplateTypeOption represents a template type for UI selection.

func ValidTemplateTypes

func ValidTemplateTypes() []TemplateTypeOption

ValidTemplateTypes returns all available template types matching OTRS.

type TemplateWithAttachmentCount

type TemplateWithAttachmentCount struct {
	ID              int
	Name            string
	TemplateType    string
	AttachmentCount int
}

TemplateWithAttachmentCount represents a template with its attachment assignment count.

func GetTemplatesWithAttachmentCounts

func GetTemplatesWithAttachmentCounts() ([]TemplateWithAttachmentCount, error)

GetTemplatesWithAttachmentCounts returns all valid templates with their attachment assignment counts.

type TemplateWithQueueCount

type TemplateWithQueueCount struct {
	ID           int
	Name         string
	TemplateType string
	QueueCount   int
}

TemplateWithQueueCount represents a template with its queue assignment count.

func GetTemplatesWithQueueCounts

func GetTemplatesWithQueueCounts() ([]TemplateWithQueueCount, error)

GetTemplatesWithQueueCounts returns all valid templates with their queue assignment counts.

type TicketDisplay

type TicketDisplay struct {
	models.Ticket
	QueueName    string
	PriorityName string
	StateName    string
	OwnerName    string
	CustomerName string
}

TicketDisplay represents ticket data for display purposes.

type TicketListResponse

type TicketListResponse struct {
	Success    bool                     `json:"success"`
	Data       []map[string]interface{} `json:"data"`
	Pagination PaginationInfo           `json:"pagination"`
	Error      string                   `json:"error,omitempty"`
}

TicketListResponse represents the response for ticket list API.

type TicketTemplate

type TicketTemplate struct {
	ID           int       `json:"id"`
	Name         string    `json:"name"`
	Description  string    `json:"description"`
	Subject      string    `json:"subject"`
	Body         string    `json:"body"`
	Priority     string    `json:"priority"`
	QueueID      int       `json:"queue_id"`
	TypeID       int       `json:"type_id"`
	Tags         []string  `json:"tags"`
	Placeholders []string  `json:"placeholders"`
	IsSystem     bool      `json:"is_system"`
	CreatedBy    int       `json:"created_by"`
	CreatedAt    time.Time `json:"created_at"`
	UpdatedAt    time.Time `json:"updated_at"`
}

TicketTemplate represents a reusable ticket template.

type TicketType

type TicketType struct {
	ID          int    `json:"id"`
	Name        string `json:"name"`
	ValidID     int    `json:"valid_id"`
	CreateBy    int    `json:"create_by"`
	ChangeBy    int    `json:"change_by"`
	TicketCount int    `json:"ticket_count"`
}

TicketType represents a ticket type.

type WidgetInfo

type WidgetInfo struct {
	ID         string `json:"id"` // "plugin_name:widget_id"
	PluginName string `json:"plugin_name"`
	WidgetID   string `json:"widget_id"`
	Title      string `json:"title"`
	Size       string `json:"size"`
	Enabled    bool   `json:"enabled"`
	Position   int    `json:"position"`
	X          int    `json:"x"`
	Y          int    `json:"y"`
	W          int    `json:"w"`
	H          int    `json:"h"`
}

WidgetInfo describes a widget for the configuration UI.

Source Files

Jump to

Keyboard shortcuts

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