diff --git a/app/http/endpoints/api/blacklist.go b/app/http/endpoints/api/blacklist.go
new file mode 100644
index 0000000..00348aa
--- /dev/null
+++ b/app/http/endpoints/api/blacklist.go
@@ -0,0 +1,33 @@
+package api
+
+import (
+ "fmt"
+ "github.com/TicketsBot/GoPanel/database/table"
+ "github.com/TicketsBot/GoPanel/rpc/cache"
+ "github.com/gin-gonic/gin"
+ "strconv"
+)
+
+type userData struct {
+ Username string `json:"username"`
+ Discriminator string `json:"discriminator"`
+}
+
+func GetBlacklistHandler(ctx *gin.Context) {
+ guildId := ctx.Keys["guildid"].(uint64)
+
+ data := make(map[string]userData)
+
+ blacklistedUsers := table.GetBlacklistNodes(guildId)
+ for _, row := range blacklistedUsers {
+ formattedId := strconv.FormatUint(row.User, 10)
+ user, _ := cache.Instance.GetUser(row.User)
+
+ data[formattedId] = userData{
+ Username: user.Username,
+ Discriminator: fmt.Sprintf("%04d", user.Discriminator),
+ }
+ }
+
+ ctx.JSON(200, data)
+}
diff --git a/app/http/endpoints/api/blacklistadd.go b/app/http/endpoints/api/blacklistadd.go
new file mode 100644
index 0000000..852cdb5
--- /dev/null
+++ b/app/http/endpoints/api/blacklistadd.go
@@ -0,0 +1,60 @@
+package api
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "github.com/TicketsBot/GoPanel/database/table"
+ "github.com/TicketsBot/GoPanel/rpc/cache"
+ "github.com/gin-gonic/gin"
+ "github.com/jackc/pgx/v4"
+ "strconv"
+)
+
+func AddBlacklistHandler(ctx *gin.Context) {
+ guildId := ctx.Keys["guildid"].(uint64)
+
+ var data userData
+ if err := ctx.BindJSON(&data); err != nil {
+ ctx.AbortWithStatusJSON(400, gin.H{
+ "success": false,
+ "error": err.Error(),
+ })
+ return
+ }
+
+ parsedDiscrim, err := strconv.ParseInt(data.Discriminator, 10, 16)
+ if err != nil {
+ ctx.AbortWithStatusJSON(400, gin.H{
+ "success": false,
+ "error": err.Error(),
+ })
+ return
+ }
+
+ var targetId uint64
+ if err := cache.Instance.QueryRow(context.Background(), `select users.user_id from "users" where LOWER(users.data->>'Username')=LOWER($1) AND users.data->>'Discriminator'=$2;`, data.Username, strconv.FormatInt(parsedDiscrim, 10)).Scan(&targetId); err != nil {
+ if errors.Is(err, pgx.ErrNoRows) {
+ ctx.AbortWithStatusJSON(404, gin.H{
+ "success": false,
+ "error": "user not found",
+ })
+ } else {
+ fmt.Println(err.Error())
+ ctx.AbortWithStatusJSON(500, gin.H{
+ "success": false,
+ "error": err.Error(),
+ })
+ }
+ return
+ }
+
+ // TODO: Don't blacklist staff or guild owner
+
+ go table.AddBlacklist(guildId, targetId)
+
+ ctx.JSON(200, gin.H{
+ "success": true,
+ "user_id": strconv.FormatUint(targetId, 10),
+ })
+}
diff --git a/app/http/endpoints/api/blacklistremove.go b/app/http/endpoints/api/blacklistremove.go
new file mode 100644
index 0000000..c6b28ed
--- /dev/null
+++ b/app/http/endpoints/api/blacklistremove.go
@@ -0,0 +1,26 @@
+package api
+
+import (
+ "github.com/TicketsBot/GoPanel/database/table"
+ "github.com/gin-gonic/gin"
+ "strconv"
+)
+
+func RemoveBlacklistHandler(ctx *gin.Context) {
+ guildId := ctx.Keys["guildid"].(uint64)
+
+ userId, err := strconv.ParseUint(ctx.Param("user"), 10, 64)
+ if err != nil {
+ ctx.AbortWithStatusJSON(400, gin.H{
+ "success": false,
+ "error": err.Error(),
+ })
+ return
+ }
+
+ go table.RemoveBlacklist(guildId, userId)
+
+ ctx.JSON(200, gin.H{
+ "success": true,
+ })
+}
diff --git a/app/http/endpoints/api/channels.go b/app/http/endpoints/api/channels.go
new file mode 100644
index 0000000..934b796
--- /dev/null
+++ b/app/http/endpoints/api/channels.go
@@ -0,0 +1,23 @@
+package api
+
+import (
+ "encoding/json"
+ "github.com/TicketsBot/GoPanel/rpc/cache"
+ "github.com/gin-gonic/gin"
+)
+
+func ChannelsHandler(ctx *gin.Context) {
+ guildId := ctx.Keys["guildid"].(uint64)
+
+ channels := cache.Instance.GetGuildChannels(guildId)
+ encoded, err := json.Marshal(channels)
+ if err != nil {
+ ctx.JSON(500, gin.H{
+ "success": true,
+ "error": err.Error(),
+ })
+ return
+ }
+
+ ctx.Data(200, gin.MIMEJSON, encoded)
+}
diff --git a/app/http/endpoints/api/closeticket.go b/app/http/endpoints/api/closeticket.go
new file mode 100644
index 0000000..51e1ae3
--- /dev/null
+++ b/app/http/endpoints/api/closeticket.go
@@ -0,0 +1,53 @@
+package api
+
+import (
+ "github.com/TicketsBot/GoPanel/database/table"
+ "github.com/TicketsBot/GoPanel/messagequeue"
+ "github.com/gin-gonic/gin"
+)
+
+type closeBody struct {
+ Reason string `json:"reason"`
+}
+
+func CloseTicket(ctx *gin.Context) {
+ userId := ctx.Keys["userid"].(uint64)
+ guildId := ctx.Keys["guildid"].(uint64)
+ uuid := ctx.Param("uuid")
+
+ var data closeBody
+ if err := ctx.BindJSON(&data); err != nil {
+ ctx.AbortWithStatusJSON(400, gin.H{
+ "success": true,
+ "error": "Missing reason",
+ })
+ return
+ }
+
+ // Verify that the ticket exists
+ ticketChan := make(chan table.Ticket)
+ go table.GetTicket(uuid, ticketChan)
+ ticket := <-ticketChan
+
+ if ticket.Uuid == "" {
+ ctx.AbortWithStatusJSON(404, gin.H{
+ "success": true,
+ "error": "Ticket does not exist",
+ })
+ return
+ }
+
+ if ticket.Guild != guildId {
+ ctx.AbortWithStatusJSON(403, gin.H{
+ "success": true,
+ "error": "Guild ID does not matched",
+ })
+ return
+ }
+
+ go messagequeue.Client.PublishTicketClose(ticket.Uuid, userId, data.Reason)
+
+ ctx.JSON(200, gin.H{
+ "success": true,
+ })
+}
diff --git a/app/http/endpoints/api/getticket.go b/app/http/endpoints/api/getticket.go
new file mode 100644
index 0000000..9447df1
--- /dev/null
+++ b/app/http/endpoints/api/getticket.go
@@ -0,0 +1,76 @@
+package api
+
+import (
+ "fmt"
+ "github.com/TicketsBot/GoPanel/config"
+ "github.com/TicketsBot/GoPanel/database/table"
+ "github.com/TicketsBot/GoPanel/rpc/ratelimit"
+ "github.com/TicketsBot/GoPanel/utils"
+ "github.com/gin-gonic/gin"
+ "github.com/rxdn/gdl/rest"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+var MentionRegex, _ = regexp.Compile("<@(\\d+)>")
+
+func GetTicket(ctx *gin.Context) {
+ guildId := ctx.Keys["guildid"].(uint64)
+ uuid := ctx.Param("uuid")
+
+ ticketChan := make(chan table.Ticket)
+ go table.GetTicket(uuid, ticketChan)
+ ticket := <-ticketChan
+
+ if ticket.Guild != guildId {
+ ctx.AbortWithStatusJSON(403, gin.H{
+ "success": false,
+ "error": "Guild ID doesn't match",
+ })
+ return
+ }
+
+ if !ticket.IsOpen {
+ ctx.AbortWithStatusJSON(404, gin.H{
+ "success": false,
+ "error": "Ticket does not exist",
+ })
+ return
+ }
+
+ // Get messages
+ messages, _ := rest.GetChannelMessages(config.Conf.Bot.Token, ratelimit.Ratelimiter, ticket.Channel, rest.GetChannelMessagesData{Limit: 100})
+
+ // Format messages, exclude unneeded data
+ messagesFormatted := make([]map[string]interface{}, 0)
+ for _, message := range utils.Reverse(messages) {
+ content := message.Content
+
+ // Format mentions properly
+ match := MentionRegex.FindAllStringSubmatch(content, -1)
+ for _, mention := range match {
+ if len(mention) >= 2 {
+ mentionedId, err := strconv.ParseUint(mention[1], 10, 64)
+ if err != nil {
+ continue
+ }
+
+ ch := make(chan string)
+ go table.GetUsername(mentionedId, ch)
+ content = strings.ReplaceAll(content, fmt.Sprintf("<@%d>", mentionedId), fmt.Sprintf("@%s", <-ch))
+ }
+ }
+
+ messagesFormatted = append(messagesFormatted, map[string]interface{}{
+ "username": message.Author.Username,
+ "content": content,
+ })
+ }
+
+ ctx.JSON(200, gin.H{
+ "success": true,
+ "ticket": ticket,
+ "messages": messagesFormatted,
+ })
+}
diff --git a/app/http/endpoints/api/gettickets.go b/app/http/endpoints/api/gettickets.go
new file mode 100644
index 0000000..7329c7b
--- /dev/null
+++ b/app/http/endpoints/api/gettickets.go
@@ -0,0 +1,49 @@
+package api
+
+import (
+ "fmt"
+ "github.com/TicketsBot/GoPanel/database/table"
+ "github.com/TicketsBot/GoPanel/rpc/cache"
+ "github.com/gin-gonic/gin"
+ "strconv"
+ "strings"
+)
+
+func GetTickets(ctx *gin.Context) {
+ guildId := ctx.Keys["guildid"].(uint64)
+
+ tickets := table.GetOpenTickets(guildId)
+ ticketsFormatted := make([]map[string]interface{}, 0)
+
+ for _, ticket := range tickets {
+ membersFormatted := make([]map[string]interface{}, 0)
+ for index, memberIdStr := range strings.Split(ticket.Members, ",") {
+ if memberId, err := strconv.ParseUint(memberIdStr, 10, 64); err == nil {
+ if memberId != 0 {
+ var separator string
+ if index != len(strings.Split(ticket.Members, ","))-1 {
+ separator = ", "
+ }
+
+ member, _ := cache.Instance.GetUser(memberId)
+ membersFormatted = append(membersFormatted, map[string]interface{}{
+ "username": member.Username,
+ "discrim": fmt.Sprintf("%04d", member.Discriminator),
+ "sep": separator,
+ })
+ }
+ }
+ }
+
+ owner, _ := cache.Instance.GetUser(ticket.Owner)
+ ticketsFormatted = append(ticketsFormatted, map[string]interface{}{
+ "uuid": ticket.Uuid,
+ "ticketId": ticket.TicketId,
+ "username": owner.Username,
+ "discrim": fmt.Sprintf("%04d", owner.Discriminator),
+ "members": membersFormatted,
+ })
+ }
+
+ ctx.JSON(200, ticketsFormatted)
+}
diff --git a/app/http/endpoints/api/guilds.go b/app/http/endpoints/api/guilds.go
new file mode 100644
index 0000000..f7d71a0
--- /dev/null
+++ b/app/http/endpoints/api/guilds.go
@@ -0,0 +1,30 @@
+package api
+
+import (
+ "github.com/TicketsBot/GoPanel/database/table"
+ "github.com/TicketsBot/GoPanel/utils"
+ "github.com/gin-gonic/gin"
+ "github.com/rxdn/gdl/objects/guild"
+)
+
+func GetGuilds(ctx *gin.Context) {
+ userId := ctx.Keys["userid"].(uint64)
+
+ userGuilds := table.GetGuilds(userId)
+ adminGuilds := make([]guild.Guild, 0)
+ for _, g := range userGuilds {
+ fakeGuild := guild.Guild{
+ Id: g.Id,
+ OwnerId: g.OwnerId,
+ Permissions: g.Permissions,
+ }
+
+ isAdmin := make(chan bool)
+ go utils.IsAdmin(fakeGuild, userId, isAdmin)
+ if <-isAdmin {
+ adminGuilds = append(adminGuilds, g)
+ }
+ }
+
+ ctx.JSON(200, adminGuilds)
+}
diff --git a/app/http/endpoints/api/logslist.go b/app/http/endpoints/api/logslist.go
new file mode 100644
index 0000000..9579096
--- /dev/null
+++ b/app/http/endpoints/api/logslist.go
@@ -0,0 +1,112 @@
+package api
+
+import (
+ "context"
+ "fmt"
+ "github.com/TicketsBot/GoPanel/config"
+ "github.com/TicketsBot/GoPanel/database/table"
+ "github.com/TicketsBot/GoPanel/rpc/cache"
+ "github.com/TicketsBot/GoPanel/rpc/ratelimit"
+ "github.com/TicketsBot/GoPanel/utils"
+ "github.com/apex/log"
+ "github.com/gin-gonic/gin"
+ "github.com/rxdn/gdl/rest"
+ "strconv"
+)
+
+const (
+ pageLimit = 30
+)
+
+func GetLogs(ctx *gin.Context) {
+ guildId := ctx.Keys["guildid"].(uint64)
+
+ page, err := strconv.Atoi(ctx.Param("page"))
+ if page < 1 {
+ page = 1
+ }
+
+ // Get ticket ID from URL
+ var ticketId int
+ if utils.IsInt(ctx.Query("ticketid")) {
+ ticketId, _ = strconv.Atoi(ctx.Query("ticketid"))
+ }
+
+ var tickets []table.Ticket
+
+ // Get tickets from DB
+ if ticketId > 0 {
+ ticketChan := make(chan table.Ticket)
+ go table.GetTicketById(guildId, ticketId, ticketChan)
+ ticket := <-ticketChan
+
+ if ticket.Uuid != "" && !ticket.IsOpen {
+ tickets = append(tickets, ticket)
+ }
+ } else {
+ // make slice of user IDs to filter by
+ filteredIds := make([]uint64, 0)
+
+ // Add userid param to slice
+ filteredUserId, _ := strconv.ParseUint(ctx.Query("userid"), 10, 64)
+ if filteredUserId != 0 {
+ filteredIds = append(filteredIds, filteredUserId)
+ }
+
+ // Get username from URL
+ if username := ctx.Query("username"); username != "" {
+ // username -> user id
+ rows, err := cache.Instance.PgCache.Query(context.Background(), `select users.user_id from users where LOWER("data"->>'Username') LIKE LOWER($1) and exists(SELECT FROM members where members.guild_id=$2);`, fmt.Sprintf("%%%s%%", username), guildId)
+ defer rows.Close()
+ if err != nil {
+ log.Error(err.Error())
+ return
+ }
+
+ for rows.Next() {
+ var filteredId uint64
+ if err := rows.Scan(&filteredId); err != nil {
+ continue
+ }
+
+ if filteredId != 0 {
+ filteredIds = append(filteredIds, filteredId)
+ }
+ }
+ }
+
+ if ctx.Query("userid") != "" || ctx.Query("username") != "" {
+ tickets = table.GetClosedTicketsByUserId(guildId, filteredIds)
+ } else {
+ tickets = table.GetClosedTickets(guildId)
+ }
+ }
+
+ // Select 30 logs + format them
+ formattedLogs := make([]map[string]interface{}, 0)
+ for i := (page - 1) * pageLimit; i < (page-1)*pageLimit+pageLimit; i++ {
+ if i >= len(tickets) {
+ break
+ }
+
+ ticket := tickets[i]
+
+ // get username
+ user, found := cache.Instance.GetUser(ticket.Owner)
+ if !found {
+ user, err = rest.GetUser(config.Conf.Bot.Token, ratelimit.Ratelimiter, ticket.Owner)
+ if err != nil {
+ log.Error(err.Error())
+ }
+ go cache.Instance.StoreUser(user)
+ }
+
+ formattedLogs = append(formattedLogs, map[string]interface{}{
+ "ticketid": ticket.TicketId,
+ "userid": strconv.FormatUint(ticket.Owner, 10),
+ "username": user.Username,
+ })
+ }
+
+ ctx.JSON(200, formattedLogs)
+}
diff --git a/app/http/endpoints/api/panelcreate.go b/app/http/endpoints/api/panelcreate.go
new file mode 100644
index 0000000..fa60686
--- /dev/null
+++ b/app/http/endpoints/api/panelcreate.go
@@ -0,0 +1,135 @@
+package api
+
+import (
+ "github.com/TicketsBot/GoPanel/database/table"
+ "github.com/TicketsBot/GoPanel/messagequeue"
+ "github.com/TicketsBot/GoPanel/rpc/cache"
+ "github.com/TicketsBot/GoPanel/utils"
+ "github.com/gin-gonic/gin"
+ "github.com/rxdn/gdl/objects/channel"
+)
+
+func CreatePanel(ctx *gin.Context) {
+ guildId := ctx.Keys["guildid"].(uint64)
+ var data panel
+
+ if err := ctx.BindJSON(&data); err != nil {
+ ctx.AbortWithStatusJSON(400, gin.H{
+ "success": false,
+ "error": err.Error(),
+ })
+ return
+ }
+
+ data.MessageId = 0
+
+ // Check panel quota
+ premium := make(chan bool)
+ go utils.IsPremiumGuild(guildId, premium)
+ if !<-premium {
+ panels := make(chan []table.Panel)
+ go table.GetPanelsByGuild(guildId, panels)
+ if len(<-panels) > 0 {
+ ctx.AbortWithStatusJSON(402, gin.H{
+ "success": false,
+ "error": "You have exceeded your panel quota. Purchase premium to unlock more panels.",
+ })
+ return
+ }
+ }
+
+ if !data.verifyTitle() {
+ ctx.AbortWithStatusJSON(400, gin.H{
+ "success": false,
+ "error": "Panel titles must be between 1 - 255 characters in length",
+ })
+ return
+ }
+
+ if !data.verifyContent() {
+ ctx.AbortWithStatusJSON(400, gin.H{
+ "success": false,
+ "error": "Panel content must be between 1 - 1024 characters in length",
+ })
+ return
+ }
+
+ channels := cache.Instance.GetGuildChannels(guildId)
+
+ if !data.verifyChannel(channels) {
+ ctx.AbortWithStatusJSON(400, gin.H{
+ "success": false,
+ "error": "Invalid channel",
+ })
+ return
+ }
+
+ if !data.verifyCategory(channels) {
+ ctx.AbortWithStatusJSON(400, gin.H{
+ "success": false,
+ "error": "Invalid channel category",
+ })
+ return
+ }
+
+ emoji, validEmoji := data.getEmoji()
+ if !validEmoji {
+ ctx.AbortWithStatusJSON(400, gin.H{
+ "success": false,
+ "error": "Invalid emoji. Simply use the emoji's name from Discord.",
+ })
+ return
+ }
+
+ // TODO: Move panel create logic here
+ go messagequeue.Client.PublishPanelCreate(table.Panel{
+ ChannelId: data.ChannelId,
+ GuildId: guildId,
+ Title: data.Title,
+ Content: data.Content,
+ Colour: data.Colour,
+ TargetCategory: data.CategoryId,
+ ReactionEmote: emoji,
+ })
+
+ ctx.JSON(200, gin.H{
+ "success": true,
+ })
+}
+
+func (p *panel) verifyTitle() bool {
+ return len(p.Title) > 0 && len(p.Title) < 256
+}
+
+func (p *panel) verifyContent() bool {
+ return len(p.Content) > 0 && len(p.Content) < 1025
+}
+
+func (p *panel) getEmoji() (string, bool) {
+ emoji := utils.GetEmojiByName(p.Emote)
+ return emoji, emoji != ""
+}
+
+func (p *panel) verifyChannel(channels []channel.Channel) bool {
+ var valid bool
+ for _, ch := range channels {
+ if ch.Id == p.ChannelId && ch.Type == channel.ChannelTypeGuildText {
+ valid = true
+ break
+ }
+ }
+
+ return valid
+}
+
+func (p *panel) verifyCategory(channels []channel.Channel) bool {
+ var valid bool
+ for _, ch := range channels {
+ if ch.Id == p.CategoryId && ch.Type == channel.ChannelTypeGuildCategory {
+ valid = true
+ break
+ }
+ }
+
+ return valid
+}
diff --git a/app/http/endpoints/api/paneldelete.go b/app/http/endpoints/api/paneldelete.go
new file mode 100644
index 0000000..1276d3e
--- /dev/null
+++ b/app/http/endpoints/api/paneldelete.go
@@ -0,0 +1,43 @@
+package api
+
+import (
+ "github.com/TicketsBot/GoPanel/config"
+ "github.com/TicketsBot/GoPanel/database/table"
+ "github.com/TicketsBot/GoPanel/rpc/ratelimit"
+ "github.com/gin-gonic/gin"
+ "github.com/rxdn/gdl/rest"
+ "strconv"
+)
+
+func DeletePanel(ctx *gin.Context) {
+ guildId := ctx.Keys["guildid"].(uint64)
+
+ messageId, err := strconv.ParseUint(ctx.Param("message"), 10, 64)
+ if err != nil {
+ ctx.AbortWithStatusJSON(400, gin.H{
+ "success": false,
+ "error": err.Error(),
+ })
+ return
+ }
+
+ // verify panel belongs to guild
+ panelChan := make(chan table.Panel)
+ go table.GetPanel(messageId, panelChan)
+ panel := <-panelChan
+
+ if panel.GuildId != guildId {
+ ctx.AbortWithStatusJSON(403, gin.H{
+ "success": false,
+ "error": "Guild ID doesn't match",
+ })
+ return
+ }
+
+ go table.DeletePanel(messageId)
+ go rest.DeleteMessage(config.Conf.Bot.Token, ratelimit.Ratelimiter, panel.ChannelId, panel.MessageId)
+
+ ctx.JSON(200, gin.H{
+ "success": true,
+ })
+}
diff --git a/app/http/endpoints/api/panellist.go b/app/http/endpoints/api/panellist.go
new file mode 100644
index 0000000..2b30f76
--- /dev/null
+++ b/app/http/endpoints/api/panellist.go
@@ -0,0 +1,40 @@
+package api
+
+import (
+ "github.com/TicketsBot/GoPanel/database/table"
+ "github.com/gin-gonic/gin"
+)
+
+type panel struct {
+ ChannelId uint64 `json:"channel_id,string"`
+ MessageId uint64 `json:"message_id,string"`
+ Title string `json:"title"`
+ Content string `json:"content"`
+ Colour uint32 `json:"colour"`
+ CategoryId uint64 `json:"category_id,string"`
+ Emote string `json:"emote"`
+}
+
+func ListPanels(ctx *gin.Context) {
+ guildId := ctx.Keys["guildid"].(uint64)
+
+ panelsChan := make(chan []table.Panel)
+ go table.GetPanelsByGuild(guildId, panelsChan)
+ panels := <-panelsChan
+
+ wrapped := make([]panel, len(panels))
+
+ for i, p := range panels {
+ wrapped[i] = panel{
+ ChannelId: p.ChannelId,
+ MessageId: p.MessageId,
+ Title: p.Title,
+ Content: p.Content,
+ Colour: p.Colour,
+ CategoryId: p.TargetCategory,
+ Emote: p.ReactionEmote,
+ }
+ }
+
+ ctx.JSON(200, wrapped)
+}
diff --git a/app/http/endpoints/api/premium.go b/app/http/endpoints/api/premium.go
new file mode 100644
index 0000000..ff5fe41
--- /dev/null
+++ b/app/http/endpoints/api/premium.go
@@ -0,0 +1,17 @@
+package api
+
+import (
+ "github.com/TicketsBot/GoPanel/utils"
+ "github.com/gin-gonic/gin"
+)
+
+func PremiumHandler(ctx *gin.Context) {
+ guildId := ctx.Keys["guildid"].(uint64)
+
+ isPremium := make(chan bool)
+ go utils.IsPremiumGuild(guildId, isPremium)
+
+ ctx.JSON(200, gin.H{
+ "premium": <-isPremium,
+ })
+}
diff --git a/app/http/endpoints/api/sendmessage.go b/app/http/endpoints/api/sendmessage.go
new file mode 100644
index 0000000..64ecdaa
--- /dev/null
+++ b/app/http/endpoints/api/sendmessage.go
@@ -0,0 +1,132 @@
+package api
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "github.com/TicketsBot/GoPanel/config"
+ "github.com/TicketsBot/GoPanel/database/table"
+ "github.com/TicketsBot/GoPanel/rpc/cache"
+ "github.com/TicketsBot/GoPanel/rpc/ratelimit"
+ "github.com/TicketsBot/GoPanel/utils"
+ "github.com/gin-gonic/gin"
+ "github.com/rxdn/gdl/rest"
+ "net/http"
+ "time"
+)
+
+type sendMessageBody struct {
+ Message string `json:"message"`
+}
+
+func SendMessage(ctx *gin.Context) {
+ guildId := ctx.Keys["guildid"].(uint64)
+ userId := ctx.Keys["userid"].(uint64)
+
+ var body sendMessageBody
+ if err := ctx.BindJSON(&body); err != nil {
+ ctx.AbortWithStatusJSON(400, gin.H{
+ "success": false,
+ "error": "Message is missing",
+ })
+ return
+ }
+
+ // Verify guild is premium
+ isPremium := make(chan bool)
+ go utils.IsPremiumGuild(guildId, isPremium)
+ if !<-isPremium {
+ ctx.AbortWithStatusJSON(402, gin.H{
+ "success": false,
+ "error": "Guild is not premium",
+ })
+ return
+ }
+
+ // Get ticket
+ ticketChan := make(chan table.Ticket)
+ go table.GetTicket(ctx.Param("uuid"), ticketChan)
+ ticket := <-ticketChan
+
+ // Verify the ticket exists
+ if ticket.TicketId == 0 {
+ ctx.AbortWithStatusJSON(404, gin.H{
+ "success": false,
+ "error": "Ticket not found",
+ })
+ return
+ }
+
+ // Verify the user has permission to send to this guild
+ if ticket.Guild != guildId {
+ ctx.AbortWithStatusJSON(403, gin.H{
+ "success": false,
+ "error": "Guild ID doesn't match",
+ })
+ return
+ }
+
+ user, _ := cache.Instance.GetUser(userId)
+
+ if len(body.Message) > 2000 {
+ body.Message = body.Message[0:1999]
+ }
+
+ // Preferably send via a webhook
+ webhookChan := make(chan *string)
+ go table.GetWebhookByUuid(ticket.Uuid, webhookChan)
+ webhook := <-webhookChan
+
+ success := false
+ if webhook != nil {
+ // TODO: Use gdl execute webhook wrapper
+ success = executeWebhook(ticket.Uuid, *webhook, body.Message, user.Username, user.AvatarUrl(256))
+ }
+
+ if !success {
+ body.Message = fmt.Sprintf("**%s**: %s", user.Username, body.Message)
+ if len(body.Message) > 2000 {
+ body.Message = body.Message[0:1999]
+ }
+
+ _, _ = rest.CreateMessage(config.Conf.Bot.Token, ratelimit.Ratelimiter, ticket.Channel, rest.CreateMessageData{Content: body.Message})
+ }
+
+ ctx.JSON(200, gin.H{
+ "success": true,
+ })
+}
+
+func executeWebhook(uuid, webhook, content, username, avatar string) bool {
+ body := map[string]interface{}{
+ "content": content,
+ "username": username,
+ "avatar_url": avatar,
+ }
+ encoded, err := json.Marshal(&body)
+ if err != nil {
+ return false
+ }
+ req, err := http.NewRequest("POST", fmt.Sprintf("https://canary.discordapp.com/api/webhooks/%s", webhook), bytes.NewBuffer(encoded))
+ if err != nil {
+ return false
+ }
+
+ req.Header.Set("Content-Type", "application/json")
+
+ client := &http.Client{}
+ client.Timeout = 3 * time.Second
+
+ res, err := client.Do(req)
+ if err != nil {
+ return false
+ }
+
+ if res.StatusCode == 404 || res.StatusCode == 403 {
+ go table.DeleteWebhookByUuid(uuid)
+ } else {
+ return true
+ }
+
+ return false
+}
diff --git a/app/http/endpoints/api/settings.go b/app/http/endpoints/api/settings.go
new file mode 100644
index 0000000..5a0a4e3
--- /dev/null
+++ b/app/http/endpoints/api/settings.go
@@ -0,0 +1,56 @@
+package api
+
+import (
+ "github.com/TicketsBot/GoPanel/database/table"
+ "github.com/gin-gonic/gin"
+)
+
+type Settings struct {
+ Prefix string `json:"prefix"`
+ WelcomeMessaage string `json:"welcome_message"`
+ TicketLimit int `json:"ticket_limit"`
+ Category uint64 `json:"category,string"`
+ ArchiveChannel uint64 `json:"archive_channel,string"`
+ NamingScheme table.NamingScheme `json:"naming_scheme"`
+ PingEveryone bool `json:"ping_everyone"`
+ UsersCanClose bool `json:"users_can_close"`
+}
+
+func GetSettingsHandler(ctx *gin.Context) {
+ guildId := ctx.Keys["guildid"].(uint64)
+
+ prefix := make(chan string)
+ go table.GetPrefix(guildId, prefix)
+
+ welcomeMessage := make(chan string)
+ go table.GetWelcomeMessage(guildId, welcomeMessage)
+
+ ticketLimit := make(chan int)
+ go table.GetTicketLimit(guildId, ticketLimit)
+
+ category := make(chan uint64)
+ go table.GetChannelCategory(guildId, category)
+
+ archiveChannel := make(chan uint64)
+ go table.GetArchiveChannel(guildId, archiveChannel)
+
+ allowUsersToClose := make(chan bool)
+ go table.IsUserCanClose(guildId, allowUsersToClose)
+
+ namingScheme := make(chan table.NamingScheme)
+ go table.GetTicketNamingScheme(guildId, namingScheme)
+
+ pingEveryone := make(chan bool)
+ go table.GetPingEveryone(guildId, pingEveryone)
+
+ ctx.JSON(200, Settings{
+ Prefix: <-prefix,
+ WelcomeMessaage: <-welcomeMessage,
+ TicketLimit: <-ticketLimit,
+ Category: <-category,
+ ArchiveChannel: <-archiveChannel,
+ NamingScheme: <-namingScheme,
+ PingEveryone: <-pingEveryone,
+ UsersCanClose: <-allowUsersToClose,
+ })
+}
diff --git a/app/http/endpoints/api/token.go b/app/http/endpoints/api/token.go
new file mode 100644
index 0000000..8c74e7c
--- /dev/null
+++ b/app/http/endpoints/api/token.go
@@ -0,0 +1,34 @@
+package api
+
+import (
+ "fmt"
+ "github.com/TicketsBot/GoPanel/config"
+ "github.com/TicketsBot/GoPanel/utils"
+ "github.com/dgrijalva/jwt-go"
+ "github.com/gin-gonic/contrib/sessions"
+ "github.com/gin-gonic/gin"
+ "strconv"
+)
+
+func TokenHandler(ctx *gin.Context) {
+ session := sessions.Default(ctx)
+ userId := utils.GetUserId(session)
+ //TODO : CSRF
+ token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
+ "userid": strconv.FormatUint(userId, 10),
+ })
+
+ str, err := token.SignedString([]byte(config.Conf.Server.Secret))
+ if err != nil {
+ fmt.Println(err.Error())
+ ctx.JSON(500, gin.H{
+ "success": false,
+ "error": err.Error(),
+ })
+ } else {
+ ctx.JSON(200, gin.H{
+ "success": true,
+ "token": str,
+ })
+ }
+}
diff --git a/app/http/endpoints/api/updatesettings.go b/app/http/endpoints/api/updatesettings.go
new file mode 100644
index 0000000..34089a6
--- /dev/null
+++ b/app/http/endpoints/api/updatesettings.go
@@ -0,0 +1,130 @@
+package api
+
+import (
+ "github.com/TicketsBot/GoPanel/database/table"
+ "github.com/TicketsBot/GoPanel/rpc/cache"
+ "github.com/gin-gonic/gin"
+ "github.com/rxdn/gdl/objects/channel"
+)
+
+func UpdateSettingsHandler(ctx *gin.Context) {
+ guildId := ctx.Keys["guildid"].(uint64)
+
+ var settings Settings
+ if err := ctx.BindJSON(&settings); err != nil {
+ ctx.AbortWithStatusJSON(400, gin.H{
+ "success": false,
+ "error": err.Error(),
+ })
+ return
+ }
+
+ // Get a list of all channel IDs
+ channels := cache.Instance.GetGuildChannels(guildId)
+
+ // Get prefix
+ validPrefix := settings.updatePrefix(guildId)
+ validWelcomeMessage := settings.updateWelcomeMessage(guildId)
+ validTicketLimit := settings.updateTicketLimit(guildId)
+ validArchiveChannel := settings.updateArchiveChannel(channels, guildId)
+ validCategory := settings.updateCategory(channels, guildId)
+ validNamingScheme := settings.updateNamingScheme(guildId)
+ settings.updatePingEveryone(guildId)
+ settings.updateUsersCanClose(guildId)
+
+ ctx.JSON(200, gin.H{
+ "prefix": validPrefix,
+ "welcome_message": validWelcomeMessage,
+ "ticket_limit": validTicketLimit,
+ "archive_channel": validArchiveChannel,
+ "category": validCategory,
+ "naming_scheme": validNamingScheme,
+ })
+}
+
+func (s *Settings) updatePrefix(guildId uint64) bool {
+ if s.Prefix == "" || len(s.Prefix) > 8 {
+ return false
+ }
+
+ go table.UpdatePrefix(guildId, s.Prefix)
+ return true
+}
+
+func (s *Settings) updateWelcomeMessage(guildId uint64) bool {
+ if s.WelcomeMessaage == "" || len(s.WelcomeMessaage) > 1000 {
+ return false
+ }
+
+ go table.UpdateWelcomeMessage(guildId, s.WelcomeMessaage)
+ return true
+}
+
+func (s *Settings) updateTicketLimit(guildId uint64) bool {
+ if s.TicketLimit > 10 || s.TicketLimit < 1 {
+ return false
+ }
+
+ go table.UpdateTicketLimit(guildId, s.TicketLimit)
+ return true
+}
+
+func (s *Settings) updateCategory(channels []channel.Channel, guildId uint64) bool {
+ var valid bool
+ for _, ch := range channels {
+ if ch.Id == s.Category && ch.Type == channel.ChannelTypeGuildCategory {
+ valid = true
+ break
+ }
+ }
+
+ if !valid {
+ return false
+ }
+
+ go table.UpdateChannelCategory(guildId, s.Category)
+ return true
+}
+
+func (s *Settings) updateArchiveChannel(channels []channel.Channel, guildId uint64) bool {
+ var valid bool
+ for _, ch := range channels {
+ if ch.Id == s.ArchiveChannel && ch.Type == channel.ChannelTypeGuildText {
+ valid = true
+ break
+ }
+ }
+
+ if !valid {
+ return false
+ }
+
+ go table.UpdateArchiveChannel(guildId, s.ArchiveChannel)
+ return true
+}
+
+var validScheme = []table.NamingScheme{table.Id, table.Username}
+func (s *Settings) updateNamingScheme(guildId uint64) bool {
+ var valid bool
+ for _, scheme := range validScheme {
+ if scheme == s.NamingScheme {
+ valid = true
+ break
+ }
+ }
+
+ if !valid {
+ return false
+ }
+
+ go table.SetTicketNamingScheme(guildId, s.NamingScheme)
+ return true
+}
+
+func (s *Settings) updatePingEveryone(guildId uint64) {
+ go table.UpdatePingEveryone(guildId, s.PingEveryone)
+}
+
+func (s *Settings) updateUsersCanClose(guildId uint64) {
+ go table.SetUserCanClose(guildId, s.UsersCanClose)
+}
diff --git a/app/http/endpoints/manage/blacklist.go b/app/http/endpoints/manage/blacklist.go
index ff09af9..226349d 100644
--- a/app/http/endpoints/manage/blacklist.go
+++ b/app/http/endpoints/manage/blacklist.go
@@ -2,100 +2,18 @@ package manage
import (
"github.com/TicketsBot/GoPanel/config"
- "github.com/TicketsBot/GoPanel/database/table"
- "github.com/TicketsBot/GoPanel/rpc/cache"
- "github.com/TicketsBot/GoPanel/utils"
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
- "strconv"
)
func BlacklistHandler(ctx *gin.Context) {
store := sessions.Default(ctx)
- if store == nil {
- return
- }
- defer store.Save()
+ guildId := ctx.Keys["guildid"].(uint64)
- if utils.IsLoggedIn(store) {
- userId := utils.GetUserId(store)
-
- // Verify the guild exists
- guildIdStr := ctx.Param("id")
- guildId, err := strconv.ParseUint(guildIdStr, 10, 64)
- if err != nil {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
- return
- }
-
- // Get object for selected guild
- guild, _ := cache.Instance.GetGuild(guildId, false)
-
- // Verify the user has permissions to be here
- isAdmin := make(chan bool)
- go utils.IsAdmin(guild, userId, isAdmin)
- if !<-isAdmin {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
- return
- }
-
- blacklistedUsers := table.GetBlacklistNodes(guildId)
-
- var blacklistedIds []uint64
- for _, user := range blacklistedUsers {
- blacklistedIds = append(blacklistedIds, user.User)
- }
-
- nodes := table.GetUserNodes(blacklistedIds)
-
- var blacklisted []map[string]interface{}
- for _, node := range nodes {
- blacklisted = append(blacklisted, map[string]interface{}{
- "userId": node.Id,
- "username": utils.Base64Decode(node.Name),
- "discrim": node.Discriminator,
- })
- }
-
- userNotFound := false
- isStaff := false
- if store.Get("csrf").(string) == ctx.Query("csrf") { // CSRF is correct *and* set
- username := ctx.Query("username")
- discrim := ctx.Query("discrim")
-
- // Verify that the user ID is real and in a shared guild
- targetId := table.GetUserId(username, discrim)
- exists := targetId != 0
-
- if exists {
- if guild.OwnerId == targetId || table.IsStaff(guildId, targetId) { // Prevent users from blacklisting staff
- isStaff = true
- } else {
- if !utils.Contains(blacklistedIds, targetId) { // Prevent duplicates
- table.AddBlacklist(guildId, targetId)
- blacklisted = append(blacklisted, map[string]interface{}{
- "userId": targetId,
- "username": username,
- "discrim": discrim,
- })
- }
- }
- } else {
- userNotFound = true
- }
- }
-
- ctx.HTML(200, "manage/blacklist", gin.H{
- "name": store.Get("name").(string),
- "guildId": guildIdStr,
- "csrf": store.Get("csrf").(string),
- "avatar": store.Get("avatar").(string),
- "baseUrl": config.Conf.Server.BaseUrl,
- "blacklisted": blacklisted,
- "userNotFound": userNotFound,
- "isStaff": isStaff,
- })
- } else {
- ctx.Redirect(302, "/login")
- }
+ ctx.HTML(200, "manage/blacklist", gin.H{
+ "name": store.Get("name").(string),
+ "guildId": guildId,
+ "avatar": store.Get("avatar").(string),
+ "baseUrl": config.Conf.Server.BaseUrl,
+ })
}
diff --git a/app/http/endpoints/manage/blacklistremove.go b/app/http/endpoints/manage/blacklistremove.go
deleted file mode 100644
index 81fafc0..0000000
--- a/app/http/endpoints/manage/blacklistremove.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package manage
-
-import (
- "fmt"
- "github.com/TicketsBot/GoPanel/config"
- "github.com/TicketsBot/GoPanel/database/table"
- "github.com/TicketsBot/GoPanel/rpc/cache"
- "github.com/TicketsBot/GoPanel/utils"
- "github.com/gin-gonic/contrib/sessions"
- "github.com/gin-gonic/gin"
- "strconv"
-)
-
-func BlacklistRemoveHandler(ctx *gin.Context) {
- store := sessions.Default(ctx)
- if store == nil {
- return
- }
- defer store.Save()
-
- if utils.IsLoggedIn(store) {
- userId := utils.GetUserId(store)
-
- // Verify the guild exists
- guildIdStr := ctx.Param("id")
- guildId, err := strconv.ParseUint(guildIdStr, 10, 64)
- if err != nil {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
- return
- }
-
- // Get object for selected guild
- guild, _ := cache.Instance.GetGuild(guildId, false)
-
- // Verify the user has permissions to be here
- isAdmin := make(chan bool)
- go utils.IsAdmin(guild, userId, isAdmin)
- if !<-isAdmin {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
- return
- }
-
- if ctx.Query("c") == store.Get("csrf").(string) {
- targetIdStr := ctx.Param("user")
- targetId, err := strconv.ParseUint(targetIdStr, 10, 64)
-
- if err == nil { // If it's a real ID
- table.RemoveBlacklist(guildId, targetId)
- }
- }
-
- ctx.Redirect(302, fmt.Sprintf("/manage/%s/blacklist", guildIdStr))
- } else {
- ctx.Redirect(302, "/login")
- }
-}
diff --git a/app/http/endpoints/manage/logslist.go b/app/http/endpoints/manage/logslist.go
index 9c12c98..ab1d0b6 100644
--- a/app/http/endpoints/manage/logslist.go
+++ b/app/http/endpoints/manage/logslist.go
@@ -1,154 +1,19 @@
package manage
import (
- "context"
- "fmt"
"github.com/TicketsBot/GoPanel/config"
- "github.com/TicketsBot/GoPanel/database/table"
- "github.com/TicketsBot/GoPanel/rpc/cache"
- "github.com/TicketsBot/GoPanel/rpc/ratelimit"
- "github.com/TicketsBot/GoPanel/utils"
- "github.com/apex/log"
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
- "github.com/rxdn/gdl/rest"
- "strconv"
)
func LogsHandler(ctx *gin.Context) {
store := sessions.Default(ctx)
- if store == nil {
- return
- }
- defer store.Save()
+ guildId := ctx.Keys["guildid"].(uint64)
- if utils.IsLoggedIn(store) {
- userId := utils.GetUserId(store)
-
- // Verify the guild exists
- guildIdStr := ctx.Param("id")
- guildId, err := strconv.ParseUint(guildIdStr, 10, 64)
- if err != nil {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
- return
- }
-
- pageStr := ctx.Param("page")
- page := 1
- i, err := strconv.Atoi(pageStr)
- if err == nil {
- if i > 0 {
- page = i
- }
- }
-
- // Get object for selected guild
- guild, _ := cache.Instance.GetGuild(guildId, false)
-
- // Verify the user has permissions to be here
- isAdmin := make(chan bool)
- go utils.IsAdmin(guild, userId, isAdmin)
- if !<-isAdmin {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
- return
- }
-
- pageLimit := 30
-
- // Get ticket ID from URL
- var ticketId int
- if utils.IsInt(ctx.Query("ticketid")) {
- ticketId, _ = strconv.Atoi(ctx.Query("ticketid"))
- }
-
- var tickets []table.Ticket
-
- // Get tickets from DB
- if ticketId > 0 {
- ticketChan := make(chan table.Ticket)
- go table.GetTicketById(guildId, ticketId, ticketChan)
- ticket := <-ticketChan
-
- if ticket.Uuid != "" && !ticket.IsOpen {
- tickets = append(tickets, ticket)
- }
- } else {
- // make slice of user IDs to filter by
- filteredIds := make([]uint64, 0)
-
- // Add userid param to slice
- filteredUserId, _ := strconv.ParseUint(ctx.Query("userid"), 10, 64)
- if filteredUserId != 0 {
- filteredIds = append(filteredIds, filteredUserId)
- }
-
- // Get username from URL
- if username := ctx.Query("username"); username != "" {
- // username -> user id
- rows, err := cache.Instance.PgCache.Query(context.Background(), `select users.user_id from users where LOWER("data"->>'Username') LIKE LOWER($1) and exists(SELECT FROM members where members.guild_id=$2);`, fmt.Sprintf("%%%s%%", username), guildId)
- defer rows.Close()
- if err != nil {
- log.Error(err.Error())
- return
- }
-
- for rows.Next() {
- var filteredId uint64
- if err := rows.Scan(&filteredId); err != nil {
- continue
- }
-
- if filteredId != 0 {
- filteredIds = append(filteredIds, filteredId)
- }
- }
- }
-
- if ctx.Query("userid") != "" || ctx.Query("username") != "" {
- tickets = table.GetClosedTicketsByUserId(guildId, filteredIds)
- } else {
- tickets = table.GetClosedTickets(guildId)
- }
- }
-
- // Select 30 logs + format them
- var formattedLogs []map[string]interface{}
- for i := (page - 1) * pageLimit; i < (page - 1) * pageLimit + pageLimit; i++ {
- if i >= len(tickets) {
- break
- }
-
- ticket := tickets[i]
-
- // get username
- user, found := cache.Instance.GetUser(ticket.Owner)
- if !found {
- user, err = rest.GetUser(config.Conf.Bot.Token, ratelimit.Ratelimiter, ticket.Owner)
- if err != nil {
- log.Error(err.Error())
- }
- go cache.Instance.StoreUser(user)
- }
-
- formattedLogs = append(formattedLogs, map[string]interface{}{
- "ticketid": ticket.TicketId,
- "userid": ticket.Owner,
- "username": user.Username,
- })
- }
-
- ctx.HTML(200, "manage/logs",gin.H{
- "name": store.Get("name").(string),
- "guildId": guildIdStr,
- "avatar": store.Get("avatar").(string),
- "baseUrl": config.Conf.Server.BaseUrl,
- "isPageOne": page == 1,
- "previousPage": page - 1,
- "nextPage": page + 1,
- "logs": formattedLogs,
- "page": page,
- })
- } else {
- ctx.Redirect(302, "/login")
- }
+ ctx.HTML(200, "manage/logs", gin.H{
+ "name": store.Get("name").(string),
+ "guildId": guildId,
+ "avatar": store.Get("avatar").(string),
+ "baseUrl": config.Conf.Server.BaseUrl,
+ })
}
diff --git a/app/http/endpoints/manage/logsview.go b/app/http/endpoints/manage/logsview.go
index 169094b..7c7984a 100644
--- a/app/http/endpoints/manage/logsview.go
+++ b/app/http/endpoints/manage/logsview.go
@@ -20,7 +20,6 @@ func LogViewHandler(ctx *gin.Context) {
if store == nil {
return
}
- defer store.Save()
if utils.IsLoggedIn(store) {
userId := utils.GetUserId(store)
@@ -37,7 +36,7 @@ func LogViewHandler(ctx *gin.Context) {
// format ticket ID
ticketId, err := strconv.Atoi(ctx.Param("ticket")); if err != nil {
- ctx.Redirect(302, fmt.Sprintf("/manage/%d/logs/page/1", guild.Id))
+ ctx.Redirect(302, fmt.Sprintf("/manage/%d/logs", guild.Id))
return
}
@@ -48,7 +47,7 @@ func LogViewHandler(ctx *gin.Context) {
// Verify this is a valid ticket and it is closed
if ticket.Uuid == "" || ticket.IsOpen {
- ctx.Redirect(302, fmt.Sprintf("/manage/%d/logs/page/1", guild.Id))
+ ctx.Redirect(302, fmt.Sprintf("/manage/%d/logs", guild.Id))
return
}
diff --git a/app/http/endpoints/manage/panelcreate.go b/app/http/endpoints/manage/panelcreate.go
deleted file mode 100644
index 0ba188f..0000000
--- a/app/http/endpoints/manage/panelcreate.go
+++ /dev/null
@@ -1,175 +0,0 @@
-package manage
-
-import (
- "fmt"
- "github.com/TicketsBot/GoPanel/config"
- "github.com/TicketsBot/GoPanel/database/table"
- "github.com/TicketsBot/GoPanel/messagequeue"
- "github.com/TicketsBot/GoPanel/rpc/cache"
- "github.com/TicketsBot/GoPanel/utils"
- "github.com/gin-gonic/contrib/sessions"
- "github.com/gin-gonic/gin"
- "github.com/rxdn/gdl/objects/channel"
- "strconv"
- "strings"
-)
-
-func PanelCreateHandler(ctx *gin.Context) {
- store := sessions.Default(ctx)
- if store == nil {
- return
- }
- defer store.Save()
-
- if utils.IsLoggedIn(store) {
- userId := utils.GetUserId(store)
-
- // Verify the guild exists
- guildIdStr := ctx.Param("id")
- guildId, err := strconv.ParseUint(guildIdStr, 10, 64)
- if err != nil {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
- return
- }
-
- // Get object for selected guild
- guild, _ := cache.Instance.GetGuild(guildId, false)
-
- // Verify the user has permissions to be here
- isAdmin := make(chan bool)
- go utils.IsAdmin(guild, userId, isAdmin)
- if !<-isAdmin {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
- return
- }
-
- // Get CSRF token
- csrfCorrect := ctx.PostForm("csrf") == store.Get("csrf").(string)
- if !csrfCorrect {
- ctx.Redirect(302, "/")
- return
- }
-
- // Get if the guild is premium
- premiumChan := make(chan bool)
- go utils.IsPremiumGuild(store, guildId, premiumChan)
- premium := <-premiumChan
-
- // Check the user hasn't met their panel quota
- if !premium {
- panels := make(chan []table.Panel)
- go table.GetPanelsByGuild(guildId, panels)
- if len(<-panels) > 1 {
- ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?metQuota=true", guildId))
- return
- }
- }
-
- // Validate title
- title := ctx.PostForm("title")
- if len(title) == 0 || len(title) > 255 {
- ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?validTitle=false", guildId))
- return
- }
-
- // Validate content
- content := ctx.PostForm("content")
- if len(content) == 0 || len(content) > 1024 {
- ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?validContent=false", guildId))
- return
- }
-
- // Validate colour
- validColour := true
- panelColourHex := strings.Replace(ctx.PostForm("colour"), "#", "", -1)
- panelColour, err := strconv.ParseUint(panelColourHex, 16, 32)
- if err != nil {
- validColour = false
- panelColour = 0x23A31A
- }
-
- // Validate channel
- channelIdStr := ctx.PostForm("channel")
- channelId, err := strconv.ParseUint(channelIdStr, 10, 64); if err != nil {
- ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?validChannel=false", guildId))
- return
- }
-
- validChannel := make(chan bool)
- go validateChannel(guildId, channelId, validChannel)
- if !<-validChannel {
- ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?validChannel=false", guildId))
- return
- }
-
- // Validate category
- categoryStr := ctx.PostForm("categories")
- categoryId, err := strconv.ParseUint(categoryStr, 10, 64); if err != nil {
- ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?validCategory=false", guildId))
- return
- }
-
- validCategory := make(chan bool)
- go validateCategory(guildId, categoryId, validCategory)
- if !<-validCategory {
- ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?validCategory=false", guildId))
- return
- }
-
- // Validate reaction emote
- reaction := strings.ToLower(ctx.PostForm("reaction"))
- if len(reaction) == 0 || len(reaction) > 32 {
- ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?validReaction=false", guildId))
- return
- }
- reaction = strings.Replace(reaction, ":", "", -1)
-
- emoji := utils.GetEmojiByName(reaction)
- if emoji == "" {
- ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?validReaction=false", guildId))
- return
- }
-
- settings := table.Panel{
- ChannelId: channelId,
- GuildId: guildId,
- Title: title,
- Content: content,
- Colour: int(panelColour),
- TargetCategory: categoryId,
- ReactionEmote: emoji,
- }
-
- go messagequeue.Client.PublishPanelCreate(settings)
-
- ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?created=true&validColour=%t", guildId, validColour))
- } else {
- ctx.Redirect(302, "/login")
- }
-}
-
-func validateChannel(guildId, channelId uint64, res chan bool) {
- // Compare channel IDs
- validChannel := false
- for _, guildChannel := range cache.Instance.GetGuildChannels(guildId) {
- if guildChannel.Id == channelId {
- validChannel = true
- break
- }
- }
-
- res <- validChannel
-}
-
-func validateCategory(guildId, categoryId uint64, res chan bool) {
- // Compare ch IDs
- validCategory := false
- for _, ch := range cache.Instance.GetGuildChannels(guildId) {
- if ch.Type == channel.ChannelTypeGuildCategory && ch.Id == categoryId {
- validCategory = true
- break
- }
- }
-
- res <- validCategory
-}
diff --git a/app/http/endpoints/manage/paneldelete.go b/app/http/endpoints/manage/paneldelete.go
deleted file mode 100644
index a237a4e..0000000
--- a/app/http/endpoints/manage/paneldelete.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package manage
-
-import (
- "fmt"
- "github.com/TicketsBot/GoPanel/config"
- "github.com/TicketsBot/GoPanel/database/table"
- "github.com/TicketsBot/GoPanel/rpc/cache"
- "github.com/TicketsBot/GoPanel/utils"
- "github.com/gin-gonic/contrib/sessions"
- "github.com/gin-gonic/gin"
- "strconv"
-)
-
-func PanelDeleteHandler(ctx *gin.Context) {
- store := sessions.Default(ctx)
- if store == nil {
- return
- }
- defer store.Save()
-
- if utils.IsLoggedIn(store) {
- userId := utils.GetUserId(store)
-
- // Verify the guild exists
- guildIdStr := ctx.Param("id")
- guildId, err := strconv.ParseUint(guildIdStr, 10, 64)
- if err != nil {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
- return
- }
-
- messageIdStr := ctx.Param("msg")
- messageId, err := strconv.ParseUint(messageIdStr, 10, 64); if err != nil {
- ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels", guildId))
- return
- }
-
- // Get object for selected guild
- guild, _ := cache.Instance.GetGuild(guildId, false)
-
- // Verify the user has permissions to be here
- isAdmin := make(chan bool)
- go utils.IsAdmin(guild, userId, isAdmin)
- if !<-isAdmin {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
- return
- }
-
- // Get CSRF token
- csrfCorrect := ctx.Query("csrf") == store.Get("csrf").(string)
- if !csrfCorrect {
- ctx.Redirect(302, "/")
- return
- }
-
- go table.DeletePanel(messageId)
-
- ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels", guildId))
- } else {
- ctx.Redirect(302, "/login")
- }
-}
diff --git a/app/http/endpoints/manage/panels.go b/app/http/endpoints/manage/panels.go
index ff61658..3786196 100644
--- a/app/http/endpoints/manage/panels.go
+++ b/app/http/endpoints/manage/panels.go
@@ -2,107 +2,18 @@ package manage
import (
"github.com/TicketsBot/GoPanel/config"
- "github.com/TicketsBot/GoPanel/database/table"
- "github.com/TicketsBot/GoPanel/rpc/cache"
- "github.com/TicketsBot/GoPanel/utils"
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
- "strconv"
)
-type wrappedPanel struct {
- MessageId uint64
- ChannelName string
- Title string
- Content string
- CategoryName string
-}
-
func PanelHandler(ctx *gin.Context) {
store := sessions.Default(ctx)
- if store == nil {
- return
- }
- defer store.Save()
+ guildId := ctx.Keys["guildid"].(uint64)
- if utils.IsLoggedIn(store) {
- userId := utils.GetUserId(store)
-
- // Verify the guild exists
- guildIdStr := ctx.Param("id")
- guildId, err := strconv.ParseUint(guildIdStr, 10, 64)
- if err != nil {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
- return
- }
-
- // Get object for selected guild
- guild, _ := cache.Instance.GetGuild(guildId, false)
-
- // Verify the user has permissions to be here
- isAdmin := make(chan bool)
- go utils.IsAdmin(guild, userId, isAdmin)
- if !<-isAdmin {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
- return
- }
-
- // Get active panels
- panelChan := make(chan []table.Panel)
- go table.GetPanelsByGuild(guildId, panelChan)
- panels := <-panelChan
-
- // Get channels
- channels := cache.Instance.GetGuildChannels(guildId)
-
- // Convert to wrapped panels
- wrappedPanels := make([]wrappedPanel, 0)
- for _, panel := range panels {
- wrapper := wrappedPanel{
- MessageId: panel.MessageId,
- Title: panel.Title,
- Content: panel.Content,
- CategoryName: "",
- }
-
- // Get channel name & category name
- for _, guildChannel := range channels {
- if guildChannel.Id == panel.ChannelId {
- wrapper.ChannelName = guildChannel.Name
- } else if guildChannel.Id == panel.TargetCategory {
- wrapper.CategoryName = guildChannel.Name
- }
- }
-
- wrappedPanels = append(wrappedPanels, wrapper)
- }
-
- // Get is premium
- isPremiumChan := make(chan bool)
- go utils.IsPremiumGuild(store, guildId, isPremiumChan)
- isPremium := <-isPremiumChan
-
- ctx.HTML(200, "manage/panels", gin.H{
- "name": store.Get("name").(string),
- "guildId": guildIdStr,
- "csrf": store.Get("csrf").(string),
- "avatar": store.Get("avatar").(string),
- "baseUrl": config.Conf.Server.BaseUrl,
- "panelcount": len(panels),
- "premium": isPremium,
- "panels": wrappedPanels,
- "channels": channels,
-
- "validTitle": ctx.Query("validTitle") != "true",
- "validContent": ctx.Query("validContent") != "false",
- "validColour": ctx.Query("validColour") != "false",
- "validChannel": ctx.Query("validChannel") != "false",
- "validCategory": ctx.Query("validCategory") != "false",
- "validReaction": ctx.Query("validReaction") != "false",
- "created": ctx.Query("created") == "true",
- "metQuota": ctx.Query("metQuota") == "true",
- })
- } else {
- ctx.Redirect(302, "/login")
- }
+ ctx.HTML(200, "manage/panels", gin.H{
+ "name": store.Get("name").(string),
+ "guildId": guildId,
+ "avatar": store.Get("avatar").(string),
+ "baseUrl": config.Conf.Server.BaseUrl,
+ })
}
diff --git a/app/http/endpoints/manage/sendmessage.go b/app/http/endpoints/manage/sendmessage.go
deleted file mode 100644
index d71b732..0000000
--- a/app/http/endpoints/manage/sendmessage.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package manage
-
-import (
- "fmt"
- "github.com/TicketsBot/GoPanel/config"
- "github.com/TicketsBot/GoPanel/database/table"
- "github.com/TicketsBot/GoPanel/rpc/cache"
- "github.com/TicketsBot/GoPanel/rpc/ratelimit"
- "github.com/TicketsBot/GoPanel/utils"
- "github.com/gin-gonic/contrib/sessions"
- "github.com/gin-gonic/gin"
- "github.com/rxdn/gdl/rest"
- "strconv"
-)
-
-func SendMessage(ctx *gin.Context) {
- store := sessions.Default(ctx)
- if store == nil {
- return
- }
- defer store.Save()
-
- if utils.IsLoggedIn(store) {
- userId := utils.GetUserId(store)
-
- // Verify the guild exists
- guildIdStr := ctx.Param("id")
- guildId, err := strconv.ParseUint(guildIdStr, 10, 64)
- if err != nil {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
- return
- }
-
- // Get object for selected guild
- guild, _ := cache.Instance.GetGuild(guildId, false)
-
- // Verify the user has permissions to be here
- isAdmin := make(chan bool)
- go utils.IsAdmin(guild, userId, isAdmin)
- if !<-isAdmin {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
- return
- }
-
- // Get ticket UUID from URL and verify it exists
- ticketChan := make(chan table.Ticket)
- go table.GetTicket(ctx.Param("uuid"), ticketChan)
- ticket := <-ticketChan
- exists := ticket != table.Ticket{}
-
- // Verify that the user has permission to be here
- if ticket.Guild != guildId {
- ctx.Redirect(302, fmt.Sprintf("/manage/%s/tickets", guildIdStr))
- return
- }
-
- if exists {
- content := fmt.Sprintf("**%s**: %s", store.Get("name").(string), ctx.PostForm("message"))
- if len(content) > 2000 {
- content = content[0:1999]
- }
-
- _, _ = rest.CreateMessage(config.Conf.Bot.Token, ratelimit.Ratelimiter, ticket.Channel, rest.CreateMessageData{Content: content})
- }
- } else {
- ctx.Redirect(302, "/login")
- }
-
- ctx.Redirect(301, ctx.Request.URL.String())
-}
diff --git a/app/http/endpoints/manage/settings.go b/app/http/endpoints/manage/settings.go
index 6093b62..65b7f0d 100644
--- a/app/http/endpoints/manage/settings.go
+++ b/app/http/endpoints/manage/settings.go
@@ -14,94 +14,72 @@ import (
func SettingsHandler(ctx *gin.Context) {
store := sessions.Default(ctx)
- if store == nil {
+
+ userId := utils.GetUserId(store)
+
+ // Verify the guild exists
+ guildIdStr := ctx.Param("id")
+ guildId, err := strconv.ParseUint(guildIdStr, 10, 64)
+ if err != nil {
+ ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
return
}
- defer store.Save()
- if utils.IsLoggedIn(store) {
- userId := utils.GetUserId(store)
-
- // Verify the guild exists
- guildIdStr := ctx.Param("id")
- guildId, err := strconv.ParseUint(guildIdStr, 10, 64)
- if err != nil {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
- return
- }
-
- // Check the bot is in the guild
- guild, isInGuild := cache.Instance.GetGuild(guildId, false)
- if !isInGuild {
- ctx.Redirect(302, fmt.Sprintf("https://invite.ticketsbot.net/?guild_id=%s&disable_guild_select=true&response_type=code&scope=bot%%20identify&redirect_uri=%s", guildIdStr, config.Conf.Server.BaseUrl))
- return
- }
-
- // Verify the user has permissions to be here
- isAdmin := make(chan bool)
- go utils.IsAdmin(guild, userId, isAdmin)
- if !<-isAdmin {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
- return
- }
-
- // Get settings from database
- prefix := table.GetPrefix(guildId)
- welcomeMessage := table.GetWelcomeMessage(guildId)
- limit := table.GetTicketLimit(guildId)
- pingEveryone := table.GetPingEveryone(guildId)
- archiveChannel := table.GetArchiveChannel(guildId)
- categoryId := table.GetChannelCategory(guildId)
-
- namingSchemeChan := make(chan table.NamingScheme)
- go table.GetTicketNamingScheme(guildId, namingSchemeChan)
- namingScheme := <-namingSchemeChan
-
- // get guild channels from cache
- channels := cache.Instance.GetGuildChannels(guildId)
-
- // separate out categories
- categories := make([]channel.Channel, 0)
- for _, ch := range channels {
- if ch.Type == channel.ChannelTypeGuildCategory {
- categories = append(categories, ch)
- }
- }
-
- panelSettings := table.GetPanelSettings(guildId)
-
- // Users can close
- usersCanCloseChan := make(chan bool)
- go table.IsUserCanClose(guildId, usersCanCloseChan)
- usersCanClose := <-usersCanCloseChan
-
- invalidPrefix := ctx.Query("validPrefix") == "false"
- invalidWelcomeMessage := ctx.Query("validWelcomeMessage") == "false"
- invalidTicketLimit := ctx.Query("validTicketLimit") == "false"
-
- ctx.HTML(200, "manage/settings", gin.H{
- "name": store.Get("name").(string),
- "guildId": guildIdStr,
- "avatar": store.Get("avatar").(string),
- "prefix": prefix,
- "welcomeMessage": welcomeMessage,
- "ticketLimit": limit,
- "categories": categories,
- "activecategory": categoryId,
- "channels": channels,
- "archivechannel": archiveChannel,
- "invalidPrefix": invalidPrefix,
- "invalidWelcomeMessage": invalidWelcomeMessage,
- "invalidTicketLimit": invalidTicketLimit,
- "csrf": store.Get("csrf").(string),
- "pingEveryone": pingEveryone,
- "paneltitle": panelSettings.Title,
- "panelcontent": panelSettings.Content,
- "panelcolour": strconv.FormatInt(int64(panelSettings.Colour), 16),
- "usersCanClose": usersCanClose,
- "namingScheme": string(namingScheme),
- })
- } else {
- ctx.Redirect(302, "/login")
+ // Check the bot is in the guild
+ guild, isInGuild := cache.Instance.GetGuild(guildId, false)
+ if !isInGuild {
+ ctx.Redirect(302, fmt.Sprintf("https://invite.ticketsbot.net/?guild_id=%s&disable_guild_select=true&response_type=code&scope=bot%%20identify&redirect_uri=%s", guildIdStr, config.Conf.Server.BaseUrl))
+ return
}
+
+ // Verify the user has permissions to be here
+ isAdmin := make(chan bool)
+ go utils.IsAdmin(guild, userId, isAdmin)
+ if !<-isAdmin {
+ ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
+ return
+ }
+
+ namingSchemeChan := make(chan table.NamingScheme)
+ go table.GetTicketNamingScheme(guildId, namingSchemeChan)
+ namingScheme := <-namingSchemeChan
+
+ // get guild channels from cache
+ channels := cache.Instance.GetGuildChannels(guildId)
+
+ // separate out categories
+ categories := make([]channel.Channel, 0)
+ for _, ch := range channels {
+ if ch.Type == channel.ChannelTypeGuildCategory {
+ categories = append(categories, ch)
+ }
+ }
+
+ panelSettings := table.GetPanelSettings(guildId)
+
+ // Users can close
+ usersCanCloseChan := make(chan bool)
+ go table.IsUserCanClose(guildId, usersCanCloseChan)
+ usersCanClose := <-usersCanCloseChan
+
+ invalidPrefix := ctx.Query("validPrefix") == "false"
+ invalidWelcomeMessage := ctx.Query("validWelcomeMessage") == "false"
+ invalidTicketLimit := ctx.Query("validTicketLimit") == "false"
+
+ ctx.HTML(200, "manage/settings", gin.H{
+ "name": store.Get("name").(string),
+ "guildId": guildIdStr,
+ "avatar": store.Get("avatar").(string),
+ "categories": categories,
+ "channels": channels,
+ "invalidPrefix": invalidPrefix,
+ "invalidWelcomeMessage": invalidWelcomeMessage,
+ "invalidTicketLimit": invalidTicketLimit,
+ "csrf": store.Get("csrf").(string),
+ "paneltitle": panelSettings.Title,
+ "panelcontent": panelSettings.Content,
+ "panelcolour": strconv.FormatInt(int64(panelSettings.Colour), 16),
+ "usersCanClose": usersCanClose,
+ "namingScheme": string(namingScheme),
+ })
}
diff --git a/app/http/endpoints/manage/ticketclose.go b/app/http/endpoints/manage/ticketclose.go
deleted file mode 100644
index 3e2e75f..0000000
--- a/app/http/endpoints/manage/ticketclose.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package manage
-
-import (
- "fmt"
- "github.com/TicketsBot/GoPanel/config"
- "github.com/TicketsBot/GoPanel/database/table"
- "github.com/TicketsBot/GoPanel/messagequeue"
- "github.com/TicketsBot/GoPanel/rpc/cache"
- "github.com/TicketsBot/GoPanel/utils"
- "github.com/gin-gonic/contrib/sessions"
- "github.com/gin-gonic/gin"
- "strconv"
-)
-
-func TicketCloseHandler(ctx *gin.Context) {
- store := sessions.Default(ctx)
- if store == nil {
- return
- }
- defer store.Save()
-
- if utils.IsLoggedIn(store) {
- userId := utils.GetUserId(store)
-
- // Verify the guild exists
- guildIdStr := ctx.Param("id")
- guildId, err := strconv.ParseUint(guildIdStr, 10, 64)
- if err != nil {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
- return
- }
-
- // Get object for selected guild
- guild, _ := cache.Instance.GetGuild(guildId, false)
-
- // Verify the user has permissions to be here
- isAdmin := make(chan bool)
- go utils.IsAdmin(guild, userId, isAdmin)
- if !<-isAdmin {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
- return
- }
-
- // Get CSRF token
- csrfCorrect := ctx.PostForm("csrf") == store.Get("csrf").(string)
- if !csrfCorrect {
- ctx.Redirect(302, "/")
- return
- }
-
- // Get the UUID
- uuid := ctx.Param("uuid")
-
- // Verify that tbe ticket exists
- ticketChan := make(chan table.Ticket)
- go table.GetTicket(uuid, ticketChan)
- ticket := <-ticketChan
-
- if ticket.Uuid == "" {
- ctx.Redirect(302, fmt.Sprintf("/manage/%d/tickets/view/%s?sucess=false", guildId, uuid))
- return
- }
-
- // Get the reason
- reason := ctx.PostForm("reason")
- if len(reason) > 255 {
- reason = reason[:255]
- }
-
- go messagequeue.Client.PublishTicketClose(ticket.Uuid, userId, reason)
-
- ctx.Redirect(302, fmt.Sprintf("/manage/%d/tickets", guildId))
- } else {
- ctx.Redirect(302, "/login")
- }
-}
diff --git a/app/http/endpoints/manage/ticketlist.go b/app/http/endpoints/manage/ticketlist.go
index 7d7221c..f49b78e 100644
--- a/app/http/endpoints/manage/ticketlist.go
+++ b/app/http/endpoints/manage/ticketlist.go
@@ -2,101 +2,18 @@ package manage
import (
"github.com/TicketsBot/GoPanel/config"
- "github.com/TicketsBot/GoPanel/database/table"
- "github.com/TicketsBot/GoPanel/rpc/cache"
- "github.com/TicketsBot/GoPanel/utils"
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
- "strconv"
- "strings"
)
func TicketListHandler(ctx *gin.Context) {
store := sessions.Default(ctx)
- if store == nil {
- return
- }
- defer store.Save()
+ guildId := ctx.Keys["guildid"].(uint64)
- if utils.IsLoggedIn(store) {
- userId := utils.GetUserId(store)
-
- // Verify the guild exists
- guildIdStr := ctx.Param("id")
- guildId, err := strconv.ParseUint(guildIdStr, 10, 64)
- if err != nil {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
- return
- }
-
- // Get object for selected guild
- guild, _ := cache.Instance.GetGuild(guildId, false)
-
- // Verify the user has permissions to be here
- isAdmin := make(chan bool)
- go utils.IsAdmin(guild, userId, isAdmin)
- if !<-isAdmin {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
- return
- }
-
- tickets := table.GetOpenTickets(guildId)
-
- var toFetch []uint64
- for _, ticket := range tickets {
- toFetch = append(toFetch, ticket.Owner)
-
- for _, idStr := range strings.Split(ticket.Members, ",") {
- if memberId, err := strconv.ParseUint(idStr, 10, 64); err == nil {
- toFetch = append(toFetch, memberId)
- }
- }
- }
-
- nodes := make(map[uint64]table.UsernameNode)
- for _, node := range table.GetUserNodes(toFetch) {
- nodes[node.Id] = node
- }
-
- var ticketsFormatted []map[string]interface{}
-
- for _, ticket := range tickets {
- var membersFormatted []map[string]interface{}
- for index, memberIdStr := range strings.Split(ticket.Members, ",") {
- if memberId, err := strconv.ParseUint(memberIdStr, 10, 64); err == nil {
- if memberId != 0 {
- var separator string
- if index != len(strings.Split(ticket.Members, ",")) - 1 {
- separator = ", "
- }
-
- membersFormatted = append(membersFormatted, map[string]interface{}{
- "username": nodes[memberId].Name,
- "discrim": nodes[memberId].Discriminator,
- "sep": separator,
- })
- }
- }
- }
-
- ticketsFormatted = append(ticketsFormatted, map[string]interface{}{
- "uuid": ticket.Uuid,
- "ticketId": ticket.TicketId,
- "username": nodes[ticket.Owner].Name,
- "discrim": nodes[ticket.Owner].Discriminator,
- "members": membersFormatted,
- })
- }
-
- ctx.HTML(200, "manage/ticketlist", gin.H{
- "name": store.Get("name").(string),
- "guildId": guildIdStr,
- "csrf": store.Get("csrf").(string),
- "avatar": store.Get("avatar").(string),
- "baseUrl": config.Conf.Server.BaseUrl,
- "tickets": ticketsFormatted,
- })
- } else {
- ctx.Redirect(302, "/login")
- }
+ ctx.HTML(200, "manage/ticketlist", gin.H{
+ "name": store.Get("name").(string),
+ "guildId": guildId,
+ "avatar": store.Get("avatar").(string),
+ "baseUrl": config.Conf.Server.BaseUrl,
+ })
}
diff --git a/app/http/endpoints/manage/ticketview.go b/app/http/endpoints/manage/ticketview.go
index d5c18ba..e9417eb 100644
--- a/app/http/endpoints/manage/ticketview.go
+++ b/app/http/endpoints/manage/ticketview.go
@@ -1,117 +1,20 @@
package manage
import (
- "fmt"
"github.com/TicketsBot/GoPanel/config"
- "github.com/TicketsBot/GoPanel/database/table"
- "github.com/TicketsBot/GoPanel/rpc/cache"
- "github.com/TicketsBot/GoPanel/rpc/ratelimit"
- "github.com/TicketsBot/GoPanel/utils"
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
- "github.com/rxdn/gdl/rest"
- "regexp"
- "strconv"
- "strings"
)
-var MentionRegex, _ = regexp.Compile("<@(\\d+)>")
-
func TicketViewHandler(ctx *gin.Context) {
store := sessions.Default(ctx)
- if store == nil {
- return
- }
- defer store.Save()
+ guildId := ctx.Keys["guildid"].(uint64)
- if utils.IsLoggedIn(store) {
- userId := utils.GetUserId(store)
-
- // Verify the guild exists
- guildIdStr := ctx.Param("id")
- guildId, err := strconv.ParseUint(guildIdStr, 10, 64)
- if err != nil {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
- return
- }
-
- // Get object for selected guild
- guild, _ := cache.Instance.GetGuild(guildId, false)
-
- // Verify the user has permissions to be here
- isAdmin := make(chan bool)
- go utils.IsAdmin(guild, userId, isAdmin)
- if !<-isAdmin {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
- return
- }
-
- // Get ticket UUID from URL and verify it exists
- uuid := ctx.Param("uuid")
- ticketChan := make(chan table.Ticket)
- go table.GetTicket(uuid, ticketChan)
- ticket := <-ticketChan
- exists := ticket != table.Ticket{}
-
- // If invalid ticket UUID, take user to ticket list
- if !exists {
- ctx.Redirect(302, fmt.Sprintf("/manage/%s/tickets", guildIdStr))
- return
- }
-
- // Verify that the user has permission to be here
- if ticket.Guild != guildId {
- ctx.Redirect(302, fmt.Sprintf("/manage/%s/tickets", guildIdStr))
- return
- }
-
- // Get messages
- messages, err := rest.GetChannelMessages(config.Conf.Bot.Token, ratelimit.Ratelimiter, ticket.Channel, rest.GetChannelMessagesData{Limit: 100})
-
- // Format messages, exclude unneeded data
- var messagesFormatted []map[string]interface{}
- for _, message := range utils.Reverse(messages) {
- content := message.Content
-
- // Format mentions properly
- match := MentionRegex.FindAllStringSubmatch(content, -1)
- for _, mention := range match {
- if len(mention) >= 2 {
- mentionedId, err := strconv.ParseUint(mention[1], 10, 64)
- if err != nil {
- continue
- }
-
- ch := make(chan string)
- go table.GetUsername(mentionedId, ch)
- content = strings.ReplaceAll(content, fmt.Sprintf("<@%d>", mentionedId), fmt.Sprintf("@%s", <-ch))
- }
- }
-
- messagesFormatted = append(messagesFormatted, map[string]interface{}{
- "username": message.Author.Username,
- "content": content,
- })
- }
-
- premium := make(chan bool)
- go utils.IsPremiumGuild(store, guildId, premium)
-
- ctx.HTML(200, "manage/ticketview", gin.H{
- "name": store.Get("name").(string),
- "guildId": guildIdStr,
- "csrf": store.Get("csrf").(string),
- "avatar": store.Get("avatar").(string),
- "baseUrl": config.Conf.Server.BaseUrl,
- "isError": false,
- "error": "",
- "messages": messagesFormatted,
- "ticketId": ticket.TicketId,
- "uuid": ticket.Uuid,
- "include_mock": true,
- "premium": <-premium,
- })
- } else {
- ctx.Redirect(302, "/login")
- }
+ ctx.HTML(200, "manage/ticketview", gin.H{
+ "name": store.Get("name").(string),
+ "guildId": guildId,
+ "avatar": store.Get("avatar").(string),
+ "baseUrl": config.Conf.Server.BaseUrl,
+ "uuid": ctx.Param("uuid"),
+ })
}
diff --git a/app/http/endpoints/manage/updatesettings.go b/app/http/endpoints/manage/updatesettings.go
deleted file mode 100644
index 9087f22..0000000
--- a/app/http/endpoints/manage/updatesettings.go
+++ /dev/null
@@ -1,136 +0,0 @@
-package manage
-
-import (
- "fmt"
- "github.com/TicketsBot/GoPanel/config"
- "github.com/TicketsBot/GoPanel/database/table"
- "github.com/TicketsBot/GoPanel/rpc/cache"
- "github.com/TicketsBot/GoPanel/utils"
- "github.com/gin-gonic/contrib/sessions"
- "github.com/gin-gonic/gin"
- "github.com/rxdn/gdl/objects/channel"
- "strconv"
-)
-
-func UpdateSettingsHandler(ctx *gin.Context) {
- store := sessions.Default(ctx)
- if store == nil {
- return
- }
- defer store.Save()
-
- if utils.IsLoggedIn(store) {
- userId := utils.GetUserId(store)
-
- // Verify the guild exists
- guildIdStr := ctx.Param("id")
- guildId, err := strconv.ParseUint(guildIdStr, 10, 64)
- if err != nil {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
- return
- }
-
- // Get object for selected guild
- guild, _ := cache.Instance.GetGuild(guildId, false)
-
- // Verify the user has permissions to be here
- isAdmin := make(chan bool)
- go utils.IsAdmin(guild, userId, isAdmin)
- if !<-isAdmin {
- ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
- return
- }
-
- // Get CSRF token
- csrfCorrect := ctx.PostForm("csrf") == store.Get("csrf").(string)
- if !csrfCorrect {
- ctx.Redirect(302, "/")
- return
- }
-
- // Get prefix
- prefix := ctx.PostForm("prefix")
- prefixValid := false
- if prefix != "" && len(prefix) < 8 {
- table.UpdatePrefix(guildId, prefix)
- prefixValid = true
- }
-
- // Get welcome message
- welcomeMessageValid := false
- welcomeMessage := ctx.PostForm("welcomeMessage")
- if welcomeMessage != "" && len(welcomeMessage) < 1000 {
- table.UpdateWelcomeMessage(guildId, welcomeMessage)
- welcomeMessageValid = true
- }
-
- // Get ticket limit
- var limit int
- limitStr := ctx.PostForm("ticketlimit")
-
- // Verify input is an int and overwrite default limit
- if utils.IsInt(limitStr) {
- limit, _ = strconv.Atoi(limitStr)
- }
-
- // Update limit, or get current limit if user input is invalid
- ticketLimitValid := false
- if limitStr != "" && utils.IsInt(limitStr) && limit >= 1 && limit <= 10 {
- table.UpdateTicketLimit(guildId, limit)
- ticketLimitValid = true
- }
-
- // Ping everyone
- pingEveryone := ctx.PostForm("pingeveryone") == "on"
- table.UpdatePingEveryone(guildId, pingEveryone)
-
- // Get a list of actual category IDs
- channels := cache.Instance.GetGuildChannels(guildId)
-
- // Update category
- if categoryId, err := strconv.ParseUint(ctx.PostForm("category"), 10, 64); err == nil {
- for _, ch := range channels {
- if ch.Id == categoryId { // compare ID
- if ch.Type == channel.ChannelTypeGuildCategory { // verify we're dealing with a category
- table.UpdateChannelCategory(guildId, categoryId)
- }
- break
- }
- }
- }
-
- // Archive channel
- if archiveChannelId, err := strconv.ParseUint(ctx.PostForm("archivechannel"), 10, 64); err == nil {
- for _, ch := range channels {
- if ch.Id == archiveChannelId { // compare ID
- if ch.Type == channel.ChannelTypeGuildText { // verify channel type
- table.UpdateArchiveChannel(guildId, archiveChannelId)
- }
- break
- }
- }
- }
-
- // Users can close
- usersCanClose := ctx.PostForm("userscanclose") == "on"
- table.SetUserCanClose(guildId, usersCanClose)
-
- // Get naming scheme
- namingScheme := table.NamingScheme(ctx.PostForm("namingscheme"))
- isValidScheme := false
- for _, validNamingScheme := range table.Schemes {
- if validNamingScheme == namingScheme {
- isValidScheme = true
- break
- }
- }
-
- if isValidScheme {
- go table.SetTicketNamingScheme(guildId, namingScheme)
- }
-
- ctx.Redirect(302, fmt.Sprintf("/manage/%d/settings?validPrefix=%t&validWelcomeMessage=%t&validTicketLimit=%t", guildId, prefixValid, welcomeMessageValid, ticketLimitValid))
- } else {
- ctx.Redirect(302, "/login")
- }
-}
diff --git a/app/http/endpoints/manage/webchatws.go b/app/http/endpoints/manage/webchatws.go
index b5c80da..7877972 100644
--- a/app/http/endpoints/manage/webchatws.go
+++ b/app/http/endpoints/manage/webchatws.go
@@ -1,22 +1,14 @@
package manage
import (
- "bytes"
- "encoding/json"
"fmt"
- "github.com/TicketsBot/GoPanel/config"
- "github.com/TicketsBot/GoPanel/database/table"
"github.com/TicketsBot/GoPanel/rpc/cache"
- "github.com/TicketsBot/GoPanel/rpc/ratelimit"
"github.com/TicketsBot/GoPanel/utils"
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
- "github.com/rxdn/gdl/rest"
- "net/http"
"strconv"
"sync"
- "time"
)
var upgrader = websocket.Upgrader{
@@ -47,169 +39,95 @@ type (
func WebChatWs(ctx *gin.Context) {
store := sessions.Default(ctx)
- if store == nil {
+
+ conn, err := upgrader.Upgrade(ctx.Writer, ctx.Request, nil)
+ if err != nil {
+ fmt.Println(err.Error())
return
}
- defer store.Save()
- if utils.IsLoggedIn(store) {
- conn, err := upgrader.Upgrade(ctx.Writer, ctx.Request, nil)
- if err != nil {
- fmt.Println(err.Error())
- return
- }
-
- socket := &Socket{
- Ws: conn,
- }
-
- conn.SetCloseHandler(func(code int, text string) error {
- i := -1
- SocketsLock.Lock()
-
- for index, element := range Sockets {
- if element == socket {
- i = index
- break
- }
- }
-
- if i != -1 {
- Sockets = Sockets[:i+copy(Sockets[i:], Sockets[i+1:])]
- }
- SocketsLock.Unlock()
-
- return nil
- })
+ socket := &Socket{
+ Ws: conn,
+ }
+ conn.SetCloseHandler(func(code int, text string) error {
+ i := -1
SocketsLock.Lock()
- Sockets = append(Sockets, socket)
- SocketsLock.Unlock()
- userId := utils.GetUserId(store)
- var guildId string
- var guildIdParsed uint64
- var ticket int
-
- for {
- var evnt WsEvent
- err := conn.ReadJSON(&evnt)
- if err != nil {
+ for index, element := range Sockets {
+ if element == socket {
+ i = index
break
}
+ }
- if guildId == "" && evnt.Type != "auth" {
+ if i != -1 {
+ Sockets = Sockets[:i+copy(Sockets[i:], Sockets[i+1:])]
+ }
+ SocketsLock.Unlock()
+
+ return nil
+ })
+
+ SocketsLock.Lock()
+ Sockets = append(Sockets, socket)
+ SocketsLock.Unlock()
+ userId := utils.GetUserId(store)
+
+ var guildId string
+ var guildIdParsed uint64
+ var ticket int
+
+ for {
+ var evnt WsEvent
+ err := conn.ReadJSON(&evnt)
+ if err != nil {
+ break
+ }
+
+ if guildId == "" && evnt.Type != "auth" {
+ conn.Close()
+ break
+ } else if evnt.Type == "auth" {
+ data := evnt.Data.(map[string]interface{})
+
+ guildId = data["guild"].(string)
+ ticket, err = strconv.Atoi(data["ticket"].(string))
+ if err != nil {
conn.Close()
break
- } else if evnt.Type == "auth" {
- data := evnt.Data.(map[string]interface{})
+ }
- guildId = data["guild"].(string)
- ticket, err = strconv.Atoi(data["ticket"].(string));
- if err != nil {
- conn.Close()
- }
+ socket.Guild = guildId
+ socket.Ticket = ticket
- socket.Guild = guildId
- socket.Ticket = ticket
+ // Verify the guild exists
+ guildIdParsed, err = strconv.ParseUint(guildId, 10, 64)
+ if err != nil {
+ fmt.Println(err.Error())
+ conn.Close()
+ return
+ }
- // Verify the guild exists
- guildIdParsed, err = strconv.ParseUint(guildId, 10, 64)
- if err != nil {
- fmt.Println(err.Error())
- conn.Close()
- return
- }
+ // Get object for selected guild
+ guild, _ := cache.Instance.GetGuild(guildIdParsed, false)
- // Get object for selected guild
- guild, _ := cache.Instance.GetGuild(guildIdParsed, false)
+ // Verify the user has permissions to be here
+ isAdmin := make(chan bool)
+ go utils.IsAdmin(guild, userId, isAdmin)
+ if !<-isAdmin {
+ fmt.Println(err.Error())
+ conn.Close()
+ return
+ }
- // Verify the user has permissions to be here
- isAdmin := make(chan bool)
- go utils.IsAdmin(guild, userId, isAdmin)
- if !<-isAdmin {
- fmt.Println(err.Error())
- conn.Close()
- return
- }
-
- // Verify the guild is premium
- premium := make(chan bool)
- go utils.IsPremiumGuild(store, guildIdParsed, premium)
- if !<-premium {
- conn.Close()
- return
- }
- } else if evnt.Type == "send" {
- data := evnt.Data.(string)
-
- if data == "" {
- continue
- }
-
- // Get ticket UUID from URL and verify it exists
- ticketChan := make(chan table.Ticket)
- go table.GetTicketById(guildIdParsed, ticket, ticketChan)
- ticket := <-ticketChan
- exists := ticket != table.Ticket{}
-
- if exists {
- content := data
- if len(content) > 2000 {
- content = content[0:1999]
- }
-
- // Preferably send via a webhook
- webhookChan := make(chan *string)
- go table.GetWebhookByUuid(ticket.Uuid, webhookChan)
- webhook := <-webhookChan
-
- success := false
- if webhook != nil {
- success = executeWebhook( ticket.Uuid, *webhook, content, store)
- }
-
- if !success {
- content = fmt.Sprintf("**%s**: %s", store.Get("name").(string), data)
- if len(content) > 2000 {
- content = content[0:1999]
- }
-
- _, _ = rest.CreateMessage(config.Conf.Bot.Token, ratelimit.Ratelimiter, ticket.Channel, rest.CreateMessageData{Content: content})
- }
- }
+ // Verify the guild is premium
+ premium := make(chan bool)
+ go utils.IsPremiumGuild(guildIdParsed, premium)
+ if !<-premium {
+ conn.Close()
+ return
}
}
}
}
-
-func executeWebhook(uuid, webhook, content string, store sessions.Session) bool {
- body := map[string]interface{}{
- "content": content,
- "username": store.Get("name").(string),
- "avatar_url": store.Get("avatar").(string),
- }
- encoded, err := json.Marshal(&body); if err != nil {
- return false
- }
- req, err := http.NewRequest("POST", fmt.Sprintf("https://canary.discordapp.com/api/webhooks/%s", webhook), bytes.NewBuffer(encoded)); if err != nil {
- return false
- }
-
- req.Header.Set("Content-Type", "application/json")
-
- client := &http.Client{}
- client.Timeout = 3 * time.Second
-
- res, err := client.Do(req); if err != nil {
- return false
- }
-
- if res.StatusCode == 404 || res.StatusCode == 403 {
- go table.DeleteWebhookByUuid(uuid)
- } else {
- return true
- }
-
- return false
-}
diff --git a/app/http/endpoints/root/index.go b/app/http/endpoints/root/index.go
index f91cac3..f2a4308 100644
--- a/app/http/endpoints/root/index.go
+++ b/app/http/endpoints/root/index.go
@@ -11,39 +11,27 @@ import (
func IndexHandler(ctx *gin.Context) {
store := sessions.Default(ctx)
- if store == nil {
- return
- }
- defer store.Save()
+ userId := utils.GetUserId(store)
- if utils.IsLoggedIn(store) {
- userId := utils.GetUserId(store)
-
- userGuilds := table.GetGuilds(userId)
- adminGuilds := make([]guild.Guild, 0)
- for _, g := range userGuilds {
- fakeGuild := guild.Guild{
- Id: g.Id,
- OwnerId: g.OwnerId,
- Permissions: g.Permissions,
- }
-
- isAdmin := make(chan bool)
- go utils.IsAdmin(fakeGuild, userId, isAdmin)
- if <-isAdmin {
- adminGuilds = append(adminGuilds, g)
- }
+ userGuilds := table.GetGuilds(userId)
+ adminGuilds := make([]guild.Guild, 0)
+ for _, g := range userGuilds {
+ fakeGuild := guild.Guild{
+ Id: g.Id,
+ OwnerId: g.OwnerId,
+ Permissions: g.Permissions,
}
- ctx.HTML(200, "main/index", gin.H{
- "name": store.Get("name").(string),
- "baseurl": config.Conf.Server.BaseUrl,
- "servers": adminGuilds,
- "empty": len(adminGuilds) == 0,
- "isIndex": true,
- "avatar": store.Get("avatar").(string),
- })
- } else {
- ctx.Redirect(302, "/login")
+ isAdmin := make(chan bool)
+ go utils.IsAdmin(fakeGuild, userId, isAdmin)
+ if <-isAdmin {
+ adminGuilds = append(adminGuilds, g)
+ }
}
+
+ ctx.HTML(200, "main/index", gin.H{
+ "name": store.Get("name").(string),
+ "baseurl": config.Conf.Server.BaseUrl,
+ "avatar": store.Get("avatar").(string),
+ })
}
diff --git a/app/http/middleware/authenticatecookie.go b/app/http/middleware/authenticatecookie.go
new file mode 100644
index 0000000..0f34458
--- /dev/null
+++ b/app/http/middleware/authenticatecookie.go
@@ -0,0 +1,24 @@
+package middleware
+
+import (
+ "github.com/TicketsBot/GoPanel/utils"
+ "github.com/gin-gonic/contrib/sessions"
+ "github.com/gin-gonic/gin"
+)
+
+func AuthenticateCookie(ctx *gin.Context) {
+ store := sessions.Default(ctx)
+ if store == nil {
+ ctx.Redirect(302, "/login")
+ ctx.Abort()
+ return
+ }
+
+ if !utils.IsLoggedIn(store) {
+ ctx.Redirect(302, "/login")
+ ctx.Abort()
+ return
+ }
+
+ ctx.Keys["userid"] = utils.GetUserId(store)
+}
diff --git a/app/http/middleware/authenticateguild.go b/app/http/middleware/authenticateguild.go
new file mode 100644
index 0000000..6eb5af5
--- /dev/null
+++ b/app/http/middleware/authenticateguild.go
@@ -0,0 +1,71 @@
+package middleware
+
+import (
+ "fmt"
+ "github.com/TicketsBot/GoPanel/config"
+ "github.com/TicketsBot/GoPanel/rpc/cache"
+ "github.com/TicketsBot/GoPanel/utils"
+ "github.com/gin-gonic/gin"
+ "strconv"
+)
+
+// requires AuthenticateCookie middleware to be run before
+func AuthenticateGuild(isApiMethod bool) gin.HandlerFunc {
+ return func(ctx *gin.Context) {
+ if guildId, ok := ctx.Params.Get("id"); ok {
+ parsed, err := strconv.ParseUint(guildId, 10, 64)
+ if err != nil {
+ if isApiMethod {
+ ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
+ ctx.Abort()
+ } else {
+ ctx.AbortWithStatusJSON(400, gin.H{
+ "success": false,
+ "error": "Invalid guild ID",
+ })
+ }
+ return
+ }
+
+ ctx.Keys["guildid"] = parsed
+
+ guild, found := cache.Instance.GetGuild(parsed, false)
+ if !found {
+ if isApiMethod {
+ ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
+ } else {
+ ctx.Redirect(302, fmt.Sprintf("https://invite.ticketsbot.net/?guild_id=%d&disable_guild_select=true&response_type=code&scope=bot%%20identify&redirect_uri=%s", parsed, config.Conf.Server.BaseUrl))
+ }
+ ctx.Abort()
+ return
+ }
+
+ ctx.Keys["guild"] = guild
+
+ // Verify the user has permissions to be here
+ isAdmin := make(chan bool)
+ go utils.IsAdmin(guild, ctx.Keys["userid"].(uint64), isAdmin)
+ if !<-isAdmin {
+ if isApiMethod {
+ ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
+ ctx.Abort()
+ } else {
+ ctx.AbortWithStatusJSON(403, gin.H{
+ "success": false,
+ "error": "Unauthorized",
+ })
+ }
+ }
+ } else {
+ if isApiMethod {
+ ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
+ ctx.Abort()
+ } else {
+ ctx.AbortWithStatusJSON(400, gin.H{
+ "success": false,
+ "error": "Invalid guild ID",
+ })
+ }
+ }
+ }
+}
diff --git a/app/http/middleware/authenticatetoken.go b/app/http/middleware/authenticatetoken.go
new file mode 100644
index 0000000..4bab341
--- /dev/null
+++ b/app/http/middleware/authenticatetoken.go
@@ -0,0 +1,54 @@
+package middleware
+
+import (
+ "errors"
+ "fmt"
+ "github.com/TicketsBot/GoPanel/config"
+ "github.com/dgrijalva/jwt-go"
+ "github.com/gin-gonic/gin"
+ "strconv"
+)
+
+func AuthenticateToken(ctx *gin.Context) {
+ header := ctx.GetHeader("Authorization")
+
+ token, err := jwt.Parse(header, func(token *jwt.Token) (interface{}, error) {
+ if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
+ return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
+ }
+
+ return []byte(config.Conf.Server.Secret), nil
+ })
+
+ if err != nil {
+ ctx.AbortWithStatusJSON(403, gin.H{
+ "error": err.Error(),
+ })
+ return
+ }
+
+ if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
+ userId, hasUserId := claims["userid"]
+ if !hasUserId {
+ ctx.AbortWithStatusJSON(403, gin.H{
+ "error": errors.New("token is invalid"),
+ })
+ return
+ }
+
+ parsedId, err := strconv.ParseUint(userId.(string), 10, 64)
+ if err != nil {
+ ctx.AbortWithStatusJSON(403, gin.H{
+ "error": errors.New("token is invalid"),
+ })
+ return
+ }
+
+ ctx.Keys["userid"] = parsedId
+ } else {
+ ctx.AbortWithStatusJSON(403, gin.H{
+ "error": errors.New("token is invalid"),
+ })
+ return
+ }
+}
diff --git a/app/http/server.go b/app/http/server.go
index c463480..2d3b0e9 100644
--- a/app/http/server.go
+++ b/app/http/server.go
@@ -2,8 +2,10 @@ package http
import (
"fmt"
+ "github.com/TicketsBot/GoPanel/app/http/endpoints/api"
"github.com/TicketsBot/GoPanel/app/http/endpoints/manage"
"github.com/TicketsBot/GoPanel/app/http/endpoints/root"
+ "github.com/TicketsBot/GoPanel/app/http/middleware"
"github.com/TicketsBot/GoPanel/config"
"github.com/gin-contrib/multitemplate"
"github.com/gin-contrib/static"
@@ -31,33 +33,66 @@ func StartServer() {
// Handle static asset requests
router.Use(static.Serve("/assets/", static.LocalFile("./public/static", false)))
+ router.Use(gin.Recovery())
+
// Register templates
router.HTMLRender = createRenderer()
- router.GET("/", root.IndexHandler)
-
router.GET("/login", root.LoginHandler)
router.GET("/callback", root.CallbackHandler)
- router.GET("/logout", root.LogoutHandler)
- router.GET("/manage/:id/settings", manage.SettingsHandler)
- router.POST("/manage/:id/settings", manage.UpdateSettingsHandler)
+ router.GET("/manage/:id/logs/view/:ticket", manage.LogViewHandler) // we check in the actual handler bc of a custom redirect
- router.GET("/manage/:id/logs/page/:page", manage.LogsHandler)
- router.GET("/manage/:id/logs/view/:ticket", manage.LogViewHandler)
+ authorized := router.Group("/", middleware.AuthenticateCookie)
+ {
+ authorized.POST("/token", api.TokenHandler)
- router.GET("/manage/:id/blacklist", manage.BlacklistHandler)
- router.GET("/manage/:id/blacklist/remove/:user", manage.BlacklistRemoveHandler)
+ authenticateGuild := authorized.Group("/", middleware.AuthenticateGuild(false))
- router.GET("/manage/:id/panels", manage.PanelHandler)
- router.POST("/manage/:id/panels/create", manage.PanelCreateHandler)
- router.GET("/manage/:id/panels/delete/:msg", manage.PanelDeleteHandler)
+ authorized.GET("/", root.IndexHandler)
+ authorized.GET("/logout", root.LogoutHandler)
- router.GET("/manage/:id/tickets", manage.TicketListHandler)
- router.GET("/manage/:id/tickets/view/:uuid", manage.TicketViewHandler)
- router.POST("/manage/:id/tickets/view/:uuid/close", manage.TicketCloseHandler)
- router.POST("/manage/:id/tickets/view/:uuid", manage.SendMessage)
- router.GET("/webchat", manage.WebChatWs)
+ authenticateGuild.GET("/manage/:id/settings", manage.SettingsHandler)
+ authenticateGuild.GET("/manage/:id/logs", manage.LogsHandler)
+ authenticateGuild.GET("/manage/:id/blacklist", manage.BlacklistHandler)
+ authenticateGuild.GET("/manage/:id/panels", manage.PanelHandler)
+
+ authenticateGuild.GET("/manage/:id/tickets", manage.TicketListHandler)
+ authenticateGuild.GET("/manage/:id/tickets/view/:uuid", manage.TicketViewHandler)
+ authenticateGuild.POST("/manage/:id/tickets/view/:uuid", api.SendMessage)
+
+ authorized.GET("/webchat", manage.WebChatWs)
+ }
+
+ apiGroup := router.Group("/api", middleware.AuthenticateToken)
+ guildAuthApi := apiGroup.Group("/", middleware.AuthenticateGuild(true))
+ {
+ guildAuthApi.GET("/:id/channels", api.ChannelsHandler)
+ guildAuthApi.GET("/:id/premium", api.PremiumHandler)
+
+ guildAuthApi.GET("/:id/settings", api.GetSettingsHandler)
+ guildAuthApi.POST("/:id/settings", api.UpdateSettingsHandler)
+
+ guildAuthApi.GET("/:id/blacklist", api.GetBlacklistHandler)
+ guildAuthApi.PUT("/:id/blacklist", api.AddBlacklistHandler)
+ guildAuthApi.DELETE("/:id/blacklist/:user", api.RemoveBlacklistHandler)
+
+ guildAuthApi.GET("/:id/panels", api.ListPanels)
+ guildAuthApi.PUT("/:id/panels", api.CreatePanel)
+ guildAuthApi.DELETE("/:id/panels/:message", api.DeletePanel)
+
+ guildAuthApi.GET("/:id/logs/:page", api.GetLogs)
+
+ guildAuthApi.GET("/:id/tickets", api.GetTickets)
+ guildAuthApi.GET("/:id/tickets/:uuid", api.GetTicket)
+ guildAuthApi.POST("/:id/tickets/:uuid", api.SendMessage)
+ guildAuthApi.DELETE("/:id/tickets/:uuid", api.CloseTicket)
+ }
+
+ userGroup := router.Group("/user", middleware.AuthenticateToken)
+ {
+ userGroup.GET("/guilds", api.GetGuilds)
+ }
if err := router.Run(config.Conf.Server.Host); err != nil {
panic(err)
diff --git a/config/config.go b/config/config.go
index be0dcf3..4c713d5 100644
--- a/config/config.go
+++ b/config/config.go
@@ -22,6 +22,7 @@ type (
MainSite string
Ratelimit Ratelimit
Session Session
+ Secret string
}
Ratelimit struct {
diff --git a/database/table/archivechannel.go b/database/table/archivechannel.go
index 6748a9d..8bbc01b 100644
--- a/database/table/archivechannel.go
+++ b/database/table/archivechannel.go
@@ -18,9 +18,8 @@ func UpdateArchiveChannel(guildId uint64, channelId uint64) {
database.Database.Where(ArchiveChannel{Guild: guildId}).Assign(ArchiveChannel{Channel: channelId}).FirstOrCreate(&channel)
}
-func GetArchiveChannel(guildId uint64) uint64 {
+func GetArchiveChannel(guildId uint64, ch chan uint64) {
var channel ArchiveChannel
database.Database.Where(&ArchiveChannel{Guild: guildId}).First(&channel)
-
- return channel.Channel
+ ch <- channel.Channel
}
diff --git a/database/table/blacklist.go b/database/table/blacklist.go
index d59b8c2..6c36166 100644
--- a/database/table/blacklist.go
+++ b/database/table/blacklist.go
@@ -25,9 +25,7 @@ func AddBlacklist(guildId, userId uint64) {
}
func RemoveBlacklist(guildId, userId uint64) {
- var node BlacklistNode
- database.Database.Where(BlacklistNode{Guild: guildId, User: userId}).Take(&node)
- database.Database.Delete(&node)
+ database.Database.Where(BlacklistNode{Guild: guildId, User: userId}).Delete(BlacklistNode{})
}
func GetBlacklistNodes(guildId uint64) []BlacklistNode {
diff --git a/database/table/channelcategory.go b/database/table/channelcategory.go
index 40bd9de..b1f905c 100644
--- a/database/table/channelcategory.go
+++ b/database/table/channelcategory.go
@@ -17,9 +17,8 @@ func UpdateChannelCategory(guildId uint64, categoryId uint64) {
database.Database.Where(&ChannelCategory{GuildId: guildId}).Assign(&ChannelCategory{Category: categoryId}).FirstOrCreate(&ChannelCategory{})
}
-func GetChannelCategory(guildId uint64) uint64 {
+func GetChannelCategory(guildId uint64, ch chan uint64) {
var category ChannelCategory
database.Database.Where(&ChannelCategory{GuildId: guildId}).First(&category)
-
- return category.Category
+ ch <- category.Category
}
diff --git a/database/table/panels.go b/database/table/panels.go
index c65a006..59ed716 100644
--- a/database/table/panels.go
+++ b/database/table/panels.go
@@ -5,12 +5,12 @@ import (
)
type Panel struct {
- MessageId uint64 `gorm:"column:MESSAGEID"`
- ChannelId uint64 `gorm:"column:CHANNELID"`
- GuildId uint64 `gorm:"column:GUILDID"` // Might be useful in the future so we store it
+ MessageId uint64 `gorm:"column:MESSAGEID"`
+ ChannelId uint64 `gorm:"column:CHANNELID"`
+ GuildId uint64 `gorm:"column:GUILDID"` // Might be useful in the future so we store it
Title string `gorm:"column:TITLE;type:VARCHAR(255)"`
Content string `gorm:"column:CONTENT;type:TEXT"`
- Colour int `gorm:"column:COLOUR`
+ Colour uint32 `gorm:"column:COLOUR`
TargetCategory uint64 `gorm:"column:TARGETCATEGORY"`
ReactionEmote string `gorm:"column:REACTIONEMOTE;type:VARCHAR(32)"`
}
@@ -19,7 +19,7 @@ func (Panel) TableName() string {
return "panels"
}
-func AddPanel(messageId, channelId, guildId uint64, title, content string, colour int, targetCategory uint64, reactionEmote string) {
+func AddPanel(messageId, channelId, guildId uint64, title, content string, colour uint32, targetCategory uint64, reactionEmote string) {
database.Database.Create(&Panel{
MessageId: messageId,
ChannelId: channelId,
@@ -45,6 +45,12 @@ func GetPanelsByGuild(guildId uint64, ch chan []Panel) {
ch <- panels
}
+func GetPanel(messageId uint64, ch chan Panel) {
+ var row Panel
+ database.Database.Where(Panel{MessageId: messageId}).Take(&row)
+ ch <- row
+}
+
func DeletePanel(msgId uint64) {
database.Database.Where(Panel{MessageId: msgId}).Delete(Panel{})
}
diff --git a/database/table/pingeveryone.go b/database/table/pingeveryone.go
index 02c788e..b3c2003 100644
--- a/database/table/pingeveryone.go
+++ b/database/table/pingeveryone.go
@@ -29,9 +29,8 @@ func UpdatePingEveryone(guildId uint64, pingEveryone bool) {
//database.Database.Where(&PingEveryone{GuildId: guildId}).Assign(&updated).FirstOrCreate(&PingEveryone{})
}
-func GetPingEveryone(guildId uint64) bool {
+func GetPingEveryone(guildId uint64, ch chan bool) {
pingEveryone := PingEveryone{PingEveryone: true}
database.Database.Where(&PingEveryone{GuildId: guildId}).First(&pingEveryone)
-
- return pingEveryone.PingEveryone
+ ch <- pingEveryone.PingEveryone
}
diff --git a/database/table/prefix.go b/database/table/prefix.go
index cd76ee6..f0325d0 100644
--- a/database/table/prefix.go
+++ b/database/table/prefix.go
@@ -17,9 +17,8 @@ func UpdatePrefix(guildId uint64, prefix string) {
database.Database.Where(&Prefix{GuildId: guildId}).Assign(&Prefix{Prefix: prefix}).FirstOrCreate(&Prefix{})
}
-func GetPrefix(guildId uint64) string {
+func GetPrefix(guildId uint64, ch chan string) {
prefix := Prefix{Prefix: "t!"}
database.Database.Where(&Prefix{GuildId: guildId}).First(&prefix)
-
- return prefix.Prefix
+ ch <- prefix.Prefix
}
diff --git a/database/table/ticketlimit.go b/database/table/ticketlimit.go
index f463e37..874a47a 100644
--- a/database/table/ticketlimit.go
+++ b/database/table/ticketlimit.go
@@ -17,9 +17,8 @@ func UpdateTicketLimit(guildId uint64, limit int) {
database.Database.Where(&TicketLimit{GuildId: guildId}).Assign(&TicketLimit{Limit: limit}).FirstOrCreate(&TicketLimit{})
}
-func GetTicketLimit(guildId uint64) int {
+func GetTicketLimit(guildId uint64, ch chan int) {
limit := TicketLimit{Limit: 5}
database.Database.Where(&TicketLimit{GuildId: guildId}).First(&limit)
-
- return limit.Limit
+ ch <- limit.Limit
}
diff --git a/database/table/welcomemessage.go b/database/table/welcomemessage.go
index f932860..6b4187d 100644
--- a/database/table/welcomemessage.go
+++ b/database/table/welcomemessage.go
@@ -17,9 +17,8 @@ func UpdateWelcomeMessage(guildId uint64, message string) {
database.Database.Where(&WelcomeMessage{GuildId: guildId}).Assign(&WelcomeMessage{Message: message}).FirstOrCreate(&WelcomeMessage{})
}
-func GetWelcomeMessage(guildId uint64) string {
+func GetWelcomeMessage(guildId uint64, ch chan string) {
message := WelcomeMessage{Message: "No message specified"}
database.Database.Where(&WelcomeMessage{GuildId: guildId}).First(&message)
-
- return message.Message
+ ch <- message.Message
}
diff --git a/go.mod b/go.mod
index 7856fe2..f5d787a 100644
--- a/go.mod
+++ b/go.mod
@@ -7,6 +7,7 @@ require (
github.com/TicketsBot/archiverclient v0.0.0-20200420161043-3532ff9ea943
github.com/apex/log v1.1.2
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
+ github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gin-contrib/multitemplate v0.0.0-20200226145339-3e397ee01bc6
github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2
github.com/gin-gonic/contrib v0.0.0-20191209060500-d6e26eeaa607
@@ -21,5 +22,5 @@ require (
github.com/pkg/errors v0.9.1
github.com/robfig/go-cache v0.0.0-20130306151617-9fc39e0dbf62
github.com/rxdn/gdl v0.0.0-20200417164852-76b2d3c847c1
- golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a // indirect
+ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
)
diff --git a/public/static/css/discordmock.css b/public/static/css/discordmock.css
index c89b02a..b737a37 100644
--- a/public/static/css/discordmock.css
+++ b/public/static/css/discordmock.css
@@ -26,7 +26,7 @@
align-items: center;
}
-.channel-name {
+#channel-name {
color: white;
padding-left: 20px;
}
@@ -56,12 +56,6 @@
min-height: 100%;
}
-.form-control:focus {
- border-color: #2e3136 !important;
- background-color: #2e3136 !important;
- color: white !important;
-}
-
.message-input:focus {
border-color: #2e3136 !important;
background-color: #2e3136 !important;
diff --git a/public/static/css/style.css b/public/static/css/style.css
index 7526190..cd06d91 100644
--- a/public/static/css/style.css
+++ b/public/static/css/style.css
@@ -3,10 +3,19 @@ body {
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
+ background-color: #121212 !important;
+ color: white;
+}
+
+.card {
+ background-color: #272727 !important;
+}
+
+.card-title {
+ color: white;
}
.sidebar {
- background: url("/assets/img/sidebar-2.jpg");
background-size: cover;
overflow-x: hidden !important;
}
@@ -37,3 +46,68 @@ body {
margin: 0 !important;
padding: 0 !important;
}
+
+.form-control, .input-group-text, .form-check-input {
+ border-color: #2e3136 !important;
+ background-color: #2e3136 !important;
+ color: white !important;
+}
+
+.server {
+ color: white;
+}
+
+#bg-dark {
+ background-color: #272727 !important;
+}
+
+#sidebar-gradient:after {
+ background: #272727 !important;
+}
+
+.simple-text {
+ padding: 0 !important;
+}
+
+.white {
+ color: white !important;
+}
+
+.icon {
+ color: white;
+ float: left;
+ vertical-align: middle;
+ padding-right: 5px;
+}
+
+.avatar {
+ width: 32px;
+ height: 32px;
+ display: block;
+ background-size: cover;
+ border-radius: 50%;
+ margin-left: 5px;
+ float: right;
+ vertical-align: middle;
+ bottom: 100%;
+ top: 0;
+}
+
+.toast-header {
+ background-color: #272727 !important;
+ color: white !important;
+}
+
+.toast-body {
+ background-color: #2e3136 !important;
+ color: white !important;
+}
+
+.toast {
+ border-radius: .25rem !important;
+ border-color: #272727 !important;
+}
+
+#premium-ad {
+ display: none;
+}
diff --git a/public/templates/includes/head.tmpl b/public/templates/includes/head.tmpl
index 1704ec9..edb190d 100644
--- a/public/templates/includes/head.tmpl
+++ b/public/templates/includes/head.tmpl
@@ -14,16 +14,55 @@
+
+
+
+
+
+
@@ -51,6 +90,49 @@
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-161945537-1', 'auto');
ga('send', 'pageview');
-
-
+
+
+
+
+
+
{{end}}
\ No newline at end of file
diff --git a/public/templates/includes/navbar.tmpl b/public/templates/includes/navbar.tmpl
index d6022a8..b58c103 100644
--- a/public/templates/includes/navbar.tmpl
+++ b/public/templates/includes/navbar.tmpl
@@ -1,23 +1,32 @@
{{define "navbar"}}
-