package api import ( "git.aiterp.net/stufflog3/stufflog3-api/internal/auth" "git.aiterp.net/stufflog3/stufflog3-api/internal/database" "git.aiterp.net/stufflog3/stufflog3-api/internal/models" "git.aiterp.net/stufflog3/stufflog3-api/internal/slerrors" "github.com/gin-gonic/gin" "time" ) func Items(g *gin.RouterGroup, db database.Database) { g.Use(scopeIDMiddleware(db)) g.GET("/", handler("items", func(c *gin.Context) (res interface{}, err error) { mode := c.Query("mode") var fromTime, toTime time.Time var fromDate, toDate models.Date if mode != "scheduled" { if c.Query("to") == "" { toTime = time.Now() } else { toTime, err = time.Parse(time.RFC3339Nano, c.Query("to")) if err != nil { return nil, slerrors.BadRequest("Invalid value for to") } } if c.Query("from") == "" { fromTime = time.Now().Add(-time.Hour * 24 * 30) } else { fromTime, err = time.Parse(time.RFC3339Nano, c.Query("from")) if err != nil { return nil, slerrors.BadRequest("Invalid value for from") } } if toTime.After(toTime) { return nil, slerrors.BadRequest("the flow of time itself is convoluted, but not so for the database") } } else { if c.Query("to") == "" { toDate, _ = models.ParseDate(time.Now().Add(time.Hour * 24 * 7).Format("2006-01-02")) } else { toDate, err = models.ParseDate(c.Query("to")) if err != nil { return nil, slerrors.BadRequest("Invalid value for to") } } if c.Query("from") == "" { fromDate, _ = models.ParseDate(time.Now().Format("2006-01-02")) } else { fromDate, err = models.ParseDate(c.Query("from")) if err != nil { return nil, slerrors.BadRequest("Invalid value for from") } } } switch mode { case "", "created": return db.Items(getScope(c).ID).ListCreated(c.Request.Context(), fromTime, toTime) case "acquired": return db.Items(getScope(c).ID).ListAcquired(c.Request.Context(), fromTime, toTime) case "loose": return db.Items(getScope(c).ID).ListLoose(c.Request.Context(), fromTime, toTime) case "scheduled": return db.Items(getScope(c).ID).ListScheduled(c.Request.Context(), fromDate, toDate) default: return nil, slerrors.BadRequest("unknown mode") } })) g.GET("/:id", handler("item", func(c *gin.Context) (interface{}, error) { id, err := reqInt(c, "id") if err != nil { return nil, err } return db.Items(getScope(c).ID).Find(c.Request.Context(), id) })) g.POST("/", handler("item", func(c *gin.Context) (interface{}, error) { item := &models.Item{} err := c.BindJSON(item) if err != nil { return nil, slerrors.BadRequest("Invalid JSON input: " + err.Error()) } item.CreatedTime = time.Now() if item.Name == "" { return nil, slerrors.BadRequest("Blank item name not allowed") } if item.OwnerID == "" { item.OwnerID = auth.UserID(c) } else if getScope(c).HasMember(item.OwnerID) { return nil, slerrors.Forbidden("Owner is not part of scope.") } for _, stat := range item.Stats { if getScope(c).Stat(stat.ID) == nil { return nil, slerrors.Forbidden("One or more stats are not part of scope.") } if stat.Acquired == 0 && stat.Required == 0 { return nil, slerrors.BadRequest("0/0 stats are not allowed when creating items.") } } return db.Items(getScope(c).ID).Create(c.Request.Context(), *item) })) g.PUT("/:id", handler("item", func(c *gin.Context) (interface{}, error) { id, err := reqInt(c, "id") if err != nil { return nil, err } update := &models.ItemUpdate{} err = c.BindJSON(update) if err != nil { return nil, slerrors.BadRequest("Invalid JSON input: " + err.Error()) } item, err := db.Items(getScope(c).ID).Find(c.Request.Context(), id) if err != nil { return nil, err } if update.Name != nil && *update.Name == "" { return nil, slerrors.BadRequest("Blank item name not allowed") } if update.OwnerID != nil || !getScope(c).HasMember(item.OwnerID) { return nil, slerrors.Forbidden("New item is not part of scope.") } for _, stat := range update.Stats { statRef := getScope(c).Stat(stat.ID) if stat.Required != 0 && stat.Acquired != 0 && statRef == nil { return nil, slerrors.Forbidden("One or more stats are not part of the scope.") } if !statRef.AllowsAmount(stat.Required) { return nil, slerrors.Forbidden("One or more stats have a disallowed required amount.") } } return db.Items(getScope(c).ID).Update(c.Request.Context(), *item, *update) })) g.DELETE("/:id", handler("item", func(c *gin.Context) (interface{}, error) { id, err := reqInt(c, "id") if err != nil { return nil, err } item, err := db.Items(getScope(c).ID).Find(c.Request.Context(), id) if err != nil { return nil, err } err = db.Items(getScope(c).ID).Delete(c.Request.Context(), *item) if err != nil { return nil, err } return item, nil })) }