This commit is contained in:
Deerain 2026-10-28 22:38:29 +03:00
commit ed1d845933
9 changed files with 280 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.env

0
README.md Normal file
View file

1
app/app.go Normal file
View file

@ -0,0 +1 @@
package app

3
go.mod Normal file
View file

@ -0,0 +1,3 @@
module github.com/deeerain/togo-domain-manager
go 1.27.2

44
main.go Normal file
View file

@ -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)
}

82
playtogo/client.go Normal file
View file

@ -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
}

39
playtogo/domain.go Normal file
View file

@ -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
}

55
playtogo/dto.go Normal file
View file

@ -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"`
}

55
playtogo/records.go Normal file
View file

@ -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
}