api

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 22, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package api is a typed client for the spoo.me HTTP API.

Index

Constants

View Source
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

View Source
var Version = "dev"

Version is the CLI release, injected by goreleaser via ldflags.

Functions

func ParseExpiry

func ParseExpiry(raw string, now time.Time) (string, error)

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

func (*APIError) Error

func (e *APIError) Error() string

type APIKey

type APIKey struct {
	ID          string   `json:"id"`
	Name        string   `json:"name"`
	Description string   `json:"description"`
	Scopes      []string `json:"scopes"`
	CreatedAt   int64    `json:"created_at"`
	ExpiresAt   int64    `json:"expires_at"`
	Revoked     bool     `json:"revoked"`
	TokenPrefix string   `json:"token_prefix"`
}

type AliasCheck

type AliasCheck struct {
	Available bool   `json:"available"`
	Reason    string `json:"reason"`
}

type Client

type Client struct {
	// contains filtered or unexported fields
}

func New

func New(base string, store *auth.Store) *Client

func (*Client) CheckAlias

func (c *Client) CheckAlias(ctx context.Context, alias, domain string) (*AliasCheck, error)

func (*Client) DeleteKey

func (c *Client) DeleteKey(ctx context.Context, id string, revoke bool) error

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) DeleteURL

func (c *Client) DeleteURL(ctx context.Context, id string) error

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

func (c *Client) Export(ctx context.Context, q StatsQuery, format string) (string, []byte, error)

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

func (c *Client) Inspect(ctx context.Context, shortCode string) (*InspectResult, error)

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) ListKeys

func (c *Client) ListKeys(ctx context.Context) ([]APIKey, error)

func (*Client) ListURLs

func (c *Client) ListURLs(ctx context.Context, opts ListURLsOptions) (*URLPage, error)

func (*Client) Me

func (c *Client) Me(ctx context.Context) (*User, error)

func (*Client) Shorten

func (c *Client) Shorten(ctx context.Context, req ShortenRequest) (*ShortURL, error)

func (*Client) Stats

func (c *Client) Stats(ctx context.Context, q StatsQuery) (*StatsResponse, error)

func (*Client) UpdateURL

func (c *Client) UpdateURL(ctx context.Context, id string, fields map[string]any) (*UpdatedURL, error)

UpdateURL patches the given fields (snake_case keys per the API: long_url, alias, password, max_clicks, expire_after, status, ...).

type DeviceTokens

type DeviceTokens struct {
	AccessToken  string `json:"access_token"`
	RefreshToken string `json:"refresh_token"`
	User         User   `json:"user"`
}

type InspectResult

type InspectResult struct {
	ShortURL    string `json:"short_url"`
	Status      int    `json:"status"`
	Destination string `json:"destination,omitempty"`
}

type ListURLsOptions

type ListURLsOptions struct {
	Page      int
	PageSize  int
	SortBy    string // created_at | last_click | total_clicks
	SortOrder string // ascending | descending
	Search    string
	Status    string // ACTIVE | INACTIVE | BLOCKED | EXPIRED
	Domain    string
}

type MetricPoint

type MetricPoint struct {
	Label string
	Value float64
}

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 StatsSummary struct {
	TotalClicks        int     `json:"total_clicks"`
	UniqueClicks       int     `json:"unique_clicks"`
	FirstClick         string  `json:"first_click"`
	LastClick          string  `json:"last_click"`
	AvgRedirectionTime float64 `json:"avg_redirection_time"`
}

type StatsTimeRange

type StatsTimeRange struct {
	StartDate string `json:"start_date"`
	EndDate   string `json:"end_date"`
}

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 URLPage

type URLPage struct {
	Items    []URLItem `json:"items"`
	Page     int       `json:"page"`
	PageSize int       `json:"pageSize"`
	Total    int       `json:"total"`
	HasNext  bool      `json:"hasNext"`
}

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.

type User

type User struct {
	ID            string `json:"id"`
	Email         string `json:"email"`
	EmailVerified bool   `json:"email_verified"`
	Name          string `json:"name"`
	Plan          string `json:"plan"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL