initial commit
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/owen/vibedns/internal/app"
|
||||
"github.com/owen/vibedns/internal/validate"
|
||||
)
|
||||
|
||||
// recordInputFromForm assembles a record from either the type-specific editor
|
||||
// (field_* inputs) or the advanced raw editor.
|
||||
func recordInputFromForm(r *http.Request) app.RecordInput {
|
||||
enabled := formBool(r, "enabled")
|
||||
in := app.RecordInput{
|
||||
Name: formString(r, "name"),
|
||||
Type: formString(r, "type"),
|
||||
TTL: formUint32Ptr(r, "ttl"),
|
||||
Enabled: &enabled,
|
||||
Comment: formString(r, "comment"),
|
||||
}
|
||||
|
||||
if formBool(r, "advanced") {
|
||||
// The advanced editor supplies rdata verbatim, and may override the
|
||||
// record type with one that has no dedicated editor.
|
||||
in.Data = formString(r, "data")
|
||||
if t := formString(r, "field_rtype"); t != "" {
|
||||
in.Type = t
|
||||
}
|
||||
if d := formString(r, "field_rdata"); d != "" {
|
||||
in.Data = d
|
||||
}
|
||||
return in
|
||||
}
|
||||
|
||||
fields := map[string]string{}
|
||||
for key, values := range r.Form {
|
||||
if !strings.HasPrefix(key, "field_") || len(values) == 0 {
|
||||
continue
|
||||
}
|
||||
fields[strings.TrimPrefix(key, "field_")] = values[0]
|
||||
}
|
||||
if len(fields) > 0 {
|
||||
in.Fields = fields
|
||||
} else {
|
||||
in.Data = formString(r, "data")
|
||||
}
|
||||
return in
|
||||
}
|
||||
|
||||
func (s *Server) handleRecordCreate(w http.ResponseWriter, r *http.Request) error {
|
||||
zoneID, err := pathID(r, "id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
rec, err := s.app.CreateRecord(r.Context(), s.actor(r), zoneID, recordInputFromForm(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
setFlash(w, r, "success", fmt.Sprintf("Added %s %s record.", rec.Name, rec.Type))
|
||||
s.redirectBack(w, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) handleRecordUpdate(w http.ResponseWriter, r *http.Request) error {
|
||||
id, err := pathID(r, "id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
rec, err := s.app.UpdateRecord(r.Context(), s.actor(r), id, recordInputFromForm(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
setFlash(w, r, "success", fmt.Sprintf("Saved %s %s record.", rec.Name, rec.Type))
|
||||
s.redirectBack(w, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) handleRecordDelete(w http.ResponseWriter, r *http.Request) error {
|
||||
id, err := pathID(r, "id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.app.DeleteRecord(r.Context(), s.actor(r), id); err != nil {
|
||||
return err
|
||||
}
|
||||
setFlash(w, r, "success", "Record deleted.")
|
||||
s.redirectBack(w, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) handleRecordToggle(w http.ResponseWriter, r *http.Request) error {
|
||||
id, err := pathID(r, "id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
enabled := formBool(r, "enabled")
|
||||
if err := s.app.SetRecordEnabled(r.Context(), s.actor(r), id, enabled); err != nil {
|
||||
return err
|
||||
}
|
||||
state := "disabled"
|
||||
if enabled {
|
||||
state = "enabled"
|
||||
}
|
||||
setFlash(w, r, "success", "Record "+state+".")
|
||||
s.redirectBack(w, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) handleRecordBulk(w http.ResponseWriter, r *http.Request) error {
|
||||
zoneID, err := pathID(r, "id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
ids := formInt64s(r, "record_id")
|
||||
action := app.BulkAction(formString(r, "action"))
|
||||
|
||||
n, err := s.app.BulkRecords(r.Context(), s.actor(r), zoneID, ids, action)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
verb := map[app.BulkAction]string{
|
||||
app.BulkDelete: "deleted",
|
||||
app.BulkEnable: "enabled",
|
||||
app.BulkDisable: "disabled",
|
||||
}[action]
|
||||
setFlash(w, r, "success", fmt.Sprintf("%d record%s %s.", n, plural(n), verb))
|
||||
s.redirectBack(w, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func plural(n int) string {
|
||||
if n == 1 {
|
||||
return ""
|
||||
}
|
||||
return "s"
|
||||
}
|
||||
|
||||
// recordFieldValues splits a stored record back into editor fields, so an
|
||||
// existing record opens in its dedicated form rather than as raw rdata.
|
||||
func recordFieldValues(rtype, data string) map[string]string {
|
||||
return validate.SplitRData(rtype, data)
|
||||
}
|
||||
|
||||
// copyLimited copies at most n bytes, reporting an error past the limit.
|
||||
func copyLimited(dst io.Writer, src io.Reader, n int64) (int64, error) {
|
||||
return io.Copy(dst, io.LimitReader(src, n))
|
||||
}
|
||||
Reference in New Issue
Block a user