package api import "time" // Deployment states, as they appear on the wire. const ( StatePending = "pending" StateUploading = "uploading" StateReady = "ready" StateFailed = "failed" StateDeleting = "deleting" ) // Key scopes, as they appear on the wire. const ( ScopeAdmin = "admin" ScopeProject = "project" ) // ---------------------------------------------------------------- projects // Project is the server's view of a project. // // Every mutable setting also appears in ProjectPatch. Adding a field here that // cannot be changed afterwards is a deliberate choice, not an oversight: Name // is immutable because renaming would invalidate every deployed URL and every // webroot symlink pointing at it. type Project struct { Name string `json:"name"` DisplayName string `json:"display_name,omitempty"` IndexFile string `json:"index_file"` NotFoundFile string `json:"not_found_file,omitempty"` SPAFallback bool `json:"spa_fallback"` CacheControl string `json:"cache_control"` RetentionCount int `json:"retention_count"` RetentionGrace int `json:"retention_grace_s"` MaxFiles int `json:"max_files"` MaxFileBytes int64 `json:"max_file_bytes"` MaxTotalBytes int64 `json:"max_total_bytes"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` // ActiveDeployment is nil when the project has never been deployed, which // is why it is a pointer rather than a zero-valued struct. ActiveDeployment *Deployment `json:"active_deployment,omitempty"` // URL is where the active deployment is served, when the server knows its // public base URL. URL string `json:"url,omitempty"` } // CreateProjectRequest creates a project. Everything except Name is optional // and falls back to the server's defaults. type CreateProjectRequest struct { Name string `json:"name"` Patch *ProjectPatch `json:"config,omitempty"` } // ProjectPatch is a partial update. // // Every field is a pointer so the server can tell "leave this alone" from "set // this to the zero value" — without that, PATCH could never clear a custom 404 // document or turn the SPA fallback off. type ProjectPatch struct { DisplayName *string `json:"display_name,omitempty"` IndexFile *string `json:"index_file,omitempty"` NotFoundFile *string `json:"not_found_file,omitempty"` SPAFallback *bool `json:"spa_fallback,omitempty"` CacheControl *string `json:"cache_control,omitempty"` RetentionCount *int `json:"retention_count,omitempty"` RetentionGrace *int `json:"retention_grace_s,omitempty"` MaxFiles *int `json:"max_files,omitempty"` MaxFileBytes *int64 `json:"max_file_bytes,omitempty"` MaxTotalBytes *int64 `json:"max_total_bytes,omitempty"` } // ProjectList is the paged response for GET /api/v1/projects. type ProjectList struct { Projects []Project `json:"projects"` NextCursor string `json:"next_cursor,omitempty"` } // -------------------------------------------------------------------- keys // Key describes an API key. It never carries the secret: the full token exists // on the wire exactly once, in CreateKeyResponse. type Key struct { ID string `json:"id"` Scope string `json:"scope"` Project string `json:"project,omitempty"` Name string `json:"name,omitempty"` CreatedAt time.Time `json:"created_at"` ExpiresAt *time.Time `json:"expires_at,omitempty"` LastUsed *time.Time `json:"last_used_at,omitempty"` RevokedAt *time.Time `json:"revoked_at,omitempty"` } // Revoked reports whether the key has been revoked. func (k Key) Revoked() bool { return k.RevokedAt != nil } // CreateKeyRequest mints a key. Project is set by the URL for the // project-scoped endpoint and must be empty otherwise. type CreateKeyRequest struct { Name string `json:"name,omitempty"` // ExpiresAt is absolute, not a duration: the CLI parses "90d" locally so a // clock skew between client and server cannot silently shift expiry. ExpiresAt *time.Time `json:"expires_at,omitempty"` } // CreateKeyResponse is the only place a full token ever appears. type CreateKeyResponse struct { Key Key `json:"key"` // Token is shown once and never retrievable again. Clients must not log it. Token string `json:"token"` } // KeyList is the response for the key listing endpoints. type KeyList struct { Keys []Key `json:"keys"` } // WhoAmI describes the caller's own credential. type WhoAmI struct { KeyID string `json:"key_id"` Scope string `json:"scope"` Project string `json:"project,omitempty"` Name string `json:"name,omitempty"` ExpiresAt *time.Time `json:"expires_at,omitempty"` } // ------------------------------------------------------------- deployments // Deployment is the server's view of one upload. type Deployment struct { ID string `json:"id"` Project string `json:"project"` State string `json:"state"` Active bool `json:"active"` FileCount int `json:"file_count"` TotalBytes int64 `json:"total_bytes"` Meta map[string]string `json:"meta,omitempty"` Error string `json:"error,omitempty"` CreatedAt time.Time `json:"created_at"` FinalizedAt *time.Time `json:"finalized_at,omitempty"` ActivatedAt *time.Time `json:"activated_at,omitempty"` // URL is where this deployment is served, set on the response to an // activation when the server knows its public base URL. It is the project's // URL: only the active deployment has one, since there are no per-version // preview addresses. URL string `json:"url,omitempty"` // Files is populated only by GET .../deployments/{id}?files=true. Files []FileEntry `json:"files,omitempty"` } // FileEntry is one line of a manifest. Digest is lowercase hex; the server // stores the raw 32 bytes, and hex exists only at this boundary. type FileEntry struct { Path string `json:"path"` Digest string `json:"digest"` Size int64 `json:"size"` } // CreateDeploymentRequest starts a deployment. type CreateDeploymentRequest struct { Meta map[string]string `json:"meta,omitempty"` } // ManifestRequest declares the complete file list of a deployment. type ManifestRequest struct { Files []FileEntry `json:"files"` } // ManifestResponse tells the client which blobs the server does not have yet. // // Missing is the number that makes content-addressed upload worth having, so // the CLI prints it: "142 files, 3.1 MiB; 11 new blobs, 402 KiB to upload". type ManifestResponse struct { Missing []string `json:"missing"` MissingBytes int64 `json:"missing_bytes"` Have int `json:"have"` FileCount int `json:"file_count"` TotalBytes int64 `json:"total_bytes"` } // BlobResponse acknowledges an uploaded blob. type BlobResponse struct { Digest string `json:"digest"` Size int64 `json:"size"` } // DeploymentList is the paged response for the deployment listing endpoint. type DeploymentList struct { Deployments []Deployment `json:"deployments"` NextCursor string `json:"next_cursor,omitempty"` } // ------------------------------------------------------------------ system // SystemInfo is the response for GET /api/v1/system/info. type SystemInfo struct { Version string `json:"version"` UptimeS int64 `json:"uptime_s"` Projects int64 `json:"projects"` Deployments int64 `json:"deployments"` Blobs int64 `json:"blobs"` CASBytes int64 `json:"cas_bytes"` LinkMode string `json:"link_mode"` SchemaVer int `json:"schema_version"` } // GCRequest asks for a garbage collection pass. type GCRequest struct { DryRun bool `json:"dry_run,omitempty"` } // GCStats reports what a collection pass did, or would have done. // // On a dry run the blob numbers count what is collectable right now, not what // deleting the listed deployments would additionally free: nothing was deleted, // so those blobs are still referenced. The figures are a floor. type GCStats struct { DryRun bool `json:"dry_run"` DeploymentsDeleted int `json:"deployments_deleted"` BlobsDeleted int `json:"blobs_deleted"` BytesFreed int64 `json:"bytes_freed"` } // FsckRequest asks for a consistency check, optionally correcting what it // finds. type FsckRequest struct { Repair bool `json:"repair,omitempty"` } // FsckReport is the result of a consistency check. type FsckReport struct { // Blobs is how many were examined, DriftCount how many disagreed with the // manifests that reference them. Drift lists the first hundred of them, // because the list is for a person to read. Blobs int64 `json:"blobs"` DriftCount int `json:"drift_count"` Drift []BlobDrift `json:"drift,omitempty"` Repaired int `json:"repaired"` } // BlobDrift is one blob whose stored reference count is not the number of // manifest entries that name it. type BlobDrift struct { Digest string `json:"digest"` // Stored above Actual only wastes disk. Stored below Actual is the // dangerous direction: the collector may remove content a deployment still // needs. Stored int64 `json:"stored"` Actual int64 `json:"actual"` }