updates
This commit is contained in:
25
server/internal/handlers/category_handler.go
Normal file
25
server/internal/handlers/category_handler.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"go-server/internal/service"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type CategoryController struct {
|
||||
categoryService *service.CategoryService
|
||||
}
|
||||
|
||||
func NewCategoryController(categoryService *service.CategoryService) *CategoryController {
|
||||
return &CategoryController{categoryService: categoryService}
|
||||
}
|
||||
|
||||
func (h *CategoryController) GetAll(c *gin.Context) {
|
||||
categories, err := h.categoryService.GetAll(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, categories)
|
||||
}
|
||||
93
server/internal/handlers/daily_overview_handler.go
Normal file
93
server/internal/handlers/daily_overview_handler.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"go-server/internal/service"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type DailyOverviewController struct {
|
||||
service *service.DailyOverviewService
|
||||
}
|
||||
|
||||
func NewDailyOverviewController(service *service.DailyOverviewService) *DailyOverviewController {
|
||||
return &DailyOverviewController{
|
||||
service: service,
|
||||
}
|
||||
}
|
||||
|
||||
// GetDailyOverview returns comprehensive daily nutrient overview
|
||||
// GET /api/daily-overview/overview
|
||||
func (h *DailyOverviewController) GetDailyOverview(c *gin.Context) {
|
||||
overview, err := h.service.GetDailyOverview(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get daily overview"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, overview)
|
||||
}
|
||||
|
||||
// GetNutrientTotals returns just the aggregated nutrient totals
|
||||
// GET /api/daily-overview/totals
|
||||
func (h *DailyOverviewController) GetNutrientTotals(c *gin.Context) {
|
||||
totals, err := h.service.GetNutrientTotals(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get nutrient totals"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, totals)
|
||||
}
|
||||
|
||||
// GetSupplementBreakdown returns detailed breakdown by supplement
|
||||
// GET /api/daily-overview/breakdown
|
||||
func (h *DailyOverviewController) GetSupplementBreakdown(c *gin.Context) {
|
||||
breakdown, err := h.service.GetSupplementBreakdown(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get supplement breakdown"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, breakdown)
|
||||
}
|
||||
|
||||
// ExecuteCustomQuery allows executing custom SQL queries via API
|
||||
// POST /api/daily-overview/query
|
||||
// Body: {"query": "SELECT ...", "args": [...]}
|
||||
func (h *DailyOverviewController) ExecuteCustomQuery(c *gin.Context) {
|
||||
var request struct {
|
||||
Query string `json:"query"`
|
||||
Args []interface{} `json:"args"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
if request.Query == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Query is required"})
|
||||
return
|
||||
}
|
||||
|
||||
// Basic security: only allow SELECT statements
|
||||
queryUpper := strings.ToUpper(strings.TrimSpace(request.Query))
|
||||
if !strings.HasPrefix(queryUpper, "SELECT") {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Only SELECT queries are allowed"})
|
||||
return
|
||||
}
|
||||
|
||||
results, err := h.service.ExecuteCustomQuery(c.Request.Context(), request.Query, request.Args...)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to execute query"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"results": results,
|
||||
"count": len(results),
|
||||
})
|
||||
}
|
||||
26
server/internal/handlers/nutrient_handler.go
Normal file
26
server/internal/handlers/nutrient_handler.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"go-server/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type NutrientController struct {
|
||||
service *service.NutrientService
|
||||
}
|
||||
|
||||
func NewNutrientController(service *service.NutrientService) *NutrientController {
|
||||
return &NutrientController{service}
|
||||
}
|
||||
|
||||
func (c *NutrientController) GetAll(ctx *gin.Context) {
|
||||
nutrients, err := c.service.GetAll(ctx)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, nutrients)
|
||||
}
|
||||
35
server/internal/handlers/supplement_handler.go
Normal file
35
server/internal/handlers/supplement_handler.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"go-server/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type SupplementController struct {
|
||||
service *service.SupplementService
|
||||
}
|
||||
|
||||
func NewSupplementController(service *service.SupplementService) *SupplementController {
|
||||
return &SupplementController{service}
|
||||
}
|
||||
|
||||
func (c *SupplementController) GetAll(ctx *gin.Context) {
|
||||
supplements, err := c.service.GetAll(ctx)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, supplements)
|
||||
}
|
||||
|
||||
func (c *SupplementController) GetDailySupplementsOverview(ctx *gin.Context) {
|
||||
supplements, err := c.service.GetDailySupplementsOverview(ctx)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, supplements)
|
||||
}
|
||||
269
server/internal/handlers/todo_handler.go
Normal file
269
server/internal/handlers/todo_handler.go
Normal file
@@ -0,0 +1,269 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"go-server/internal/models"
|
||||
"go-server/internal/service"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type TodoController struct {
|
||||
service *service.TodoService
|
||||
}
|
||||
|
||||
func NewTodoController(service *service.TodoService) *TodoController {
|
||||
return &TodoController{
|
||||
service: service,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTodo creates a new todo
|
||||
// POST /api/todo/create
|
||||
func (h *TodoController) CreateTodo(c *gin.Context) {
|
||||
userID := getUserIDFromContext(c) // You'll need to implement this helper
|
||||
if userID.IsZero() {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
var req service.CreateTodoRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
todo, err := h.service.CreateTodo(c.Request.Context(), userID, req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create todo"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, todo)
|
||||
}
|
||||
|
||||
// GetTodos gets all todos with stats for the authenticated user
|
||||
// GET /api/todo/list
|
||||
func (h *TodoController) GetTodos(c *gin.Context) {
|
||||
userID := getUserIDFromContext(c)
|
||||
if userID.IsZero() {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
todos, err := h.service.GetTodosWithStats(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get todos"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, todos)
|
||||
}
|
||||
|
||||
// GetTodaysSummary gets today's todo summary
|
||||
// GET /api/todo/today
|
||||
func (h *TodoController) GetTodaysSummary(c *gin.Context) {
|
||||
userID := getUserIDFromContext(c)
|
||||
if userID.IsZero() {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
summary, err := h.service.GetTodaysSummary(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get today's summary"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, summary)
|
||||
}
|
||||
|
||||
// UpdateTodo updates a todo
|
||||
// PUT /api/todo/:id
|
||||
func (h *TodoController) UpdateTodo(c *gin.Context) {
|
||||
userID := getUserIDFromContext(c)
|
||||
if userID.IsZero() {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
todoIDStr := c.Param("id")
|
||||
todoID, err := models.ParseULID(todoIDStr)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid todo ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var req service.UpdateTodoRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
todo, err := h.service.UpdateTodo(c.Request.Context(), todoID, userID, req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update todo"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, todo)
|
||||
}
|
||||
|
||||
// DeleteTodo deletes a todo
|
||||
// DELETE /api/todo/:id
|
||||
func (h *TodoController) DeleteTodo(c *gin.Context) {
|
||||
userID := getUserIDFromContext(c)
|
||||
if userID.IsZero() {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
todoIDStr := c.Param("id")
|
||||
todoID, err := models.ParseULID(todoIDStr)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid todo ID"})
|
||||
return
|
||||
}
|
||||
|
||||
err = h.service.DeleteTodo(c.Request.Context(), todoID, userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete todo"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Todo deleted successfully"})
|
||||
}
|
||||
|
||||
// CompleteTodo marks a todo as completed
|
||||
// POST /api/todo/:id/complete
|
||||
func (h *TodoController) CompleteTodo(c *gin.Context) {
|
||||
userID := getUserIDFromContext(c)
|
||||
if userID.IsZero() {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
todoIDStr := c.Param("id")
|
||||
todoID, err := models.ParseULID(todoIDStr)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid todo ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var req service.CompleteTodoRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
err = h.service.CompleteTodo(c.Request.Context(), todoID, userID, req)
|
||||
if err != nil {
|
||||
if err.Error() == "todo already completed today" {
|
||||
c.JSON(http.StatusConflict, gin.H{"error": "Todo already completed today"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to complete todo"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Todo completed successfully"})
|
||||
}
|
||||
|
||||
// GetActivityLog gets the user's activity log
|
||||
// GET /api/todo/activity
|
||||
func (h *TodoController) GetActivityLog(c *gin.Context) {
|
||||
userID := getUserIDFromContext(c)
|
||||
if userID.IsZero() {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
// Parse pagination parameters
|
||||
limitStr := c.DefaultQuery("limit", "50")
|
||||
offsetStr := c.DefaultQuery("offset", "0")
|
||||
|
||||
limit, err := strconv.Atoi(limitStr)
|
||||
if err != nil {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
offset, err := strconv.Atoi(offsetStr)
|
||||
if err != nil {
|
||||
offset = 0
|
||||
}
|
||||
|
||||
activities, err := h.service.GetActivityLog(c.Request.Context(), userID, limit, offset)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get activity log"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, activities)
|
||||
}
|
||||
|
||||
// GetActivityLogByDate gets activity log for a specific date
|
||||
// GET /api/todo/activity/:date
|
||||
func (h *TodoController) GetActivityLogByDate(c *gin.Context) {
|
||||
userID := getUserIDFromContext(c)
|
||||
if userID.IsZero() {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
dateStr := c.Param("date")
|
||||
date, err := time.Parse("2006-01-02", dateStr)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid date format. Use YYYY-MM-DD"})
|
||||
return
|
||||
}
|
||||
|
||||
activities, err := h.service.GetActivityLogByDate(c.Request.Context(), userID, date)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get activity log"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, activities)
|
||||
}
|
||||
|
||||
// GetWeeklySummary gets a weekly summary of todo completions
|
||||
// GET /api/todo/weekly
|
||||
func (h *TodoController) GetWeeklySummary(c *gin.Context) {
|
||||
userID := getUserIDFromContext(c)
|
||||
if userID.IsZero() {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
summary, err := h.service.GetWeeklySummary(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get weekly summary"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, summary)
|
||||
}
|
||||
|
||||
// Helper function to get user ID from context
|
||||
// You'll need to implement this based on your authentication middleware
|
||||
func getUserIDFromContext(c *gin.Context) models.ULID {
|
||||
testUserId := models.MustParseULID("01K54QBS528HKQDF985XNW2J6R")
|
||||
return testUserId
|
||||
// This is a placeholder - implement based on your auth system
|
||||
// For example, if you store the user in context after JWT validation:
|
||||
|
||||
// userInterface, exists := c.Get("user")
|
||||
// if !exists {
|
||||
// return models.ULID{} // Return zero value
|
||||
// }
|
||||
|
||||
// user, ok := userInterface.(*models.User)
|
||||
// if !ok {
|
||||
// return models.ULID{} // Return zero value
|
||||
// }
|
||||
|
||||
// return user.ID
|
||||
}
|
||||
|
||||
81
server/internal/handlers/user_handler.go
Normal file
81
server/internal/handlers/user_handler.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"go-server/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type AdminController struct {
|
||||
service *service.UserService
|
||||
}
|
||||
|
||||
func NewAdminController(service *service.UserService) *AdminController {
|
||||
return &AdminController{service}
|
||||
}
|
||||
|
||||
func (uc *AdminController) GetUserByID(c *gin.Context) {
|
||||
id := uuid.MustParse(c.Param("id"))
|
||||
user, err := uc.service.GetByID(c, id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, user)
|
||||
}
|
||||
|
||||
func (uc *AdminController) GetUsers(c *gin.Context) {
|
||||
users, err := uc.service.GetAll(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, users)
|
||||
}
|
||||
|
||||
func (uc *AdminController) CreateUser(c *gin.Context) {
|
||||
var input service.CreateUserInput
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := uc.service.Create(c, input)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, user)
|
||||
}
|
||||
|
||||
func (uc *AdminController) DeleteUser(c *gin.Context) {
|
||||
id := uuid.MustParse(c.Param("id"))
|
||||
err := uc.service.Delete(c, id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "User deleted successfully"})
|
||||
}
|
||||
|
||||
func (uc *AdminController) ChangePassword(c *gin.Context) {
|
||||
id := uuid.MustParse(c.Param("id"))
|
||||
var input service.ChangePasswordInput
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
|
||||
return
|
||||
}
|
||||
err := uc.service.ChangePassword(c, id, input)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Password changed successfully"})
|
||||
}
|
||||
Reference in New Issue
Block a user