tangled
alpha
login
or
join now
evan.jarrett.net
/
at-container-registry
66
fork
atom
A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
atcr.io
docker
container
atproto
go
66
fork
atom
overview
issues
1
pulls
pipelines
make navbar a component
evan.jarrett.net
4 months ago
9af56daa
55afa99e
verified
This commit was signed with the committer's
known signature
.
evan.jarrett.net
SSH Key Fingerprint:
SHA256:bznk0uVPp7XFOl67P0uTM1pCjf2A4ojeP/lsUE7uauQ=
+55
-50
8 changed files
expand all
collapse all
unified
split
pkg
appview
config.go
templates
components
nav-brand.html
nav-search.html
nav-theme-toggle.html
nav-user.html
nav.html
pages
login.html
auth
oauth
client.go
+1
pkg/appview/config.go
···
251
251
// Auto-detect from HTTP addr
252
252
if httpAddr[0] == ':' {
253
253
// Just a port, assume localhost
254
254
+
// Use "127.0.0.1" per RFC 8252 (OAuth servers reject "localhost")
254
255
return fmt.Sprintf("http://127.0.0.1%s", httpAddr)
255
256
}
256
257
+5
pkg/appview/templates/components/nav-brand.html
···
1
1
+
{{ define "nav-brand" }}
2
2
+
<div class="nav-brand">
3
3
+
<a href="/"><span class="at-protocol">at://</span>Container Registry</a>
4
4
+
</div>
5
5
+
{{ end }}
+7
pkg/appview/templates/components/nav-search.html
···
1
1
+
{{ define "nav-search" }}
2
2
+
<div class="nav-search">
3
3
+
<form action="/search" method="get">
4
4
+
<input type="text" name="q" placeholder="Search images..." value="{{ .Query }}" />
5
5
+
</form>
6
6
+
</div>
7
7
+
{{ end }}
+3
pkg/appview/templates/components/nav-theme-toggle.html
···
1
1
+
{{ define "nav-theme-toggle" }}
2
2
+
<button id="theme-toggle" onclick="toggleTheme()" class="btn-link theme-toggle-btn" aria-label="Toggle theme"></button>
3
3
+
{{ end }}
+27
pkg/appview/templates/components/nav-user.html
···
1
1
+
{{ define "nav-user" }}
2
2
+
{{ if .User }}
3
3
+
<div class="user-dropdown">
4
4
+
<button class="user-menu-btn" id="user-menu-btn" aria-expanded="false" aria-haspopup="true">
5
5
+
{{ if .User.Avatar }}
6
6
+
<img src="{{ .User.Avatar }}" alt="{{ .User.Handle }}" class="user-avatar">
7
7
+
{{ else }}
8
8
+
<div class="user-avatar-placeholder">{{ firstChar .User.Handle }}</div>
9
9
+
{{ end }}
10
10
+
<span class="user-handle">@{{ .User.Handle }}</span>
11
11
+
<svg class="dropdown-arrow" width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
12
12
+
<path d="M6 9L1 4h10z"/>
13
13
+
</svg>
14
14
+
</button>
15
15
+
<div class="dropdown-menu" id="user-dropdown-menu" hidden>
16
16
+
<a href="/u/{{ .User.Handle }}" class="dropdown-item">Your Repositories</a>
17
17
+
<a href="/settings" class="dropdown-item">Settings</a>
18
18
+
<hr class="dropdown-divider">
19
19
+
<form action="/auth/logout" method="POST">
20
20
+
<button type="submit" class="dropdown-item logout-btn">Logout</button>
21
21
+
</form>
22
22
+
</div>
23
23
+
</div>
24
24
+
{{ else }}
25
25
+
<a href="/auth/oauth/login?return_to=/" class="btn-primary">Login</a>
26
26
+
{{ end }}
27
27
+
{{ end }}
+11
-34
pkg/appview/templates/components/nav.html
···
1
1
{{ define "nav" }}
2
2
<nav class="navbar">
3
3
-
<div class="nav-brand">
4
4
-
<a href="/"><span class="at-protocol">at://</span>Container Registry</a>
3
3
+
{{ template "nav-brand" }}
4
4
+
{{ template "nav-search" . }}
5
5
+
<div class="nav-links">
6
6
+
{{ template "nav-theme-toggle" }}
7
7
+
{{ template "nav-user" . }}
5
8
</div>
9
9
+
</nav>
10
10
+
{{ end }}
6
11
7
7
-
<div class="nav-search">
8
8
-
<form action="/search" method="get">
9
9
-
<input type="text" name="q" placeholder="Search images..." value="{{ .Query }}" />
10
10
-
</form>
11
11
-
</div>
12
12
-
12
12
+
{{ define "nav-simple" }}
13
13
+
<nav class="navbar">
14
14
+
{{ template "nav-brand" }}
13
15
<div class="nav-links">
14
14
-
<button id="theme-toggle" onclick="toggleTheme()" class="btn-link theme-toggle-btn" aria-label="Toggle theme"></button>
15
15
-
{{ if .User }}
16
16
-
<div class="user-dropdown">
17
17
-
<button class="user-menu-btn" id="user-menu-btn" aria-expanded="false" aria-haspopup="true">
18
18
-
{{ if .User.Avatar }}
19
19
-
<img src="{{ .User.Avatar }}" alt="{{ .User.Handle }}" class="user-avatar">
20
20
-
{{ else }}
21
21
-
<div class="user-avatar-placeholder">{{ firstChar .User.Handle }}</div>
22
22
-
{{ end }}
23
23
-
<span class="user-handle">@{{ .User.Handle }}</span>
24
24
-
<svg class="dropdown-arrow" width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
25
25
-
<path d="M6 9L1 4h10z"/>
26
26
-
</svg>
27
27
-
</button>
28
28
-
<div class="dropdown-menu" id="user-dropdown-menu" hidden>
29
29
-
<a href="/u/{{ .User.Handle }}" class="dropdown-item">Your Repositories</a>
30
30
-
<a href="/settings" class="dropdown-item">Settings</a>
31
31
-
<hr class="dropdown-divider">
32
32
-
<form action="/auth/logout" method="POST">
33
33
-
<button type="submit" class="dropdown-item logout-btn">Logout</button>
34
34
-
</form>
35
35
-
</div>
36
36
-
</div>
37
37
-
{{ else }}
38
38
-
<a href="/auth/oauth/login?return_to=/" class="btn-primary">Login</a>
39
39
-
{{ end }}
16
16
+
{{ template "nav-theme-toggle" }}
40
17
</div>
41
18
</nav>
42
19
{{ end }}
+1
-5
pkg/appview/templates/pages/login.html
···
6
6
{{ template "head" . }}
7
7
</head>
8
8
<body>
9
9
-
<nav class="navbar">
10
10
-
<div class="nav-brand">
11
11
-
<a href="/">ATCR</a>
12
12
-
</div>
13
13
-
</nav>
9
9
+
{{ template "nav-simple" . }}
14
10
15
11
<main class="container">
16
12
<div class="login-page">
-11
pkg/auth/oauth/client.go
···
62
62
} else {
63
63
config = oauth.NewLocalhostConfig(redirectURI, scopes)
64
64
65
65
-
// Append client_name to localhost client ID query string
66
66
-
if clientName != "" {
67
67
-
u, err := url.Parse(config.ClientID)
68
68
-
if err == nil {
69
69
-
q := u.Query()
70
70
-
q.Set("client_name", clientName)
71
71
-
u.RawQuery = q.Encode()
72
72
-
config.ClientID = u.String()
73
73
-
}
74
74
-
}
75
75
-
76
65
slog.Info("Using public OAuth client (localhost development)")
77
66
}
78
67