mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-08-29 12:17:00 +00:00
minor ui fixes, update privacy page with manged holds list
This commit is contained in:
@@ -37,3 +37,4 @@ pkg/hold/admin/public/css/style.css
|
||||
Thumbs.db
|
||||
node_modules
|
||||
.impeccable.md
|
||||
.playwright-mcp/
|
||||
|
||||
@@ -44,6 +44,7 @@ type BaseUIHandler struct {
|
||||
|
||||
// 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"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
@@ -12,6 +13,7 @@ type LegalPageData struct {
|
||||
CompanyName string
|
||||
Jurisdiction string
|
||||
LastUpdated string
|
||||
ManagedHolds []string // Resolved display names of the holds this appview operates
|
||||
}
|
||||
|
||||
// legalDefaults applies sensible fallbacks for operators who haven't set
|
||||
@@ -52,6 +54,19 @@ type PrivacyPolicyHandler struct {
|
||||
BaseUIHandler
|
||||
}
|
||||
|
||||
// resolveManagedHoldNames maps the configured managed-hold DIDs to friendly
|
||||
// display names (handle, decoded did:web domain, or truncated did:plc) for
|
||||
// listing on the privacy page.
|
||||
func (h *PrivacyPolicyHandler) resolveManagedHoldNames(ctx context.Context) []string {
|
||||
names := make([]string, 0, len(h.ManagedHolds))
|
||||
for _, did := range h.ManagedHolds {
|
||||
if name := resolveHoldDisplayName(ctx, &h.BaseUIHandler, did); name != "" {
|
||||
names = append(names, name)
|
||||
}
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func (h *PrivacyPolicyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
meta := NewPageMeta(
|
||||
"Privacy Policy - "+h.ClientShortName,
|
||||
@@ -66,6 +81,7 @@ func (h *PrivacyPolicyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
|
||||
CompanyName: company,
|
||||
Jurisdiction: jurisdiction,
|
||||
LastUpdated: formatLegalDate(privacyLastUpdated),
|
||||
ManagedHolds: h.resolveManagedHoldNames(r.Context()),
|
||||
}
|
||||
|
||||
if err := h.Templates.ExecuteTemplate(w, "privacy", data); err != nil {
|
||||
|
||||
@@ -40,6 +40,7 @@ type UIDependencies struct {
|
||||
ReadmeFetcher *readme.Fetcher
|
||||
Templates *template.Template
|
||||
DefaultHoldDID string
|
||||
ManagedHolds []string // DIDs of holds this appview operates (server.managed_holds)
|
||||
LegalConfig LegalConfig
|
||||
ClientName string // Full name: "AT Container Registry"
|
||||
ClientShortName string // Short name: "ATCR"
|
||||
@@ -79,6 +80,7 @@ func RegisterUIRoutes(router chi.Router, deps UIDependencies) {
|
||||
BillingManager: deps.BillingManager,
|
||||
WebhookDispatcher: deps.WebhookDispatcher,
|
||||
DefaultHoldDID: deps.DefaultHoldDID,
|
||||
ManagedHolds: deps.ManagedHolds,
|
||||
CompanyName: deps.LegalConfig.CompanyName,
|
||||
Jurisdiction: deps.LegalConfig.Jurisdiction,
|
||||
ClientName: deps.ClientName,
|
||||
|
||||
@@ -346,6 +346,7 @@ func NewAppViewServer(cfg *Config, branding *BrandingOverrides) (*AppViewServer,
|
||||
ReadmeFetcher: s.ReadmeFetcher,
|
||||
Templates: s.Templates,
|
||||
DefaultHoldDID: defaultHoldDID,
|
||||
ManagedHolds: cfg.Server.ManagedHolds,
|
||||
ClientName: cfg.Server.ClientName,
|
||||
ClientShortName: cfg.Server.ClientShortName,
|
||||
BillingManager: s.BillingManager,
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
<link rel="dns-prefetch" href="https://imgs.blue">
|
||||
|
||||
<!-- Preload critical assets. Font list is discovered from the public FS
|
||||
at startup so renaming a .woff2 doesn't silently 404. -->
|
||||
<link rel="preload" href="/icons.svg" as="image" type="image/svg+xml">
|
||||
at startup so renaming a .woff2 doesn't silently 404.
|
||||
icons.svg is intentionally NOT preloaded: it is consumed via SVG
|
||||
<use href="/icons.svg#id"> references, whose fetch has request
|
||||
destination "empty" and never matches an as="image" (or any) preload,
|
||||
so the hint was always dropped as "preloaded but not used". -->
|
||||
{{ range fontPreloads }}
|
||||
<link rel="preload" href="{{ . }}" as="font" type="font/woff2" crossorigin>
|
||||
{{ end }}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
{{/* Renders the comma-separated list of managed holds we operate, falling back
|
||||
to a generic example if none are configured. Expects LegalPageData as dot. */}}
|
||||
{{ define "managedHoldsList" }}{{ if .ManagedHolds }}{{ range $i, $h := .ManagedHolds }}{{ if $i }}, {{ end }}<code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">{{ $h }}</code>{{ end }}{{ else }}<code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">hold01.{{ .SiteURL }}</code>{{ end }}{{ end }}
|
||||
{{ define "privacy" }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@@ -21,7 +24,7 @@
|
||||
|
||||
<h3 class="text-lg font-medium mt-6">Data Stored on Our Infrastructure</h3>
|
||||
|
||||
<p><strong>Layer Records:</strong> Our hold services (e.g., <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">hold01.{{ .SiteURL }}</code>) maintain records in their embedded PDS that reference container image layers you publish. These records are public and link your AT Protocol identity (DID) to content-addressed SHA identifiers.</p>
|
||||
<p><strong>Layer Records:</strong> Our hold services ({{ template "managedHoldsList" . }}) maintain records in their embedded PDS that reference container image layers you publish. These records are public and link your AT Protocol identity (DID) to content-addressed SHA identifiers.</p>
|
||||
|
||||
<p><strong>OCI Blobs:</strong> Container image layers are stored in our object storage (S3). These blobs are content-addressed and deduplicated—meaning identical layers uploaded by different users are stored only once.</p>
|
||||
|
||||
@@ -58,7 +61,7 @@
|
||||
</ul>
|
||||
|
||||
<h3 class="text-lg font-medium mt-6">{{ .ClientShortName }}-Hosted Hold Services</h3>
|
||||
<p>Storage backends we operate (e.g., <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">hold01.{{ .SiteURL }}</code>). Each hold has an embedded PDS and stores:</p>
|
||||
<p>Storage backends we operate ({{ template "managedHoldsList" . }}). Each hold has an embedded PDS and stores:</p>
|
||||
<ul class="list-disc list-inside space-y-1 ml-4">
|
||||
<li>OCI blobs (container image layers) in object storage</li>
|
||||
<li>Layer records in the hold's embedded PDS linking your DID to blob references</li>
|
||||
@@ -276,7 +279,7 @@
|
||||
<p>{{ .CompanyName }} supports "Bring Your Own Storage" where users can deploy their own hold services to store container image blobs. This section explains how BYOS affects your privacy rights.</p>
|
||||
|
||||
<h3 class="text-lg font-medium mt-4">{{ .CompanyName }}-Hosted Holds</h3>
|
||||
<p>Hold services on <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">*.{{ .SiteURL }}</code> domains (e.g., <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">hold01.{{ .SiteURL }}</code>) are operated by us and fully covered by this privacy policy. We can fulfill all data access, export, and deletion requests for these services.</p>
|
||||
<p>Hold services on <code class="bg-base-300 px-1.5 py-0.5 rounded text-sm font-mono">*.{{ .SiteURL }}</code> domains ({{ template "managedHoldsList" . }}) are operated by us and fully covered by this privacy policy. We can fulfill all data access, export, and deletion requests for these services.</p>
|
||||
|
||||
<h3 class="text-lg font-medium mt-6">User-Deployed Holds</h3>
|
||||
<p>If you use a hold service not operated by us:</p>
|
||||
|
||||
@@ -216,7 +216,7 @@
|
||||
<!-- Manifest Delete Confirmation Modal -->
|
||||
<dialog id="manifest-delete-modal" class="modal" aria-modal="true" aria-labelledby="manifest-delete-title">
|
||||
<div class="modal-box bg-base-200">
|
||||
<h3 id="manifest-delete-title" class="text-lg font-bold">Confirm Deletion</h3>
|
||||
<h2 id="manifest-delete-title" class="text-lg font-bold">Confirm Deletion</h2>
|
||||
<p id="manifest-delete-message" class="py-2">This manifest has associated tags that will also be deleted:</p>
|
||||
<ul id="manifest-delete-tags" class="list-disc list-inside text-sm space-y-1"></ul>
|
||||
<p class="font-bold py-2 text-error">This action cannot be undone.</p>
|
||||
@@ -233,7 +233,7 @@
|
||||
<!-- Untagged Manifests Delete Confirmation Modal -->
|
||||
<dialog id="untagged-delete-modal" class="modal" aria-modal="true" aria-labelledby="untagged-delete-title">
|
||||
<div class="modal-box bg-base-200">
|
||||
<h3 id="untagged-delete-title" class="text-lg font-bold">Delete Untagged Manifests</h3>
|
||||
<h2 id="untagged-delete-title" class="text-lg font-bold">Delete Untagged Manifests</h2>
|
||||
<p class="py-2">This will delete <strong>all</strong> untagged manifests in this repository, including those not currently visible.</p>
|
||||
<p class="font-bold py-2 text-error">This action cannot be undone.</p>
|
||||
<div class="modal-action">
|
||||
@@ -251,7 +251,7 @@
|
||||
<!-- Attestation Details Modal -->
|
||||
<dialog id="attestation-detail-modal" class="modal" aria-modal="true" aria-labelledby="attestation-detail-title">
|
||||
<div class="modal-box bg-base-200 max-w-2xl">
|
||||
<h3 id="attestation-detail-title" class="text-lg font-bold">Attestation Details</h3>
|
||||
<h2 id="attestation-detail-title" class="text-lg font-bold">Attestation Details</h2>
|
||||
<div id="attestation-modal-body" class="py-4">
|
||||
<span class="loading loading-spinner loading-md"></span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user