From f7dde29539536a67687cbeff9662a9617e077150 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Mon, 18 Aug 2025 03:12:41 +0800 Subject: 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 +++++++-- .../incoming/web/handlers/not_implemented.go | 11 +++- .../internal/incoming/web/handlers/repo/handler.go | 15 ++++++ .../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 +-- 18 files changed, 173 insertions(+), 56 deletions(-) create mode 100644 forged/internal/global/global.go create mode 100644 forged/internal/incoming/web/handlers/repo/handler.go (limited to 'forged') diff --git a/forged/internal/global/global.go b/forged/internal/global/global.go new file mode 100644 index 0000000..7d2f03f --- /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 d0f57f5..941b03f 100644 --- a/forged/internal/incoming/hooks/hooks.go +++ b/forged/internal/incoming/hooks/hooks.go @@ -10,12 +10,14 @@ import ( "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 @@ type hookInfo struct { 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 61b1caf..d7e5ef4 100644 --- a/forged/internal/incoming/lmtp/lmtp.go +++ b/forged/internal/incoming/lmtp/lmtp.go @@ -8,6 +8,7 @@ import ( "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 @@ type Server struct { 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 9338eca..dc03501 100644 --- a/forged/internal/incoming/ssh/ssh.go +++ b/forged/internal/incoming/ssh/ssh.go @@ -9,26 +9,27 @@ import ( 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 @@ func New(config Config) (server *Server, err error) { 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 da2a2e0..bc50b33 100644 --- a/forged/internal/incoming/web/handler.go +++ b/forged/internal/incoming/web/handler.go @@ -5,6 +5,8 @@ import ( "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 @@ func NewHandler(cfg Config) http.Handler { 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 0c631c3..e56a3b5 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 @@ func (h *GroupHTTP) Index(w http.ResponseWriter, r *http.Request, _ wtypes.Vars) GroupPath: "/" + strings.Join(base.GroupPath, "/") + "/", }) } - diff --git a/forged/internal/incoming/web/handlers/index.go b/forged/internal/incoming/web/handlers/index.go index 259ca4a..22e6201 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 472f73b..6813c88 100644 --- a/forged/internal/incoming/web/handlers/not_implemented.go +++ b/forged/internal/incoming/web/handlers/not_implemented.go @@ -3,12 +3,19 @@ package handlers 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 0000000..2881d7d --- /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 dd1382f..1a804b2 100644 --- a/forged/internal/incoming/web/handlers/repo/index.go +++ b/forged/internal/incoming/web/handlers/repo/index.go @@ -4,16 +4,9 @@ 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) repo := v["repo"] @@ -25,4 +18,3 @@ func (h *HTTP) Index(w http.ResponseWriter, r *http.Request, v wtypes.Vars) { Repo: repo, }) } - diff --git a/forged/internal/incoming/web/router.go b/forged/internal/incoming/web/router.go index 46eb935..7c2717d 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 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { 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 @@ func (r *Router) err500(w http.ResponseWriter, b *wtypes.BaseData, msg string) { } 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 6229bf0..f81886f 100644 --- a/forged/internal/incoming/web/server.go +++ b/forged/internal/incoming/web/server.go @@ -9,6 +9,8 @@ import ( "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 @@ type Server struct { 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 @@ func New(config Config) (server *Server) { 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 d47b13a..1301fe9 100644 --- a/forged/internal/incoming/web/types/types.go +++ b/forged/internal/incoming/web/types/types.go @@ -3,18 +3,24 @@ package types 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 86332a7..ecd5fe1 100644 --- a/forged/internal/server/server.go +++ b/forged/internal/server/server.go @@ -6,6 +6,8 @@ import ( "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 @@ type Server struct { 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 @@ func New(configPath string) (server *Server, err error) { 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 07fe5e7..5b48fc4 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 22a3958..11e2365 100644 --- a/forged/templates/_footer.tmpl +++ b/forged/templates/_footer.tmpl @@ -4,7 +4,7 @@ */}} {{- 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 340a2ac..39d3491 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 66bd177..fa9b6a0 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 @@ - {{- 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 -}} -- cgit v1.2.3