package api import ( "database/sql" "fmt" "log" "net/http" "strconv" "strings" "git.hatecomputers.club/hatecomputers/hatecomputers.club/adapters/cloudflare" "git.hatecomputers.club/hatecomputers/hatecomputers.club/database" "git.hatecomputers.club/hatecomputers/hatecomputers.club/utils" ) const MAX_USER_RECORDS = 65 type FormError struct { Errors []string } func userCanFuckWithDNSRecord(dbConn *sql.DB, user *database.User, record *database.DNSRecord) bool { ownedByUser := (user.ID == record.UserID) if !ownedByUser { return false } if !record.Internal { userOwnedDomains := []string{ fmt.Sprintf("%s", user.Username), fmt.Sprintf("%s.endpoints", user.Username), } for _, domain := range userOwnedDomains { isInSubDomain := strings.HasSuffix(record.Name, "."+domain) if domain == record.Name || isInSubDomain { return true } } return false } owner, err := database.FindFirstDomainOwnerId(dbConn, record.Name) if err != nil { log.Println(err) return false } userIsOwnerOfDomain := owner == user.ID return ownedByUser && userIsOwnerOfDomain } func ListDNSRecordsContinuation(context *RequestContext, req *http.Request, resp http.ResponseWriter) ContinuationChain { return func(success Continuation, failure Continuation) ContinuationChain { dnsRecords, err := database.GetUserDNSRecords(context.DBConn, context.User.ID) if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } (*context.TemplateData)["DNSRecords"] = dnsRecords return success(context, req, resp) } } func CreateDNSRecordContinuation(context *RequestContext, req *http.Request, resp http.ResponseWriter) ContinuationChain { return func(success Continuation, failure Continuation) ContinuationChain { formErrors := FormError{ Errors: []string{}, } internal := req.FormValue("internal") == "on" name := req.FormValue("name") if internal && !strings.HasSuffix(name, ".") { name += "." } recordType := req.FormValue("type") recordType = strings.ToUpper(recordType) recordContent := req.FormValue("content") ttl := req.FormValue("ttl") ttlNum, err := strconv.Atoi(ttl) if err != nil { formErrors.Errors = append(formErrors.Errors, "invalid ttl") } dnsRecordCount, err := database.CountUserDNSRecords(context.DBConn, context.User.ID) if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } if dnsRecordCount >= MAX_USER_RECORDS { formErrors.Errors = append(formErrors.Errors, "max records reached") } dnsRecord := &database.DNSRecord{ UserID: context.User.ID, Name: name, Type: recordType, Content: recordContent, TTL: ttlNum, Internal: internal, } if !userCanFuckWithDNSRecord(context.DBConn, context.User, dnsRecord) { formErrors.Errors = append(formErrors.Errors, "'name' must end with "+context.User.Username+" or you must be a domain owner for internal domains") } if len(formErrors.Errors) == 0 { if dnsRecord.Internal { dnsRecord.ID = utils.RandomId() } else { cloudflareRecordId, err := cloudflare.CreateDNSRecord(context.Args.CloudflareZone, context.Args.CloudflareToken, dnsRecord) if err != nil { log.Println(err) formErrors.Errors = append(formErrors.Errors, err.Error()) } dnsRecord.ID = cloudflareRecordId } } if len(formErrors.Errors) == 0 { _, err := database.SaveDNSRecord(context.DBConn, dnsRecord) if err != nil { log.Println(err) formErrors.Errors = append(formErrors.Errors, "error saving record") } } if len(formErrors.Errors) == 0 { http.Redirect(resp, req, "/dns", http.StatusFound) return success(context, req, resp) } (*context.TemplateData)["FormError"] = &formErrors (*context.TemplateData)["RecordForm"] = dnsRecord resp.WriteHeader(http.StatusBadRequest) return failure(context, req, resp) } } func DeleteDNSRecordContinuation(context *RequestContext, req *http.Request, resp http.ResponseWriter) ContinuationChain { return func(success Continuation, failure Continuation) ContinuationChain { recordId := req.FormValue("id") record, err := database.GetDNSRecord(context.DBConn, recordId) if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } if !userCanFuckWithDNSRecord(context.DBConn, context.User, record) { resp.WriteHeader(http.StatusUnauthorized) return failure(context, req, resp) } if !record.Internal { err = cloudflare.DeleteDNSRecord(context.Args.CloudflareZone, context.Args.CloudflareToken, recordId) if err != nil { log.Println(err) resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } } err = database.DeleteDNSRecord(context.DBConn, recordId) if err != nil { resp.WriteHeader(http.StatusInternalServerError) return failure(context, req, resp) } http.Redirect(resp, req, "/dns", http.StatusFound) return success(context, req, resp) } }