Optimizations. User-Role handling in templates and routes.
This commit is contained in:
24
auth.go
24
auth.go
@@ -46,7 +46,7 @@ func hashPassword(password string) (string, error) {
|
|||||||
return string(hashedPassword), nil
|
return string(hashedPassword), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AuthRequired() gin.HandlerFunc {
|
func ReaderAuthRequired() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
auth, okAuth := session.GetAuthenticated(c)
|
auth, okAuth := session.GetAuthenticated(c)
|
||||||
username, okUser := session.GetUsername(c)
|
username, okUser := session.GetUsername(c)
|
||||||
@@ -60,6 +60,20 @@ func AuthRequired() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AuthorAuthRequired() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
auth, okAuth := session.GetAuthenticated(c)
|
||||||
|
username, okUser := session.GetUsername(c)
|
||||||
|
|
||||||
|
if !okAuth || !okUser || !auth || !controllers.IsUserEnabled(username) || controllers.GetUserRole(username) == models.ReaderRole {
|
||||||
|
redirectToLogin(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func AdminAuthRequired() gin.HandlerFunc {
|
func AdminAuthRequired() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
auth, okAuth := session.GetAuthenticated(c)
|
auth, okAuth := session.GetAuthenticated(c)
|
||||||
@@ -74,14 +88,6 @@ func AdminAuthRequired() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isUserAdmin(c *gin.Context) bool {
|
|
||||||
username, ok := session.GetUsername(c)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return controllers.IsUserAdmin(username)
|
|
||||||
}
|
|
||||||
|
|
||||||
func redirectToLogin(c *gin.Context) {
|
func redirectToLogin(c *gin.Context) {
|
||||||
if err := session.InvalidateSession(c); err != nil {
|
if err := session.InvalidateSession(c); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|||||||
@@ -20,12 +20,17 @@ type UpdateCacheInput struct {
|
|||||||
Score float32 `json:"score" gorm:"default:-1.0"`
|
Score float32 `json:"score" gorm:"default:-1.0"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCacheByPlayerID GET /cache/:player_id?game_tag=TAG
|
// GetCacheByPlayerID GET /cache/:player_id
|
||||||
func GetCacheByPlayerID(c *gin.Context) {
|
func GetCacheByPlayerID(c *gin.Context) {
|
||||||
playerId := utils.StringToUint(c.Param("player_id"))
|
playerId := utils.StringToUint(c.Param("player_id"))
|
||||||
gameTag := c.Request.URL.Query().Get("game_tag")
|
|
||||||
|
|
||||||
val, err := cache.GetScore(playerId, gameTag)
|
game, err := GetActiveGame(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "No active game available!"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err := cache.GetScore(playerId, game.Tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"})
|
||||||
} else {
|
} else {
|
||||||
@@ -91,11 +96,11 @@ func DeleteAllCaches(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetScoreByPlayerID GET /score/:player_id?game_tag=TAG
|
// GetScoreByPlayerID GET /score/:player_id
|
||||||
func GetScoreByPlayerID(c *gin.Context) {
|
func GetScoreByPlayerID(c *gin.Context) {
|
||||||
var player models.Player
|
var player models.Player
|
||||||
var gameTag = c.Request.URL.Query().Get("game_tag")
|
|
||||||
var playerId = utils.StringToUint(c.Param("player_id"))
|
var playerId = utils.StringToUint(c.Param("player_id"))
|
||||||
|
|
||||||
if err := models.DB.
|
if err := models.DB.
|
||||||
Model(&models.Player{}).
|
Model(&models.Player{}).
|
||||||
Where("id = ?", playerId).
|
Where("id = ?", playerId).
|
||||||
@@ -105,11 +110,17 @@ func GetScoreByPlayerID(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
score, err := cache.GetScore(player.ID, gameTag)
|
game, err := GetActiveGame(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "No active game available!"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
score, err := cache.GetScore(player.ID, game.Tag)
|
||||||
if err != nil || score == -1 {
|
if err != nil || score == -1 {
|
||||||
score = utils.CalcPlayerScore(player.Name, gameTag)
|
score = utils.CalcPlayerScore(player.Name, game.Tag)
|
||||||
if score == score && score != -1 { // not NaN
|
if score == score && score != -1 { // not NaN
|
||||||
if err := cache.SetScore(player.ID, gameTag, score); err != nil {
|
if err := cache.SetScore(player.ID, game.Tag, score); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
|
"html/template"
|
||||||
"internal/cache"
|
"internal/cache"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type AddPlayerInput struct {
|
type AddPlayerInput struct {
|
||||||
@@ -44,14 +44,6 @@ func GetPlayersByClanHTML(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.ReadFile("./templates/player_list_item.html")
|
|
||||||
if err != nil {
|
|
||||||
c.String(http.StatusBadRequest, "")
|
|
||||||
log.Fatal(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
playerItem := string(file)
|
|
||||||
|
|
||||||
game, err := GetActiveGame(c)
|
game, err := GetActiveGame(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(http.StatusBadRequest, "")
|
c.String(http.StatusBadRequest, "")
|
||||||
@@ -59,19 +51,37 @@ func GetPlayersByClanHTML(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var htmlOptions string
|
scores, err := cache.GetScores(players, game.Tag)
|
||||||
for _, player := range players {
|
if err != nil {
|
||||||
var score string
|
c.String(http.StatusBadRequest, "")
|
||||||
if val, err := cache.GetScore(player.ID, game.Tag); err != nil || val == -1.0 {
|
log.Fatal(err)
|
||||||
score = "<i class=\"bi bi-dash me-2\" style=\"margin-left:0.7rem;\"></i>"
|
|
||||||
} else {
|
|
||||||
score = fmt.Sprintf("%.2f", val)
|
|
||||||
}
|
|
||||||
htmlOptions += fmt.Sprintf(playerItem, player.Name, score, utils.UintToString(player.ID)+"?game_tag="+game.Tag, player.ID, player.ID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Header("Content-Type", "text/html")
|
userRole := GetUserRoleByCtx(c)
|
||||||
c.String(http.StatusOK, htmlOptions)
|
|
||||||
|
var data []map[string]interface{}
|
||||||
|
for i, player := range players {
|
||||||
|
|
||||||
|
score := scores[i]
|
||||||
|
var scoreStr string
|
||||||
|
if score == -1.0 {
|
||||||
|
scoreStr = "<i class=\"bi bi-dash me-2\" style=\"margin-left:0.7rem;\"></i>"
|
||||||
|
} else {
|
||||||
|
scoreStr = fmt.Sprintf("%.2f", score)
|
||||||
|
}
|
||||||
|
|
||||||
|
data = append(data, map[string]interface{}{
|
||||||
|
"PlayerName": player.Name,
|
||||||
|
"Score": template.HTML(scoreStr),
|
||||||
|
"PlayerID": player.ID,
|
||||||
|
"UserRole": userRole,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
err = utils.PlayerItemTemplate.Execute(c.Writer, data)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPlayer POST /player
|
// AddPlayer POST /player
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"InfantrySkillCalculator/models"
|
"InfantrySkillCalculator/models"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
"log"
|
"log"
|
||||||
|
"session"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateUser(username string, hashedPassword string, enabled bool, usedCode string) {
|
func CreateUser(username string, hashedPassword string, enabled bool, usedCode string) {
|
||||||
@@ -56,6 +58,23 @@ func IsUserEnabled(username string) bool {
|
|||||||
return user.Enabled
|
return user.Enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetUserRole(username string) models.Role {
|
||||||
|
var user models.User
|
||||||
|
err := models.DB.Where("username = ?", username).First(&user).Error
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return user.UserRole
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUserRoleByCtx(c *gin.Context) models.Role {
|
||||||
|
username, ok := session.GetUsername(c)
|
||||||
|
if !ok {
|
||||||
|
return models.ReaderRole
|
||||||
|
}
|
||||||
|
return GetUserRole(username)
|
||||||
|
}
|
||||||
|
|
||||||
func IsUserAdmin(username string) bool {
|
func IsUserAdmin(username string) bool {
|
||||||
var user models.User
|
var user models.User
|
||||||
err := models.DB.Where("username = ?", username).First(&user).Error
|
err := models.DB.Where("username = ?", username).First(&user).Error
|
||||||
|
|||||||
28
internal/cache/cache.go
vendored
28
internal/cache/cache.go
vendored
@@ -41,6 +41,34 @@ func GetScore(playerId uint, gameTag string) (float32, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetScores(players []models.Player, gameTag string) ([]float32, error) {
|
||||||
|
vals, err := models.Cache.Pipelined(ctx, func(pipe redis.Pipeliner) error {
|
||||||
|
for _, p := range players {
|
||||||
|
key := GetPlayerCacheKey(p.ID, gameTag)
|
||||||
|
pipe.Get(ctx, key)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil && !errors.Is(err, redis.Nil) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var scores []float32
|
||||||
|
for _, val := range vals {
|
||||||
|
score, err := val.(*redis.StringCmd).Float32()
|
||||||
|
|
||||||
|
if errors.Is(err, redis.Nil) { // cache miss
|
||||||
|
score = -1
|
||||||
|
err = nil
|
||||||
|
} else if err != nil { // cache error
|
||||||
|
score = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
scores = append(scores, score)
|
||||||
|
}
|
||||||
|
return scores, nil
|
||||||
|
}
|
||||||
|
|
||||||
func DeleteScore(playerId uint, gameTag string) error {
|
func DeleteScore(playerId uint, gameTag string) error {
|
||||||
key := GetPlayerCacheKey(playerId, gameTag)
|
key := GetPlayerCacheKey(playerId, gameTag)
|
||||||
return models.Cache.Del(ctx, key).Err()
|
return models.Cache.Del(ctx, key).Err()
|
||||||
|
|||||||
71
main.go
71
main.go
@@ -12,13 +12,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var mainPageTemplates *template.Template
|
|
||||||
var loginPageTemplates *template.Template
|
|
||||||
var registerPageTemplates *template.Template
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
var err error
|
var err error
|
||||||
mainPageTemplates, err = template.ParseFiles(
|
utils.MainPageTemplates, err = template.ParseFiles(
|
||||||
"./templates/index.html",
|
"./templates/index.html",
|
||||||
"./templates/components/home_clan_bar.html",
|
"./templates/components/home_clan_bar.html",
|
||||||
"./templates/components/opp_clan_bar.html",
|
"./templates/components/opp_clan_bar.html",
|
||||||
@@ -38,7 +34,7 @@ func init() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
loginPageTemplates, err = template.ParseFiles(
|
utils.LoginPageTemplates, err = template.ParseFiles(
|
||||||
"./templates/login.html",
|
"./templates/login.html",
|
||||||
"./templates/components/header.html",
|
"./templates/components/header.html",
|
||||||
)
|
)
|
||||||
@@ -46,7 +42,7 @@ func init() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
registerPageTemplates, err = template.ParseFiles(
|
utils.RegisterPageTemplates, err = template.ParseFiles(
|
||||||
"./templates/register.html",
|
"./templates/register.html",
|
||||||
"./templates/components/header.html",
|
"./templates/components/header.html",
|
||||||
)
|
)
|
||||||
@@ -54,6 +50,13 @@ func init() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utils.PlayerItemTemplate, err = template.ParseFiles(
|
||||||
|
"./templates/player_list_item.html",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
controllers.LoadMetrics()
|
controllers.LoadMetrics()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,8 +71,10 @@ func main() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
router.LoadHTMLGlob("templates/**/*")
|
router.LoadHTMLGlob("templates/**/*")
|
||||||
protected := router.Group("/")
|
reader := router.Group("/")
|
||||||
protected.Use(AuthRequired())
|
reader.Use(ReaderAuthRequired())
|
||||||
|
author := router.Group("/")
|
||||||
|
author.Use(AuthorAuthRequired())
|
||||||
admin := router.Group("/admin")
|
admin := router.Group("/admin")
|
||||||
admin.Use(AdminAuthRequired())
|
admin.Use(AdminAuthRequired())
|
||||||
|
|
||||||
@@ -89,7 +94,11 @@ func main() {
|
|||||||
gin.LoggerWithWriter(utils.GinWriter, "/static"),
|
gin.LoggerWithWriter(utils.GinWriter, "/static"),
|
||||||
gin.Recovery(),
|
gin.Recovery(),
|
||||||
)
|
)
|
||||||
protected.Use(
|
reader.Use(
|
||||||
|
gin.LoggerWithWriter(utils.GinWriter),
|
||||||
|
gin.Recovery(),
|
||||||
|
)
|
||||||
|
author.Use(
|
||||||
gin.LoggerWithWriter(utils.GinWriter),
|
gin.LoggerWithWriter(utils.GinWriter),
|
||||||
gin.Recovery(),
|
gin.Recovery(),
|
||||||
)
|
)
|
||||||
@@ -106,33 +115,33 @@ func main() {
|
|||||||
router.GET("/register", registerPage)
|
router.GET("/register", registerPage)
|
||||||
router.POST("/register", registerPost)
|
router.POST("/register", registerPost)
|
||||||
|
|
||||||
protected.GET("/", mainPage)
|
reader.GET("/", mainPage)
|
||||||
|
|
||||||
protected.GET("/clans", controllers.GetAllClans)
|
reader.GET("/clans", controllers.GetAllClans)
|
||||||
protected.GET("/clans_html", controllers.GetAllClansHTML)
|
reader.GET("/clans_html", controllers.GetAllClansHTML)
|
||||||
protected.GET("/clan/:id", controllers.GetClanByID)
|
reader.GET("/clan/:id", controllers.GetClanByID)
|
||||||
protected.POST("/clan", controllers.AddClan)
|
author.POST("/clan", controllers.AddClan)
|
||||||
protected.PATCH("/clan/:id", controllers.UpdateClanByID)
|
author.PATCH("/clan/:id", controllers.UpdateClanByID)
|
||||||
protected.DELETE("/clan/:id", controllers.DeleteClanByID)
|
author.DELETE("/clan/:id", controllers.DeleteClanByID)
|
||||||
|
|
||||||
protected.GET("/players", controllers.GetAllPlayers)
|
reader.GET("/players", controllers.GetAllPlayers)
|
||||||
protected.GET("/players_html", controllers.GetPlayersByClanHTML)
|
reader.GET("/players_html", controllers.GetPlayersByClanHTML)
|
||||||
protected.GET("/player/:id", controllers.GetPlayerByID)
|
reader.GET("/player/:id", controllers.GetPlayerByID)
|
||||||
protected.GET("/playerid/:name", controllers.GetPlayerIDByName)
|
reader.GET("/playerid/:name", controllers.GetPlayerIDByName)
|
||||||
protected.POST("/player", controllers.AddPlayer)
|
author.POST("/player", controllers.AddPlayer)
|
||||||
protected.PATCH("/player/:id", controllers.UpdatePlayerByID)
|
author.PATCH("/player/:id", controllers.UpdatePlayerByID)
|
||||||
protected.DELETE("/player/:id", controllers.DeletePlayerByID)
|
author.DELETE("/player/:id", controllers.DeletePlayerByID)
|
||||||
|
|
||||||
protected.GET("/cache/:player_id", controllers.GetCacheByPlayerID)
|
reader.GET("/cache/:player_id", controllers.GetCacheByPlayerID)
|
||||||
|
|
||||||
protected.GET("/score/:player_id", controllers.GetScoreByPlayerID)
|
reader.GET("/score/:player_id", controllers.GetScoreByPlayerID)
|
||||||
protected.POST("/score/:player_name", controllers.GetScoreByPlayerName)
|
reader.POST("/score/:player_name", controllers.GetScoreByPlayerName)
|
||||||
|
|
||||||
protected.GET("/game", controllers.GetGames)
|
reader.GET("/game", controllers.GetGames)
|
||||||
protected.GET("/game_html", controllers.GetGamesHTML)
|
reader.GET("/game_html", controllers.GetGamesHTML)
|
||||||
|
|
||||||
protected.GET("/settings", controllers.GetSettings)
|
reader.GET("/settings", controllers.GetSettings)
|
||||||
protected.PATCH("/settings", controllers.UpdateSettings)
|
reader.PATCH("/settings", controllers.UpdateSettings)
|
||||||
|
|
||||||
admin.DELETE("/clear_cache", controllers.DeleteAllCaches)
|
admin.DELETE("/clear_cache", controllers.DeleteAllCaches)
|
||||||
admin.DELETE("/purge_players", controllers.DeleteAllPlayers)
|
admin.DELETE("/purge_players", controllers.DeleteAllPlayers)
|
||||||
|
|||||||
9
pages.go
9
pages.go
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"InfantrySkillCalculator/controllers"
|
"InfantrySkillCalculator/controllers"
|
||||||
|
"InfantrySkillCalculator/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -10,10 +11,10 @@ import (
|
|||||||
|
|
||||||
func mainPage(c *gin.Context) {
|
func mainPage(c *gin.Context) {
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"isAdmin": isUserAdmin(c),
|
"UserRole": controllers.GetUserRoleByCtx(c),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := mainPageTemplates.Execute(c.Writer, data)
|
err := utils.MainPageTemplates.Execute(c.Writer, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -25,7 +26,7 @@ func loginPage(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := loginPageTemplates.Execute(c.Writer, nil)
|
err := utils.LoginPageTemplates.Execute(c.Writer, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -64,7 +65,7 @@ func registerPage(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := registerPageTemplates.Execute(c.Writer, nil)
|
err := utils.RegisterPageTemplates.Execute(c.Writer, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<button class="btn btn-lg btn-outline-secondary text-secondary-emphasis me-2" data-bs-toggle="modal" data-bs-target="#settingsModal">
|
<button class="btn btn-lg btn-outline-secondary text-secondary-emphasis me-2" data-bs-toggle="modal" data-bs-target="#settingsModal">
|
||||||
<i class="bi bi-gear-fill"></i>
|
<i class="bi bi-gear-fill"></i>
|
||||||
</button>
|
</button>
|
||||||
{{ if .isAdmin }}
|
{{ if (eq .UserRole "ADMIN") }}
|
||||||
<div class="dropdown">
|
<div class="dropdown">
|
||||||
<button class="btn btn-lg btn-outline-secondary text-secondary-emphasis dropdown-toggle" type="button" data-bs-toggle="dropdown">
|
<button class="btn btn-lg btn-outline-secondary text-secondary-emphasis dropdown-toggle" type="button" data-bs-toggle="dropdown">
|
||||||
<i class="bi bi-tools"></i>
|
<i class="bi bi-tools"></i>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
<option disabled selected value>Auswählen...</option>
|
<option disabled selected value>Auswählen...</option>
|
||||||
<!-- Options will be loaded dynamically -->
|
<!-- Options will be loaded dynamically -->
|
||||||
</select>
|
</select>
|
||||||
|
{{ if not (eq .UserRole "READER") }}
|
||||||
<button class="btn btn-lg btn-outline-secondary text-danger bg-secondary-subtle" type="button" id="home-delete" data-bs-toggle="modal" data-bs-list="#home-clan" data-bs-target="#deleteClanModal" disabled>
|
<button class="btn btn-lg btn-outline-secondary text-danger bg-secondary-subtle" type="button" id="home-delete" data-bs-toggle="modal" data-bs-list="#home-clan" data-bs-target="#deleteClanModal" disabled>
|
||||||
<i class="bi bi-trash3" data-bs-toggle="tooltip" data-bs-title="Clan löschen"></i>
|
<i class="bi bi-trash3" data-bs-toggle="tooltip" data-bs-title="Clan löschen"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -19,6 +20,7 @@
|
|||||||
<button class="btn btn-lg btn-outline-secondary text-success" type="button" data-bs-toggle="modal" data-bs-list="#home-clan" data-bs-target="#addClanModal" id="home-add">
|
<button class="btn btn-lg btn-outline-secondary text-success" type="button" data-bs-toggle="modal" data-bs-list="#home-clan" data-bs-target="#addClanModal" id="home-add">
|
||||||
<i class="bi bi-plus-lg" data-bs-toggle="tooltip" data-bs-title="Clan hinzufügen"></i>
|
<i class="bi bi-plus-lg" data-bs-toggle="tooltip" data-bs-title="Clan hinzufügen"></i>
|
||||||
</button>
|
</button>
|
||||||
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,10 +15,12 @@
|
|||||||
<i class="bi bi-square fs-4" data-bs-toggle="tooltip" data-bs-title="Nichts auswählen"></i>
|
<i class="bi bi-square fs-4" data-bs-toggle="tooltip" data-bs-title="Nichts auswählen"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{{ if not (eq .UserRole "READER") }}
|
||||||
<br>
|
<br>
|
||||||
<button type="button" class="btn btn-outline-secondary text-success px-3 mt-2 bg-secondary-subtle" id="home-player-add" data-bs-toggle="modal" data-bs-list="#home-player-list" data-bs-target="#addPlayerModal" disabled>
|
<button type="button" class="btn btn-outline-secondary text-success px-3 mt-2 bg-secondary-subtle" id="home-player-add" data-bs-toggle="modal" data-bs-list="#home-player-list" data-bs-target="#addPlayerModal" disabled>
|
||||||
<i class="bi bi-person-add fs-4" data-bs-toggle="tooltip" data-bs-title="Spieler hinzufügen"></i>
|
<i class="bi bi-person-add fs-4" data-bs-toggle="tooltip" data-bs-title="Spieler hinzufügen"></i>
|
||||||
</button>
|
</button>
|
||||||
|
{{ end }}
|
||||||
<br>
|
<br>
|
||||||
<div class="vstack text-center border border-secondary rounded mt-2" data-bs-toggle="tooltip" data-bs-title="Ausgwählte Spieler">
|
<div class="vstack text-center border border-secondary rounded mt-2" data-bs-toggle="tooltip" data-bs-title="Ausgwählte Spieler">
|
||||||
<i class="bi bi-ui-checks fs-4 mt-2 mb-1"></i>
|
<i class="bi bi-ui-checks fs-4 mt-2 mb-1"></i>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
<select class="form-select form-control border-secondary" id="opponent-clan" hx-get="/players_html" hx-target="#opponent-player-list" hx-vals='js:{"clan_id": getSelectedClanId("opponent-clan")}'>
|
<select class="form-select form-control border-secondary" id="opponent-clan" hx-get="/players_html" hx-target="#opponent-player-list" hx-vals='js:{"clan_id": getSelectedClanId("opponent-clan")}'>
|
||||||
<option disabled selected value>Auswählen...</option>
|
<option disabled selected value>Auswählen...</option>
|
||||||
</select>
|
</select>
|
||||||
|
{{ if not (eq .UserRole "READER") }}
|
||||||
<button class="btn btn-lg btn-outline-secondary text-danger bg-secondary-subtle" type="button" id="opponent-delete" data-bs-toggle="modal" data-bs-list="#opponent-clan" data-bs-target="#deleteClanModal" disabled>
|
<button class="btn btn-lg btn-outline-secondary text-danger bg-secondary-subtle" type="button" id="opponent-delete" data-bs-toggle="modal" data-bs-list="#opponent-clan" data-bs-target="#deleteClanModal" disabled>
|
||||||
<i class="bi bi-trash3" data-bs-toggle="tooltip" data-bs-title="Clan löschen"></i>
|
<i class="bi bi-trash3" data-bs-toggle="tooltip" data-bs-title="Clan löschen"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -18,6 +19,7 @@
|
|||||||
<button class="btn btn-lg btn-outline-secondary text-success" type="button" data-bs-toggle="modal" data-bs-list="#opponent-clan" data-bs-target="#addClanModal" id="opponent-add">
|
<button class="btn btn-lg btn-outline-secondary text-success" type="button" data-bs-toggle="modal" data-bs-list="#opponent-clan" data-bs-target="#addClanModal" id="opponent-add">
|
||||||
<i class="bi bi-plus-lg" data-bs-toggle="tooltip" data-bs-title="Clan hinzufügen"></i>
|
<i class="bi bi-plus-lg" data-bs-toggle="tooltip" data-bs-title="Clan hinzufügen"></i>
|
||||||
</button>
|
</button>
|
||||||
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,10 +15,12 @@
|
|||||||
<i class="bi bi-square fs-4" data-bs-toggle="tooltip" data-bs-title="Nichts auswählen"></i>
|
<i class="bi bi-square fs-4" data-bs-toggle="tooltip" data-bs-title="Nichts auswählen"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{{ if not (eq .UserRole "READER") }}
|
||||||
<br>
|
<br>
|
||||||
<button type="button" class="btn btn-outline-secondary text-success px-3 mt-2 bg-secondary-subtle" id="opponent-player-add" data-bs-toggle="modal" data-bs-list="#opponent-player-list" data-bs-target="#addPlayerModal" disabled>
|
<button type="button" class="btn btn-outline-secondary text-success px-3 mt-2 bg-secondary-subtle" id="opponent-player-add" data-bs-toggle="modal" data-bs-list="#opponent-player-list" data-bs-target="#addPlayerModal" disabled>
|
||||||
<i class="bi bi-person-add fs-4" data-bs-toggle="tooltip" data-bs-title="Spieler hinzufügen"></i>
|
<i class="bi bi-person-add fs-4" data-bs-toggle="tooltip" data-bs-title="Spieler hinzufügen"></i>
|
||||||
</button>
|
</button>
|
||||||
|
{{ end }}
|
||||||
<br>
|
<br>
|
||||||
<div class="vstack text-center border border-secondary rounded mt-2" data-bs-toggle="tooltip" data-bs-title="Spieler hinzufügen">
|
<div class="vstack text-center border border-secondary rounded mt-2" data-bs-toggle="tooltip" data-bs-title="Spieler hinzufügen">
|
||||||
<i class="bi bi-ui-checks fs-4 mt-2 mb-1"></i>
|
<i class="bi bi-ui-checks fs-4 mt-2 mb-1"></i>
|
||||||
|
|||||||
@@ -1,21 +1,25 @@
|
|||||||
|
{{ range . }}
|
||||||
<div class="input-group input-group-lg mb-1">
|
<div class="input-group input-group-lg mb-1">
|
||||||
<div class="input-group-text py-1 px-2">
|
<div class="input-group-text py-1 px-2">
|
||||||
<input class="form-check-input fs-4 border-secondary mt-0" type="checkbox" value="" onchange="updateSelectedPlayers(this)">
|
<input class="form-check-input fs-4 border-secondary mt-0" type="checkbox" value="" onchange="updateSelectedPlayers(this)">
|
||||||
</div>
|
</div>
|
||||||
<span class="form-control py-2 px-3" style="width: 10em">%s</span>
|
<span class="form-control py-2 px-3" style="width: 10em">{{ .PlayerName }}</span>
|
||||||
<div class="form-control text-center px-2 text-secondary">
|
<div class="form-control text-center px-2 text-secondary">
|
||||||
<i class="bi bi-trophy me-3 text-warning"></i><span id="quickScore" class="text-secondary-emphasis">%s</span>
|
<i class="bi bi-trophy me-3 text-warning"></i><span id="quickScore" class="text-secondary-emphasis">{{ .Score }}</span>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-outline-secondary" hx-get="/score/%s" hx-target="previous #quickScore" onclick="singleCalcSpinner(this)">
|
<button type="button" class="btn btn-outline-secondary" hx-get="/score/{{ .PlayerID }}" hx-target="previous #quickScore" onclick="singleCalcSpinner(this)">
|
||||||
<i class="bi bi-calculator text-info"></i>
|
<i class="bi bi-calculator text-info"></i>
|
||||||
</button>
|
</button>
|
||||||
|
{{ if not (eq .UserRole "READER") }}
|
||||||
<button class="btn btn-outline-secondary text-secondary-emphasis dropdown-toggle py-1" type="button" data-bs-toggle="dropdown"></button>
|
<button class="btn btn-outline-secondary text-secondary-emphasis dropdown-toggle py-1" type="button" data-bs-toggle="dropdown"></button>
|
||||||
<ul class="dropdown-menu dropdown-menu-end">
|
<ul class="dropdown-menu dropdown-menu-end">
|
||||||
<li><a class="dropdown-item text-primary fs-5" href="#" data-bs-toggle="modal" data-bs-id="%d" data-bs-target="#editPlayerModal">
|
<li><button class="dropdown-item text-primary fs-5" data-bs-toggle="modal" data-bs-id="{{ .PlayerID }}" data-bs-target="#editPlayerModal">
|
||||||
<i class="bi bi-person-gear fs-4 me-2"></i>Bearbeiten
|
<i class="bi bi-person-gear fs-4 me-2"></i>Bearbeiten
|
||||||
</a></li>
|
</button></li>
|
||||||
<li><a class="dropdown-item text-danger fs-5" href="#" data-bs-toggle="modal" data-bs-id="%d" data-bs-target="#deletePlayerModal">
|
<li><button class="dropdown-item text-danger fs-5" data-bs-toggle="modal" data-bs-id="{{ .PlayerID }}" data-bs-target="#deletePlayerModal">
|
||||||
<i class="bi bi-person-dash fs-4 me-2"></i>Löschen
|
<i class="bi bi-person-dash fs-4 me-2"></i>Löschen
|
||||||
</a></li>
|
</button></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
{{ end }}
|
||||||
@@ -2,6 +2,7 @@ package utils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"InfantrySkillCalculator/models"
|
"InfantrySkillCalculator/models"
|
||||||
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -9,3 +10,8 @@ import (
|
|||||||
var GinWriter io.Writer = nil
|
var GinWriter io.Writer = nil
|
||||||
var GameMetrics models.GameMetrics
|
var GameMetrics models.GameMetrics
|
||||||
var PlayerCacheLifetime = 24 * time.Hour
|
var PlayerCacheLifetime = 24 * time.Hour
|
||||||
|
|
||||||
|
var MainPageTemplates *template.Template
|
||||||
|
var LoginPageTemplates *template.Template
|
||||||
|
var RegisterPageTemplates *template.Template
|
||||||
|
var PlayerItemTemplate *template.Template
|
||||||
|
|||||||
Reference in New Issue
Block a user