+{{ end }}
\ No newline at end of file
diff --git a/resources/templates/base.gohtml b/resources/templates/base.gohtml
new file mode 100644
index 0000000..ec873ef
--- /dev/null
+++ b/resources/templates/base.gohtml
@@ -0,0 +1,56 @@
+{{ define "base" }}
+
+
+ Gobble - {{ .Title }}
+
+
+
+
+
+ {{ if not .DisableNavbar }}
+
+ {{ end }}
+
+ {{ template "content" .Data}}
+
+
+
+
+{{ end }}
\ No newline at end of file
diff --git a/resources/templates/error.gohtml b/resources/templates/error.gohtml
new file mode 100644
index 0000000..070ba53
--- /dev/null
+++ b/resources/templates/error.gohtml
@@ -0,0 +1,5 @@
+{{ define "content" }}
+
+
Error occurred: {{ . }}
+
+{{ end }}
\ No newline at end of file
diff --git a/resources/templates/home.gohtml b/resources/templates/home.gohtml
new file mode 100644
index 0000000..9badaed
--- /dev/null
+++ b/resources/templates/home.gohtml
@@ -0,0 +1,6 @@
+{{ define "content" }}
+
+
Home
+
Welcome to Gobble!
+
+{{ end }}
\ No newline at end of file
diff --git a/resources/templates/profiles/create.gohtml b/resources/templates/profiles/create.gohtml
new file mode 100644
index 0000000..13ddb4c
--- /dev/null
+++ b/resources/templates/profiles/create.gohtml
@@ -0,0 +1,28 @@
+{{ define "content" }}
+
+
Create profile
+
+
+{{ end }}
\ No newline at end of file
diff --git a/resources/templates/profiles/edit.gohtml b/resources/templates/profiles/edit.gohtml
new file mode 100644
index 0000000..353f13f
--- /dev/null
+++ b/resources/templates/profiles/edit.gohtml
@@ -0,0 +1,35 @@
+{{ define "content" }}
+
+
Edit profile
+
+
+{{ end }}
\ No newline at end of file
diff --git a/resources/templates/profiles/overview.gohtml b/resources/templates/profiles/overview.gohtml
new file mode 100644
index 0000000..902ab2d
--- /dev/null
+++ b/resources/templates/profiles/overview.gohtml
@@ -0,0 +1,25 @@
+{{ define "content" }}
+
+{{ end }}
\ No newline at end of file
diff --git a/resources/templates/profiles/show.gohtml b/resources/templates/profiles/show.gohtml
new file mode 100644
index 0000000..fd4a9fe
--- /dev/null
+++ b/resources/templates/profiles/show.gohtml
@@ -0,0 +1,37 @@
+{{ define "content" }}
+
+
Profile
+
+
+
+{{ end }}
\ No newline at end of file
diff --git a/resources/templates/systems/create.gohtml b/resources/templates/systems/create.gohtml
new file mode 100644
index 0000000..f8116b7
--- /dev/null
+++ b/resources/templates/systems/create.gohtml
@@ -0,0 +1,28 @@
+{{ define "content" }}
+
+
Create system
+
+
+{{ end }}
\ No newline at end of file
diff --git a/resources/templates/systems/edit.gohtml b/resources/templates/systems/edit.gohtml
new file mode 100644
index 0000000..ef17f50
--- /dev/null
+++ b/resources/templates/systems/edit.gohtml
@@ -0,0 +1,44 @@
+{{ define "content" }}
+
+
Edit system
+
+
+{{ end }}
\ No newline at end of file
diff --git a/resources/templates/systems/overview.gohtml b/resources/templates/systems/overview.gohtml
new file mode 100644
index 0000000..f45efed
--- /dev/null
+++ b/resources/templates/systems/overview.gohtml
@@ -0,0 +1,25 @@
+{{ define "content" }}
+
+{{ end }}
\ No newline at end of file
diff --git a/resources/templates/systems/show.gohtml b/resources/templates/systems/show.gohtml
new file mode 100644
index 0000000..6872d8f
--- /dev/null
+++ b/resources/templates/systems/show.gohtml
@@ -0,0 +1,44 @@
+{{ define "content" }}
+
+
System
+
+
+
+{{ end }}
\ No newline at end of file
diff --git a/server/handlers/api_user_handlers.go b/server/handlers/api_handlers/api_user_handlers.go
similarity index 94%
rename from server/handlers/api_user_handlers.go
rename to server/handlers/api_handlers/api_user_handlers.go
index 7ffb245..95a0e04 100644
--- a/server/handlers/api_user_handlers.go
+++ b/server/handlers/api_handlers/api_user_handlers.go
@@ -1,4 +1,4 @@
-package handlers
+package api_handlers
import (
"encoding/json"
@@ -6,6 +6,7 @@ import (
"github.com/evanebb/gobble/api/auth"
"github.com/evanebb/gobble/api/response"
"github.com/evanebb/gobble/repository"
+ "github.com/evanebb/gobble/server/handlers"
"github.com/google/uuid"
"golang.org/x/crypto/bcrypt"
"net/http"
@@ -63,7 +64,7 @@ func (h ApiUserHandlerGroup) GetUsers(w http.ResponseWriter, r *http.Request) er
}
func (h ApiUserHandlerGroup) GetUser(w http.ResponseWriter, r *http.Request) error {
- userID, err := getUUIDFromRequest(r)
+ userID, err := handlers.GetUUIDFromRequest(r)
if err != nil {
return NewHTTPError(err, http.StatusBadRequest)
}
@@ -108,7 +109,7 @@ func (h ApiUserHandlerGroup) CreateUser(w http.ResponseWriter, r *http.Request)
func (h ApiUserHandlerGroup) PutUser(w http.ResponseWriter, r *http.Request) error {
var req userRequest
- userID, err := getUUIDFromRequest(r)
+ userID, err := handlers.GetUUIDFromRequest(r)
if err != nil {
return NewHTTPError(err, http.StatusBadRequest)
}
@@ -135,7 +136,7 @@ func (h ApiUserHandlerGroup) PutUser(w http.ResponseWriter, r *http.Request) err
}
func (h ApiUserHandlerGroup) DeleteUser(w http.ResponseWriter, r *http.Request) error {
- userID, err := getUUIDFromRequest(r)
+ userID, err := handlers.GetUUIDFromRequest(r)
if err != nil {
return NewHTTPError(err, http.StatusBadRequest)
}
diff --git a/server/handlers/errors.go b/server/handlers/api_handlers/errors.go
similarity index 93%
rename from server/handlers/errors.go
rename to server/handlers/api_handlers/errors.go
index 1eab00a..38ae426 100644
--- a/server/handlers/errors.go
+++ b/server/handlers/api_handlers/errors.go
@@ -1,4 +1,4 @@
-package handlers
+package api_handlers
type HTTPError struct {
err error
diff --git a/server/handlers/handlers.go b/server/handlers/api_handlers/handlers.go
similarity index 86%
rename from server/handlers/handlers.go
rename to server/handlers/api_handlers/handlers.go
index e321870..84a29b3 100644
--- a/server/handlers/handlers.go
+++ b/server/handlers/api_handlers/handlers.go
@@ -1,4 +1,4 @@
-package handlers
+package api_handlers
import (
"errors"
@@ -49,8 +49,3 @@ func ErrorHandler(h ErrorHandlerFunc) http.HandlerFunc {
func UnknownEndpointHandler(w http.ResponseWriter, r *http.Request) error {
return response.Error(w, http.StatusNotFound, "unknown endpoint, please refer to the documentation for available endpoints")
}
-
-func IndexHandler(w http.ResponseWriter, r *http.Request) error {
- html := "
Welcome to gobble!
Refer to the documentation for the available API endpoints.
"
- return response.HTML(w, http.StatusOK, html)
-}
diff --git a/server/handlers/profile_handlers.go b/server/handlers/api_handlers/profile_handlers.go
similarity index 95%
rename from server/handlers/profile_handlers.go
rename to server/handlers/api_handlers/profile_handlers.go
index 21b73f4..63b34c5 100644
--- a/server/handlers/profile_handlers.go
+++ b/server/handlers/api_handlers/profile_handlers.go
@@ -1,4 +1,4 @@
-package handlers
+package api_handlers
import (
"encoding/json"
@@ -7,6 +7,7 @@ import (
"github.com/evanebb/gobble/kernelparameters"
"github.com/evanebb/gobble/profile"
"github.com/evanebb/gobble/repository"
+ "github.com/evanebb/gobble/server/handlers"
"github.com/google/uuid"
"net/http"
)
@@ -74,7 +75,7 @@ func (h ProfileHandlerGroup) GetProfiles(w http.ResponseWriter, r *http.Request)
}
func (h ProfileHandlerGroup) GetProfile(w http.ResponseWriter, r *http.Request) error {
- profileId, err := getUUIDFromRequest(r)
+ profileId, err := handlers.GetUUIDFromRequest(r)
if err != nil {
return NewHTTPError(err, http.StatusBadRequest)
}
@@ -122,7 +123,7 @@ func (h ProfileHandlerGroup) CreateProfile(w http.ResponseWriter, r *http.Reques
func (h ProfileHandlerGroup) PutProfile(w http.ResponseWriter, r *http.Request) error {
var req profileRequest
- profileId, err := getUUIDFromRequest(r)
+ profileId, err := handlers.GetUUIDFromRequest(r)
if err != nil {
return NewHTTPError(err, http.StatusBadRequest)
}
@@ -153,7 +154,7 @@ func (h ProfileHandlerGroup) PutProfile(w http.ResponseWriter, r *http.Request)
}
func (h ProfileHandlerGroup) PatchProfile(w http.ResponseWriter, r *http.Request) error {
- profileId, err := getUUIDFromRequest(r)
+ profileId, err := handlers.GetUUIDFromRequest(r)
if err != nil {
return NewHTTPError(err, http.StatusBadRequest)
}
@@ -202,7 +203,7 @@ func (h ProfileHandlerGroup) PatchProfile(w http.ResponseWriter, r *http.Request
}
func (h ProfileHandlerGroup) DeleteProfile(w http.ResponseWriter, r *http.Request) error {
- profileId, err := getUUIDFromRequest(r)
+ profileId, err := handlers.GetUUIDFromRequest(r)
if err != nil {
return NewHTTPError(err, http.StatusBadRequest)
}
diff --git a/server/handlers/pxe_config_handlers.go b/server/handlers/api_handlers/pxe_config_handlers.go
similarity index 98%
rename from server/handlers/pxe_config_handlers.go
rename to server/handlers/api_handlers/pxe_config_handlers.go
index a98f249..e978ca1 100644
--- a/server/handlers/pxe_config_handlers.go
+++ b/server/handlers/api_handlers/pxe_config_handlers.go
@@ -1,4 +1,4 @@
-package handlers
+package api_handlers
import (
"errors"
diff --git a/server/handlers/system_handlers.go b/server/handlers/api_handlers/system_handlers.go
similarity index 96%
rename from server/handlers/system_handlers.go
rename to server/handlers/api_handlers/system_handlers.go
index b8901a6..3cdf34d 100644
--- a/server/handlers/system_handlers.go
+++ b/server/handlers/api_handlers/system_handlers.go
@@ -1,4 +1,4 @@
-package handlers
+package api_handlers
import (
"encoding/json"
@@ -6,6 +6,7 @@ import (
"github.com/evanebb/gobble/api/response"
"github.com/evanebb/gobble/kernelparameters"
"github.com/evanebb/gobble/repository"
+ "github.com/evanebb/gobble/server/handlers"
"github.com/evanebb/gobble/system"
"github.com/google/uuid"
"net"
@@ -75,7 +76,7 @@ func (h SystemHandlerGroup) GetSystems(w http.ResponseWriter, r *http.Request) e
}
func (h SystemHandlerGroup) GetSystem(w http.ResponseWriter, r *http.Request) error {
- systemId, err := getUUIDFromRequest(r)
+ systemId, err := handlers.GetUUIDFromRequest(r)
if err != nil {
return NewHTTPError(err, http.StatusBadRequest)
}
@@ -128,7 +129,7 @@ func (h SystemHandlerGroup) CreateSystem(w http.ResponseWriter, r *http.Request)
func (h SystemHandlerGroup) PutSystem(w http.ResponseWriter, r *http.Request) error {
var req systemRequest
- systemId, err := getUUIDFromRequest(r)
+ systemId, err := handlers.GetUUIDFromRequest(r)
if err != nil {
return NewHTTPError(err, http.StatusBadRequest)
}
@@ -164,7 +165,7 @@ func (h SystemHandlerGroup) PutSystem(w http.ResponseWriter, r *http.Request) er
}
func (h SystemHandlerGroup) PatchSystem(w http.ResponseWriter, r *http.Request) error {
- systemId, err := getUUIDFromRequest(r)
+ systemId, err := handlers.GetUUIDFromRequest(r)
if err != nil {
return NewHTTPError(err, http.StatusBadRequest)
}
@@ -218,7 +219,7 @@ func (h SystemHandlerGroup) PatchSystem(w http.ResponseWriter, r *http.Request)
}
func (h SystemHandlerGroup) DeleteSystem(w http.ResponseWriter, r *http.Request) error {
- systemId, err := getUUIDFromRequest(r)
+ systemId, err := handlers.GetUUIDFromRequest(r)
if err != nil {
return NewHTTPError(err, http.StatusBadRequest)
}
diff --git a/server/handlers/common.go b/server/handlers/common.go
index 4a297a2..ed58059 100644
--- a/server/handlers/common.go
+++ b/server/handlers/common.go
@@ -7,8 +7,8 @@ import (
"net/http"
)
-// getUUIDFromRequest gets and parses the UUID from the request. If it's not a valid UUID, an error is returned.
-func getUUIDFromRequest(r *http.Request) (uuid.UUID, error) {
+// GetUUIDFromRequest gets and parses the UUID from the request. If it's not a valid UUID, an error is returned.
+func GetUUIDFromRequest(r *http.Request) (uuid.UUID, error) {
uuidString := chi.URLParam(r, "uuid")
UUID, err := uuid.Parse(uuidString)
if err != nil {
diff --git a/server/handlers/middleware.go b/server/handlers/middleware.go
new file mode 100644
index 0000000..822b27d
--- /dev/null
+++ b/server/handlers/middleware.go
@@ -0,0 +1,19 @@
+package handlers
+
+import "net/http"
+
+// MethodOverride will check for a hidden "_method" input in the POST form,
+// so that PUT, PATCH and DELETE requests can be supported
+func MethodOverride(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.Method == "POST" {
+ method := r.PostFormValue("_method")
+
+ if method == "PUT" || method == "PATCH" || method == "DELETE" {
+ r.Method = method
+ }
+ }
+
+ next.ServeHTTP(w, r)
+ })
+}
diff --git a/server/handlers/ui_handlers/handlers.go b/server/handlers/ui_handlers/handlers.go
new file mode 100644
index 0000000..0e1b6ea
--- /dev/null
+++ b/server/handlers/ui_handlers/handlers.go
@@ -0,0 +1,13 @@
+package ui_handlers
+
+import "net/http"
+
+func PageNotFound(w http.ResponseWriter, r *http.Request) {
+ d := templateData{Title: "Page not found", DisableNavbar: true}
+ renderTemplate(w, "404", d)
+}
+
+func HomePage(w http.ResponseWriter, r *http.Request) {
+ d := templateData{Title: "Home"}
+ renderTemplate(w, "home", d)
+}
diff --git a/server/handlers/ui_handlers/profile_handlers.go b/server/handlers/ui_handlers/profile_handlers.go
new file mode 100644
index 0000000..10555a5
--- /dev/null
+++ b/server/handlers/ui_handlers/profile_handlers.go
@@ -0,0 +1,165 @@
+package ui_handlers
+
+import (
+ "errors"
+ "github.com/evanebb/gobble/kernelparameters"
+ "github.com/evanebb/gobble/profile"
+ "github.com/evanebb/gobble/server/handlers"
+ "github.com/google/uuid"
+ "net/http"
+ "strings"
+)
+
+func parseProfileFromPostForm(r *http.Request) (profile.Profile, error) {
+ var p profile.Profile
+
+ err := r.ParseForm()
+ if err != nil {
+ return p, err
+ }
+
+ requiredKeys := []string{"name", "description", "kernel", "initrd", "kernelParameters"}
+ for _, v := range requiredKeys {
+ if !r.PostForm.Has(v) {
+ return p, errors.New("missing value " + v + " in POST form")
+ }
+ }
+
+ kp, err := kernelparameters.New(strings.Split(r.PostFormValue("kernelParameters"), " "))
+ if err != nil {
+ return p, err
+ }
+
+ return profile.New(
+ // the UUID needs to be set properly afterward by the caller, depending on whether we are creating a new one or updating an existing one
+ uuid.Nil,
+ r.PostFormValue("name"),
+ r.PostFormValue("description"),
+ r.PostFormValue("kernel"),
+ r.PostFormValue("initrd"),
+ kp,
+ )
+}
+
+type UiProfileHandlerGroup struct {
+ profileRepo profile.Repository
+}
+
+func NewUiProfileHandlerGroup(pr profile.Repository) UiProfileHandlerGroup {
+ return UiProfileHandlerGroup{pr}
+}
+
+// Overview will list all profiles.
+func (h UiProfileHandlerGroup) Overview(w http.ResponseWriter, r *http.Request) {
+ profiles, err := h.profileRepo.GetProfiles()
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ d := templateData{Title: "Profiles", Data: profiles}
+ renderTemplate(w, "profiles/overview", d)
+}
+
+// Show will show information about a single profile.
+func (h UiProfileHandlerGroup) Show(w http.ResponseWriter, r *http.Request) {
+ profileId, err := handlers.GetUUIDFromRequest(r)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ p, err := h.profileRepo.GetProfileById(profileId)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ d := templateData{Title: "Profile Information", Data: p}
+ renderTemplate(w, "profiles/show", d)
+}
+
+// Create shows the page for creating a new profile.
+func (h UiProfileHandlerGroup) Create(w http.ResponseWriter, r *http.Request) {
+ d := templateData{Title: "Create profile"}
+ renderTemplate(w, "profiles/create", d)
+}
+
+// Store will store a newly created profile.
+func (h UiProfileHandlerGroup) Store(w http.ResponseWriter, r *http.Request) {
+ p, err := parseProfileFromPostForm(r)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ p.Id = uuid.New()
+
+ err = h.profileRepo.SetProfile(p)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ http.Redirect(w, r, "/ui/profiles/"+p.Id.String(), http.StatusSeeOther)
+}
+
+// Edit shows the page for editing an existing profile.
+func (h UiProfileHandlerGroup) Edit(w http.ResponseWriter, r *http.Request) {
+ profileId, err := handlers.GetUUIDFromRequest(r)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ p, err := h.profileRepo.GetProfileById(profileId)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ d := templateData{Title: "Edit Profile", Data: p}
+ renderTemplate(w, "profiles/edit", d)
+}
+
+// Update will update the specified profile.
+func (h UiProfileHandlerGroup) Update(w http.ResponseWriter, r *http.Request) {
+ profileId, err := handlers.GetUUIDFromRequest(r)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ p, err := parseProfileFromPostForm(r)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ p.Id = profileId
+
+ err = h.profileRepo.SetProfile(p)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ http.Redirect(w, r, "/ui/profiles/"+p.Id.String(), http.StatusSeeOther)
+}
+
+// Delete will delete the specified profile.
+func (h UiProfileHandlerGroup) Delete(w http.ResponseWriter, r *http.Request) {
+ profileId, err := handlers.GetUUIDFromRequest(r)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ err = h.profileRepo.DeleteProfileById(profileId)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ http.Redirect(w, r, "/ui/profiles", http.StatusSeeOther)
+}
diff --git a/server/handlers/ui_handlers/system_handlers.go b/server/handlers/ui_handlers/system_handlers.go
new file mode 100644
index 0000000..eec40af
--- /dev/null
+++ b/server/handlers/ui_handlers/system_handlers.go
@@ -0,0 +1,202 @@
+package ui_handlers
+
+import (
+ "errors"
+ "github.com/evanebb/gobble/kernelparameters"
+ "github.com/evanebb/gobble/profile"
+ "github.com/evanebb/gobble/server/handlers"
+ "github.com/evanebb/gobble/system"
+ "github.com/google/uuid"
+ "net"
+ "net/http"
+ "strings"
+)
+
+func parseSystemFromPostForm(r *http.Request) (system.System, error) {
+ var s system.System
+
+ err := r.ParseForm()
+ if err != nil {
+ return s, err
+ }
+
+ requiredKeys := []string{"name", "description", "profile", "mac", "kernelParameters"}
+ for _, v := range requiredKeys {
+ if !r.PostForm.Has(v) {
+ return s, errors.New("missing value " + v + " in POST form")
+ }
+ }
+
+ kp, err := kernelparameters.New(strings.Split(r.PostFormValue("kernelParameters"), " "))
+ if err != nil {
+ return s, err
+ }
+
+ profileId, err := uuid.Parse(r.PostFormValue("profile"))
+ if err != nil {
+ return s, err
+ }
+
+ macAddress, err := net.ParseMAC(r.PostFormValue("mac"))
+ if err != nil {
+ return s, err
+ }
+
+ return system.New(
+ // the UUID needs to be set properly afterward by the caller, depending on whether we are creating a new one or updating an existing one
+ uuid.Nil,
+ r.PostFormValue("name"),
+ r.PostFormValue("description"),
+ profileId,
+ macAddress,
+ kp,
+ )
+}
+
+type UiSystemHandlerGroup struct {
+ systemRepo system.Repository
+ profileRepo profile.Repository
+}
+
+func NewUiSystemHandlerGroup(sr system.Repository, pr profile.Repository) UiSystemHandlerGroup {
+ return UiSystemHandlerGroup{sr, pr}
+}
+
+// Overview will list all systems.
+func (h UiSystemHandlerGroup) Overview(w http.ResponseWriter, r *http.Request) {
+ systems, err := h.systemRepo.GetSystems()
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ d := templateData{Title: "Systems", Data: systems}
+ renderTemplate(w, "systems/overview", d)
+}
+
+// Show will show information about a single system.
+func (h UiSystemHandlerGroup) Show(w http.ResponseWriter, r *http.Request) {
+ systemId, err := handlers.GetUUIDFromRequest(r)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ s, err := h.systemRepo.GetSystemById(systemId)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ p, err := h.profileRepo.GetProfileById(s.Profile)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ d := templateData{Title: "System information", Data: struct {
+ System system.System
+ Profile profile.Profile
+ }{
+ System: s,
+ Profile: p,
+ }}
+ renderTemplate(w, "systems/show", d)
+}
+
+// Create shows the page for creating a new system.
+func (h UiSystemHandlerGroup) Create(w http.ResponseWriter, r *http.Request) {
+ d := templateData{Title: "Create system"}
+ renderTemplate(w, "systems/create", d)
+}
+
+// Store will store a newly created system.
+func (h UiSystemHandlerGroup) Store(w http.ResponseWriter, r *http.Request) {
+ s, err := parseSystemFromPostForm(r)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ s.Id = uuid.New()
+
+ err = h.systemRepo.SetSystem(s)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ http.Redirect(w, r, "/ui/systems/"+s.Id.String(), http.StatusSeeOther)
+}
+
+// Edit shows the page for editing an existing system.
+func (h UiSystemHandlerGroup) Edit(w http.ResponseWriter, r *http.Request) {
+ systemId, err := handlers.GetUUIDFromRequest(r)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ s, err := h.systemRepo.GetSystemById(systemId)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ profiles, err := h.profileRepo.GetProfiles()
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ d := templateData{Title: "Edit system", Data: struct {
+ System system.System
+ Profiles []profile.Profile
+ }{
+ System: s,
+ Profiles: profiles,
+ }}
+ renderTemplate(w, "systems/edit", d)
+}
+
+// Update will update the specified system.
+func (h UiSystemHandlerGroup) Update(w http.ResponseWriter, r *http.Request) {
+ systemId, err := handlers.GetUUIDFromRequest(r)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ s, err := parseSystemFromPostForm(r)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ s.Id = systemId
+
+ err = h.systemRepo.SetSystem(s)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ http.Redirect(w, r, "/ui/systems/"+s.Id.String(), http.StatusSeeOther)
+}
+
+// Delete will delete the specified system.
+func (h UiSystemHandlerGroup) Delete(w http.ResponseWriter, r *http.Request) {
+ systemId, err := handlers.GetUUIDFromRequest(r)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ err = h.systemRepo.DeleteSystemById(systemId)
+ if err != nil {
+ renderError(w, err)
+ return
+ }
+
+ http.Redirect(w, r, "/ui/systems", http.StatusSeeOther)
+}
diff --git a/server/handlers/ui_handlers/template.go b/server/handlers/ui_handlers/template.go
new file mode 100644
index 0000000..f89f59e
--- /dev/null
+++ b/server/handlers/ui_handlers/template.go
@@ -0,0 +1,50 @@
+package ui_handlers
+
+import (
+ "github.com/evanebb/gobble/resources"
+ "html/template"
+ "io"
+)
+
+type templateData struct {
+ Title string
+ Data any
+ DisableNavbar bool
+}
+
+// renderTemplateErr will render the template referenced by the passed name and return an error if it encounters one
+func renderTemplateErr(w io.Writer, templateName string, d templateData) error {
+ var err error
+
+ templateFile := "templates/" + templateName + ".gohtml"
+ tmpl, err := template.New("base").ParseFS(resources.Templates, "templates/base.gohtml", templateFile)
+ if err != nil {
+ return err
+ }
+
+ err = tmpl.Execute(w, d)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// renderTemplate will render the template referenced by the passed name and render an error page if an error occurs
+func renderTemplate(w io.Writer, templateName string, d templateData) {
+ err := renderTemplateErr(w, templateName, d)
+ if err != nil {
+ renderError(w, err)
+ }
+}
+
+// renderError will render the error template with the message from the given error
+func renderError(w io.Writer, err error) {
+ data := templateData{
+ Title: "Error",
+ Data: err.Error(),
+ }
+
+ // If we get to the point that we can't even render the error template, just do nothing
+ _ = renderTemplateErr(w, "error", data)
+}
diff --git a/server/routes.go b/server/routes.go
index e97f535..62f37cf 100644
--- a/server/routes.go
+++ b/server/routes.go
@@ -2,58 +2,105 @@ package server
import (
"github.com/evanebb/gobble/api/auth"
+ "github.com/evanebb/gobble/resources"
"github.com/evanebb/gobble/server/handlers"
+ "github.com/evanebb/gobble/server/handlers/api_handlers"
+ "github.com/evanebb/gobble/server/handlers/ui_handlers"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
+ "net/http"
)
func (s *Server) routes() {
- s.router.Use(middleware.Logger)
+ s.router.Use(handlers.MethodOverride, middleware.Logger)
- s.router.Get("/", handlers.ErrorHandler(handlers.IndexHandler))
+ // API route group
s.router.Route("/api", func(r chi.Router) {
- r.Use(auth.BasicAuth(s.apiUserRepo))
- r.NotFound(handlers.ErrorHandler(handlers.UnknownEndpointHandler))
+ r.Use(auth.ApiBasicAuth(s.apiUserRepo))
+ r.NotFound(api_handlers.ErrorHandler(api_handlers.UnknownEndpointHandler))
r.Route("/profiles", func(r chi.Router) {
- h := handlers.NewProfileHandlerGroup(s.profileRepo)
+ h := api_handlers.NewProfileHandlerGroup(s.profileRepo)
- r.Get("/", handlers.ErrorHandler(h.GetProfiles))
- r.Post("/", handlers.ErrorHandler(h.CreateProfile))
+ r.Get("/", api_handlers.ErrorHandler(h.GetProfiles))
+ r.Post("/", api_handlers.ErrorHandler(h.CreateProfile))
r.Route("/{uuid}", func(r chi.Router) {
- r.Get("/", handlers.ErrorHandler(h.GetProfile))
- r.Put("/", handlers.ErrorHandler(h.PutProfile))
- r.Patch("/", handlers.ErrorHandler(h.PatchProfile))
- r.Delete("/", handlers.ErrorHandler(h.DeleteProfile))
+ r.Get("/", api_handlers.ErrorHandler(h.GetProfile))
+ r.Put("/", api_handlers.ErrorHandler(h.PutProfile))
+ r.Patch("/", api_handlers.ErrorHandler(h.PatchProfile))
+ r.Delete("/", api_handlers.ErrorHandler(h.DeleteProfile))
})
})
r.Route("/systems", func(r chi.Router) {
- h := handlers.NewSystemHandlerGroup(s.systemRepo)
+ h := api_handlers.NewSystemHandlerGroup(s.systemRepo)
- r.Get("/", handlers.ErrorHandler(h.GetSystems))
- r.Post("/", handlers.ErrorHandler(h.CreateSystem))
+ r.Get("/", api_handlers.ErrorHandler(h.GetSystems))
+ r.Post("/", api_handlers.ErrorHandler(h.CreateSystem))
r.Route("/{uuid}", func(r chi.Router) {
- r.Get("/", handlers.ErrorHandler(h.GetSystem))
- r.Put("/", handlers.ErrorHandler(h.PutSystem))
- r.Patch("/", handlers.ErrorHandler(h.PatchSystem))
- r.Delete("/", handlers.ErrorHandler(h.DeleteSystem))
+ r.Get("/", api_handlers.ErrorHandler(h.GetSystem))
+ r.Put("/", api_handlers.ErrorHandler(h.PutSystem))
+ r.Patch("/", api_handlers.ErrorHandler(h.PatchSystem))
+ r.Delete("/", api_handlers.ErrorHandler(h.DeleteSystem))
})
})
r.Route("/users", func(r chi.Router) {
- h := handlers.NewApiUserHandlerGroup(s.apiUserRepo)
+ h := api_handlers.NewApiUserHandlerGroup(s.apiUserRepo)
- r.Get("/", handlers.ErrorHandler(h.GetUsers))
- r.Post("/", handlers.ErrorHandler(h.CreateUser))
+ r.Get("/", api_handlers.ErrorHandler(h.GetUsers))
+ r.Post("/", api_handlers.ErrorHandler(h.CreateUser))
r.Route("/{uuid}", func(r chi.Router) {
- r.Get("/", handlers.ErrorHandler(h.GetUser))
- r.Put("/", handlers.ErrorHandler(h.PutUser))
- r.Delete("/", handlers.ErrorHandler(h.DeleteUser))
+ r.Get("/", api_handlers.ErrorHandler(h.GetUser))
+ r.Put("/", api_handlers.ErrorHandler(h.PutUser))
+ r.Delete("/", api_handlers.ErrorHandler(h.DeleteUser))
})
})
})
+
// This endpoint should not have authentication, so it lives outside the /api group above
- h := handlers.NewPxeConfigHandlerGroup(s.systemRepo, s.profileRepo)
- s.router.Get("/api/pxe-config", handlers.ErrorHandler(h.GetPxeConfig))
+ h := api_handlers.NewPxeConfigHandlerGroup(s.systemRepo, s.profileRepo)
+ s.router.Get("/api/pxe-config", api_handlers.ErrorHandler(h.GetPxeConfig))
+
+ // Redirect the index to the UI by default
+ s.router.Handle("/", http.RedirectHandler("ui/", http.StatusMovedPermanently))
+
+ // Front-end (UI) routes
+ s.router.Route("/ui/", func(r chi.Router) {
+ r.Use(auth.BrowserBasicAuth(s.apiUserRepo))
+
+ r.NotFound(ui_handlers.PageNotFound)
+ r.Get("/", ui_handlers.HomePage)
+ r.Handle("/static/*", http.StripPrefix("/ui/", http.FileServer(http.FS(resources.Static))))
+
+ r.Route("/profiles", func(r chi.Router) {
+ h := ui_handlers.NewUiProfileHandlerGroup(s.profileRepo)
+
+ r.Get("/", h.Overview)
+ r.Get("/create", h.Create)
+ r.Post("/", h.Store)
+
+ r.Route("/{uuid}", func(r chi.Router) {
+ r.Get("/", h.Show)
+ r.Get("/edit", h.Edit)
+ r.Put("/", h.Update)
+ r.Delete("/", h.Delete)
+ })
+ })
+
+ r.Route("/systems", func(r chi.Router) {
+ h := ui_handlers.NewUiSystemHandlerGroup(s.systemRepo, s.profileRepo)
+
+ r.Get("/", h.Overview)
+ r.Get("/create", h.Create)
+ r.Post("/", h.Store)
+
+ r.Route("/{uuid}", func(r chi.Router) {
+ r.Get("/", h.Show)
+ r.Get("/edit", h.Edit)
+ r.Put("/", h.Update)
+ r.Delete("/", h.Delete)
+ })
+ })
+ })
}
From 34665b597a0365d0d6b474cb05b9e7f69f8b7697 Mon Sep 17 00:00:00 2001
From: evanebb <78433178+evanebb@users.noreply.github.com>
Date: Sat, 6 Jan 2024 22:25:32 +0100
Subject: [PATCH 2/8] Remove unused PXE config textarea
---
resources/templates/systems/show.gohtml | 4 ----
1 file changed, 4 deletions(-)
diff --git a/resources/templates/systems/show.gohtml b/resources/templates/systems/show.gohtml
index 6872d8f..8d37c2d 100644
--- a/resources/templates/systems/show.gohtml
+++ b/resources/templates/systems/show.gohtml
@@ -30,10 +30,6 @@
-