mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-03 00:36:56 +00:00
appview: make the footer Bluesky link configurable
The link was hardcoded beforefa34da0too — 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 reasonfa34da0changed 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
This commit is contained in:
co-authored by
Claude Opus 5
parent
34b4516aa7
commit
5112425673
@@ -50,6 +50,8 @@ ui:
|
||||
libsql_sync_interval: 1m0s
|
||||
# Source code URL displayed in the footer "Source" link. Defaults to the upstream ATCR project.
|
||||
source_url: https://tangled.org/evan.jarrett.net/at-container-registry
|
||||
# Bluesky handle or DID linked from the footer. Prefer a DID: handles can change. Defaults to the ATCR project account. Set to "" in YAML to hide the link (an empty env var will not do it: Viper treats it as unset).
|
||||
bluesky_profile: did:plc:wfj5kyialpmcv2fzk6uqwsln
|
||||
# Health check and cache settings.
|
||||
health:
|
||||
# How long to cache hold health check results.
|
||||
|
||||
@@ -96,6 +96,10 @@ type UIConfig struct {
|
||||
|
||||
// Source code URL displayed in the footer "Source" link.
|
||||
SourceURL string `yaml:"source_url" comment:"Source code URL displayed in the footer \"Source\" link. Defaults to the upstream ATCR project."`
|
||||
|
||||
// Bluesky profile linked from the footer. Handle or DID; prefer a DID,
|
||||
// since handles change and a stale handle link breaks silently.
|
||||
BlueskyProfile string `yaml:"bluesky_profile" comment:"Bluesky handle or DID linked from the footer. Prefer a DID: handles can change. Defaults to the ATCR project account. Set to \"\" in YAML to hide the link (an empty env var will not do it: Viper treats it as unset)."`
|
||||
}
|
||||
|
||||
// HealthConfig defines health check and cache settings
|
||||
@@ -236,6 +240,7 @@ func setDefaults(v *viper.Viper) {
|
||||
v.SetDefault("ui.libsql_auth_token", "")
|
||||
v.SetDefault("ui.libsql_sync_interval", "60s")
|
||||
v.SetDefault("ui.source_url", "https://tangled.org/evan.jarrett.net/at-container-registry")
|
||||
v.SetDefault("ui.bluesky_profile", "did:plc:wfj5kyialpmcv2fzk6uqwsln")
|
||||
|
||||
// Health defaults
|
||||
v.SetDefault("health.cache_ttl", "15m")
|
||||
|
||||
@@ -53,6 +53,7 @@ type BaseUIHandler struct {
|
||||
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)
|
||||
}
|
||||
|
||||
// IsManagedHold reports whether a hold DID is one of the appview's managed
|
||||
|
||||
@@ -20,6 +20,7 @@ type PageData struct {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -51,6 +52,7 @@ func NewPageData(r *http.Request, h *BaseUIHandler) PageData {
|
||||
OciClient: ociClient,
|
||||
AIAdvisorEnabled: h.AIAdvisorEnabled,
|
||||
SourceURL: h.SourceURL,
|
||||
BlueskyProfile: h.BlueskyProfile,
|
||||
CurrentPath: r.URL.RequestURI(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ type UIDependencies struct {
|
||||
WebhookDispatcher *webhooks.Dispatcher // Webhook dispatcher (nil if not configured)
|
||||
ClaudeAPIKey string // Anthropic API key for AI advisor (empty = disabled)
|
||||
SourceURL string // Source code URL for the footer "Source" link
|
||||
BlueskyProfile string // Bluesky handle or DID for the footer link ("" hides it)
|
||||
}
|
||||
|
||||
// RegisterUIRoutes registers all web UI and API routes on the provided router
|
||||
@@ -88,6 +89,7 @@ func RegisterUIRoutes(router chi.Router, deps UIDependencies) {
|
||||
BillingEnabled: deps.BillingManager != nil && deps.BillingManager.Enabled(),
|
||||
AIAdvisorEnabled: deps.BillingManager != nil && deps.BillingManager.Enabled() && deps.ClaudeAPIKey != "",
|
||||
SourceURL: deps.SourceURL,
|
||||
BlueskyProfile: deps.BlueskyProfile,
|
||||
}
|
||||
|
||||
// OAuth login routes (public)
|
||||
|
||||
@@ -412,6 +412,7 @@ func NewAppViewServer(cfg *Config, branding *BrandingOverrides) (*AppViewServer,
|
||||
WebhookDispatcher: s.WebhookDispatcher,
|
||||
ClaudeAPIKey: cfg.AI.APIKey,
|
||||
SourceURL: cfg.UI.SourceURL,
|
||||
BlueskyProfile: cfg.UI.BlueskyProfile,
|
||||
LegalConfig: routes.LegalConfig{
|
||||
CompanyName: cfg.Legal.CompanyName,
|
||||
Jurisdiction: cfg.Legal.Jurisdiction,
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
<a href="/privacy" class="link link-hover">Privacy</a>
|
||||
<span aria-hidden="true" class="text-base-content/30">·</span>
|
||||
<a href="/terms" class="link link-hover">Terms</a>
|
||||
{{ with .BlueskyProfile }}
|
||||
<span aria-hidden="true" class="text-base-content/30">·</span>
|
||||
<a href="https://bsky.app/profile/did:plc:wfj5kyialpmcv2fzk6uqwsln" target="_blank" rel="noopener" aria-label="{{ .ClientShortName }} on Bluesky" class="link link-hover inline-flex items-center gap-1">
|
||||
<a href="https://bsky.app/profile/{{ . }}" target="_blank" rel="noopener" aria-label="{{ $.ClientShortName }} on Bluesky" class="link link-hover inline-flex items-center gap-1">
|
||||
<svg aria-hidden="true" class="size-3.5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 568 501"><path fill="currentColor" d="M123.121 33.664C188.241 82.553 258.281 181.68 284 234.873c25.719-53.192 95.759-152.32 160.879-201.21C491.866-1.611 568-28.906 568 57.947c0 17.346-9.945 145.713-15.778 166.555-20.275 72.453-94.155 90.933-159.875 79.748C507.222 323.8 536.444 388.56 473.333 453.32c-119.86 122.992-172.272-30.859-185.702-70.281-2.462-7.227-3.614-10.608-3.631-7.733-.017-2.875-1.169.506-3.631 7.733-13.43 39.422-65.842 193.273-185.702 70.281-63.111-64.76-33.89-129.52 80.986-149.071-65.72 11.185-139.6-7.295-159.875-79.748C9.945 203.659 0 75.291 0 57.946 0-28.906 76.135-1.612 123.121 33.664Z"/></svg>
|
||||
Bluesky
|
||||
</a>
|
||||
{{ end }}
|
||||
{{ with .SourceURL }}
|
||||
<span aria-hidden="true" class="text-base-content/30">·</span>
|
||||
<a href="{{ . }}" target="_blank" rel="noopener" class="link link-hover inline-flex items-center gap-1">
|
||||
|
||||
Reference in New Issue
Block a user