Add admin-route & dropdown-menu. Add clear-cache & create-code. Adjustments for activation-code and user models. Add sweet-alert for admin-tools.
This commit is contained in:
2
.idea/InfantrySkillCalculator.iml
generated
2
.idea/InfantrySkillCalculator.iml
generated
@@ -7,5 +7,7 @@
|
|||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="bootstrap" level="application" />
|
<orderEntry type="library" name="bootstrap" level="application" />
|
||||||
<orderEntry type="library" name="bootstrap-icons" level="application" />
|
<orderEntry type="library" name="bootstrap-icons" level="application" />
|
||||||
|
<orderEntry type="library" name="sweetalert2" level="application" />
|
||||||
|
<orderEntry type="library" name="@sweetalert2/theme-dark" level="application" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
||||||
3
.idea/inspectionProfiles/Project_Default.xml
generated
3
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -4,12 +4,13 @@
|
|||||||
<inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
|
<inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
<option name="myValues">
|
<option name="myValues">
|
||||||
<value>
|
<value>
|
||||||
<list size="5">
|
<list size="6">
|
||||||
<item index="0" class="java.lang.String" itemvalue="hx-get" />
|
<item index="0" class="java.lang.String" itemvalue="hx-get" />
|
||||||
<item index="1" class="java.lang.String" itemvalue="hx-target" />
|
<item index="1" class="java.lang.String" itemvalue="hx-target" />
|
||||||
<item index="2" class="java.lang.String" itemvalue="hx-vals" />
|
<item index="2" class="java.lang.String" itemvalue="hx-vals" />
|
||||||
<item index="3" class="java.lang.String" itemvalue="hx-post" />
|
<item index="3" class="java.lang.String" itemvalue="hx-post" />
|
||||||
<item index="4" class="java.lang.String" itemvalue="hx-trigger" />
|
<item index="4" class="java.lang.String" itemvalue="hx-trigger" />
|
||||||
|
<item index="5" class="java.lang.String" itemvalue="hx-swap" />
|
||||||
</list>
|
</list>
|
||||||
</value>
|
</value>
|
||||||
</option>
|
</option>
|
||||||
|
|||||||
2
.idea/jsLibraryMappings.xml
generated
2
.idea/jsLibraryMappings.xml
generated
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="JavaScriptLibraryMappings">
|
<component name="JavaScriptLibraryMappings">
|
||||||
<file url="PROJECT" libraries="{bootstrap, bootstrap-icons}" />
|
<file url="PROJECT" libraries="{@sweetalert2/theme-dark, bootstrap, bootstrap-icons, sweetalert2}" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
61
auth.go
61
auth.go
@@ -50,37 +50,56 @@ func AuthRequired() gin.HandlerFunc {
|
|||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
session, _ := utils.Store.Get(c.Request, utils.LoginSessionName)
|
session, _ := utils.Store.Get(c.Request, utils.LoginSessionName)
|
||||||
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth || !controllers.IsUserEnabled(session.Values["username"].(string)) {
|
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth || !controllers.IsUserEnabled(session.Values["username"].(string)) {
|
||||||
session.Options.MaxAge = -1
|
redirectToLogin(c)
|
||||||
err := session.Save(c.Request, c.Writer)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
c.Redirect(http.StatusFound, "/login")
|
|
||||||
c.Abort()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AdminAuthRequired() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
session, _ := utils.Store.Get(c.Request, utils.LoginSessionName)
|
||||||
|
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
||||||
|
redirectToLogin(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
username, ok := session.Values["username"].(string)
|
||||||
|
if !ok || !controllers.IsUserEnabled(username) || !controllers.IsUserAdmin(username) {
|
||||||
|
redirectToLogin(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUserAdmin(c *gin.Context) bool {
|
||||||
|
session, _ := utils.Store.Get(c.Request, utils.LoginSessionName)
|
||||||
|
username, ok := session.Values["username"].(string)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return controllers.IsUserAdmin(username)
|
||||||
|
}
|
||||||
|
|
||||||
|
func redirectToLogin(c *gin.Context) {
|
||||||
|
session, _ := utils.Store.Get(c.Request, utils.LoginSessionName)
|
||||||
|
session.Options.MaxAge = -1
|
||||||
|
err := session.Save(c.Request, c.Writer)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
c.Redirect(http.StatusFound, "/login")
|
||||||
|
c.Abort()
|
||||||
|
}
|
||||||
|
|
||||||
func isValidCode(code string) bool {
|
func isValidCode(code string) bool {
|
||||||
var activationCode models.ActivationCode
|
var activationCode models.ActivationCode
|
||||||
if err := models.DB.Where("code = ?", code).First(&activationCode).Error; err != nil {
|
if err := models.DB.Where("code = ?", code).First(&activationCode).Error; err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if activationCode.Code == code && !activationCode.Used {
|
return activationCode.Code == code && activationCode.UsedForUsername == ""
|
||||||
models.DB.Model(&activationCode).Updates(map[string]interface{}{
|
|
||||||
"Code": code,
|
|
||||||
"Used": true,
|
|
||||||
})
|
|
||||||
|
|
||||||
newCode := utils.GenerateActivationCode()
|
|
||||||
newCodeObj := models.ActivationCode{Code: newCode, Used: false}
|
|
||||||
models.DB.Create(&newCodeObj)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"InfantrySkillCalculator/utils"
|
"InfantrySkillCalculator/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -49,8 +50,6 @@ func AddCache(c *gin.Context) {
|
|||||||
|
|
||||||
cache := models.PlayerCache{CacheDate: input.CacheDate, PlayerID: input.PlayerID, Score: input.Score, Game: input.Game}
|
cache := models.PlayerCache{CacheDate: input.CacheDate, PlayerID: input.PlayerID, Score: input.Score, Game: input.Game}
|
||||||
|
|
||||||
//CreateOrUpdateCache(cache)
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, cache)
|
c.JSON(http.StatusOK, cache)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,15 +67,11 @@ func UpdateCacheByPlayerID(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//log.Println("Updating cache for player #" + utils.UintToString(cache.PlayerID))
|
|
||||||
|
|
||||||
models.DB.Model(&cache).Updates(map[string]interface{}{
|
models.DB.Model(&cache).Updates(map[string]interface{}{
|
||||||
"CacheDate": input.CacheDate,
|
"CacheDate": input.CacheDate,
|
||||||
"Score": input.Score,
|
"Score": input.Score,
|
||||||
})
|
})
|
||||||
|
|
||||||
//UpdateCacheTimestamp()
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, cache)
|
c.JSON(http.StatusOK, cache)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,11 +85,21 @@ func DeleteCacheByPlayerID(c *gin.Context) {
|
|||||||
|
|
||||||
models.DB.Delete(&cache)
|
models.DB.Delete(&cache)
|
||||||
|
|
||||||
//UpdateCacheTimestamp()
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, true)
|
c.JSON(http.StatusOK, true)
|
||||||
|
}
|
||||||
|
|
||||||
//log.Println("Deleted cache for player #" + utils.UintToString(cache.PlayerID))
|
// DeleteAllCaches DELETE /cache
|
||||||
|
func DeleteAllCaches(c *gin.Context) {
|
||||||
|
var caches []models.PlayerCache
|
||||||
|
if err := models.DB.
|
||||||
|
Session(&gorm.Session{AllowGlobalUpdate: true}).
|
||||||
|
Clauses(clause.Returning{}).
|
||||||
|
Delete(&caches).Error; err != nil {
|
||||||
|
c.String(http.StatusBadRequest, "Purge failed! Error: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.String(http.StatusOK, "Purged "+utils.UintToString(uint(len(caches)))+" caches!")
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindCacheGin(out interface{}, c *gin.Context) *gorm.DB {
|
func FindCacheGin(out interface{}, c *gin.Context) *gorm.DB {
|
||||||
|
|||||||
37
controllers/code_controller.go
Normal file
37
controllers/code_controller.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"InfantrySkillCalculator/models"
|
||||||
|
"InfantrySkillCalculator/utils"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateCode POST /code
|
||||||
|
func CreateCode(c *gin.Context) {
|
||||||
|
userRole, ok := c.GetPostForm("user_role")
|
||||||
|
if !ok {
|
||||||
|
c.String(http.StatusBadRequest, "Missing user role")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var role models.Role
|
||||||
|
switch userRole {
|
||||||
|
case "ADMIN":
|
||||||
|
role = models.AdminRole
|
||||||
|
case "AUTHOR":
|
||||||
|
role = models.AuthorRole
|
||||||
|
case "READER":
|
||||||
|
role = models.ReaderRole
|
||||||
|
default:
|
||||||
|
c.String(http.StatusInternalServerError, "Invalid user role: "+userRole)
|
||||||
|
}
|
||||||
|
|
||||||
|
newCode := utils.GenerateActivationCode()
|
||||||
|
newCodeObj := models.ActivationCode{Code: newCode, UserRole: role}
|
||||||
|
if err := models.DB.Create(&newCodeObj).Error; err != nil {
|
||||||
|
c.String(http.StatusInternalServerError, "Failed to create new code: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.String(http.StatusOK, "Activation code for role '"+string(role)+"': "+newCode)
|
||||||
|
}
|
||||||
@@ -7,17 +7,39 @@ import (
|
|||||||
|
|
||||||
func CreateUser(username string, hashedPassword string, enabled bool, usedCode string) {
|
func CreateUser(username string, hashedPassword string, enabled bool, usedCode string) {
|
||||||
user := models.User{Username: username, Password: hashedPassword, Enabled: enabled}
|
user := models.User{Username: username, Password: hashedPassword, Enabled: enabled}
|
||||||
models.DB.Create(&user)
|
err := models.DB.Create(&user).Error
|
||||||
|
|
||||||
err := models.DB.Model(&models.ActivationCode{}).
|
|
||||||
Where("code = ?", usedCode).
|
|
||||||
Update("Used", true).Error
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatalf("Error while creating user: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var code models.ActivationCode
|
||||||
|
err = models.DB.
|
||||||
|
Model(&models.ActivationCode{}).
|
||||||
|
Where("code = ?", usedCode).
|
||||||
|
First(&code).Error
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error while getting activation code: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
code.UsedForUsername = username
|
||||||
|
err = models.DB.Save(&code).Error
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error while updating activation code: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
user.UserRole = code.UserRole
|
||||||
|
err = models.DB.Save(&user).Error
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error while updating user role: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var bf2042 models.Game
|
var bf2042 models.Game
|
||||||
models.DB.Where("tag = ?", "BF2042").First(&bf2042)
|
err = models.DB.
|
||||||
|
Where("tag = ?", "BF2042").
|
||||||
|
First(&bf2042).Error
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error while getting game: %v", err)
|
||||||
|
}
|
||||||
userSettings := models.UserSettings{
|
userSettings := models.UserSettings{
|
||||||
Username: username,
|
Username: username,
|
||||||
ActiveGameID: bf2042.ID,
|
ActiveGameID: bf2042.ID,
|
||||||
@@ -33,3 +55,13 @@ func IsUserEnabled(username string) bool {
|
|||||||
models.DB.Where("username = ?", username).First(&user)
|
models.DB.Where("username = ?", username).First(&user)
|
||||||
return user.Enabled
|
return user.Enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsUserAdmin(username string) bool {
|
||||||
|
var user models.User
|
||||||
|
err := models.DB.Where("username = ?", username).First(&user).Error
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return user.UserRole == models.AdminRole
|
||||||
|
}
|
||||||
|
|||||||
9
main.go
9
main.go
@@ -22,6 +22,8 @@ func main() {
|
|||||||
router.LoadHTMLGlob("templates/**/*")
|
router.LoadHTMLGlob("templates/**/*")
|
||||||
protected := router.Group("/")
|
protected := router.Group("/")
|
||||||
protected.Use(AuthRequired())
|
protected.Use(AuthRequired())
|
||||||
|
admin := router.Group("/admin")
|
||||||
|
admin.Use(AdminAuthRequired())
|
||||||
|
|
||||||
models.ConnectDatabase()
|
models.ConnectDatabase()
|
||||||
|
|
||||||
@@ -35,6 +37,10 @@ func main() {
|
|||||||
gin.LoggerWithWriter(utils.GinWriter),
|
gin.LoggerWithWriter(utils.GinWriter),
|
||||||
gin.Recovery(),
|
gin.Recovery(),
|
||||||
)
|
)
|
||||||
|
admin.Use(
|
||||||
|
gin.LoggerWithWriter(utils.GinWriter),
|
||||||
|
gin.Recovery(),
|
||||||
|
)
|
||||||
|
|
||||||
router.Static("/static", "./static")
|
router.Static("/static", "./static")
|
||||||
|
|
||||||
@@ -67,6 +73,9 @@ func main() {
|
|||||||
protected.GET("/settings", controllers.GetSettings)
|
protected.GET("/settings", controllers.GetSettings)
|
||||||
protected.PATCH("/settings", controllers.UpdateSettings)
|
protected.PATCH("/settings", controllers.UpdateSettings)
|
||||||
|
|
||||||
|
admin.GET("/clear_cache", controllers.DeleteAllCaches)
|
||||||
|
admin.POST("/create_code", controllers.CreateCode)
|
||||||
|
|
||||||
log.Println("Running on 8000...")
|
log.Println("Running on 8000...")
|
||||||
log.Fatal(router.Run(":8000"))
|
log.Fatal(router.Run(":8000"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
type ActivationCode struct {
|
type ActivationCode struct {
|
||||||
Code string
|
Code string `gorm:"primary_key"`
|
||||||
Used bool
|
UserRole Role
|
||||||
|
UsedForUsername string
|
||||||
|
UsedFor User `gorm:"foreignKey:UsedForUsername"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ func ConnectDatabase() {
|
|||||||
var code ActivationCode
|
var code ActivationCode
|
||||||
if err := database.First(&code).Error; err != nil {
|
if err := database.First(&code).Error; err != nil {
|
||||||
firstCode := utils.GenerateActivationCode()
|
firstCode := utils.GenerateActivationCode()
|
||||||
database.Create(&ActivationCode{Code: firstCode, Used: false})
|
database.Create(&ActivationCode{Code: firstCode, UserRole: AdminRole})
|
||||||
log.Println("Created first activation code: " + firstCode)
|
log.Println("Created first activation code with ADMIN role:\n" + firstCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = database.AutoMigrate(&Game{})
|
err = database.AutoMigrate(&Game{})
|
||||||
|
|||||||
@@ -4,4 +4,13 @@ type User struct {
|
|||||||
Username string `json:"username" gorm:"primary_key"`
|
Username string `json:"username" gorm:"primary_key"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Enabled bool `json:"enabled" default:"1"`
|
Enabled bool `json:"enabled" default:"1"`
|
||||||
|
UserRole Role `json:"user_role" default:"READER"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Role string
|
||||||
|
|
||||||
|
const (
|
||||||
|
AdminRole Role = "ADMIN"
|
||||||
|
AuthorRole Role = "AUTHOR"
|
||||||
|
ReaderRole Role = "READER"
|
||||||
|
)
|
||||||
|
|||||||
6
pages.go
6
pages.go
@@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"InfantrySkillCalculator/controllers"
|
"InfantrySkillCalculator/controllers"
|
||||||
"InfantrySkillCalculator/models"
|
|
||||||
"InfantrySkillCalculator/utils"
|
"InfantrySkillCalculator/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"html/template"
|
"html/template"
|
||||||
@@ -32,11 +31,8 @@ func mainPage(c *gin.Context) {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var clans []models.Clan
|
|
||||||
models.DB.Find(&clans)
|
|
||||||
|
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"clans": clans,
|
"isAdmin": isUserAdmin(c),
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tmpl.Execute(c.Writer, data)
|
err = tmpl.Execute(c.Writer, data)
|
||||||
|
|||||||
@@ -144,3 +144,39 @@ function deselectAllPlayers(playerListId) {
|
|||||||
|
|
||||||
checkCounter.innerText = 0;
|
checkCounter.innerText = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function confirmAndTrigger(btn) {
|
||||||
|
Swal.fire({
|
||||||
|
title: btn.innerText,
|
||||||
|
text: 'Do you want to continue?',
|
||||||
|
confirmButtonText: 'Yes',
|
||||||
|
confirmButtonColor: '#dd6b55',
|
||||||
|
denyButtonColor: '#3085d6',
|
||||||
|
icon: 'warning',
|
||||||
|
showDenyButton: true
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
htmx.trigger(btn, 'confirmed');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCodeDialog(btn) {
|
||||||
|
Swal.fire({
|
||||||
|
title: btn.innerText,
|
||||||
|
input: 'select',
|
||||||
|
inputOptions: {
|
||||||
|
'READER': 'Reader',
|
||||||
|
'AUTHOR': 'Author',
|
||||||
|
'ADMIN': 'Admin'
|
||||||
|
},
|
||||||
|
inputPlaceholder: 'Select a role',
|
||||||
|
confirmButtonText: 'Generate',
|
||||||
|
showCancelButton: true
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
btn.setAttribute('hx-vals', '{"user_role": "' + result.value + '"}');
|
||||||
|
htmx.trigger(btn, 'confirmed');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,13 +2,77 @@
|
|||||||
|
|
||||||
<div class="row justify-content-between border-top pt-4 position-relative mb-5">
|
<div class="row justify-content-between border-top pt-4 position-relative mb-5">
|
||||||
<div class="col-auto position-absolute start-0">
|
<div class="col-auto position-absolute start-0">
|
||||||
<a class="btn btn-lg btn-outline-secondary text-secondary-emphasis" href="/logout" data-bs-toggle="tooltip" data-bs-title="Abmelden">
|
<div class="btn-toolbar" role="toolbar">
|
||||||
<i class="bi bi-door-closed"></i>
|
<a class="btn btn-lg btn-outline-secondary text-secondary-emphasis me-2" href="/logout" data-bs-toggle="tooltip" data-bs-title="Abmelden">
|
||||||
</a>
|
<i class="bi bi-door-closed"></i>
|
||||||
<button class="btn btn-lg btn-outline-secondary text-secondary-emphasis" data-bs-toggle="modal" data-bs-target="#settingsModal">
|
</a>
|
||||||
<i class="bi bi-gear-fill me-2"></i>
|
<button class="btn btn-lg btn-outline-secondary text-secondary-emphasis me-2" data-bs-toggle="modal" data-bs-target="#settingsModal">
|
||||||
Einstellungen
|
<i class="bi bi-gear-fill"></i>
|
||||||
</button>
|
</button>
|
||||||
|
{{ if .isAdmin }}
|
||||||
|
<div class="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>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><h6 class="dropdown-header">ACCOUNTS</h6></li>
|
||||||
|
<li>
|
||||||
|
<button class="dropdown-item fs-5 text-info-emphasis fst-italic"
|
||||||
|
hx-post="/admin/create_code" hx-trigger="confirmed" hx-swap="none"
|
||||||
|
onclick="createCodeDialog(this)">
|
||||||
|
Create Code
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button class="dropdown-item fs-5 text-info-emphasis fst-italic"
|
||||||
|
hx-get="/admin/reset_password" hx-trigger="confirmed" hx-swap="none"
|
||||||
|
onclick="confirmAndTrigger(this)">
|
||||||
|
Reset Password
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button class="dropdown-item fs-5 text-info-emphasis fst-italic"
|
||||||
|
hx-get="/admin/disable_account" hx-trigger="confirmed" hx-swap="none"
|
||||||
|
onclick="confirmAndTrigger(this)">
|
||||||
|
Disable Account
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li><hr class="dropdown-divider"></li>
|
||||||
|
<li><h6 class="dropdown-header">TOOLS</h6></li>
|
||||||
|
<li>
|
||||||
|
<button class="dropdown-item fs-5 text-warning-emphasis"
|
||||||
|
hx-get="/admin/clear_cache" hx-trigger="confirmed" hx-swap="none"
|
||||||
|
onclick="confirmAndTrigger(this)">
|
||||||
|
Clear Cache
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button class="dropdown-item fs-5 text-warning-emphasis fst-italic"
|
||||||
|
hx-get="/admin/update_cache" hx-trigger="confirmed" hx-swap="none"
|
||||||
|
onclick="confirmAndTrigger(this)">
|
||||||
|
Update Cache
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li><hr class="dropdown-divider"></li>
|
||||||
|
<li><h6 class="dropdown-header">DANGER ZONE</h6></li>
|
||||||
|
<li>
|
||||||
|
<button class="dropdown-item fs-5 text-danger-emphasis fst-italic"
|
||||||
|
hx-get="/admin/purge_players" hx-trigger="confirmed" hx-swap="none"
|
||||||
|
onclick="confirmAndTrigger(this)">
|
||||||
|
Purge Players
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button class="dropdown-item fs-5 text-danger-emphasis fst-italic"
|
||||||
|
hx-get="/admin/purge_cache" hx-trigger="confirmed" hx-swap="none"
|
||||||
|
onclick="confirmAndTrigger(this)">
|
||||||
|
Purge Clans
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto position-absolute start-50 translate-middle-x">
|
<div class="col-auto position-absolute start-50 translate-middle-x">
|
||||||
<button class="btn btn-lg btn-outline-primary">
|
<button class="btn btn-lg btn-outline-primary">
|
||||||
@@ -24,4 +88,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.body.addEventListener('htmx:afterRequest', function (event) {
|
||||||
|
if (event.detail.pathInfo.requestPath.startsWith("/admin/")) {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Action executed',
|
||||||
|
text: event.detail.xhr.response,
|
||||||
|
icon: event.detail.xhr.status === 200 ? 'success' : 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
@@ -9,6 +9,8 @@
|
|||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/@sweetalert2/theme-dark@4/dark.css" rel="stylesheet">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.js"></script>
|
||||||
<link rel="stylesheet" href="../static/index.css">
|
<link rel="stylesheet" href="../static/index.css">
|
||||||
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
Reference in New Issue
Block a user