package main
import (
"bytes"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"sort"
"strconv"
"strings"
"time"
)
var (
PublicKey = getEnv("DISPERSED_PUBLIC_KEY", "pk_your_public_key")
SecretKey = getEnv("DISPERSED_SECRET_KEY", "sk_your_secret_key")
BaseURL = getEnv("DISPERSED_API_BASE_URL", "https://api.compute.x.io")
)
func getEnv(key, fallback string) string {
if value := os.Getenv(key); value != "" {
return value
}
return fallback
}
func canonicalizeQueryString(query url.Values) string {
if len(query) == 0 {
return ""
}
keys := make([]string, 0, len(query))
for k := range query {
keys = append(keys, k)
}
sort.Strings(keys)
var parts []string
for _, k := range keys {
values := query[k]
sortedValues := make([]string, len(values))
copy(sortedValues, values)
sort.Strings(sortedValues)
for _, v := range sortedValues {
parts = append(parts, url.QueryEscape(k)+"="+url.QueryEscape(v))
}
}
return strings.Join(parts, "&")
}
func generateAuthHeaders(method, pathname string, query url.Values, body interface{}) (http.Header, error) {
timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10)
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
return nil, err
}
nonce := hex.EncodeToString(b)
queryString := canonicalizeQueryString(query)
var bodySha256 string
if body != nil {
jsonBytes, err := json.Marshal(body)
if err != nil {
return nil, err
}
hash := sha256.Sum256(jsonBytes)
bodySha256 = hex.EncodeToString(hash[:])
} else {
hash := sha256.Sum256([]byte{})
bodySha256 = hex.EncodeToString(hash[:])
}
canonicalString := strings.Join([]string{
PublicKey, timestamp, nonce, strings.ToUpper(method),
pathname, queryString, bodySha256,
}, "|")
mac := hmac.New(sha256.New, []byte(SecretKey))
mac.Write([]byte(canonicalString))
signature := hex.EncodeToString(mac.Sum(nil))
headers := http.Header{}
headers.Set("X-API-Key", PublicKey)
headers.Set("X-Time", timestamp)
headers.Set("X-Nonce", nonce)
headers.Set("X-Signature", signature)
headers.Set("Content-Type", "application/json")
return headers, nil
}
// Example: List jobs
func ListJobs(page, limit int) ([]byte, error) {
pathname := "/v1/jobs"
query := url.Values{
"page": {strconv.Itoa(page)},
"limit": {strconv.Itoa(limit)},
}
headers, err := generateAuthHeaders("GET", pathname, query, nil)
if err != nil {
return nil, err
}
req, _ := http.NewRequest("GET", BaseURL+pathname+"?"+query.Encode(), nil)
req.Header = headers
resp, err := (&http.Client{Timeout: 30 * time.Second}).Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}