package api import ( "bytes" "fmt" "io" "log" "net/http" "os" "strings" "git.freecumextremist.com/grumbulon/pomme/internal" "git.freecumextremist.com/grumbulon/pomme/internal/util" "github.com/go-chi/jwtauth/v5" "github.com/miekg/dns" "gorm.io/gorm" ) // ZoneRequest represents a Zone file request. type ZoneRequest struct { *Zone User string `json:"user,omitempty" gorm:"foreignKey:username;references:User"` } // Zone struct represents a zonefile. type Zone struct { gorm.Model FileName string `json:"name"` RawFileName string `json:"rawname"` Body string `json:"body,omitempty"` } // Upload godoc // // @Summary upload a zonefile // @Description upload a file -- you must specify "Bearer" before entering your token // @Tags DNS // @Accept mpfd // @Produce json // @Param file formData file true "Zonefile to upload" // @Success 200 {object} httpSuccess // @Failure 500 {object} httpInternalServerError // @Param Authorization header string true "Bearer Token" // // @Security Bearer // // @Router /api/upload [post] func ReceiveFile(w http.ResponseWriter, r *http.Request) { _, claims, _ := jwtauth.FromContext(r.Context()) var buf bytes.Buffer r.Body = http.MaxBytesReader(w, r.Body, 1*1024*1024) // approx 1 mb max upload file, header, err := r.FormFile("file") if err != nil { internalServerError(w, fmt.Sprintf("file upload failed: %v", err)) return } defer file.Close() //nolint: errcheck name := strings.Split(header.Filename, ".") if _, err = io.Copy(&buf, file); err != nil { internalServerError(w, "internal server error") return } if err = util.MakeLocal(name[0], claims["username"].(string), buf); err != nil { internalServerError(w, "internal server error") return } db, ok := r.Context().Value(keyPrincipalContextID).(*gorm.DB) if !ok { internalServerError(w, "internal server error") return } db.Create( &ZoneRequest{ User: claims["username"].(string), Zone: &Zone{ FileName: fmt.Sprintf("tmpfile-%s-%s", name[0], claims["username"].(string)), RawFileName: name[0], }, }) buf.Reset() } // Parse godoc // // @Summary parse your zonefile // @Description parse your zonefile -- you must specify "Bearer" before entering your token // @Tags DNS // @Accept mpfd // @Produce json // @Param filename query string true "Zonefile name" // @Success 200 {object} httpSuccess // @Failure 500 {object} httpInternalServerError // @Param Authorization header string true "Bearer Token" // // @Security Bearer // // @Router /api/parse [post] func ZoneFiles(w http.ResponseWriter, r *http.Request) { var result internal.ZoneRequest _, claims, _ := jwtauth.FromContext(r.Context()) err := r.ParseForm() if err != nil { internalServerError(w, "unable to parse request") return } filename := r.Form.Get("filename") if filename == "" { internalServerError(w, "no filename parsed") return } db, ok := r.Context().Value(keyPrincipalContextID).(*gorm.DB) if !ok { internalServerError(w, "internal server error") return } db.Where(ZoneRequest{ Zone: &Zone{ RawFileName: filename, }, User: claims["username"].(string), }).First(&result) if result == (internal.ZoneRequest{}) { internalServerError(w, "internal server error") return } zoneFile := newZoneRequest(result.RawFileName, claims["username"].(string)) if err := zoneFile.Parse(); err != nil { internalServerError(w, fmt.Sprintf("unable to parse zonefile: %v", err)) return } } func newZoneRequest(filename string, user string) *ZoneRequest { dat, err := os.ReadFile(fmt.Sprintf("/tmp/tmpfile-%s-%s", filename, user)) if err != nil { return &ZoneRequest{} } return &ZoneRequest{ User: user, Zone: &Zone{ FileName: fmt.Sprintf("tmpfile-%s-%s", filename, user), RawFileName: filename, Body: string(dat), }, } } // Parse will be used to parse zonefiles. func (zone *ZoneRequest) Parse() error { zp := dns.NewZoneParser(strings.NewReader(zone.Body), "", "") for rr, ok := zp.Next(); ok; rr, ok = zp.Next() { log.Println(rr) } if err := zp.Err(); err != nil { return fmt.Errorf("unable to parse Zonefile: %w", err) } return nil }