package http import ( "encoding/json" "net/http" "github.com/example/storage-appliance/internal/auth" "github.com/go-chi/chi/v5" ) // UsersHandler shows the users management page func (a *App) UsersHandler(w http.ResponseWriter, r *http.Request) { data := templateData(r, map[string]interface{}{ "Title": "User Management", }) if err := templates.ExecuteTemplate(w, "users", data); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } // HXUsersHandler returns HTMX partial for users list func (a *App) HXUsersHandler(w http.ResponseWriter, r *http.Request) { rbacStore := auth.NewRBACStore(a.DB) // Get all users (simplified - in production, you'd want pagination) rows, err := a.DB.QueryContext(r.Context(), `SELECT id, username, created_at FROM users ORDER BY username`) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer rows.Close() type UserWithRoles struct { ID string Username string CreatedAt string Roles []auth.Role } var users []UserWithRoles for rows.Next() { var u UserWithRoles if err := rows.Scan(&u.ID, &u.Username, &u.CreatedAt); err != nil { continue } roles, _ := rbacStore.GetUserRoles(r.Context(), u.ID) u.Roles = roles users = append(users, u) } data := map[string]interface{}{ "Users": users, } if err := templates.ExecuteTemplate(w, "hx_users", data); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } // CreateUserHandler creates a new user func (a *App) CreateUserHandler(w http.ResponseWriter, r *http.Request) { username := r.FormValue("username") password := r.FormValue("password") if username == "" || password == "" { http.Error(w, "username and password required", http.StatusBadRequest) return } userStore := auth.NewUserStore(a.DB) _, err := userStore.CreateUser(r.Context(), username, password) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } // Return HTMX partial or redirect if r.Header.Get("HX-Request") == "true" { w.Header().Set("HX-Refresh", "true") w.WriteHeader(http.StatusOK) } else { http.Redirect(w, r, "/admin/users", http.StatusFound) } } // DeleteUserHandler deletes a user func (a *App) DeleteUserHandler(w http.ResponseWriter, r *http.Request) { userID := chi.URLParam(r, "id") _, err := a.DB.ExecContext(r.Context(), `DELETE FROM users WHERE id = ?`, userID) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if r.Header.Get("HX-Request") == "true" { w.Header().Set("HX-Refresh", "true") w.WriteHeader(http.StatusOK) } else { http.Redirect(w, r, "/admin/users", http.StatusFound) } } // UpdateUserRolesHandler updates roles for a user func (a *App) UpdateUserRolesHandler(w http.ResponseWriter, r *http.Request) { userID := chi.URLParam(r, "id") var req struct { RoleIDs []string `json:"role_ids"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid request", http.StatusBadRequest) return } rbacStore := auth.NewRBACStore(a.DB) // Get current roles currentRoles, _ := rbacStore.GetUserRoles(r.Context(), userID) // Remove all current roles for _, role := range currentRoles { rbacStore.RemoveRoleFromUser(r.Context(), userID, role.ID) } // Add new roles for _, roleID := range req.RoleIDs { rbacStore.AssignRoleToUser(r.Context(), userID, roleID) } if r.Header.Get("HX-Request") == "true" { w.Header().Set("HX-Refresh", "true") w.WriteHeader(http.StatusOK) } else { http.Redirect(w, r, "/admin/users", http.StatusFound) } } // RolesHandler shows the roles management page func (a *App) RolesHandler(w http.ResponseWriter, r *http.Request) { data := templateData(r, map[string]interface{}{ "Title": "Role Management", }) if err := templates.ExecuteTemplate(w, "roles", data); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } // HXRolesHandler returns HTMX partial for roles list func (a *App) HXRolesHandler(w http.ResponseWriter, r *http.Request) { rbacStore := auth.NewRBACStore(a.DB) roles, err := rbacStore.GetAllRoles(r.Context()) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } type RoleWithPermissions struct { auth.Role Permissions []auth.Permission } var rolesWithPerms []RoleWithPermissions for _, role := range roles { rwp := RoleWithPermissions{Role: role} perms, _ := rbacStore.GetRolePermissions(r.Context(), role.ID) rwp.Permissions = perms rolesWithPerms = append(rolesWithPerms, rwp) } data := map[string]interface{}{ "Roles": rolesWithPerms, } if err := templates.ExecuteTemplate(w, "hx_roles", data); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } // CreateRoleHandler creates a new role func (a *App) CreateRoleHandler(w http.ResponseWriter, r *http.Request) { name := r.FormValue("name") description := r.FormValue("description") if name == "" { http.Error(w, "name required", http.StatusBadRequest) return } roleID := name // Using name as ID for simplicity _, err := a.DB.ExecContext(r.Context(), `INSERT INTO roles (id, name, description) VALUES (?, ?, ?)`, roleID, name, description) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if r.Header.Get("HX-Request") == "true" { w.Header().Set("HX-Refresh", "true") w.WriteHeader(http.StatusOK) } else { http.Redirect(w, r, "/admin/roles", http.StatusFound) } } // DeleteRoleHandler deletes a role func (a *App) DeleteRoleHandler(w http.ResponseWriter, r *http.Request) { roleID := chi.URLParam(r, "id") _, err := a.DB.ExecContext(r.Context(), `DELETE FROM roles WHERE id = ?`, roleID) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if r.Header.Get("HX-Request") == "true" { w.Header().Set("HX-Refresh", "true") w.WriteHeader(http.StatusOK) } else { http.Redirect(w, r, "/admin/roles", http.StatusFound) } } // UpdateRolePermissionsHandler updates permissions for a role func (a *App) UpdateRolePermissionsHandler(w http.ResponseWriter, r *http.Request) { roleID := chi.URLParam(r, "id") var req struct { PermissionIDs []string `json:"permission_ids"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid request", http.StatusBadRequest) return } rbacStore := auth.NewRBACStore(a.DB) // Get current permissions currentPerms, _ := rbacStore.GetRolePermissions(r.Context(), roleID) // Remove all current permissions for _, perm := range currentPerms { rbacStore.RemovePermissionFromRole(r.Context(), roleID, perm.ID) } // Add new permissions for _, permID := range req.PermissionIDs { rbacStore.AssignPermissionToRole(r.Context(), roleID, permID) } if r.Header.Get("HX-Request") == "true" { w.Header().Set("HX-Refresh", "true") w.WriteHeader(http.StatusOK) } else { http.Redirect(w, r, "/admin/roles", http.StatusFound) } }