mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-18 00:00:12 +01:00
321 lines
8 KiB
Go
321 lines
8 KiB
Go
|
|
package handlers
|
||
|
|
|
||
|
|
import (
|
||
|
|
"net/http"
|
||
|
|
"strconv"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/TracksApp/tracks/internal/middleware"
|
||
|
|
"github.com/TracksApp/tracks/internal/models"
|
||
|
|
"github.com/TracksApp/tracks/internal/services"
|
||
|
|
"github.com/gin-gonic/gin"
|
||
|
|
)
|
||
|
|
|
||
|
|
// TodoHandler handles todo endpoints
|
||
|
|
type TodoHandler struct {
|
||
|
|
todoService *services.TodoService
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewTodoHandler creates a new TodoHandler
|
||
|
|
func NewTodoHandler(todoService *services.TodoService) *TodoHandler {
|
||
|
|
return &TodoHandler{
|
||
|
|
todoService: todoService,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ListTodos handles GET /api/todos
|
||
|
|
func (h *TodoHandler) ListTodos(c *gin.Context) {
|
||
|
|
userID, err := middleware.GetCurrentUserID(c)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Not authenticated"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// Parse filters from query parameters
|
||
|
|
filter := services.ListTodosFilter{
|
||
|
|
IncludeTags: c.Query("include_tags") == "true",
|
||
|
|
}
|
||
|
|
|
||
|
|
if state := c.Query("state"); state != "" {
|
||
|
|
filter.State = models.TodoState(state)
|
||
|
|
}
|
||
|
|
|
||
|
|
if contextIDStr := c.Query("context_id"); contextIDStr != "" {
|
||
|
|
if contextID, err := strconv.ParseUint(contextIDStr, 10, 32); err == nil {
|
||
|
|
id := uint(contextID)
|
||
|
|
filter.ContextID = &id
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if projectIDStr := c.Query("project_id"); projectIDStr != "" {
|
||
|
|
if projectID, err := strconv.ParseUint(projectIDStr, 10, 32); err == nil {
|
||
|
|
id := uint(projectID)
|
||
|
|
filter.ProjectID = &id
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if tagName := c.Query("tag"); tagName != "" {
|
||
|
|
filter.TagName = &tagName
|
||
|
|
}
|
||
|
|
|
||
|
|
if c.Query("starred") == "true" {
|
||
|
|
starred := true
|
||
|
|
filter.Starred = &starred
|
||
|
|
}
|
||
|
|
|
||
|
|
if c.Query("overdue") == "true" {
|
||
|
|
overdue := true
|
||
|
|
filter.Overdue = &overdue
|
||
|
|
}
|
||
|
|
|
||
|
|
if c.Query("due_today") == "true" {
|
||
|
|
dueToday := true
|
||
|
|
filter.DueToday = &dueToday
|
||
|
|
}
|
||
|
|
|
||
|
|
if c.Query("show_from") == "true" {
|
||
|
|
showFrom := true
|
||
|
|
filter.ShowFrom = &showFrom
|
||
|
|
}
|
||
|
|
|
||
|
|
todos, err := h.todoService.GetTodos(userID, filter)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, todos)
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetTodo handles GET /api/todos/:id
|
||
|
|
func (h *TodoHandler) GetTodo(c *gin.Context) {
|
||
|
|
userID, err := middleware.GetCurrentUserID(c)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Not authenticated"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
todoID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid todo ID"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
todo, err := h.todoService.GetTodo(userID, uint(todoID))
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, todo)
|
||
|
|
}
|
||
|
|
|
||
|
|
// CreateTodo handles POST /api/todos
|
||
|
|
func (h *TodoHandler) CreateTodo(c *gin.Context) {
|
||
|
|
userID, err := middleware.GetCurrentUserID(c)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Not authenticated"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
var req services.CreateTodoRequest
|
||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
todo, err := h.todoService.CreateTodo(userID, req)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusCreated, todo)
|
||
|
|
}
|
||
|
|
|
||
|
|
// UpdateTodo handles PUT /api/todos/:id
|
||
|
|
func (h *TodoHandler) UpdateTodo(c *gin.Context) {
|
||
|
|
userID, err := middleware.GetCurrentUserID(c)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Not authenticated"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
todoID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid todo ID"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
var req services.UpdateTodoRequest
|
||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
todo, err := h.todoService.UpdateTodo(userID, uint(todoID), req)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, todo)
|
||
|
|
}
|
||
|
|
|
||
|
|
// DeleteTodo handles DELETE /api/todos/:id
|
||
|
|
func (h *TodoHandler) DeleteTodo(c *gin.Context) {
|
||
|
|
userID, err := middleware.GetCurrentUserID(c)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Not authenticated"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
todoID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid todo ID"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := h.todoService.DeleteTodo(userID, uint(todoID)); err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, gin.H{"message": "Todo deleted"})
|
||
|
|
}
|
||
|
|
|
||
|
|
// CompleteTodo handles POST /api/todos/:id/complete
|
||
|
|
func (h *TodoHandler) CompleteTodo(c *gin.Context) {
|
||
|
|
userID, err := middleware.GetCurrentUserID(c)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Not authenticated"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
todoID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid todo ID"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
todo, err := h.todoService.CompleteTodo(userID, uint(todoID))
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, todo)
|
||
|
|
}
|
||
|
|
|
||
|
|
// ActivateTodo handles POST /api/todos/:id/activate
|
||
|
|
func (h *TodoHandler) ActivateTodo(c *gin.Context) {
|
||
|
|
userID, err := middleware.GetCurrentUserID(c)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Not authenticated"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
todoID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid todo ID"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
todo, err := h.todoService.ActivateTodo(userID, uint(todoID))
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, todo)
|
||
|
|
}
|
||
|
|
|
||
|
|
// DeferTodo handles POST /api/todos/:id/defer
|
||
|
|
func (h *TodoHandler) DeferTodo(c *gin.Context) {
|
||
|
|
userID, err := middleware.GetCurrentUserID(c)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Not authenticated"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
todoID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid todo ID"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
var req struct {
|
||
|
|
ShowFrom time.Time `json:"show_from" binding:"required"`
|
||
|
|
}
|
||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
todo, err := h.todoService.DeferTodo(userID, uint(todoID), req.ShowFrom)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, todo)
|
||
|
|
}
|
||
|
|
|
||
|
|
// AddDependency handles POST /api/todos/:id/dependencies
|
||
|
|
func (h *TodoHandler) AddDependency(c *gin.Context) {
|
||
|
|
userID, err := middleware.GetCurrentUserID(c)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Not authenticated"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
predecessorID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid todo ID"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
var req struct {
|
||
|
|
SuccessorID uint `json:"successor_id" binding:"required"`
|
||
|
|
}
|
||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := h.todoService.AddDependency(userID, uint(predecessorID), req.SuccessorID); err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusCreated, gin.H{"message": "Dependency added"})
|
||
|
|
}
|
||
|
|
|
||
|
|
// RemoveDependency handles DELETE /api/todos/:id/dependencies/:successor_id
|
||
|
|
func (h *TodoHandler) RemoveDependency(c *gin.Context) {
|
||
|
|
userID, err := middleware.GetCurrentUserID(c)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Not authenticated"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
predecessorID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid todo ID"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
successorID, err := strconv.ParseUint(c.Param("successor_id"), 10, 32)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid successor ID"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := h.todoService.RemoveDependency(userID, uint(predecessorID), uint(successorID)); err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, gin.H{"message": "Dependency removed"})
|
||
|
|
}
|