package dns import ( "net" "strings" dnsModel "dove/models/dns" domainModel "dove/models/domain" dnsRepo "dove/repositories/dns" domainRepo "dove/repositories/domain" "dove/utils/shortcuts" ) type RecordEntry struct { ID uint `json:"id"` Type string `json:"type"` Name string `json:"name"` Value string `json:"value"` TTL uint32 `json:"ttl"` Priority uint16 `json:"priority"` } type DomainRecordsResponse struct { Domain domainModel.Domain `json:"domain"` Records []RecordEntry `json:"records"` } type UpdateRecordRequest struct { Name string `form:"name"` Value string `form:"value"` TTL uint32 `form:"ttl"` Priority uint16 `form:"priority"` } type CreateRecordRequest struct { DomainID uint `form:"domain_id"` RecordType string `form:"record_type"` Name string `form:"name"` Value string `form:"value"` TTL uint32 `form:"ttl"` Priority uint16 `form:"priority"` Weight uint16 `form:"weight"` Port uint16 `form:"port"` Protocol string `form:"protocol"` } func DomainRecords(domainID uint) (*DomainRecordsResponse, *shortcuts.Error) { foundDomain := domainRepo.FindDomainByID(domainID) if foundDomain == nil { return nil, shortcuts.ServiceError(shortcuts.NotFound, DomainNotFound) } records := collectAllRecords(domainID) return &DomainRecordsResponse{ Domain: *foundDomain, Records: records, }, nil } func CreateRecord(request CreateRecordRequest) *shortcuts.Error { foundDomain := domainRepo.FindDomainByID(request.DomainID) if foundDomain == nil { return shortcuts.ServiceError(shortcuts.NotFound, DomainNotFound) } name := strings.TrimSpace(request.Name) value := strings.TrimSpace(request.Value) ttl := request.TTL if ttl == 0 { ttl = 1 } switch request.RecordType { case "A": return createARecord(request.DomainID, name, value, ttl) case "AAAA": return createAAAARecord(request.DomainID, name, value, ttl) case "CNAME": return createCNAMERecord(request.DomainID, name, value, ttl) case "MX": return createMXRecord(request.DomainID, name, value, ttl, request.Priority) case "TXT": return createTXTRecord(request.DomainID, name, value, ttl) case "SRV": return createSRVRecord(request.DomainID, name, value, ttl, request.Priority, request.Weight, request.Port, request.Protocol) default: return shortcuts.ServiceError(shortcuts.BadRequest, TypeInvalid) } } func UpdateRecord(recordType string, recordID uint, request UpdateRecordRequest) *shortcuts.Error { name := strings.TrimSpace(request.Name) value := strings.TrimSpace(request.Value) ttl := request.TTL if ttl == 0 { ttl = 1 } switch recordType { case "A": record := dnsRepo.FindARecordByID(recordID) if record == nil { return shortcuts.ServiceError(shortcuts.NotFound, RecordNotFound) } if name != "" { record.Name = name } if value != "" { record.Address = value } record.TTL = ttl if updateError := dnsRepo.UpdateARecord(record); updateError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordUpdateFailed) } case "AAAA": record := dnsRepo.FindAAAARecordByID(recordID) if record == nil { return shortcuts.ServiceError(shortcuts.NotFound, RecordNotFound) } if name != "" { record.Name = name } if value != "" { record.Address = value } record.TTL = ttl if updateError := dnsRepo.UpdateAAAARecord(record); updateError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordUpdateFailed) } case "CNAME": record := dnsRepo.FindCNAMERecordByID(recordID) if record == nil { return shortcuts.ServiceError(shortcuts.NotFound, RecordNotFound) } if name != "" { record.Name = name } if value != "" { record.Target = value } record.TTL = ttl if updateError := dnsRepo.UpdateCNAMERecord(record); updateError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordUpdateFailed) } case "MX": record := dnsRepo.FindMXRecordByID(recordID) if record == nil { return shortcuts.ServiceError(shortcuts.NotFound, RecordNotFound) } if name != "" { record.Name = name } if value != "" { record.Target = value } if request.Priority > 0 { record.Priority = request.Priority } record.TTL = ttl if updateError := dnsRepo.UpdateMXRecord(record); updateError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordUpdateFailed) } case "TXT": record := dnsRepo.FindTXTRecordByID(recordID) if record == nil { return shortcuts.ServiceError(shortcuts.NotFound, RecordNotFound) } if name != "" { record.Name = name } if value != "" { record.Content = value } record.TTL = ttl if updateError := dnsRepo.UpdateTXTRecord(record); updateError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordUpdateFailed) } case "SRV": record := dnsRepo.FindSRVRecordByID(recordID) if record == nil { return shortcuts.ServiceError(shortcuts.NotFound, RecordNotFound) } if name != "" { record.Name = name } if value != "" { record.Target = value } if request.Priority > 0 { record.Priority = request.Priority } record.TTL = ttl if updateError := dnsRepo.UpdateSRVRecord(record); updateError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordUpdateFailed) } default: return shortcuts.ServiceError(shortcuts.BadRequest, TypeInvalid) } return nil } func DeleteRecord(recordType string, recordID uint) *shortcuts.Error { switch recordType { case "A": record := dnsRepo.FindARecordByID(recordID) if record == nil { return shortcuts.ServiceError(shortcuts.NotFound, RecordNotFound) } if deleteError := dnsRepo.DeleteARecord(record); deleteError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordDeletionFailed) } case "AAAA": record := dnsRepo.FindAAAARecordByID(recordID) if record == nil { return shortcuts.ServiceError(shortcuts.NotFound, RecordNotFound) } if deleteError := dnsRepo.DeleteAAAARecord(record); deleteError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordDeletionFailed) } case "CNAME": record := dnsRepo.FindCNAMERecordByID(recordID) if record == nil { return shortcuts.ServiceError(shortcuts.NotFound, RecordNotFound) } if deleteError := dnsRepo.DeleteCNAMERecord(record); deleteError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordDeletionFailed) } case "MX": record := dnsRepo.FindMXRecordByID(recordID) if record == nil { return shortcuts.ServiceError(shortcuts.NotFound, RecordNotFound) } if deleteError := dnsRepo.DeleteMXRecord(record); deleteError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordDeletionFailed) } case "TXT": record := dnsRepo.FindTXTRecordByID(recordID) if record == nil { return shortcuts.ServiceError(shortcuts.NotFound, RecordNotFound) } if deleteError := dnsRepo.DeleteTXTRecord(record); deleteError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordDeletionFailed) } case "SRV": record := dnsRepo.FindSRVRecordByID(recordID) if record == nil { return shortcuts.ServiceError(shortcuts.NotFound, RecordNotFound) } if deleteError := dnsRepo.DeleteSRVRecord(record); deleteError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordDeletionFailed) } default: return shortcuts.ServiceError(shortcuts.BadRequest, TypeInvalid) } return nil } func collectAllRecords(domainID uint) []RecordEntry { var entries []RecordEntry for _, record := range dnsRepo.FindARecordsByDomainID(domainID) { entries = append(entries, RecordEntry{ ID: record.ID, Type: "A", Name: record.Name, Value: record.Address, TTL: record.TTL, }) } for _, record := range dnsRepo.FindAAAARecordsByDomainID(domainID) { entries = append(entries, RecordEntry{ ID: record.ID, Type: "AAAA", Name: record.Name, Value: record.Address, TTL: record.TTL, }) } for _, record := range dnsRepo.FindCNAMERecordsByDomainID(domainID) { entries = append(entries, RecordEntry{ ID: record.ID, Type: "CNAME", Name: record.Name, Value: record.Target, TTL: record.TTL, }) } for _, record := range dnsRepo.FindMXRecordsByDomainID(domainID) { entries = append(entries, RecordEntry{ ID: record.ID, Type: "MX", Name: record.Name, Value: record.Target, TTL: record.TTL, Priority: record.Priority, }) } for _, record := range dnsRepo.FindTXTRecordsByDomainID(domainID) { entries = append(entries, RecordEntry{ ID: record.ID, Type: "TXT", Name: record.Name, Value: record.Content, TTL: record.TTL, }) } for _, record := range dnsRepo.FindSRVRecordsByDomainID(domainID) { entries = append(entries, RecordEntry{ ID: record.ID, Type: "SRV", Name: record.Name, Value: record.Target, TTL: record.TTL, Priority: record.Priority, }) } return entries } func createARecord(domainID uint, name string, address string, ttl uint32) *shortcuts.Error { if address == "" { return shortcuts.ServiceError(shortcuts.BadRequest, AddressRequired) } parsedIP := net.ParseIP(address) if parsedIP == nil || parsedIP.To4() == nil { return shortcuts.ServiceError(shortcuts.BadRequest, AddressInvalid) } if name == "" { name = "@" } record := &dnsModel.ARecord{ DomainID: domainID, Name: name, Address: address, TTL: ttl, } if createError := dnsRepo.CreateARecord(record); createError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordCreationFailed) } return nil } func createAAAARecord(domainID uint, name string, address string, ttl uint32) *shortcuts.Error { if address == "" { return shortcuts.ServiceError(shortcuts.BadRequest, AddressRequired) } parsedIP := net.ParseIP(address) if parsedIP == nil || parsedIP.To16() == nil || parsedIP.To4() != nil { return shortcuts.ServiceError(shortcuts.BadRequest, AddressIPv6Invalid) } if name == "" { name = "@" } record := &dnsModel.AAAARecord{ DomainID: domainID, Name: name, Address: address, TTL: ttl, } if createError := dnsRepo.CreateAAAARecord(record); createError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordCreationFailed) } return nil } func createCNAMERecord(domainID uint, name string, target string, ttl uint32) *shortcuts.Error { if name == "" { return shortcuts.ServiceError(shortcuts.BadRequest, NameRequired) } if target == "" { return shortcuts.ServiceError(shortcuts.BadRequest, TargetRequired) } record := &dnsModel.CNAMERecord{ DomainID: domainID, Name: name, Target: target, TTL: ttl, } if createError := dnsRepo.CreateCNAMERecord(record); createError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordCreationFailed) } return nil } func createMXRecord(domainID uint, name string, target string, ttl uint32, priority uint16) *shortcuts.Error { if target == "" { return shortcuts.ServiceError(shortcuts.BadRequest, TargetRequired) } if name == "" { name = "@" } if priority == 0 { priority = 10 } record := &dnsModel.MXRecord{ DomainID: domainID, Name: name, Target: target, Priority: priority, TTL: ttl, } if createError := dnsRepo.CreateMXRecord(record); createError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordCreationFailed) } return nil } func createTXTRecord(domainID uint, name string, content string, ttl uint32) *shortcuts.Error { if content == "" { return shortcuts.ServiceError(shortcuts.BadRequest, ContentRequired) } if name == "" { name = "@" } record := &dnsModel.TXTRecord{ DomainID: domainID, Name: name, Content: content, TTL: ttl, } if createError := dnsRepo.CreateTXTRecord(record); createError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordCreationFailed) } return nil } func createSRVRecord(domainID uint, name string, target string, ttl uint32, priority uint16, weight uint16, port uint16, protocol string) *shortcuts.Error { if name == "" { return shortcuts.ServiceError(shortcuts.BadRequest, NameRequired) } if target == "" { return shortcuts.ServiceError(shortcuts.BadRequest, TargetRequired) } if port == 0 { return shortcuts.ServiceError(shortcuts.BadRequest, PortRequired) } if protocol == "" { protocol = "tcp" } record := &dnsModel.SRVRecord{ DomainID: domainID, Name: name, Target: target, Priority: priority, Weight: weight, Port: port, Protocol: protocol, TTL: ttl, } if createError := dnsRepo.CreateSRVRecord(record); createError != nil { return shortcuts.ServiceError(shortcuts.Internal, RecordCreationFailed) } return nil }