bindist

package module
v0.0.0-...-7aa162d Latest Latest
Warning

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

Go to latest
Published: Apr 7, 2026 License: MIT Imports: 13 Imported by: 0

README

BinDist Go API Client

Go client library for the BinDist API.

Requirements

  • Go 1.21+

Installation

go get github.com/BinDist/bindist-api-go

Usage

Customer Client

Use Client for end-user operations like listing applications and downloading files.

package main

import (
    "context"
    "fmt"
    "os"

    bindist "github.com/BinDist/bindist-api-go"
)

func main() {
    ctx := context.Background()
    client := bindist.NewClient("https://api.bindist.com", "your-api-key")

    // List applications
    apps, err := client.ListApplications(ctx, nil)
    if err != nil {
        panic(err)
    }
    if apps.Success {
        for _, app := range apps.Data {
            fmt.Printf("%s (%s)\n", app.Name, app.ApplicationID)
        }
    }

    // List applications with filters
    apps, err = client.ListApplications(ctx, &bindist.ListApplicationsOptions{
        Search:   "myapp",
        Tags:     []string{"windows", "desktop"},
        Page:     1,
        PageSize: 10,
    })

    // Get application details
    app, err := client.GetApplication(ctx, "myapp")
    if err != nil {
        panic(err)
    }
    if app.Success {
        fmt.Printf("Name: %s\n", app.Data.Name)
        fmt.Printf("Description: %s\n", app.Data.Description)
    }

    // List versions
    versions, err := client.ListVersions(ctx, "myapp")
    if err != nil {
        panic(err)
    }
    if versions.Success {
        for _, ver := range versions.Data {
            fmt.Printf("%s - %d bytes\n", ver.Version, ver.FileSize)
        }
    }

    // List files in a version
    files, err := client.ListVersionFiles(ctx, "myapp", "1.0.0")
    if err != nil {
        panic(err)
    }
    if files.Success {
        for _, f := range files.Data {
            fmt.Printf("%s (%s) - %d bytes\n", f.FileName, f.FileType, f.FileSize)
        }
    }

    // Get download URL
    download, err := client.GetDownloadInfo(ctx, "myapp", "1.0.0", "")
    if err != nil {
        panic(err)
    }
    if download.Success {
        fmt.Printf("URL: %s\n", download.Data.URL)
        fmt.Printf("Expires: %s\n", download.Data.ExpiresAt)
    }

    // Download file with checksum verification
    content, metadata, err := client.DownloadFile(ctx, "myapp", "1.0.0", "", true)
    if err != nil {
        panic(err)
    }
    err = os.WriteFile(metadata.FileName, content, 0644)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Downloaded %s (%d bytes)\n", metadata.FileName, metadata.FileSize)

    // Download to file directly
    file, err := os.Create("output.exe")
    if err != nil {
        panic(err)
    }
    defer file.Close()
    metadata, err = client.DownloadFileToWriter(ctx, "myapp", "1.0.0", "", file)
    if err != nil {
        panic(err)
    }

    // Create a share link
    share, err := client.CreateShareLink(ctx, "myapp", "1.0.0", "", 60)
    if err != nil {
        panic(err)
    }
    if share.Success {
        fmt.Printf("Share URL: %s\n", share.Data.ShareURL)
    }

    // Get download statistics
    stats, err := client.GetStats(ctx, "myapp")
    if err != nil {
        panic(err)
    }
    if stats.Success {
        fmt.Printf("Total downloads: %d\n", stats.Data.TotalDownloads)
    }
}
Admin Client

Use AdminClient for administrative operations like creating applications and uploading files.

package main

import (
    "context"
    "fmt"
    "os"

    bindist "github.com/BinDist/bindist-api-go"
)

