api

package
v0.5.1 Latest Latest
Warning

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

Go to latest
Published: Jan 8, 2026 License: Apache-2.0 Imports: 74 Imported by: 0

Documentation

Overview

Package api provides HTTP handlers for the GOTRS application.

Package api provides HTTP API handlers for GOTRS.

Package api provides HTTP handlers for the GOTRS 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"
)

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 (
	HandleLoginPage           = handleLoginPage
	HandleCustomerLoginPage   = handleCustomerLoginPage
	HandleLogout              = handleLogout
	HandleDashboard           = handleDashboard
	HandleDashboardStats      = handleDashboardStats
	HandleRecentTickets       = handleRecentTickets
	DashboardQueueStatus      = dashboard_queue_status
	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
	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)
	}
		dbService, err := adapter.GetDatabase()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
			return
		}
		handleAdminUploadCustomerPortalLogo(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 (
	HandleAdminSettings  gin.HandlerFunc = handleAdminSettings
	HandleAdminTemplates gin.HandlerFunc = handleAdminTemplates
	HandleAdminReports   gin.HandlerFunc = handleAdminReports
	HandleAdminLogs      gin.HandlerFunc = handleAdminLogs
	HandleAdminBackup    gin.HandlerFunc = handleAdminBackup
)
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
	HandleAgentTicketDraft    = AgentHandlerExports.HandleAgentTicketDraft
)

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)
	HandleCustomerKnowledgeBase  = wrapAdapterDBHandler(handleCustomerKnowledgeBase)
	HandleCustomerKBSearch       = wrapAdapterDBHandler(handleCustomerKBSearch)
	HandleCustomerKBArticle      = wrapAdapterDBHandler(handleCustomerKBArticle)
	HandleCustomerCompanyInfo    = wrapAdapterDBHandler(handleCustomerCompanyInfo)
	HandleCustomerCompanyUsers   = wrapAdapterDBHandler(handleCustomerCompanyUsers)
)

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
	HandleAgentTicketDraft     gin.HandlerFunc
	HandleAgentCustomerTickets gin.HandlerFunc
	HandleAgentCustomerView    gin.HandlerFunc
	HandleAgentSearch          gin.HandlerFunc
	HandleAgentSearchResults   gin.HandlerFunc
	HandleAgentNewTicket       gin.HandlerFunc
	HandleAgentCreateTicket    gin.HandlerFunc
	HandleAgentQueues          gin.HandlerFunc
	HandleAgentQueueView       gin.HandlerFunc
	HandleAgentQueueLock       gin.HandlerFunc
	HandleAgentCustomers       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),
	HandleAgentTicketDraft:     wrapDBHandler(handleAgentTicketDraft),
	HandleAgentCustomerTickets: wrapDBHandler(handleAgentCustomerTickets),
	HandleAgentCustomerView:    wrapDBHandler(handleAgentCustomerView),
	HandleAgentSearch:          wrapDBHandler(handleAgentSearch),
	HandleAgentSearchResults:   wrapDBHandler(handleAgentSearchResults),
	HandleAgentNewTicket:       wrapDBHandler(HandleAgentNewTicket),
	HandleAgentCreateTicket:    wrapDBHandler(HandleAgentCreateTicket),
	HandleAgentQueues:          wrapDBHandler(handleAgentQueues),
	HandleAgentQueueView:       wrapDBHandler(handleAgentQueueView),
	HandleAgentQueueLock:       wrapDBHandler(handleAgentQueueLock),
	HandleAgentCustomers:       wrapDBHandler(handleAgentCustomers),
}

AgentHandlerExports provides exported handler functions for agent routes.

View Source
var CannedResponseHandlerExports = NewCannedResponseHandlers()

CannedResponseHandlerExports provides singleton access for YAML route registration.

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 = HandleLoginAPI

Auth handlers.

View Source
var HandleAPIv1AuthLogout = HandleLogoutAPI
View Source
var HandleAPIv1AuthRefresh = HandleRefreshTokenAPI
View Source
var HandleAPIv1AuthRegister = HandleRegisterAPI
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 = 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 HandleAuthCheck = func(c *gin.Context) {
	userID, exists := c.Get("user_id")
	if !exists {
		c.JSON(http.StatusUnauthorized, gin.H{
			"authenticated": false,
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"authenticated": true,
		"userID":        userID,
	})
}

HandleAuthCheck checks if user is authenticated.

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
	}

	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, accessToken, refreshToken, err := authService.Login(ctx, username, password)
	if err != nil {

		if strings.Contains(contentType, "application/json") {
			adminUser := strings.TrimSpace(os.Getenv("ADMIN_USER"))
			adminPass := strings.TrimSpace(os.Getenv("ADMIN_PASSWORD"))
			isAdminMatch := adminUser != "" && adminPass != "" && password == adminPass &&
				(username == adminUser || (adminUser == "admin@example.com" && username == "root@localhost"))
			if isAdminMatch {

				jwtm := shared.GetJWTManager()
				if jwtm == nil {
					c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "jwt manager unavailable"})
					return
				}

				tok, err2 := jwtm.GenerateToken(1, username, "Admin", 0)
				if err2 != nil {
					c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "token generation failed"})
					return
				}
				c.JSON(http.StatusOK, gin.H{
					"success": true, "access_token": tok, "token_type": "Bearer",
					"user": gin.H{"id": 1, "login": username, "role": "Admin"},
				})
				return
			}
		}
		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
	}

	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 sessionTimeout <= 0 {
		sessionTimeout = constants.DefaultSessionTimeout
	}

	c.SetCookie(
		"auth_token",
		accessToken,
		sessionTimeout,
		"/",
		"",
		false,
		true,
	)

	c.SetCookie(
		"access_token",
		accessToken,
		sessionTimeout,
		"/",
		"",
		false,
		true,
	)

	c.SetCookie(
		"refresh_token",
		refreshToken,
		constants.RefreshTokenTimeout,
		"/",
		"",
		false,
		true,
	)

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

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

	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 HandleAuthLogout = func(c *gin.Context) {

	c.SetCookie("auth_token", "", -1, "/", "", false, true)
	c.SetCookie("access_token", "", -1, "/", "", false, true)
	c.SetCookie("refresh_token", "", -1, "/", "", false, true)

	c.Redirect(http.StatusSeeOther, "/login")
}

HandleAuthLogout handles user logout.

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 added in v0.5.0

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 added in v0.5.0

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

BulkSetScreenConfigForField replaces all screen configs for a field.

func CheckDynamicFieldNameExists added in v0.5.0

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

CheckDynamicFieldNameExists checks if a field name already exists.

func CheckSignatureNameExists added in v0.5.0

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

CheckSignatureNameExists checks if a signature name already exists.

func CheckTemplateNameExists added in v0.5.0

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

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

func CreateDynamicField added in v0.5.0

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

CreateDynamicField creates a new dynamic field.

func CreateSignature added in v0.5.0

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

CreateSignature creates a new signature.

func CreateStandardTemplate added in v0.5.0

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

CreateStandardTemplate creates a new template.

