2024-03-28 00:55:22 -04:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
2024-03-28 16:58:07 -04:00
|
|
|
"database/sql"
|
2024-03-28 00:55:22 -04:00
|
|
|
"log"
|
|
|
|
"net/http"
|
2024-03-28 12:57:35 -04:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2024-03-28 00:55:22 -04:00
|
|
|
|
2024-03-28 12:57:35 -04:00
|
|
|
"git.hatecomputers.club/hatecomputers/hatecomputers.club/adapters/cloudflare"
|
2024-03-28 00:55:22 -04:00
|
|
|
"git.hatecomputers.club/hatecomputers/hatecomputers.club/database"
|
2024-03-28 16:58:07 -04:00
|
|
|
"git.hatecomputers.club/hatecomputers/hatecomputers.club/utils"
|
2024-03-28 00:55:22 -04:00
|
|
|
)
|
|
|
|
|
2024-03-28 16:58:07 -04:00
|
|
|
const MAX_USER_RECORDS = 65
|
2024-03-28 12:57:35 -04:00
|
|
|
|
|
|
|
type FormError struct {
|
|
|
|
Errors []string
|
|
|
|
}
|
|
|
|
|
2024-03-28 16:58:07 -04:00
|
|
|
func userCanFuckWithDNSRecord(dbConn *sql.DB, user *database.User, record *database.DNSRecord) bool {
|
|
|
|
ownedByUser := (user.ID == record.UserID)
|
|
|
|
|
|
|
|
if !record.Internal {
|
|
|
|
publicallyOwnedByUser := (record.Name == user.Username || strings.HasSuffix(record.Name, "."+user.Username))
|
|
|
|
return ownedByUser && publicallyOwnedByUser
|
|
|
|
}
|
|
|
|
|
|
|
|
owner, err := database.FindFirstDomainOwnerId(dbConn, record.Name)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
userIsOwnerOfDomain := owner == user.ID
|
|
|
|
return ownedByUser && userIsOwnerOfDomain
|
2024-03-28 12:57:35 -04:00
|
|
|
}
|
|
|
|
|
2024-03-28 00:55:22 -04:00
|
|
|
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
|
2024-03-28 12:57:35 -04:00
|
|
|
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{},
|
|
|
|
}
|
|
|
|
|
2024-03-28 16:58:07 -04:00
|
|
|
internal := req.FormValue("internal") == "on"
|
2024-03-28 12:57:35 -04:00
|
|
|
name := req.FormValue("name")
|
2024-03-28 16:58:07 -04:00
|
|
|
if internal && !strings.HasSuffix(name, ".") {
|
|
|
|
name += "."
|
|
|
|
}
|
|
|
|
|
2024-03-28 12:57:35 -04:00
|
|
|
recordType := req.FormValue("type")
|
2024-03-28 16:58:07 -04:00
|
|
|
recordType = strings.ToUpper(recordType)
|
|
|
|
|
2024-03-28 12:57:35 -04:00
|
|
|
recordContent := req.FormValue("content")
|
|
|
|
ttl := req.FormValue("ttl")
|
|
|
|
ttlNum, err := strconv.Atoi(ttl)
|
|
|
|
if err != nil {
|
|
|
|
formErrors.Errors = append(formErrors.Errors, "invalid ttl")
|
|
|
|
}
|
|
|
|
|
|
|
|
dnsRecord := &database.DNSRecord{
|
2024-03-28 16:58:07 -04:00
|
|
|
UserID: context.User.ID,
|
|
|
|
Name: name,
|
|
|
|
Type: recordType,
|
|
|
|
Content: recordContent,
|
|
|
|
TTL: ttlNum,
|
|
|
|
Internal: internal,
|
2024-03-28 12:57:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
dnsRecords, err := database.GetUserDNSRecords(context.DBConn, context.User.ID)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
resp.WriteHeader(http.StatusInternalServerError)
|
|
|
|
return failure(context, req, resp)
|
|
|
|
}
|
|
|
|
if len(dnsRecords) >= MAX_USER_RECORDS {
|
|
|
|
formErrors.Errors = append(formErrors.Errors, "max records reached")
|
|
|
|
}
|
|
|
|
|
2024-03-28 16:58:07 -04:00
|
|
|
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")
|
2024-03-28 12:57:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(formErrors.Errors) == 0 {
|
2024-03-28 16:58:07 -04:00
|
|
|
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
|
2024-03-28 12:57:35 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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)["DNSRecords"] = dnsRecords
|
|
|
|
(*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)
|
|
|
|
}
|
|
|
|
|
2024-03-28 16:58:07 -04:00
|
|
|
if !userCanFuckWithDNSRecord(context.DBConn, context.User, record) {
|
2024-03-28 12:57:35 -04:00
|
|
|
resp.WriteHeader(http.StatusUnauthorized)
|
|
|
|
return failure(context, req, resp)
|
|
|
|
}
|
|
|
|
|
2024-03-28 16:58:07 -04:00
|
|
|
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)
|
|
|
|
}
|
2024-03-28 12:57:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
err = database.DeleteDNSRecord(context.DBConn, recordId)
|
|
|
|
if err != nil {
|
|
|
|
resp.WriteHeader(http.StatusInternalServerError)
|
|
|
|
return failure(context, req, resp)
|
|
|
|
}
|
2024-03-28 00:55:22 -04:00
|
|
|
|
2024-03-28 12:57:35 -04:00
|
|
|
http.Redirect(resp, req, "/dns", http.StatusFound)
|
2024-03-28 00:55:22 -04:00
|
|
|
return success(context, req, resp)
|
|
|
|
}
|
|
|
|
}
|