Files
at-container-registry/pkg/appview/handlers/common.go
T
Evan JarrettandClaude Opus 5 5112425673 appview: make the footer Bluesky link configurable
The link was hardcoded before fa34da0 too — it pointed at
bsky.app/profile/atcr.io. That commit was right to switch to a DID, since
handles change and a stale handle link breaks silently, but the DID went into
components/footer.html, a shared template. Every self-hoster's footer therefore
links to the project's Bluesky account.

Now ui.bluesky_profile, following source_url in the same footer exactly:
config field with a default, plumbed through UIDependencies and PageData, and
guarded with {{ with }} so an unset value omits the link rather than rendering
something wrong. It takes a handle or a DID; the comment says to prefer a DID
for the reason fa34da0 changed it.

Defaulting to the project account matches source_url's logic — both name the
upstream project rather than the operator — and self-hosters who want their own
or none set one line.

The aria-label switched to $.ClientShortName: `with` rebinds the dot, so the
label would otherwise have silently rendered empty.

Two things worth recording:

  * `{{ with }}` hides the link on an empty value, but you cannot get an empty
    value from the environment. Viper runs with AllowEmptyEnv(false), so an
    empty env var reads as unset and the default wins. Only "" in YAML works.
    That applies to every string field in this config, not just this one, and
    the comment now says so.
  * config-appview.example.yaml must NOT be regenerated with `config init`,
    despite what the checklist in CLAUDE.md says. The file is hand-curated well
    past the defaults, and regenerating replaces real Stripe price IDs with
    price_xxx placeholders and blanks registry_domains, managed_holds, theme
    and the tier names. Added by hand instead.

Verified live both ways: the configured value renders, and `bluesky_profile: ""`
in YAML drops the link while leaving the Source link intact.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SeaUS5AFPX9gqCahoLRMRh
2026-08-25 16:34:26 -05:00

67 lines
2.4 KiB
Go

package handlers
import (
"net/http"
"slices"
"strings"
"atcr.io/pkg/appview/db"
"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)
}
// 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(),
}
}
// 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
}