mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-02 00:06:58 +00:00
clean up layouts, add favicons
This commit is contained in:
@@ -525,7 +525,7 @@ This ensures:
|
||||
### Key Design Decisions
|
||||
|
||||
1. **No fork of distribution**: Uses distribution as library, extends via middleware
|
||||
2. **Hybrid storage**: Manifests in ATProto (small, federated), blobs in S3 or BYOS (cheap, scalable)
|
||||
2. **Hybrid storage**: Manifests in ATProto (small), blobs in S3 or BYOS (cheap, scalable)
|
||||
3. **Content addressing**: Manifests stored by digest, blobs deduplicated globally
|
||||
4. **ATProto-native**: Manifests are first-class ATProto records, discoverable via AT Protocol
|
||||
5. **OCI compliant**: Fully compatible with Docker/containerd/podman
|
||||
|
||||
+22
-2
@@ -338,8 +338,28 @@ func serveRegistry(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// Mount static files if UI is enabled
|
||||
if uiSessionStore != nil && uiTemplates != nil {
|
||||
// Mount static files
|
||||
mainRouter.Handle("/static/*", http.StripPrefix("/static/", appview.StaticHandler()))
|
||||
// Register dynamic routes for root-level files (favicons, manifests, etc.)
|
||||
staticHandler := appview.StaticHandler()
|
||||
rootFiles, err := appview.StaticRootFiles()
|
||||
if err != nil {
|
||||
slog.Warn("Failed to scan static root files", "error", err)
|
||||
} else {
|
||||
for _, filename := range rootFiles {
|
||||
// Create a closure to capture the filename
|
||||
file := filename
|
||||
mainRouter.Get("/"+file, func(w http.ResponseWriter, r *http.Request) {
|
||||
// Serve the specific file from static root
|
||||
r.URL.Path = "/" + file
|
||||
staticHandler.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
slog.Info("Registered dynamic root file routes", "count", len(rootFiles), "files", rootFiles)
|
||||
}
|
||||
|
||||
// Mount subdirectory routes with clean paths
|
||||
mainRouter.Handle("/css/*", http.StripPrefix("/css/", appview.StaticSubdir("css")))
|
||||
mainRouter.Handle("/js/*", http.StripPrefix("/js/", appview.StaticSubdir("js")))
|
||||
mainRouter.Handle("/static/*", http.StripPrefix("/static/", appview.StaticSubdir("static")))
|
||||
|
||||
slog.Info("UI enabled", "home", "/", "settings", "/settings")
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
The ATCR AppView UI provides a web interface for discovering, managing, and configuring container images in the ATCR registry. Version 1 focuses on three core pages that leverage existing functionality:
|
||||
|
||||
1. **Front Page** - Federated image discovery via firehose
|
||||
1. **Front Page** - Distributed image discovery via firehose
|
||||
2. **Settings Page** - Profile and hold configuration
|
||||
3. **Personal Page** - Manage your images and tags
|
||||
|
||||
@@ -204,7 +204,7 @@ Jetstream is simpler and filters events server-side.
|
||||
|
||||
## Page Specifications
|
||||
|
||||
### 1. Front Page - Federated Discovery
|
||||
### 1. Front Page - Distributed Discovery
|
||||
|
||||
**URL:** `/ui/` or `/ui/explore`
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ Several ATProto projects face similar challenges with large data storage:
|
||||
| **stream.place** | Video segments | Stream info, chat | Embedded "static PDS" |
|
||||
| **ATCR** | Container blobs | Manifests, comments, builds | Embedded PDS in hold service |
|
||||
|
||||
**Common problem:** Large binary data can't realistically live in user PDSs, but application metadata needs a federated home.
|
||||
**Common problem:** Large binary data can't realistically live in user PDSs, but application metadata needs a distributed home.
|
||||
|
||||
**ATCR's approach:** Each hold service is a full ATProto actor with its own embedded PDS for **shared data** (captain + crew records, not user-specific data). This PDS stores access control and metadata about the hold itself.
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 288 KiB |
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "MyWebSite",
|
||||
"short_name": "MySite",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/web-app-manifest-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/web-app-manifest-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 259 KiB |
@@ -0,0 +1,20 @@
|
||||
{{ define "head" }}
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<!-- Favicons -->
|
||||
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="shortcut icon" href="/favicon.ico" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
|
||||
<!-- Stylesheets -->
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
|
||||
<!-- HTMX -->
|
||||
<script src="https://unpkg.com/htmx.org@2.0.8/dist/htmx.min.js"></script>
|
||||
|
||||
<!-- App Scripts -->
|
||||
<script src="/js/app.js"></script>
|
||||
{{ end }}
|
||||
@@ -1,24 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ block "title" . }}ATCR - Federated Container Registry{{ end }}</title>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||||
{{ block "head" . }}{{ end }}
|
||||
</head>
|
||||
<body>
|
||||
{{ template "nav" . }}
|
||||
|
||||
<main class="container">
|
||||
{{ block "content" . }}{{ end }}
|
||||
</main>
|
||||
|
||||
<!-- Modal container for HTMX -->
|
||||
<div id="modal"></div>
|
||||
|
||||
<script src="/static/js/app.js"></script>
|
||||
{{ block "scripts" . }}{{ end }}
|
||||
</body>
|
||||
</html>
|
||||
@@ -2,11 +2,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ATCR - Federated Container Registry</title>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||||
<title>ATCR - Distributed Container Registry</title>
|
||||
{{ template "head" . }}
|
||||
</head>
|
||||
<body>
|
||||
{{ template "nav" . }}
|
||||
@@ -86,8 +83,6 @@
|
||||
|
||||
<!-- Modal container for HTMX -->
|
||||
<div id="modal"></div>
|
||||
|
||||
<script src="/static/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
|
||||
@@ -2,11 +2,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Install ATCR Credential Helper - ATCR</title>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||||
{{ template "head" . }}
|
||||
</head>
|
||||
<body>
|
||||
{{ template "nav" . }}
|
||||
@@ -139,7 +136,6 @@ export PATH="/usr/local/bin:$PATH"</code></div>
|
||||
</script>
|
||||
|
||||
<div id="modal"></div>
|
||||
<script src="/static/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login - ATCR</title>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
{{ template "head" . }}
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar">
|
||||
|
||||
@@ -2,12 +2,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ if .Repository.Title }}{{ .Repository.Title }}{{ else }}{{ .Owner.Handle }}/{{ .Repository.Name }}{{ end }} - ATCR</title>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||||
<script src="/static/js/app.js"></script>
|
||||
{{ template "head" . }}
|
||||
</head>
|
||||
<body>
|
||||
{{ template "nav" . }}
|
||||
|
||||
@@ -2,11 +2,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Search: {{ .SearchQuery }} - ATCR</title>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||||
<title>Search{{ if .SearchQuery }}: {{ .SearchQuery }}{{ end }} - ATCR</title>
|
||||
{{ template "head" . }}
|
||||
</head>
|
||||
<body>
|
||||
{{ template "nav" . }}
|
||||
@@ -31,8 +28,6 @@
|
||||
|
||||
<!-- Modal container for HTMX -->
|
||||
<div id="modal"></div>
|
||||
|
||||
<script src="/static/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
|
||||
@@ -2,11 +2,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Settings - ATCR</title>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||||
{{ template "head" . }}
|
||||
</head>
|
||||
<body>
|
||||
{{ template "nav" . }}
|
||||
@@ -113,7 +110,7 @@
|
||||
</main>
|
||||
|
||||
|
||||
<script src="/static/js/app.js"></script>
|
||||
<script src="/js/app.js"></script>
|
||||
|
||||
<script>
|
||||
// Default Hold Update - Dynamic display update
|
||||
|
||||
@@ -2,12 +2,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ .ViewedUser.Handle }} - ATCR</title>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||||
<script src="/static/js/app.js"></script>
|
||||
{{ template "head" . }}
|
||||
</head>
|
||||
<body>
|
||||
{{ template "nav" . }}
|
||||
|
||||
@@ -109,3 +109,29 @@ func StaticHandler() http.Handler {
|
||||
}
|
||||
return http.FileServer(http.FS(sub))
|
||||
}
|
||||
|
||||
// StaticRootFiles returns list of root-level files in static directory (not subdirectories)
|
||||
func StaticRootFiles() ([]string, error) {
|
||||
entries, err := staticFS.ReadDir("static")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var files []string
|
||||
for _, entry := range entries {
|
||||
// Only include files, not directories
|
||||
if !entry.IsDir() {
|
||||
files = append(files, entry.Name())
|
||||
}
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// StaticSubdir returns an fs.FS for a subdirectory within static/
|
||||
func StaticSubdir(name string) http.Handler {
|
||||
sub, err := fs.Sub(staticFS, "static/"+name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return http.FileServer(http.FS(sub))
|
||||
}
|
||||
|
||||
@@ -510,7 +510,7 @@ func (t *CaptainRecord) UnmarshalCBOR(r io.Reader) (err error) {
|
||||
|
||||
n := extra
|
||||
|
||||
nameBuf := make([]byte, 19)
|
||||
nameBuf := make([]byte, 18)
|
||||
for i := uint64(0); i < n; i++ {
|
||||
nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user