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 | 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/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> |