multipanels
This commit is contained in:
parent
e61295dd3c
commit
189ec33874
236
app/http/endpoints/api/panel/multipanelcreate.go
Normal file
236
app/http/endpoints/api/panel/multipanelcreate.go
Normal file
@ -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
|
||||||
|
}
|
58
app/http/endpoints/api/panel/multipaneldelete.go
Normal file
58
app/http/endpoints/api/panel/multipaneldelete.go
Normal file
@ -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)
|
||||||
|
}
|
56
app/http/endpoints/api/panel/multipanellist.go
Normal file
56
app/http/endpoints/api/panel/multipanellist.go
Normal file
@ -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,
|
||||||
|
})
|
||||||
|
}
|
143
app/http/endpoints/api/panel/multipanelupdate.go
Normal file
143
app/http/endpoints/api/panel/multipanelupdate.go
Normal file
@ -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,
|
||||||
|
})
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"github.com/TicketsBot/GoPanel/botcontext"
|
"github.com/TicketsBot/GoPanel/botcontext"
|
||||||
dbclient "github.com/TicketsBot/GoPanel/database"
|
dbclient "github.com/TicketsBot/GoPanel/database"
|
||||||
"github.com/TicketsBot/GoPanel/rpc"
|
"github.com/TicketsBot/GoPanel/rpc"
|
||||||
@ -64,6 +65,7 @@ func CreatePanel(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !data.doValidations(ctx, guildId) {
|
if !data.doValidations(ctx, guildId) {
|
||||||
|
ctx.JSON(400, utils.ErrorToResponse(errors.New("Validation failed")))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -1,15 +1,20 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"github.com/TicketsBot/GoPanel/botcontext"
|
"github.com/TicketsBot/GoPanel/botcontext"
|
||||||
dbclient "github.com/TicketsBot/GoPanel/database"
|
dbclient "github.com/TicketsBot/GoPanel/database"
|
||||||
"github.com/TicketsBot/GoPanel/rpc"
|
"github.com/TicketsBot/GoPanel/rpc"
|
||||||
|
"github.com/TicketsBot/GoPanel/utils"
|
||||||
"github.com/TicketsBot/common/premium"
|
"github.com/TicketsBot/common/premium"
|
||||||
"github.com/TicketsBot/database"
|
"github.com/TicketsBot/database"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/rxdn/gdl/rest"
|
"github.com/rxdn/gdl/rest"
|
||||||
"github.com/rxdn/gdl/rest/request"
|
"github.com/rxdn/gdl/rest/request"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
func UpdatePanel(ctx *gin.Context) {
|
func UpdatePanel(ctx *gin.Context) {
|
||||||
@ -17,29 +22,20 @@ func UpdatePanel(ctx *gin.Context) {
|
|||||||
|
|
||||||
botContext, err := botcontext.ContextForGuild(guildId)
|
botContext, err := botcontext.ContextForGuild(guildId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithStatusJSON(500, gin.H{
|
ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
|
||||||
"success": false,
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var data panel
|
var data panel
|
||||||
|
|
||||||
if err := ctx.BindJSON(&data); err != nil {
|
if err := ctx.BindJSON(&data); err != nil {
|
||||||
ctx.AbortWithStatusJSON(400, gin.H{
|
ctx.AbortWithStatusJSON(400, utils.ErrorToResponse(err))
|
||||||
"success": false,
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
messageId, err := strconv.ParseUint(ctx.Param("message"), 10, 64)
|
messageId, err := strconv.ParseUint(ctx.Param("message"), 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithStatusJSON(400, gin.H{
|
ctx.AbortWithStatusJSON(400, utils.ErrorToResponse(err))
|
||||||
"success": false,
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,10 +44,7 @@ func UpdatePanel(ctx *gin.Context) {
|
|||||||
// get existing
|
// get existing
|
||||||
existing, err := dbclient.Client.Panel.Get(data.MessageId)
|
existing, err := dbclient.Client.Panel.Get(data.MessageId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithStatusJSON(500, gin.H{
|
ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
|
||||||
"success": false,
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +58,59 @@ func UpdatePanel(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !data.doValidations(ctx, guildId) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,10 +144,7 @@ func UpdatePanel(ctx *gin.Context) {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// TODO: Most appropriate error?
|
// TODO: Most appropriate error?
|
||||||
ctx.AbortWithStatusJSON(500, gin.H{
|
ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
|
||||||
"success": false,
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -116,10 +159,7 @@ func UpdatePanel(ctx *gin.Context) {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// TODO: Most appropriate error?
|
// TODO: Most appropriate error?
|
||||||
ctx.AbortWithStatusJSON(500, gin.H{
|
ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
|
||||||
"success": false,
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -140,29 +180,20 @@ func UpdatePanel(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err = dbclient.Client.Panel.Update(messageId, panel); err != nil {
|
if err = dbclient.Client.Panel.Update(messageId, panel); err != nil {
|
||||||
ctx.AbortWithStatusJSON(500, gin.H{
|
ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
|
||||||
"success": false,
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert role mention data
|
// insert role mention data
|
||||||
// delete old data
|
// delete old data
|
||||||
if err = dbclient.Client.PanelRoleMentions.DeleteAll(newMessageId); err != nil {
|
if err = dbclient.Client.PanelRoleMentions.DeleteAll(newMessageId); err != nil {
|
||||||
ctx.AbortWithStatusJSON(500, gin.H{
|
ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
|
||||||
"success": false,
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Reduce to 1 query
|
// TODO: Reduce to 1 query
|
||||||
if err = dbclient.Client.PanelUserMention.Set(newMessageId, false); err != nil {
|
if err = dbclient.Client.PanelUserMention.Set(newMessageId, false); err != nil {
|
||||||
ctx.AbortWithStatusJSON(500, gin.H{
|
ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
|
||||||
"success": false,
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,19 +201,13 @@ func UpdatePanel(ctx *gin.Context) {
|
|||||||
for _, mention := range data.Mentions {
|
for _, mention := range data.Mentions {
|
||||||
if mention == "user" {
|
if mention == "user" {
|
||||||
if err = dbclient.Client.PanelUserMention.Set(newMessageId, true); err != nil {
|
if err = dbclient.Client.PanelUserMention.Set(newMessageId, true); err != nil {
|
||||||
ctx.AbortWithStatusJSON(500, gin.H{
|
ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
|
||||||
"success": false,
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
roleId, err := strconv.ParseUint(mention, 10, 64)
|
roleId, err := strconv.ParseUint(mention, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.AbortWithStatusJSON(500, gin.H{
|
ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
|
||||||
"success": false,
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,10 +215,7 @@ func UpdatePanel(ctx *gin.Context) {
|
|||||||
// not too much of an issue if it isnt
|
// not too much of an issue if it isnt
|
||||||
|
|
||||||
if err = dbclient.Client.PanelRoleMentions.Add(newMessageId, roleId); err != nil {
|
if err = dbclient.Client.PanelRoleMentions.Add(newMessageId, roleId); err != nil {
|
||||||
ctx.AbortWithStatusJSON(500, gin.H{
|
ctx.AbortWithStatusJSON(500, utils.ErrorToResponse(err))
|
||||||
"success": false,
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,6 +3,14 @@ package http
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/TicketsBot/GoPanel/app/http/endpoints/api"
|
"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/manage"
|
||||||
"github.com/TicketsBot/GoPanel/app/http/endpoints/root"
|
"github.com/TicketsBot/GoPanel/app/http/endpoints/root"
|
||||||
"github.com/TicketsBot/GoPanel/app/http/middleware"
|
"github.com/TicketsBot/GoPanel/app/http/middleware"
|
||||||
@ -83,35 +91,40 @@ func StartServer() {
|
|||||||
guildAuthApiSupport.GET("/user/:user", api.UserHandler)
|
guildAuthApiSupport.GET("/user/:user", api.UserHandler)
|
||||||
guildAuthApiSupport.GET("/roles", api.RolesHandler)
|
guildAuthApiSupport.GET("/roles", api.RolesHandler)
|
||||||
|
|
||||||
guildAuthApiAdmin.GET("/settings", api.GetSettingsHandler)
|
guildAuthApiAdmin.GET("/settings", api_settings.GetSettingsHandler)
|
||||||
guildAuthApiAdmin.POST("/settings", api.UpdateSettingsHandler)
|
guildAuthApiAdmin.POST("/settings", api_settings.UpdateSettingsHandler)
|
||||||
|
|
||||||
guildAuthApiSupport.GET("/blacklist", api.GetBlacklistHandler)
|
guildAuthApiSupport.GET("/blacklist", api_blacklist.GetBlacklistHandler)
|
||||||
guildAuthApiSupport.PUT("/blacklist", api.AddBlacklistHandler)
|
guildAuthApiSupport.PUT("/blacklist", api_blacklist.AddBlacklistHandler)
|
||||||
guildAuthApiSupport.DELETE("/blacklist/:user", api.RemoveBlacklistHandler)
|
guildAuthApiSupport.DELETE("/blacklist/:user", api_blacklist.RemoveBlacklistHandler)
|
||||||
|
|
||||||
guildAuthApiAdmin.GET("/panels", api.ListPanels)
|
guildAuthApiAdmin.GET("/panels", api_panels.ListPanels)
|
||||||
guildAuthApiAdmin.PUT("/panels", api.CreatePanel)
|
guildAuthApiAdmin.PUT("/panels", api_panels.CreatePanel)
|
||||||
guildAuthApiAdmin.PUT("/panels/:message", api.UpdatePanel)
|
guildAuthApiAdmin.PUT("/panels/:message", api_panels.UpdatePanel)
|
||||||
guildAuthApiAdmin.DELETE("/panels/:message", api.DeletePanel)
|
guildAuthApiAdmin.DELETE("/panels/:message", api_panels.DeletePanel)
|
||||||
|
|
||||||
guildAuthApiSupport.GET("/logs/", api.GetLogs)
|
guildAuthApiAdmin.GET("/multipanels", api_panels.MultiPanelList)
|
||||||
guildAuthApiSupport.GET("/modmail/logs/", api.GetModmailLogs)
|
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("/logs/", api_logs.GetLogs)
|
||||||
guildAuthApiSupport.GET("/tickets/:ticketId", api.GetTicket)
|
guildAuthApiSupport.GET("/modmail/logs/", api_logs.GetModmailLogs)
|
||||||
guildAuthApiSupport.POST("/tickets/:ticketId", api.SendMessage)
|
|
||||||
guildAuthApiSupport.DELETE("/tickets/:ticketId", api.CloseTicket)
|
|
||||||
|
|
||||||
guildAuthApiSupport.GET("/tags", api.TagsListHandler)
|
guildAuthApiSupport.GET("/tickets", api_ticket.GetTickets)
|
||||||
guildAuthApiSupport.PUT("/tags", api.CreateTag)
|
guildAuthApiSupport.GET("/tickets/:ticketId", api_ticket.GetTicket)
|
||||||
guildAuthApiSupport.DELETE("/tags/:tag", api.DeleteTag)
|
guildAuthApiSupport.POST("/tickets/:ticketId", api_ticket.SendMessage)
|
||||||
|
guildAuthApiSupport.DELETE("/tickets/:ticketId", api_ticket.CloseTicket)
|
||||||
|
|
||||||
guildAuthApiAdmin.GET("/claimsettings", api.GetClaimSettings)
|
guildAuthApiSupport.GET("/tags", api_tags.TagsListHandler)
|
||||||
guildAuthApiAdmin.POST("/claimsettings", api.PostClaimSettings)
|
guildAuthApiSupport.PUT("/tags", api_tags.CreateTag)
|
||||||
|
guildAuthApiSupport.DELETE("/tags/:tag", api_tags.DeleteTag)
|
||||||
|
|
||||||
guildAuthApiAdmin.GET("/autoclose", api.GetAutoClose)
|
guildAuthApiAdmin.GET("/claimsettings", api_settings.GetClaimSettings)
|
||||||
guildAuthApiAdmin.POST("/autoclose", api.PostAutoClose)
|
guildAuthApiAdmin.POST("/claimsettings", api_settings.PostClaimSettings)
|
||||||
|
|
||||||
|
guildAuthApiAdmin.GET("/autoclose", api_autoclose.GetAutoClose)
|
||||||
|
guildAuthApiAdmin.POST("/autoclose", api_autoclose.PostAutoClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
userGroup := router.Group("/user", middleware.AuthenticateToken)
|
userGroup := router.Group("/user", middleware.AuthenticateToken)
|
||||||
@ -123,13 +136,13 @@ func StartServer() {
|
|||||||
whitelabelGroup := userGroup.Group("/whitelabel", middleware.VerifyWhitelabel(false))
|
whitelabelGroup := userGroup.Group("/whitelabel", middleware.VerifyWhitelabel(false))
|
||||||
whitelabelApiGroup := userGroup.Group("/whitelabel", middleware.VerifyWhitelabel(true))
|
whitelabelApiGroup := userGroup.Group("/whitelabel", middleware.VerifyWhitelabel(true))
|
||||||
|
|
||||||
whitelabelGroup.GET("/", api.WhitelabelGet)
|
whitelabelGroup.GET("/", api_whitelabel.WhitelabelGet)
|
||||||
whitelabelApiGroup.GET("/errors", api.WhitelabelGetErrors)
|
whitelabelApiGroup.GET("/errors", api_whitelabel.WhitelabelGetErrors)
|
||||||
whitelabelApiGroup.GET("/guilds", api.WhitelabelGetGuilds)
|
whitelabelApiGroup.GET("/guilds", api_whitelabel.WhitelabelGetGuilds)
|
||||||
whitelabelApiGroup.POST("/modmail", api.WhitelabelModmailPost)
|
whitelabelApiGroup.POST("/modmail", api_whitelabel.WhitelabelModmailPost)
|
||||||
|
|
||||||
whitelabelApiGroup.Group("/").Use(createLimiter(10, time.Minute)).POST("/", api.WhitelabelPost)
|
whitelabelApiGroup.Group("/").Use(createLimiter(10, time.Minute)).POST("/", api_whitelabel.WhitelabelPost)
|
||||||
whitelabelApiGroup.Group("/").Use(createLimiter(1, time.Second * 5)).POST("/status", api.WhitelabelStatusPost)
|
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, "settings", "./public/templates/includes/substitutionmodal.tmpl")
|
||||||
r = addManageTemplate(r, "ticketlist")
|
r = addManageTemplate(r, "ticketlist")
|
||||||
r = addManageTemplate(r, "ticketview")
|
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 = addManageTemplate(r, "tags")
|
||||||
|
|
||||||
r = addErrorTemplate(r)
|
r = addErrorTemplate(r)
|
||||||
@ -180,6 +193,7 @@ func addManageTemplate(renderer multitemplate.Renderer, name string, extra ...st
|
|||||||
"./public/templates/includes/sidebar.tmpl",
|
"./public/templates/includes/sidebar.tmpl",
|
||||||
"./public/templates/includes/navbar.tmpl",
|
"./public/templates/includes/navbar.tmpl",
|
||||||
"./public/templates/includes/loadingscreen.tmpl",
|
"./public/templates/includes/loadingscreen.tmpl",
|
||||||
|
"./public/templates/includes/notifymodal.tmpl",
|
||||||
fmt.Sprintf("./public/templates/views/%s.tmpl", name),
|
fmt.Sprintf("./public/templates/views/%s.tmpl", name),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
go.mod
2
go.mod
@ -6,7 +6,7 @@ require (
|
|||||||
github.com/BurntSushi/toml v0.3.1
|
github.com/BurntSushi/toml v0.3.1
|
||||||
github.com/TicketsBot/archiverclient v0.0.0-20200704164621-09d42dd941e0
|
github.com/TicketsBot/archiverclient v0.0.0-20200704164621-09d42dd941e0
|
||||||
github.com/TicketsBot/common v0.0.0-20200702195837-7afe5e77d1df
|
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/TicketsBot/logarchiver v0.0.0-20200425163447-199b93429026 // indirect
|
||||||
github.com/apex/log v1.1.2
|
github.com/apex/log v1.1.2
|
||||||
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
|
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
|
||||||
|
@ -212,7 +212,7 @@ html > ::-webkit-scrollbar {
|
|||||||
background-color: #121212;
|
background-color: #121212;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
border-radius: 25px;
|
border-radius: 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
147
public/templates/includes/multipaneleditmodal.tmpl
Normal file
147
public/templates/includes/multipaneleditmodal.tmpl
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
{{define "multipaneleditmodal"}}
|
||||||
|
<div class="modal fade" id="multieditmodal" tabindex="-1" role="dialog" aria-labelledby="multieditmodal" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title"><b>Edit Multi-Panel</b></h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<input type="hidden" id="multi-edit-id">
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4 pr-1">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="black">Embed Title</label>
|
||||||
|
<input type="text" class="form-control" placeholder="React to open a ticket" id="multi-edit-title">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8 pr-1">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="black">Embed Content</label>
|
||||||
|
<textarea type="text" class="form-control"
|
||||||
|
placeholder="Let users know which reaction corresponds to which panel. You are able to use emojis here."
|
||||||
|
id="multi-edit-content"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-2 pr-1">
|
||||||
|
<label class="black">Embed Colour</label>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input type="color" class="form-control input-fill" id="multi-edit-colour">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4 pr-1">
|
||||||
|
<label class="black">Embed Channel</label>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<div class="input-group-text">#</div>
|
||||||
|
</div>
|
||||||
|
<select class="form-control" id="multi-edit-channel-container">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 pr-1">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="black" for="mentions">Panels</label>
|
||||||
|
<select class="selectpicker form-control" id="multi-edit-panels" multiple data-live-search="true" data-dropup-auto="false" data-size="5" data-display="static">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-primary btn-fill" onclick="updateMultiPanel()">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function resetMultiEditModal() {
|
||||||
|
clear('multi-edit-title', 'multi-edit-content', 'multi-edit-colour');
|
||||||
|
$('#multi-edit-panels').selectpicker('deselectAll');
|
||||||
|
}
|
||||||
|
|
||||||
|
registerHideListener('multieditmodal');
|
||||||
|
$('#multieditmodal').on('hidden.bs.modal', resetEditModal);
|
||||||
|
|
||||||
|
async function openMultiEditModal(id) {
|
||||||
|
resetMultiEditModal();
|
||||||
|
|
||||||
|
const res = await axios.get('/api/{{.guildId}}/multipanels');
|
||||||
|
if (res.status !== 200) {
|
||||||
|
showToast("Error", res.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const panel = res.data.data.find(panel => panel.id === id);
|
||||||
|
if (panel === undefined) {
|
||||||
|
showToast('Error', 'Panel not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await fillMultiEditData(panel);
|
||||||
|
|
||||||
|
$('#multieditmodal').modal('show');
|
||||||
|
showBackdrop();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fillMultiEditData(panel) {
|
||||||
|
document.getElementById('multi-edit-id').value = panel.id;
|
||||||
|
document.getElementById('multi-edit-title').value = panel.title;
|
||||||
|
document.getElementById('multi-edit-content').value = panel.content;
|
||||||
|
document.getElementById('multi-edit-colour').value = `#${panel.colour.toString(16)}`;
|
||||||
|
|
||||||
|
const channels = await getChannels();
|
||||||
|
fillChannels('multi-edit-channel-container', channels);
|
||||||
|
setActiveChannel('multi-edit-channel-container', panel.channel_id);
|
||||||
|
|
||||||
|
// fill panel dropdown
|
||||||
|
const res = await axios.get('/api/{{.guildId}}/panels');
|
||||||
|
if (res.status !== 200) {
|
||||||
|
showToast("Error", res.data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#multi-edit-panels').selectpicker('val', panel.panels.map(p => p.message_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateMultiPanel() {
|
||||||
|
const channelContainer = document.getElementById('multi-edit-channel-container');
|
||||||
|
|
||||||
|
const panelId = getValue('multi-edit-id');
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
'title': getValue('multi-edit-title'),
|
||||||
|
'content': getValue('multi-edit-content'),
|
||||||
|
'colour': parseInt(`0x${getValue('multi-edit-colour').slice(1)}`),
|
||||||
|
'channel_id': channelContainer.options[channelContainer.selectedIndex].value,
|
||||||
|
'panels': $('#multi-edit-panels').val()
|
||||||
|
};
|
||||||
|
|
||||||
|
$('#multieditmodal').modal('hide');
|
||||||
|
|
||||||
|
const res = await axios.patch('/api/{{.guildId}}/multipanels/' + panelId, data);
|
||||||
|
|
||||||
|
if (res.status !== 200 || !res.data.success) {
|
||||||
|
notifyError(res.data.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update table
|
||||||
|
const tr = document.getElementById(panelId);
|
||||||
|
tr.children[0].textContent = data.title;
|
||||||
|
|
||||||
|
notify('Success', 'Multi-panel updated');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{{end}}
|
52
public/templates/includes/notifymodal.tmpl
Normal file
52
public/templates/includes/notifymodal.tmpl
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{{define "notifymodal"}}
|
||||||
|
<div class="modal fade" id="notificationmodal" tabindex="-1" role="dialog" aria-labelledby="notificationmodal" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title"><b id="notification-title"></b></h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-10 offset-md-1">
|
||||||
|
<p id="notification-message" style="text-align: center"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-primary btn-fill" onclick="closeNotificationModal()">Dismiss</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
registerHideListener('notificationmodal');
|
||||||
|
|
||||||
|
function notify(title, message) {
|
||||||
|
document.getElementById('notification-title').textContent = title;
|
||||||
|
document.getElementById('notification-message').textContent = message;
|
||||||
|
|
||||||
|
$('#notificationmodal').modal('show');
|
||||||
|
showBackdrop();
|
||||||
|
}
|
||||||
|
|
||||||
|
function notifyError(message) {
|
||||||
|
notify('Error', message);
|
||||||
|
}
|
||||||
|
|
||||||
|
function notifySuccess(message) {
|
||||||
|
notify('Success', message);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeNotificationModal() {
|
||||||
|
$('#notificationmodal').modal('hide');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{{end}}
|
@ -147,25 +147,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const channels = await getChannels();
|
const channels = await getChannels();
|
||||||
await fillChannels('edit-channel-container', channels);
|
fillChannels('edit-channel-container', channels);
|
||||||
await fillCategories('edit-category-container', channels);
|
fillCategories('edit-category-container', channels);
|
||||||
await fillMentions('edit-mentions');
|
await fillMentions('edit-mentions');
|
||||||
|
|
||||||
setActiveChannel(panel);
|
setActiveChannel('edit-channel-container', panel.channel_id);
|
||||||
setActiveCategory(panel);
|
setActiveCategory(panel);
|
||||||
setActiveMentions(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) {
|
function setActiveCategory(panel) {
|
||||||
const select = document.getElementById('edit-category-container');
|
const select = document.getElementById('edit-category-container');
|
||||||
for (let i = 0; i < select.children.length; i++) {
|
for (let i = 0; i < select.children.length; i++) {
|
||||||
|
@ -8,6 +8,10 @@
|
|||||||
<div class="main-panel" style="width: 100% !important;">
|
<div class="main-panel" style="width: 100% !important;">
|
||||||
{{template "navbar" .}}
|
{{template "navbar" .}}
|
||||||
{{template "loadingscreen" .}}
|
{{template "loadingscreen" .}}
|
||||||
|
|
||||||
|
<script src="/assets/js/modalbackdrop.js"></script>
|
||||||
|
{{template "notifymodal" .}}
|
||||||
|
|
||||||
{{template "content" .}}
|
{{template "content" .}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<script src="/assets/js/modalbackdrop.js"></script>
|
|
||||||
{{template "substitutions" .}}
|
{{template "substitutions" .}}
|
||||||
{{template "paneleditmodal" .}}
|
{{template "paneleditmodal" .}}
|
||||||
|
{{template "multipaneleditmodal" .}}
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-8">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h4 class="card-title">Reaction Panels</h4>
|
<h4 class="card-title">Reaction Panels</h4>
|
||||||
@ -31,6 +31,30 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h4 class="card-title">Multi-Panels</h4>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<table class="table table-hover table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Embed Title</th>
|
||||||
|
<th>Edit</th>
|
||||||
|
<th>Delete</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="multi-panel-container">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h4 class="card-title">Create A Panel</h4>
|
<h4 class="card-title">Create A Panel</h4>
|
||||||
@ -133,15 +157,83 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h4 class="card-title">Create A Multi-Panel</h4>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<form onsubmit="createMultiPanel(); return false;">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="black">Embed Title</label>
|
||||||
|
<input type="text" class="form-control" placeholder="React to open a ticket" id="multi-title">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="black">Embed Content</label>
|
||||||
|
<textarea type="text" class="form-control"
|
||||||
|
placeholder="Let users know which reaction corresponds to which panel. You are able to use emojis here." id="multi-content"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="black">Embed Colour</label>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input name="colour" type="color" class="form-control input-fill" value="#7289da" id="multi-colour">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div aria-live="polite" aria-atomic="true" style="position: relative; min-height: 200px;">
|
<div class="col-md-9">
|
||||||
|
<label class="black">Embed Channel</label>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<div class="input-group-text">#</div>
|
||||||
|
</div>
|
||||||
|
<select class="form-control" id="multi-channel-container">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<label class="black" for="mentions">Panels</label>
|
||||||
|
<select class="selectpicker form-control" id="multi-panels" multiple data-live-search="true" data-dropup-auto="false" data-size="5" data-display="static">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 pr-1 offset-md-3">
|
||||||
|
<div class="form-group text-center">
|
||||||
|
<button type="submit" class="btn btn-primary btn-fill" style="width: 100%;"><i class="fas fa-paper-plane"></i> Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div aria-live="polite" aria-atomic="true" style="position: relative">
|
||||||
<div style="position: absolute; right: 10px" id="toast-container">
|
<div style="position: absolute; right: 10px" id="toast-container">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
function getValue(elementId) {
|
||||||
|
return document.getElementById(elementId).value;
|
||||||
|
}
|
||||||
|
|
||||||
async function getChannels() {
|
async function getChannels() {
|
||||||
const res = await axios.get('/api/{{.guildId}}/channels');
|
const res = await axios.get('/api/{{.guildId}}/channels');
|
||||||
return res.data;
|
return res.data;
|
||||||
@ -169,6 +261,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function deleteMultiPanel(panelId) {
|
||||||
|
const res = await axios.delete('/api/{{.guildId}}/multipanels/' + panelId);
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data.success) {
|
||||||
|
notifySuccess('Multi-panel deleted successfully');
|
||||||
|
|
||||||
|
const el = document.getElementById(panelId);
|
||||||
|
el.parentNode.removeChild(el);
|
||||||
|
} else {
|
||||||
|
notifyError(res.data.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function createPanel() {
|
async function createPanel() {
|
||||||
const title = document.getElementById('title').value;
|
const title = document.getElementById('title').value;
|
||||||
const content = document.getElementById('content').value;
|
const content = document.getElementById('content').value;
|
||||||
@ -196,7 +301,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fillChannels(elementId, channels) {
|
async function createMultiPanel() {
|
||||||
|
const channelContainer = document.getElementById('multi-channel-container');
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
'title': getValue('multi-title'),
|
||||||
|
'content': getValue('multi-content'),
|
||||||
|
'colour': parseInt(`0x${getValue('multi-colour').slice(1)}`),
|
||||||
|
'channel_id': channelContainer.options[channelContainer.selectedIndex].value,
|
||||||
|
'panels': $('#multi-panels').val()
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await axios.post('/api/{{.guildId}}/multipanels', data);
|
||||||
|
if (res.status !== 200 || !res.data.success) {
|
||||||
|
notifyError(res.data.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
appendMultiPanel(res.data.data);
|
||||||
|
notify('Success', 'Multi-panel created successfully. Note: Don\'t delete the existing panels, or your they will disappear from your multi-panel.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillChannels(elementId, channels) {
|
||||||
const container = document.getElementById(elementId);
|
const container = document.getElementById(elementId);
|
||||||
|
|
||||||
channels.filter(ch => ch.type === 0).forEach(ch => {
|
channels.filter(ch => ch.type === 0).forEach(ch => {
|
||||||
@ -207,7 +333,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fillCategories(elementId, channels) {
|
function fillCategories(elementId, channels) {
|
||||||
const container = document.getElementById(elementId);
|
const container = document.getElementById(elementId);
|
||||||
|
|
||||||
channels.filter(ch => ch.type === 4).forEach(ch => {
|
channels.filter(ch => ch.type === 4).forEach(ch => {
|
||||||
@ -218,6 +344,16 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setActiveChannel(elementId, channelId) {
|
||||||
|
const select = document.getElementById(elementId);
|
||||||
|
for (let i = 0; i < select.children.length; i++) {
|
||||||
|
const child = select.children[i];
|
||||||
|
if (child.value === channelId) {
|
||||||
|
select.selectedIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Update on append / delete
|
// TODO: Update on append / delete
|
||||||
async function fillPanelQuota(panelCount) {
|
async function fillPanelQuota(panelCount) {
|
||||||
const res = await axios.get('/api/{{.guildId}}/premium');
|
const res = await axios.get('/api/{{.guildId}}/premium');
|
||||||
@ -267,6 +403,37 @@
|
|||||||
container.appendChild(tr);
|
container.appendChild(tr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function appendMultiPanel(panel) {
|
||||||
|
const container = document.getElementById('multi-panel-container');
|
||||||
|
|
||||||
|
const tr = document.createElement('tr');
|
||||||
|
tr.id = panel.id;
|
||||||
|
|
||||||
|
appendTd(tr, panel.title);
|
||||||
|
|
||||||
|
// build edit button
|
||||||
|
const editTd = document.createElement('td');
|
||||||
|
const editButton = document.createElement('button');
|
||||||
|
editButton.type = 'button';
|
||||||
|
editButton.classList.add('btn', 'btn-primary', 'btn-fill', 'mx-auto');
|
||||||
|
editButton.appendChild(document.createTextNode('Edit'));
|
||||||
|
editButton.onclick = () => { openMultiEditModal(panel.id) };
|
||||||
|
editTd.appendChild(editButton);
|
||||||
|
tr.appendChild(editTd);
|
||||||
|
|
||||||
|
// build remove button
|
||||||
|
const deleteTd = document.createElement('td');
|
||||||
|
const deleteButton = document.createElement('button');
|
||||||
|
deleteButton.type = 'submit';
|
||||||
|
deleteButton.classList.add('btn', 'btn-primary', 'btn-fill', 'mx-auto');
|
||||||
|
deleteButton.appendChild(document.createTextNode('Delete'));
|
||||||
|
deleteButton.onclick = () => {deleteMultiPanel(panel.id)};
|
||||||
|
deleteTd.appendChild(deleteButton);
|
||||||
|
tr.appendChild(deleteTd);
|
||||||
|
|
||||||
|
container.appendChild(tr);
|
||||||
|
}
|
||||||
|
|
||||||
async function fillPanels(channels) {
|
async function fillPanels(channels) {
|
||||||
const res = await axios.get('/api/{{.guildId}}/panels');
|
const res = await axios.get('/api/{{.guildId}}/panels');
|
||||||
if (res.status !== 200) {
|
if (res.status !== 200) {
|
||||||
@ -278,9 +445,37 @@
|
|||||||
appendPanel(panel, channels);
|
appendPanel(panel, channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appendPanelDropdownPanels('multi-panels', res.data);
|
||||||
|
appendPanelDropdownPanels('multi-edit-panels', res.data);
|
||||||
|
|
||||||
return res.data.length;
|
return res.data.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fillMultiPanels() {
|
||||||
|
const res = await axios.get('/api/{{.guildId}}/multipanels');
|
||||||
|
if (res.status !== 200) {
|
||||||
|
showToast("Error", res.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const multiPanel of res.data.data) {
|
||||||
|
appendMultiPanel(multiPanel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendPanelDropdownPanels(elementId, panels) {
|
||||||
|
const select = document.getElementById(elementId);
|
||||||
|
|
||||||
|
for (const panel of panels) {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = panel.message_id;
|
||||||
|
option.appendChild(document.createTextNode(panel.title));
|
||||||
|
select.appendChild(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(`#${elementId}`).selectpicker('refresh');
|
||||||
|
}
|
||||||
|
|
||||||
async function fillMentions(elementId) {
|
async function fillMentions(elementId) {
|
||||||
const select = document.getElementById(elementId);
|
const select = document.getElementById(elementId);
|
||||||
|
|
||||||
@ -313,9 +508,12 @@
|
|||||||
const channels = await getChannels();
|
const channels = await getChannels();
|
||||||
const panelCount = await fillPanels(channels);
|
const panelCount = await fillPanels(channels);
|
||||||
|
|
||||||
|
await fillMultiPanels();
|
||||||
|
|
||||||
await fillPanelQuota(panelCount);
|
await fillPanelQuota(panelCount);
|
||||||
await fillChannels('channel-container', channels);
|
fillChannels('channel-container', channels);
|
||||||
await fillCategories('category-container', channels);
|
fillChannels('multi-channel-container', channels);
|
||||||
|
fillCategories('category-container', channels);
|
||||||
await fillMentions('mentions');
|
await fillMentions('mentions');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@
|
|||||||
document.getElementById('id').value = '';
|
document.getElementById('id').value = '';
|
||||||
document.getElementById('content').value = '';
|
document.getElementById('content').value = '';
|
||||||
|
|
||||||
appendTag(data);
|
appendTag(data.id, data.content);
|
||||||
} else {
|
} else {
|
||||||
showToast('Error', res.data.error);
|
showToast('Error', res.data.error);
|
||||||
}
|
}
|
||||||
|
14
utils/requestutils.go
Normal file
14
utils/requestutils.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
func ErrorToResponse(err error) map[string]interface{} {
|
||||||
|
return gin.H {
|
||||||
|
"success": false,
|
||||||
|
"error": err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var SuccessResponse = gin.H{
|
||||||
|
"success": true,
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user