Files
pages/internal/client/calls.go
T
2026-08-15 07:13:00 +00:00

211 lines
6.8 KiB
Go

package client
import (
"context"
"net/http"
"net/url"
"strconv"
"github.com/iceBear67/simplepages/api"
)
// ---------------------------------------------------------------- identity
// WhoAmI describes the credential the client is using.
func (c *Client) WhoAmI(ctx context.Context) (api.WhoAmI, error) {
var out api.WhoAmI
err := c.do(ctx, http.MethodGet, api.PathWhoAmI(), nil, &out)
return out, err
}
// SystemInfo returns server-wide counters. Admin scope only.
func (c *Client) SystemInfo(ctx context.Context) (api.SystemInfo, error) {
var out api.SystemInfo
err := c.do(ctx, http.MethodGet, api.PathSystemInfo(), nil, &out)
return out, err
}
// ---------------------------------------------------------------- projects
// CreateProject creates a project. Admin scope only.
func (c *Client) CreateProject(ctx context.Context, req api.CreateProjectRequest) (api.Project, error) {
var out api.Project
err := c.do(ctx, http.MethodPost, api.PathProjects(), req, &out)
return out, err
}
// ListOptions pages a listing endpoint. A zero Limit takes the server default.
type ListOptions struct {
Limit int
Cursor string
}
func (o ListOptions) query() string {
q := url.Values{}
if o.Limit > 0 {
q.Set("limit", strconv.Itoa(o.Limit))
}
if o.Cursor != "" {
q.Set("cursor", o.Cursor)
}
if len(q) == 0 {
return ""
}
return "?" + q.Encode()
}
// ListProjects returns one page of projects. Admin scope only.
func (c *Client) ListProjects(ctx context.Context, opts ListOptions) (api.ProjectList, error) {
var out api.ProjectList
err := c.do(ctx, http.MethodGet, api.PathProjects()+opts.query(), nil, &out)
return out, err
}
// ListAllProjects follows the cursor to the end.
//
// The CLI pages on the user's behalf because "pages project list" that silently
// showed the first hundred of three hundred projects would be a lie; a caller
// that wants one page asks for ListProjects.
func (c *Client) ListAllProjects(ctx context.Context) ([]api.Project, error) {
var all []api.Project
opts := ListOptions{Limit: 500}
for {
page, err := c.ListProjects(ctx, opts)
if err != nil {
return nil, err
}
all = append(all, page.Projects...)
if page.NextCursor == "" || len(page.Projects) == 0 {
return all, nil
}
opts.Cursor = page.NextCursor
}
}
// GetProject reads one project. A project-scoped key may read only its own.
func (c *Client) GetProject(ctx context.Context, name string) (api.Project, error) {
var out api.Project
err := c.do(ctx, http.MethodGet, api.PathProject(name), nil, &out)
return out, err
}
// PatchProject applies a partial update. Admin scope only.
func (c *Client) PatchProject(ctx context.Context, name string, patch api.ProjectPatch) (api.Project, error) {
var out api.Project
err := c.do(ctx, http.MethodPatch, api.PathProject(name), patch, &out)
return out, err
}
// DeleteProject removes a project and everything under it. Admin scope only.
func (c *Client) DeleteProject(ctx context.Context, name string) error {
return c.do(ctx, http.MethodDelete, api.PathProject(name), nil, nil)
}
// -------------------------------------------------------------------- keys
// CreateAdminKey mints an admin key. Admin scope only.
//
// The returned token is the only copy that will ever exist; the caller shows it
// once and must not log it.
func (c *Client) CreateAdminKey(ctx context.Context, req api.CreateKeyRequest) (api.CreateKeyResponse, error) {
var out api.CreateKeyResponse
err := c.do(ctx, http.MethodPost, api.PathKeys(), req, &out)
return out, err
}
// CreateProjectKey mints a key scoped to one project. Admin scope only.
func (c *Client) CreateProjectKey(ctx context.Context, project string, req api.CreateKeyRequest) (api.CreateKeyResponse, error) {
var out api.CreateKeyResponse
err := c.do(ctx, http.MethodPost, api.PathProjectKeys(project), req, &out)
return out, err
}
// ListKeys returns every key on the server. Admin scope only.
func (c *Client) ListKeys(ctx context.Context) (api.KeyList, error) {
var out api.KeyList
err := c.do(ctx, http.MethodGet, api.PathKeys(), nil, &out)
return out, err
}
// ListProjectKeys returns the keys of one project.
func (c *Client) ListProjectKeys(ctx context.Context, project string) (api.KeyList, error) {
var out api.KeyList
err := c.do(ctx, http.MethodGet, api.PathProjectKeys(project), nil, &out)
return out, err
}
// RevokeKey revokes a key by id. It takes effect immediately, and is
// idempotent: revoking an already-revoked key succeeds.
func (c *Client) RevokeKey(ctx context.Context, keyID string) error {
return c.do(ctx, http.MethodDelete, api.PathKey(keyID), nil, nil)
}
// ------------------------------------------------------------- deployments
// DeploymentListOptions selects and pages a project's deployments. A zero State
// lists every state.
type DeploymentListOptions struct {
ListOptions
State string
}
func (o DeploymentListOptions) query() string {
q := url.Values{}
if o.Limit > 0 {
q.Set("limit", strconv.Itoa(o.Limit))
}
if o.Cursor != "" {
q.Set("cursor", o.Cursor)
}
if o.State != "" {
q.Set("state", o.State)
}
if len(q) == 0 {
return ""
}
return "?" + q.Encode()
}
// ListDeployments returns one page of a project's deployments, newest first.
func (c *Client) ListDeployments(ctx context.Context, project string, opts DeploymentListOptions) (api.DeploymentList, error) {
var out api.DeploymentList
err := c.do(ctx, http.MethodGet, api.PathDeployments(project)+opts.query(), nil, &out)
return out, err
}
// GetDeployment reads one deployment. withFiles asks for its manifest too,
// which for a large site is a great deal more response than the rest of it.
func (c *Client) GetDeployment(ctx context.Context, project, id string, withFiles bool) (api.Deployment, error) {
var out api.Deployment
path := api.PathDeployment(project, id)
if withFiles {
path += "?files=true"
}
err := c.do(ctx, http.MethodGet, path, nil, &out)
return out, err
}
// DeleteDeployment removes a deployment and its files. The active one cannot be
// deleted; the server answers deployment_active until something else is
// activated.
func (c *Client) DeleteDeployment(ctx context.Context, project, id string) error {
return c.do(ctx, http.MethodDelete, api.PathDeployment(project, id), nil, nil)
}
// ---------------------------------------------------------------- upkeep
// Collect runs a garbage collection pass. Admin scope only.
func (c *Client) Collect(ctx context.Context, dryRun bool) (api.GCStats, error) {
var out api.GCStats
err := c.do(ctx, http.MethodPost, api.PathGC(), api.GCRequest{DryRun: dryRun}, &out)
return out, err
}
// Fsck checks the blob reference counts against the manifests, optionally
// correcting them. Admin scope only.
func (c *Client) Fsck(ctx context.Context, repair bool) (api.FsckReport, error) {
var out api.FsckReport
err := c.do(ctx, http.MethodPost, api.PathFsck(), api.FsckRequest{Repair: repair}, &out)
return out, err
}