feat: add Go SDK package (REST client + VL1 agent + TAP device)
This commit is contained in:
214
sdk/sdk.go
Normal file
214
sdk/sdk.go
Normal file
@@ -0,0 +1,214 @@
|
||||
package sdk
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ControllerURL string
|
||||
Token string
|
||||
PSK string
|
||||
NetworkID uint32
|
||||
ListenPort int
|
||||
TapName string
|
||||
TapMTU int
|
||||
IdentityPath string
|
||||
HTTPTimeout time.Duration
|
||||
}
|
||||
|
||||
func DefaultConfig() Config {
|
||||
return Config{
|
||||
ControllerURL: "http://127.0.0.1:10001",
|
||||
ListenPort: 0,
|
||||
TapName: "zeromesh0",
|
||||
TapMTU: 2800,
|
||||
IdentityPath: "./data/agent.identity",
|
||||
HTTPTimeout: 30 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
cfg Config
|
||||
httpc *http.Client
|
||||
identity *Identity
|
||||
}
|
||||
|
||||
func New(cfg Config) *Client {
|
||||
if cfg.HTTPTimeout == 0 {
|
||||
cfg.HTTPTimeout = 30 * time.Second
|
||||
}
|
||||
if cfg.TapName == "" {
|
||||
cfg.TapName = "zeromesh0"
|
||||
}
|
||||
if cfg.TapMTU == 0 {
|
||||
cfg.TapMTU = 2800
|
||||
}
|
||||
if cfg.IdentityPath == "" {
|
||||
cfg.IdentityPath = "./data/agent.identity"
|
||||
}
|
||||
return &Client{
|
||||
cfg: cfg,
|
||||
httpc: &http.Client{
|
||||
Timeout: cfg.HTTPTimeout,
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: 10,
|
||||
IdleConnTimeout: 60 * time.Second,
|
||||
DisableCompression: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Config() Config { return c.cfg }
|
||||
func (c *Client) HTTPClient() *http.Client { return c.httpc }
|
||||
func (c *Client) Identity() *Identity { return c.identity }
|
||||
func (c *Client) SetToken(tok string) { c.cfg.Token = tok }
|
||||
func (c *Client) Token() string { return c.cfg.Token }
|
||||
|
||||
type Address [5]byte
|
||||
|
||||
func (a Address) String() string { return hex.EncodeToString(a[:]) }
|
||||
|
||||
func AddressFromPublicKey(pub []byte) Address {
|
||||
var a Address
|
||||
h := hashBytes(pub)
|
||||
copy(a[:], h[:5])
|
||||
return a
|
||||
}
|
||||
|
||||
type Identity struct {
|
||||
PublicKey ed25519.PublicKey
|
||||
PrivateKey ed25519.PrivateKey
|
||||
Address Address
|
||||
}
|
||||
|
||||
func GenerateIdentity() *Identity {
|
||||
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &Identity{
|
||||
PublicKey: pub,
|
||||
PrivateKey: priv,
|
||||
Address: AddressFromPublicKey(pub),
|
||||
}
|
||||
}
|
||||
|
||||
func (id *Identity) PublicKeyHex() string { return hex.EncodeToString(id.PublicKey) }
|
||||
func (id *Identity) PrivateKeyHex() string { return hex.EncodeToString(id.PrivateKey) }
|
||||
|
||||
func hashBytes(data []byte) []byte {
|
||||
h := make([]byte, 32)
|
||||
for i, b := range data {
|
||||
h[i%32] ^= b
|
||||
}
|
||||
for round := 0; round < 3; round++ {
|
||||
for i := 0; i < 32; i++ {
|
||||
h[i] = h[i] ^ h[(i+1)%32] ^ h[(i+7)%32]
|
||||
h[i] = (h[i] << 3) | (h[i] >> 5)
|
||||
}
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
type AuthResponse struct {
|
||||
Token string `json:"token"`
|
||||
User *User `json:"user"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID uint `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Role string `json:"role"`
|
||||
QuotaNetworks int `json:"quota_networks"`
|
||||
QuotaNodes int `json:"quota_nodes"`
|
||||
}
|
||||
|
||||
type Network struct {
|
||||
ID uint `json:"id"`
|
||||
NetworkID uint32 `json:"network_id"`
|
||||
UserID uint `json:"user_id"`
|
||||
Name string `json:"name"`
|
||||
IPRange string `json:"ip_range"`
|
||||
MTU int `json:"mtu"`
|
||||
Private bool `json:"private"`
|
||||
Members []NetworkMember `json:"members,omitempty"`
|
||||
}
|
||||
|
||||
type NetworkMember struct {
|
||||
ID uint `json:"id"`
|
||||
NetworkID uint32 `json:"network_id"`
|
||||
NodeID string `json:"node_id"`
|
||||
IPAddress string `json:"ip_address"`
|
||||
Authorized bool `json:"authorized"`
|
||||
Label string `json:"label"`
|
||||
}
|
||||
|
||||
type Node struct {
|
||||
ID uint `json:"id"`
|
||||
UserID uint `json:"user_id"`
|
||||
NodeID string `json:"node_id"`
|
||||
PublicKey string `json:"public_key"`
|
||||
Name string `json:"name"`
|
||||
IPAddress string `json:"ip_address"`
|
||||
Port int `json:"port"`
|
||||
Online bool `json:"online"`
|
||||
LastSeen *string `json:"last_seen"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type DashboardStats struct {
|
||||
AuthorizedMembers int `json:"authorized_members"`
|
||||
NodesTotal int `json:"nodes_total"`
|
||||
NodesOnline int `json:"nodes_online"`
|
||||
NetworksCount int `json:"networks_count"`
|
||||
}
|
||||
|
||||
type NetworkListResponse struct {
|
||||
Networks []Network `json:"networks"`
|
||||
}
|
||||
|
||||
type NodeListResponse struct {
|
||||
Nodes []Node `json:"nodes"`
|
||||
}
|
||||
|
||||
type MembersResponse struct {
|
||||
Members []NetworkMember `json:"members"`
|
||||
}
|
||||
|
||||
type ProfileResponse struct {
|
||||
User User `json:"user"`
|
||||
UsedNetworks int `json:"used_networks"`
|
||||
UsedNodes int `json:"used_nodes"`
|
||||
}
|
||||
|
||||
func (a Address) MarshalText() ([]byte, error) {
|
||||
return []byte(a.String()), nil
|
||||
}
|
||||
|
||||
func (a *Address) UnmarshalText(text []byte) error {
|
||||
decoded, err := hex.DecodeString(string(text))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(decoded) != 5 {
|
||||
return err
|
||||
}
|
||||
copy(a[:], decoded)
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetPreferredIP() string {
|
||||
conn, err := net.Dial("udp", "8.8.8.8:80")
|
||||
if err != nil {
|
||||
return "0.0.0.0"
|
||||
}
|
||||
defer conn.Close()
|
||||
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
||||
return localAddr.IP.String()
|
||||
}
|
||||
Reference in New Issue
Block a user