Go · 2678 bytes Raw Blame History
1 // SPDX-License-Identifier: AGPL-3.0-or-later
2
3 package handlers
4
5 import (
6 "html/template"
7 "log/slog"
8 "net/http"
9
10 "github.com/tenseleyFlow/shithub/internal/version"
11 "github.com/tenseleyFlow/shithub/internal/web/middleware"
12 "github.com/tenseleyFlow/shithub/internal/web/render"
13 )
14
15 type helloHandler struct {
16 render *render.Renderer
17 logoSVG string
18 baseURL string
19 logger *slog.Logger
20 }
21
22 type helloData struct {
23 Title string
24 Version string
25 Commit string
26 BuiltAt string
27 LogoSVG template.HTML
28 // Viewer + CSRFToken mirror the fields _nav.html branches on. Typed
29 // page-data structs must populate them explicitly - the renderer
30 // only auto-injects for map[string]any data.
31 Viewer middleware.CurrentUser
32 CSRFToken string
33 // SEO/social fields are read by optional layout helpers, so typed
34 // page-data structs may provide only the fields they actually need.
35 MetaDescription string
36 CanonicalURL string
37 OGTitle string
38 OGDescription string
39 OGImage string
40 OGType string
41 StructuredData template.JS
42 // GlobalSearchQuery is referenced by _nav.html's search input to
43 // preserve the query when re-rendering after a search. Hello has
44 // no query of its own, but the field must exist or template
45 // execution errors out.
46 GlobalSearchQuery string
47 // Repo/Org are optional nav contexts. They are nil on the home page,
48 // but typed data must still expose them because _nav.html probes
49 // these fields before deciding whether to render context tabs.
50 Repo any
51 Org any
52 }
53
54 func (h helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
55 viewer := middleware.CurrentUserFromContext(r.Context())
56 data := helloData{
57 Title: "Welcome",
58 Version: version.Version,
59 Commit: version.Commit,
60 BuiltAt: version.BuiltAt,
61 LogoSVG: template.HTML(h.logoSVG), // #nosec G203 - embedded server-owned asset
62 Viewer: viewer,
63 CSRFToken: middleware.CSRFTokenForRequest(r),
64 MetaDescription: defaultMetaDescription,
65 CanonicalURL: canonicalURL(h.baseURL, r, "/"),
66 OGTitle: "shithub: GitHub-style git hosting without Copilot",
67 OGDescription: "A self-hostable, AGPL GitHub alternative with familiar repositories, pull requests, issues, organizations, code search, and Actions-style CI.",
68 OGType: "website",
69 StructuredData: organizationStructuredData(publicBaseURL(h.baseURL, r)),
70 }
71 w.Header().Set("Content-Type", "text/html; charset=utf-8")
72 if err := h.render.RenderPage(w, r, "hello", data); err != nil {
73 h.logger.Error("render hello", "error", err)
74 http.Error(w, "internal server error", http.StatusInternalServerError)
75 }
76 }
77