tenseleyflow/shithub / fa20687

Browse files

Route signed-in users to Explore

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
fa206875632258b9201dd30158f77776d1e36b10
Parents
cabd341
Tree
bf3c35e

9 changed files

StatusFile+-
M internal/web/handlers/auth/auth.go 3 1
M internal/web/handlers/auth/auth_test.go 4 4
M internal/web/handlers/auth/twofactor.go 1 1
M internal/web/handlers/auth/twofactor_test.go 2 2
M internal/web/handlers/handlers.go 1 1
M internal/web/handlers/hello.go 0 44
M internal/web/templates/_nav_offcanvas.html 4 0
D internal/web/templates/dashboard.html 0 80
M internal/web/templates/hello.html 9 19
internal/web/handlers/auth/auth.gomodified
@@ -338,6 +338,8 @@ type loginForm struct {
338
 	Username string
338
 	Username string
339
 }
339
 }
340
 
340
 
341
+const defaultPostLoginPath = "/explore"
342
+
341
 func (h *Handlers) loginUser(ctx context.Context, identifier string) (usersdb.User, error) {
343
 func (h *Handlers) loginUser(ctx context.Context, identifier string) (usersdb.User, error) {
342
 	if strings.Contains(identifier, "@") {
344
 	if strings.Contains(identifier, "@") {
343
 		em, err := h.q.GetUserEmailByAddress(ctx, h.d.Pool, identifier)
345
 		em, err := h.q.GetUserEmailByAddress(ctx, h.d.Pool, identifier)
@@ -489,7 +491,7 @@ func (h *Handlers) loginSubmit(w http.ResponseWriter, r *http.Request) {
489
 		return
491
 		return
490
 	}
492
 	}
491
 
493
 
492
-	dest := "/"
494
+	dest := defaultPostLoginPath
493
 	if next != "" && strings.HasPrefix(next, "/") && !strings.HasPrefix(next, "//") {
495
 	if next != "" && strings.HasPrefix(next, "/") && !strings.HasPrefix(next, "//") {
494
 		// dest is constrained to single-leading-slash relative paths above,
496
 		// dest is constrained to single-leading-slash relative paths above,
495
 		// which prevents the protocol-relative ("//evil.com") form gosec warns about.
497
 		// which prevents the protocol-relative ("//evil.com") form gosec warns about.
internal/web/handlers/auth/auth_test.gomodified
@@ -391,8 +391,8 @@ func TestSignup_Verify_Login_Logout(t *testing.T) {
391
 		body, _ := io.ReadAll(resp.Body)
391
 		body, _ := io.ReadAll(resp.Body)
392
 		t.Fatalf("verified login: status %d body=%s", resp.StatusCode, body)
392
 		t.Fatalf("verified login: status %d body=%s", resp.StatusCode, body)
393
 	}
393
 	}
394
-	if loc := resp.Header.Get("Location"); loc != "/" {
394
+	if loc := resp.Header.Get("Location"); loc != "/explore" {
395
-		t.Fatalf("login redirect: %q, want /", loc)
395
+		t.Fatalf("login redirect: %q, want /explore", loc)
396
 	}
396
 	}
397
 	_ = resp.Body.Close()
397
 	_ = resp.Body.Close()
398
 
398
 
@@ -425,8 +425,8 @@ func TestLogin_AcceptsEmailAddress(t *testing.T) {
425
 		body, _ := io.ReadAll(resp.Body)
425
 		body, _ := io.ReadAll(resp.Body)
426
 		t.Fatalf("email login: status %d body=%s", resp.StatusCode, body)
426
 		t.Fatalf("email login: status %d body=%s", resp.StatusCode, body)
427
 	}
427
 	}
428
-	if loc := resp.Header.Get("Location"); loc != "/" {
428
+	if loc := resp.Header.Get("Location"); loc != "/explore" {
429
-		t.Fatalf("email login redirect: %q, want /", loc)
429
+		t.Fatalf("email login redirect: %q, want /explore", loc)
430
 	}
430
 	}
431
 	_ = resp.Body.Close()
431
 	_ = resp.Body.Close()
432
 }
432
 }
internal/web/handlers/auth/twofactor.gomodified
@@ -137,7 +137,7 @@ func (h *Handlers) twoFactorChallengeSubmit(w http.ResponseWriter, r *http.Reque
137
 		return
137
 		return
138
 	}
138
 	}
