diff --git a/server/db/db.go b/server/db/db.go index 7100102..5cbe393 100644 --- a/server/db/db.go +++ b/server/db/db.go @@ -7,17 +7,21 @@ import ( _ "github.com/jinzhu/gorm/dialects/sqlite" ) -// Conn - database connection -var Conn *gorm.DB +var conn *gorm.DB // Init - initialize database -func Init() { +func Init(migrations ...interface{}) { var err error - Conn, err = gorm.Open("sqlite3", "data.db") + conn, err = gorm.Open("sqlite3", "data.db") if err != nil { panic("failed to connect database") } - - Conn.DB().SetMaxIdleConns(1) + conn.DB().SetMaxIdleConns(1) + conn.AutoMigrate(migrations...) +} + +// GetConn - get db connection +func GetConn() *gorm.DB { + return conn } diff --git a/server/logger/model.go b/server/logger/model.go index e4a950e..394e419 100644 --- a/server/logger/model.go +++ b/server/logger/model.go @@ -26,7 +26,7 @@ type Message struct { // Save - func (m *Message) Save() error { - return db.Conn.Save(m).Error + return db.GetConn().Save(m).Error } // Attachment - discord message attachment @@ -55,5 +55,5 @@ type User struct { // Save - func (u *User) Save() error { - return db.Conn.Save(u).Error + return db.GetConn().Save(u).Error } diff --git a/server/logger/operations.go b/server/logger/operations.go index 2958160..ccbb5a7 100644 --- a/server/logger/operations.go +++ b/server/logger/operations.go @@ -15,7 +15,7 @@ var linkedPostsCache map[string]int // GetMessages - returns all messages - must use paging func GetMessages(page int) ([]Message, error) { messages := []Message{} - err := db.Conn.Offset(page*100).Limit(100).Order("timestamp desc", true).Preload("User").Find(&messages).Error + err := db.GetConn().Offset(page*100).Limit(100).Order("timestamp desc", true).Preload("User").Find(&messages).Error return messages, err } @@ -28,7 +28,7 @@ func GetLinkedMessages() (map[string]int, error) { } result := []map[string]interface{}{} - rows, err := db.Conn.Table("messages"). + rows, err := db.GetConn().Table("messages"). Select("users.username, messages.content"). Joins("join users on messages.user_id = users.id"). Rows() diff --git a/server/main.go b/server/main.go index 9df0244..42f56d3 100644 --- a/server/main.go +++ b/server/main.go @@ -8,6 +8,7 @@ import ( "github.com/mgerb/go-discord-bot/server/db" "github.com/mgerb/go-discord-bot/server/logger" "github.com/mgerb/go-discord-bot/server/webserver" + "github.com/mgerb/go-discord-bot/server/webserver/model" log "github.com/sirupsen/logrus" ) @@ -20,8 +21,13 @@ func init() { config.Init() if config.Config.Logger { - db.Init() - db.Conn.AutoMigrate(&logger.Message{}, &logger.Attachment{}, &logger.User{}) + migrations := []interface{}{ + &logger.Message{}, + &logger.Attachment{}, + &logger.User{}, + &model.VideoArchive{}, + } + db.Init(migrations...) } } diff --git a/server/webserver/handlers/config.go b/server/webserver/handlers/config.go deleted file mode 100644 index 22964cd..0000000 --- a/server/webserver/handlers/config.go +++ /dev/null @@ -1,10 +0,0 @@ -package handlers - -import ( - "github.com/gin-gonic/gin" - "github.com/mgerb/go-discord-bot/server/config" -) - -func GetClientID(c *gin.Context) { - c.JSON(200, map[string]string{"id": config.Config.ClientID}) -} diff --git a/server/webserver/model/video_archive.go b/server/webserver/model/video_archive.go new file mode 100644 index 0000000..048358e --- /dev/null +++ b/server/webserver/model/video_archive.go @@ -0,0 +1,39 @@ +package model + +import ( + "time" + + "github.com/jinzhu/gorm" +) + +// VideoArchive - +type VideoArchive struct { + ID uint `gorm:"primary_key" json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + YoutubeID string `gorm:"column:youtube_id" json:"youtube_id"` + URL string `gorm:"column:url" json:"url"` + Title string `json:"title"` + Description string `json:"description"` + DatePublished time.Time `json:"date_published"` + Author string `json:"author"` + Duration int `json:"duration"` + UploadedBy string `json:"uploaded_by"` +} + +// VideoArchiveSave - +func VideoArchiveSave(conn *gorm.DB, v *VideoArchive) error { + return conn.Save(v).Error +} + +// VideoArchiveDelete - +func VideoArchiveDelete(conn *gorm.DB, id string) error { + return conn.Unscoped().Delete(VideoArchive{}, "id = ?", id).Error +} + +// VideoArchiveList - return list of all video archives +func VideoArchiveList(conn *gorm.DB) ([]VideoArchive, error) { + v := []VideoArchive{} + err := conn.Find(&v).Error + return v, err +} diff --git a/server/webserver/pubg/pubg.go b/server/webserver/pubg/pubg.go deleted file mode 100644 index 748c922..0000000 --- a/server/webserver/pubg/pubg.go +++ /dev/null @@ -1,67 +0,0 @@ -package pubg - -/** - * DEPRECATED - * I no longer have a use for this so I'm ripping it out - */ - -import ( - "sync" - "time" - - "github.com/gin-gonic/gin" - pubgClient "github.com/mgerb/go-pubg" - log "github.com/sirupsen/logrus" -) - -var ( - apiKey string - stats = map[string]*pubgClient.Player{} - mut = &sync.Mutex{} -) - -// Start - -func Start(key string, players []string) { - - apiKey = key - - log.Debug("Gathering pubg data...") - - go fetchStats(players) -} - -func fetchStats(players []string) { - - api, err := pubgClient.New(apiKey) - - if err != nil { - log.Fatal(err) - } - - // fetch new stats every 30 seconds - for { - - for _, player := range players { - newStats, err := api.GetPlayer(player) - - if err != nil { - log.Error(err) - continue - } - - mut.Lock() - stats[player] = newStats - mut.Unlock() - - time.Sleep(time.Second * 2) - } - - time.Sleep(time.Second * 30) - } - -} - -// Handler - returns the pubg stats -func Handler(c *gin.Context) { - c.JSON(200, stats) -} diff --git a/server/webserver/response/response.go b/server/webserver/response/response.go new file mode 100644 index 0000000..c7d6857 --- /dev/null +++ b/server/webserver/response/response.go @@ -0,0 +1,32 @@ +package response + +import ( + "net/http" + + "github.com/gin-gonic/gin" + log "github.com/sirupsen/logrus" +) + +// BadRequest - +func BadRequest(c *gin.Context, message string) { + dataMap := map[string]string{ + "data": message, + } + c.JSON(http.StatusBadRequest, dataMap) +} + +// TODO: don't return the error on production + +// InternalError - +func InternalError(c *gin.Context, err error) { + log.Error(err) + c.JSON(http.StatusInternalServerError, err) +} + +// Success - +func Success(c *gin.Context, data interface{}) { + dataMap := map[string]interface{}{ + "data": data, + } + c.JSON(http.StatusOK, dataMap) +} diff --git a/server/webserver/routes/config.go b/server/webserver/routes/config.go new file mode 100644 index 0000000..1f6ea9d --- /dev/null +++ b/server/webserver/routes/config.go @@ -0,0 +1,15 @@ +package routes + +import ( + "github.com/gin-gonic/gin" + "github.com/mgerb/go-discord-bot/server/config" +) + +// AddConfigRoutes - +func AddConfigRoutes(group *gin.RouterGroup) { + group.GET("/config/client_id", getClientIDHandler) +} + +func getClientIDHandler(c *gin.Context) { + c.JSON(200, map[string]string{"id": config.Config.ClientID}) +} diff --git a/server/webserver/handlers/downloader.go b/server/webserver/routes/downloader.go similarity index 88% rename from server/webserver/handlers/downloader.go rename to server/webserver/routes/downloader.go index 2e52aff..1e2f9cc 100644 --- a/server/webserver/handlers/downloader.go +++ b/server/webserver/routes/downloader.go @@ -1,4 +1,4 @@ -package handlers +package routes import ( "bytes" @@ -11,8 +11,12 @@ import ( log "github.com/sirupsen/logrus" ) -// Downloader - -func Downloader(c *gin.Context) { +// AddDownloaderRoutes - +func AddDownloaderRoutes(group *gin.RouterGroup) { + group.GET("/ytdownloader", downloaderHandler) +} + +func downloaderHandler(c *gin.Context) { url := c.Query("url") fileType := c.Query("fileType") diff --git a/server/webserver/handlers/logger.go b/server/webserver/routes/logger.go similarity index 59% rename from server/webserver/handlers/logger.go rename to server/webserver/routes/logger.go index f7fe4d3..14c5c93 100644 --- a/server/webserver/handlers/logger.go +++ b/server/webserver/routes/logger.go @@ -1,4 +1,4 @@ -package handlers +package routes import ( "strconv" @@ -7,8 +7,13 @@ import ( "github.com/mgerb/go-discord-bot/server/logger" ) -// GetMessages - get all messages -func GetMessages(c *gin.Context) { +// AddLoggerRoutes - +func AddLoggerRoutes(group *gin.RouterGroup) { + group.GET("/logger/messages", getMessagesHandler) + group.GET("/logger/linkedmessages", getLinkedMessagesHandler) +} + +func getMessagesHandler(c *gin.Context) { page, err := strconv.Atoi(c.Query("page")) if err != nil { @@ -25,8 +30,7 @@ func GetMessages(c *gin.Context) { c.JSON(200, messages) } -// GetLinkedMessages - -func GetLinkedMessages(c *gin.Context) { +func getLinkedMessagesHandler(c *gin.Context) { posts, err := logger.GetLinkedMessages() if err != nil { diff --git a/server/webserver/handlers/oauth.go b/server/webserver/routes/oauth.go similarity index 84% rename from server/webserver/handlers/oauth.go rename to server/webserver/routes/oauth.go index 1256265..c60c167 100644 --- a/server/webserver/handlers/oauth.go +++ b/server/webserver/routes/oauth.go @@ -1,4 +1,4 @@ -package handlers +package routes import ( "github.com/gin-gonic/gin" @@ -13,8 +13,12 @@ type oauthReq struct { Code string `json:"code"` } -// Oauth - -func Oauth(c *gin.Context) { +// AddOauthRoutes - +func AddOauthRoutes(group *gin.RouterGroup) { + group.POST("/oauth", oauthHandler) +} + +func oauthHandler(c *gin.Context) { var json oauthReq diff --git a/server/webserver/handlers/soundlist.go b/server/webserver/routes/soundlist.go similarity index 81% rename from server/webserver/handlers/soundlist.go rename to server/webserver/routes/soundlist.go index 5962912..728f39c 100644 --- a/server/webserver/handlers/soundlist.go +++ b/server/webserver/routes/soundlist.go @@ -1,4 +1,4 @@ -package handlers +package routes import ( "io/ioutil" @@ -17,8 +17,13 @@ type sound struct { Extension string `json:"extension"` } -// SoundList - -func SoundList(c *gin.Context) { +// AddSoundListRoutes - +func AddSoundListRoutes(group *gin.RouterGroup) { + group.GET("/soundlist", soundListHandler) + group.GET("/cliplist", clipListHandler) +} + +func soundListHandler(c *gin.Context) { soundList, err := readSoundsDir(config.Config.SoundsPath) @@ -31,8 +36,7 @@ func SoundList(c *gin.Context) { c.JSON(200, soundList) } -// ClipList - -func ClipList(c *gin.Context) { +func clipListHandler(c *gin.Context) { clipList, err := readSoundsDir(config.Config.ClipsPath) diff --git a/server/webserver/handlers/upload.go b/server/webserver/routes/upload.go similarity index 81% rename from server/webserver/handlers/upload.go rename to server/webserver/routes/upload.go index 685f4e1..5edc972 100644 --- a/server/webserver/handlers/upload.go +++ b/server/webserver/routes/upload.go @@ -1,4 +1,4 @@ -package handlers +package routes import ( "os" @@ -8,11 +8,16 @@ import ( "github.com/gin-gonic/gin" "github.com/mgerb/go-discord-bot/server/config" + "github.com/mgerb/go-discord-bot/server/webserver/middleware" log "github.com/sirupsen/logrus" ) -// FileUpload - -func FileUpload(c *gin.Context) { +// AddUploadRoutes - add file upload routes +func AddUploadRoutes(group *gin.RouterGroup) { + group.POST("/upload", middleware.AuthorizedJWT(), fileUploadHandler) +} + +func fileUploadHandler(c *gin.Context) { // originalClaims, _ := c.Get("claims") // claims, _ := originalClaims.(*middleware.CustomClaims) diff --git a/server/webserver/routes/video_archive.go b/server/webserver/routes/video_archive.go new file mode 100644 index 0000000..401ba8e --- /dev/null +++ b/server/webserver/routes/video_archive.go @@ -0,0 +1,104 @@ +package routes + +import ( + "errors" + + "github.com/gin-gonic/gin" + "github.com/mgerb/go-discord-bot/server/db" + "github.com/mgerb/go-discord-bot/server/webserver/middleware" + "github.com/mgerb/go-discord-bot/server/webserver/model" + "github.com/mgerb/go-discord-bot/server/webserver/response" + "github.com/rylio/ytdl" +) + +// AddVideoArchiveRoutes - +func AddVideoArchiveRoutes(group *gin.RouterGroup) { + group.GET("/video-archives", listVideoArchivesHandler) + + authGroup := group.Group("", middleware.AuthorizedJWT()) + authGroup.POST("/video-archives", middleware.AuthPermissions(middleware.PermMod), postVideoArchivesHandler) + authGroup.DELETE("/video-archives/:id", middleware.AuthPermissions(middleware.PermAdmin), deleteVideoArchivesHandler) +} + +func listVideoArchivesHandler(c *gin.Context) { + archives, err := model.VideoArchiveList(db.GetConn()) + + if err != nil { + response.InternalError(c, err) + return + } + + response.Success(c, archives) +} + +func deleteVideoArchivesHandler(c *gin.Context) { + id := c.Param("id") + + if id == "" { + response.BadRequest(c, "Invalid ID") + return + } + + err := model.VideoArchiveDelete(db.GetConn(), id) + + if err != nil { + response.InternalError(c, err) + return + } + + response.Success(c, "deleted") +} + +func postVideoArchivesHandler(c *gin.Context) { + params := struct { + URL string `json:"url"` + }{} + + c.BindJSON(¶ms) + + if params.URL == "" { + response.BadRequest(c, "URL Required") + return + } + + cl, _ := c.Get("claims") + claims, ok := cl.(*middleware.CustomClaims) + + if !ok { + response.InternalError(c, errors.New("Claims error")) + return + } + + info, err := ytdl.GetVideoInfo(params.URL) + + if err != nil { + response.InternalError(c, err) + return + } + + // if title and author are blank + if info.Title == "" && info.Author == "" { + response.BadRequest(c, "Invalid URL") + return + } + + videoArchive := model.VideoArchive{ + Author: info.Author, + DatePublished: info.DatePublished, + Description: info.Description, + Duration: int(info.Duration.Seconds()), + Title: info.Title, + URL: params.URL, + YoutubeID: info.ID, + UploadedBy: claims.Username, + } + + err = model.VideoArchiveSave(db.GetConn(), &videoArchive) + + if err != nil { + response.InternalError(c, err) + return + } + + response.Success(c, "saved") +} diff --git a/server/webserver/server.go b/server/webserver/server.go index 8309157..af95d52 100644 --- a/server/webserver/server.go +++ b/server/webserver/server.go @@ -1,12 +1,14 @@ package webserver import ( + "strings" + "github.com/gobuffalo/packr" + "github.com/mgerb/go-discord-bot/server/webserver/response" "github.com/gin-gonic/gin" "github.com/mgerb/go-discord-bot/server/config" - "github.com/mgerb/go-discord-bot/server/webserver/handlers" - "github.com/mgerb/go-discord-bot/server/webserver/middleware" + "github.com/mgerb/go-discord-bot/server/webserver/routes" ) func getRouter() *gin.Engine { @@ -19,29 +21,30 @@ func getRouter() *gin.Engine { router.Static("/public/youtube", config.Config.YoutubePath) router.Static("/public/clips", config.Config.ClipsPath) - router.NoRoute(func(c *gin.Context) { - c.Data(200, "text/html", box.Bytes("index.html")) - }) - api := router.Group("/api") - api.GET("/ytdownloader", handlers.Downloader) - api.GET("/soundlist", handlers.SoundList) - api.GET("/cliplist", handlers.ClipList) - api.POST("/oauth", handlers.Oauth) - api.GET("/logger/messages", handlers.GetMessages) - api.GET("/logger/linkedmessages", handlers.GetLinkedMessages) - api.GET("/config/client_id", handlers.GetClientID) - authorizedAPI := router.Group("/api") - authorizedAPI.Use(middleware.AuthorizedJWT()) - authorizedAPI.POST("/upload", handlers.FileUpload) + // add api routes + routes.AddSoundListRoutes(api) + routes.AddOauthRoutes(api) + routes.AddLoggerRoutes(api) + routes.AddDownloaderRoutes(api) + routes.AddConfigRoutes(api) + routes.AddUploadRoutes(api) + routes.AddVideoArchiveRoutes(api) + + router.NoRoute(func(c *gin.Context) { + if strings.HasPrefix(c.Request.URL.String(), "/api/") { + response.BadRequest(c, "404 Not Found") + } else { + c.Data(200, "text/html", box.Bytes("index.html")) + } + }) return router } // Start - func Start() { - router := getRouter() router.Run(config.Config.ServerAddr) }