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 }