166 lines
4.0 KiB
Go
166 lines
4.0 KiB
Go
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))
|
|
}
|