Files
at-container-registry/pkg/appview/handlers/base.go
T
Evan JarrettandClaude Opus 5 9d8bd513da appview: render the install scripts from config instead of shipping ATCR's
seamark.dev's /install and /settings/devices told users to pipe
seamark.dev/static/install.sh into bash. That file was the unmodified ATCR
script: it announced itself as the "ATCR Credential Helper Installer",
installed docker-credential-atcr, and finished by telling the user to configure
credHelpers for atcr.io, the wrong registry for that deployment. Anyone
following the documented setup ended up pointed at another service. The
templates hardcoded docker-credential-atcr, "atcr" and ~/.atcr/device.json
alongside a correctly themed {{ .RegistryURL }}.

The scripts are now rendered from config by a handler, rather than forked per
brand. A theme overlay was the alternative and was worse: it needed a full copy
of both install.sh and install.ps1 per brand, four scripts to keep in sync, and
the operator asked for these values to come from config.

credential_helper.name is the single knob. Docker resolves a credHelpers value
x by exec'ing docker-credential-x, so the credHelpers value, the binary suffix
and the config directory are genuinely one word, not three that can drift. It
is validated against a strict pattern because it is interpolated into a shell
script.

install.sh renders byte-identical to the deleted static file under the atcr
default, so existing installs are unaffected. install.ps1 differs by one line,
where a stale usage comment named a path the script is not served at.

Two behaviour changes worth noting: these two URLs drop from a one-year
Cache-Control to five minutes, since the body now depends on deployment config;
and credential_helper.tangled_repo becomes a real overridable default. It was
previously assigned over unconditionally and read by nothing, while the shipped
script used a different URL form.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PDqoCE1j3njokkZ9b1C5n9
2026-09-02 21:38:10 -05:00

75 lines
2.7 KiB
Go

package handlers
import (
"database/sql"
"html/template"
"slices"
"atcr.io/pkg/appview/db"
"atcr.io/pkg/appview/holdhealth"
"atcr.io/pkg/appview/installscript"
"atcr.io/pkg/appview/readme"
"atcr.io/pkg/appview/webhooks"
"atcr.io/pkg/auth/oauth"
"atcr.io/pkg/billing"
indigooauth "github.com/bluesky-social/indigo/atproto/auth/oauth"
"github.com/bluesky-social/indigo/atproto/identity"
)
// BaseUIHandler contains all dependencies for UI handlers.
// Handlers embed this and use whatever fields they need.
// Route registration becomes simply &Handler{base} for everything.
type BaseUIHandler struct {
// Display
Templates *template.Template
RegistryURL string // Primary Docker registry domain (e.g., "buoy.cr" or "atcr.io")
RegistryDomains []string // All configured registry domains; users may pick one as their default
SiteURL string // Website domain (e.g., "seamark.dev" or "atcr.io")
// Database (handlers choose which to use)
DB *sql.DB // Write access
ReadOnlyDB *sql.DB // Read-only access
// Services
Refresher *oauth.Refresher
HealthChecker *holdhealth.Checker
ReadmeFetcher *readme.Fetcher
Directory identity.Directory
BillingManager *billing.Manager
WebhookDispatcher *webhooks.Dispatcher
OAuthClientApp *indigooauth.ClientApp
// Stores
SessionStore *db.SessionStore
DeviceStore *db.DeviceStore
OAuthStore *db.OAuthStore
// Config
DefaultHoldDID string
ManagedHolds []string // DIDs of holds this appview operates (server.managed_holds)
CompanyName string
Jurisdiction string
ClientName string // Full name: "AT Container Registry"
ClientShortName string // Short name: "ATCR"
AIAdvisorEnabled bool // True when billing is fully configured AND Claude API key is set
BillingEnabled bool // True when the billing build is compiled in and Stripe is configured
SourceURL string // Source code URL for the footer "Source" link
BlueskyProfile string // Bluesky handle or DID for the footer link ("" hides it)
// CredHelper is this deployment's credential helper identity: the Docker
// credHelpers value, the docker-credential-<name> binary and the ~/.<name>
// config directory. Templates and the rendered install scripts read it
// from here so a rebranded deployment never names atcr's helper.
CredHelper installscript.Brand
}
// IsManagedHold reports whether a hold DID is one of the appview's managed
// holds. An empty holdDID counts as managed: the user has no explicit default
// hold and falls back to the operator's primary managed hold.
func (h *BaseUIHandler) IsManagedHold(holdDID string) bool {
if holdDID == "" {
return true
}
return slices.Contains(h.ManagedHolds, holdDID)
}