feat: implement account, profile, network, and activity management

This commit is contained in:
2025-06-07 10:40:04 +08:00
commit 2f8cd28ee7
135 changed files with 11665 additions and 0 deletions

35
internal/api/client.go Normal file
View File

@@ -0,0 +1,35 @@
//go:build js && wasm
// +build js,wasm
package api
import (
"context"
"github.com/syumai/workers/cloudflare/fetch"
)
type Response interface {
UnmarshalJSON(data []byte) error
}
type Client interface {
MarketAPI
}
type client struct {
fc *fetch.Client
ctx context.Context
MarketAPI
}
func NewClient(ctx context.Context) *client {
fc := fetch.NewClient()
c := &client{
fc: fc,
ctx: ctx,
}
marketAPI := NewMarketAPI(c, ctx)
c.MarketAPI = marketAPI
return c
}

117
internal/api/market.go Normal file
View File

@@ -0,0 +1,117 @@
//go:build js && wasm
// +build js,wasm
package api
import (
"context"
"encoding/json"
"fmt"
)
const (
kCryptoAPIURL = "https://api.alternative.me"
kCryptoAPIListings = "/v2/listings"
kCryptoAPITickers = "/v2/ticker"
kCryptoAPIGlobal = "/v2/global"
)
type MarketAPI interface {
Listings(symbol string) (*ListingsResponse, error)
Ticker(symbol string) (*TickersResponse, error)
GlobalMarket() (*GlobalMarketResponse, error)
}
type marketAPI struct {
client *client
ctx context.Context
}
func NewMarketAPI(c *client, ctx context.Context) *marketAPI {
return &marketAPI{
client: c,
ctx: ctx,
}
}
func (m *marketAPI) Listings(symbol string) (*ListingsResponse, error) {
r := buildRequest(m.ctx, fmt.Sprintf("%s/%s", kCryptoAPIListings, symbol))
v := &ListingsResponse{}
err := doFetch(m.client.fc, r, v)
if err != nil {
return nil, err
}
return v, nil
}
func (m *marketAPI) Ticker(symbol string) (*TickersResponse, error) {
r := buildRequest(m.ctx, fmt.Sprintf("%s/%s", kCryptoAPITickers, symbol))
v := &TickersResponse{}
err := doFetch(m.client.fc, r, v)
if err != nil {
return nil, err
}
return v, nil
}
func (m *marketAPI) GlobalMarket() (*GlobalMarketResponse, error) {
r := buildRequest(m.ctx, kCryptoAPIGlobal)
v := &GlobalMarketResponse{}
err := doFetch(m.client.fc, r, v)
if err != nil {
return nil, err
}
return v, nil
}
type ListingsResponse struct {
Data []struct {
ID string `json:"id"`
Name string `json:"name"`
Symbol string `json:"symbol"`
WebsiteSlug string `json:"website_slug"`
} `json:"data"`
Metadata struct {
Timestamp int `json:"timestamp"`
NumCryptocurrencies int `json:"num_cryptocurrencies"`
Error any `json:"error"`
} `json:"metadata"`
}
func (r *ListingsResponse) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, r)
}
type TickersResponse struct {
Data []struct {
Symbol string `json:"symbol"`
Price struct {
USD float64 `json:"USD"`
} `json:"price"`
} `json:"data"`
Metadata struct {
Timestamp int `json:"timestamp"`
Error any `json:"error"`
} `json:"metadata"`
}
func (r *TickersResponse) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, r)
}
type GlobalMarketResponse struct {
Data []struct {
Symbol string `json:"symbol"`
Price struct {
USD float64 `json:"USD"`
} `json:"price"`
} `json:"data"`
Metadata struct {
Timestamp int `json:"timestamp"`
Error any `json:"error"`
} `json:"metadata"`
}
func (r *GlobalMarketResponse) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, r)
}

43
internal/api/utils.go Normal file
View File

@@ -0,0 +1,43 @@
//go:build js && wasm
// +build js,wasm
package api
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/syumai/workers/cloudflare/fetch"
)
func buildRequest(c context.Context, url string) *fetch.Request {
r, err := fetch.NewRequest(c, http.MethodGet, url, nil)
if err != nil {
fmt.Println(err)
return nil
}
r.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/111.0")
return r
}
func doFetch(c *fetch.Client, r *fetch.Request, v Response) error {
resp, err := c.Do(r, nil)
if err != nil {
return fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close() // Ensure body is always closed
// Check for non-200 status codes
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
// Directly decode JSON into the response struct
if err := json.NewDecoder(resp.Body).Decode(v); err != nil {
return fmt.Errorf("failed to decode response: %w", err)
}
return nil
}