217 lines
5.4 KiB
Go
217 lines
5.4 KiB
Go
package routes
|
|
|
|
import (
|
|
"dk/internal/components"
|
|
"dk/internal/database"
|
|
"dk/internal/helpers"
|
|
"dk/internal/models/news"
|
|
"dk/internal/models/users"
|
|
"fmt"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
sushi "git.sharkk.net/Sharkk/Sushi"
|
|
"git.sharkk.net/Sharkk/Sushi/auth"
|
|
"git.sharkk.net/Sharkk/Sushi/password"
|
|
)
|
|
|
|
func RegisterAdminRoutes(app *sushi.App) {
|
|
group := app.Group("/admin")
|
|
group.Use(auth.RequireAuth())
|
|
group.Use(func(ctx sushi.Ctx, next func()) {
|
|
if ctx.GetCurrentUser().(*users.User).Auth < 4 {
|
|
ctx.Redirect("/")
|
|
return
|
|
}
|
|
next()
|
|
})
|
|
|
|
group.Get("/", adminIndex)
|
|
group.Get("/news", adminNewsForm)
|
|
group.Post("/news", adminNewsCreate)
|
|
group.Get("/users", adminUsersIndex)
|
|
group.Get("/users/:id", adminUserEdit)
|
|
group.Post("/users/:id", adminUserUpdate)
|
|
}
|
|
|
|
func adminIndex(ctx sushi.Ctx) {
|
|
var m runtime.MemStats
|
|
runtime.ReadMemStats(&m)
|
|
|
|
components.RenderAdminPage(ctx, "", "admin/home.html", map[string]any{
|
|
"alloc_mb": bToMb(m.Alloc),
|
|
"total_alloc_mb": bToMb(m.TotalAlloc),
|
|
"sys_mb": bToMb(m.Sys),
|
|
"heap_alloc_mb": bToMb(m.HeapAlloc),
|
|
"heap_sys_mb": bToMb(m.HeapSys),
|
|
"heap_released_mb": bToMb(m.HeapReleased),
|
|
"gc_cycles": m.NumGC,
|
|
"gc_pause_total": m.PauseTotalNs / 1000000, // ms
|
|
"goroutines": runtime.NumGoroutine(),
|
|
"cpu_cores": runtime.NumCPU(),
|
|
"go_version": runtime.Version(),
|
|
})
|
|
}
|
|
|
|
func adminNewsForm(ctx sushi.Ctx) {
|
|
components.RenderAdminPage(ctx, "", "admin/news.html", map[string]any{})
|
|
}
|
|
|
|
func adminNewsCreate(ctx sushi.Ctx) {
|
|
sess := ctx.GetCurrentSession()
|
|
content := strings.TrimSpace(ctx.Form("content").String())
|
|
|
|
if content == "" {
|
|
sess.SetFlash("error", "Content cannot be empty")
|
|
ctx.Redirect("/admin/news")
|
|
return
|
|
}
|
|
|
|
user := ctx.GetCurrentUser().(*users.User)
|
|
newsPost := &news.News{
|
|
Author: user.ID,
|
|
Content: content,
|
|
Posted: time.Now().Unix(),
|
|
}
|
|
|
|
if err := newsPost.Insert(); err != nil {
|
|
sess.SetFlash("error", "Failed to create news post")
|
|
ctx.Redirect("/admin/news")
|
|
return
|
|
}
|
|
|
|
sess.SetFlash("success", "News post created successfully")
|
|
ctx.Redirect("/admin")
|
|
}
|
|
|
|
func adminUsersIndex(ctx sushi.Ctx) {
|
|
pagination := helpers.Pagination{
|
|
Page: max(int(ctx.QueryArgs().GetUintOrZero("page")), 1),
|
|
PerPage: 30,
|
|
}
|
|
|
|
type UserData struct {
|
|
ID int
|
|
Username string
|
|
Email string
|
|
Level int
|
|
Auth int
|
|
ClassID int
|
|
ClassName string
|
|
HP int
|
|
MaxHP int
|
|
Registered int64
|
|
LastOnline int64
|
|
}
|
|
|
|
var userList []*UserData
|
|
err := database.Select(&userList, `
|
|
SELECT u.id, u.username, u.email, u.level, u.auth, u.class_id,
|
|
COALESCE(c.name, 'Unknown') as class_name,
|
|
u.hp, u.max_hp, u.registered, u.last_online
|
|
FROM users u
|
|
LEFT JOIN classes c ON u.class_id = c.id
|
|
ORDER BY u.id ASC
|
|
LIMIT %d OFFSET %d`, pagination.PerPage, pagination.Offset())
|
|
|
|
if err != nil {
|
|
fmt.Printf("Error getting user list for admin index: %s", err.Error())
|
|
userList = make([]*UserData, 0)
|
|
}
|
|
|
|
type CountResult struct{ Count int }
|
|
var result CountResult
|
|
database.Get(&result, "SELECT COUNT(*) as count FROM users")
|
|
pagination.Total = result.Count
|
|
|
|
components.RenderAdminPage(ctx, "User Management", "admin/users/index.html", map[string]any{
|
|
"users": userList,
|
|
"currentPage": pagination.Page,
|
|
"totalPages": pagination.TotalPages(),
|
|
"hasNext": pagination.HasNext(),
|
|
"hasPrev": pagination.HasPrev(),
|
|
})
|
|
}
|
|
|
|
func adminUserEdit(ctx sushi.Ctx) {
|
|
sess := ctx.GetCurrentSession()
|
|
id := ctx.Param("id").Int()
|
|
|
|
user, err := users.Find(id)
|
|
if err != nil {
|
|
sess.SetFlash("error", fmt.Sprintf("User %d not found", id))
|
|
ctx.Redirect("/admin/users")
|
|
return
|
|
}
|
|
|
|
components.RenderAdminPage(ctx, fmt.Sprintf("Edit User: %s", user.Username), "admin/users/edit.html", map[string]any{
|
|
"user": user,
|
|
})
|
|
}
|
|
|
|
func adminUserUpdate(ctx sushi.Ctx) {
|
|
sess := ctx.GetCurrentSession()
|
|
id := ctx.Param("id").Int()
|
|
|
|
user, err := users.Find(id)
|
|
if err != nil {
|
|
sess.SetFlash("error", fmt.Sprintf("User %d not found", id))
|
|
ctx.Redirect("/admin/users")
|
|
return
|
|
}
|
|
|
|
// Update fields
|
|
username := strings.TrimSpace(ctx.Form("username").String())
|
|
email := strings.TrimSpace(ctx.Form("email").String())
|
|
level, _ := strconv.Atoi(ctx.Form("level").String())
|
|
auth, _ := strconv.Atoi(ctx.Form("auth").String())
|
|
hp, _ := strconv.Atoi(ctx.Form("hp").String())
|
|
maxHP, _ := strconv.Atoi(ctx.Form("max_hp").String())
|
|
newPassword := strings.TrimSpace(ctx.Form("new_password").String())
|
|
|
|
if username == "" || email == "" {
|
|
sess.SetFlash("error", "Username and email are required")
|
|
ctx.Redirect(fmt.Sprintf("/admin/users/%d", id))
|
|
return
|
|
}
|
|
|
|
user.Username = username
|
|
user.Email = email
|
|
user.Level = level
|
|
user.Auth = auth
|
|
user.HP = hp
|
|
user.MaxHP = maxHP
|
|
|
|
if newPassword != "" {
|
|
user.Password = password.HashPassword(newPassword)
|
|
}
|
|
|
|
fields := map[string]any{
|
|
"username": user.Username,
|
|
"email": user.Email,
|
|
"level": user.Level,
|
|
"auth": user.Auth,
|
|
"hp": user.HP,
|
|
"max_hp": user.MaxHP,
|
|
}
|
|
|
|
if newPassword != "" {
|
|
fields["password"] = user.Password
|
|
}
|
|
|
|
if err := database.Update("users", fields, "id", id); err != nil {
|
|
sess.SetFlash("error", "Failed to update user")
|
|
ctx.Redirect(fmt.Sprintf("/admin/users/%d", id))
|
|
return
|
|
}
|
|
|
|
sess.SetFlash("success", fmt.Sprintf("User %s updated successfully", user.Username))
|
|
ctx.Redirect("/admin/users")
|
|
}
|
|
|
|
func bToMb(b uint64) uint64 {
|
|
return b / 1024 / 1024
|
|
}
|