func CustomerOnlyGuard added in v0.5.0

func CustomerOnlyGuard(enabled bool) gin.HandlerFunc

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

func DeleteDynamicField added in v0.5.0

func DeleteDynamicField(id int) error

DeleteDynamicField deletes a dynamic field and its values.

func DeleteSignature added in v0.5.0

func DeleteSignature(id int) error

DeleteSignature deletes a signature by ID.

func DeleteStandardTemplate added in v0.5.0

func DeleteStandardTemplate(id int) error

DeleteStandardTemplate deletes a template and its relationships.

func ExportAllSignatures added in v0.5.0

func ExportAllSignatures() ([]byte, error)

ExportAllSignatures exports all signatures to YAML.

func ExportSignature added in v0.5.0

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

ExportSignature exports a single signature to YAML.

func ExtractToken

func ExtractToken(c *gin.Context) string

ExtractToken extracts the JWT token from the Authorization header.

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 GetArticleAttachments

func GetArticleAttachments(articleID int) ([]map[string]interface{}, error)

GetArticleAttachments retrieves attachments for a specific article from the database.

func GetAuthService

func GetAuthService() *service.AuthService

GetAuthService returns the singleton auth service instance.

func GetDistinctDynamicFieldValues added in v0.5.0

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

Useful for populating filter dropdown options.

func GetDynamicFieldsForScreenGeneric added in v0.5.0

func GetDynamicFieldsForScreenGeneric(screenKey, objectType string) ([]interface{}, error)

Returns []interface{} to avoid import cycles.

func GetDynamicFieldsGroupedByObjectType added in v0.5.0

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 GetLookupRepository

func GetLookupRepository() repository.LookupRepository

func GetLookupService

func GetLookupService() *service.LookupService

GetLookupService returns the singleton lookup service instance.

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 GetStorageService

func GetStorageService() service.StorageService

GetStorageService returns the singleton storage service instance.

func GetTemplateAttachments added in v0.5.0

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

GetTemplateAttachments returns all attachment IDs assigned to a template.

func GetTemplateQueues added in v0.5.0

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

GetTemplateQueues returns all queues assigned to a template.

func GetTicketAttachments

func GetTicketAttachments(ticketID int) (map[int][]map[string]interface{}, error)

GetTicketAttachments retrieves all attachments for all articles in a ticket.

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 added in v0.5.0

func GetTicketTemplateVariables(ticketID int) map[string]string

GetTicketTemplateVariables returns template variables for a ticket.

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.

func HandleAPIQueueGet

func HandleAPIQueueGet(c *gin.Context)

HandleAPIQueueGet handles GET /api/queues/:id.

func HandleAPIQueueStatus

func HandleAPIQueueStatus(c *gin.Context)

HandleAPIQueueStatus handles PUT /api/queues/:id/status.

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 HandleAddUserToGroupAPI

func HandleAddUserToGroupAPI(c *gin.Context)

HandleAddUserToGroupAPI handles POST /api/v1/users/:id/groups.

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 HandleAdminDynamicIndex added in v0.5.0

func HandleAdminDynamicIndex(c *gin.Context)

func HandleAdminDynamicModule added in v0.5.0

func HandleAdminDynamicModule(c *gin.Context)

func HandleAdminDynamicModuleFor added in v0.5.0

func HandleAdminDynamicModuleFor(module string) gin.HandlerFunc

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

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 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 HandleAdminUsersCreate

func HandleAdminUsersCreate(c *gin.Context)

HandleAdminUsersCreate handles POST /admin/users.

func HandleAdminUsersDelete

func HandleAdminUsersDelete(c *gin.Context)

HandleAdminUsersDelete handles DELETE /admin/users/:id.

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 HandleAdminUsersUpdate

func HandleAdminUsersUpdate(c *gin.Context)

HandleAdminUsersUpdate handles PUT /admin/users/:id.

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 HandleAgentPerformanceAPI

func HandleAgentPerformanceAPI(c *gin.Context)

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

func HandleAssignQueueGroupAPI

func HandleAssignQueueGroupAPI(c *gin.Context)

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

func HandleAssignTicketAPI

func HandleAssignTicketAPI(c *gin.Context)

HandleAssignTicketAPI handles ticket assignment via API.

func HandleCloseTicketAPI

func HandleCloseTicketAPI(c *gin.Context)

HandleCloseTicketAPI handles ticket closure via API.

func HandleCreateArticleAPI

func HandleCreateArticleAPI(c *gin.Context)

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

func HandleCreateInternalNote

func HandleCreateInternalNote(c *gin.Context)

HandleCreateInternalNote creates a new internal note.

func HandleCreatePriorityAPI

func HandleCreatePriorityAPI(c *gin.Context)

HandleCreatePriorityAPI handles POST /api/v1/priorities.

func HandleCreateQueueAPI

func HandleCreateQueueAPI(c *gin.Context)

HandleCreateQueueAPI handles POST /api/v1/queues.

func HandleCreateSLAAPI

func HandleCreateSLAAPI(c *gin.Context)

HandleCreateSLAAPI handles POST /api/v1/slas.

func HandleCreateSalutationAPI added in v0.5.0

func HandleCreateSalutationAPI(c *gin.Context)

func HandleCreateSignatureAPI added in v0.5.0

func HandleCreateSignatureAPI(c *gin.Context)

func HandleCreateSystemAddressAPI added in v0.5.0

func HandleCreateSystemAddressAPI(c *gin.Context)

func HandleCreateTicketAPI

func HandleCreateTicketAPI(c *gin.Context)

HandleCreateTicketAPI handles ticket creation via API.

func HandleCreateTicketStateAPI

func HandleCreateTicketStateAPI(c *gin.Context)

HandleCreateTicketStateAPI handles POST /api/v1/ticket-states.

func HandleCreateUserAPI

func HandleCreateUserAPI(c *gin.Context)

HandleCreateUserAPI handles POST /api/v1/users.

func HandleCustomerStatisticsAPI

func HandleCustomerStatisticsAPI(c *gin.Context)

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

func HandleDashboardStatisticsAPI

func HandleDashboardStatisticsAPI(c *gin.Context)

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

func HandleDebugConfigSources added in v0.5.0

func HandleDebugConfigSources(c *gin.Context)

HandleDebugConfigSources lists configuration settings with their source.

func HandleDebugTicketNumber added in v0.5.0

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.

func HandleDeleteInternalNote

func HandleDeleteInternalNote(c *gin.Context)

HandleDeleteInternalNote deletes an internal note.

func HandleDeletePriorityAPI

func HandleDeletePriorityAPI(c *gin.Context)

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

func HandleDeleteQueueAPI

func HandleDeleteQueueAPI(c *gin.Context)

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

func HandleDeleteSLAAPI

func HandleDeleteSLAAPI(c *gin.Context)

HandleDeleteSLAAPI handles DELETE /api/v1/slas/:id.

func HandleDeleteTicketAPI

func HandleDeleteTicketAPI(c *gin.Context)

In OTRS, tickets are never hard deleted, only archived.