139
 
139
 
140
-	dest := "/"
140
+	dest := defaultPostLoginPath
141
 	if next != "" && strings.HasPrefix(next, "/") && !strings.HasPrefix(next, "//") {
141
 	if next != "" && strings.HasPrefix(next, "/") && !strings.HasPrefix(next, "//") {
142
 		dest = next
142
 		dest = next
143
 	}
143
 	}
internal/web/handlers/auth/twofactor_test.gomodified
@@ -160,8 +160,8 @@ func TestTwoFactor_Enroll_Logout_Login_Challenge_FullSession(t *testing.T) {
160
 		body, _ := io.ReadAll(resp.Body)
160
 		body, _ := io.ReadAll(resp.Body)
161
 		t.Fatalf("2fa challenge: %d body=%s", resp.StatusCode, body)
161
 		t.Fatalf("2fa challenge: %d body=%s", resp.StatusCode, body)
162
 	}
162
 	}
163
-	if loc := resp.Header.Get("Location"); loc != "/" {
163
+	if loc := resp.Header.Get("Location"); loc != "/explore" {
164
-		t.Fatalf("expected / after challenge, got %q", loc)
164
+		t.Fatalf("expected /explore after challenge, got %q", loc)
165
 	}
165
 	}
166
 	_ = resp.Body.Close()
166
 	_ = resp.Body.Close()
167
 }
167
 }
