259 lines
9.0 KiB
Go
259 lines
9.0 KiB
Go
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"`
|
|
}
|