func HandleDeleteTicketStateAPI

func HandleDeleteTicketStateAPI(c *gin.Context)

HandleDeleteTicketStateAPI handles DELETE /api/v1/ticket-states/:id.

func HandleDeleteUserAPI

func HandleDeleteUserAPI(c *gin.Context)

This performs a soft delete by setting valid_id = 2 (OTRS pattern).

func HandleDeleteWebhookAPI

func HandleDeleteWebhookAPI(c *gin.Context)

HandleDeleteWebhookAPI handles DELETE /api/v1/webhooks/:id.

func HandleExportStatisticsAPI

func HandleExportStatisticsAPI(c *gin.Context)

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

func HandleGetArticleAPI

func HandleGetArticleAPI(c *gin.Context)

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

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 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.

func HandleGetQueueAPI

func HandleGetQueueAPI(c *gin.Context)

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

func HandleGetQueueAgentsAPI

func HandleGetQueueAgentsAPI(c *gin.Context)

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

func HandleGetQueueStatsAPI

func HandleGetQueueStatsAPI(c *gin.Context)

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

func HandleGetQueues

func HandleGetQueues(c *gin.Context)

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

func HandleGetSLAAPI

func HandleGetSLAAPI(c *gin.Context)

HandleGetSLAAPI handles GET /api/v1/slas/:id.

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 HandleGetTicketAPI

func HandleGetTicketAPI(c *gin.Context)

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

func HandleGetTicketStateAPI

func HandleGetTicketStateAPI(c *gin.Context)

HandleGetTicketStateAPI handles GET /api/v1/ticket-states/:id.

func HandleGetTypes

func HandleGetTypes(c *gin.Context)

HandleGetTypes returns list of ticket types as JSON or HTML options for HTMX. Behavior:

  • If a DB connection is available and the query succeeds, return the SQL-backed shape: [{id, name, comments, valid_id}].
  • Otherwise fall back to the lookup service shape (value/label/order/active).
  • For HTMX requests, return HTML <option> elements instead of JSON.

func HandleGetUserAPI

func HandleGetUserAPI(c *gin.Context)

HandleGetUserAPI handles GET /api/v1/users/:id.

func HandleGetUserGroupsAPI

func HandleGetUserGroupsAPI(c *gin.Context)

HandleGetUserGroupsAPI handles GET /api/v1/users/:id/groups.

func HandleGetWebhookAPI

func HandleGetWebhookAPI(c *gin.Context)

HandleGetWebhookAPI handles GET /api/v1/webhooks/:id.

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.

func HandleListGroupsAPI added in v0.5.0

func HandleListGroupsAPI(c *gin.Context)

HandleListGroupsAPI handles GET /api/v1/groups.

func HandleListPrioritiesAPI

func HandleListPrioritiesAPI(c *gin.Context)

HandleListPrioritiesAPI handles GET /api/v1/priorities.

func HandleListQueuesAPI

func HandleListQueuesAPI(c *gin.Context)

HandleListQueuesAPI handles GET /api/v1/queues.

func HandleListSLAsAPI

func HandleListSLAsAPI(c *gin.Context)

HandleListSLAsAPI handles GET /api/v1/slas.

func HandleListSalutationsAPI added in v0.5.0

func HandleListSalutationsAPI(c *gin.Context)

func HandleListSignaturesAPI added in v0.5.0

func HandleListSignaturesAPI(c *gin.Context)

func HandleListStatesAPI

func HandleListStatesAPI(c *gin.Context)

HandleListStatesAPI handles GET /api/v1/states.

func HandleListSystemAddressesAPI added in v0.5.0

func HandleListSystemAddressesAPI(c *gin.Context)

func HandleListTicketStatesAPI

func HandleListTicketStatesAPI(c *gin.Context)

HandleListTicketStatesAPI handles GET /api/v1/ticket-states.

func HandleListTicketsAPI

func HandleListTicketsAPI(c *gin.Context)

HandleListTicketsAPI handles GET /api/v1/tickets.

func HandleListTypesAPI

func HandleListTypesAPI(c *gin.Context)

HandleListTypesAPI handles GET /api/v1/types.

func HandleListUsersAPI

func HandleListUsersAPI(c *gin.Context)

HandleListUsersAPI handles GET /api/v1/users.

func HandleListWebhooksAPI

func HandleListWebhooksAPI(c *gin.Context)

HandleListWebhooksAPI handles GET /api/v1/webhooks.

func HandleLoginAPI

func HandleLoginAPI(c *gin.Context)

HandleLoginAPI authenticates a user and returns JWT tokens.

func HandleLogoutAPI

func HandleLogoutAPI(c *gin.Context)

HandleLogoutAPI logs out a user (client-side token removal).

func HandleMailAccountPollStatus added in v0.5.0

func HandleMailAccountPollStatus(c *gin.Context)

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

func HandlePasswordPolicy

func HandlePasswordPolicy(c *gin.Context)

HandlePasswordPolicy returns the current password policy settings.

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.

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 HandleRefreshTokenAPI

func HandleRefreshTokenAPI(c *gin.Context)

HandleRefreshTokenAPI refreshes an expired JWT token.

func HandleRegisterAPI

func HandleRegisterAPI(c *gin.Context)

HandleRegisterAPI registers a new user (if enabled).

func HandleRegisterWebhookAPI

func HandleRegisterWebhookAPI(c *gin.Context)

HandleRegisterWebhookAPI handles POST /api/v1/webhooks.

func HandleReindexAPI

func HandleReindexAPI(c *gin.Context)

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

func HandleRemoveQueueGroupAPI

func HandleRemoveQueueGroupAPI(c *gin.Context)

HandleRemoveQueueGroupAPI handles DELETE /api/v1/queues/:id/groups/:group_id.

func HandleRemoveUserFromGroupAPI

func HandleRemoveUserFromGroupAPI(c *gin.Context)

HandleRemoveUserFromGroupAPI handles DELETE /api/v1/users/:id/groups/:group_id.

func HandleReopenTicketAPI

func HandleReopenTicketAPI(c *gin.Context)

HandleReopenTicketAPI handles ticket reopening via API.

func HandleRetryWebhookDeliveryAPI

func HandleRetryWebhookDeliveryAPI(c *gin.Context)

HandleRetryWebhookDeliveryAPI handles POST /api/v1/webhooks/deliveries/:id/retry.

func HandleSLAMetricsAPI

func HandleSLAMetricsAPI(c *gin.Context)

HandleSLAMetricsAPI handles GET /api/v1/slas/:id/metrics.

func HandleSearchAPI

func HandleSearchAPI(c *gin.Context)

HandleSearchAPI handles POST /api/v1/search.

func HandleSearchHealthAPI

func HandleSearchHealthAPI(c *gin.Context)

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

func HandleSearchSuggestionsAPI

func HandleSearchSuggestionsAPI(c *gin.Context)

HandleSearchSuggestionsAPI handles GET /api/v1/search/suggestions.

func HandleSetSessionTimeout

func HandleSetSessionTimeout(c *gin.Context)