func main() {
    ctx := context.Background()
    admin := bindist.NewAdminClient("https://api.bindist.com", "admin-api-key")

    // Create a customer
    customer, err := admin.CreateCustomer(ctx, "Acme Corp", "", "Enterprise customer")
    if err != nil {
        panic(err)
    }
    if customer.Success {
        fmt.Printf("Customer ID: %s\n", customer.Data.CustomerID)
        fmt.Printf("API Key: %s\n", customer.Data.APIKey)
    }

    // Create an application
    app, err := admin.CreateApplication(ctx, bindist.CreateApplicationOptions{
        ApplicationID: "myapp",
        Name:          "My Application",
        CustomerIDs:   []string{"customer-1", "customer-2"},
        Description:   "A great application",
        Tags:          []string{"windows", "desktop"},
    })
    if err != nil {
        panic(err)
    }

    // Upload a small file (< 10MB)
    content, err := os.ReadFile("app.exe")
    if err != nil {
        panic(err)
    }

    result, err := admin.UploadSmallFile(ctx, "myapp", "1.0.0", "app.exe", content, "Initial release")
    if err != nil {
        panic(err)
    }

    // Upload a large file (>= 10MB)
    largeContent, err := os.ReadFile("large-app.exe")
    if err != nil {
        panic(err)
    }

    result, err = admin.UploadLargeFile(ctx, "myapp", "2.0.0", "large-app.exe", largeContent, "Major update")
    if err != nil {
        panic(err)
    }

    // Update version metadata
    isEnabled := true
    releaseNotes := "Updated release notes"
    _, err = admin.UpdateVersion(ctx, "myapp", "1.0.0", bindist.UpdateVersionOptions{
        IsEnabled:    &isEnabled,
        ReleaseNotes: &releaseNotes,
    })
    if err != nil {
        panic(err)
    }

    // Update customer
    newName := "New Name"
    isActive := true
    _, err = admin.UpdateCustomer(ctx, "customer-1", &newName, &isActive, nil)
    if err != nil {
        panic(err)
    }

    // Delete an application (soft delete)
    _, err = admin.DeleteApplication(ctx, "myapp")
    if err != nil {
        panic(err)
    }

    // List activity (uploads and downloads)
    activity, err := admin.ListActivity(ctx, "download", "", 1, 20)
    if err != nil {
        panic(err)
    }
    if activity.Success {
        for _, item := range activity.Data {
            fmt.Printf("%s: %s v%s\n", item.Type, item.ApplicationID, item.Version)
        }
    }

    // List customers
    customers, err := admin.ListCustomers(ctx, 1, 20)
    if err != nil {
        panic(err)
    }
    if customers.Success {
        for _, c := range customers.Data {
            fmt.Printf("%s (%s)\n", c.Name, c.CustomerID)
        }
    }
}
Context Support

All API methods accept a context.Context as the first parameter, enabling:

  • Request cancellation
  • Timeouts
  • Deadlines
// With timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

apps, err := client.ListApplications(ctx, nil)
if err != nil {
    if ctx.Err() == context.DeadlineExceeded {
        log.Println("Request timed out")
    }
}

// With cancellation
ctx, cancel := context.WithCancel(context.Background())
go func() {
    // Cancel after some condition
    cancel()
}()

API Response

All API methods return a Response[T] struct:

type Response[T any] struct {
    Success    bool
    Data       T
    Error      *ApiError
    Meta       *Meta
    HTTPStatus int
}

type ApiError struct {
    Code       string
    Message    string
    HTTPStatus int // set for errors synthesized from non-2xx responses
}

type Meta struct {
    RequestID  string
    Pagination *Pagination
}
Error Handling
response, err := client.ListApplications(ctx, nil)
if err != nil {
    // Network or parsing error
    log.Fatalf("Request failed: %v", err)
}

if response.Success {
    // Process response.Data
    apps := response.Data
} else {
    // API error
    fmt.Printf("Error: %s\n", response.Error.Message)
    fmt.Printf("Code: %s\n", response.Error.Code)
}
Errors from outside the API envelope

Not every error reaches the API's structured error renderer. Responses from auth middleware, reverse proxies, load balancers, rate limiters, and gateway timeouts arrive with a plain body (or no body at all) and cannot be wrapped in the standard {"success":false,"error":{...}} envelope.

When the client sees a non-2xx response, it normalizes it into the usual Response[T] shape so consumers only need one code path:

  • response.Success is forced to false.
  • response.HTTPStatus is set to the underlying HTTP status code.
  • response.Error is either the server-provided error (with HTTPStatus filled in) or a synthesized ApiError whose Code is derived from the HTTP status (unauthorized, forbidden, not_found, rate_limited, server_error, http_error, ...) and whose Message is extracted from a bare {"message":...}/{"error":...} body or falls back to http.StatusText.

A synthesized error means the failure originated outside the API application itself, so its Code is coarser than a code returned by the server's own error renderer. Switch on HTTPStatus if you need to react to the transport-level status, and on Error.Code if you need to react to specific semantic categories:

response, err := client.ListApplications(ctx, nil)
if err != nil {
    log.Fatalf("Request failed: %v", err)
}
if !response.Success {
    switch response.Error.Code {
    case "unauthorized":
        // Bad/missing API key — often from auth middleware, not the app.
    case "rate_limited":
        // Back off and retry.
    default:
        log.Printf("API error %d: %s", response.HTTPStatus, response.Error.Message)
    }
}
Release Channels

