big ole refactor

This commit is contained in:
2026-02-14 19:48:59 +11:00
parent e34bec2f9d
commit f9283c0563
66 changed files with 989 additions and 1114 deletions

View File

@@ -8,10 +8,8 @@ import (
"time"
"git.haelnorr.com/h/golib/hws"
"git.haelnorr.com/h/oslstats/internal/auditlog"
"git.haelnorr.com/h/oslstats/internal/db"
"git.haelnorr.com/h/oslstats/internal/roles"
"git.haelnorr.com/h/oslstats/internal/throw"
"git.haelnorr.com/h/oslstats/internal/validation"
adminview "git.haelnorr.com/h/oslstats/internal/view/adminview"
"github.com/pkg/errors"
@@ -19,20 +17,15 @@ import (
)
// AdminRoles renders the full admin dashboard page with roles section
func AdminRoles(s *hws.Server, conn *bun.DB) http.Handler {
func AdminRoles(s *hws.Server, conn *db.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var pageOpts *db.PageOpts
if r.Method == "GET" {
pageOpts = pageOptsFromQuery(s, w, r)
} else {
pageOpts = pageOptsFromForm(s, w, r)
}
if pageOpts == nil {
pageOpts, ok := db.GetPageOpts(s, w, r)
if !ok {
return
}
var rolesList *db.List[db.Role]
if ok := db.WithReadTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) {
if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
var err error
rolesList, err = db.GetRoles(ctx, tx, pageOpts)
if err != nil {
@@ -59,7 +52,7 @@ func AdminRoleCreateForm(s *hws.Server) http.Handler {
}
// AdminRoleCreate creates a new role
func AdminRoleCreate(s *hws.Server, conn *bun.DB, audit *auditlog.Logger) http.Handler {
func AdminRoleCreate(s *hws.Server, conn *db.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
getter, ok := validation.ParseFormOrNotify(s, w, r)
if !ok {
@@ -74,14 +67,14 @@ func AdminRoleCreate(s *hws.Server, conn *bun.DB, audit *auditlog.Logger) http.H
return
}
pageOpts := pageOptsFromForm(s, w, r)
if pageOpts == nil {
pageOpts, ok := db.GetPageOpts(s, w, r)
if !ok {
return
}
var rolesList *db.List[db.Role]
var newRole *db.Role
if ok := db.WithNotifyTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) {
if ok := conn.WithNotifyTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
newRole = &db.Role{
Name: roles.Role(name),
DisplayName: displayName,
@@ -90,9 +83,9 @@ func AdminRoleCreate(s *hws.Server, conn *bun.DB, audit *auditlog.Logger) http.H
CreatedAt: time.Now().Unix(),
}
err := db.Insert(tx, newRole).WithAudit(r, audit.Callback()).Exec(ctx)
err := db.CreateRole(ctx, tx, newRole, db.NewAudit(r, nil))
if err != nil {
return false, errors.Wrap(err, "db.Insert")
return false, errors.Wrap(err, "db.CreateRole")
}
rolesList, err = db.GetRoles(ctx, tx, pageOpts)
@@ -110,7 +103,7 @@ func AdminRoleCreate(s *hws.Server, conn *bun.DB, audit *auditlog.Logger) http.H
}
// AdminRoleManage shows the role management modal with details and actions
func AdminRoleManage(s *hws.Server, conn *bun.DB) http.Handler {
func AdminRoleManage(s *hws.Server, conn *db.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
roleIDStr := r.PathValue("id")
roleID, err := strconv.Atoi(roleIDStr)
@@ -120,7 +113,7 @@ func AdminRoleManage(s *hws.Server, conn *bun.DB) http.Handler {
}
var role *db.Role
if ok := db.WithNotifyTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) {
if ok := conn.WithNotifyTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
var err error
role, err = db.GetRoleByID(ctx, tx, roleID)
if err != nil {
@@ -139,7 +132,7 @@ func AdminRoleManage(s *hws.Server, conn *bun.DB) http.Handler {
}
// AdminRoleDeleteConfirm shows the delete confirmation dialog
func AdminRoleDeleteConfirm(s *hws.Server, conn *bun.DB) http.Handler {
func AdminRoleDeleteConfirm(s *hws.Server, conn *db.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
roleIDStr := r.PathValue("id")
roleID, err := strconv.Atoi(roleIDStr)
@@ -149,7 +142,7 @@ func AdminRoleDeleteConfirm(s *hws.Server, conn *bun.DB) http.Handler {
}
var role *db.Role
if ok := db.WithNotifyTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) {
if ok := conn.WithNotifyTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
var err error
role, err = db.GetRoleByID(ctx, tx, roleID)
if err != nil {
@@ -168,7 +161,7 @@ func AdminRoleDeleteConfirm(s *hws.Server, conn *bun.DB) http.Handler {
}
// AdminRoleDelete deletes a role
func AdminRoleDelete(s *hws.Server, conn *bun.DB, audit *auditlog.Logger) http.Handler {
func AdminRoleDelete(s *hws.Server, conn *db.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
roleIDStr := r.PathValue("id")
roleID, err := strconv.Atoi(roleIDStr)
@@ -177,13 +170,13 @@ func AdminRoleDelete(s *hws.Server, conn *bun.DB, audit *auditlog.Logger) http.H
return
}
pageOpts := pageOptsFromForm(s, w, r)
if pageOpts == nil {
pageOpts, ok := db.GetPageOpts(s, w, r)
if !ok {
return
}
var rolesList *db.List[db.Role]
if ok := db.WithNotifyTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) {
if ok := conn.WithNotifyTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
// First check if role exists and get its details
role, err := db.GetRoleByID(ctx, tx, roleID)
if err != nil {
@@ -199,9 +192,9 @@ func AdminRoleDelete(s *hws.Server, conn *bun.DB, audit *auditlog.Logger) http.H
}
// Delete the role with audit logging
err = db.DeleteByID[db.Role](tx, roleID).WithAudit(r, audit.Callback()).Delete(ctx)
err = db.DeleteRole(ctx, tx, roleID, db.NewAudit(r, nil))
if err != nil {
return false, errors.Wrap(err, "db.DeleteByID")
return false, errors.Wrap(err, "db.DeleteRole")
}
// Reload roles
@@ -220,7 +213,7 @@ func AdminRoleDelete(s *hws.Server, conn *bun.DB, audit *auditlog.Logger) http.H
}
// AdminRolePermissionsModal shows the permissions management modal for a role
func AdminRolePermissionsModal(s *hws.Server, conn *bun.DB) http.Handler {
func AdminRolePermissionsModal(s *hws.Server, conn *db.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
roleIDStr := r.PathValue("id")
roleID, err := strconv.Atoi(roleIDStr)
@@ -234,12 +227,12 @@ func AdminRolePermissionsModal(s *hws.Server, conn *bun.DB) http.Handler {
var groupedPerms []adminview.PermissionsByResource
var rolePermIDs map[int]bool
if ok := db.WithNotifyTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) {
if ok := conn.WithNotifyTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
// Load role with permissions
var err error
role, err = db.GetRoleWithPermissions(ctx, tx, roleID)
role, err = db.GetRoleByID(ctx, tx, roleID)
if err != nil {
return false, errors.Wrap(err, "db.GetRoleWithPermissions")
return false, errors.Wrap(err, "db.GetRoleByID")
}
if role == nil {
return false, errors.New("role not found")
@@ -283,7 +276,7 @@ func AdminRolePermissionsModal(s *hws.Server, conn *bun.DB) http.Handler {
}
// AdminRolePermissionsUpdate updates the permissions for a role
func AdminRolePermissionsUpdate(s *hws.Server, conn *bun.DB, audit *auditlog.Logger) http.Handler {
func AdminRolePermissionsUpdate(s *hws.Server, conn *db.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
roleIDStr := r.PathValue("id")
roleID, err := strconv.Atoi(roleIDStr)
@@ -291,7 +284,6 @@ func AdminRolePermissionsUpdate(s *hws.Server, conn *bun.DB, audit *auditlog.Log
w.WriteHeader(http.StatusBadRequest)
return
}
user := db.CurrentUser(r.Context())
getter, ok := validation.ParseFormOrNotify(s, w, r)
if !ok {
@@ -304,80 +296,24 @@ func AdminRolePermissionsUpdate(s *hws.Server, conn *bun.DB, audit *auditlog.Log
return
}
selectedPermIDs := make(map[int]bool)
for _, id := range permissionIDs {
selectedPermIDs[id] = true
}
pageOpts := pageOptsFromForm(s, w, r)
if pageOpts == nil {
pageOpts, ok := db.GetPageOpts(s, w, r)
if !ok {
return
}
var rolesList *db.List[db.Role]
if ok := db.WithWriteTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) {
// Get role with current permissions
role, err := db.GetRoleWithPermissions(ctx, tx, roleID)
if ok := conn.WithWriteTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
role, err := db.GetRoleByID(ctx, tx, roleID)
if err != nil {
return false, errors.Wrap(err, "db.GetRoleWithPermissions")
return false, errors.Wrap(err, "db.GetRoleByID")
}
if role == nil {
throw.NotFound(s, w, r, "Role not found")
w.WriteHeader(http.StatusBadRequest)
return false, nil
}
// Get all permissions to know what exists
allPermissions, err := db.ListAllPermissions(ctx, tx)
err = role.UpdatePermissions(ctx, tx, permissionIDs, db.NewAudit(r, nil))
if err != nil {
return false, errors.Wrap(err, "db.ListAllPermissions")
}
// Build map of current permissions
currentPermIDs := make(map[int]bool)
for _, perm := range role.Permissions {
currentPermIDs[perm.ID] = true
}
var addedPerms []string
var removedPerms []string
// Determine what to add and remove
for _, perm := range allPermissions {
hasNow := currentPermIDs[perm.ID]
shouldHave := selectedPermIDs[perm.ID]
if shouldHave && !hasNow {
// Add permission
err := db.AddPermissionToRole(ctx, tx, roleID, perm.ID)
if err != nil {
return false, errors.Wrap(err, "db.AddPermissionToRole")
}
addedPerms = append(addedPerms, string(perm.Name))
} else if !shouldHave && hasNow {
// Remove permission
err := db.RemovePermissionFromRole(ctx, tx, roleID, perm.ID)
if err != nil {
return false, errors.Wrap(err, "db.RemovePermissionFromRole")
}
removedPerms = append(removedPerms, string(perm.Name))
}
}
// Log the permission changes
if len(addedPerms) > 0 || len(removedPerms) > 0 {
details := map[string]any{
"role_name": string(role.Name),
}
if len(addedPerms) > 0 {
details["added_permissions"] = addedPerms
}
if len(removedPerms) > 0 {
details["removed_permissions"] = removedPerms
}
err = audit.LogSuccess(ctx, tx, user, "update", "role_permissions", roleID, details, r)
if err != nil {
return false, errors.Wrap(err, "audit.LogSuccess")
}
return false, errors.Wrap(err, "role.UpdatePermissions")
}
// Reload roles