HandleSetSessionTimeout sets the user's session timeout preference.

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 HandleTestWebhookAPI

func HandleTestWebhookAPI(c *gin.Context)

HandleTestWebhookAPI handles POST /api/v1/webhooks/:id/test.

func HandleTicketHistoryFragment added in v0.5.0

func HandleTicketHistoryFragment(c *gin.Context)

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

func HandleTicketLinksFragment added in v0.5.0

func HandleTicketLinksFragment(c *gin.Context)

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

func HandleTicketStateStatisticsAPI

func HandleTicketStateStatisticsAPI(c *gin.Context)

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

func HandleTicketTrendsAPI

func HandleTicketTrendsAPI(c *gin.Context)

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

func HandleTimeBasedAnalyticsAPI

func HandleTimeBasedAnalyticsAPI(c *gin.Context)

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

func HandleUpdateArticleAPI

func HandleUpdateArticleAPI(c *gin.Context)

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

func HandleUpdateInternalNote

func HandleUpdateInternalNote(c *gin.Context)

HandleUpdateInternalNote updates an existing internal note.

func HandleUpdatePriorityAPI

func HandleUpdatePriorityAPI(c *gin.Context)

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

func HandleUpdateQueueAPI

func HandleUpdateQueueAPI(c *gin.Context)

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

func HandleUpdateSLAAPI

func HandleUpdateSLAAPI(c *gin.Context)

HandleUpdateSLAAPI handles PUT /api/v1/slas/:id.

func HandleUpdateSalutationAPI added in v0.5.0

func HandleUpdateSalutationAPI(c *gin.Context)

func HandleUpdateSignatureAPI added in v0.5.0

func HandleUpdateSignatureAPI(c *gin.Context)

func HandleUpdateSystemAddressAPI added in v0.5.0

func HandleUpdateSystemAddressAPI(c *gin.Context)

func HandleUpdateTicketAPI

func HandleUpdateTicketAPI(c *gin.Context)

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

func HandleUpdateTicketStateAPI

func HandleUpdateTicketStateAPI(c *gin.Context)

HandleUpdateTicketStateAPI handles PUT /api/v1/ticket-states/:id.

func HandleUpdateUserAPI

func HandleUpdateUserAPI(c *gin.Context)

HandleUpdateUserAPI handles PUT /api/v1/users/:id.

func HandleUpdateWebhookAPI

func HandleUpdateWebhookAPI(c *gin.Context)

HandleUpdateWebhookAPI handles PUT /api/v1/webhooks/:id.

func HandleUserMeAPI

func HandleUserMeAPI(c *gin.Context)

HandleUserMeAPI returns the current authenticated user's information.

func HandleWebhookDeliveriesAPI

func HandleWebhookDeliveriesAPI(c *gin.Context)

HandleWebhookDeliveriesAPI handles GET /api/v1/webhooks/:id/deliveries.

func ImportSignatures added in v0.5.0

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

ImportSignatures imports signatures from YAML data.

func ImportTemplates added in v0.5.0

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

ImportTemplates imports templates from YAML data.

func ImprovedHandleAdminUserGet

func ImprovedHandleAdminUserGet(c *gin.Context)

ImprovedHandleAdminUserGet handles GET /admin/users/:id with enhanced error handling and logging.

func ImprovedHandleAdminUserUpdate

func ImprovedHandleAdminUserUpdate(c *gin.Context)

ImprovedHandleAdminUserUpdate handles PUT /admin/users/:id with enhanced group handling.

func InitializeLDAPService

func InitializeLDAPService()

InitializeLDAPService initializes the LDAP service with repositories.

func InitializeServices

func InitializeServices()

InitializeServices initializes singleton service instances.

func JWTAuthMiddleware

func JWTAuthMiddleware() gin.HandlerFunc

JWTAuthMiddleware is a middleware that requires JWT authentication.

func JoinTemplateTypes added in v0.5.0

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 LoadTicketStatesForForm

func LoadTicketStatesForForm(db *sql.DB) ([]gin.H, map[string]gin.H, error)

LoadTicketStatesForForm fetches valid ticket states and builds alias lookup data for forms.

func NewHTMXRouter

func NewHTMXRouter(jwtManager *auth.JWTManager, ldapProvider *ldap.Provider) *gin.Engine

NewHTMXRouter creates all routes for the HTMX UI.

func NewSimpleRouter

func NewSimpleRouter() *gin.Engine

NewSimpleRouter creates a router with basic routes.

func NewSimpleRouterWithDB added in v0.5.0

func NewSimpleRouterWithDB(db *sql.DB) *gin.Engine

NewSimpleRouterWithDB creates a router with basic routes and a specific database connection.

func ParseTemplateTypes added in v0.5.0

func ParseTemplateTypes(typeStr string) []string

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

func ProcessArticleDynamicFieldsFromForm added in v0.5.0

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

ProcessArticleDynamicFieldsFromForm processes dynamic fields with "ArticleDynamicField_" prefix.

func ProcessDynamicFieldsFromForm added in v0.5.0

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 RegisterAdminRoutes added in v0.5.0

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 RegisterCannedResponseHandlers added in v0.5.0

func RegisterCannedResponseHandlers(r *gin.RouterGroup)

RegisterCannedResponseHandlers registers all canned response API routes.

func RegisterCustomerRoutes

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

RegisterCustomerRoutes registers all customer portal routes.

func RegisterDynamicFieldRoutes added in v0.5.0

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

RegisterDynamicFieldRoutes registers all dynamic field admin routes.

func RegisterDynamicModuleHandlersIntoRegistry added in v0.5.0

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.

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 HTML with Tailwind styling.

func RootRedirectTarget added in v0.5.0

func RootRedirectTarget() string

func SetDynamicFieldValue added in v0.5.0

func SetDynamicFieldValue(value *DynamicFieldValue) error

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

func SetScreenConfig added in v0.5.0

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

SetScreenConfig sets a single field-screen config.

func SetTemplateAttachments added in v0.5.0

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

SetTemplateAttachments sets the attachment assignments for a template.

func SetTemplateQueues added in v0.5.0

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

SetTemplateQueues sets the queue assignments for a template.

func SetValkeyCache added in v0.5.0

func SetValkeyCache(c *cache.RedisCache)

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

func SetupAPIv1Routes

func SetupAPIv1Routes(r *gin.Engine, jwtManager *auth.JWTManager, ldapProvider *ldap.Provider, i18nSvc interface{})

SetupAPIv1Routes configures the v1 API routes.

func SetupBasicRoutes

func SetupBasicRoutes(r *gin.Engine)

SetupBasicRoutes adds basic routes to an existing router.

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 SetupLDAPRoutes

func SetupLDAPRoutes(r *gin.Engine)

SetupLDAPRoutes configures LDAP API routes.

func SetupTestRouterWithRoutes added in v0.5.0

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 SetupTestTemplateRenderer added in v0.5.0

func SetupTestTemplateRenderer(t *testing.T)

SetupTestTemplateRenderer initializes the global template renderer for tests. This MUST be called by any test that exercises handlers calling shared.GetGlobalRenderer(). Safe to call multiple times - initialization happens only once.

func SubstituteSignatureVariables added in v0.5.0

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

Reuses the same variable substitution logic as templates.

func SubstituteTemplateVariables added in v0.5.0

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

SubstituteTemplateVariables replaces template variables with actual values.

func UpdateDynamicField added in v0.5.0

func UpdateDynamicField(field *DynamicField, userID int) error

UpdateDynamicField updates an existing dynamic field.

func UpdateSignature added in v0.5.0

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

UpdateSignature updates an existing signature.

func UpdateStandardTemplate added in v0.5.0

func UpdateStandardTemplate(t *StandardTemplate, userID int) error

UpdateStandardTemplate updates an existing template.

func ValidFieldTypes added in v0.5.0

func ValidFieldTypes() []string

ValidFieldTypes returns all supported field types.

func ValidObjectTypes added in v0.5.0

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 added in v0.5.0

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.

Types

type AdminRouteDefinition added in v0.5.0

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

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

func GetAdminDynamicFieldsRoutes added in v0.5.0

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 added in v0.5.0

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 added in v0.5.0

type ArticleInsertParams struct {
	TicketID             int64
	CommunicationChannel int // 1=email, 2=phone, 3=internal, 4=chat
	IsVisibleForCustomer int
	CreateBy             int64
}

ArticleInsertParams holds parameters for creating an article.

type ArticleMimeParams added in v0.5.0

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 Attachment

type Attachment struct {
	ID          int       `json:"id"`
	TicketID    int       `json:"ticket_id"`
	Filename    string    `json:"filename"`
	ContentType string    `json:"content_type"`
	Size        int64     `json:"size"`
	StoragePath string    `json:"-"` // Hidden from JSON
	Description string    `json:"description"`
	Tags        []string  `json:"tags"`
	Internal    bool      `json:"internal"`
	UploadedBy  int       `json:"uploaded_by"`
	UploadedAt  time.Time `json:"uploaded_at"`
	Downloaded  int       `json:"download_count"`
}

Attachment represents a file attached to a ticket.

type AttachmentInfo added in v0.5.0

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 added in v0.5.0

func GetAttachmentsByIDs(ids []int) []AttachmentInfo

GetAttachmentsByIDs returns attachment details for given IDs.

type AuthHandler

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

func NewAuthHandler

func NewAuthHandler(authService *service.AuthService) *AuthHandler

func (*AuthHandler) ChangePassword

func (h *AuthHandler) ChangePassword(c *gin.Context)

func (*AuthHandler) GetCurrentUser

func (h *AuthHandler) GetCurrentUser(c *gin.Context)

func (*AuthHandler) Login

func (h *AuthHandler) Login(c *gin.Context)

func (*AuthHandler) Logout

func (h *AuthHandler) Logout(c *gin.Context)

func (*AuthHandler) RefreshToken

func (h *AuthHandler) RefreshToken(c *gin.Context)

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 added in v0.5.0

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 added in v0.5.0

func (r *CannedResponseDB) ToCannedResponse() *CannedResponse

ToCannedResponse converts database model to API model.

type CannedResponseFilters added in v0.5.0

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

CannedResponseFilters holds filter options for listing responses.

type CannedResponseHandlers

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

CannedResponseHandlers manages canned response API endpoints.

func NewCannedResponseHandlers

func NewCannedResponseHandlers() *CannedResponseHandlers

NewCannedResponseHandlers creates a new canned response handlers instance.

func (*CannedResponseHandlers) ApplyResponse

func (h *CannedResponseHandlers) ApplyResponse(c *gin.Context)

ApplyResponse applies a canned response to a ticket.

func (*CannedResponseHandlers) CreateResponse

func (h *CannedResponseHandlers) CreateResponse(c *gin.Context)

CreateResponse creates a new canned response.

func (*CannedResponseHandlers) DeleteResponse

func (h *CannedResponseHandlers) DeleteResponse(c *gin.Context)

DeleteResponse deletes a canned response.

func (*CannedResponseHandlers) ExportResponses

func (h *CannedResponseHandlers) ExportResponses(c *gin.Context)

ExportResponses exports all canned responses as JSON.

func (*CannedResponseHandlers) GetCategories

func (h *CannedResponseHandlers) GetCategories(c *gin.Context)

GetCategories retrieves all canned response categories.

func (*CannedResponseHandlers) GetPopularResponses

func (h *CannedResponseHandlers) GetPopularResponses(c *gin.Context)

GetPopularResponses retrieves the most used responses.

func (*CannedResponseHandlers) GetQuickResponses

func (h *CannedResponseHandlers) GetQuickResponses(c *gin.Context)

GetQuickResponses retrieves responses with shortcuts.

func (*CannedResponseHandlers) GetResponseByID

func (h *CannedResponseHandlers) GetResponseByID(c *gin.Context)

GetResponseByID retrieves a specific canned response.

func (*CannedResponseHandlers) GetResponses

func (h *CannedResponseHandlers) GetResponses(c *gin.Context)

GetResponses retrieves all active canned responses.

func (*CannedResponseHandlers) GetResponsesByCategory

func (h *CannedResponseHandlers) GetResponsesByCategory(c *gin.Context)

GetResponsesByCategory retrieves responses by category.

func (*CannedResponseHandlers) GetResponsesForUser

func (h *CannedResponseHandlers) GetResponsesForUser(c *gin.Context)

GetResponsesForUser retrieves responses accessible to the current user.

func (*CannedResponseHandlers) ImportResponses

func (h *CannedResponseHandlers) ImportResponses(c *gin.Context)

ImportResponses imports canned responses from JSON.

func (*CannedResponseHandlers) SearchResponses

func (h *CannedResponseHandlers) SearchResponses(c *gin.Context)

SearchResponses searches for canned responses.

func (*CannedResponseHandlers) UpdateResponse

func (h *CannedResponseHandlers) UpdateResponse(c *gin.Context)

UpdateResponse updates an existing canned response.

type CannedResponseRepository added in v0.5.0

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

CannedResponseRepository handles database operations for canned responses.

func NewCannedResponseRepository added in v0.5.0

func NewCannedResponseRepository() (*CannedResponseRepository, error)

NewCannedResponseRepository creates a new repository.

func (*CannedResponseRepository) CheckDuplicate added in v0.5.0

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 added in v0.5.0

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

Create inserts a new canned response.

func (*CannedResponseRepository) Delete added in v0.5.0

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

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

func (*CannedResponseRepository) GetByID added in v0.5.0

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

GetByID retrieves a canned response by ID.

func (*CannedResponseRepository) IncrementUsage added in v0.5.0

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

IncrementUsage increments usage count and updates last_used.

func (*CannedResponseRepository) ListAccessible added in v0.5.0

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

ListAccessible returns canned responses accessible to a user.

func (*CannedResponseRepository) ListCategories added in v0.5.0

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

ListCategories returns all canned response categories.

func (*CannedResponseRepository) Update added in v0.5.0

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

Update updates a canned response.