Versions can be published on different release channels. By default, requests return only versions on the production channel. Pass WithChannel to ListVersions, GetDownloadInfo, DownloadFile, or DownloadFileToWriter to access versions on a non-default channel — for example, the built-in ChannelTest constant exposes disabled/pre-release versions.

// List versions including disabled ones on the Test channel
versions, err := client.ListVersions(ctx, "myapp", bindist.WithChannel(bindist.ChannelTest))

// Get a download URL for a version that is only available on the Test channel
download, err := client.GetDownloadInfo(ctx, "myapp", "1.2.3-rc1", "",
    bindist.WithChannel(bindist.ChannelTest))

// Download directly from the Test channel
content, metadata, err := client.DownloadFile(ctx, "myapp", "1.2.3-rc1", "", true,
    bindist.WithChannel(bindist.ChannelTest))

Internally, WithChannel sets the X-Channel HTTP header on the request.

Download with Checksum Verification

The DownloadFile method can optionally verify SHA256 checksums:

// With verification (recommended)
content, metadata, err := client.DownloadFile(ctx, "myapp", "1.0.0", "", true)
if err != nil {
    // Could be a checksum mismatch error
    log.Fatalf("Download failed: %v", err)
}

// Without verification
content, metadata, err := client.DownloadFile(ctx, "myapp", "1.0.0", "", false)

License

MIT License - see LICENSE for details.

Documentation

Overview

Package bindist provides a client for the BinDist API.

Index

Constants

View Source
const (
	ChannelTest = "Test"
)

Release channel constants. Channels allow access to versions outside the default (production) channel. For example, the "Test" channel exposes disabled versions for pre-release testing.

Variables

This section is empty.

Functions

This section is empty.

Types

type Activity

type Activity struct {
	Type          string    `json:"type"`
	ApplicationID string    `json:"applicationId"`
	Version       string    `json:"version"`
	CustomerID    string    `json:"customerId"`
	Timestamp     time.Time `json:"timestamp"`
}

Activity represents an upload or download activity.

type AdminClient

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

AdminClient is the BinDist admin API client.

func NewAdminClient

func NewAdminClient(baseURL, apiKey string) *AdminClient

NewAdminClient creates a new BinDist admin API client.

func (*AdminClient) CompleteLargeUpload

func (c *AdminClient) CompleteLargeUpload(ctx context.Context, uploadID, applicationID, version, fileName string, fileSize int64, checksum, releaseNotes string) (*Response[UploadResponse], error)

CompleteLargeUpload completes a large file upload.

func (*AdminClient) CreateApplication

func (c *AdminClient) CreateApplication(ctx context.Context, opts CreateApplicationOptions) (*Response[Application], error)

CreateApplication creates a new application.

func (*AdminClient) CreateCustomer

func (c *AdminClient) CreateCustomer(ctx context.Context, name string, parentCustomerID string, notes string) (*Response[CreateCustomerResponse], error)

CreateCustomer creates a new customer with an API key.

func (*AdminClient) DeleteApplication

func (c *AdminClient) DeleteApplication(ctx context.Context, applicationID string) (*Response[map[string]interface{}], error)

DeleteApplication soft-deletes an application.

func (*AdminClient) GetLargeUploadURL

func (c *AdminClient) GetLargeUploadURL(ctx context.Context, applicationID, version, fileName string, fileSize int64, contentType string) (*Response[LargeUploadURLResponse], error)

GetLargeUploadURL gets a pre-signed URL for uploading a large file.

func (*AdminClient) ListActivity

func (c *AdminClient) ListActivity(ctx context.Context, activityType, applicationID string, page, pageSize int) (*Response[[]Activity], error)

ListActivity lists upload and download activity.

func (*AdminClient) ListCustomers

func (c *AdminClient) ListCustomers(ctx context.Context, page, pageSize int) (*Response[[]Customer], error)

ListCustomers lists all customers.

func (*AdminClient) SetHTTPClient

func (c *AdminClient) SetHTTPClient(client *http.Client)

SetHTTPClient sets a custom HTTP client.

func (*AdminClient) UpdateCustomer

func (c *AdminClient) UpdateCustomer(ctx context.Context, customerID string, name *string, isActive *bool, notes *string) (*Response[Customer], error)

UpdateCustomer updates customer metadata.

func (*AdminClient) UpdateVersion

func (c *AdminClient) UpdateVersion(ctx context.Context, applicationID, version string, opts UpdateVersionOptions) (*Response[Version], error)

UpdateVersion updates version metadata.

