|
@@ -0,0 +1,193 @@
|
|
|
|
+package handlers
|
|
|
|
+
|
|
|
|
+import (
|
|
|
|
+ "bufio"
|
|
|
|
+ "byom-infra-api/config"
|
|
|
|
+ "encoding/json"
|
|
|
|
+ "fmt"
|
|
|
|
+ "log"
|
|
|
|
+ "math/rand"
|
|
|
|
+ "net/http"
|
|
|
|
+ "os"
|
|
|
|
+ "regexp"
|
|
|
|
+ "strings"
|
|
|
|
+
|
|
|
|
+ "github.com/gorilla/mux"
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+type DNSRecord struct {
|
|
|
|
+ FieldType string `json:"fieldType"`
|
|
|
|
+ SubDomain string `json:"subDomain"`
|
|
|
|
+ Target string `json:"target"`
|
|
|
|
+ TTL int `json:"ttl"`
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+type DNSHandler struct {
|
|
|
|
+ Zone string
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func NewDNSHandler() *DNSHandler {
|
|
|
|
+ // Try to get zone from environment variable first
|
|
|
|
+ zone := os.Getenv("OVH_DNS_ZONE")
|
|
|
|
+
|
|
|
|
+ // Fallback to default if neither is set
|
|
|
|
+ if zone == "" {
|
|
|
|
+ zone = "linuxforward.com"
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return &DNSHandler{
|
|
|
|
+ Zone: zone,
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// generateRandomWord generates a random 6-letter word from /usr/share/dict/words
|
|
|
|
+func (h *DNSHandler) generateRandomWord() (string, error) {
|
|
|
|
+ file, err := os.Open("/usr/share/dict/words")
|
|
|
|
+ if err != nil {
|
|
|
|
+ return "", err
|
|
|
|
+ }
|
|
|
|
+ defer file.Close()
|
|
|
|
+
|
|
|
|
+ var words []string
|
|
|
|
+ scanner := bufio.NewScanner(file)
|
|
|
|
+ regex := regexp.MustCompile(`^[a-zA-Z]{6}$`)
|
|
|
|
+
|
|
|
|
+ for scanner.Scan() {
|
|
|
|
+ word := strings.ToLower(scanner.Text())
|
|
|
|
+ if regex.MatchString(word) {
|
|
|
|
+ words = append(words, word)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if len(words) == 0 {
|
|
|
|
+ return "", fmt.Errorf("no suitable words found")
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return words[rand.Intn(len(words))], nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// CreateDNSRecord handles the creation of a new DNS record
|
|
|
|
+func (h *DNSHandler) CreateDNSRecord(w http.ResponseWriter, r *http.Request) {
|
|
|
|
+ log.Printf("Handling POST request for new DNS record from %s", r.RemoteAddr)
|
|
|
|
+
|
|
|
|
+ // Parse request body for target IP
|
|
|
|
+ var request struct {
|
|
|
|
+ Target string `json:"target"`
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
|
|
|
+ log.Printf("Error decoding request body: %v", err)
|
|
|
|
+ http.Error(w, "Invalid request body", http.StatusBadRequest)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Generate random subdomain
|
|
|
|
+ word1, err := h.generateRandomWord()
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Printf("Error generating first word: %v", err)
|
|
|
|
+ http.Error(w, "Failed to generate subdomain", http.StatusInternalServerError)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ word2, err := h.generateRandomWord()
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Printf("Error generating second word: %v", err)
|
|
|
|
+ http.Error(w, "Failed to generate subdomain", http.StatusInternalServerError)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Create DNS record
|
|
|
|
+ record := DNSRecord{
|
|
|
|
+ FieldType: "A",
|
|
|
|
+ SubDomain: word1 + "-" + word2,
|
|
|
|
+ Target: request.Target,
|
|
|
|
+ TTL: 0,
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var response DNSRecord
|
|
|
|
+ err = config.OVHClient.Post("/domain/zone/"+h.Zone+"/record", record, &response)
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Printf("Error creating DNS record: %v", err)
|
|
|
|
+ http.Error(w, "Failed to create DNS record: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Refresh the zone
|
|
|
|
+ err = h.refreshZone()
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Printf("Warning: zone refresh failed: %v", err)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ log.Printf("Successfully created DNS record: %s.%s", response.SubDomain, h.Zone)
|
|
|
|
+
|
|
|
|
+ w.Header().Set("Content-Type", "application/json")
|
|
|
|
+ w.WriteHeader(http.StatusCreated)
|
|
|
|
+ if err := json.NewEncoder(w).Encode(response); err != nil {
|
|
|
|
+ log.Printf("Error encoding response: %v", err)
|
|
|
|
+ http.Error(w, "Failed to encode response", http.StatusInternalServerError)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// DeleteDNSRecord handles the deletion of a DNS record
|
|
|
|
+func (h *DNSHandler) DeleteDNSRecord(w http.ResponseWriter, r *http.Request) {
|
|
|
|
+ vars := mux.Vars(r)
|
|
|
|
+ recordID := vars["id"]
|
|
|
|
+ log.Printf("Handling DELETE request for DNS record: %s from %s", recordID, r.RemoteAddr)
|
|
|
|
+
|
|
|
|
+ err := config.OVHClient.Delete("/domain/zone/"+h.Zone+"/record/"+recordID, nil)
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Printf("Error deleting DNS record %s: %v", recordID, err)
|
|
|
|
+ http.Error(w, "Failed to delete DNS record: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Refresh the zone
|
|
|
|
+ err = h.refreshZone()
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Printf("Warning: zone refresh failed: %v", err)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ log.Printf("Successfully deleted DNS record: %s", recordID)
|
|
|
|
+ w.WriteHeader(http.StatusNoContent)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// refreshZone refreshes the DNS zone
|
|
|
|
+func (h *DNSHandler) refreshZone() error {
|
|
|
|
+ return config.OVHClient.Post("/domain/zone/"+h.Zone+"/refresh", nil, nil)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// ListDNSRecords handles listing all A records in the DNS zone
|
|
|
|
+func (h *DNSHandler) ListDNSRecords(w http.ResponseWriter, r *http.Request) {
|
|
|
|
+ log.Printf("Handling GET request for DNS records from %s", r.RemoteAddr)
|
|
|
|
+
|
|
|
|
+ var recordIDs []int
|
|
|
|
+ err := config.OVHClient.Get("/domain/zone/"+h.Zone+"/record", &recordIDs)
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Printf("Error getting DNS record IDs: %v", err)
|
|
|
|
+ http.Error(w, "Failed to get DNS records: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var records []DNSRecord
|
|
|
|
+ for _, id := range recordIDs {
|
|
|
|
+ var record DNSRecord
|
|
|
|
+ err := config.OVHClient.Get(fmt.Sprintf("/domain/zone/%s/record/%d", h.Zone, id), &record)
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Printf("Error getting DNS record %d: %v", id, err)
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Only include A records
|
|
|
|
+ if record.FieldType == "A" {
|
|
|
|
+ records = append(records, record)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ w.Header().Set("Content-Type", "application/json")
|
|
|
|
+ if err := json.NewEncoder(w).Encode(records); err != nil {
|
|
|
|
+ log.Printf("Error encoding response: %v", err)
|
|
|
|
+ http.Error(w, "Failed to encode response", http.StatusInternalServerError)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+}
|