type CoverageResponse

type CoverageResponse struct {
	Languages []LanguageCoverage `json:"languages"`
	Summary   struct {
		TotalKeys       int     `json:"total_keys"`
		AverageCoverage float64 `json:"average_coverage"`
	} `json:"summary"`
}

CoverageResponse represents the coverage statistics response.

type CreateUserRequest

type CreateUserRequest struct {
	Login     string `json:"login" binding:"required"`
	Email     string `json:"email" binding:"required,email"`
	Password  string `json:"password" binding:"required,min=8"`
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	ValidID   int    `json:"valid_id"`
	Groups    []int  `json:"groups"` // Optional group IDs to assign
}

CreateUserRequest represents the request to create a new user.

type DynamicField added in v0.5.0

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 added in v0.5.0

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

GetDynamicField retrieves a single dynamic field by ID.

func GetDynamicFieldByName added in v0.5.0

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

GetDynamicFieldByName retrieves a single dynamic field by name.

func GetDynamicFields added in v0.5.0

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

GetDynamicFields retrieves all dynamic fields.

func GetDynamicFieldsForScreen added in v0.5.0

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

GetDynamicFieldsForScreen retrieves active fields for a specific screen.

func (*DynamicField) GetValueColumn added in v0.5.0

func (df *DynamicField) GetValueColumn() string

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

func (*DynamicField) IsActive added in v0.5.0

func (df *DynamicField) IsActive() bool

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

func (*DynamicField) IsInternal added in v0.5.0

func (df *DynamicField) IsInternal() bool

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

func (*DynamicField) ParseConfig added in v0.5.0

func (df *DynamicField) ParseConfig() error

ParseConfig deserializes YAML config blob into DynamicFieldConfig.

func (*DynamicField) SerializeConfig added in v0.5.0

func (df *DynamicField) SerializeConfig() error

SerializeConfig serializes DynamicFieldConfig to YAML for storage.

func (*DynamicField) Validate added in v0.5.0

func (df *DynamicField) Validate() error

Validate checks the dynamic field for errors.

type DynamicFieldConfig added in v0.5.0

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"`
}

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

type DynamicFieldDisplay added in v0.5.0

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 added in v0.5.0

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

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

type DynamicFieldFilter added in v0.5.0

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 added in v0.5.0

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 added in v0.5.0

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 added in v0.5.0

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

GetScreenConfigForField gets all screen configs for a single field.

func GetScreenConfigForScreen added in v0.5.0

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

GetScreenConfigForScreen gets all field configs for a specific screen.

type DynamicFieldValue added in v0.5.0

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 added in v0.5.0

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

GetDynamicFieldValues retrieves all values for an object.

type EndpointContract added in v0.5.0

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 added in v0.5.0

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 added in v0.5.0

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 EscalationResult

type EscalationResult struct {
	ShouldEscalate  bool     `json:"should_escalate"`
	EscalationLevel string   `json:"escalation_level"`
	NotifyList      []string `json:"notify_list"`
	Level           string   `json:"level"`
}

EscalationResult represents the result of escalation check.

type FieldWithScreenConfig added in v0.5.0

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

FieldWithScreenConfig pairs a field with its screen configuration value.

func GetFieldsForScreenWithConfig added in v0.5.0

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

GetFieldsForScreenWithConfig gets fields enabled for a specific screen.

type I18nHandlers

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

I18nHandlers handles internationalization-related requests.

func NewI18nHandlers

func NewI18nHandlers() *I18nHandlers

NewI18nHandlers creates new i18n handlers.

func (*I18nHandlers) ExportTranslations

func (h *I18nHandlers) ExportTranslations(c *gin.Context)

@Router /api/v1/i18n/export/{lang} [get].

func (*I18nHandlers) GetCurrentTranslations

func (h *I18nHandlers) GetCurrentTranslations(c *gin.Context)

@Router /api/v1/i18n/translations [get].

func (*I18nHandlers) GetLanguageStats

func (h *I18nHandlers) GetLanguageStats(c *gin.Context)

@Router /api/v1/i18n/stats [get].

func (*I18nHandlers) GetMissingTranslations

func (h *I18nHandlers) GetMissingTranslations(c *gin.Context)

@Router /api/v1/i18n/missing/{lang} [get].

func (*I18nHandlers) GetSupportedLanguages

func (h *I18nHandlers) GetSupportedLanguages(c *gin.Context)

@Router /api/v1/i18n/languages [get].

func (*I18nHandlers) GetTranslationCoverage

func (h *I18nHandlers) GetTranslationCoverage(c *gin.Context)

@Router /api/v1/i18n/coverage [get].

func (*I18nHandlers) GetTranslations

func (h *I18nHandlers) GetTranslations(c *gin.Context)

@Router /api/v1/i18n/translations/{lang} [get].

func (*I18nHandlers) RegisterRoutes

func (h *I18nHandlers) RegisterRoutes(router *gin.RouterGroup)

RegisterRoutes registers i18n routes.

func (*I18nHandlers) SetLanguage

func (h *I18nHandlers) SetLanguage(c *gin.Context)

@Router /api/v1/i18n/language [post].

func (*I18nHandlers) Translate

func (h *I18nHandlers) Translate(c *gin.Context)

@Router /api/v1/i18n/translate [post].

func (*I18nHandlers) ValidateTranslations

func (h *I18nHandlers) ValidateTranslations(c *gin.Context)

@Router /api/v1/i18n/validate/{lang} [get].

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
	IsPriority       bool       `json:"is_priority"`
	Category         string     `json:"category"`
	Mentions         []string   `json:"mentions"`
	HasMentions      bool       `json:"has_mentions"`
	HasTeamMention   bool       `json:"has_team_mention"`
	Attachments      []int      `json:"attachments"`
	IsEdited         bool       `json:"is_edited"`
	EditedAt         *time.Time `json:"edited_at,omitempty"`
	CreatedAt        time.Time  `json:"created_at"`
	UpdatedAt        time.Time  `json:"updated_at"`
}

InternalNote represents an internal note on a ticket.

type InternalNoteHandlers

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

InternalNoteHandlers manages internal note API endpoints.

func NewInternalNoteHandlers

func NewInternalNoteHandlers() *InternalNoteHandlers

NewInternalNoteHandlers creates a new internal note handlers instance.

func (*InternalNoteHandlers) CreateNote

func (h *InternalNoteHandlers) CreateNote(c *gin.Context)

CreateNote creates a new internal note.

func (*InternalNoteHandlers) CreateNoteFromTemplate

func (h *InternalNoteHandlers) CreateNoteFromTemplate(c *gin.Context)

CreateNoteFromTemplate creates a note from a template.

func (*InternalNoteHandlers) CreateTemplate

func (h *InternalNoteHandlers) CreateTemplate(c *gin.Context)

CreateTemplate creates a new template.

func (*InternalNoteHandlers) DeleteNote

func (h *InternalNoteHandlers) DeleteNote(c *gin.Context)

DeleteNote deletes a note.

func (*InternalNoteHandlers) DeleteTemplate

func (h *InternalNoteHandlers) DeleteTemplate(c *gin.Context)

DeleteTemplate deletes a template.

func (*InternalNoteHandlers) ExportNotes

func (h *InternalNoteHandlers) ExportNotes(c *gin.Context)

ExportNotes exports notes in various formats.

func (*InternalNoteHandlers) GetCategories

func (h *InternalNoteHandlers) GetCategories(c *gin.Context)

GetCategories retrieves all note categories.

func (*InternalNoteHandlers) GetEditHistory

func (h *InternalNoteHandlers) GetEditHistory(c *gin.Context)

GetEditHistory retrieves edit history for a note.

func (*InternalNoteHandlers) GetImportantNotes

func (h *InternalNoteHandlers) GetImportantNotes(c *gin.Context)

GetImportantNotes retrieves important notes for a ticket.

func (*InternalNoteHandlers) GetNoteByID

func (h *InternalNoteHandlers) GetNoteByID(c *gin.Context)

GetNoteByID retrieves a specific note.

func (*InternalNoteHandlers) GetNoteStatistics

func (h *InternalNoteHandlers) GetNoteStatistics(c *gin.Context)

GetNoteStatistics retrieves statistics for notes.

func (*InternalNoteHandlers) GetNotes

func (h *InternalNoteHandlers) GetNotes(c *gin.Context)

GetNotes retrieves all notes for a ticket.

func (*InternalNoteHandlers) GetPinnedNotes

func (h *InternalNoteHandlers) GetPinnedNotes(c *gin.Context)

GetPinnedNotes retrieves pinned notes for a ticket.

func (*InternalNoteHandlers) GetRecentActivity

func (h *InternalNoteHandlers) GetRecentActivity(c *gin.Context)

GetRecentActivity retrieves recent activity.

func (*InternalNoteHandlers) GetTemplates

func (h *InternalNoteHandlers) GetTemplates(c *gin.Context)

GetTemplates retrieves all note templates.

func (*InternalNoteHandlers) MarkImportant

func (h *InternalNoteHandlers) MarkImportant(c *gin.Context)

MarkImportant marks or unmarks a note as important.

func (*InternalNoteHandlers) PinNote

func (h *InternalNoteHandlers) PinNote(c *gin.Context)

PinNote pins or unpins a note.

func (*InternalNoteHandlers) SearchNotes

func (h *InternalNoteHandlers) SearchNotes(c *gin.Context)

SearchNotes searches for notes.

func (*InternalNoteHandlers) UpdateNote

func (h *InternalNoteHandlers) UpdateNote(c *gin.Context)

UpdateNote updates an existing note.

func (*InternalNoteHandlers) UpdateTemplate

func (h *InternalNoteHandlers) UpdateTemplate(c *gin.Context)

UpdateTemplate updates a template.

type LanguageCoverage

type LanguageCoverage struct {
	Code           string  `json:"code"`
	Name           string  `json:"name"`
	TotalKeys      int     `json:"total_keys"`
	TranslatedKeys int     `json:"translated_keys"`
	MissingCount   int     `json:"missing_count"`
	Coverage       float64 `json:"coverage"`
}

LanguageCoverage represents coverage data for a language.

type LanguageInfo

type LanguageInfo struct {
	Code       string `json:"code"`
	Name       string `json:"name"`
	NativeName string `json:"native_name"`
	Active     bool   `json:"active"`
}

LanguageInfo represents information about a language.

type LanguageStats

type LanguageStats struct {
	Code       string  `json:"code"`
	Users      int     `json:"users"`
	Percentage float64 `json:"percentage"`
}

LanguageStats represents language usage statistics.

type LanguageStatsResponse

type LanguageStatsResponse struct {
	TotalUsers      int             `json:"total_users"`
	Languages       []LanguageStats `json:"languages"`
	DefaultLanguage string          `json:"default_language"`
}

LanguageStatsResponse represents language statistics response.

type LanguagesResponse

type LanguagesResponse struct {
	Languages []LanguageInfo `json:"languages"`
	Current   string         `json:"current"`
	Default   string         `json:"default"`
}

LanguagesResponse represents the response for supported languages.

type MergeHistory

type MergeHistory struct {
	Action      string    `json:"action"`       // "merged" or "unmerged"
	Tickets     []int     `json:"tickets"`      // Ticket IDs involved
	Reason      string    `json:"reason"`       // Reason for action
	PerformedBy string    `json:"performed_by"` // User who performed action
	PerformedAt time.Time `json:"performed_at"` // When action was performed
}

MergeHistory represents merge/unmerge history.

type MergeRequest

type MergeRequest struct {
	TicketIDs string `form:"ticket_ids"`
	Reason    string `form:"reason"`
}

MergeRequest represents a ticket merge request.

type MissingKey

type MissingKey struct {
	Key          string `json:"key"`
	EnglishValue string `json:"english_value"`
	Category     string `json:"category"`
}

MissingKey represents a missing translation key.

type MissingKeysResponse

type MissingKeysResponse struct {
	Language    string       `json:"language"`
	MissingKeys []MissingKey `json:"missing_keys"`
	Count       int          `json:"count"`
}

MissingKeysResponse represents missing keys response.

type NoteHistory

type NoteHistory struct {
	ID       int       `json:"id"`
	NoteID   int       `json:"note_id"`
	Content  string    `json:"content"`
	EditedBy int       `json:"edited_by"`
	EditedAt time.Time `json:"edited_at"`
	Version  int       `json:"version"`
}

NoteHistory represents edit history for a note.

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 QueueBasic added in v0.5.0

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

QueueBasic represents minimal queue info for display.

func GetSignatureQueues added in v0.5.0

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

GetSignatureQueues returns queues that use a specific signature.

type RegEx added in v0.5.0

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 SLA

type SLA struct {
	Status        string    `json:"status"` // within, warning, overdue
	Deadline      time.Time `json:"deadline"`
	PercentUsed   float64   `json:"percent_used"`
	TimeRemaining string    `json:"time_remaining"`
}

SLA represents Service Level Agreement status.

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 SavedSearch

