2024-07-04 22:29:32 +02:00
|
|
|
package flightlesssomething
|
|
|
|
|
|
|
|
import (
|
2024-07-14 16:51:34 +02:00
|
|
|
"bytes"
|
2024-07-14 17:10:04 +02:00
|
|
|
"fmt"
|
2024-07-04 22:29:32 +02:00
|
|
|
"html/template"
|
2024-07-14 16:51:34 +02:00
|
|
|
"io/fs"
|
2024-07-14 17:49:10 +02:00
|
|
|
"log"
|
2024-07-04 22:29:32 +02:00
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/gin-contrib/sessions"
|
|
|
|
gormsessions "github.com/gin-contrib/sessions/gorm"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/glebarez/sqlite"
|
|
|
|
"github.com/ravener/discord-oauth2"
|
|
|
|
"golang.org/x/oauth2"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// GORM database object
|
|
|
|
db *gorm.DB
|
|
|
|
|
|
|
|
// Discord conf object
|
|
|
|
discordConf *oauth2.Config
|
|
|
|
|
|
|
|
// Benchmarks directory
|
|
|
|
benchmarksDir string
|
|
|
|
)
|
|
|
|
|
2024-07-14 17:49:10 +02:00
|
|
|
func Start(c *Config, version string) {
|
2024-07-04 22:29:32 +02:00
|
|
|
// Setup data dir //
|
|
|
|
|
2024-07-04 23:17:48 +02:00
|
|
|
_, err := os.Stat(c.DataDir)
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
err := os.Mkdir(c.DataDir, 0755)
|
|
|
|
if err != nil {
|
|
|
|
panic("Failed to create data dir: " + err.Error())
|
|
|
|
}
|
|
|
|
} else if err != nil {
|
|
|
|
panic("Failed to check data dir: " + err.Error())
|
|
|
|
}
|
|
|
|
|
2024-07-04 22:29:32 +02:00
|
|
|
benchmarksDir = filepath.Join(c.DataDir, "benchmarks")
|
|
|
|
_, err = os.Stat(benchmarksDir)
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
err := os.Mkdir(benchmarksDir, 0755)
|
|
|
|
if err != nil {
|
|
|
|
panic("Failed to create benchmarks dir: " + err.Error())
|
|
|
|
}
|
|
|
|
} else if err != nil {
|
|
|
|
panic("Failed to check benchmarks dir: " + err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup Discord OAuth2 //
|
|
|
|
|
|
|
|
discordConf = &oauth2.Config{
|
|
|
|
Endpoint: discord.Endpoint,
|
|
|
|
Scopes: []string{discord.ScopeIdentify},
|
|
|
|
RedirectURL: c.DiscordRedirectURL,
|
|
|
|
ClientID: c.DiscordClientID,
|
|
|
|
ClientSecret: c.DiscordClientSecret,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup gorm (database) //
|
|
|
|
|
|
|
|
db, err = gorm.Open(sqlite.Open(filepath.Join(c.DataDir, "database.db")), &gorm.Config{})
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2024-07-12 22:57:42 +02:00
|
|
|
store := gormsessions.NewStore(db, true, []byte(c.SessionSecret))
|
2024-07-04 22:29:32 +02:00
|
|
|
db.AutoMigrate(&Benchmark{})
|
|
|
|
|
|
|
|
// Setup gin //
|
|
|
|
|
2024-07-14 18:38:51 +02:00
|
|
|
if version == "" {
|
|
|
|
gin.SetMode(gin.DebugMode)
|
|
|
|
} else {
|
|
|
|
gin.SetMode(gin.ReleaseMode)
|
|
|
|
}
|
2024-07-04 22:29:32 +02:00
|
|
|
r := gin.Default()
|
|
|
|
r.Use(sessions.Sessions("mysession", store))
|
|
|
|
|
2024-07-14 17:49:10 +02:00
|
|
|
// Create a new FuncMap and add the version function
|
|
|
|
funcMap := template.FuncMap{
|
|
|
|
"version": func() string {
|
2024-07-14 18:38:51 +02:00
|
|
|
if version == "" {
|
|
|
|
return "dev"
|
|
|
|
}
|
2024-07-14 17:49:10 +02:00
|
|
|
return version
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new template, apply the function map, and parse the templates
|
|
|
|
tmpl := template.New("").Funcs(funcMap)
|
|
|
|
tmpl, err = tmpl.ParseFS(templatesFS, "templates/*.tmpl")
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Failed to parse templates: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the HTML template for Gin
|
2024-07-04 22:29:32 +02:00
|
|
|
r.SetHTMLTemplate(tmpl)
|
|
|
|
|
2024-07-11 19:29:14 +02:00
|
|
|
// Serve static files
|
|
|
|
r.GET("/static/*filepath", func(c *gin.Context) {
|
2024-07-14 16:51:34 +02:00
|
|
|
filepath := c.Param("filepath")
|
|
|
|
file, err := staticFS.Open("static" + filepath)
|
|
|
|
if err != nil {
|
|
|
|
c.Status(http.StatusNotFound)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
// Get file info
|
|
|
|
fileInfo, err := file.Stat()
|
|
|
|
if err != nil {
|
|
|
|
c.Status(http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-07-14 17:10:04 +02:00
|
|
|
// Generate ETag based on file modification time
|
|
|
|
etag := fmt.Sprintf("%x-%x", fileInfo.ModTime().Unix(), fileInfo.Size())
|
2024-07-14 16:51:34 +02:00
|
|
|
|
2024-07-14 17:10:04 +02:00
|
|
|
// Set ETag header
|
2024-07-14 16:51:34 +02:00
|
|
|
c.Header("ETag", etag)
|
|
|
|
|
|
|
|
// Check if the ETag matches
|
|
|
|
if match := c.GetHeader("If-None-Match"); match == etag {
|
|
|
|
c.Status(http.StatusNotModified)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-07-14 17:10:04 +02:00
|
|
|
// Read file content into a byte slice
|
|
|
|
content, err := fs.ReadFile(staticFS, "static"+filepath)
|
|
|
|
if err != nil {
|
|
|
|
c.Status(http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-07-14 16:51:34 +02:00
|
|
|
// Serve the file with ETag and Last-Modified headers
|
|
|
|
http.ServeContent(c.Writer, c.Request, fileInfo.Name(), fileInfo.ModTime(), bytes.NewReader(content))
|
2024-07-11 19:29:14 +02:00
|
|
|
})
|
|
|
|
|
2024-07-04 22:29:32 +02:00
|
|
|
r.GET("/", func(c *gin.Context) { c.Redirect(http.StatusTemporaryRedirect, "/benchmarks") })
|
|
|
|
|
|
|
|
r.GET("/benchmarks", getBenchmarks)
|
|
|
|
|
|
|
|
r.GET("/benchmark", getBenchmarkCreate)
|
|
|
|
r.POST("/benchmark", postBenchmarkCreate)
|
|
|
|
r.GET("/benchmark/:id", getBenchmark)
|
|
|
|
r.DELETE("/benchmark/:id", deleteBenchmark)
|
2024-07-12 09:45:48 +02:00
|
|
|
r.GET("/benchmark/:id/download", getBenchmarkDownload)
|
2024-07-04 22:29:32 +02:00
|
|
|
|
|
|
|
r.GET("/user/:id", getUser)
|
|
|
|
|
|
|
|
r.GET("/login", getLogin)
|
|
|
|
r.GET("/login/callback", getLoginCallback)
|
|
|
|
r.GET("/logout", getLogout)
|
|
|
|
|
|
|
|
r.Run(c.Bind)
|
|
|
|
}
|