package goinwx import ( "errors" "strconv" "time" "github.com/fatih/structs" "github.com/go-viper/mapstructure/v2" ) const ( methodDomainCheck = "domain.check" methodDomainCreate = "domain.create" methodDomainDelete = "domain.delete" methodDomainGetPrices = "domain.getPrices" methodDomainGetRules = "domain.getRules" methodDomainInfo = "domain.info" methodDomainList = "domain.list" methodDomainLog = "domain.log" methodDomainPush = "domain.push" methodDomainRenew = "domain.renew" methodDomainRestore = "domain.restore" methodDomainStats = "domain.stats" methodDomainTrade = "domain.trade" methodDomainTransfer = "domain.transfer" methodDomainTransferOut = "domain.transferOut" methodDomainUpdate = "domain.update" methodDomainWhois = "domain.whois" ) // DomainService API access to Domain. type DomainService service // Check checks domains. func (s *DomainService) Check(domains []string) ([]DomainCheckResponse, error) { req := s.client.NewRequest(methodDomainCheck, map[string]any{ "domain": domains, "wide": "2", }) resp, err := s.client.Do(req) if err != nil { return nil, err } root := new(domainCheckResponseRoot) err = mapstructure.Decode(resp, &root) if err != nil { return nil, err } return root.Domains, nil } // GetPrices gets TLDS prices. func (s *DomainService) GetPrices(tlds []string) ([]DomainPriceResponse, error) { req := s.client.NewRequest(methodDomainGetPrices, map[string]any{ "tld": tlds, "vat": false, }) resp, err := s.client.Do(req) if err != nil { return nil, err } root := new(domainPriceResponseRoot) err = mapstructure.Decode(resp, &root) if err != nil { return nil, err } return root.Prices, nil } // Register registers a domain. func (s *DomainService) Register(request *DomainRegisterRequest) (*DomainRegisterResponse, error) { req := s.client.NewRequest(methodDomainCreate, structs.Map(request)) resp, err := s.client.Do(req) if err != nil { return nil, err } var result DomainRegisterResponse err = mapstructure.Decode(resp, &result) if err != nil { return nil, err } return &result, nil } // Delete deletes a domain. func (s *DomainService) Delete(domain string, scheduledDate time.Time) error { req := s.client.NewRequest(methodDomainDelete, map[string]any{ "domain": domain, "scDate": scheduledDate.Format(time.RFC3339), }) _, err := s.client.Do(req) return err } // Info gets information about a domain. func (s *DomainService) Info(domain string, roID int) (*DomainInfoResponse, error) { req := s.client.NewRequest(methodDomainInfo, map[string]any{ "domain": domain, "wide": "2", }) if roID != 0 { req.Args["roId"] = roID } resp, err := s.client.Do(req) if err != nil { return nil, err } var result DomainInfoResponse err = mapstructure.Decode(resp, &result) if err != nil { return nil, err } return &result, nil } // List lists domains. func (s *DomainService) List(request *DomainListRequest) (*DomainList, error) { if request == nil { return nil, errors.New("request can't be nil") } requestMap := structs.Map(request) requestMap["wide"] = "2" req := s.client.NewRequest(methodDomainList, requestMap) resp, err := s.client.Do(req) if err != nil { return nil, err } // This is a (temporary) workaround to convert the API response from string to int. // The code only applies when string is being return, otherwise it's being skipped. // As per docs at https://www.inwx.com/en/help/apidoc/f/ch02s08.html#domain.list we should get int here, but apparently it's not the case. // Ticket 10026265 with INWX was raised. if countStr, ok := resp["count"].(string); ok { // If we land here, we got string back, but we expect int. // Converting value to int and writing it to the response. if num, ok := strconv.Atoi(countStr); ok == nil { resp["count"] = num } } var result DomainList err = mapstructure.Decode(resp, &result) if err != nil { return nil, err } return &result, nil } // Whois information about a domains. func (s *DomainService) Whois(domain string) (string, error) { req := s.client.NewRequest(methodDomainWhois, map[string]any{ "domain": domain, }) resp, err := s.client.Do(req) if err != nil { return "", err } var result map[string]string err = mapstructure.Decode(resp, &result) if err != nil { return "", err } return result["whois"], nil } // Update updates domain information. func (s *DomainService) Update(request *DomainUpdateRequest) (float32, error) { req := s.client.NewRequest(methodDomainUpdate, structs.Map(request)) resp, err := s.client.Do(req) if err != nil { return 0, err } var result DomainUpdateResponse err = mapstructure.Decode(resp, &result) if err != nil { return 0, err } return result.Price, nil } type domainCheckResponseRoot struct { Domains []DomainCheckResponse `mapstructure:"domain"` } // DomainCheckResponse API model. type DomainCheckResponse struct { Available int `mapstructure:"avail"` Status string `mapstructure:"status"` Name string `mapstructure:"name"` Domain string `mapstructure:"domain"` TLD string `mapstructure:"tld"` CheckMethod string `mapstructure:"checkmethod"` Price float32 `mapstructure:"price"` CheckTime float32 `mapstructure:"checktime"` } type domainPriceResponseRoot struct { Prices []DomainPriceResponse `mapstructure:"price"` } // DomainPriceResponse API model. type DomainPriceResponse struct { Tld string `mapstructure:"tld"` Currency string `mapstructure:"currency"` CreatePrice float32 `mapstructure:"createPrice"` MonthlyCreatePrice float32 `mapstructure:"monthlyCreatePrice"` TransferPrice float32 `mapstructure:"transferPrice"` RenewalPrice float32 `mapstructure:"renewalPrice"` MonthlyRenewalPrice float32 `mapstructure:"monthlyRenewalPrice"` UpdatePrice float32 `mapstructure:"updatePrice"` TradePrice float32 `mapstructure:"tradePrice"` TrusteePrice float32 `mapstructure:"trusteePrice"` MonthlyTrusteePrice float32 `mapstructure:"monthlyTrusteePrice"` CreatePeriod int `mapstructure:"createPeriod"` TransferPeriod int `mapstructure:"transferPeriod"` RenewalPeriod int `mapstructure:"renewalPeriod"` TradePeriod int `mapstructure:"tradePeriod"` } // DomainRegisterRequest API model. type DomainRegisterRequest struct { Domain string `structs:"domain"` Period string `structs:"period,omitempty"` Registrant int `structs:"registrant"` Admin int `structs:"admin"` Tech int `structs:"tech"` Billing int `structs:"billing"` Nameservers []string `structs:"ns,omitempty"` TransferLock string `structs:"transferLock,omitempty"` RenewalMode string `structs:"renewalMode,omitempty"` WhoisProvider string `structs:"whoisProvider,omitempty"` WhoisURL string `structs:"whoisUrl,omitempty"` ScDate string `structs:"scDate,omitempty"` ExtDate string `structs:"extDate,omitempty"` Asynchron string `structs:"asynchron,omitempty"` Voucher string `structs:"voucher,omitempty"` Testing string `structs:"testing,omitempty"` } // DomainRegisterResponse API model. type DomainRegisterResponse struct { RoID int `mapstructure:"roId"` Price float32 `mapstructure:"price"` Currency string `mapstructure:"currency"` } // DomainInfoResponse API model. type DomainInfoResponse struct { RoID int `mapstructure:"roId"` Domain string `mapstructure:"domain"` DomainAce string `mapstructure:"domainAce"` Period string `mapstructure:"period"` CrDate time.Time `mapstructure:"crDate"` ExDate time.Time `mapstructure:"exDate"` UpDate time.Time `mapstructure:"upDate"` ReDate time.Time `mapstructure:"reDate"` ScDate time.Time `mapstructure:"scDate"` TransferLock bool `mapstructure:"transferLock"` Status string `mapstructure:"status"` AuthCode string `mapstructure:"authCode"` RenewalMode string `mapstructure:"renewalMode"` TransferMode string `mapstructure:"transferMode"` Registrant int `mapstructure:"registrant"` Admin int `mapstructure:"admin"` Tech int `mapstructure:"tech"` Billing int `mapstructure:"billing"` Nameservers []string `mapstructure:"ns"` NoDelegation bool `mapstructure:"noDelegation"` Contacts map[string]Contact `mapstructure:"contact"` } // Contact API model. type Contact struct { RoID int `mapstructure:"roId"` ID string `mapstructure:"id"` Type string `mapstructure:"type"` Name string `mapstructure:"name"` Org string `mapstructure:"org"` Street string `mapstructure:"street"` City string `mapstructure:"city"` PostalCode string `mapstructure:"pc"` StateProvince string `mapstructure:"sp"` Country string `mapstructure:"cc"` Phone string `mapstructure:"voice"` Fax string `mapstructure:"fax"` Email string `mapstructure:"email"` Remarks string `mapstructure:"remarks"` Protection string `mapstructure:"protection"` } // DomainListRequest API model. type DomainListRequest struct { Domain string `structs:"domain,omitempty"` RoID int `structs:"roId,omitempty"` Status int `structs:"status,omitempty"` Registrant int `structs:"registrant,omitempty"` Admin int `structs:"admin,omitempty"` Tech int `structs:"tech,omitempty"` Billing int `structs:"billing,omitempty"` RenewalMode int `structs:"renewalMode,omitempty"` TransferLock int `structs:"transferLock,omitempty"` NoDelegation int `structs:"noDelegation,omitempty"` Tag int `structs:"tag,omitempty"` Order int `structs:"order,omitempty"` Page int `structs:"page,omitempty"` PageLimit int `structs:"pagelimit,omitempty"` } // DomainList API model. type DomainList struct { Count int `mapstructure:"count"` Domains []DomainInfoResponse `mapstructure:"domain"` } // DomainUpdateRequest API model. type DomainUpdateRequest struct { Domain string `structs:"domain"` Nameservers []string `structs:"ns,omitempty"` TransferLock int `structs:"transferLock,omitempty"` RenewalMode string `structs:"renewalMode,omitempty"` TransferMode string `structs:"transferMode,omitempty"` // unsupported fields: // registrant: New owner contact handle id (int, false) // admin: New administrative contact handle id (int, false) // tech: New technical contact handle id (int, false) // billing: New billing contact handle id (int, false) // authCode: Authorization code (if supported) (text64, false) // scDate: Time of scheduled execution (timestamp, false) // whoisProvider: Whois provider (token0255, false) // whoisUrl: Whois url (token0255, false) // extData: Domain extra data (extData, false) // asynchron: Asynchron domain update boolean (false, false) // testing: Execute command in testing mode boolean (false, false) } // DomainUpdateResponse API model. type DomainUpdateResponse struct { Price float32 `mapstructure:"price"` }