type SavedSearch struct {
	ID        int       `json:"id"`
	UserID    int       `json:"user_id"`
	Name      string    `json:"name"`
	Query     string    `json:"query"`
	Filters   string    `json:"filters"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

SavedSearch represents a user's saved search.

type ScreenConfigMatrix added in v0.5.0

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 added in v0.5.0

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

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

type ScreenDefinition added in v0.5.0

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 added in v0.5.0

func GetScreenDefinitions() []ScreenDefinition

GetScreenDefinitions returns all screens that support dynamic fields.

type SearchHandlers

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

SearchHandlers handles search-related API endpoints.

func NewSearchHandlers

func NewSearchHandlers() *SearchHandlers

NewSearchHandlers creates new search handlers.

func (*SearchHandlers) AdvancedSearch

func (h *SearchHandlers) AdvancedSearch(c *gin.Context)

AdvancedSearch performs an advanced search with filters.

func (*SearchHandlers) DeleteTicketFromIndex

func (h *SearchHandlers) DeleteTicketFromIndex(c *gin.Context)

DeleteTicketFromIndex removes a ticket from the search index.

func (*SearchHandlers) ExecuteSavedSearch

func (h *SearchHandlers) ExecuteSavedSearch(c *gin.Context)

ExecuteSavedSearch executes a saved search.

func (*SearchHandlers) GetSavedSearch

func (h *SearchHandlers) GetSavedSearch(c *gin.Context)

GetSavedSearch retrieves a specific saved search.

func (*SearchHandlers) GetSavedSearches

func (h *SearchHandlers) GetSavedSearches(c *gin.Context)

GetSavedSearches retrieves saved searches for a user.

func (*SearchHandlers) GetSearchAnalytics

func (h *SearchHandlers) GetSearchAnalytics(c *gin.Context)

GetSearchAnalytics retrieves search analytics.

func (*SearchHandlers) GetSearchHistory

func (h *SearchHandlers) GetSearchHistory(c *gin.Context)

GetSearchHistory retrieves search history for a user.

func (*SearchHandlers) GetSearchSuggestions

func (h *SearchHandlers) GetSearchSuggestions(c *gin.Context)

GetSearchSuggestions provides search suggestions.

func (*SearchHandlers) IndexTicket

func (h *SearchHandlers) IndexTicket(c *gin.Context)

IndexTicket indexes a single ticket.

func (*SearchHandlers) ReindexTickets

func (h *SearchHandlers) ReindexTickets(c *gin.Context)

ReindexTickets triggers reindexing of all tickets.

func (*SearchHandlers) SaveSearch

func (h *SearchHandlers) SaveSearch(c *gin.Context)

SaveSearch saves a search query.

func (*SearchHandlers) SearchTickets

func (h *SearchHandlers) SearchTickets(c *gin.Context)

SearchTickets performs a ticket search.

func (*SearchHandlers) UpdateTicketIndex

func (h *SearchHandlers) UpdateTicketIndex(c *gin.Context)

UpdateTicketIndex updates a ticket in the search index.

type SearchHistory

type SearchHistory struct {
	ID        int       `json:"id"`
	UserID    int       `json:"user_id"`
	Query     string    `json:"query"`
	Name      string    `json:"name,omitempty"`
	Results   int       `json:"results"`
	CreatedAt time.Time `json:"created_at"`
}

SearchHistory represents a user's search history entry.

type SearchResult

type SearchResult struct {
	ID           int               `json:"id"`
	Subject      string            `json:"subject"`
	Body         string            `json:"body"`
	Status       string            `json:"status"`
	Priority     string            `json:"priority"`
	QueueID      int               `json:"queue_id"`
	QueueName    string            `json:"queue_name"`
	AssigneeID   int               `json:"assignee_id"`
	AssigneeName string            `json:"assignee_name"`
	CreatedAt    time.Time         `json:"created_at"`
	UpdatedAt    time.Time         `json:"updated_at"`
	Score        float64           `json:"score"`
	Highlights   map[string]string `json:"highlights,omitempty"`
}

SearchResult represents a search result item.

type SearchableDynamicField added in v0.5.0

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

SearchableDynamicField wraps DynamicField with template-friendly Options.

func GetFieldsForSearch added in v0.5.0

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 SetLanguageRequest

type SetLanguageRequest struct {
	Language string `json:"language" binding:"required"`
}

SetLanguageRequest represents a request to set language.

type Signature added in v0.5.0

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 added in v0.5.0

func GetAllSignatures() ([]Signature, error)

GetAllSignatures returns all valid signatures for dropdowns.

func GetSignature added in v0.5.0

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

GetSignature returns a single signature by ID.

type SignatureExportData added in v0.5.0

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 added in v0.5.0

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 added in v0.5.0

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

GetQueueSignature returns the signature assigned to a specific queue.

type SignatureWithStats added in v0.5.0

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

SignatureWithStats includes queue usage statistics.

func ListSignatures added in v0.5.0

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 added in v0.5.0

func ListStandardAttachments() ([]StandardAttachment, error)

ListStandardAttachments returns all valid standard attachments.

type StandardTemplate added in v0.5.0

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 added in v0.5.0

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

GetStandardTemplate retrieves a single template by ID.

type StandardTemplateWithStats added in v0.5.0

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

StandardTemplateWithStats includes usage statistics.

func ListStandardTemplates added in v0.5.0

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 added in v0.5.0

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 added in v0.5.0

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

ExportTemplate exports a single template with its relationships.

type TemplateExportFile added in v0.5.0

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 added in v0.5.0

func ExportAllTemplates() (*TemplateExportFile, error)

ExportAllTemplates exports all templates.

type TemplateForAgent added in v0.5.0

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 added in v0.5.0

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

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

type TemplateTypeOption added in v0.5.0

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

TemplateTypeOption represents a template type for UI selection.

func ValidTemplateTypes added in v0.5.0

func ValidTemplateTypes() []TemplateTypeOption

ValidTemplateTypes returns all available template types matching Znuny.

type TestConfig

type TestConfig struct {
	UserLogin     string
	UserFirstName string
	UserLastName  string
	UserEmail     string
	UserGroups    []string
	QueueName     string
	GroupName     string
	CompanyName   string
}

GetTestConfig returns test configuration from environment variables with safe defaults.

func GetTestConfig

func GetTestConfig() TestConfig

GetTestConfig retrieves parameterized test configuration.

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 TranslateRequest

type TranslateRequest struct {
	Key      string        `json:"key" binding:"required"`
	Language string        `json:"language"`
	Args     []interface{} `json:"args"`
}

TranslateRequest represents a request to translate a key.

type TranslateResponse

type TranslateResponse struct {
	Key         string `json:"key"`
	Translation string `json:"translation"`
	Language    string `json:"language"`
}

TranslateResponse represents the response for translation.

type TranslationsResponse

type TranslationsResponse struct {
	Language     string                 `json:"language"`
	Translations map[string]interface{} `json:"translations"`
}

TranslationsResponse represents the response for translations.

type UnmergeRequest

type UnmergeRequest struct {
	Reason string `form:"reason"`
}

UnmergeRequest represents a ticket unmerge request.

type UpdateUserRequest

type UpdateUserRequest struct {
	Email     *string `json:"email"`
	FirstName *string `json:"first_name"`
	LastName  *string `json:"last_name"`
	Password  *string `json:"password"`
	ValidID   *int    `json:"valid_id"`
}

UpdateUserRequest represents the request to update a user.

type ValidationResponse

type ValidationResponse struct {
	Language   string   `json:"language"`
	IsValid    bool     `json:"is_valid"`
	IsComplete bool     `json:"is_complete"`
	Coverage   float64  `json:"coverage"`
	Errors     []string `json:"errors"`
	Warnings   []string `json:"warnings"`
}

ValidationResponse represents translation validation response.

Source Files

Directories

Path Synopsis
Package api provides shared API handlers and utilities for article creation.
Package api provides shared API handlers and utilities for article creation.

Jump to

Keyboard shortcuts

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