Route signed-in users to Explore
Authored by
mfwolffe <wolffemf@dukes.jmu.edu>
- SHA
fa206875632258b9201dd30158f77776d1e36b10- Parents
-
cabd341 - Tree
bf3c35e
fa20687
fa206875632258b9201dd30158f77776d1e36b10cabd341
bf3c35einternal/web/handlers/auth/auth.gomodified@@ -338,6 +338,8 @@ type loginForm struct { | ||
| 338 | 338 | Username string |
| 339 | 339 | } |
| 340 | 340 | |
| 341 | +const defaultPostLoginPath = "/explore" | |
| 342 | + | |
| 341 | 343 | func (h *Handlers) loginUser(ctx context.Context, identifier string) (usersdb.User, error) { |
| 342 | 344 | if strings.Contains(identifier, "@") { |
| 343 | 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 | 491 | return |
| 490 | 492 | } |
| 491 | 493 | |
| 492 | - dest := "/" | |
| 494 | + dest := defaultPostLoginPath | |
| 493 | 495 | if next != "" && strings.HasPrefix(next, "/") && !strings.HasPrefix(next, "//") { |
| 494 | 496 | // dest is constrained to single-leading-slash relative paths above, |
| 495 | 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 | 391 | body, _ := io.ReadAll(resp.Body) |
| 392 | 392 | t.Fatalf("verified login: status %d body=%s", resp.StatusCode, body) |
| 393 | 393 | } |
| 394 | - if loc := resp.Header.Get("Location"); loc != "/" { | |
| 395 | - t.Fatalf("login redirect: %q, want /", loc) | |
| 394 | + if loc := resp.Header.Get("Location"); loc != "/explore" { | |
| 395 | + t.Fatalf("login redirect: %q, want /explore", loc) | |
| 396 | 396 | } |
| 397 | 397 | _ = resp.Body.Close() |
| 398 | 398 | |
@@ -425,8 +425,8 @@ func TestLogin_AcceptsEmailAddress(t *testing.T) { | ||
| 425 | 425 | body, _ := io.ReadAll(resp.Body) |
| 426 | 426 | t.Fatalf("email login: status %d body=%s", resp.StatusCode, body) |
| 427 | 427 | } |
| 428 | - if loc := resp.Header.Get("Location"); loc != "/" { | |
| 429 | - t.Fatalf("email login redirect: %q, want /", loc) | |
| 428 | + if loc := resp.Header.Get("Location"); loc != "/explore" { | |
| 429 | + t.Fatalf("email login redirect: %q, want /explore", loc) | |
| 430 | 430 | } |
| 431 | 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 | 137 | return |
| 138 | 138 | } |
| 139 | 139 | |
| 140 | - dest := "/" | |
| 140 | + dest := defaultPostLoginPath | |
| 141 | 141 | if next != "" && strings.HasPrefix(next, "/") && !strings.HasPrefix(next, "//") { |
| 142 | 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 | 160 | body, _ := io.ReadAll(resp.Body) |
| 161 | 161 | t.Fatalf("2fa challenge: %d body=%s", resp.StatusCode, body) |
| 162 | 162 | } |
| 163 | - if loc := resp.Header.Get("Location"); loc != "/" { | |
| 164 | - t.Fatalf("expected / after challenge, got %q", loc) | |
| 163 | + if loc := resp.Header.Get("Location"); loc != "/explore" { | |
| 164 | + t.Fatalf("expected /explore after challenge, got %q", loc) | |
| 165 | 165 | } |
| 166 | 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 | 255 | r.Use(middleware.Compress) |
| 256 | 256 | r.Use(middleware.Timeout(30 * time.Second)) |
| 257 | 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 | 259 | r.Get("/explore", exploreHandler{render: rr, logger: deps.Logger, pool: deps.Pool}.ServeExplore) |
| 260 | 260 | r.Get("/trending", exploreHandler{render: rr, logger: deps.Logger, pool: deps.Pool}.ServeTrending) |
| 261 | 261 | // /internal/panic is a dev affordance: GET it to trigger the |
internal/web/handlers/hello.gomodified@@ -7,9 +7,6 @@ import ( | ||
| 7 | 7 | "log/slog" |
| 8 | 8 | "net/http" |
| 9 | 9 | |
| 10 | - "github.com/jackc/pgx/v5/pgxpool" | |
| 11 | - | |
| 12 | - "github.com/tenseleyFlow/shithub/internal/social" | |
| 13 | 10 | "github.com/tenseleyFlow/shithub/internal/version" |
| 14 | 11 | "github.com/tenseleyFlow/shithub/internal/web/middleware" |
| 15 | 12 | "github.com/tenseleyFlow/shithub/internal/web/render" |
@@ -19,7 +16,6 @@ type helloHandler struct { | ||
| 19 | 16 | render *render.Renderer |
| 20 | 17 | logoSVG string |
| 21 | 18 | logger *slog.Logger |
| 22 | - pool *pgxpool.Pool | |
| 23 | 19 | } |
| 24 | 20 | |
| 25 | 21 | type helloData struct { |
@@ -54,11 +50,6 @@ type helloData struct { | ||
| 54 | 50 | |
| 55 | 51 | func (h helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
| 56 | 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 | 53 | data := helloData{ |
| 63 | 54 | Title: "Welcome", |
| 64 | 55 | Version: version.Version, |
@@ -74,38 +65,3 @@ func (h helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
| 74 | 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/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 | 6 | <h1 class="hello-title">shithub</h1> |
| 7 | 7 | <p class="hello-tagline">GitHub. Open source. Without Copilot.</p> |
| 8 | 8 | |
| 9 | - {{ if .Viewer.ID }} | |
| 10 | - {{/* Authed dashboard surface — concise, points at the things you | |
| 11 | - most likely want when you sign in. The full activity feed is a | |
| 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. */}} | |
| 9 | + {{/* Landing page — marketing copy for the v1 launch. Honest and | |
| 10 | + concise; the heavy "what works / what doesn't" lives in the | |
| 11 | + README and docs site, not on the homepage. */}} | |
| 27 | 12 | <section class="shithub-landing-pitch"> |
| 28 | 13 | <p> |
| 29 | 14 | A self-hostable git forge that aims to look and feel like GitHub. |
@@ -62,8 +47,14 @@ | ||
| 62 | 47 | </ul> |
| 63 | 48 | |
| 64 | 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 | 55 | <a class="shithub-landing-cta-primary" href="/signup">Create an account</a> |
| 66 | 56 | <a class="shithub-landing-cta-secondary" href="/login">Sign in</a> |
| 57 | + {{ end }} | |
| 67 | 58 | <a class="shithub-landing-cta-secondary" href="https://docs.shithub.sh/">Read the docs</a> |
| 68 | 59 | <a class="shithub-landing-cta-secondary" href="/shithub/shithub">Read the source</a> |
| 69 | 60 | </nav> |
@@ -74,7 +65,6 @@ | ||
| 74 | 65 | <a href="https://docs.shithub.sh/">docs</a> are honest about |
| 75 | 66 | what works and what's still in flight. |
| 76 | 67 | </p> |
| 77 | - {{ end }} | |
| 78 | 68 | |
| 79 | 69 | <dl class="hello-meta"> |
| 80 | 70 | <dt>Version</dt><dd>{{ .Version }}</dd> |