From f7dde29539536a67687cbeff9662a9617e077150 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Mon, 18 Aug 2025 03:12:41 +0800 Subject: [PATCH] Make the index page work --- forged/internal/global/global.go | 8 ++++++++ forged/internal/incoming/hooks/hooks.go | 5 ++++- forged/internal/incoming/lmtp/lmtp.go | 5 ++++- forged/internal/incoming/ssh/ssh.go | 11 ++++++----- forged/internal/incoming/web/handler.go | 8 +++++--- forged/internal/incoming/web/handlers/group.go | 7 +++++-- forged/internal/incoming/web/handlers/index.go | 24 +++++++++++++++++++----- forged/internal/incoming/web/handlers/not_implemented.go | 11 +++++++++-- forged/internal/incoming/web/handlers/repo/handler.go | 15 +++++++++++++++ forged/internal/incoming/web/handlers/repo/index.go | 8 -------- forged/internal/incoming/web/router.go | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++--- forged/internal/incoming/web/server.go | 8 ++++++-- forged/internal/incoming/web/types/types.go | 10 ++++++++-- forged/internal/server/server.go | 21 ++++++++++++--------- forged/sql/queries/groups.sql | 3 +++ forged/templates/_footer.tmpl | 2 +- forged/templates/_header.tmpl | 16 ++++++++-------- forged/templates/index.tmpl | 8 ++++---- diff --git a/forged/internal/global/global.go b/forged/internal/global/global.go new file mode 100644 index 0000000000000000000000000000000000000000..7d2f03ff4d7ce3b4ec7c15aa1df2eb0943d5bd04 --- /dev/null +++ b/forged/internal/global/global.go @@ -0,0 +1,8 @@ +package global + +type GlobalData struct { + ForgeTitle string + ForgeVersion string + SSHPubkey string + SSHFingerprint string +} diff --git a/forged/internal/incoming/hooks/hooks.go b/forged/internal/incoming/hooks/hooks.go index d0f57f569a13ea61b8fd90d39f35c2293ac75202..941b03fa7ce05ceeb2818ee462267a106eea327f 100644 --- a/forged/internal/incoming/hooks/hooks.go +++ b/forged/internal/incoming/hooks/hooks.go @@ -10,12 +10,14 @@ "github.com/gliderlabs/ssh" "go.lindenii.runxiyu.org/forge/forged/internal/common/cmap" "go.lindenii.runxiyu.org/forge/forged/internal/common/misc" + "go.lindenii.runxiyu.org/forge/forged/internal/global" ) type Server struct { hookMap cmap.Map[string, hookInfo] socketPath string executablesPath string + globalData *global.GlobalData } type hookInfo struct { session ssh.Session @@ -30,11 +32,12 @@ repoName string contribReq string } -func New(config Config) (server *Server) { +func New(config Config, globalData *global.GlobalData) (server *Server) { return &Server{ socketPath: config.Socket, executablesPath: config.Execs, hookMap: cmap.Map[string, hookInfo]{}, + globalData: globalData, } } diff --git a/forged/internal/incoming/lmtp/lmtp.go b/forged/internal/incoming/lmtp/lmtp.go index 61b1caf1fba9c830cca5e52047e6fc6602b402d2..d7e5ef47c86860d703e676e179c3f75e96b7187d 100644 --- a/forged/internal/incoming/lmtp/lmtp.go +++ b/forged/internal/incoming/lmtp/lmtp.go @@ -8,6 +8,7 @@ "net" "time" "go.lindenii.runxiyu.org/forge/forged/internal/common/misc" + "go.lindenii.runxiyu.org/forge/forged/internal/global" ) type Server struct { @@ -16,15 +17,17 @@ domain string maxSize int64 writeTimeout uint32 readTimeout uint32 + globalData *global.GlobalData } -func New(config Config) (server *Server) { +func New(config Config, globalData *global.GlobalData) (server *Server) { return &Server{ socket: config.Socket, domain: config.Domain, maxSize: config.MaxSize, writeTimeout: config.WriteTimeout, readTimeout: config.ReadTimeout, + globalData: globalData, } } diff --git a/forged/internal/incoming/ssh/ssh.go b/forged/internal/incoming/ssh/ssh.go index 9338eca47eb2398f7a804e280be6ee235ea0b525..dc03501d60d98314ff178b7805bb40eeaf0c8bde 100644 --- a/forged/internal/incoming/ssh/ssh.go +++ b/forged/internal/incoming/ssh/ssh.go @@ -9,26 +9,27 @@ "time" gliderssh "github.com/gliderlabs/ssh" "go.lindenii.runxiyu.org/forge/forged/internal/common/misc" + "go.lindenii.runxiyu.org/forge/forged/internal/global" gossh "golang.org/x/crypto/ssh" ) type Server struct { gliderServer *gliderssh.Server privkey gossh.Signer - pubkeyString string - pubkeyFP string net string addr string root string shutdownTimeout uint32 + globalData *global.GlobalData } -func New(config Config) (server *Server, err error) { +func New(config Config, globalData *global.GlobalData) (server *Server, err error) { server = &Server{ net: config.Net, addr: config.Addr, root: config.Root, shutdownTimeout: config.ShutdownTimeout, + globalData: globalData, } //exhaustruct:ignore var privkeyBytes []byte @@ -43,8 +44,8 @@ if err != nil { return server, fmt.Errorf("parse SSH private key: %w", err) } - server.pubkeyString = misc.BytesToString(gossh.MarshalAuthorizedKey(server.privkey.PublicKey())) - server.pubkeyFP = gossh.FingerprintSHA256(server.privkey.PublicKey()) + server.globalData.SSHPubkey = misc.BytesToString(gossh.MarshalAuthorizedKey(server.privkey.PublicKey())) + server.globalData.SSHFingerprint = gossh.FingerprintSHA256(server.privkey.PublicKey()) server.gliderServer = &gliderssh.Server{ Handler: handle, diff --git a/forged/internal/incoming/web/handler.go b/forged/internal/incoming/web/handler.go index da2a2e062d2ecd63a2e499338cf997d751538f7b..bc50b332482f35df70741b8754dd1c41dad6651a 100644 --- a/forged/internal/incoming/web/handler.go +++ b/forged/internal/incoming/web/handler.go @@ -5,6 +5,8 @@ "html/template" "net/http" "go.lindenii.runxiyu.org/forge/forged/internal/common/misc" + "go.lindenii.runxiyu.org/forge/forged/internal/database/queries" + "go.lindenii.runxiyu.org/forge/forged/internal/global" handlers "go.lindenii.runxiyu.org/forge/forged/internal/incoming/web/handlers" repoHandlers "go.lindenii.runxiyu.org/forge/forged/internal/incoming/web/handlers/repo" "go.lindenii.runxiyu.org/forge/forged/internal/incoming/web/templates" @@ -14,8 +16,8 @@ type handler struct { r *Router } -func NewHandler(cfg Config) http.Handler { - h := &handler{r: NewRouter().ReverseProxy(cfg.ReverseProxy)} +func NewHandler(cfg Config, globalData *global.GlobalData, queries *queries.Queries) *handler { + h := &handler{r: NewRouter().ReverseProxy(cfg.ReverseProxy).Global(globalData).Queries(queries)} staticFS := http.FileServer(http.Dir(cfg.StaticPath)) h.r.ANYHTTP("-/static/*rest", @@ -36,7 +38,7 @@ indexHTTP := handlers.NewIndexHTTP(renderer) groupHTTP := handlers.NewGroupHTTP(renderer) repoHTTP := repoHandlers.NewHTTP(renderer) - notImpl := handlers.NewNotImplementedHTTP() + notImpl := handlers.NewNotImplementedHTTP(renderer) // Index h.r.GET("/", indexHTTP.Index) diff --git a/forged/internal/incoming/web/handlers/group.go b/forged/internal/incoming/web/handlers/group.go index 0c631c36fc21105baac482175df899b875c91017..e56a3b53f8ea8514f94721b953e88fbed96d7ad1 100644 --- a/forged/internal/incoming/web/handlers/group.go +++ b/forged/internal/incoming/web/handlers/group.go @@ -12,7 +12,11 @@ type GroupHTTP struct { r templates.Renderer } -func NewGroupHTTP(r templates.Renderer) *GroupHTTP { return &GroupHTTP{r: r} } +func NewGroupHTTP(r templates.Renderer) *GroupHTTP { + return &GroupHTTP{ + r: r, + } +} func (h *GroupHTTP) Index(w http.ResponseWriter, r *http.Request, _ wtypes.Vars) { base := wtypes.Base(r) @@ -22,4 +26,3 @@ }{ GroupPath: "/" + strings.Join(base.GroupPath, "/") + "/", }) } - diff --git a/forged/internal/incoming/web/handlers/index.go b/forged/internal/incoming/web/handlers/index.go index 259ca4a578a61b3242b6dfc7a25d91dbcc79b0d1..22e6201d5b92feb738b370b1f6a0b1bfb4fcb7fe 100644 --- a/forged/internal/incoming/web/handlers/index.go +++ b/forged/internal/incoming/web/handlers/index.go @@ -4,7 +4,9 @@ import ( "log" "net/http" + "go.lindenii.runxiyu.org/forge/forged/internal/database/queries" "go.lindenii.runxiyu.org/forge/forged/internal/incoming/web/templates" + "go.lindenii.runxiyu.org/forge/forged/internal/incoming/web/types" wtypes "go.lindenii.runxiyu.org/forge/forged/internal/incoming/web/types" ) @@ -12,13 +14,25 @@ type IndexHTTP struct { r templates.Renderer } -func NewIndexHTTP(r templates.Renderer) *IndexHTTP { return &IndexHTTP{r: r} } +func NewIndexHTTP(r templates.Renderer) *IndexHTTP { + return &IndexHTTP{ + r: r, + } +} -func (h *IndexHTTP) Index(w http.ResponseWriter, _ *http.Request, _ wtypes.Vars) { - err := h.r.Render(w, "index", struct { - Title string +func (h *IndexHTTP) Index(w http.ResponseWriter, r *http.Request, _ wtypes.Vars) { + groups, err := types.Base(r).Queries.GetRootGroups(r.Context()) + if err != nil { + http.Error(w, "failed to get root groups", http.StatusInternalServerError) + log.Println("failed to get root groups", "error", err) + return + } + err = h.r.Render(w, "index", struct { + BaseData *types.BaseData + Groups []queries.GetRootGroupsRow }{ - Title: "Home", + BaseData: types.Base(r), + Groups: groups, }) if err != nil { log.Println("failed to render index page", "error", err) diff --git a/forged/internal/incoming/web/handlers/not_implemented.go b/forged/internal/incoming/web/handlers/not_implemented.go index 472f73bdac4d13f2da896a09e0f578582208c6b1..6813c88a024308294e24b8fb7eef8656a489b8ac 100644 --- a/forged/internal/incoming/web/handlers/not_implemented.go +++ b/forged/internal/incoming/web/handlers/not_implemented.go @@ -3,12 +3,19 @@ import ( "net/http" + "go.lindenii.runxiyu.org/forge/forged/internal/incoming/web/templates" wtypes "go.lindenii.runxiyu.org/forge/forged/internal/incoming/web/types" ) -type NotImplementedHTTP struct{} +type NotImplementedHTTP struct { + r templates.Renderer +} -func NewNotImplementedHTTP() *NotImplementedHTTP { return &NotImplementedHTTP{} } +func NewNotImplementedHTTP(r templates.Renderer) *NotImplementedHTTP { + return &NotImplementedHTTP{ + r: r, + } +} func (h *NotImplementedHTTP) Handle(w http.ResponseWriter, _ *http.Request, _ wtypes.Vars) { http.Error(w, "not implemented", http.StatusNotImplemented) diff --git a/forged/internal/incoming/web/handlers/repo/handler.go b/forged/internal/incoming/web/handlers/repo/handler.go new file mode 100644 index 0000000000000000000000000000000000000000..2881d7dea5654db9420c7550d7a3384e5a27415c --- /dev/null +++ b/forged/internal/incoming/web/handlers/repo/handler.go @@ -0,0 +1,15 @@ +package repo + +import ( + "go.lindenii.runxiyu.org/forge/forged/internal/incoming/web/templates" +) + +type HTTP struct { + r templates.Renderer +} + +func NewHTTP(r templates.Renderer) *HTTP { + return &HTTP{ + r: r, + } +} diff --git a/forged/internal/incoming/web/handlers/repo/index.go b/forged/internal/incoming/web/handlers/repo/index.go index dd1382f991a135f105b3f3765bb83052d2412b7a..1a804b28c4a690be8ae7cc7b6b56d14f453d827d 100644 --- a/forged/internal/incoming/web/handlers/repo/index.go +++ b/forged/internal/incoming/web/handlers/repo/index.go @@ -4,15 +4,8 @@ import ( "net/http" "strings" - "go.lindenii.runxiyu.org/forge/forged/internal/incoming/web/templates" wtypes "go.lindenii.runxiyu.org/forge/forged/internal/incoming/web/types" ) - -type HTTP struct { - r templates.Renderer -} - -func NewHTTP(r templates.Renderer) *HTTP { return &HTTP{r: r} } func (h *HTTP) Index(w http.ResponseWriter, r *http.Request, v wtypes.Vars) { base := wtypes.Base(r) @@ -25,4 +18,3 @@ Group: "/" + strings.Join(base.GroupPath, "/") + "/", Repo: repo, }) } - diff --git a/forged/internal/incoming/web/router.go b/forged/internal/incoming/web/router.go index 46eb93561e6307532a1dd91aa703b9fa0173cfae..7c2717db17744e80abd9427c76762f37abf7f748 100644 --- a/forged/internal/incoming/web/router.go +++ b/forged/internal/incoming/web/router.go @@ -1,15 +1,18 @@ package web import ( + "fmt" "net/http" "net/url" "sort" "strings" + "go.lindenii.runxiyu.org/forge/forged/internal/database/queries" + "go.lindenii.runxiyu.org/forge/forged/internal/global" wtypes "go.lindenii.runxiyu.org/forge/forged/internal/incoming/web/types" ) -type UserResolver func(*http.Request) (id int, username string, err error) +type UserResolver func(*http.Request) (id string, username string, err error) type ErrorRenderers struct { BadRequest func(http.ResponseWriter, *wtypes.BaseData, string) @@ -57,13 +60,21 @@ type Router struct { routes []route errors ErrorRenderers user UserResolver - global any + global *global.GlobalData reverseProxy bool + queries *queries.Queries } func NewRouter() *Router { return &Router{} } -func (r *Router) Global(v any) *Router { r.global = v; return r } +func (r *Router) Global(g *global.GlobalData) *Router { + r.global = g + return r +} +func (r *Router) Queries(q *queries.Queries) *Router { + r.queries = q + return r +} func (r *Router) ReverseProxy(enabled bool) *Router { r.reverseProxy = enabled; return r } func (r *Router) Errors(e ErrorRenderers) *Router { r.errors = e; return r } func (r *Router) UserResolver(u UserResolver) *Router { r.user = u; return r } @@ -138,6 +149,13 @@ bd := &wtypes.BaseData{ Global: r.global, URLSegments: segments, DirMode: dirMode, + Queries: r.queries, + } + + bd.RefType, bd.RefName, err = GetParamRefTypeName(req) + if err != nil { + r.err400(w, bd, "Error parsing ref query parameters: "+err.Error()) + return } if r.user != nil { @@ -379,3 +397,38 @@ return } http.Error(w, msg, http.StatusInternalServerError) } + +func GetParamRefTypeName(request *http.Request) (retRefType, retRefName string, err error) { + rawQuery := request.URL.RawQuery + queryValues, err := url.ParseQuery(rawQuery) + if err != nil { + return + } + done := false + for _, refType := range []string{"commit", "branch", "tag"} { + refName, ok := queryValues[refType] + if ok { + if done { + err = errDupRefSpec + return + } + done = true + if len(refName) != 1 { + err = errDupRefSpec + return + } + retRefName = refName[0] + retRefType = refType + } + } + if !done { + retRefType = "" + retRefName = "" + err = nil // actually returning empty strings is enough? + } + return +} + +var ( + errDupRefSpec = fmt.Errorf("duplicate ref specifications") +) diff --git a/forged/internal/incoming/web/server.go b/forged/internal/incoming/web/server.go index 6229bf0a5b23121eb29e8afa2833121056ab7151..f81886f2d6ec53c5ab158af5e8e59b836cb848aa 100644 --- a/forged/internal/incoming/web/server.go +++ b/forged/internal/incoming/web/server.go @@ -9,6 +9,8 @@ "net/http" "time" "go.lindenii.runxiyu.org/forge/forged/internal/common/misc" + "go.lindenii.runxiyu.org/forge/forged/internal/database/queries" + "go.lindenii.runxiyu.org/forge/forged/internal/global" ) type Server struct { @@ -17,11 +19,12 @@ addr string root string httpServer *http.Server shutdownTimeout uint32 + globalData *global.GlobalData } -func New(config Config) (server *Server) { +func New(config Config, globalData *global.GlobalData, queries *queries.Queries) *Server { httpServer := &http.Server{ - Handler: NewHandler(config), + Handler: NewHandler(config, globalData, queries), ReadTimeout: time.Duration(config.ReadTimeout) * time.Second, WriteTimeout: time.Duration(config.WriteTimeout) * time.Second, IdleTimeout: time.Duration(config.IdleTimeout) * time.Second, @@ -33,6 +36,7 @@ addr: config.Addr, root: config.Root, shutdownTimeout: config.ShutdownTimeout, httpServer: httpServer, + globalData: globalData, } } diff --git a/forged/internal/incoming/web/types/types.go b/forged/internal/incoming/web/types/types.go index d47b13ab5965146d6c100e7f85f2ab50c6d6eff3..1301fe930a33060422630d59e1aa9a536df1761e 100644 --- a/forged/internal/incoming/web/types/types.go +++ b/forged/internal/incoming/web/types/types.go @@ -3,18 +3,24 @@ import ( "context" "net/http" + + "go.lindenii.runxiyu.org/forge/forged/internal/database/queries" + "go.lindenii.runxiyu.org/forge/forged/internal/global" ) // BaseData is per-request context computed by the router and read by handlers. // Keep it small and stable; page-specific data should live in view models. type BaseData struct { - Global any - UserID int + UserID string Username string URLSegments []string DirMode bool GroupPath []string SeparatorIndex int + RefType string + RefName string + Global *global.GlobalData + Queries *queries.Queries } type ctxKey struct{} diff --git a/forged/internal/server/server.go b/forged/internal/server/server.go index 86332a7656366703873f04b63cac16ffea7159ef..ecd5fe1ee2ecc1631580a6f56a737c416c71a774 100644 --- a/forged/internal/server/server.go +++ b/forged/internal/server/server.go @@ -6,6 +6,8 @@ "fmt" "go.lindenii.runxiyu.org/forge/forged/internal/config" "go.lindenii.runxiyu.org/forge/forged/internal/database" + "go.lindenii.runxiyu.org/forge/forged/internal/database/queries" + "go.lindenii.runxiyu.org/forge/forged/internal/global" "go.lindenii.runxiyu.org/forge/forged/internal/incoming/hooks" "go.lindenii.runxiyu.org/forge/forged/internal/incoming/lmtp" "go.lindenii.runxiyu.org/forge/forged/internal/incoming/ssh" @@ -22,11 +24,7 @@ lmtpServer *lmtp.Server webServer *web.Server sshServer *ssh.Server - globalData struct { - SSHPubkey string - SSHFingerprint string - Version string - } + globalData global.GlobalData } func New(configPath string) (server *Server, err error) { @@ -37,10 +35,15 @@ if err != nil { return server, fmt.Errorf("open config: %w", err) } - server.hookServer = hooks.New(server.config.Hooks) - server.lmtpServer = lmtp.New(server.config.LMTP) - server.webServer = web.New(server.config.Web) - server.sshServer, err = ssh.New(server.config.SSH) + queries := queries.New(&server.database) + + server.globalData.ForgeVersion = "unknown" // TODO + server.globalData.ForgeTitle = server.config.General.Title + + server.hookServer = hooks.New(server.config.Hooks, &server.globalData) + server.lmtpServer = lmtp.New(server.config.LMTP, &server.globalData) + server.webServer = web.New(server.config.Web, &server.globalData, queries) + server.sshServer, err = ssh.New(server.config.SSH, &server.globalData) if err != nil { return server, fmt.Errorf("create SSH server: %w", err) } diff --git a/forged/sql/queries/groups.sql b/forged/sql/queries/groups.sql index 07fe5e7fdddf5c7e1319ae59f334a639a4b2855a..5b48fc40ceb5809e14f5bf774900d423dc23104a 100644 --- a/forged/sql/queries/groups.sql +++ b/forged/sql/queries/groups.sql @@ -1,3 +1,6 @@ +-- name: GetRootGroups :many +SELECT name, COALESCE(description, '') FROM groups WHERE parent_group IS NULL; + -- name: GetGroupIDDescByPath :one WITH RECURSIVE group_path_cte AS ( SELECT diff --git a/forged/templates/_footer.tmpl b/forged/templates/_footer.tmpl index 22a39585997833a586f30f56112f37134b79cba3..11e236550616dc46fb3dbf3b4d2c779cd31925a7 100644 --- a/forged/templates/_footer.tmpl +++ b/forged/templates/_footer.tmpl @@ -4,7 +4,7 @@ SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu */}} {{- define "footer" -}} Lindenii Forge -{{ .global.forge_version }} +{{ .BaseData.Global.ForgeVersion }} (upstream, license, support) diff --git a/forged/templates/_header.tmpl b/forged/templates/_header.tmpl index 340a2ac6d295e5ce78f6b0d089844229323fe557..39d3491c1c09e5c645f2f6110ef855128dd851de 100644 --- a/forged/templates/_header.tmpl +++ b/forged/templates/_header.tmpl @@ -5,15 +5,15 @@ */}} {{- define "header" -}}
- {{- if ne .user_id_string "" -}} - {{- .username -}} + {{- if ne .BaseData.UserID "" -}} + {{- .BaseData.Username -}} {{- else -}} Login {{- end -}} diff --git a/forged/templates/index.tmpl b/forged/templates/index.tmpl index 66bd17712fac0cd217298dd9ae894d4c9c6a7090..fa9b6a0e3a31180319a5109b52551b6d63d1869f 100644 --- a/forged/templates/index.tmpl +++ b/forged/templates/index.tmpl @@ -7,7 +7,7 @@ {{- template "head_common" . -}} - Index – {{ .global.forge_title -}} + Index – {{ .BaseData.Global.ForgeTitle -}} {{- template "header" . -}} @@ -24,7 +24,7 @@ Description - {{- range .groups -}} + {{- range .Groups -}} {{- .Name -}} @@ -47,11 +47,11 @@ SSH public key - {{- .global.server_public_key_string -}} + {{- .BaseData.Global.SSHPubkey -}} SSH fingerprint - {{- .global.server_public_key_fingerprint -}} + {{- .BaseData.Global.SSHFingerprint -}} -- 2.48.1