From c42808f38d546913c5a5c4321e46ffed4c9fffe8 Mon Sep 17 00:00:00 2001 From: rxdn <29165304+rxdn@users.noreply.github.com> Date: Sat, 16 Nov 2024 20:29:56 +0000 Subject: [PATCH] Refactoring --- app/http/endpoints/api/forms/createform.go | 20 ++-- app/http/endpoints/api/forms/deleteform.go | 24 ++-- app/http/endpoints/api/forms/getforms.go | 17 +-- app/http/endpoints/api/forms/updateform.go | 30 ++--- app/http/endpoints/api/forms/updateinputs.go | 55 ++++----- .../endpoints/api/panel/multipanelcreate.go | 39 +++---- .../endpoints/api/panel/multipaneldelete.go | 43 ++++--- .../endpoints/api/panel/multipanelupdate.go | 37 +++--- app/http/endpoints/api/panel/panelcreate.go | 32 ++--- app/http/endpoints/api/panel/paneldelete.go | 57 ++++----- app/http/endpoints/api/panel/panelupdate.go | 109 ++++++++++-------- app/http/endpoints/api/whitelabel/delete.go | 21 +++- .../whitelabelcreateinteractions.go | 18 +-- .../endpoints/api/whitelabel/whitelabelget.go | 37 +++--- .../api/whitelabel/whitelabelgeterrors.go | 13 ++- .../api/whitelabel/whitelabelgetguilds.go | 33 +++--- .../api/whitelabel/whitelabelpost.go | 68 ++++++----- .../api/whitelabel/whitelabelstatuspost.go | 26 +++-- go.mod | 2 +- go.sum | 4 + 20 files changed, 376 insertions(+), 309 deletions(-) diff --git a/app/http/endpoints/api/forms/createform.go b/app/http/endpoints/api/forms/createform.go index 55ca64b..f3f3c53 100644 --- a/app/http/endpoints/api/forms/createform.go +++ b/app/http/endpoints/api/forms/createform.go @@ -1,40 +1,42 @@ package forms import ( + "github.com/TicketsBot/GoPanel/app" dbclient "github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/utils" "github.com/TicketsBot/database" "github.com/gin-gonic/gin" + "net/http" ) type createFormBody struct { Title string `json:"title"` } -func CreateForm(ctx *gin.Context) { - guildId := ctx.Keys["guildid"].(uint64) +func CreateForm(c *gin.Context) { + guildId := c.Keys["guildid"].(uint64) var data createFormBody - if err := ctx.BindJSON(&data); err != nil { - ctx.JSON(400, utils.ErrorJson(err)) + if err := c.BindJSON(&data); err != nil { + c.JSON(400, utils.ErrorStr("Invalid request body")) return } if len(data.Title) > 45 { - ctx.JSON(400, utils.ErrorStr("Title is too long")) + c.JSON(400, utils.ErrorStr("Title is too long")) return } // 26^50 chance of collision customId, err := utils.RandString(30) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } - id, err := dbclient.Client.Forms.Create(ctx, guildId, data.Title, customId) + id, err := dbclient.Client.Forms.Create(c, guildId, data.Title, customId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -45,5 +47,5 @@ func CreateForm(ctx *gin.Context) { CustomId: customId, } - ctx.JSON(200, form) + c.JSON(200, form) } diff --git a/app/http/endpoints/api/forms/deleteform.go b/app/http/endpoints/api/forms/deleteform.go index 0f707e8..521f4e0 100644 --- a/app/http/endpoints/api/forms/deleteform.go +++ b/app/http/endpoints/api/forms/deleteform.go @@ -1,41 +1,43 @@ package forms import ( + "github.com/TicketsBot/GoPanel/app" dbclient "github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/utils" "github.com/gin-gonic/gin" + "net/http" "strconv" ) -func DeleteForm(ctx *gin.Context) { - guildId := ctx.Keys["guildid"].(uint64) +func DeleteForm(c *gin.Context) { + guildId := c.Keys["guildid"].(uint64) - formId, err := strconv.Atoi(ctx.Param("form_id")) + formId, err := strconv.Atoi(c.Param("form_id")) if err != nil { - ctx.JSON(400, utils.ErrorStr("Invalid form ID")) + c.JSON(400, utils.ErrorStr("Invalid form ID")) return } - form, ok, err := dbclient.Client.Forms.Get(ctx, formId) + form, ok, err := dbclient.Client.Forms.Get(c, formId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } if !ok { - ctx.JSON(404, utils.ErrorStr("Form not found")) + c.JSON(404, utils.ErrorStr("Form not found")) return } if form.GuildId != guildId { - ctx.JSON(403, utils.ErrorStr("Form does not belong to this guild")) + c.JSON(403, utils.ErrorStr("Form does not belong to this guild")) return } - if err := dbclient.Client.Forms.Delete(ctx, formId); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + if err := dbclient.Client.Forms.Delete(c, formId); err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } - ctx.JSON(200, utils.SuccessResponse) + c.JSON(200, utils.SuccessResponse) } diff --git a/app/http/endpoints/api/forms/getforms.go b/app/http/endpoints/api/forms/getforms.go index c635557..627296f 100644 --- a/app/http/endpoints/api/forms/getforms.go +++ b/app/http/endpoints/api/forms/getforms.go @@ -1,10 +1,11 @@ package forms import ( + "github.com/TicketsBot/GoPanel/app" dbclient "github.com/TicketsBot/GoPanel/database" - "github.com/TicketsBot/GoPanel/utils" "github.com/TicketsBot/database" "github.com/gin-gonic/gin" + "net/http" ) type embeddedForm struct { @@ -12,18 +13,18 @@ type embeddedForm struct { Inputs []database.FormInput `json:"inputs"` } -func GetForms(ctx *gin.Context) { - guildId := ctx.Keys["guildid"].(uint64) +func GetForms(c *gin.Context) { + guildId := c.Keys["guildid"].(uint64) - forms, err := dbclient.Client.Forms.GetForms(ctx, guildId) + forms, err := dbclient.Client.Forms.GetForms(c, guildId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } - inputs, err := dbclient.Client.FormInput.GetInputsForGuild(ctx, guildId) + inputs, err := dbclient.Client.FormInput.GetInputsForGuild(c, guildId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -40,5 +41,5 @@ func GetForms(ctx *gin.Context) { } } - ctx.JSON(200, data) + c.JSON(200, data) } diff --git a/app/http/endpoints/api/forms/updateform.go b/app/http/endpoints/api/forms/updateform.go index 4c24142..340478a 100644 --- a/app/http/endpoints/api/forms/updateform.go +++ b/app/http/endpoints/api/forms/updateform.go @@ -1,52 +1,54 @@ package forms import ( + "github.com/TicketsBot/GoPanel/app" dbclient "github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/utils" "github.com/gin-gonic/gin" + "net/http" "strconv" ) -func UpdateForm(ctx *gin.Context) { - guildId := ctx.Keys["guildid"].(uint64) +func UpdateForm(c *gin.Context) { + guildId := c.Keys["guildid"].(uint64) var data createFormBody - if err := ctx.BindJSON(&data); err != nil { - ctx.JSON(400, utils.ErrorJson(err)) + if err := c.BindJSON(&data); err != nil { + c.JSON(400, utils.ErrorJson(err)) return } if len(data.Title) > 45 { - ctx.JSON(400, utils.ErrorStr("Title is too long")) + c.JSON(400, utils.ErrorStr("Title is too long")) return } - formId, err := strconv.Atoi(ctx.Param("form_id")) + formId, err := strconv.Atoi(c.Param("form_id")) if err != nil { - ctx.JSON(400, utils.ErrorStr("Invalid form ID")) + c.JSON(400, utils.ErrorStr("Invalid form ID")) return } - form, ok, err := dbclient.Client.Forms.Get(ctx, formId) + form, ok, err := dbclient.Client.Forms.Get(c, formId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } if !ok { - ctx.JSON(404, utils.ErrorStr("Form not found")) + c.JSON(404, utils.ErrorStr("Form not found")) return } if form.GuildId != guildId { - ctx.JSON(403, utils.ErrorStr("Form does not belong to this guild")) + c.JSON(403, utils.ErrorStr("Form does not belong to this guild")) return } - if err := dbclient.Client.Forms.UpdateTitle(ctx, formId, data.Title); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + if err := dbclient.Client.Forms.UpdateTitle(c, formId, data.Title); err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } - ctx.JSON(200, utils.SuccessResponse) + c.JSON(200, utils.SuccessResponse) } diff --git a/app/http/endpoints/api/forms/updateinputs.go b/app/http/endpoints/api/forms/updateinputs.go index 18d3825..742770c 100644 --- a/app/http/endpoints/api/forms/updateinputs.go +++ b/app/http/endpoints/api/forms/updateinputs.go @@ -2,13 +2,16 @@ package forms import ( "context" + "errors" "fmt" + "github.com/TicketsBot/GoPanel/app" dbclient "github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/utils" "github.com/TicketsBot/database" "github.com/gin-gonic/gin" "github.com/go-playground/validator/v10" "github.com/rxdn/gdl/objects/interaction/component" + "net/http" "sort" "strconv" ) @@ -38,66 +41,66 @@ type ( var validate = validator.New() -func UpdateInputs(ctx *gin.Context) { - guildId := ctx.Keys["guildid"].(uint64) +func UpdateInputs(c *gin.Context) { + guildId := c.Keys["guildid"].(uint64) - formId, err := strconv.Atoi(ctx.Param("form_id")) + formId, err := strconv.Atoi(c.Param("form_id")) if err != nil { - ctx.JSON(400, utils.ErrorStr("Invalid form ID")) + c.JSON(400, utils.ErrorStr("Invalid form ID")) return } var data updateInputsBody - if err := ctx.BindJSON(&data); err != nil { - ctx.JSON(400, utils.ErrorJson(err)) + if err := c.BindJSON(&data); err != nil { + c.JSON(400, utils.ErrorJson(err)) return } if err := validate.Struct(data); err != nil { - validationErrors, ok := err.(validator.ValidationErrors) - if !ok { - ctx.JSON(500, utils.ErrorStr("An error occurred while validating the integration")) + var validationErrors validator.ValidationErrors + if !errors.As(err, &validationErrors) { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "An error occurred while validating the integration")) return } formatted := "Your input contained the following errors:\n" + utils.FormatValidationErrors(validationErrors) - ctx.JSON(400, utils.ErrorStr(formatted)) + c.JSON(400, utils.ErrorStr(formatted)) return } fieldCount := len(data.Create) + len(data.Update) if fieldCount <= 0 || fieldCount > 5 { - ctx.JSON(400, utils.ErrorStr("Forms must have between 1 and 5 inputs")) + c.JSON(400, utils.ErrorStr("Forms must have between 1 and 5 inputs")) return } // Verify form exists and is from the right guild - form, ok, err := dbclient.Client.Forms.Get(ctx, formId) + form, ok, err := dbclient.Client.Forms.Get(c, formId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } if !ok { - ctx.JSON(404, utils.ErrorStr("Form not found")) + c.JSON(404, utils.ErrorStr("Form not found")) return } if form.GuildId != guildId { - ctx.JSON(403, utils.ErrorStr("Form does not belong to this guild")) + c.JSON(403, utils.ErrorStr("Form does not belong to this guild")) return } - existingInputs, err := dbclient.Client.FormInput.GetInputs(ctx, formId) + existingInputs, err := dbclient.Client.FormInput.GetInputs(c, formId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } // Verify that the UPDATE inputs exist for _, input := range data.Update { if !utils.ExistsMap(existingInputs, input.Id, idMapper) { - ctx.JSON(400, utils.ErrorStr("Input (to be updated) not found")) + c.JSON(400, utils.ErrorStr("Input (to be updated) not found")) return } } @@ -105,7 +108,7 @@ func UpdateInputs(ctx *gin.Context) { // Verify that the DELETE inputs exist for _, id := range data.Delete { if !utils.ExistsMap(existingInputs, id, idMapper) { - ctx.JSON(400, utils.ErrorStr("Input (to be deleted) not found")) + c.JSON(400, utils.ErrorStr("Input (to be deleted) not found")) return } } @@ -113,7 +116,7 @@ func UpdateInputs(ctx *gin.Context) { // Ensure no overlap between DELETE and UPDATE for _, id := range data.Delete { if utils.ExistsMap(data.Update, id, idMapperBody) { - ctx.JSON(400, utils.ErrorStr("Delete and update overlap")) + c.JSON(400, utils.ErrorStr("Delete and update overlap")) return } } @@ -128,29 +131,29 @@ func UpdateInputs(ctx *gin.Context) { // Now verify that the contents match exactly if len(remainingExisting) != len(data.Update) { - ctx.JSON(400, utils.ErrorStr("All inputs must be included in the update array")) + c.JSON(400, utils.ErrorStr("All inputs must be included in the update array")) return } for _, input := range data.Update { if !utils.Exists(remainingExisting, input.Id) { - ctx.JSON(400, utils.ErrorStr("All inputs must be included in the update array")) + c.JSON(400, utils.ErrorStr("All inputs must be included in the update array")) return } } // Verify that the positions are unique, and are in ascending order if !arePositionsCorrect(data) { - ctx.JSON(400, utils.ErrorStr("Positions must be unique and in ascending order")) + c.JSON(400, utils.ErrorStr("Positions must be unique and in ascending order")) return } - if err := saveInputs(ctx, formId, data, existingInputs); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + if err := saveInputs(c, formId, data, existingInputs); err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } - ctx.Status(204) + c.Status(204) } func idMapper(input database.FormInput) int { diff --git a/app/http/endpoints/api/panel/multipanelcreate.go b/app/http/endpoints/api/panel/multipanelcreate.go index c8edf47..25da404 100644 --- a/app/http/endpoints/api/panel/multipanelcreate.go +++ b/app/http/endpoints/api/panel/multipanelcreate.go @@ -3,6 +3,7 @@ package api import ( "context" "errors" + "github.com/TicketsBot/GoPanel/app" "github.com/TicketsBot/GoPanel/botcontext" dbclient "github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/rpc" @@ -16,6 +17,7 @@ import ( "github.com/rxdn/gdl/objects/channel" "github.com/rxdn/gdl/rest/request" "golang.org/x/sync/errgroup" + "net/http" ) type multiPanelCreateData struct { @@ -36,48 +38,45 @@ func (d *multiPanelCreateData) IntoMessageData(isPremium bool) multiPanelMessage } } -func MultiPanelCreate(ctx *gin.Context) { - guildId := ctx.Keys["guildid"].(uint64) +func MultiPanelCreate(c *gin.Context) { + guildId := c.Keys["guildid"].(uint64) var data multiPanelCreateData - if err := ctx.ShouldBindJSON(&data); err != nil { - ctx.JSON(400, utils.ErrorJson(err)) + if err := c.ShouldBindJSON(&data); err != nil { + c.JSON(400, utils.ErrorJson(err)) return } if err := validate.Struct(data); err != nil { var validationErrors validator.ValidationErrors if ok := errors.As(err, &validationErrors); !ok { - ctx.JSON(500, utils.ErrorStr("An error occurred while validating the panel")) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "An error occurred while validating the panel")) return } formatted := "Your input contained the following errors:\n" + utils.FormatValidationErrors(validationErrors) - ctx.JSON(400, utils.ErrorStr(formatted)) + c.JSON(400, utils.ErrorStr(formatted)) return } // validate body & get sub-panels panels, err := data.doValidations(guildId) if err != nil { - ctx.JSON(400, utils.ErrorJson(err)) + c.JSON(400, utils.ErrorJson(err)) return } // get bot context botContext, err := botcontext.ContextForGuild(guildId) if err != nil { - ctx.JSON(500, gin.H{ - "success": false, - "error": err.Error(), - }) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } // get premium status - premiumTier, err := rpc.PremiumClient.GetTierByGuildId(ctx, guildId, true, botContext.Token, botContext.RateLimiter) + premiumTier, err := rpc.PremiumClient.GetTierByGuildId(c, guildId, true, botContext.Token, botContext.RateLimiter) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -86,9 +85,9 @@ func MultiPanelCreate(ctx *gin.Context) { if err != nil { var unwrapped request.RestError if errors.As(err, &unwrapped); unwrapped.StatusCode == 403 { - ctx.JSON(500, utils.ErrorJson(errors.New("I do not have permission to send messages in the provided channel"))) + c.JSON(http.StatusBadRequest, utils.ErrorJson(errors.New("I do not have permission to send messages in the provided channel"))) } else { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) } return @@ -107,9 +106,9 @@ func MultiPanelCreate(ctx *gin.Context) { }, } - multiPanel.Id, err = dbclient.Client.MultiPanels.Create(ctx, multiPanel) + multiPanel.Id, err = dbclient.Client.MultiPanels.Create(c, multiPanel) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -118,16 +117,16 @@ func MultiPanelCreate(ctx *gin.Context) { panel := panel group.Go(func() error { - return dbclient.Client.MultiPanelTargets.Insert(ctx, multiPanel.Id, panel.PanelId) + return dbclient.Client.MultiPanelTargets.Insert(c, multiPanel.Id, panel.PanelId) }) } if err := group.Wait(); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } - ctx.JSON(200, gin.H{ + c.JSON(200, gin.H{ "success": true, "data": multiPanel, }) diff --git a/app/http/endpoints/api/panel/multipaneldelete.go b/app/http/endpoints/api/panel/multipaneldelete.go index 3234488..7b98a46 100644 --- a/app/http/endpoints/api/panel/multipaneldelete.go +++ b/app/http/endpoints/api/panel/multipaneldelete.go @@ -1,61 +1,70 @@ package api import ( - "context" "errors" + "github.com/TicketsBot/GoPanel/app" "github.com/TicketsBot/GoPanel/botcontext" dbclient "github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/utils" "github.com/gin-gonic/gin" "github.com/rxdn/gdl/rest" "github.com/rxdn/gdl/rest/request" + "net/http" "strconv" ) -func MultiPanelDelete(ctx *gin.Context) { - guildId := ctx.Keys["guildid"].(uint64) +func MultiPanelDelete(c *gin.Context) { + guildId := c.Keys["guildid"].(uint64) - multiPanelId, err := strconv.Atoi(ctx.Param("panelid")) + multiPanelId, err := strconv.Atoi(c.Param("panelid")) if err != nil { - ctx.JSON(400, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } // get bot context botContext, err := botcontext.ContextForGuild(guildId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } - panel, ok, err := dbclient.Client.MultiPanels.Get(ctx, multiPanelId) + panel, ok, err := dbclient.Client.MultiPanels.Get(c, multiPanelId) if !ok { - ctx.JSON(404, utils.ErrorStr("No panel with matching ID found")) + c.JSON(404, utils.ErrorStr("No panel with matching ID found")) return } if panel.GuildId != guildId { - ctx.JSON(403, utils.ErrorStr("Guild ID doesn't match")) + c.JSON(403, utils.ErrorStr("Guild ID doesn't match")) return } - var unwrapped request.RestError // TODO: Use proper context - if err := rest.DeleteMessage(context.Background(), botContext.Token, botContext.RateLimiter, panel.ChannelId, panel.MessageId); err != nil && !(errors.As(err, &unwrapped) && unwrapped.IsClientError()) { - ctx.JSON(500, utils.ErrorJson(err)) - return + if err := rest.DeleteMessage(c, botContext.Token, botContext.RateLimiter, panel.ChannelId, panel.MessageId); err != nil { + var unwrapped request.RestError + if errors.As(err, &unwrapped) { + // Swallow 403 / 404 + if unwrapped.StatusCode != http.StatusForbidden && unwrapped.StatusCode != http.StatusNotFound { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) + return + } + } else { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) + return + } } - success, err := dbclient.Client.MultiPanels.Delete(ctx, guildId, multiPanelId) + success, err := dbclient.Client.MultiPanels.Delete(c, guildId, multiPanelId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } if !success { - ctx.JSON(404, utils.ErrorJson(errors.New("No panel with matching ID found"))) + c.JSON(404, utils.ErrorJson(errors.New("No panel with matching ID found"))) return } - ctx.JSON(200, utils.SuccessResponse) + c.JSON(200, utils.SuccessResponse) } diff --git a/app/http/endpoints/api/panel/multipanelupdate.go b/app/http/endpoints/api/panel/multipanelupdate.go index ceb2ded..d68fab0 100644 --- a/app/http/endpoints/api/panel/multipanelupdate.go +++ b/app/http/endpoints/api/panel/multipanelupdate.go @@ -15,6 +15,7 @@ import ( "github.com/rxdn/gdl/rest" "github.com/rxdn/gdl/rest/request" "golang.org/x/sync/errgroup" + "net/http" "strconv" ) @@ -24,21 +25,21 @@ func MultiPanelUpdate(c *gin.Context) { // parse body var data multiPanelCreateData if err := c.ShouldBindJSON(&data); err != nil { - c.JSON(400, utils.ErrorJson(err)) + c.JSON(400, utils.ErrorStr("Invalid request body")) return } // parse panel ID panelId, err := strconv.Atoi(c.Param("panelid")) if err != nil { - c.JSON(400, utils.ErrorJson(err)) + c.JSON(400, utils.ErrorStr("Missing panel ID")) return } // retrieve panel from DB multiPanel, ok, err := dbclient.Client.MultiPanels.Get(c, panelId) if err != nil { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -57,7 +58,7 @@ func MultiPanelUpdate(c *gin.Context) { if err := validate.Struct(data); err != nil { var validationErrors validator.ValidationErrors if ok := errors.As(err, &validationErrors); !ok { - c.JSON(500, utils.ErrorStr("An error occurred while validating the panel")) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "An error occurred while validating the panel")) return } @@ -77,12 +78,12 @@ func MultiPanelUpdate(c *gin.Context) { if panel.CustomId == "" { panel.CustomId, err = utils.RandString(30) if err != nil { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } if err := dbclient.Client.Panel.Update(c, panel); err != nil { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } } @@ -91,7 +92,7 @@ func MultiPanelUpdate(c *gin.Context) { // get bot context botContext, err := botcontext.ContextForGuild(guildId) if err != nil { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -99,17 +100,19 @@ func MultiPanelUpdate(c *gin.Context) { ctx, cancel := app.DefaultContext() defer cancel() - var unwrapped request.RestError - if err := rest.DeleteMessage(ctx, botContext.Token, botContext.RateLimiter, multiPanel.ChannelId, multiPanel.MessageId); err != nil && !(errors.As(err, &unwrapped) && unwrapped.IsClientError()) { - c.JSON(500, utils.ErrorJson(err)) - return + if err := rest.DeleteMessage(ctx, botContext.Token, botContext.RateLimiter, multiPanel.ChannelId, multiPanel.MessageId); err != nil { + var unwrapped request.RestError + if !errors.As(err, &unwrapped) || !unwrapped.IsClientError() { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) + return + } } cancel() // get premium status premiumTier, err := rpc.PremiumClient.GetTierByGuildId(c, guildId, true, botContext.Token, botContext.RateLimiter) if err != nil { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -119,9 +122,9 @@ func MultiPanelUpdate(c *gin.Context) { if err != nil { var unwrapped request.RestError if errors.As(err, &unwrapped) && unwrapped.StatusCode == 403 { - c.JSON(500, utils.ErrorJson(errors.New("I do not have permission to send messages in the provided channel"))) + c.JSON(http.StatusBadRequest, utils.ErrorJson(errors.New("I do not have permission to send messages in the provided channel"))) } else { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) } return @@ -143,14 +146,14 @@ func MultiPanelUpdate(c *gin.Context) { } if err = dbclient.Client.MultiPanels.Update(c, multiPanel.Id, updated); err != nil { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } // TODO: one query for ACID purposes // delete old targets if err := dbclient.Client.MultiPanelTargets.DeleteAll(c, multiPanel.Id); err != nil { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -165,7 +168,7 @@ func MultiPanelUpdate(c *gin.Context) { } if err := group.Wait(); err != nil { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } diff --git a/app/http/endpoints/api/panel/panelcreate.go b/app/http/endpoints/api/panel/panelcreate.go index ddbc050..2068074 100644 --- a/app/http/endpoints/api/panel/panelcreate.go +++ b/app/http/endpoints/api/panel/panelcreate.go @@ -18,6 +18,7 @@ import ( "github.com/rxdn/gdl/objects/guild/emoji" "github.com/rxdn/gdl/objects/interaction/component" "github.com/rxdn/gdl/rest/request" + "net/http" "strconv" ) @@ -70,14 +71,14 @@ func CreatePanel(c *gin.Context) { botContext, err := botcontext.ContextForGuild(guildId) if err != nil { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } var data panelBody if err := c.BindJSON(&data); err != nil { - c.JSON(400, utils.ErrorJson(err)) + c.JSON(400, utils.ErrorStr("Invalid request body")) return } @@ -86,14 +87,14 @@ func CreatePanel(c *gin.Context) { // Check panel quota premiumTier, err := rpc.PremiumClient.GetTierByGuildId(c, guildId, false, botContext.Token, botContext.RateLimiter) if err != nil { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } if premiumTier == premium.None { panels, err := dbclient.Client.Panel.GetByGuild(c, guildId) if err != nil { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -111,13 +112,13 @@ func CreatePanel(c *gin.Context) { channels, err := botContext.GetGuildChannels(ctx, guildId) if err != nil { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } roles, err := botContext.GetGuildRoles(ctx, guildId) if err != nil { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -136,7 +137,7 @@ func CreatePanel(c *gin.Context) { if errors.As(err, &validationError) { c.JSON(400, utils.ErrorStr(validationError.Error())) } else { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) } return @@ -157,7 +158,7 @@ func CreatePanel(c *gin.Context) { customId, err := utils.RandString(30) if err != nil { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -165,11 +166,14 @@ func CreatePanel(c *gin.Context) { msgId, err := messageData.send(botContext) if err != nil { var unwrapped request.RestError - if errors.As(err, &unwrapped) && unwrapped.StatusCode == 403 { - c.JSON(500, utils.ErrorStr("I do not have permission to send messages in the specified channel")) + if errors.As(err, &unwrapped) { + if unwrapped.StatusCode == http.StatusForbidden { + c.JSON(400, utils.ErrorStr("I do not have permission to send messages in the specified channel")) + } else { + c.JSON(400, utils.ErrorStr("Error sending panel message: "+unwrapped.ApiError.Message)) + } } else { - // TODO: Most appropriate error? - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) } return @@ -196,7 +200,7 @@ func CreatePanel(c *gin.Context) { id, err := dbclient.Client.Embeds.CreateWithFields(c, embed, fields) if err != nil { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -256,7 +260,7 @@ func CreatePanel(c *gin.Context) { panelId, err := storePanel(c, panel, createOptions) if err != nil { - c.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } diff --git a/app/http/endpoints/api/panel/paneldelete.go b/app/http/endpoints/api/panel/paneldelete.go index b58ade4..2f11fa8 100644 --- a/app/http/endpoints/api/panel/paneldelete.go +++ b/app/http/endpoints/api/panel/paneldelete.go @@ -1,8 +1,8 @@ package api import ( - "context" "errors" + "github.com/TicketsBot/GoPanel/app" "github.com/TicketsBot/GoPanel/botcontext" "github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/rpc" @@ -11,74 +11,75 @@ import ( "github.com/gin-gonic/gin" "github.com/rxdn/gdl/rest" "github.com/rxdn/gdl/rest/request" + "net/http" "strconv" ) -func DeletePanel(ctx *gin.Context) { - guildId := ctx.Keys["guildid"].(uint64) +func DeletePanel(c *gin.Context) { + guildId := c.Keys["guildid"].(uint64) botContext, err := botcontext.ContextForGuild(guildId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } - panelId, err := strconv.Atoi(ctx.Param("panelid")) + panelId, err := strconv.Atoi(c.Param("panelid")) if err != nil { - ctx.JSON(400, utils.ErrorJson(err)) + c.JSON(400, utils.ErrorStr("Missing panel ID")) return } - panel, err := database.Client.Panel.GetById(ctx, panelId) + panel, err := database.Client.Panel.GetById(c, panelId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } if panel.PanelId == 0 { - ctx.JSON(404, utils.ErrorStr("Panel not found")) + c.JSON(404, utils.ErrorStr("Panel not found")) return } // verify panel belongs to guild if panel.GuildId != guildId { - ctx.JSON(403, utils.ErrorStr("Guild ID doesn't match")) + c.JSON(403, utils.ErrorStr("Guild ID doesn't match")) return } // Get any multi panels this panel is part of to use later - multiPanels, err := database.Client.MultiPanelTargets.GetMultiPanels(ctx, panelId) + multiPanels, err := database.Client.MultiPanelTargets.GetMultiPanels(c, panelId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } // Delete welcome message embed if panel.WelcomeMessageEmbed != nil { - if err := database.Client.Embeds.Delete(ctx, *panel.WelcomeMessageEmbed); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + if err := database.Client.Embeds.Delete(c, *panel.WelcomeMessageEmbed); err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } } - if err := database.Client.Panel.Delete(ctx, panelId); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + if err := database.Client.Panel.Delete(c, panelId); err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } - // TODO: Use proper context - if err := rest.DeleteMessage(context.Background(), botContext.Token, botContext.RateLimiter, panel.ChannelId, panel.MessageId); err != nil { + // TODO: Set timeout on context + if err := rest.DeleteMessage(c, botContext.Token, botContext.RateLimiter, panel.ChannelId, panel.MessageId); err != nil { var unwrapped request.RestError if !errors.As(err, &unwrapped) || unwrapped.StatusCode != 404 { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } } // Get premium tier - premiumTier, err := rpc.PremiumClient.GetTierByGuildId(ctx, guildId, true, botContext.Token, botContext.RateLimiter) + premiumTier, err := rpc.PremiumClient.GetTierByGuildId(c, guildId, true, botContext.Token, botContext.RateLimiter) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -89,9 +90,9 @@ func DeletePanel(ctx *gin.Context) { break } - panels, err := database.Client.MultiPanelTargets.GetPanels(ctx, multiPanel.Id) + panels, err := database.Client.MultiPanelTargets.GetPanels(c, multiPanel.Id) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -99,19 +100,19 @@ func DeletePanel(ctx *gin.Context) { messageId, err := messageData.send(botContext, panels) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } - if err := database.Client.MultiPanels.UpdateMessageId(ctx, multiPanel.Id, messageId); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + if err := database.Client.MultiPanels.UpdateMessageId(c, multiPanel.Id, messageId); err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } // Delete old panel // TODO: Use proper context - _ = rest.DeleteMessage(context.Background(), botContext.Token, botContext.RateLimiter, multiPanel.ChannelId, multiPanel.MessageId) + _ = rest.DeleteMessage(c, botContext.Token, botContext.RateLimiter, multiPanel.ChannelId, multiPanel.MessageId) } - ctx.JSON(200, utils.SuccessResponse) + c.JSON(200, utils.SuccessResponse) } diff --git a/app/http/endpoints/api/panel/panelupdate.go b/app/http/endpoints/api/panel/panelupdate.go index c15a385..9fa5821 100644 --- a/app/http/endpoints/api/panel/panelupdate.go +++ b/app/http/endpoints/api/panel/panelupdate.go @@ -3,6 +3,7 @@ package api import ( "context" "errors" + "github.com/TicketsBot/GoPanel/app" "github.com/TicketsBot/GoPanel/app/http/validation" "github.com/TicketsBot/GoPanel/botcontext" dbclient "github.com/TicketsBot/GoPanel/database" @@ -16,68 +17,69 @@ import ( "github.com/rxdn/gdl/objects/interaction/component" "github.com/rxdn/gdl/rest" "github.com/rxdn/gdl/rest/request" + "net/http" "strconv" ) -func UpdatePanel(ctx *gin.Context) { - guildId := ctx.Keys["guildid"].(uint64) +func UpdatePanel(c *gin.Context) { + guildId := c.Keys["guildid"].(uint64) botContext, err := botcontext.ContextForGuild(guildId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } var data panelBody - if err := ctx.BindJSON(&data); err != nil { - ctx.JSON(400, utils.ErrorJson(err)) + if err := c.BindJSON(&data); err != nil { + c.JSON(400, utils.ErrorStr("Invalid request body")) return } - panelId, err := strconv.Atoi(ctx.Param("panelid")) + panelId, err := strconv.Atoi(c.Param("panelid")) if err != nil { - ctx.JSON(400, utils.ErrorJson(err)) + c.JSON(400, utils.ErrorStr("Missing panel ID")) return } // get existing - existing, err := dbclient.Client.Panel.GetById(ctx, panelId) + existing, err := dbclient.Client.Panel.GetById(c, panelId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } // check guild ID matches if existing.GuildId != guildId { - ctx.JSON(400, utils.ErrorStr("Guild ID does not match")) + c.JSON(400, utils.ErrorStr("Guild ID does not match")) return } if existing.ForceDisabled { - ctx.JSON(400, utils.ErrorStr("This panel is disabled and cannot be modified: please reactivate premium to re-enable it")) + c.JSON(400, utils.ErrorStr("This panel is disabled and cannot be modified: please reactivate premium to re-enable it")) return } // Apply defaults ApplyPanelDefaults(&data) - premiumTier, err := rpc.PremiumClient.GetTierByGuildId(ctx, guildId, true, botContext.Token, botContext.RateLimiter) + premiumTier, err := rpc.PremiumClient.GetTierByGuildId(c, guildId, true, botContext.Token, botContext.RateLimiter) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } // TODO: Use proper context channels, err := botContext.GetGuildChannels(context.Background(), guildId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } // TODO: Use proper context roles, err := botContext.GetGuildRoles(context.Background(), guildId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + c.JSON(500, utils.ErrorJson(err)) return } @@ -94,9 +96,9 @@ func UpdatePanel(ctx *gin.Context) { if err := ValidatePanelBody(validationContext); err != nil { var validationError *validation.InvalidInputError if errors.As(err, &validationError) { - ctx.JSON(400, utils.ErrorStr(validationError.Error())) + c.JSON(400, utils.ErrorStr(validationError.Error())) } else { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) } return @@ -104,14 +106,14 @@ func UpdatePanel(ctx *gin.Context) { // Do tag validation if err := validate.Struct(data); err != nil { - validationErrors, ok := err.(validator.ValidationErrors) - if !ok { - ctx.JSON(500, utils.ErrorStr("An error occurred while validating the panel")) + var validationErrors validator.ValidationErrors + if !errors.As(err, &validationErrors) { + c.JSON(500, utils.ErrorStr("An error occurred while validating the panel")) return } formatted := "Your input contained the following errors:\n" + utils.FormatValidationErrors(validationErrors) - ctx.JSON(400, utils.ErrorStr(formatted)) + c.JSON(400, utils.ErrorStr(formatted)) return } @@ -146,7 +148,7 @@ func UpdatePanel(ctx *gin.Context) { if shouldUpdateMessage { // delete old message, ignoring error // TODO: Use proper context - _ = rest.DeleteMessage(context.Background(), botContext.Token, botContext.RateLimiter, existing.ChannelId, existing.MessageId) + _ = rest.DeleteMessage(c, botContext.Token, botContext.RateLimiter, existing.ChannelId, existing.MessageId) messageData := data.IntoPanelMessageData(existing.CustomId, premiumTier > premium.None) newMessageId, err = messageData.send(botContext) @@ -154,16 +156,17 @@ func UpdatePanel(ctx *gin.Context) { var unwrapped request.RestError if errors.As(err, &unwrapped) { if unwrapped.StatusCode == 403 { - ctx.JSON(403, utils.ErrorStr("I do not have permission to send messages in the specified channel")) + c.JSON(403, utils.ErrorStr("I do not have permission to send messages in the specified channel")) return } else if unwrapped.StatusCode == 404 { // Swallow error // TODO: Make channel_id column nullable, and set to null } else { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) + return } } else { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } } @@ -173,8 +176,8 @@ func UpdatePanel(ctx *gin.Context) { var welcomeMessageEmbed *int if data.WelcomeMessage == nil { if existing.WelcomeMessageEmbed != nil { // If welcome message wasn't null, but now is, delete the embed - if err := dbclient.Client.Embeds.Delete(ctx, *existing.WelcomeMessageEmbed); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + if err := dbclient.Client.Embeds.Delete(c, *existing.WelcomeMessageEmbed); err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } } // else, welcomeMessageEmbed will be nil @@ -184,9 +187,9 @@ func UpdatePanel(ctx *gin.Context) { embed, fields := data.WelcomeMessage.IntoDatabaseStruct() embed.GuildId = guildId - id, err := dbclient.Client.Embeds.CreateWithFields(ctx, embed, fields) + id, err := dbclient.Client.Embeds.CreateWithFields(c, embed, fields) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -198,8 +201,8 @@ func UpdatePanel(ctx *gin.Context) { embed.Id = *existing.WelcomeMessageEmbed embed.GuildId = guildId - if err := dbclient.Client.Embeds.UpdateWithFields(ctx, embed, fields); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + if err := dbclient.Client.Embeds.UpdateWithFields(c, embed, fields); err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } } @@ -243,7 +246,7 @@ func UpdatePanel(ctx *gin.Context) { } else { roleId, err := strconv.ParseUint(mention, 10, 64) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -253,25 +256,25 @@ func UpdatePanel(ctx *gin.Context) { } } - err = dbclient.Client.Panel.BeginFunc(ctx, func(tx pgx.Tx) error { - if err := dbclient.Client.Panel.UpdateWithTx(ctx, tx, panel); err != nil { + err = dbclient.Client.Panel.BeginFunc(c, func(tx pgx.Tx) error { + if err := dbclient.Client.Panel.UpdateWithTx(c, tx, panel); err != nil { return err } - if err := dbclient.Client.PanelUserMention.SetWithTx(ctx, tx, panel.PanelId, shouldMentionUser); err != nil { + if err := dbclient.Client.PanelUserMention.SetWithTx(c, tx, panel.PanelId, shouldMentionUser); err != nil { return err } - if err := dbclient.Client.PanelRoleMentions.ReplaceWithTx(ctx, tx, panel.PanelId, roleMentions); err != nil { + if err := dbclient.Client.PanelRoleMentions.ReplaceWithTx(c, tx, panel.PanelId, roleMentions); err != nil { return err } // We are safe to insert, team IDs already validated - if err := dbclient.Client.PanelTeams.ReplaceWithTx(ctx, tx, panel.PanelId, data.Teams); err != nil { + if err := dbclient.Client.PanelTeams.ReplaceWithTx(c, tx, panel.PanelId, data.Teams); err != nil { return err } - if err := dbclient.Client.PanelAccessControlRules.ReplaceWithTx(ctx, tx, panel.PanelId, data.AccessControlList); err != nil { + if err := dbclient.Client.PanelAccessControlRules.ReplaceWithTx(c, tx, panel.PanelId, data.AccessControlList); err != nil { return err } @@ -279,7 +282,7 @@ func UpdatePanel(ctx *gin.Context) { }) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -288,9 +291,9 @@ func UpdatePanel(ctx *gin.Context) { // check if this will break a multi-panel; // first, get any multipanels this panel belongs to - multiPanels, err := dbclient.Client.MultiPanelTargets.GetMultiPanels(ctx, existing.PanelId) + multiPanels, err := dbclient.Client.MultiPanelTargets.GetMultiPanels(c, existing.PanelId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -300,9 +303,9 @@ func UpdatePanel(ctx *gin.Context) { break } - panels, err := dbclient.Client.MultiPanelTargets.GetPanels(ctx, multiPanel.Id) + panels, err := dbclient.Client.MultiPanelTargets.GetPanels(c, multiPanel.Id) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -310,19 +313,29 @@ func UpdatePanel(ctx *gin.Context) { messageId, err := messageData.send(botContext, panels) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + var unwrapped request.RestError + if errors.As(err, &unwrapped) { + if unwrapped.StatusCode == http.StatusForbidden { + c.JSON(400, utils.ErrorStr("I do not have permission to send messages in the specified channel")) + } else { + c.JSON(400, utils.ErrorStr("Error sending panel message: "+unwrapped.ApiError.Message)) + } + } else { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) + } + return } - if err := dbclient.Client.MultiPanels.UpdateMessageId(ctx, multiPanel.Id, messageId); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + if err := dbclient.Client.MultiPanels.UpdateMessageId(c, multiPanel.Id, messageId); err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } // Delete old panel // TODO: Use proper context - _ = rest.DeleteMessage(context.Background(), botContext.Token, botContext.RateLimiter, multiPanel.ChannelId, multiPanel.MessageId) + _ = rest.DeleteMessage(c, botContext.Token, botContext.RateLimiter, multiPanel.ChannelId, multiPanel.MessageId) } - ctx.JSON(200, utils.SuccessResponse) + c.JSON(200, utils.SuccessResponse) } diff --git a/app/http/endpoints/api/whitelabel/delete.go b/app/http/endpoints/api/whitelabel/delete.go index 80125c2..0243733 100644 --- a/app/http/endpoints/api/whitelabel/delete.go +++ b/app/http/endpoints/api/whitelabel/delete.go @@ -1,20 +1,29 @@ package api import ( + "github.com/TicketsBot/GoPanel/app" "github.com/TicketsBot/GoPanel/database" - "github.com/TicketsBot/GoPanel/utils" + "github.com/TicketsBot/GoPanel/redis" + "github.com/TicketsBot/common/whitelabeldelete" "github.com/gin-gonic/gin" "net/http" ) -func WhitelabelDelete(ctx *gin.Context) { - userId := ctx.Keys["userid"].(uint64) +func WhitelabelDelete(c *gin.Context) { + userId := c.Keys["userid"].(uint64) // Check if this is a different token - if err := database.Client.Whitelabel.Delete(ctx, userId); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + botId, err := database.Client.Whitelabel.Delete(c, userId) + if err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } - ctx.Status(http.StatusNoContent) + if botId != nil { + // TODO: Kafka + go whitelabeldelete.Publish(redis.Client.Client, *botId) + + } + + c.Status(http.StatusNoContent) } diff --git a/app/http/endpoints/api/whitelabel/whitelabelcreateinteractions.go b/app/http/endpoints/api/whitelabel/whitelabelcreateinteractions.go index 693ba1d..a71a7af 100644 --- a/app/http/endpoints/api/whitelabel/whitelabelcreateinteractions.go +++ b/app/http/endpoints/api/whitelabel/whitelabelcreateinteractions.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/TicketsBot/GoPanel/app" "github.com/TicketsBot/GoPanel/botcontext" "github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/redis" @@ -11,6 +12,7 @@ import ( "github.com/TicketsBot/worker/bot/command/manager" "github.com/gin-gonic/gin" "github.com/rxdn/gdl/rest" + "net/http" "time" ) @@ -19,33 +21,33 @@ func GetWhitelabelCreateInteractions() func(*gin.Context) { cm := new(manager.CommandManager) cm.RegisterCommands() - return func(ctx *gin.Context) { - userId := ctx.Keys["userid"].(uint64) + return func(c *gin.Context) { + userId := c.Keys["userid"].(uint64) // Get bot - bot, err := database.Client.Whitelabel.GetByUserId(ctx, userId) + bot, err := database.Client.Whitelabel.GetByUserId(c, userId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } // Ensure bot exists if bot.BotId == 0 { - ctx.JSON(404, utils.ErrorStr("No bot found")) + c.JSON(404, utils.ErrorStr("No bot found")) return } if err := createInteractions(cm, bot.BotId, bot.Token); err != nil { if errors.Is(err, ErrInteractionCreateCooldown) { - ctx.JSON(400, utils.ErrorJson(err)) + c.JSON(http.StatusTooManyRequests, utils.ErrorJson(err)) } else { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) } return } - ctx.JSON(200, utils.SuccessResponse) + c.JSON(200, utils.SuccessResponse) } } diff --git a/app/http/endpoints/api/whitelabel/whitelabelget.go b/app/http/endpoints/api/whitelabel/whitelabelget.go index 1e337e8..7b070e0 100644 --- a/app/http/endpoints/api/whitelabel/whitelabelget.go +++ b/app/http/endpoints/api/whitelabel/whitelabelget.go @@ -2,55 +2,48 @@ package api import ( "context" + "github.com/TicketsBot/GoPanel/app" "github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/utils" "github.com/gin-gonic/gin" "github.com/rxdn/gdl/objects/user" "github.com/rxdn/gdl/rest" + "net/http" ) type whitelabelResponse struct { - Id uint64 `json:"id,string"` - PublicKey string `json:"public_key"` - Username string `json:"username"` + Id uint64 `json:"id,string"` + Username string `json:"username"` statusUpdateBody } -func WhitelabelGet(ctx *gin.Context) { - userId := ctx.Keys["userid"].(uint64) +func WhitelabelGet(c *gin.Context) { + userId := c.Keys["userid"].(uint64) // Check if this is a different token - bot, err := database.Client.Whitelabel.GetByUserId(ctx, userId) + bot, err := database.Client.Whitelabel.GetByUserId(c, userId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } if bot.BotId == 0 { - ctx.JSON(404, utils.ErrorStr("No bot found")) - return - } - - // Get public key - publicKey, err := database.Client.WhitelabelKeys.Get(ctx, bot.BotId) - if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + c.JSON(404, utils.ErrorStr("No bot found")) return } // Get status - status, statusType, _, err := database.Client.WhitelabelStatuses.Get(ctx, bot.BotId) + status, statusType, _, err := database.Client.WhitelabelStatuses.Get(c, bot.BotId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } - username := getBotUsername(context.Background(), bot.Token) + username := getBotUsername(c, bot.Token) - ctx.JSON(200, whitelabelResponse{ - Id: bot.BotId, - PublicKey: publicKey, - Username: username, + c.JSON(200, whitelabelResponse{ + Id: bot.BotId, + Username: username, statusUpdateBody: statusUpdateBody{ // Zero values if no status is fine Status: status, StatusType: user.ActivityType(statusType), diff --git a/app/http/endpoints/api/whitelabel/whitelabelgeterrors.go b/app/http/endpoints/api/whitelabel/whitelabelgeterrors.go index f30d4b6..fd44952 100644 --- a/app/http/endpoints/api/whitelabel/whitelabelgeterrors.go +++ b/app/http/endpoints/api/whitelabel/whitelabelgeterrors.go @@ -1,21 +1,22 @@ package api import ( + "github.com/TicketsBot/GoPanel/app" "github.com/TicketsBot/GoPanel/database" - "github.com/TicketsBot/GoPanel/utils" "github.com/gin-gonic/gin" + "net/http" ) -func WhitelabelGetErrors(ctx *gin.Context) { - userId := ctx.Keys["userid"].(uint64) +func WhitelabelGetErrors(c *gin.Context) { + userId := c.Keys["userid"].(uint64) - errors, err := database.Client.WhitelabelErrors.GetRecent(ctx, userId, 10) + errors, err := database.Client.WhitelabelErrors.GetRecent(c, userId, 10) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } - ctx.JSON(200, gin.H{ + c.JSON(200, gin.H{ "success": true, "errors": errors, }) diff --git a/app/http/endpoints/api/whitelabel/whitelabelgetguilds.go b/app/http/endpoints/api/whitelabel/whitelabelgetguilds.go index 6ca7340..d404905 100644 --- a/app/http/endpoints/api/whitelabel/whitelabelgetguilds.go +++ b/app/http/endpoints/api/whitelabel/whitelabelgetguilds.go @@ -3,40 +3,45 @@ package api import ( "context" "errors" + "github.com/TicketsBot/GoPanel/app" "github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/rpc/cache" "github.com/TicketsBot/GoPanel/utils" "github.com/gin-gonic/gin" cache2 "github.com/rxdn/gdl/cache" + "net/http" "strconv" ) -func WhitelabelGetGuilds(ctx *gin.Context) { - userId := ctx.Keys["userid"].(uint64) +func WhitelabelGetGuilds(c *gin.Context) { + userId := c.Keys["userid"].(uint64) - bot, err := database.Client.Whitelabel.GetByUserId(ctx, userId) + bot, err := database.Client.Whitelabel.GetByUserId(c, userId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } // id -> name - guilds := make(map[string]string) if bot.BotId == 0 { - ctx.JSON(404, gin.H{ - "success": false, - "guilds": guilds, - }) + c.JSON(400, utils.ErrorStr("Whitelabel bot not found")) return } - ids, err := database.Client.WhitelabelGuilds.GetGuilds(ctx, bot.BotId) + ids, err := database.Client.WhitelabelGuilds.GetGuilds(c, bot.BotId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } - for _, id := range ids { + guilds := make(map[string]string) + for i, id := range ids { + if i >= 10 { + idStr := strconv.FormatUint(id, 10) + guilds[idStr] = idStr + continue + } + // get guild name // TODO: Use proper context guild, err := cache.Instance.GetGuild(context.Background(), id) @@ -44,7 +49,7 @@ func WhitelabelGetGuilds(ctx *gin.Context) { if errors.Is(err, cache2.ErrNotFound) { continue } else { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } } @@ -52,7 +57,7 @@ func WhitelabelGetGuilds(ctx *gin.Context) { guilds[strconv.FormatUint(id, 10)] = guild.Name } - ctx.JSON(200, gin.H{ + c.JSON(200, gin.H{ "success": true, "guilds": guilds, }) diff --git a/app/http/endpoints/api/whitelabel/whitelabelpost.go b/app/http/endpoints/api/whitelabel/whitelabelpost.go index a15ed78..d344fb8 100644 --- a/app/http/endpoints/api/whitelabel/whitelabelpost.go +++ b/app/http/endpoints/api/whitelabel/whitelabelpost.go @@ -3,7 +3,9 @@ package api import ( "context" "encoding/base64" + "errors" "fmt" + "github.com/TicketsBot/GoPanel/app" dbclient "github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/redis" "github.com/TicketsBot/GoPanel/utils" @@ -14,6 +16,8 @@ import ( "github.com/gin-gonic/gin" "github.com/rxdn/gdl/objects/application" "github.com/rxdn/gdl/rest" + "github.com/rxdn/gdl/rest/request" + "net/http" "strconv" "strings" ) @@ -22,8 +26,8 @@ func WhitelabelPost() func(*gin.Context) { cm := new(manager.CommandManager) cm.RegisterCommands() - return func(ctx *gin.Context) { - userId := ctx.Keys["userid"].(uint64) + return func(c *gin.Context) { + userId := c.Keys["userid"].(uint64) type whitelabelPostBody struct { Token string `json:"token"` @@ -31,24 +35,29 @@ func WhitelabelPost() func(*gin.Context) { // Get token var data whitelabelPostBody - if err := ctx.BindJSON(&data); err != nil { - ctx.JSON(400, gin.H{ - "success": false, - "error": "Missing token", - }) + if err := c.BindJSON(&data); err != nil { + c.JSON(http.StatusBadRequest, utils.ErrorStr("Invalid request body")) return } - bot, err := fetchApplication(data.Token) + bot, err := fetchApplication(c, data.Token) if err != nil { - ctx.JSON(400, utils.ErrorJson(err)) + var restError request.RestError + if errors.Is(err, errInvalidToken) { + c.JSON(http.StatusBadRequest, utils.ErrorStr("Invalid token")) + } else if errors.As(err, &restError) && restError.StatusCode == http.StatusUnauthorized { + c.JSON(http.StatusBadRequest, utils.ErrorStr("Invalid token")) + } else { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) + } + return } // Check if this is a different token - existing, err := dbclient.Client.Whitelabel.GetByUserId(ctx, userId) + existing, err := dbclient.Client.Whitelabel.GetByUserId(c, userId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -59,17 +68,18 @@ func WhitelabelPost() func(*gin.Context) { // Set token in DB so that http-gateway can use it when Discord validates the interactions endpoint // TODO: Use a transaction - if err := dbclient.Client.Whitelabel.Set(ctx, database.WhitelabelBot{ - UserId: userId, - BotId: bot.Id, - Token: data.Token, + if err := dbclient.Client.Whitelabel.Set(c, database.WhitelabelBot{ + UserId: userId, + BotId: bot.Id, + PublicKey: bot.VerifyKey, + Token: data.Token, }); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } - if err := dbclient.Client.WhitelabelKeys.Set(ctx, bot.Id, bot.VerifyKey); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + if err := dbclient.Client.WhitelabelKeys.Set(c, bot.Id, bot.VerifyKey); err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -91,12 +101,12 @@ func WhitelabelPost() func(*gin.Context) { if _, err := rest.EditCurrentApplication(context.Background(), data.Token, nil, editData); err != nil { // TODO: Use a transaction - if err := dbclient.Client.Whitelabel.Delete(ctx, bot.Id); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + if err := dbclient.Client.Whitelabel.Delete(c, bot.Id); err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } @@ -107,16 +117,16 @@ func WhitelabelPost() func(*gin.Context) { } if err := tokenchange.PublishTokenChange(redis.Client.Client, tokenChangeData); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } if err := createInteractions(cm, bot.Id, data.Token); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } - ctx.JSON(200, gin.H{ + c.JSON(200, gin.H{ "success": true, "bot": bot, "username": bot.Bot.Username, @@ -124,6 +134,8 @@ func WhitelabelPost() func(*gin.Context) { } } +var errInvalidToken = fmt.Errorf("invalid token") + func validateToken(token string) bool { split := strings.Split(token, ".") @@ -151,20 +163,20 @@ func validateToken(token string) bool { return true } -func fetchApplication(token string) (*application.Application, error) { +func fetchApplication(ctx context.Context, token string) (*application.Application, error) { if !validateToken(token) { - return nil, fmt.Errorf("Invalid token") + return nil, errInvalidToken } // Validate token + get bot ID // TODO: Use proper context - app, err := rest.GetCurrentApplication(context.Background(), token, nil) + app, err := rest.GetCurrentApplication(ctx, token, nil) if err != nil { return nil, err } if app.Id == 0 { - return nil, fmt.Errorf("Invalid token") + return nil, errInvalidToken } return &app, nil diff --git a/app/http/endpoints/api/whitelabel/whitelabelstatuspost.go b/app/http/endpoints/api/whitelabel/whitelabelstatuspost.go index 5917fc1..d9488ab 100644 --- a/app/http/endpoints/api/whitelabel/whitelabelstatuspost.go +++ b/app/http/endpoints/api/whitelabel/whitelabelstatuspost.go @@ -1,12 +1,14 @@ package api import ( + "github.com/TicketsBot/GoPanel/app" "github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/redis" "github.com/TicketsBot/GoPanel/utils" "github.com/TicketsBot/common/statusupdates" "github.com/gin-gonic/gin" "github.com/rxdn/gdl/objects/user" + "net/http" ) type statusUpdateBody struct { @@ -14,32 +16,32 @@ type statusUpdateBody struct { StatusType user.ActivityType `json:"status_type,string"` } -func WhitelabelStatusPost(ctx *gin.Context) { - userId := ctx.Keys["userid"].(uint64) +func WhitelabelStatusPost(c *gin.Context) { + userId := c.Keys["userid"].(uint64) // Get bot - bot, err := database.Client.Whitelabel.GetByUserId(ctx, userId) + bot, err := database.Client.Whitelabel.GetByUserId(c, userId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } // Ensure bot exists if bot.BotId == 0 { - ctx.JSON(404, utils.ErrorStr("No bot found")) + c.JSON(404, utils.ErrorStr("No bot found")) return } // Parse status var data statusUpdateBody - if err := ctx.BindJSON(&data); err != nil { - ctx.JSON(400, utils.ErrorStr("Invalid request body")) + if err := c.BindJSON(&data); err != nil { + c.JSON(400, utils.ErrorStr("Invalid request body")) return } // Validate status length if len(data.Status) == 0 || len(data.Status) > 255 { - ctx.JSON(400, utils.ErrorStr("Status must be between 1-255 characters in length")) + c.JSON(400, utils.ErrorStr("Status must be between 1-255 characters in length")) return } @@ -51,18 +53,18 @@ func WhitelabelStatusPost(ctx *gin.Context) { } if !utils.Contains(validActivities, data.StatusType) { - ctx.JSON(400, utils.ErrorStr("Invalid status type")) + c.JSON(400, utils.ErrorStr("Invalid status type")) return } // Update in database - if err := database.Client.WhitelabelStatuses.Set(ctx, bot.BotId, data.Status, int16(data.StatusType)); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + if err := database.Client.WhitelabelStatuses.Set(c, bot.BotId, data.Status, int16(data.StatusType)); err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err)) return } // Send status update to sharder go statusupdates.Publish(redis.Client.Client, bot.BotId) - ctx.JSON(200, utils.SuccessResponse) + c.JSON(200, utils.SuccessResponse) } diff --git a/go.mod b/go.mod index cc183d3..bcaa5b0 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/BurntSushi/toml v1.2.1 github.com/TicketsBot/archiverclient v0.0.0-20241012221057-16a920bfb454 github.com/TicketsBot/common v0.0.0-20241104184641-e39c64bdcf3e - github.com/TicketsBot/database v0.0.0-20241113215509-c84281e50a7e + github.com/TicketsBot/database v0.0.0-20241116202646-1741ded5d50f github.com/TicketsBot/logarchiver v0.0.0-20241012220745-5f3ba17a5138 github.com/TicketsBot/worker v0.0.0-20241110222533-ba74e19de868 github.com/apex/log v1.1.2 diff --git a/go.sum b/go.sum index b0feaa7..5dead2f 100644 --- a/go.sum +++ b/go.sum @@ -51,6 +51,10 @@ github.com/TicketsBot/database v0.0.0-20241110235041-04a08e1a42a4 h1:9YMD+x2nBQN github.com/TicketsBot/database v0.0.0-20241110235041-04a08e1a42a4/go.mod h1:mpVkDO8tnnWn1pMGEphVg6YSeGIhDwLAN43lBTkpGmU= github.com/TicketsBot/database v0.0.0-20241113215509-c84281e50a7e h1:8nvgWvmFAJfjuAUhaGdOO9y7puPAOHRjgGWn+/DI/HA= github.com/TicketsBot/database v0.0.0-20241113215509-c84281e50a7e/go.mod h1:mpVkDO8tnnWn1pMGEphVg6YSeGIhDwLAN43lBTkpGmU= +github.com/TicketsBot/database v0.0.0-20241116201101-f77e9f58a6e0 h1:zVh8CnjkMh3qS+n/H5rOWL9E1RGOJYNi8kktMw21YZE= +github.com/TicketsBot/database v0.0.0-20241116201101-f77e9f58a6e0/go.mod h1:mpVkDO8tnnWn1pMGEphVg6YSeGIhDwLAN43lBTkpGmU= +github.com/TicketsBot/database v0.0.0-20241116202646-1741ded5d50f h1:CdYauI7Vkg0O9a/j8eZTFue2payodBblXkS6jqjrss4= +github.com/TicketsBot/database v0.0.0-20241116202646-1741ded5d50f/go.mod h1:mpVkDO8tnnWn1pMGEphVg6YSeGIhDwLAN43lBTkpGmU= github.com/TicketsBot/logarchiver v0.0.0-20241012220745-5f3ba17a5138 h1:wsR5ESeaQKo122qsmzPcblxlJdE0GIQbp2B/7/uX+TA= github.com/TicketsBot/logarchiver v0.0.0-20241012220745-5f3ba17a5138/go.mod h1:4Rq0CgSCgXVW6uEyEUvWzxOmFp+L57rFfCjPDFPHFiw= github.com/TicketsBot/ttlcache v1.6.1-0.20200405150101-acc18e37b261 h1:NHD5GB6cjlkpZFjC76Yli2S63/J2nhr8MuE6KlYJpQM=