package vercel import ( "context" "encoding/json" "fmt" "net/http" vercelClient "github.com/vercel/terraform-provider-vercel/client" ) // DNSRecord is a helper struct to unmarshal the JSON response. // It embeds vercelClient.DNSRecord to reuse the upstream type, // but adds fields to handle API inconsistencies (type vs recordType, mxPriority). type DNSRecord struct { vercelClient.DNSRecord Type string `json:"type"` // Normally MXPriority would be uint16 type, but since vercelClient.DNSRecord uses int64, we'd better be consistent here // Later in GetZoneRecords we do a `uint16OrZero` to ensure the type is correct MXPriority int64 `json:"mxPriority"` } // pagination represents the pagination object in Vercel API responses. type pagination struct { Count int64 `json:"count"` Next *int64 `json:"next"` Prev *int64 `json:"prev"` } // listResponse represents the response from the Vercel List DNS Records API. type listResponse struct { Records []DNSRecord `json:"records"` Pagination pagination `json:"pagination"` } // Vercel API limit is max 100 const vercelAPIPaginationLimit = 100 // ListDNSRecords retrieves all DNS records for a domain, handling pagination. func (c *vercelProvider) ListDNSRecords(ctx context.Context, domain string) ([]DNSRecord, error) { var allRecords []DNSRecord var nextTimestamp int64 for { url := fmt.Sprintf("https://api.vercel.com/v4/domains/%s/records?limit=%d", domain, vercelAPIPaginationLimit) if c.teamID != "" { url += fmt.Sprintf("&teamId=%s", c.teamID) } if nextTimestamp != 0 { url += fmt.Sprintf("&until=%d", nextTimestamp) } var result listResponse err := c.doRequest(clientRequest{ ctx: ctx, method: http.MethodGet, url: url, }, &result, c.listLimiter) if err != nil { return nil, fmt.Errorf("failed to list DNS records: %w", err) } for _, r := range result.Records { // The official SDK expects 'recordType' but the API returns 'type'. // We explicitly map it here to fix the discrepancy. r.RecordType = r.Type // Ensure Domain field is set (it might not be in the record object itself) if r.Domain == "" { r.Domain = domain } if r.TeamID == "" { r.TeamID = c.teamID } allRecords = append(allRecords, r) } if result.Pagination.Next == nil { break } nextTimestamp = *result.Pagination.Next } return allRecords, nil } // httpsRecord structure for Vercel API type httpsRecord struct { Priority int64 `json:"priority"` Target string `json:"target"` Params string `json:"params,omitempty"` } // createDNSRecordRequest embeds the official SDK request but adds HTTPS support type createDNSRecordRequest struct { vercelClient.CreateDNSRecordRequest HTTPS *httpsRecord `json:"https,omitempty"` } // CreateDNSRecord creates a DNS record. func (c *vercelProvider) CreateDNSRecord(ctx context.Context, req createDNSRecordRequest) (*vercelClient.DNSRecord, error) { url := fmt.Sprintf("https://api.vercel.com/v4/domains/%s/records", req.Domain) if c.teamID != "" { url += "?teamId=" + c.teamID } var response struct { RecordID string `json:"uid"` } payloadJSON, err := json.Marshal(req) if err != nil { return nil, err } err = c.doRequest(clientRequest{ ctx: ctx, method: http.MethodPost, url: url, body: string(payloadJSON), }, &response, c.createLimiter) if err != nil { return nil, err } return &vercelClient.DNSRecord{ID: response.RecordID}, nil } // updateDNSRecordRequest embeds the official SDK request but adds HTTPS support type updateDNSRecordRequest struct { vercelClient.UpdateDNSRecordRequest HTTPS *httpsRecord `json:"https,omitempty"` } // UpdateDNSRecord updates a DNS record. func (c *vercelProvider) UpdateDNSRecord(ctx context.Context, recordID string, req updateDNSRecordRequest) (*vercelClient.DNSRecord, error) { url := fmt.Sprintf("https://api.vercel.com/v4/domains/records/%s", recordID) if c.teamID != "" { url += "?teamId=" + c.teamID } payloadJSON, err := json.Marshal(req) if err != nil { return nil, err } var result vercelClient.DNSRecord err = c.doRequest(clientRequest{ ctx: ctx, method: http.MethodPatch, url: url, body: string(payloadJSON), }, &result, c.updateLimiter) return &result, err } // DeleteDNSRecord deletes a DNS record. func (c *vercelProvider) DeleteDNSRecord(ctx context.Context, domain string, recordID string) error { url := fmt.Sprintf("https://api.vercel.com/v2/domains/%s/records/%s", domain, recordID) if c.teamID != "" { url += "?teamId=" + c.teamID } return c.doRequest(clientRequest{ ctx: ctx, method: http.MethodDelete, url: url, }, nil, c.deleteLimiter) }