From bc3fa195c1d12af331ffccdd9390e2bb777e606f Mon Sep 17 00:00:00 2001 From: Dot-Rar Date: Sat, 25 May 2019 23:25:00 +0100 Subject: [PATCH] latest --- app/http/endpoints/index.go | 22 --- app/http/endpoints/login.go | 20 --- app/http/endpoints/manage/logs.go | 67 +++++++ app/http/endpoints/manage/settings.go | 169 ++++++++++++++++++ app/http/endpoints/{ => root}/callback.go | 38 +++- app/http/endpoints/root/index.go | 61 +++++++ app/http/endpoints/root/login.go | 25 +++ app/http/endpoints/root/logout.go | 17 ++ app/http/layouts.go | 25 --- app/http/server.go | 29 +-- app/http/template/layout.go | 22 --- app/http/template/template.go | 61 +++++-- app/http/templates.go | 28 --- config.toml.example | 3 + config/config.go | 4 +- database/database.go | 4 +- database/table/channelcategory.go | 25 +++ database/table/guilds.go | 37 ++++ database/table/permissions.go | 32 ++++ database/table/prefix.go | 25 +++ database/table/ticketarchive.go | 44 +++++ database/table/ticketlimit.go | 25 +++ database/table/welcomemessage.go | 25 +++ public/templates/layouts/main.mustache | 2 +- public/templates/views/index.mustache | 13 +- public/templates/views/logs.mustache | 11 +- public/templates/views/settings.mustache | 28 +-- utils/discord/endpoints.go | 14 +- utils/discord/endpoints/guild/getGuild.go | 4 +- .../endpoints/guild/getGuildChannels.go | 14 ++ utils/discord/endpoints/user/currentUser.go | 1 + .../endpoints/user/currentUserGuilds.go | 1 + utils/discord/objects/guild.go | 12 ++ utils/httputils.go | 7 + utils/sessionutils.go | 5 + utils/sliceutils.go | 34 ++++ utils/stringutils.go | 10 +- 37 files changed, 785 insertions(+), 179 deletions(-) delete mode 100644 app/http/endpoints/index.go delete mode 100644 app/http/endpoints/login.go create mode 100644 app/http/endpoints/manage/logs.go create mode 100644 app/http/endpoints/manage/settings.go rename app/http/endpoints/{ => root}/callback.go (64%) create mode 100644 app/http/endpoints/root/index.go create mode 100644 app/http/endpoints/root/login.go create mode 100644 app/http/endpoints/root/logout.go delete mode 100644 app/http/layouts.go delete mode 100644 app/http/template/layout.go delete mode 100644 app/http/templates.go create mode 100644 database/table/channelcategory.go create mode 100644 database/table/guilds.go create mode 100644 database/table/permissions.go create mode 100644 database/table/prefix.go create mode 100644 database/table/ticketarchive.go create mode 100644 database/table/ticketlimit.go create mode 100644 database/table/welcomemessage.go create mode 100644 utils/discord/endpoints/guild/getGuildChannels.go create mode 100644 utils/httputils.go create mode 100644 utils/sliceutils.go diff --git a/app/http/endpoints/index.go b/app/http/endpoints/index.go deleted file mode 100644 index 3899d21..0000000 --- a/app/http/endpoints/index.go +++ /dev/null @@ -1,22 +0,0 @@ -package endpoints - -import ( - "github.com/TicketsBot/GoPanel/utils" - "github.com/gin-gonic/contrib/sessions" - "github.com/gin-gonic/gin" -) - -func IndexHandler(ctx *gin.Context) { - store := sessions.Default(ctx) - if store == nil { - return - } - defer store.Save() - - if utils.IsLoggedIn(store) { - - } else { - ctx.Redirect(302, "/login") - } -} - diff --git a/app/http/endpoints/login.go b/app/http/endpoints/login.go deleted file mode 100644 index d10e2fb..0000000 --- a/app/http/endpoints/login.go +++ /dev/null @@ -1,20 +0,0 @@ -package endpoints - -import ( - "fmt" - "github.com/TicketsBot/GoPanel/config" - "github.com/gin-gonic/contrib/sessions" - "github.com/gin-gonic/gin" - "net/url" -) - -func LoginHandler(ctx *gin.Context) { - store := sessions.Default(ctx) - if store == nil { - return - } - defer store.Save() - - redirect := url.QueryEscape(fmt.Sprintf("%s/callback", config.Conf.Server.BaseUrl)) - ctx.Redirect(302, fmt.Sprintf("https://discordapp.com/oauth2/authorize?response_type=code&redirect_uri=%s&scope=identify+guilds&client_id=%d", redirect, config.Conf.Oauth.Id)) -} diff --git a/app/http/endpoints/manage/logs.go b/app/http/endpoints/manage/logs.go new file mode 100644 index 0000000..cab671b --- /dev/null +++ b/app/http/endpoints/manage/logs.go @@ -0,0 +1,67 @@ +package manage + +import ( + "github.com/TicketsBot/GoPanel/app/http/template" + "github.com/TicketsBot/GoPanel/config" + "github.com/TicketsBot/GoPanel/database/table" + "github.com/TicketsBot/GoPanel/utils" + "github.com/TicketsBot/GoPanel/utils/discord/objects" + "github.com/gin-gonic/contrib/sessions" + "github.com/gin-gonic/gin" + "strconv" +) + +func LogsHandler(ctx *gin.Context) { + store := sessions.Default(ctx) + if store == nil { + return + } + defer store.Save() + + if utils.IsLoggedIn(store) { + userIdStr := store.Get("userid").(string) + userId, err := utils.GetUserId(store); if err != nil { + ctx.String(500, err.Error()) + return + } + + // Verify the guild exists + guildIdStr := ctx.Param("id") + guildId, err := strconv.ParseInt(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 + var guild objects.Guild + for _, g := range table.GetGuilds(userIdStr) { + if g.Id == guildIdStr { + guild = g + break + } + } + + // Verify the user has permissions to be here + if !guild.Owner && !table.IsAdmin(guildId, userId) { + ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page + return + } + + + + utils.Respond(ctx, template.TemplateSettings.Render(map[string]interface{}{ + "name": store.Get("name").(string), + "guildId": guildIdStr, + })) + } else { + ctx.Redirect(302, "/login") + } +} diff --git a/app/http/endpoints/manage/settings.go b/app/http/endpoints/manage/settings.go new file mode 100644 index 0000000..53deccd --- /dev/null +++ b/app/http/endpoints/manage/settings.go @@ -0,0 +1,169 @@ +package manage + +import ( + "encoding/base64" + "encoding/json" + "github.com/TicketsBot/GoPanel/app/http/template" + "github.com/TicketsBot/GoPanel/config" + "github.com/TicketsBot/GoPanel/database/table" + "github.com/TicketsBot/GoPanel/utils" + guildendpoint "github.com/TicketsBot/GoPanel/utils/discord/endpoints/guild" + "github.com/TicketsBot/GoPanel/utils/discord/objects" + "github.com/apex/log" + "github.com/gin-gonic/contrib/sessions" + "github.com/gin-gonic/gin" + "strconv" +) + +func SettingsHandler(ctx *gin.Context) { + store := sessions.Default(ctx) + if store == nil { + return + } + defer store.Save() + + if utils.IsLoggedIn(store) { + userIdStr := store.Get("userid").(string) + userId, err := utils.GetUserId(store); if err != nil { + ctx.String(500, err.Error()) + return + } + + // Verify the guild exists + guildIdStr := ctx.Param("id") + guildId, err := strconv.ParseInt(guildIdStr, 10, 64); if err != nil { + ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page + return + } + + // Get object for selected guild + var guild objects.Guild + for _, g := range table.GetGuilds(userIdStr) { + if g.Id == guildIdStr { + guild = g + break + } + } + + // Verify the user has permissions to be here + if !guild.Owner && !table.IsAdmin(guildId, userId) { + ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page + return + } + + // Get prefix + prefix := ctx.Query("prefix") + if prefix == "" { + prefix = table.GetPrefix(guildId) + } else { + table.UpdatePrefix(guildId, prefix) + } + + // Get welcome message + welcomeMessage := ctx.Query("welcomeMessage") + if welcomeMessage == "" { + welcomeMessage = table.GetWelcomeMessage(guildId) + } else { + table.UpdateWelcomeMessage(guildId, welcomeMessage) + } + + // Get ticket limit + limitStr := ctx.Query("ticketlimit") + limit := 5 + + // 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 + if limitStr == "" || !utils.IsInt(limitStr) { + limit = table.GetTicketLimit(guildId) + } else { + table.UpdateTicketLimit(guildId, limit) + } + + // Get a list of actual category IDs + categories := guild.GetCategories() + + // /users/@me/guilds doesn't return channels, so we have to get them for the specific guild + if len(categories) == 0 { + var channels []objects.Channel + endpoint := guildendpoint.GetGuildChannels(int(guildId)) + err = endpoint.Request(store, nil, nil, &channels) + + if err != nil { + // Not in guild + } else { + guild.Channels = channels + categories = guild.GetCategories() + + // Update cache of categories now that we have them + guilds := table.GetGuilds(userIdStr) + + // Get index of guild + index := -1 + for i, g := range guilds { + if g.Id == guild.Id { + index = i + break + } + } + + if index != -1 { + // Delete + guilds = append(guilds[:index], guilds[index+1:]...) + + // Insert updated guild + guilds = utils.Insert(guilds, index, guild) + + marshalled, err := json.Marshal(guilds); if err != nil { + log.Error(err.Error()) + } else { + table.UpdateGuilds(userIdStr, base64.StdEncoding.EncodeToString(marshalled)) + } + } + } + } + + var categoryIds []string + for _, c := range categories { + categoryIds = append(categoryIds, c.Id) + } + + categoryStr := ctx.Query("category") + var category int64 + + // Verify category ID is an int and set default category ID + if utils.IsInt(categoryStr) { + category, _ = strconv.ParseInt(categoryStr, 10, 64) + } + + // Update category, or get current category if user input is invalid + if categoryStr == "" || !utils.IsInt(categoryStr) || !utils.Contains(categoryIds, categoryStr) { + category = table.GetChannelCategory(guildId) + } else { + table.UpdateChannelCategory(guildId, category) + } + + var formattedCategories []map[string]interface{} + for _, c := range categories { + formattedCategories = append(formattedCategories, map[string]interface{}{ + "categoryid": c.Id, + "categoryname": c.Name, + "active": c.Id == strconv.Itoa(int(category)), + }) + } + + utils.Respond(ctx, template.TemplateSettings.Render(map[string]interface{}{ + "name": store.Get("name").(string), + "guildId": guildIdStr, + "prefix": prefix, + "welcomeMessage": welcomeMessage, + "ticketLimit": limit, + "categories": formattedCategories, + })) + } else { + ctx.Redirect(302, "/login") + } +} diff --git a/app/http/endpoints/callback.go b/app/http/endpoints/root/callback.go similarity index 64% rename from app/http/endpoints/callback.go rename to app/http/endpoints/root/callback.go index aa74c44..1e036e3 100644 --- a/app/http/endpoints/callback.go +++ b/app/http/endpoints/root/callback.go @@ -1,9 +1,15 @@ -package endpoints +package root import ( + "encoding/base64" + "encoding/json" + "github.com/TicketsBot/GoPanel/config" + "github.com/TicketsBot/GoPanel/database/table" + "github.com/TicketsBot/GoPanel/utils" "github.com/TicketsBot/GoPanel/utils/discord" "github.com/TicketsBot/GoPanel/utils/discord/endpoints/user" "github.com/TicketsBot/GoPanel/utils/discord/objects" + "github.com/apex/log" "github.com/gin-gonic/contrib/sessions" "github.com/gin-gonic/gin" "time" @@ -35,6 +41,11 @@ func CallbackHandler(ctx *gin.Context) { } defer store.Save() + if utils.IsLoggedIn(store) { + ctx.Redirect(302, config.Conf.Server.BaseUrl) + return + } + code := ctx.DefaultQuery("code", "") if code == "" { ctx.String(400, "Discord provided an invalid Oauth2 code") @@ -58,10 +69,25 @@ func CallbackHandler(ctx *gin.Context) { store.Set("userid", currentUser.Id) store.Set("name", currentUser.Username) - - // Get Guilds - var currentUserGuilds []objects.Guild - err = user.CurrentUserGuilds.Request(store, nil, nil, ¤tUserGuilds); if err != nil { - ctx.String(500, err.Error()) + if err = store.Save(); err != nil { + log.Error(err.Error()) } + + ctx.Redirect(302,config.Conf.Server.BaseUrl) + + // Cache guilds because Discord takes like 2 whole seconds to return then + go func() { + var guilds []objects.Guild + err = user.CurrentUserGuilds.Request(store, nil, nil, &guilds); if err != nil { + log.Error(err.Error()) + return + } + + marshalled, err := json.Marshal(guilds); if err != nil { + log.Error(err.Error()) + return + } + + table.UpdateGuilds(currentUser.Id, base64.StdEncoding.EncodeToString(marshalled)) + }() } diff --git a/app/http/endpoints/root/index.go b/app/http/endpoints/root/index.go new file mode 100644 index 0000000..8f07b6b --- /dev/null +++ b/app/http/endpoints/root/index.go @@ -0,0 +1,61 @@ +package root + +import ( + "github.com/TicketsBot/GoPanel/app/http/template" + "github.com/TicketsBot/GoPanel/config" + "github.com/TicketsBot/GoPanel/database/table" + "github.com/TicketsBot/GoPanel/utils" + "github.com/TicketsBot/GoPanel/utils/discord/objects" + "github.com/gin-gonic/contrib/sessions" + "github.com/gin-gonic/gin" + "strconv" +) + +func IndexHandler(ctx *gin.Context) { + store := sessions.Default(ctx) + if store == nil { + return + } + defer store.Save() + + if utils.IsLoggedIn(store) { + userIdStr := store.Get("userid").(string) + userId, err := utils.GetUserId(store); if err != nil { + ctx.String(500, err.Error()) + return + } + + adminGuilds := make([]objects.Guild, 0) + adminGuildIds := table.GetAdminGuilds(userId) + for _, guild := range table.GetGuilds(userIdStr) { + guildId, err := strconv.ParseInt(guild.Id, 10, 64); if err != nil { + ctx.String(500, err.Error()) + return + } + + if guild.Owner || utils.Contains(adminGuildIds, guildId) { + adminGuilds = append(adminGuilds, guild) + } + } + + var servers []map[string]string + for _, server := range adminGuilds { + element := map[string]string{ + "serverid": server.Id, + "servername": server.Name, + } + + servers = append(servers, element) + } + + utils.Respond(ctx, template.TemplateIndex.Render(map[string]interface{}{ + "name": store.Get("name").(string), + "baseurl": config.Conf.Server.BaseUrl, + "servers": servers, + "empty": len(servers) == 0, + })) + } else { + ctx.Redirect(302, "/login") + } +} + diff --git a/app/http/endpoints/root/login.go b/app/http/endpoints/root/login.go new file mode 100644 index 0000000..24f50b9 --- /dev/null +++ b/app/http/endpoints/root/login.go @@ -0,0 +1,25 @@ +package root + +import ( + "fmt" + "github.com/TicketsBot/GoPanel/config" + "github.com/TicketsBot/GoPanel/utils" + "github.com/gin-gonic/contrib/sessions" + "github.com/gin-gonic/gin" + "net/url" +) + +func LoginHandler(ctx *gin.Context) { + store := sessions.Default(ctx) + if store == nil { + return + } + defer store.Save() + + if utils.IsLoggedIn(store) { + ctx.Redirect(302, config.Conf.Server.BaseUrl) + } else { + redirect := url.QueryEscape(config.Conf.Oauth.RedirectUri) + ctx.Redirect(302, fmt.Sprintf("https://discordapp.com/oauth2/authorize?response_type=code&redirect_uri=%s&scope=identify+guilds&client_id=%d", redirect, config.Conf.Oauth.Id)) + } +} diff --git a/app/http/endpoints/root/logout.go b/app/http/endpoints/root/logout.go new file mode 100644 index 0000000..df5da25 --- /dev/null +++ b/app/http/endpoints/root/logout.go @@ -0,0 +1,17 @@ +package root + +import ( + "github.com/gin-gonic/contrib/sessions" + "github.com/gin-gonic/gin" +) + +func LogoutHandler(ctx *gin.Context) { + store := sessions.Default(ctx) + if store == nil { + return + } + defer store.Save() + + store.Clear() + ctx.Redirect(302, "https://ticketsbot.net") +} diff --git a/app/http/layouts.go b/app/http/layouts.go deleted file mode 100644 index f866501..0000000 --- a/app/http/layouts.go +++ /dev/null @@ -1,25 +0,0 @@ -package http - -import ( - "github.com/TicketsBot/GoPanel/app/http/template" -) - -type Layout string - -const( - Main Layout = "main" -) - -var( - layouts = map[string]template.Layout{ - Main.ToString(): template.LoadLayout(Main.ToString()), - } -) - -func (l Layout) ToString() string { - return string(l) -} - -func (l Layout) GetInstance() template.Layout { - return layouts[l.ToString()] -} diff --git a/app/http/server.go b/app/http/server.go index 04822d2..c3564f0 100644 --- a/app/http/server.go +++ b/app/http/server.go @@ -2,7 +2,9 @@ package http import ( "fmt" - "github.com/TicketsBot/GoPanel/app/http/endpoints" + "github.com/TicketsBot/GoPanel/app/http/endpoints/manage" + "github.com/TicketsBot/GoPanel/app/http/endpoints/root" + "github.com/TicketsBot/GoPanel/app/http/template" "github.com/TicketsBot/GoPanel/config" "github.com/gin-contrib/static" "github.com/gin-gonic/contrib/sessions" @@ -13,6 +15,10 @@ import ( func StartServer() { log.Println("Starting HTTP server") + // Compile templates + template.LoadLayouts() + template.LoadTemplates() + router := gin.Default() // Sessions @@ -30,19 +36,22 @@ func StartServer() { router.Use(static.Serve("/assets/", static.LocalFile("./public/static", false))) // Root - router.GET("/", func(c *gin.Context) { - endpoints.IndexHandler(c) - }) + router.GET("/", root.IndexHandler) // /login - router.GET("/login", func(c *gin.Context) { - endpoints.LoginHandler(c) - }) + router.GET("/login", root.LoginHandler) // /callback - router.GET("/callback", func(c *gin.Context) { - endpoints.CallbackHandler(c) - }) + router.GET("/callback", root.CallbackHandler) + + // /logout + router.GET("/logout", root.LogoutHandler) + + // /manage/:id/settings + router.GET("/manage/:id/settings", manage.SettingsHandler) + + // /manage/:id/logs/page/:page + router.GET("/manage/:id/logs/page/:page", manage.LogsHandler) if err := router.Run(config.Conf.Server.Host); err != nil { panic(err) diff --git a/app/http/template/layout.go b/app/http/template/layout.go deleted file mode 100644 index 10ea26f..0000000 --- a/app/http/template/layout.go +++ /dev/null @@ -1,22 +0,0 @@ -package template - -import ( - "fmt" - "github.com/TicketsBot/GoPanel/utils" -) - -type Layout struct { - Name string - Content string -} - -func LoadLayout(name string) Layout { - content, err := utils.ReadFile(fmt.Sprintf("./public/templates/layouts/%s.mustache", name)); if err != nil { - panic(err) - } - - return Layout{ - Name: name, - Content: content, - } -} diff --git a/app/http/template/template.go b/app/http/template/template.go index c994c2c..41364db 100644 --- a/app/http/template/template.go +++ b/app/http/template/template.go @@ -2,26 +2,63 @@ package template import ( "fmt" - "github.com/TicketsBot/GoPanel/utils" "github.com/hoisie/mustache" ) -type Template struct { - Layout Layout - Content string +type Layout struct { + compiled *mustache.Template } -func LoadTemplate(layout Layout, name string) Template { - content, err := utils.ReadFile(fmt.Sprintf("./public/templates/views/%s.mustache", name)); if err != nil { +type Template struct { + compiled *mustache.Template + Layout Layout +} + +var( + LayoutMain Layout + + TemplateIndex Template + TemplateLogs Template + TemplateSettings Template +) + +func (t *Template) Render(context ...interface{}) string { + return t.compiled.RenderInLayout(t.Layout.compiled, context[0]) +} + +func LoadLayouts() { + LayoutMain = Layout{ + compiled: loadLayout("main"), + } +} + +func LoadTemplates() { + TemplateIndex = Template{ + compiled: loadTemplate("index"), + Layout: LayoutMain, + } + TemplateLogs = Template{ + compiled: loadTemplate("logs"), + Layout: LayoutMain, + } + TemplateSettings = Template{ + compiled: loadTemplate("settings"), + Layout: LayoutMain, + } +} + +func loadLayout(name string) *mustache.Template { + tmpl, err := mustache.ParseFile(fmt.Sprintf("./public/templates/layouts/%s.mustache", name)); if err != nil { panic(err) } - return Template{ - Layout: layout, - Content: content, - } + return tmpl } -func (t *Template) Render(context ...interface{}) string { - return mustache.RenderInLayout(t.Content, t.Layout.Content, context) +func loadTemplate(name string) *mustache.Template { + tmpl, err := mustache.ParseFile(fmt.Sprintf("./public/templates/views/%s.mustache", name)); if err != nil { + panic(err) + } + + return tmpl } diff --git a/app/http/templates.go b/app/http/templates.go deleted file mode 100644 index b817a41..0000000 --- a/app/http/templates.go +++ /dev/null @@ -1,28 +0,0 @@ -package http - -import "github.com/TicketsBot/GoPanel/app/http/template" - -type Template string - -const( - Index Template = "index" -) - -var( - templates = map[string]template.Template{ - Index.ToString(): template.LoadTemplate(Main.GetInstance(), Index.ToString()), - } -) - -func (t Template) ToString() string { - return string(t) -} - -func (t Template) GetInstance() template.Template { - return templates[t.ToString()] -} - -func (t Template) Render(context ...interface{}) string { - temp := t.GetInstance() - return temp.Render(context) -} diff --git a/config.toml.example b/config.toml.example index 0525770..e1a4012 100644 --- a/config.toml.example +++ b/config.toml.example @@ -21,6 +21,9 @@ password="ryan" database="tickets" threads=5 +[bot] +token="" + [redis] host="127.0.0.1" port=6379 diff --git a/config/config.go b/config/config.go index 3587882..159474b 100644 --- a/config/config.go +++ b/config/config.go @@ -18,6 +18,7 @@ type( Host string BaseUrl string MainSite string + CsrfKey string Ratelimit Ratelimit Session Session } @@ -47,8 +48,7 @@ type( } Bot struct { - Key string - HttpServer []string + Token string } Redis struct { diff --git a/database/database.go b/database/database.go index 4643896..82de6d4 100644 --- a/database/database.go +++ b/database/database.go @@ -8,7 +8,7 @@ import ( ) var( - database **gorm.DB + Database gorm.DB ) func ConnectToDatabase() { @@ -27,5 +27,5 @@ func ConnectToDatabase() { db.DB().SetMaxOpenConns(config.Conf.MariaDB.Threads) db.DB().SetMaxIdleConns(0) - database = &db + Database = *db } diff --git a/database/table/channelcategory.go b/database/table/channelcategory.go new file mode 100644 index 0000000..8152757 --- /dev/null +++ b/database/table/channelcategory.go @@ -0,0 +1,25 @@ +package table + +import ( + "github.com/TicketsBot/GoPanel/database" +) + +type ChannelCategory struct { + GuildId int64 `gorm:"column:GUILDID"` + Category int64 `gorm:"column:CATEGORYID"` +} + +func (ChannelCategory) TableName() string { + return "channelcategory" +} + +func UpdateChannelCategory(guildId int64, categoryId int64) { + database.Database.Where(&ChannelCategory{GuildId: guildId}).Assign(&ChannelCategory{Category: categoryId}).FirstOrCreate(&ChannelCategory{}) +} + +func GetChannelCategory(guildId int64) int64 { + var category ChannelCategory + database.Database.Where(&ChannelCategory{GuildId: guildId}).First(&category) + + return category.Category +} diff --git a/database/table/guilds.go b/database/table/guilds.go new file mode 100644 index 0000000..f92692b --- /dev/null +++ b/database/table/guilds.go @@ -0,0 +1,37 @@ +package table + +import ( + "encoding/base64" + "encoding/json" + "github.com/TicketsBot/GoPanel/database" + "github.com/TicketsBot/GoPanel/utils/discord/objects" +) + +type GuildCache struct { + UserId string `gorm:"column:USERID;type:varchar(20)"` // Apparently I made this a VARCHAR in the JS version + Guilds string `gorm:"column:guilds;type:mediumtext"` +} + +func (GuildCache) TableName() string { + return "guildscache" +} + +func UpdateGuilds(userId string, guilds string) { + var cache GuildCache + database.Database.Where(&GuildCache{UserId: userId}).Assign(&GuildCache{Guilds: guilds}).FirstOrCreate(&cache) +} + +func GetGuilds(userId string) []objects.Guild { + var cache GuildCache + database.Database.Where(&GuildCache{UserId: userId}).First(&cache) + decoded, err := base64.StdEncoding.DecodeString(cache.Guilds); if err != nil { + return make([]objects.Guild, 0) + } + + var guilds []objects.Guild + if err := json.Unmarshal(decoded, &guilds); err != nil { + return make([]objects.Guild, 0) + } + + return guilds +} diff --git a/database/table/permissions.go b/database/table/permissions.go new file mode 100644 index 0000000..f93db90 --- /dev/null +++ b/database/table/permissions.go @@ -0,0 +1,32 @@ +package table + +import "github.com/TicketsBot/GoPanel/database" + +type PermissionNode struct { + GuildId int64 `gorm:"column:GUILDID"` + UserId int64 `gorm:"column:USERID"` + IsSupport bool `gorm:"column:ISSUPPORT"` + IsAdmin bool `gorm:"column:ISADMIN"` +} + +func (PermissionNode) TableName() string { + return "permissions" +} + +func GetAdminGuilds(userId int64) []int64 { + var nodes []PermissionNode + database.Database.Where(&PermissionNode{UserId: userId}).Find(&nodes) + + ids := make([]int64, 0) + for _, node := range nodes { + ids = append(ids, node.GuildId) + } + + return ids +} + +func IsAdmin(guildId int64, userId int64) bool { + var node PermissionNode + database.Database.Where(&PermissionNode{GuildId: guildId, UserId: userId}).Take(&node) + return node.IsAdmin +} diff --git a/database/table/prefix.go b/database/table/prefix.go new file mode 100644 index 0000000..e603b61 --- /dev/null +++ b/database/table/prefix.go @@ -0,0 +1,25 @@ +package table + +import ( + "github.com/TicketsBot/GoPanel/database" +) + +type Prefix struct { + GuildId int64 `gorm:"column:GUILDID"` + Prefix string `gorm:"column:PREFIX;type:varchar(8)"` +} + +func (Prefix) TableName() string { + return "prefix" +} + +func UpdatePrefix(guildId int64, prefix string) { + database.Database.Where(&Prefix{GuildId: guildId}).Assign(&Prefix{Prefix: prefix}).FirstOrCreate(&Prefix{}) +} + +func GetPrefix(guildId int64) string { + prefix := Prefix{Prefix:"t!"} + database.Database.Where(&Prefix{GuildId: guildId}).First(&prefix) + + return prefix.Prefix +} diff --git a/database/table/ticketarchive.go b/database/table/ticketarchive.go new file mode 100644 index 0000000..5d7cd6b --- /dev/null +++ b/database/table/ticketarchive.go @@ -0,0 +1,44 @@ +package table + +import ( + "github.com/TicketsBot/GoPanel/database" +) + +type TicketArchive struct { + Uuid string `gorm:"column:UUID;type:varchar(36)"` + Guild int64 `gorm:"column:GUILDID"` + User int64 `gorm:"column:USERID"` + Username string `gorm:"column:USERNAME;type:varchar(32)"` + TicketId int `gorm:"column:TICKETID"` + CdnUrl string `gorm:"column:CDNURL;type:varchar(100)"` +} + +func (TicketArchive) TableName() string { + return "ticketarchive" +} + +func GetTicketArchives(guildId int64) []TicketArchive { + var archives []TicketArchive + database.Database.Where(&TicketArchive{Guild: guildId}).Find(&archives) + + return archives +} + +func GetFilteredTicketArchives(guildId int64, userId int64, username string, ticketId int) []TicketArchive { + var archives []TicketArchive + + query := database.Database.Where(&TicketArchive{Guild: guildId}) + if userId != 0 { + query = query.Where(&TicketArchive{User: userId}) + } + if username != "" { + query = query.Where(&TicketArchive{Username: username}) + } + if ticketId != 0 { + query = query.Where(&TicketArchive{TicketId: ticketId}) + } + + query.Find(&archives) + + return archives +} diff --git a/database/table/ticketlimit.go b/database/table/ticketlimit.go new file mode 100644 index 0000000..86cc5fe --- /dev/null +++ b/database/table/ticketlimit.go @@ -0,0 +1,25 @@ +package table + +import ( + "github.com/TicketsBot/GoPanel/database" +) + +type TicketLimit struct { + GuildId int64 `gorm:"column:GUILDID"` + Limit int `gorm:"column:TICKETLIMIT"` +} + +func (TicketLimit) TableName() string { + return "ticketlimit" +} + +func UpdateTicketLimit(guildId int64, limit int) { + database.Database.Where(&TicketLimit{GuildId: guildId}).Assign(&TicketLimit{Limit: limit}).FirstOrCreate(&TicketLimit{}) +} + +func GetTicketLimit(guildId int64) int { + limit := TicketLimit{Limit: 5} + database.Database.Where(&TicketLimit{GuildId: guildId}).First(&limit) + + return limit.Limit +} diff --git a/database/table/welcomemessage.go b/database/table/welcomemessage.go new file mode 100644 index 0000000..f0ce57a --- /dev/null +++ b/database/table/welcomemessage.go @@ -0,0 +1,25 @@ +package table + +import ( + "github.com/TicketsBot/GoPanel/database" +) + +type WelcomeMessage struct { + GuildId int64 `gorm:"column:GUILDID"` + Message string `gorm:"column:MESSAGE;type:text"` +} + +func (WelcomeMessage) TableName() string { + return "welcomemessages" +} + +func UpdateWelcomeMessage(guildId int64, message string) { + database.Database.Where(&WelcomeMessage{GuildId: guildId}).Assign(&WelcomeMessage{Message: message}).FirstOrCreate(&WelcomeMessage{}) +} + +func GetWelcomeMessage(guildId int64) string { + message := WelcomeMessage{Message:"No message specified"} + database.Database.Where(&WelcomeMessage{GuildId: guildId}).First(&message) + + return message.Message +} diff --git a/public/templates/layouts/main.mustache b/public/templates/layouts/main.mustache index 962a021..d5d0f37 100644 --- a/public/templates/layouts/main.mustache +++ b/public/templates/layouts/main.mustache @@ -37,6 +37,6 @@ - {{{body}}} + {{{content}}} diff --git a/public/templates/views/index.mustache b/public/templates/views/index.mustache index 553d994..509295b 100644 --- a/public/templates/views/index.mustache +++ b/public/templates/views/index.mustache @@ -1,23 +1,24 @@
- {{#if isNotAdmin}} + {{#empty}}

You are not the admin of any guilds that the bot is in. Click below to invite the bot:
Invite

- {{else}} + {{/empty}} + {{^empty}}

Select a server to manage below

- {{/if}} + {{/empty}}
diff --git a/public/templates/views/logs.mustache b/public/templates/views/logs.mustache index 0a56239..018c1c3 100644 --- a/public/templates/views/logs.mustache +++ b/public/templates/views/logs.mustache @@ -59,7 +59,7 @@ - {{#each logs}} + {{#logs}} {{{baseUrl}}} {{TICKETID}} @@ -67,7 +67,7 @@ {{USERID}} {{UUID}} - {{/each}} + {{/logs}} @@ -77,11 +77,12 @@

Pages

diff --git a/public/templates/views/settings.mustache b/public/templates/views/settings.mustache index 1894889..65f1b09 100644 --- a/public/templates/views/settings.mustache +++ b/public/templates/views/settings.mustache @@ -1,16 +1,15 @@
@@ -53,13 +52,14 @@

Channel Category

diff --git a/utils/discord/endpoints.go b/utils/discord/endpoints.go index a934711..34faaf0 100644 --- a/utils/discord/endpoints.go +++ b/utils/discord/endpoints.go @@ -3,6 +3,7 @@ package discord import ( "bytes" "encoding/json" + "github.com/TicketsBot/GoPanel/config" "github.com/gin-gonic/contrib/sessions" "github.com/pasztorpisti/qs" "github.com/pkg/errors" @@ -13,12 +14,16 @@ import ( type RequestType string type ContentType string +type AuthorizationType string const( GET RequestType = "GET" POST RequestType = "POST" PATCH RequestType = "PATCH" + BEARER AuthorizationType = "Bearer" + BOT AuthorizationType = "BOT" + ApplicationJson ContentType = "application/json" ApplicationFormUrlEncoded ContentType = "application/x-www-form-urlencoded" @@ -27,6 +32,7 @@ const( type Endpoint struct { RequestType RequestType + AuthorizationType AuthorizationType Endpoint string } @@ -65,7 +71,7 @@ func (e *Endpoint) Request(store sessions.Session, contentType *ContentType, bod if contentType != nil { req.Header.Set("Content-Type", string(*contentType)) } - req.Header.Set("User-Agent", "DiscordBot (https://github.com/TicketsBot/GoPanel 1.0.0)") + req.Header.Set("User-Agent", "DiscordBot (https://github.com/TicketsBot/GoPanel, 1.0.0)") // Auth accessToken := store.Get("access_token").(string) @@ -86,7 +92,11 @@ func (e *Endpoint) Request(store sessions.Session, contentType *ContentType, bod accessToken = res.AccessToken } - req.Header.Set("Authorization", "Bearer " + accessToken) + + switch e.AuthorizationType{ + case BEARER: req.Header.Set("Authorization", "Bearer " + accessToken) + case BOT: req.Header.Set("Authorization", "Bot " + config.Conf.Bot.Token) + } client := &http.Client{} client.Timeout = 3 * time.Second diff --git a/utils/discord/endpoints/guild/getGuild.go b/utils/discord/endpoints/guild/getGuild.go index bd2da3e..96a794f 100644 --- a/utils/discord/endpoints/guild/getGuild.go +++ b/utils/discord/endpoints/guild/getGuild.go @@ -3,11 +3,13 @@ package guild import ( "fmt" "github.com/TicketsBot/GoPanel/utils/discord" + "strconv" ) func GetGuild(id int) discord.Endpoint { return discord.Endpoint{ RequestType: discord.GET, - Endpoint: fmt.Sprintf("/guilds/%d", id), + AuthorizationType: discord.BOT, + Endpoint: fmt.Sprintf("/guilds/%s", strconv.Itoa(id)), } } diff --git a/utils/discord/endpoints/guild/getGuildChannels.go b/utils/discord/endpoints/guild/getGuildChannels.go new file mode 100644 index 0000000..185eecf --- /dev/null +++ b/utils/discord/endpoints/guild/getGuildChannels.go @@ -0,0 +1,14 @@ +package guild + +import ( + "fmt" + "github.com/TicketsBot/GoPanel/utils/discord" +) + +func GetGuildChannels(id int) discord.Endpoint { + return discord.Endpoint{ + RequestType: discord.GET, + AuthorizationType: discord.BOT, + Endpoint: fmt.Sprintf("/guilds/%d/channels", id), + } +} diff --git a/utils/discord/endpoints/user/currentUser.go b/utils/discord/endpoints/user/currentUser.go index 742cdea..0f3df2e 100644 --- a/utils/discord/endpoints/user/currentUser.go +++ b/utils/discord/endpoints/user/currentUser.go @@ -4,5 +4,6 @@ import "github.com/TicketsBot/GoPanel/utils/discord" var CurrentUser = discord.Endpoint{ RequestType: discord.GET, + AuthorizationType: discord.BEARER, Endpoint: "/users/@me", } diff --git a/utils/discord/endpoints/user/currentUserGuilds.go b/utils/discord/endpoints/user/currentUserGuilds.go index b92bf83..6227841 100644 --- a/utils/discord/endpoints/user/currentUserGuilds.go +++ b/utils/discord/endpoints/user/currentUserGuilds.go @@ -4,5 +4,6 @@ import "github.com/TicketsBot/GoPanel/utils/discord" var CurrentUserGuilds = discord.Endpoint{ RequestType: discord.GET, + AuthorizationType: discord.BEARER, Endpoint: "/users/@me/guilds", } diff --git a/utils/discord/objects/guild.go b/utils/discord/objects/guild.go index 77e635d..9521085 100644 --- a/utils/discord/objects/guild.go +++ b/utils/discord/objects/guild.go @@ -38,3 +38,15 @@ type Guild struct { Description string Banner string } + +func (g *Guild) GetCategories() []Channel { + var categories []Channel + + for _, channel := range g.Channels { + if channel.Type == 4 { + categories = append(categories, channel) + } + } + + return categories +} diff --git a/utils/httputils.go b/utils/httputils.go new file mode 100644 index 0000000..5f42a8a --- /dev/null +++ b/utils/httputils.go @@ -0,0 +1,7 @@ +package utils + +import "github.com/gin-gonic/gin" + +func Respond(ctx *gin.Context, s string) { + ctx.Data(200, "text/html; charset=utf-8", []byte(s)) +} diff --git a/utils/sessionutils.go b/utils/sessionutils.go index 8d36773..a3ecdb5 100644 --- a/utils/sessionutils.go +++ b/utils/sessionutils.go @@ -2,8 +2,13 @@ package utils import ( "github.com/gin-gonic/contrib/sessions" + "strconv" ) func IsLoggedIn(store sessions.Session) bool { return store.Get("access_token") != nil && store.Get("expiry") != nil && store.Get("refresh_token") != nil && store.Get("userid") != nil && store.Get("name") != nil } + +func GetUserId(store sessions.Session) (int64, error) { + return strconv.ParseInt(store.Get("userid").(string), 10, 64) +} diff --git a/utils/sliceutils.go b/utils/sliceutils.go new file mode 100644 index 0000000..01d6e44 --- /dev/null +++ b/utils/sliceutils.go @@ -0,0 +1,34 @@ +package utils + +import ( + "github.com/TicketsBot/GoPanel/utils/discord/objects" + "reflect" +) + +func Contains(s interface{}, elem interface{}) bool { + arrV := reflect.ValueOf(s) + + if arrV.Kind() == reflect.Slice { + for i := 0; i < arrV.Len(); i++ { + + // XXX - panics if slice element points to an unexported struct field + // see https://golang.org/pkg/reflect/#Value.Interface + if arrV.Index(i).Interface() == elem { + return true + } + } + } + + return false +} + +func Insert(slice []objects.Guild, index int , value objects.Guild) []objects.Guild { + // Grow the slice by one element. + slice = slice[0 : len(slice)+1] + // Use copy to move the upper part of the slice out of the way and open a hole. + copy(slice[index+1:], slice[index:]) + // Store the new value. + slice[index] = value + // Return the result. + return slice +} diff --git a/utils/stringutils.go b/utils/stringutils.go index 4abd468..f655c14 100644 --- a/utils/stringutils.go +++ b/utils/stringutils.go @@ -1,6 +1,9 @@ package utils -import "math/rand" +import ( + "math/rand" + "strconv" +) var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") @@ -11,3 +14,8 @@ func RandStringRunes(length int) string { } return string(b) } + +func IsInt(str string) bool { + _, err := strconv.ParseInt(str, 10, 64) + return err == nil +}