2024-09-24 06:17:11 -04:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
2024-11-28 22:32:17 -05:00
|
|
|
"embed"
|
|
|
|
"errors"
|
|
|
|
"io/fs"
|
|
|
|
"net/http"
|
2024-11-28 14:53:36 -05:00
|
|
|
"path/filepath"
|
|
|
|
"pcgamedb/crawler"
|
|
|
|
"pcgamedb/db"
|
|
|
|
"pcgamedb/log"
|
2024-11-20 06:09:04 -05:00
|
|
|
"pcgamedb/server/handler"
|
|
|
|
"pcgamedb/server/middleware"
|
2024-11-28 17:04:28 -05:00
|
|
|
"strconv"
|
2024-11-28 22:32:17 -05:00
|
|
|
"strings"
|
2024-09-24 06:17:11 -04:00
|
|
|
|
|
|
|
"github.com/gin-contrib/cors"
|
2024-11-28 17:04:28 -05:00
|
|
|
"github.com/gin-contrib/multitemplate"
|
2024-09-24 06:17:11 -04:00
|
|
|
"github.com/gin-gonic/gin"
|
2024-11-28 14:53:36 -05:00
|
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
|
|
"go.uber.org/zap"
|
2024-09-24 06:17:11 -04:00
|
|
|
|
2024-11-21 12:30:26 -05:00
|
|
|
"pcgamedb/docs"
|
2024-09-24 06:17:11 -04:00
|
|
|
|
|
|
|
swaggerfiles "github.com/swaggo/files"
|
|
|
|
ginSwagger "github.com/swaggo/gin-swagger"
|
|
|
|
)
|
|
|
|
|
2024-11-28 22:32:17 -05:00
|
|
|
//go:embed templates templates/layouts static
|
|
|
|
var frontendFS embed.FS
|
|
|
|
|
2024-09-24 06:17:11 -04:00
|
|
|
func initRoute(app *gin.Engine) {
|
|
|
|
app.Use(cors.New(cors.Config{
|
|
|
|
AllowAllOrigins: true,
|
|
|
|
}))
|
|
|
|
|
2024-11-28 14:53:36 -05:00
|
|
|
initFrontend(app)
|
|
|
|
initApi(app)
|
|
|
|
}
|
|
|
|
|
|
|
|
func initFrontend(app *gin.Engine) {
|
2024-12-04 14:23:41 -05:00
|
|
|
app.Use(middleware.Logger())
|
|
|
|
|
2024-11-28 22:32:17 -05:00
|
|
|
// Load static files
|
|
|
|
staticFs, err := fs.Sub(frontendFS, "static")
|
|
|
|
if err != nil {
|
|
|
|
log.Logger.Fatal("Error loading static files", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
app.StaticFS("/static", http.FS(staticFs))
|
2024-11-28 17:04:28 -05:00
|
|
|
|
2024-11-28 22:32:17 -05:00
|
|
|
// Load templates
|
|
|
|
// directly using templates app.LoadHTMLFiles() to load all templates leads to error
|
|
|
|
// because use templates with same name in different html file will case overridden
|
|
|
|
// so we need to load all templates manually and use multitemplate to render them
|
2024-11-28 17:04:28 -05:00
|
|
|
r := multitemplate.NewRenderer()
|
|
|
|
|
2024-11-28 22:32:17 -05:00
|
|
|
layoutFiles, err := frontendFS.ReadDir("templates/layouts")
|
2024-11-28 14:53:36 -05:00
|
|
|
if err != nil {
|
|
|
|
log.Logger.Fatal("Error loading layout templates", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-11-28 22:32:17 -05:00
|
|
|
rootFiles, err := frontendFS.ReadDir("templates")
|
2024-11-28 14:53:36 -05:00
|
|
|
if err != nil {
|
|
|
|
log.Logger.Fatal("Error loading root templates", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-11-28 17:04:28 -05:00
|
|
|
for _, rootFile := range rootFiles {
|
2024-11-28 22:32:17 -05:00
|
|
|
if rootFile.IsDir() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
name := filepath.Base(rootFile.Name())
|
|
|
|
templateFiles := []string{"templates/" + name}
|
|
|
|
for _, layout := range layoutFiles {
|
|
|
|
if !layout.IsDir() {
|
|
|
|
templateFiles = append(templateFiles, "templates/layouts/"+layout.Name())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
r.AddFromFS(name, frontendFS, templateFiles...)
|
2024-11-28 17:04:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
app.HTMLRender = r
|
2024-11-28 14:53:36 -05:00
|
|
|
|
2024-11-28 22:32:17 -05:00
|
|
|
// Load routes
|
2024-11-28 14:53:36 -05:00
|
|
|
app.GET("/", func(ctx *gin.Context) {
|
2024-12-04 12:36:55 -05:00
|
|
|
monthTop, err := crawler.GetSteam250MonthTop50()
|
2024-11-28 17:04:28 -05:00
|
|
|
if err != nil {
|
|
|
|
ctx.HTML(500, "500.html", err)
|
|
|
|
return
|
|
|
|
}
|
2024-12-04 12:36:55 -05:00
|
|
|
mostPlayed, err := crawler.GetSteam250MostPlayed()
|
2024-11-28 17:04:28 -05:00
|
|
|
if err != nil {
|
|
|
|
ctx.HTML(500, "500.html", err)
|
|
|
|
return
|
|
|
|
}
|
2024-12-04 12:36:55 -05:00
|
|
|
bestOfTheYear, err := crawler.GetSteam250BestOfTheYear()
|
2024-11-28 14:53:36 -05:00
|
|
|
if err != nil {
|
2024-11-28 14:55:45 -05:00
|
|
|
ctx.HTML(500, "500.html", err)
|
2024-11-28 14:53:36 -05:00
|
|
|
return
|
|
|
|
}
|
2024-11-28 14:55:45 -05:00
|
|
|
ctx.HTML(200, "index.html", gin.H{
|
2024-11-28 17:04:28 -05:00
|
|
|
"MonthTop": monthTop,
|
|
|
|
"MostPlayed": mostPlayed,
|
|
|
|
"BestOfTheYear": bestOfTheYear,
|
2024-11-28 14:53:36 -05:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
app.GET("/game/:id", func(ctx *gin.Context) {
|
|
|
|
idStr := ctx.Param("id")
|
|
|
|
id, err := primitive.ObjectIDFromHex(idStr)
|
|
|
|
if err != nil {
|
2024-11-28 14:55:45 -05:00
|
|
|
ctx.HTML(400, "400.html", nil)
|
2024-11-28 14:53:36 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
info, err := db.GetGameInfoByID(id)
|
|
|
|
if err != nil {
|
2024-11-28 14:55:45 -05:00
|
|
|
ctx.HTML(500, "500.html", err)
|
2024-11-28 14:53:36 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
games, err := db.GetGameItemsByIDs(info.GameIDs)
|
|
|
|
if err != nil {
|
2024-11-28 14:55:45 -05:00
|
|
|
ctx.HTML(500, "500.html", err)
|
2024-11-28 14:53:36 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
info.Games = games
|
2024-11-28 14:55:45 -05:00
|
|
|
ctx.HTML(200, "game.html", info)
|
2024-11-28 14:53:36 -05:00
|
|
|
})
|
2024-11-28 17:04:28 -05:00
|
|
|
|
|
|
|
app.GET("/search", func(ctx *gin.Context) {
|
|
|
|
key := ctx.Query("key")
|
|
|
|
page := ctx.Query("page")
|
2024-11-28 22:32:17 -05:00
|
|
|
key = strings.TrimSpace(key)
|
2024-11-28 17:04:28 -05:00
|
|
|
if len(key) < 2 {
|
2024-11-28 22:32:17 -05:00
|
|
|
ctx.HTML(400, "400.html", errors.New("Search key should be at least 2 characters long"))
|
2024-11-28 17:04:28 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if page == "" {
|
|
|
|
page = "1"
|
|
|
|
}
|
|
|
|
pageInt, err := strconv.Atoi(page)
|
|
|
|
if err != nil {
|
2024-11-28 22:32:17 -05:00
|
|
|
ctx.HTML(400, "400.html", err)
|
2024-11-28 17:04:28 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
games, totalPage, err := db.SearchGameInfos(key, pageInt, 10)
|
|
|
|
if err != nil {
|
|
|
|
ctx.HTML(500, "500.html", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
res := gin.H{
|
|
|
|
"Games": games,
|
|
|
|
"TotalPage": totalPage,
|
|
|
|
"CurrentPage": pageInt,
|
|
|
|
"Key": key,
|
|
|
|
}
|
|
|
|
if pageInt > 1 {
|
|
|
|
res["PrevPage"] = pageInt - 1
|
|
|
|
}
|
|
|
|
if pageInt < totalPage {
|
|
|
|
res["NextPage"] = pageInt + 1
|
|
|
|
}
|
|
|
|
ctx.HTML(200, "search.html", res)
|
|
|
|
})
|
2024-11-28 14:53:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func initApi(app *gin.Engine) {
|
|
|
|
apiGroup := app.Group("/api")
|
|
|
|
|
|
|
|
GameInfoGroup := apiGroup.Group("/game")
|
2024-12-04 14:23:41 -05:00
|
|
|
GameInfoGroup.Use(middleware.Logger())
|
2024-11-16 00:48:48 -05:00
|
|
|
GameItemGroup := GameInfoGroup.Group("/raw")
|
2024-12-04 14:23:41 -05:00
|
|
|
GameItemGroup.Use(middleware.Logger())
|
2024-09-24 06:17:11 -04:00
|
|
|
|
2024-11-16 00:48:48 -05:00
|
|
|
GameItemGroup.GET("/unorganized", handler.GetUnorganizedGameItemsHandler)
|
|
|
|
GameItemGroup.POST("/organize", middleware.Auth(), handler.OrganizeGameItemHandler)
|
|
|
|
GameItemGroup.GET("/id/:id", handler.GetGameItemByIDHanlder)
|
|
|
|
GameItemGroup.GET("/name/:name", handler.GetGameItemByRawNameHandler)
|
|
|
|
GameItemGroup.GET("/author/:author", handler.GetGameItemsByAuthorHandler)
|
2024-09-24 06:17:11 -04:00
|
|
|
|
|
|
|
GameInfoGroup.GET("/search", handler.SearchGamesHandler)
|
|
|
|
GameInfoGroup.GET("/name/:name", handler.GetGameInfosByNameHandler)
|
|
|
|
GameInfoGroup.GET("/platform/:platform_type/:platform_id", handler.GetGameInfoByPlatformIDHandler)
|
|
|
|
GameInfoGroup.GET("/id/:id", handler.GetGameInfoByIDHandler)
|
2024-09-25 09:33:46 -04:00
|
|
|
GameInfoGroup.DELETE("/id/:id", middleware.Auth(), handler.DeleteGameInfoHandler)
|
2024-09-24 06:17:11 -04:00
|
|
|
|
2024-12-04 14:23:41 -05:00
|
|
|
apiGroup.GET("/popular/:type", middleware.Logger(), handler.GetPopularGameInfosHandler)
|
2024-11-28 14:53:36 -05:00
|
|
|
apiGroup.GET("/healthcheck", handler.HealthCheckHandler)
|
2024-12-04 14:23:41 -05:00
|
|
|
apiGroup.GET("/author", middleware.Logger(), handler.GetAllAuthorsHandler)
|
|
|
|
apiGroup.POST("/clean", middleware.Logger(), middleware.Auth(), handler.CleanGameHandler)
|
2024-09-24 06:17:11 -04:00
|
|
|
|
|
|
|
docs.SwaggerInfo.BasePath = "/api"
|
2024-11-28 14:53:36 -05:00
|
|
|
apiGroup.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
|
2024-09-24 06:17:11 -04:00
|
|
|
}
|