diff --git a/app/http/endpoints/api/autocloseget.go b/app/http/endpoints/api/autoclose/autocloseget.go
similarity index 100%
rename from app/http/endpoints/api/autocloseget.go
rename to app/http/endpoints/api/autoclose/autocloseget.go
diff --git a/app/http/endpoints/api/autoclosepost.go b/app/http/endpoints/api/autoclose/autoclosepost.go
similarity index 100%
rename from app/http/endpoints/api/autoclosepost.go
rename to app/http/endpoints/api/autoclose/autoclosepost.go
diff --git a/app/http/endpoints/api/blacklist.go b/app/http/endpoints/api/blacklist/blacklist.go
similarity index 100%
rename from app/http/endpoints/api/blacklist.go
rename to app/http/endpoints/api/blacklist/blacklist.go
diff --git a/app/http/endpoints/api/blacklistadd.go b/app/http/endpoints/api/blacklist/blacklistadd.go
similarity index 100%
rename from app/http/endpoints/api/blacklistadd.go
rename to app/http/endpoints/api/blacklist/blacklistadd.go
diff --git a/app/http/endpoints/api/blacklistremove.go b/app/http/endpoints/api/blacklist/blacklistremove.go
similarity index 100%
rename from app/http/endpoints/api/blacklistremove.go
rename to app/http/endpoints/api/blacklist/blacklistremove.go
diff --git a/app/http/endpoints/api/logslist.go b/app/http/endpoints/api/logs/logslist.go
similarity index 100%
rename from app/http/endpoints/api/logslist.go
rename to app/http/endpoints/api/logs/logslist.go
diff --git a/app/http/endpoints/api/modmaillogslist.go b/app/http/endpoints/api/logs/modmaillogslist.go
similarity index 100%
rename from app/http/endpoints/api/modmaillogslist.go
rename to app/http/endpoints/api/logs/modmaillogslist.go
diff --git a/app/http/endpoints/api/panel/multipanelcreate.go b/app/http/endpoints/api/panel/multipanelcreate.go
new file mode 100644
index 0000000..00582cc
--- /dev/null
+++ b/app/http/endpoints/api/panel/multipanelcreate.go
@@ -0,0 +1,236 @@
+package api
+
+import (
+ "context"
+ "errors"
+ "github.com/TicketsBot/GoPanel/botcontext"
+ dbclient "github.com/TicketsBot/GoPanel/database"
+ "github.com/TicketsBot/GoPanel/rpc"
+ "github.com/TicketsBot/GoPanel/rpc/cache"
+ "github.com/TicketsBot/GoPanel/utils"
+ "github.com/TicketsBot/common/premium"
+ "github.com/TicketsBot/database"
+ "github.com/gin-gonic/gin"
+ "github.com/rxdn/gdl/objects/channel"
+ "github.com/rxdn/gdl/objects/channel/embed"
+ "github.com/rxdn/gdl/objects/channel/message"
+ "github.com/rxdn/gdl/rest"
+ "github.com/rxdn/gdl/rest/request"
+ gdlutils "github.com/rxdn/gdl/utils"
+ "golang.org/x/sync/errgroup"
+)
+
+type multiPanelCreateData struct {
+ Title string `json:"title"`
+ Content string `json:"content"`
+ Colour int32 `json:"colour"`
+ ChannelId uint64 `json:"channel_id,string"`
+ Panels gdlutils.Uint64StringSlice `json:"panels"`
+}
+
+func MultiPanelCreate(ctx *gin.Context) {
+ guildId := ctx.Keys["guildid"].(uint64)
+
+ var data multiPanelCreateData
+ if err := ctx.ShouldBindJSON(&data); err != nil {
+ ctx.JSON(400, utils.ErrorToResponse(err))
+ return
+ }
+
+ // validate body & get sub-panels
+ panels, err := data.doValidations(guildId)
+ if err != nil {
+ ctx.JSON(400, utils.ErrorToResponse(err))
+ return
+ }
+
+ // get bot context
+ botContext, err := botcontext.ContextForGuild(guildId)
+ if err != nil {
+ ctx.AbortWithStatusJSON(500, gin.H{
+ "success": false,
+ "error": err.Error(),
+ })
+ return
+ }
+
+ // get premium status
+ premiumTier := rpc.PremiumClient.GetTierByGuildId(guildId, true, botContext.Token, botContext.RateLimiter)
+
+ messageId, err := data.sendEmbed(&botContext, premiumTier > premium.None)
+ if err != nil {
+ if err == request.ErrForbidden {
+ ctx.JSON(500, utils.ErrorToResponse(errors.New("I do not have permission to send messages in the provided channel")))
+ } else {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ }
+
+ return
+ }
+
+ if err := data.addReactions(&botContext, data.ChannelId, messageId, panels); err != nil {
+ if err == request.ErrForbidden {
+ ctx.JSON(500, utils.ErrorToResponse(errors.New("I do not have permission to add reactions in the provided channel")))
+ } else {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ }
+
+ return
+ }
+
+ multiPanel := database.MultiPanel{
+ MessageId: messageId,
+ ChannelId: data.ChannelId,
+ GuildId: guildId,
+ Title: data.Title,
+ Content: data.Content,
+ Colour: int(data.Colour),
+ }
+
+ multiPanel.Id, err = dbclient.Client.MultiPanels.Create(multiPanel)
+ if err != nil {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ return
+ }
+
+ group, _ := errgroup.WithContext(context.Background())
+ for _, panel := range panels {
+ panel := panel
+
+ group.Go(func() error {
+ return dbclient.Client.MultiPanelTargets.Insert(multiPanel.Id, panel.MessageId)
+ })
+ }
+
+ if err := group.Wait(); err != nil {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ return
+ }
+
+ ctx.JSON(200, gin.H{
+ "success": true,
+ "data": multiPanel,
+ })
+}
+
+func (d *multiPanelCreateData) doValidations(guildId uint64) (panels []database.Panel, err error) {
+ group, _ := errgroup.WithContext(context.Background())
+
+ group.Go(d.validateTitle)
+ group.Go(d.validateContent)
+ group.Go(d.validateChannel(guildId))
+ group.Go(func() (e error) {
+ panels, e = d.validatePanels(guildId)
+ return
+ })
+
+ err = group.Wait()
+ return
+}
+
+func (d *multiPanelCreateData) validateTitle() (err error) {
+ if len(d.Title) > 255 || len(d.Title) < 1 {
+ err = errors.New("embed title must be between 1 and 255 characters")
+ }
+ return
+}
+
+func (d *multiPanelCreateData) validateContent() (err error) {
+ if len(d.Content) > 1024 || len(d.Title) < 1 {
+ err = errors.New("embed content must be between 1 and 1024 characters")
+ }
+ return
+}
+
+func (d *multiPanelCreateData) validateChannel(guildId uint64) func() error {
+ return func() (err error) {
+ channels := cache.Instance.GetGuildChannels(guildId)
+
+ var valid bool
+ for _, ch := range channels {
+ if ch.Id == d.ChannelId && ch.Type == channel.ChannelTypeGuildText {
+ valid = true
+ break
+ }
+ }
+
+ if !valid {
+ err = errors.New("channel does not exist")
+ }
+
+ return
+ }
+}
+
+func (d *multiPanelCreateData) validatePanels(guildId uint64) (panels []database.Panel, err error) {
+ if len(d.Panels) < 2 {
+ err = errors.New("a multi-panel must contain at least 2 sub-panels")
+ return
+ }
+
+ if len(d.Panels) > 15 {
+ err = errors.New("multi-panels cannot contain more than 15 sub-panels")
+ return
+ }
+
+ existingPanels, err := dbclient.Client.Panel.GetByGuild(guildId)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, panelId := range d.Panels {
+ var valid bool
+ // find panel struct
+ for _, panel := range existingPanels {
+ if panel.MessageId == panelId {
+ // check there isn't a panel with the same reaction emote
+ for _, previous := range panels {
+ if previous.ReactionEmote == panel.ReactionEmote {
+ return nil, errors.New("2 sub-panels cannot have the same reaction emotes")
+ }
+ }
+
+ valid = true
+ panels = append(panels, panel)
+ }
+ }
+
+ if !valid {
+ return nil, errors.New("invalid panel ID")
+ }
+ }
+
+ return
+}
+
+func (d *multiPanelCreateData) sendEmbed(ctx *botcontext.BotContext, isPremium bool) (messageId uint64, err error) {
+ e := embed.NewEmbed().
+ SetTitle(d.Title).
+ SetDescription(d.Content).
+ SetColor(int(d.Colour))
+
+ if !isPremium {
+ // TODO: Don't harcode
+ e.SetFooter("Powered by ticketsbot.net", "https://cdn.discordapp.com/avatars/508391840525975553/ac2647ffd4025009e2aa852f719a8027.png?size=256")
+ }
+
+ var msg message.Message
+ msg, err = rest.CreateMessage(ctx.Token, ctx.RateLimiter, d.ChannelId, rest.CreateMessageData{Embed: e})
+ if err != nil {
+ return
+ }
+
+ messageId = msg.Id
+ return
+}
+
+
+func (d *multiPanelCreateData) addReactions(ctx *botcontext.BotContext, channelId, messageId uint64, panels []database.Panel) (err error) {
+ for _, panel := range panels {
+ if err = rest.CreateReaction(ctx.Token, ctx.RateLimiter, channelId, messageId, panel.ReactionEmote); err != nil {
+ return err
+ }
+ }
+
+ return
+}
\ No newline at end of file
diff --git a/app/http/endpoints/api/panel/multipaneldelete.go b/app/http/endpoints/api/panel/multipaneldelete.go
new file mode 100644
index 0000000..1899a95
--- /dev/null
+++ b/app/http/endpoints/api/panel/multipaneldelete.go
@@ -0,0 +1,58 @@
+package api
+
+import (
+ "errors"
+ "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"
+ "strconv"
+)
+
+func MultiPanelDelete(ctx *gin.Context) {
+ guildId := ctx.Keys["guildid"].(uint64)
+
+ multiPanelId, err := strconv.Atoi(ctx.Param("panelid"))
+ if err != nil {
+ ctx.JSON(400, utils.ErrorToResponse(err))
+ return
+ }
+
+ // get bot context
+ botContext, err := botcontext.ContextForGuild(guildId)
+ if err != nil {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ return
+ }
+
+ panel, ok, err := dbclient.Client.MultiPanels.Get(multiPanelId)
+ if !ok {
+ ctx.JSON(404, utils.ErrorToResponse(errors.New("No panel with matching ID found")))
+ return
+ }
+
+ if panel.GuildId != guildId {
+ ctx.JSON(403, utils.ErrorToResponse(errors.New("Guild ID doesn't match")))
+ return
+ }
+
+ if err := rest.DeleteMessage(botContext.Token, botContext.RateLimiter, panel.ChannelId, panel.MessageId); err != nil && !request.IsClientError(err) {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ return
+ }
+
+ success, err := dbclient.Client.MultiPanels.Delete(guildId, multiPanelId)
+ if err != nil {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ return
+ }
+
+ if !success {
+ ctx.JSON(404, utils.ErrorToResponse(errors.New("No panel with matching ID found")))
+ return
+ }
+
+ ctx.JSON(200, utils.SuccessResponse)
+}
diff --git a/app/http/endpoints/api/panel/multipanellist.go b/app/http/endpoints/api/panel/multipanellist.go
new file mode 100644
index 0000000..dc83766
--- /dev/null
+++ b/app/http/endpoints/api/panel/multipanellist.go
@@ -0,0 +1,56 @@
+package api
+
+import (
+ "context"
+ dbclient "github.com/TicketsBot/GoPanel/database"
+ "github.com/TicketsBot/GoPanel/utils"
+ "github.com/TicketsBot/database"
+ "github.com/gin-gonic/gin"
+ "golang.org/x/sync/errgroup"
+)
+
+func MultiPanelList(ctx *gin.Context) {
+ type multiPanelResponse struct {
+ database.MultiPanel
+ Panels []database.Panel `json:"panels"`
+ }
+
+ guildId := ctx.Keys["guildid"].(uint64)
+
+ multiPanels, err := dbclient.Client.MultiPanels.GetByGuild(guildId)
+ if err != nil {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ return
+ }
+
+ data := make([]multiPanelResponse, len(multiPanels))
+ group, _ := errgroup.WithContext(context.Background())
+ for i, multiPanel := range multiPanels {
+ i := i
+ multiPanel := multiPanel
+
+ data[i] = multiPanelResponse{
+ MultiPanel: multiPanel,
+ }
+
+ group.Go(func() error {
+ panels, err := dbclient.Client.MultiPanelTargets.GetPanels(multiPanel.Id)
+ if err != nil {
+ return err
+ }
+
+ data[i].Panels = panels
+ return nil
+ })
+ }
+
+ if err := group.Wait(); err != nil {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ return
+ }
+
+ ctx.JSON(200, gin.H{
+ "success": true,
+ "data": data,
+ })
+}
diff --git a/app/http/endpoints/api/panel/multipanelupdate.go b/app/http/endpoints/api/panel/multipanelupdate.go
new file mode 100644
index 0000000..65dcc5b
--- /dev/null
+++ b/app/http/endpoints/api/panel/multipanelupdate.go
@@ -0,0 +1,143 @@
+package api
+
+import (
+ "context"
+ "errors"
+ "github.com/TicketsBot/GoPanel/botcontext"
+ dbclient "github.com/TicketsBot/GoPanel/database"
+ "github.com/TicketsBot/GoPanel/rpc"
+ "github.com/TicketsBot/GoPanel/utils"
+ "github.com/TicketsBot/common/premium"
+ "github.com/TicketsBot/database"
+ "github.com/gin-gonic/gin"
+ "github.com/rxdn/gdl/rest"
+ "github.com/rxdn/gdl/rest/request"
+ "golang.org/x/sync/errgroup"
+ "strconv"
+)
+
+func MultiPanelUpdate(ctx *gin.Context) {
+ guildId := ctx.Keys["guildid"].(uint64)
+
+ // parse body
+ var data multiPanelCreateData
+ if err := ctx.ShouldBindJSON(&data); err != nil {
+ ctx.JSON(400, utils.ErrorToResponse(err))
+ return
+ }
+
+ // parse panel ID
+ panelId, err := strconv.Atoi(ctx.Param("panelid"))
+ if err != nil {
+ ctx.JSON(400, utils.ErrorToResponse(err))
+ return
+ }
+
+ // retrieve panel from DB
+ multiPanel, ok, err := dbclient.Client.MultiPanels.Get(panelId)
+ if err != nil {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ return
+ }
+
+ // check panel exists
+ if !ok {
+ ctx.JSON(404, utils.ErrorToResponse(errors.New("No panel with the provided ID found")))
+ return
+ }
+
+ // check panel is in the same guild
+ if guildId != multiPanel.GuildId {
+ ctx.JSON(403, utils.ErrorToResponse(errors.New("Guild ID doesn't match")))
+ return
+ }
+
+ // validate body & get sub-panels
+ panels, err := data.doValidations(guildId)
+ if err != nil {
+ ctx.JSON(400, utils.ErrorToResponse(err))
+ return
+ }
+
+ // get bot context
+ botContext, err := botcontext.ContextForGuild(guildId)
+ if err != nil {
+ ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
+ return
+ }
+
+ // delete old message
+ if err := rest.DeleteMessage(botContext.Token, botContext.RateLimiter, multiPanel.ChannelId, multiPanel.MessageId); err != nil && !request.IsClientError(err) {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ return
+ }
+
+ // get premium status
+ premiumTier := rpc.PremiumClient.GetTierByGuildId(guildId, true, botContext.Token, botContext.RateLimiter)
+
+ // send new message
+ messageId, err := data.sendEmbed(&botContext, premiumTier > premium.None)
+ if err != nil {
+ if err == request.ErrForbidden {
+ ctx.JSON(500, utils.ErrorToResponse(errors.New("I do not have permission to send messages in the provided channel")))
+ } else {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ }
+
+ return
+ }
+
+ // add reactions to new message
+ if err := data.addReactions(&botContext, data.ChannelId, messageId, panels); err != nil {
+ if err == request.ErrForbidden {
+ ctx.JSON(500, utils.ErrorToResponse(errors.New("I do not have permission to add reactions in the provided channel")))
+ } else {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ }
+
+ return
+ }
+
+ // update DB
+ updated := database.MultiPanel{
+ Id: multiPanel.Id,
+ MessageId: messageId,
+ ChannelId: data.ChannelId,
+ GuildId: guildId,
+ Title: data.Title,
+ Content: data.Content,
+ Colour: int(data.Colour),
+ }
+
+ if err = dbclient.Client.MultiPanels.Update(multiPanel.Id, updated); err != nil {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ return
+ }
+
+ // TODO: one query for ACID purposes
+ // delete old targets
+ if err := dbclient.Client.MultiPanelTargets.DeleteAll(multiPanel.Id); err != nil {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ return
+ }
+
+ // insert new targets
+ group, _ := errgroup.WithContext(context.Background())
+ for _, panel := range panels {
+ panel := panel
+
+ group.Go(func() error {
+ return dbclient.Client.MultiPanelTargets.Insert(multiPanel.Id, panel.MessageId)
+ })
+ }
+
+ if err := group.Wait(); err != nil {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ return
+ }
+
+ ctx.JSON(200, gin.H{
+ "success": true,
+ "data": multiPanel,
+ })
+}
diff --git a/app/http/endpoints/api/panelcreate.go b/app/http/endpoints/api/panel/panelcreate.go
similarity index 98%
rename from app/http/endpoints/api/panelcreate.go
rename to app/http/endpoints/api/panel/panelcreate.go
index 0b8c56d..e29b8c0 100644
--- a/app/http/endpoints/api/panelcreate.go
+++ b/app/http/endpoints/api/panel/panelcreate.go
@@ -1,6 +1,7 @@
package api
import (
+ "errors"
"github.com/TicketsBot/GoPanel/botcontext"
dbclient "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/rpc"
@@ -64,6 +65,7 @@ func CreatePanel(ctx *gin.Context) {
}
if !data.doValidations(ctx, guildId) {
+ ctx.JSON(400, utils.ErrorToResponse(errors.New("Validation failed")))
return
}
diff --git a/app/http/endpoints/api/paneldelete.go b/app/http/endpoints/api/panel/paneldelete.go
similarity index 100%
rename from app/http/endpoints/api/paneldelete.go
rename to app/http/endpoints/api/panel/paneldelete.go
diff --git a/app/http/endpoints/api/panellist.go b/app/http/endpoints/api/panel/panellist.go
similarity index 100%
rename from app/http/endpoints/api/panellist.go
rename to app/http/endpoints/api/panel/panellist.go
diff --git a/app/http/endpoints/api/panelupdate.go b/app/http/endpoints/api/panel/panelupdate.go
similarity index 64%
rename from app/http/endpoints/api/panelupdate.go
rename to app/http/endpoints/api/panel/panelupdate.go
index 1a1f580..3c6931c 100644
--- a/app/http/endpoints/api/panelupdate.go
+++ b/app/http/endpoints/api/panel/panelupdate.go
@@ -1,15 +1,20 @@
package api
import (
+ "context"
+ "errors"
"github.com/TicketsBot/GoPanel/botcontext"
dbclient "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/rpc"
+ "github.com/TicketsBot/GoPanel/utils"
"github.com/TicketsBot/common/premium"
"github.com/TicketsBot/database"
"github.com/gin-gonic/gin"
"github.com/rxdn/gdl/rest"
"github.com/rxdn/gdl/rest/request"
+ "golang.org/x/sync/errgroup"
"strconv"
+ "sync"
)
func UpdatePanel(ctx *gin.Context) {
@@ -17,29 +22,20 @@ func UpdatePanel(ctx *gin.Context) {
botContext, err := botcontext.ContextForGuild(guildId)
if err != nil {
- ctx.AbortWithStatusJSON(500, gin.H{
- "success": false,
- "error": err.Error(),
- })
+ ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
return
}
var data panel
if err := ctx.BindJSON(&data); err != nil {
- ctx.AbortWithStatusJSON(400, gin.H{
- "success": false,
- "error": err.Error(),
- })
+ ctx.AbortWithStatusJSON(400, utils.ErrorToResponse(err))
return
}
messageId, err := strconv.ParseUint(ctx.Param("message"), 10, 64)
if err != nil {
- ctx.AbortWithStatusJSON(400, gin.H{
- "success": false,
- "error": err.Error(),
- })
+ ctx.AbortWithStatusJSON(400, utils.ErrorToResponse(err))
return
}
@@ -48,10 +44,7 @@ func UpdatePanel(ctx *gin.Context) {
// get existing
existing, err := dbclient.Client.Panel.Get(data.MessageId)
if err != nil {
- ctx.AbortWithStatusJSON(500, gin.H{
- "success": false,
- "error": err.Error(),
- })
+ ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
return
}
@@ -65,6 +58,59 @@ func UpdatePanel(ctx *gin.Context) {
}
if !data.doValidations(ctx, guildId) {
+ ctx.JSON(400, utils.ErrorToResponse(errors.New("Validation failed")))
+ return
+ }
+
+ // check if this will break a multi-panel;
+ // first, get any multipanels this panel belongs to
+ multiPanels, err := dbclient.Client.MultiPanelTargets.GetMultiPanels(existing.MessageId)
+ if err != nil {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ return
+ }
+
+ var wouldHaveDuplicateEmote bool
+
+ {
+ var duplicateLock sync.Mutex
+
+ group, _ := errgroup.WithContext(context.Background())
+ for _, multiPanelId := range multiPanels {
+ multiPanelId := multiPanelId
+
+ group.Go(func() error {
+ // get the sub-panels of the multi-panel
+ subPanels, err := dbclient.Client.MultiPanelTargets.GetPanels(multiPanelId)
+ if err != nil {
+ return err
+ }
+
+ for _, subPanel := range subPanels {
+ if subPanel.MessageId == existing.MessageId {
+ continue
+ }
+
+ if subPanel.ReactionEmote == data.Emote {
+ duplicateLock.Lock()
+ wouldHaveDuplicateEmote = true
+ duplicateLock.Unlock()
+ break
+ }
+ }
+
+ return nil
+ })
+ }
+
+ if err := group.Wait(); err != nil {
+ ctx.JSON(500, utils.ErrorToResponse(err))
+ return
+ }
+ }
+
+ if wouldHaveDuplicateEmote {
+ ctx.JSON(400, utils.ErrorToResponse(errors.New("Changing the reaction emote to this value would cause a conflict in a multi-panel")))
return
}
@@ -98,10 +144,7 @@ func UpdatePanel(ctx *gin.Context) {
})
} else {
// TODO: Most appropriate error?
- ctx.AbortWithStatusJSON(500, gin.H{
- "success": false,
- "error": err.Error(),
- })
+ ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
}
return
@@ -116,10 +159,7 @@ func UpdatePanel(ctx *gin.Context) {
})
} else {
// TODO: Most appropriate error?
- ctx.AbortWithStatusJSON(500, gin.H{
- "success": false,
- "error": err.Error(),
- })
+ ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
}
return
@@ -140,29 +180,20 @@ func UpdatePanel(ctx *gin.Context) {
}
if err = dbclient.Client.Panel.Update(messageId, panel); err != nil {
- ctx.AbortWithStatusJSON(500, gin.H{
- "success": false,
- "error": err.Error(),
- })
+ ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
return
}
// insert role mention data
// delete old data
if err = dbclient.Client.PanelRoleMentions.DeleteAll(newMessageId); err != nil {
- ctx.AbortWithStatusJSON(500, gin.H{
- "success": false,
- "error": err.Error(),
- })
+ ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
return
}
// TODO: Reduce to 1 query
if err = dbclient.Client.PanelUserMention.Set(newMessageId, false); err != nil {
- ctx.AbortWithStatusJSON(500, gin.H{
- "success": false,
- "error": err.Error(),
- })
+ ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
return
}
@@ -170,19 +201,13 @@ func UpdatePanel(ctx *gin.Context) {
for _, mention := range data.Mentions {
if mention == "user" {
if err = dbclient.Client.PanelUserMention.Set(newMessageId, true); err != nil {
- ctx.AbortWithStatusJSON(500, gin.H{
- "success": false,
- "error": err.Error(),
- })
+ ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
return
}
} else {
roleId, err := strconv.ParseUint(mention, 10, 64)
if err != nil {
- ctx.AbortWithStatusJSON(500, gin.H{
- "success": false,
- "error": err.Error(),
- })
+ ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
return
}
@@ -190,10 +215,7 @@ func UpdatePanel(ctx *gin.Context) {
// not too much of an issue if it isnt
if err = dbclient.Client.PanelRoleMentions.Add(newMessageId, roleId); err != nil {
- ctx.AbortWithStatusJSON(500, gin.H{
- "success": false,
- "error": err.Error(),
- })
+ ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
return
}
}
diff --git a/app/http/endpoints/api/claimsettings.go b/app/http/endpoints/api/settings/claimsettings.go
similarity index 100%
rename from app/http/endpoints/api/claimsettings.go
rename to app/http/endpoints/api/settings/claimsettings.go
diff --git a/app/http/endpoints/api/settings.go b/app/http/endpoints/api/settings/settings.go
similarity index 100%
rename from app/http/endpoints/api/settings.go
rename to app/http/endpoints/api/settings/settings.go
diff --git a/app/http/endpoints/api/updateclaimsettings.go b/app/http/endpoints/api/settings/updateclaimsettings.go
similarity index 100%
rename from app/http/endpoints/api/updateclaimsettings.go
rename to app/http/endpoints/api/settings/updateclaimsettings.go
diff --git a/app/http/endpoints/api/updatesettings.go b/app/http/endpoints/api/settings/updatesettings.go
similarity index 100%
rename from app/http/endpoints/api/updatesettings.go
rename to app/http/endpoints/api/settings/updatesettings.go
diff --git a/app/http/endpoints/api/tagcreate.go b/app/http/endpoints/api/tags/tagcreate.go
similarity index 100%
rename from app/http/endpoints/api/tagcreate.go
rename to app/http/endpoints/api/tags/tagcreate.go
diff --git a/app/http/endpoints/api/tagdelete.go b/app/http/endpoints/api/tags/tagdelete.go
similarity index 100%
rename from app/http/endpoints/api/tagdelete.go
rename to app/http/endpoints/api/tags/tagdelete.go
diff --git a/app/http/endpoints/api/tagslist.go b/app/http/endpoints/api/tags/tagslist.go
similarity index 100%
rename from app/http/endpoints/api/tagslist.go
rename to app/http/endpoints/api/tags/tagslist.go
diff --git a/app/http/endpoints/api/closeticket.go b/app/http/endpoints/api/ticket/closeticket.go
similarity index 100%
rename from app/http/endpoints/api/closeticket.go
rename to app/http/endpoints/api/ticket/closeticket.go
diff --git a/app/http/endpoints/api/getticket.go b/app/http/endpoints/api/ticket/getticket.go
similarity index 100%
rename from app/http/endpoints/api/getticket.go
rename to app/http/endpoints/api/ticket/getticket.go
diff --git a/app/http/endpoints/api/gettickets.go b/app/http/endpoints/api/ticket/gettickets.go
similarity index 100%
rename from app/http/endpoints/api/gettickets.go
rename to app/http/endpoints/api/ticket/gettickets.go
diff --git a/app/http/endpoints/api/sendmessage.go b/app/http/endpoints/api/ticket/sendmessage.go
similarity index 100%
rename from app/http/endpoints/api/sendmessage.go
rename to app/http/endpoints/api/ticket/sendmessage.go
diff --git a/app/http/endpoints/api/whitelabelget.go b/app/http/endpoints/api/whitelabel/whitelabelget.go
similarity index 100%
rename from app/http/endpoints/api/whitelabelget.go
rename to app/http/endpoints/api/whitelabel/whitelabelget.go
diff --git a/app/http/endpoints/api/whitelabelgeterrors.go b/app/http/endpoints/api/whitelabel/whitelabelgeterrors.go
similarity index 100%
rename from app/http/endpoints/api/whitelabelgeterrors.go
rename to app/http/endpoints/api/whitelabel/whitelabelgeterrors.go
diff --git a/app/http/endpoints/api/whitelabelgetguilds.go b/app/http/endpoints/api/whitelabel/whitelabelgetguilds.go
similarity index 100%
rename from app/http/endpoints/api/whitelabelgetguilds.go
rename to app/http/endpoints/api/whitelabel/whitelabelgetguilds.go
diff --git a/app/http/endpoints/api/whitelabelpost.go b/app/http/endpoints/api/whitelabel/whitelabelpost.go
similarity index 100%
rename from app/http/endpoints/api/whitelabelpost.go
rename to app/http/endpoints/api/whitelabel/whitelabelpost.go
diff --git a/app/http/endpoints/api/whitelabelpostmodmail.go b/app/http/endpoints/api/whitelabel/whitelabelpostmodmail.go
similarity index 100%
rename from app/http/endpoints/api/whitelabelpostmodmail.go
rename to app/http/endpoints/api/whitelabel/whitelabelpostmodmail.go
diff --git a/app/http/endpoints/api/whitelabelstatuspost.go b/app/http/endpoints/api/whitelabel/whitelabelstatuspost.go
similarity index 100%
rename from app/http/endpoints/api/whitelabelstatuspost.go
rename to app/http/endpoints/api/whitelabel/whitelabelstatuspost.go
diff --git a/app/http/server.go b/app/http/server.go
index 47ca281..db9c4c9 100644
--- a/app/http/server.go
+++ b/app/http/server.go
@@ -3,6 +3,14 @@ package http
import (
"fmt"
"github.com/TicketsBot/GoPanel/app/http/endpoints/api"
+ api_autoclose "github.com/TicketsBot/GoPanel/app/http/endpoints/api/autoclose"
+ api_blacklist "github.com/TicketsBot/GoPanel/app/http/endpoints/api/blacklist"
+ api_logs "github.com/TicketsBot/GoPanel/app/http/endpoints/api/logs"
+ api_panels "github.com/TicketsBot/GoPanel/app/http/endpoints/api/panel"
+ api_settings "github.com/TicketsBot/GoPanel/app/http/endpoints/api/settings"
+ api_tags "github.com/TicketsBot/GoPanel/app/http/endpoints/api/tags"
+ api_ticket "github.com/TicketsBot/GoPanel/app/http/endpoints/api/ticket"
+ api_whitelabel "github.com/TicketsBot/GoPanel/app/http/endpoints/api/whitelabel"
"github.com/TicketsBot/GoPanel/app/http/endpoints/manage"
"github.com/TicketsBot/GoPanel/app/http/endpoints/root"
"github.com/TicketsBot/GoPanel/app/http/middleware"
@@ -83,35 +91,40 @@ func StartServer() {
guildAuthApiSupport.GET("/user/:user", api.UserHandler)
guildAuthApiSupport.GET("/roles", api.RolesHandler)
- guildAuthApiAdmin.GET("/settings", api.GetSettingsHandler)
- guildAuthApiAdmin.POST("/settings", api.UpdateSettingsHandler)
+ guildAuthApiAdmin.GET("/settings", api_settings.GetSettingsHandler)
+ guildAuthApiAdmin.POST("/settings", api_settings.UpdateSettingsHandler)
- guildAuthApiSupport.GET("/blacklist", api.GetBlacklistHandler)
- guildAuthApiSupport.PUT("/blacklist", api.AddBlacklistHandler)
- guildAuthApiSupport.DELETE("/blacklist/:user", api.RemoveBlacklistHandler)
+ guildAuthApiSupport.GET("/blacklist", api_blacklist.GetBlacklistHandler)
+ guildAuthApiSupport.PUT("/blacklist", api_blacklist.AddBlacklistHandler)
+ guildAuthApiSupport.DELETE("/blacklist/:user", api_blacklist.RemoveBlacklistHandler)
- guildAuthApiAdmin.GET("/panels", api.ListPanels)
- guildAuthApiAdmin.PUT("/panels", api.CreatePanel)
- guildAuthApiAdmin.PUT("/panels/:message", api.UpdatePanel)
- guildAuthApiAdmin.DELETE("/panels/:message", api.DeletePanel)
+ guildAuthApiAdmin.GET("/panels", api_panels.ListPanels)
+ guildAuthApiAdmin.PUT("/panels", api_panels.CreatePanel)
+ guildAuthApiAdmin.PUT("/panels/:message", api_panels.UpdatePanel)
+ guildAuthApiAdmin.DELETE("/panels/:message", api_panels.DeletePanel)
- guildAuthApiSupport.GET("/logs/", api.GetLogs)
- guildAuthApiSupport.GET("/modmail/logs/", api.GetModmailLogs)
+ guildAuthApiAdmin.GET("/multipanels", api_panels.MultiPanelList)
+ guildAuthApiAdmin.POST("/multipanels", api_panels.MultiPanelCreate)
+ guildAuthApiAdmin.PATCH("/multipanels/:panelid", api_panels.MultiPanelUpdate)
+ guildAuthApiAdmin.DELETE("/multipanels/:panelid", api_panels.MultiPanelDelete)
- guildAuthApiSupport.GET("/tickets", api.GetTickets)
- guildAuthApiSupport.GET("/tickets/:ticketId", api.GetTicket)
- guildAuthApiSupport.POST("/tickets/:ticketId", api.SendMessage)
- guildAuthApiSupport.DELETE("/tickets/:ticketId", api.CloseTicket)
+ guildAuthApiSupport.GET("/logs/", api_logs.GetLogs)
+ guildAuthApiSupport.GET("/modmail/logs/", api_logs.GetModmailLogs)
- guildAuthApiSupport.GET("/tags", api.TagsListHandler)
- guildAuthApiSupport.PUT("/tags", api.CreateTag)
- guildAuthApiSupport.DELETE("/tags/:tag", api.DeleteTag)
+ guildAuthApiSupport.GET("/tickets", api_ticket.GetTickets)
+ guildAuthApiSupport.GET("/tickets/:ticketId", api_ticket.GetTicket)
+ guildAuthApiSupport.POST("/tickets/:ticketId", api_ticket.SendMessage)
+ guildAuthApiSupport.DELETE("/tickets/:ticketId", api_ticket.CloseTicket)
- guildAuthApiAdmin.GET("/claimsettings", api.GetClaimSettings)
- guildAuthApiAdmin.POST("/claimsettings", api.PostClaimSettings)
+ guildAuthApiSupport.GET("/tags", api_tags.TagsListHandler)
+ guildAuthApiSupport.PUT("/tags", api_tags.CreateTag)
+ guildAuthApiSupport.DELETE("/tags/:tag", api_tags.DeleteTag)
- guildAuthApiAdmin.GET("/autoclose", api.GetAutoClose)
- guildAuthApiAdmin.POST("/autoclose", api.PostAutoClose)
+ guildAuthApiAdmin.GET("/claimsettings", api_settings.GetClaimSettings)
+ guildAuthApiAdmin.POST("/claimsettings", api_settings.PostClaimSettings)
+
+ guildAuthApiAdmin.GET("/autoclose", api_autoclose.GetAutoClose)
+ guildAuthApiAdmin.POST("/autoclose", api_autoclose.PostAutoClose)
}
userGroup := router.Group("/user", middleware.AuthenticateToken)
@@ -123,13 +136,13 @@ func StartServer() {
whitelabelGroup := userGroup.Group("/whitelabel", middleware.VerifyWhitelabel(false))
whitelabelApiGroup := userGroup.Group("/whitelabel", middleware.VerifyWhitelabel(true))
- whitelabelGroup.GET("/", api.WhitelabelGet)
- whitelabelApiGroup.GET("/errors", api.WhitelabelGetErrors)
- whitelabelApiGroup.GET("/guilds", api.WhitelabelGetGuilds)
- whitelabelApiGroup.POST("/modmail", api.WhitelabelModmailPost)
+ whitelabelGroup.GET("/", api_whitelabel.WhitelabelGet)
+ whitelabelApiGroup.GET("/errors", api_whitelabel.WhitelabelGetErrors)
+ whitelabelApiGroup.GET("/guilds", api_whitelabel.WhitelabelGetGuilds)
+ whitelabelApiGroup.POST("/modmail", api_whitelabel.WhitelabelModmailPost)
- whitelabelApiGroup.Group("/").Use(createLimiter(10, time.Minute)).POST("/", api.WhitelabelPost)
- whitelabelApiGroup.Group("/").Use(createLimiter(1, time.Second * 5)).POST("/status", api.WhitelabelStatusPost)
+ whitelabelApiGroup.Group("/").Use(createLimiter(10, time.Minute)).POST("/", api_whitelabel.WhitelabelPost)
+ whitelabelApiGroup.Group("/").Use(createLimiter(1, time.Second * 5)).POST("/status", api_whitelabel.WhitelabelStatusPost)
}
}
@@ -150,7 +163,7 @@ func createRenderer() multitemplate.Renderer {
r = addManageTemplate(r, "settings", "./public/templates/includes/substitutionmodal.tmpl")
r = addManageTemplate(r, "ticketlist")
r = addManageTemplate(r, "ticketview")
- r = addManageTemplate(r, "panels", "./public/templates/includes/substitutionmodal.tmpl", "./public/templates/includes/paneleditmodal.tmpl")
+ r = addManageTemplate(r, "panels", "./public/templates/includes/substitutionmodal.tmpl", "./public/templates/includes/paneleditmodal.tmpl", "./public/templates/includes/multipaneleditmodal.tmpl")
r = addManageTemplate(r, "tags")
r = addErrorTemplate(r)
@@ -180,6 +193,7 @@ func addManageTemplate(renderer multitemplate.Renderer, name string, extra ...st
"./public/templates/includes/sidebar.tmpl",
"./public/templates/includes/navbar.tmpl",
"./public/templates/includes/loadingscreen.tmpl",
+ "./public/templates/includes/notifymodal.tmpl",
fmt.Sprintf("./public/templates/views/%s.tmpl", name),
}
diff --git a/go.mod b/go.mod
index ad5909b..9c4f776 100644
--- a/go.mod
+++ b/go.mod
@@ -6,7 +6,7 @@ require (
github.com/BurntSushi/toml v0.3.1
github.com/TicketsBot/archiverclient v0.0.0-20200704164621-09d42dd941e0
github.com/TicketsBot/common v0.0.0-20200702195837-7afe5e77d1df
- github.com/TicketsBot/database v0.0.0-20200708121851-08f2e7582b28
+ github.com/TicketsBot/database v0.0.0-20200723134637-72f4cd31eef6
github.com/TicketsBot/logarchiver v0.0.0-20200425163447-199b93429026 // indirect
github.com/apex/log v1.1.2
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
diff --git a/public/static/css/style.css b/public/static/css/style.css
index c5c258e..38df408 100644
--- a/public/static/css/style.css
+++ b/public/static/css/style.css
@@ -212,7 +212,7 @@ html > ::-webkit-scrollbar {
background-color: #121212;
height: 100px;
margin-bottom: 10px;
- border-radius: 25px;
+ border-radius: 10px;
display: flex;
cursor: pointer;
}
diff --git a/public/templates/includes/multipaneleditmodal.tmpl b/public/templates/includes/multipaneleditmodal.tmpl
new file mode 100644
index 0000000..d2c8c8e
--- /dev/null
+++ b/public/templates/includes/multipaneleditmodal.tmpl
@@ -0,0 +1,147 @@
+{{define "multipaneleditmodal"}}
+
+
+
+{{end}}
\ No newline at end of file
diff --git a/public/templates/includes/notifymodal.tmpl b/public/templates/includes/notifymodal.tmpl
new file mode 100644
index 0000000..d6ce47f
--- /dev/null
+++ b/public/templates/includes/notifymodal.tmpl
@@ -0,0 +1,52 @@
+{{define "notifymodal"}}
+
+
+
+{{end}}
\ No newline at end of file
diff --git a/public/templates/includes/paneleditmodal.tmpl b/public/templates/includes/paneleditmodal.tmpl
index fa76cc1..2792662 100644
--- a/public/templates/includes/paneleditmodal.tmpl
+++ b/public/templates/includes/paneleditmodal.tmpl
@@ -147,25 +147,15 @@
}
const channels = await getChannels();
- await fillChannels('edit-channel-container', channels);
- await fillCategories('edit-category-container', channels);
+ fillChannels('edit-channel-container', channels);
+ fillCategories('edit-category-container', channels);
await fillMentions('edit-mentions');
- setActiveChannel(panel);
+ setActiveChannel('edit-channel-container', panel.channel_id);
setActiveCategory(panel);
setActiveMentions(panel);
}
- function setActiveChannel(panel) {
- const select = document.getElementById('edit-channel-container');
- for (let i = 0; i < select.children.length; i++) {
- const child = select.children[i];
- if (child.value === panel.channel_id) {
- select.selectedIndex = i;
- }
- }
- }
-
function setActiveCategory(panel) {
const select = document.getElementById('edit-category-container');
for (let i = 0; i < select.children.length; i++) {
diff --git a/public/templates/layouts/manage.tmpl b/public/templates/layouts/manage.tmpl
index 62c8a79..9e92e0b 100644
--- a/public/templates/layouts/manage.tmpl
+++ b/public/templates/layouts/manage.tmpl
@@ -8,6 +8,10 @@
{{template "navbar" .}}
{{template "loadingscreen" .}}
+
+
+ {{template "notifymodal" .}}
+
{{template "content" .}}
diff --git a/public/templates/views/panels.tmpl b/public/templates/views/panels.tmpl
index 101db3a..0acbc12 100644
--- a/public/templates/views/panels.tmpl
+++ b/public/templates/views/panels.tmpl
@@ -1,12 +1,12 @@
{{define "content"}}
-
{{template "substitutions" .}}
{{template "paneleditmodal" .}}
+ {{template "multipaneleditmodal" .}}
-
+
+
+
+
+
+
+
+
+ Embed Title |
+ Edit |
+ Delete |
+
+
+
+
+
+
+
+
+
+
-