func (*AdminClient) UploadLargeFile

func (c *AdminClient) UploadLargeFile(ctx context.Context, applicationID, version, fileName string, content []byte, releaseNotes string) (*Response[UploadResponse], error)

UploadLargeFile uploads a large file using the multi-step process.

func (*AdminClient) UploadSmallFile

func (c *AdminClient) UploadSmallFile(ctx context.Context, applicationID, version, fileName string, content []byte, releaseNotes string) (*Response[UploadResponse], error)

UploadSmallFile uploads a small file (< 10MB) directly.

type ApiError

type ApiError struct {
	Code       string `json:"code"`
	Message    string `json:"message"`
	HTTPStatus int    `json:"-"`
}

ApiError represents an error returned by the API.

HTTPStatus is set by the client to the HTTP status code of the underlying response when the error was synthesized from a non-2xx response that did not conform to the standard error envelope — for example, errors raised by auth middleware, reverse proxies, load balancers, rate limiters, or gateway timeouts, which never reach the API application's own error renderer. In that case Code is derived from the HTTP status (e.g. "unauthorized", "rate_limited") and is coarser than a code returned by the server's structured error payload. HTTPStatus is zero for errors that came from the server envelope.

type Application

type Application struct {
	ApplicationID string    `json:"applicationId"`
	Name          string    `json:"name"`
	Description   string    `json:"description,omitempty"`
	IsActive      bool      `json:"isActive"`
	CreatedAt     time.Time `json:"createdAt"`
	UpdatedAt     time.Time `json:"updatedAt"`
	Tags          []string  `json:"tags,omitempty"`
}

Application represents an application.

type Client

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

Client is the BinDist API client.

func NewClient

func NewClient(baseURL, apiKey string) *Client

NewClient creates a new BinDist API client.

func (c *Client) CreateShareLink(ctx context.Context, applicationID, version, fileID string, expiresMinutes int) (*Response[ShareLink], error)

CreateShareLink creates a shareable download link for a file.

func (*Client) DownloadFile

func (c *Client) DownloadFile(ctx context.Context, applicationID, version, fileID string, verifyChecksum bool, opts ...RequestOption) ([]byte, *DownloadInfo, error)

DownloadFile downloads a file and returns its contents along with metadata. If verifyChecksum is true, the checksum is verified against the expected value.

Pass WithChannel to download from a non-default release channel.

func (*Client) DownloadFileToWriter

func (c *Client) DownloadFileToWriter(ctx context.Context, applicationID, version, fileID string, w io.Writer, opts ...RequestOption) (*DownloadInfo, error)

DownloadFileToWriter downloads a file and writes it to the provided writer. Returns the download metadata.

Pass WithChannel to download from a non-default release channel.

func (*Client) GetApplication

func (c *Client) GetApplication(ctx context.Context, applicationID string) (*Response[Application], error)

GetApplication returns details for a specific application.

func (*Client) GetDownloadInfo

func (c *Client) GetDownloadInfo(ctx context.Context, applicationID, version, fileID string, opts ...RequestOption) (*Response[DownloadInfo], error)

GetDownloadInfo returns download URL and metadata for a file.

Pass WithChannel to obtain a download URL for a version that is only accessible on a non-default channel (e.g. WithChannel(ChannelTest) for disabled versions).

func (*Client) GetStats

func (c *Client) GetStats(ctx context.Context, applicationID string) (*Response[Stats], error)

GetStats returns download statistics for an application.

func (*Client) ListApplications

func (c *Client) ListApplications(ctx context.Context, opts *ListApplicationsOptions) (*Response[[]Application], error)

ListApplications returns a list of available applications.

func (*Client) ListVersionFiles

func (c *Client) ListVersionFiles(ctx context.Context, applicationID, version string) (*Response[[]VersionFile], error)

ListVersionFiles returns all files for a specific version.

func (*Client) ListVersions

func (c *Client) ListVersions(ctx context.Context, applicationID string, opts ...RequestOption) (*Response[[]Version], error)

ListVersions returns all versions for an application.

Pass WithChannel to access versions outside the default production channel (e.g. WithChannel(ChannelTest) to include disabled versions).

func (*Client) SetHTTPClient

func (c *Client) SetHTTPClient(client *http.Client)

SetHTTPClient sets a custom HTTP client.

type CreateApplicationOptions

type CreateApplicationOptions struct {
	ApplicationID string   `json:"applicationId"`
	Name          string   `json:"name"`
	CustomerIDs   []string `json:"customerIds"`
	Description   string   `json:"description,omitempty"`
	Tags          []string `json:"tags,omitempty"`
}

