mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-25 19:54:15 +00:00
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
74 lines
2.7 KiB
Go
74 lines
2.7 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"slices"
|
|
"strings"
|
|
|
|
"atcr.io/pkg/appview/db"
|
|
"atcr.io/pkg/appview/installscript"
|
|
"atcr.io/pkg/appview/middleware"
|
|
)
|
|
|
|
// PageData contains common fields shared across all page templates
|
|
type PageData struct {
|
|
User *db.User // Logged-in user (nil if not logged in)
|
|
Query string // Search query from URL parameter
|
|
RegistryURL string // Docker registry domain (e.g., "buoy.cr")
|
|
SiteURL string // Website domain (e.g., "seamark.dev")
|
|
ClientName string // Brand name for templates (e.g., "AT Container Registry")
|
|
ClientShortName string // Brand name for templates (e.g., "ATCR")
|
|
OciClient string // Preferred OCI client for pull commands (e.g., "docker", "podman")
|
|
AIAdvisorEnabled bool // True when AI Image Advisor is available
|
|
SourceURL string // Source code URL for the footer "Source" link
|
|
BlueskyProfile string // Bluesky handle or DID for the footer link ("" hides it)
|
|
CurrentPath string // Request path (used for OAuth return_to)
|
|
|
|
// CredHelper is the credential helper brand for this deployment. Use
|
|
// .CredHelper.BinaryName / .CredHelper.Name / .CredHelper.DeviceFile in
|
|
// templates instead of hardcoding "docker-credential-atcr".
|
|
CredHelper installscript.Brand
|
|
}
|
|
|
|
// resolveRegistryURL returns the user's preferred registry domain when it is
|
|
// non-empty and still one of the configured domains; otherwise the primary.
|
|
// This keeps a stale or removed preference from breaking the displayed commands.
|
|
func resolveRegistryURL(primary string, domains []string, pref string) string {
|
|
if pref != "" && slices.Contains(domains, pref) {
|
|
return pref
|
|
}
|
|
return primary
|
|
}
|
|
|
|
// NewPageData creates a PageData struct with common fields populated from the request
|
|
func NewPageData(r *http.Request, h *BaseUIHandler) PageData {
|
|
user := middleware.GetUser(r)
|
|
var ociClient, registryPref string
|
|
if user != nil {
|
|
ociClient = user.OciClient
|
|
registryPref = user.RegistryDomain
|
|
}
|
|
return PageData{
|
|
User: user,
|
|
Query: r.URL.Query().Get("q"),
|
|
RegistryURL: resolveRegistryURL(h.RegistryURL, h.RegistryDomains, registryPref),
|
|
SiteURL: h.SiteURL,
|
|
ClientName: h.ClientName,
|
|
ClientShortName: h.ClientShortName,
|
|
OciClient: ociClient,
|
|
AIAdvisorEnabled: h.AIAdvisorEnabled,
|
|
SourceURL: h.SourceURL,
|
|
BlueskyProfile: h.BlueskyProfile,
|
|
CurrentPath: r.URL.RequestURI(),
|
|
CredHelper: h.CredHelper,
|
|
}
|
|
}
|
|
|
|
// TrimRegistryURL removes http:// or https:// prefix from a URL
|
|
// for use in Docker commands where only the host:port is needed
|
|
func TrimRegistryURL(url string) string {
|
|
url = strings.TrimPrefix(url, "https://")
|
|
url = strings.TrimPrefix(url, "http://")
|
|
return url
|
|
}
|