Documentation
¶
Overview ¶
Package api is a typed client for the spoo.me HTTP API.
Index ¶
- Constants
- Variables
- func ParseExpiry(raw string, now time.Time) (string, error)
- type APIError
- type APIKey
- type AliasCheck
- type Client
- func (c *Client) CheckAlias(ctx context.Context, alias, domain string) (*AliasCheck, error)
- func (c *Client) DeleteKey(ctx context.Context, id string, revoke bool) error
- func (c *Client) DeleteURL(ctx context.Context, id string) error
- func (c *Client) ExchangeDeviceCode(ctx context.Context, code, verifier string) (*DeviceTokens, error)
- func (c *Client) Export(ctx context.Context, q StatsQuery, format string) (string, []byte, error)
- func (c *Client) Inspect(ctx context.Context, shortCode string) (*InspectResult, error)
- func (c *Client) ListKeys(ctx context.Context) ([]APIKey, error)
- func (c *Client) ListURLs(ctx context.Context, opts ListURLsOptions) (*URLPage, error)
- func (c *Client) Me(ctx context.Context) (*User, error)
- func (c *Client) Shorten(ctx context.Context, req ShortenRequest) (*ShortURL, error)
- func (c *Client) Stats(ctx context.Context, q StatsQuery) (*StatsResponse, error)
- func (c *Client) UpdateURL(ctx context.Context, id string, fields map[string]any) (*UpdatedURL, error)
- type DeviceTokens
- type InspectResult
- type ListURLsOptions
- type MetricPoint
- type ShortURL
- type ShortenRequest
- type StatsQuery
- type StatsResponse
- type StatsSummary
- type StatsTimeRange
- type URLItem
- type URLPage
- type UpdatedURL
- type User
Constants ¶
const MaxRangeDays = 90
MaxRangeDays is the widest window the stats endpoint accepts; without explicit dates it defaults to only the LAST 7 DAYS, so clients that want "all recent activity" should request this window explicitly.
Variables ¶
var Version = "dev"
Version is the CLI release, injected by goreleaser via ldflags.
Functions ¶
func ParseExpiry ¶
ParseExpiry normalizes user expiry input to RFC 3339, which the backend always accepts. Durations ("30m", "72h") are relative to now; bare epoch seconds are converted; anything else passes through as ISO 8601. Empty input yields an empty string (no expiry change).
Types ¶
type APIError ¶
type APIError struct {
Status int `json:"-"`
Code string `json:"code"`
Message string `json:"error"`
Detail string `json:"detail"`
}
APIError mirrors the backend's error envelope {error, code, detail}.
type AliasCheck ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
func (*Client) CheckAlias ¶
func (*Client) DeleteKey ¶
DeleteKey removes a key. With revoke=true it is soft-revoked (kept in the list, unusable); with revoke=false the record is hard-deleted.
func (*Client) ExchangeDeviceCode ¶
func (c *Client) ExchangeDeviceCode(ctx context.Context, code, verifier string) (*DeviceTokens, error)
ExchangeDeviceCode trades a one-time device-auth code for a JWT pair. The code is the credential — no prior auth is required. The verifier is the PKCE code verifier whose S256 challenge was sent on the login URL.
func (*Client) Export ¶
Export downloads stats in the given format (json, csv, xlsx, xml). Returns the server-suggested filename and the file contents. csv arrives as a ZIP archive (one CSV per dimension).
func (*Client) Inspect ¶
Inspect resolves where a short code points without recording a click: the backend skips click tracking on HEAD requests, and redirects are not followed so the destination never gets hit either.
func (*Client) Stats ¶
func (c *Client) Stats(ctx context.Context, q StatsQuery) (*StatsResponse, error)
type DeviceTokens ¶
type InspectResult ¶
type ListURLsOptions ¶
type MetricPoint ¶
type ShortURL ¶
type ShortURL struct {
ShortURL string `json:"short_url"`
Alias string `json:"alias"`
LongURL string `json:"long_url"`
CreatedAt int64 `json:"created_at"`
Status string `json:"status"`
}
ShortURL mirrors UrlResponse (POST /api/v1/shorten); created_at is Unix seconds in this response.
type ShortenRequest ¶
type ShortenRequest struct {
LongURL string `json:"long_url"`
Alias string `json:"alias,omitempty"`
Password string `json:"password,omitempty"`
BlockBots bool `json:"block_bots,omitempty"`
MaxClicks int `json:"max_clicks,omitempty"`
ExpireAfter string `json:"expire_after,omitempty"` // ISO 8601 or epoch seconds
PrivateStats bool `json:"private_stats,omitempty"`
Domain string `json:"domain,omitempty"`
}
type StatsQuery ¶
type StatsQuery struct {
ShortCode string
Scope string // "all" (authed, optional code) or "anon" (code required)
StartDate string
EndDate string
GroupBy []string // time, browser, os, country, city, referrer, short_code
Timezone string // IANA name
// Filters narrows results server-side; keys are the filterable
// dimensions (browser, os, country, city, referrer, short_code).
Filters map[string][]string
}
type StatsResponse ¶
type StatsResponse struct {
Scope string `json:"scope"`
ShortCode string `json:"short_code"`
Summary StatsSummary `json:"summary"`
TimeRange StatsTimeRange `json:"time_range"`
Metrics map[string][]map[string]any `json:"metrics"`
ComputedMetrics map[string]float64 `json:"computed_metrics"`
GeneratedAt string `json:"generated_at"`
}
StatsResponse keeps Metrics loosely typed: keys are dynamic ("clicks_by_browser", "unique_clicks_by_time", ...) and each point carries its dimension label under the dimension's own name.
func (*StatsResponse) Points ¶
func (r *StatsResponse) Points(dimension, metric string) []MetricPoint
Points extracts (label, value) pairs from the loosely typed metrics payload for one dimension/metric pair, e.g. ("browser", "clicks") → the "clicks_by_browser" series with labels from the "browser" key.
type StatsSummary ¶
type StatsTimeRange ¶
type URLItem ¶
type URLItem struct {
ID string `json:"id"`
Alias string `json:"alias"`
LongURL string `json:"long_url"`
CreatedAt string `json:"created_at"`
LastClick string `json:"last_click"`
TotalClicks int `json:"total_clicks"`
Status string `json:"status"`
PasswordSet bool `json:"password_set"`
MaxClicks *int `json:"max_clicks"`
ExpireAfter *int64 `json:"expire_after"` // Unix seconds, null when unset
PrivateStats bool `json:"private_stats"`
BlockBots bool `json:"block_bots"`
Domain string `json:"domain"`
}
URLItem is a row from GET /api/v1/urls. The envelope is camelCase (pageSize, hasNext) but items are snake_case; expire_after is a Unix timestamp — see UrlListItem in the backend's schemas/dto/responses/url.py.
type UpdatedURL ¶
type UpdatedURL struct {
ID string `json:"id"`
Alias string `json:"alias"`
LongURL string `json:"long_url"`
Status string `json:"status"`
PasswordSet bool `json:"password_set"`
MaxClicks *int `json:"max_clicks"`
ExpireAfter *int64 `json:"expire_after"`
BlockBots bool `json:"block_bots"`
PrivateStats bool `json:"private_stats"`
Domain string `json:"domain"`
UpdatedAt int64 `json:"updated_at"`
}
UpdatedURL mirrors UpdateUrlResponse — unlike the shorten response it carries no short_url, and timestamps are Unix seconds.