CreateApplicationOptions contains options for creating an application.

type CreateCustomerResponse

type CreateCustomerResponse struct {
	CustomerID string `json:"customerId"`
	APIKey     string `json:"apiKey"`
	Name       string `json:"name"`
	CreatedAt  string `json:"createdAt"`
}

CreateCustomerResponse is the response from creating a customer.

type Customer

type Customer struct {
	CustomerID string    `json:"customerId"`
	Name       string    `json:"name"`
	APIKey     string    `json:"apiKey,omitempty"`
	IsActive   bool      `json:"isActive"`
	Notes      string    `json:"notes,omitempty"`
	CreatedAt  time.Time `json:"createdAt"`
}

Customer represents a customer.

type DownloadInfo

type DownloadInfo struct {
	DownloadID string    `json:"downloadId"`
	URL        string    `json:"url"`
	ExpiresAt  time.Time `json:"expiresAt"`
	FileName   string    `json:"fileName"`
	FileSize   int64     `json:"fileSize"`
	Checksum   string    `json:"checksum"`
}

DownloadInfo contains download URL and file metadata.

type LargeUploadURLResponse

type LargeUploadURLResponse struct {
	UploadID  string `json:"uploadId"`
	UploadURL string `json:"uploadUrl"`
}

LargeUploadURLResponse is the response from getting a large upload URL.

type ListApplicationsOptions

type ListApplicationsOptions struct {
	Page     int
	PageSize int
	Search   string
	Tags     []string
}

ListApplicationsOptions contains options for listing applications.

type Meta

type Meta struct {
	RequestID  string      `json:"requestId"`
	Pagination *Pagination `json:"pagination,omitempty"`
}

Meta contains response metadata.

type Pagination

type Pagination struct {
	Page        int  `json:"page"`
	Limit       int  `json:"limit"`
	Total       int  `json:"total"`
	HasNext     bool `json:"hasNext"`
	HasPrevious bool `json:"hasPrevious"`
}

Pagination contains pagination information.

type RequestOption

type RequestOption func(*requestOptions)

RequestOption configures a single API request.

func WithChannel

func WithChannel(channel string) RequestOption

WithChannel sets the release channel for the request. When set, the X-Channel header is sent and versions from that channel become accessible (e.g. ChannelTest to access disabled/pre-release versions).

type Response

type Response[T any] struct {
	Success    bool
	Data       T
	Error      *ApiError
	Meta       *Meta
	HTTPStatus int
}

Response is a generic response type.

HTTPStatus is the HTTP status code of the underlying response.

type ShareLink struct {
	ShareURL  string    `json:"shareUrl"`
	ExpiresAt time.Time `json:"expiresAt"`
}

ShareLink contains share link information.

type Stats

type Stats struct {
	TotalDownloads int `json:"totalDownloads"`
}

Stats contains application statistics.

type UpdateVersionOptions

type UpdateVersionOptions struct {
	IsEnabled    *bool   `json:"isEnabled,omitempty"`
	IsActive     *bool   `json:"isActive,omitempty"`
	ReleaseNotes *string `json:"releaseNotes,omitempty"`
}

UpdateVersionOptions contains options for updating a version.

type UploadResponse

type UploadResponse struct {
	Message       string `json:"message"`
	VersionID     string `json:"versionId"`
	ApplicationID string `json:"applicationId"`
	Version       string `json:"version"`
	FileSize      int64  `json:"fileSize"`
	Checksum      string `json:"checksum"`
}

UploadResponse is the response from uploading a file.

type Version

type Version struct {
	VersionID     string    `json:"versionId"`
	ApplicationID string    `json:"applicationId"`
	Version       string    `json:"version"`
	ReleaseNotes  string    `json:"releaseNotes,omitempty"`
	IsActive      bool      `json:"isActive"`
	IsEnabled     bool      `json:"isEnabled"`
	CreatedAt     time.Time `json:"createdAt"`
	UpdatedAt     time.Time `json:"updatedAt"`
	FileSize      int64     `json:"fileSize"`
	DownloadCount int       `json:"downloadCount"`
}

Version represents a version of an application.

type VersionFile

type VersionFile struct {
	FileID      string `json:"fileId"`
	FileName    string `json:"fileName"`
	FileType    string `json:"fileType"`
	FileSize    int64  `json:"fileSize"`
	Checksum    string `json:"checksum"`
	Order       int    `json:"order"`
	Description string `json:"description,omitempty"`
}

VersionFile represents a file within a version.

Jump to

Keyboard shortcuts

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