internal/web/handlers/handlers.gomodified
@@ -255,7 +255,7 @@ func RegisterChi(r *chi.Mux, deps Deps) (*chi.Mux, middleware.PanicHandler, http
255
 		r.Use(middleware.Compress)
255
 		r.Use(middleware.Compress)
256
 		r.Use(middleware.Timeout(30 * time.Second))
256
 		r.Use(middleware.Timeout(30 * time.Second))
257
 		r.Use(csrf)
257
 		r.Use(csrf)
258
-		r.Get("/", helloHandler{render: rr, logoSVG: deps.LogoSVG, logger: deps.Logger, pool: deps.Pool}.ServeHTTP)
258
+		r.Get("/", helloHandler{render: rr, logoSVG: deps.LogoSVG, logger: deps.Logger}.ServeHTTP)
259
 		r.Get("/explore", exploreHandler{render: rr, logger: deps.Logger, pool: deps.Pool}.ServeExplore)
259
 		r.Get("/explore", exploreHandler{render: rr, logger: deps.Logger, pool: deps.Pool}.ServeExplore)
260
 		r.Get("/trending", exploreHandler{render: rr, logger: deps.Logger, pool: deps.Pool}.ServeTrending)
260
 		r.Get("/trending", exploreHandler{render: rr, logger: deps.Logger, pool: deps.Pool}.ServeTrending)
261
 		// /internal/panic is a dev affordance: GET it to trigger the
261
 		// /internal/panic is a dev affordance: GET it to trigger the
internal/web/handlers/hello.gomodified
@@ -7,9 +7,6 @@ import (
7
 	"log/slog"
7
 	"log/slog"
8
 	"net/http"
8
 	"net/http"
9
 
9
 
10
-	"github.com/jackc/pgx/v5/pgxpool"
11
-
12
-	"github.com/tenseleyFlow/shithub/internal/social"
13
 	"github.com/tenseleyFlow/shithub/internal/version"
10
 	"github.com/tenseleyFlow/shithub/internal/version"
14
 	"github.com/tenseleyFlow/shithub/internal/web/middleware"
11
 	"github.com/tenseleyFlow/shithub/internal/web/middleware"
15
 	"github.com/tenseleyFlow/shithub/internal/web/render"
12
 	"github.com/tenseleyFlow/shithub/internal/web/render"
@@ -19,7 +16,6 @@ type helloHandler struct {
19
 	render  *render.Renderer
16
 	render  *render.Renderer
20
 	logoSVG string
17
 	logoSVG string
21
 	logger  *slog.Logger
18
 	logger  *slog.Logger
22
-	pool    *pgxpool.Pool
23
 }
19
 }
24
 
20
 
25
 type helloData struct {
21
 type helloData struct {
@@ -54,11 +50,6 @@ type helloData struct {
54
 
50
 
55
 func (h helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
51
 func (h helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
56
 	viewer := middleware.CurrentUserFromContext(r.Context())
52
 	viewer := middleware.CurrentUserFromContext(r.Context())
57
-	if viewer.ID != 0 && h.pool != nil {
58
-		h.serveDashboard(w, r, viewer)
59
-		return
60
-	}
61
-
62
 	data := helloData{
53
 	data := helloData{
63
 		Title:     "Welcome",
54
 		Title:     "Welcome",
64
 		Version:   version.Version,
55
 		Version:   version.Version,
@@ -74,38 +65,3 @@ func (h helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
74
 		http.Error(w, "internal server error", http.StatusInternalServerError)
65
 		http.Error(w, "internal server error", http.StatusInternalServerError)
75
 	}
66
 	}
76
 }
67
 }
77
-
78
-func (h helloHandler) serveDashboard(w http.ResponseWriter, r *http.Request, viewer middleware.CurrentUser) {
79
-	deps := social.Deps{Pool: h.pool, Logger: h.logger}
80
-	feed, hasNext, nextURL := feedPageFor(r, func(cursor social.FeedCursor, limit int32) ([]social.FeedItem, error) {
81
-		return social.DashboardFeed(r.Context(), deps, viewer.ID, cursor, limit)
82
-	})
83
-	repos, err := social.DashboardRepos(r.Context(), deps, viewer.ID, 8)
84
-	if err != nil && h.logger != nil {
85
-		h.logger.WarnContext(r.Context(), "dashboard repos", "error", err)
86
-	}
87
-	trendingRepos, err := social.CachedTrendingRepos(r.Context(), deps, social.TrendingScopeWeek, 7, 5)
88
-	if err != nil && h.logger != nil {
89
-		h.logger.WarnContext(r.Context(), "dashboard trending repos", "error", err)
90
-	}
91
-	trendingUsers, err := social.CachedTrendingUsers(r.Context(), deps, social.TrendingScopeWeek, 7, 5)
92
-	if err != nil && h.logger != nil {
93
-		h.logger.WarnContext(r.Context(), "dashboard trending users", "error", err)
94
-	}
95
-
96
-	data := map[string]any{
97
-		"Title":         "Home",
98
-		"TopRepos":      repos,
99
-		"Feed":          feed,
100
-		"FeedHasNext":   hasNext,
101
-		"FeedNextURL":   nextURL,
102
-		"TrendingRepos": trendingRepos,
103
-		"TrendingUsers": trendingUsers,
104
-	}
105
-	if err := h.render.RenderPage(w, r, "dashboard", data); err != nil {
106
-		if h.logger != nil {
107
-			h.logger.Error("render dashboard", "error", err)
108
-		}
109
-		http.Error(w, "internal server error", http.StatusInternalServerError)
110
-	}
111
-}
internal/web/templates/_nav_offcanvas.htmlmodified
@@ -8,7 +8,11 @@
8
     </div>
8
     </div>
9
 
9
 
10
     <nav class="shithub-offcanvas-nav" aria-label="Global navigation">
10
     <nav class="shithub-offcanvas-nav" aria-label="Global navigation">
11
+      {{ if .Viewer.ID }}
12
+      <a href="/explore" class="shithub-offcanvas-link">{{ octicon "home" }} <span>Home</span></a>
13
+      {{ else }}
11
       <a href="/" class="shithub-offcanvas-link">{{ octicon "home" }} <span>Home</span></a>
14
       <a href="/" class="shithub-offcanvas-link">{{ octicon "home" }} <span>Home</span></a>
15
+      {{ end }}
12
       <a href="/search?type=issues" class="shithub-offcanvas-link">{{ octicon "issue-opened" }} <span>All issues</span></a>
16
       <a href="/search?type=issues" class="shithub-offcanvas-link">{{ octicon "issue-opened" }} <span>All issues</span></a>
13
       <a href="/search?type=pullrequests" class="shithub-offcanvas-link">{{ octicon "git-pull-request" }} <span>All pull requests</span></a>
17
       <a href="/search?type=pullrequests" class="shithub-offcanvas-link">{{ octicon "git-pull-request" }} <span>All pull requests</span></a>
14
       <a href="/search?type=repositories" class="shithub-offcanvas-link">{{ octicon "repo" }} <span>All repositories</span></a>
18
       <a href="/search?type=repositories" class="shithub-offcanvas-link">{{ octicon "repo" }} <span>All repositories</span></a>
internal/web/templates/dashboard.htmldeleted
@@ -1,80 +0,0 @@
1
-{{ define "page" -}}
2
-<section class="shithub-dashboard-page">
3
-  <div class="shithub-dashboard-shell">
4
-    <aside class="shithub-dashboard-left" aria-label="Your repositories">
5
-      <div class="shithub-dashboard-sidehead">
6
-        <h2>Top repositories</h2>
7
-        <a href="/new" class="shithub-button shithub-button-primary shithub-button-small">{{ octicon "repo" }} New</a>
8
-      </div>
9
-      <label class="sr-only" for="dashboard-repo-filter">Find a repository</label>
10
-      <input id="dashboard-repo-filter" class="shithub-dashboard-filter" type="search" placeholder="Find a repository..." autocomplete="off">
11
-      {{ if .TopRepos }}
12
-      <ol class="shithub-dashboard-repo-list">
13
-        {{ range .TopRepos }}
14
-        <li>
15
-          <a href="/{{ $.Viewer.Username }}/{{ .Name }}"><img src="/avatars/{{ $.Viewer.Username }}" alt="" width="18" height="18"> {{ $.Viewer.Username }}/{{ .Name }}</a>
16
-        </li>
17
-        {{ end }}
18
-      </ol>
19
-      {{ else }}
20
-      <p class="shithub-dashboard-empty">No repositories yet.</p>
21
-      {{ end }}
22
-    </aside>
23
-
24
-    <main class="shithub-dashboard-main">
25
-      <h1>Home</h1>
26
-      <div class="shithub-feed-toolbar">
27
-        <h2>Feed</h2>
28
-        <a class="shithub-button shithub-button-small" href="/explore">{{ octicon "pulse" }} Explore</a>
29
-      </div>
30
-      {{ if .Feed }}
31
-      <ol class="shithub-feed-list">
32
-        {{ range .Feed }}{{ template "feed-row" . }}{{ end }}
33
-      </ol>
34
-      {{ else }}
35
-      <div class="shithub-feed-empty">
36
-        <h2>{{ octicon "people" }} Follow people and organizations to build your feed</h2>
37
-        <p>Stars, forks, pushes, issues, pull requests, and follows from your network will appear here.</p>
38
-        <a href="/explore" class="shithub-button">Explore activity</a>
39
-      </div>
40
-      {{ end }}
41
-      {{ if .FeedHasNext }}
42
-      <nav class="shithub-feed-pagination" aria-label="Feed pagination">
43
-        <a class="shithub-button" href="{{ .FeedNextURL }}">More</a>
44
-      </nav>
45
-      {{ end }}
46
-    </main>
47
-
48
-    <aside class="shithub-dashboard-right" aria-label="Discover">
49
-      <section class="shithub-side-panel">
50
-        <h2>{{ octicon "pulse" }} Trending repositories</h2>
51
-        {{ if .TrendingRepos }}
52
-        <ol class="shithub-trending-mini-list">
53
-          {{ range .TrendingRepos }}
54
-          <li>
55
-            <a href="/{{ .Owner }}/{{ .Name }}">{{ .Owner }}/{{ .Name }}</a>
56
-            {{ if .Description }}<p>{{ .Description }}</p>{{ end }}
57
-            <span>{{ octicon "star" }} {{ .StarCount }}</span>
58
-          </li>
59
-          {{ end }}
60
-        </ol>
61
-        {{ else }}<p class="shithub-dashboard-empty">No public activity yet.</p>{{ end }}
62
-      </section>
63
-
64
-      <section class="shithub-side-panel">
65
-        <h2>{{ octicon "people" }} Trending developers</h2>
66
-        {{ if .TrendingUsers }}
67
-        <ol class="shithub-trending-user-list">
68
-          {{ range .TrendingUsers }}
69
-          <li>
70
-            <a href="/{{ .Username }}"><img src="/avatars/{{ .Username }}" alt="" width="24" height="24"> {{ if .DisplayName }}{{ .DisplayName }}{{ else }}{{ .Username }}{{ end }}</a>
71
-            <span>@{{ .Username }}</span>
72
-          </li>
73
-          {{ end }}
74
-        </ol>
75
-        {{ else }}<p class="shithub-dashboard-empty">No developers trending yet.</p>{{ end }}
76
-      </section>
77
-    </aside>
78
-  </div>
79
-</section>
80
-{{- end }}
internal/web/templates/hello.htmlmodified
@@ -6,24 +6,9 @@
6
   <h1 class="hello-title">shithub</h1>
6
   <h1 class="hello-title">shithub</h1>
7
   <p class="hello-tagline">GitHub. Open source. Without Copilot.</p>
7
   <p class="hello-tagline">GitHub. Open source. Without Copilot.</p>
8
 
8
 
9
-  {{ if .Viewer.ID }}
9
+  {{/* Landing page — marketing copy for the v1 launch. Honest and
10
-  {{/* Authed dashboard surface — concise, points at the things you
10
+       concise; the heavy "what works / what doesn't" lives in the
11
-       most likely want when you sign in. The full activity feed is a
11
+       README and docs site, not on the homepage. */}}
12
-       post-MVP feature; until then the quicklinks ARE the dashboard. */}}
13
-  <div class="hello-greeting">
14
-    <p>Signed in as <strong>@{{ .Viewer.Username }}</strong>.</p>
15
-    <nav class="hello-quicklinks" aria-label="Account quick links">
16
-      <a href="/{{ .Viewer.Username }}">Your profile</a>
17
-      <a href="/new">New repository</a>
18
-      <a href="/notifications">Notifications</a>
19
-      <a href="/explore">Explore</a>
20
-      <a href="/settings/profile">Settings</a>
21
-    </nav>
22
-  </div>
23
-  {{ else }}
24
-  {{/* Anonymous landing — marketing copy for the v1 launch. Honest
25
-       and concise; the heavy "what works / what doesn't" lives in
26
-       the README and docs site, not on the homepage. */}}
27
   <section class="shithub-landing-pitch">
12
   <section class="shithub-landing-pitch">
28
     <p>
13
     <p>
29
       A self-hostable git forge that aims to look and feel like GitHub.
14
       A self-hostable git forge that aims to look and feel like GitHub.
@@ -62,8 +47,14 @@
62
   </ul>
47
   </ul>
63
 
48
 
64
   <nav class="shithub-landing-cta" aria-label="Primary actions">
49
   <nav class="shithub-landing-cta" aria-label="Primary actions">
50
+    {{ if .Viewer.ID }}
51
+    <a class="shithub-landing-cta-primary" href="/explore">Go to Explore</a>
52
+    <a class="shithub-landing-cta-secondary" href="/new">New repository</a>
53
+    <a class="shithub-landing-cta-secondary" href="/{{ .Viewer.Username }}">Your profile</a>
54
+    {{ else }}
65
     <a class="shithub-landing-cta-primary" href="/signup">Create an account</a>
55
     <a class="shithub-landing-cta-primary" href="/signup">Create an account</a>
66
     <a class="shithub-landing-cta-secondary" href="/login">Sign in</a>
56
     <a class="shithub-landing-cta-secondary" href="/login">Sign in</a>
57
+    {{ end }}
67
     <a class="shithub-landing-cta-secondary" href="https://docs.shithub.sh/">Read the docs</a>
58
     <a class="shithub-landing-cta-secondary" href="https://docs.shithub.sh/">Read the docs</a>
68
     <a class="shithub-landing-cta-secondary" href="/shithub/shithub">Read the source</a>
59
     <a class="shithub-landing-cta-secondary" href="/shithub/shithub">Read the source</a>
69
   </nav>
60
   </nav>
@@ -74,7 +65,6 @@
74
     <a href="https://docs.shithub.sh/">docs</a> are honest about
65
     <a href="https://docs.shithub.sh/">docs</a> are honest about
75
     what works and what's still in flight.
66
     what works and what's still in flight.
76
   </p>
67
   </p>
77
-  {{ end }}
78
 
68
 
79
   <dl class="hello-meta">
69
   <dl class="hello-meta">
80
     <dt>Version</dt><dd>{{ .Version }}</dd>
70
     <dt>Version</dt><dd>{{ .Version }}</dd>