commit ed1d84593316d3118984d51ee9643266edcdb170 Author: Deerain <1deerain1@gmial.com> Date: Wed Oct 28 22:38:29 2026 +0300 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/app/app.go b/app/app.go new file mode 100644 index 0000000..4879f7a --- /dev/null +++ b/app/app.go @@ -0,0 +1 @@ +package app diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c1550dd --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/deeerain/togo-domain-manager + +go 1.27.2 diff --git a/main.go b/main.go new file mode 100644 index 0000000..438bca2 --- /dev/null +++ b/main.go @@ -0,0 +1,44 @@ +package main + +import ( + "context" + "fmt" + "log/slog" + "os" + + "github.com/deeerain/togo-domain-manager/playtogo" +) + +const BASE_URL string = "https://play2go.cloud/" + +func init() { + slog.SetLogLoggerLevel(slog.LevelDebug) +} + +func main() { + ctx := context.Background() + + token := os.Getenv("ACCESS_TOKEN") + + client := playtogo.New(BASE_URL, token) + + domains, err := client.GetDomains(ctx) + fmt.Printf("%v : %v", domains, err) + + records, err := client.GetRecods(ctx, domains[0].ID) + fmt.Printf("%v : %v", records, err) + + new_rec := playtogo.CreateRecord{ + Name: "tg", + TTL: 300, + Type: playtogo.A.String(), + Records: []playtogo.CreateRecordAddress{ + { + Address: "192.168.1.1", + }, + }, + } + + cr, err := client.AddRecord(ctx, domains[0].ID, new_rec) + fmt.Errorf("%v : %v", cr, err) +} diff --git a/playtogo/client.go b/playtogo/client.go new file mode 100644 index 0000000..213d976 --- /dev/null +++ b/playtogo/client.go @@ -0,0 +1,82 @@ +package playtogo + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "log/slog" + "net/http" + "strings" + "time" +) + +type Client struct { + baseURL string + token string + httpClient *http.Client +} + +func New(baseURL, token string) *Client { + return &Client{ + baseURL: strings.TrimRight(baseURL, "/"), + token: token, + httpClient: &http.Client{ + Timeout: 10 * time.Second, + }, + } +} + +func (c *Client) do(ctx context.Context, method, path string, body, result any) error { + var reader io.Reader + + if body != nil { + data, err := json.Marshal(body) + if err != nil { + return fmt.Errorf("encode request: %s", err) + } + + reader = bytes.NewReader(data) + } + + req, err := http.NewRequestWithContext(ctx, method, c.baseURL+path, reader) + if err != nil { + return fmt.Errorf("create reqest: %s", err) + } + + headers := map[string]string{ + "Accept": "application/json", + "Authorization": "Bearer " + c.token, + } + + for k, v := range headers { + req.Header.Set(k, v) + } + + if body != nil { + req.Header.Set("Content-Type", "application/json") + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return fmt.Errorf("send reqest: %s", err) + } + defer resp.Body.Close() + + slog.Debug("response", "status code", resp.StatusCode) + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return fmt.Errorf("API returned HTTP %d", resp.StatusCode) + } + + if result == nil { + return fmt.Errorf("decode response: %s", err) + } + + if err := json.NewDecoder(resp.Body).Decode(result); err != nil { + return fmt.Errorf("decode response: %w", err) + } + + return nil +} diff --git a/playtogo/domain.go b/playtogo/domain.go new file mode 100644 index 0000000..dd686a8 --- /dev/null +++ b/playtogo/domain.go @@ -0,0 +1,39 @@ +package playtogo + +import ( + "context" + "fmt" + "net/http" +) + +type NameServer struct { + Name string `json:"name"` + IP string `json:"ip"` + IPv6 string `json:"ip6"` + SeqNR int `json:"seq_nr"` +} + +type Domain struct { + ID int `json:"id"` + Status string `json:"status"` + FullName string `json:"full_name"` + DisplayName string `json:"display_name"` + DomainName string `json:"domain_name"` + DomainExtention string `json:"domain_extention"` + DomainServers []NameServer `json:"name_servers"` +} + +func (c *Client) GetDomains(ctx context.Context) ([]Domain, error) { + var response ApiResponse[[]Domain] + path := "/api/domains/my" + + if err := c.do(ctx, http.MethodGet, path, nil, &response); err != nil { + return nil, fmt.Errorf("get subscription: %w", err) + } + + if !response.Success { + return nil, fmt.Errorf("api error: %v", response.Error) + } + + return response.Data, nil +} diff --git a/playtogo/dto.go b/playtogo/dto.go new file mode 100644 index 0000000..da9af04 --- /dev/null +++ b/playtogo/dto.go @@ -0,0 +1,55 @@ +package playtogo + +type RecordType int + +const ( + A RecordType = iota + AAAA + CNAME + MX + TXT + SPF + SRV + CAA + PTR + NS +) + +var recordTypeNames = map[RecordType]string{ + A: "A", + AAAA: "AAAA", + CNAME: "CNAME", + MX: "MX", + TXT: "TXT", + SPF: "SPF", + SRV: "SRV", + CAA: "CAA", + PTR: "PTR", + NS: "NS", +} + +func (t RecordType) String() string { + return recordTypeNames[t] +} + +type ApiResponse[T any] struct { + Success bool `json:"success"` + Data T `json:"data"` + Error any `json:"error"` +} + +type CreateRecordAddress struct { + Address string `json:"address"` +} + +type CreateRecord struct { + Name string `json:"name"` + Records []CreateRecordAddress `json:"records"` + TTL int `json:"ttl"` + Type string `json:"type"` +} + +type CreateRecordResult struct { + DomainID int `json:"domain_id"` + Zone string `json:"zone"` +} diff --git a/playtogo/records.go b/playtogo/records.go new file mode 100644 index 0000000..b913f22 --- /dev/null +++ b/playtogo/records.go @@ -0,0 +1,55 @@ +package playtogo + +import ( + "context" + "fmt" + "net/http" +) + +type Record struct { + ID string `json:"id"` + Name string `json:"name"` + FullName string `json:"full_name"` + Type string `json:"type"` + TTL int `json:"ttl"` + Disabled bool `json:"disabled"` + Content string `json:"content"` + Value string `json:"value"` +} + +type DomainRecord struct { + ID int `json:"domain_id"` + Zone string `json:"zone"` + AllowedRecordTypes []string `json:"allowed_record_types"` + Records []Record `json:"records"` +} + +func (c *Client) GetRecods(ctx context.Context, domain_id int) ([]Record, error) { + var response ApiResponse[DomainRecord] + path := fmt.Sprintf("/api/domains/%v/dns/records", domain_id) + + if err := c.do(ctx, http.MethodGet, path, nil, &response); err != nil { + return nil, fmt.Errorf("get records: %w", err) + } + + if !response.Success { + return nil, fmt.Errorf("api error: %v", response.Error) + } + + return response.Data.Records, nil +} + +func (c *Client) AddRecord(ctx context.Context, domain_id int, record CreateRecord) (*CreateRecordResult, error) { + var response ApiResponse[CreateRecordResult] + path := fmt.Sprintf("/api/domains/%v/dns/records", domain_id) + + if err := c.do(ctx, http.MethodPost, path, record, &response); err != nil { + return nil, fmt.Errorf("add record: %w", err) + } + + if !response.Success { + return nil, fmt.Errorf("api error: %v", response.Error) + } + + return &response.Data, nil +}