aboutsummaryrefslogtreecommitdiff
path: root/forged
diff options
context:
space:
mode:
Diffstat (limited to 'forged')
-rw-r--r--forged/internal/global/global.go8
-rw-r--r--forged/internal/incoming/hooks/hooks.go5
-rw-r--r--forged/internal/incoming/lmtp/lmtp.go5
-rw-r--r--forged/internal/incoming/ssh/ssh.go11
-rw-r--r--forged/internal/incoming/web/handler.go8
-rw-r--r--forged/internal/incoming/web/handlers/group.go7
-rw-r--r--forged/internal/incoming/web/handlers/index.go24
-rw-r--r--forged/internal/incoming/web/handlers/not_implemented.go11
-rw-r--r--forged/internal/incoming/web/handlers/repo/handler.go15
-rw-r--r--forged/internal/incoming/web/handlers/repo/index.go8
-rw-r--r--forged/internal/incoming/web/router.go59
-rw-r--r--forged/internal/incoming/web/server.go8
-rw-r--r--forged/internal/incoming/web/types/types.go10
-rw-r--r--forged/internal/server/server.go21
-rw-r--r--forged/sql/queries/groups.sql3
-rw-r--r--forged/templates/_footer.tmpl2
-rw-r--r--forged/templates/_header.tmpl16
-rw-r--r--forged/templates/index.tmpl8
18 files changed, 173 insertions, 56 deletions
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" -}}
<a href="https://lindenii.runxiyu.org/forge/">Lindenii Forge</a>
-{{ .global.forge_version }}
+{{ .BaseData.Global.ForgeVersion }}
(<a href="https://forge.lindenii.runxiyu.org/forge/-/repos/server/">upstream</a>,
<a href="/-/source/LICENSE">license</a>,
<a href="https://webirc.runxiyu.org/kiwiirc/#lindenii">support</a>)
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" -}}
<header id="main-header">
<div id="main-header-forge-title">
- <a href="/">{{- .global.forge_title -}}</a>
+ <a href="/">{{- .BaseData.Global.ForgeTitle -}}</a>
</div>
<nav id="breadcrumb-nav">
{{- $path := "" -}}
- {{- $url_segments := .url_segments -}}
- {{- $dir_mode := .dir_mode -}}
- {{- $ref_type := .ref_type -}}
- {{- $ref := .ref_name -}}
- {{- $separator_index := .separator_index -}}
+ {{- $url_segments := .BaseData.URLSegments -}}
+ {{- $dir_mode := .BaseData.DirMode -}}
+ {{- $ref_type := .BaseData.RefType -}}
+ {{- $ref := .BaseData.RefName -}}
+ {{- $separator_index := .BaseData.SeparatorIndex -}}
{{- if eq $separator_index -1 -}}
{{- $separator_index = len $url_segments -}}
{{- end -}}
@@ -25,8 +25,8 @@
{{- end -}}
</nav>
<div id="main-header-user">
- {{- if ne .user_id_string "" -}}
- <a href="/-/users/{{- .user_id_string -}}">{{- .username -}}</a>
+ {{- if ne .BaseData.UserID "" -}}
+ <a href="/-/users/{{- .BaseData.UserID -}}/">{{- .BaseData.Username -}}</a>
{{- else -}}
<a href="/-/login/">Login</a>
{{- 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 @@
<html lang="en">
<head>
{{- template "head_common" . -}}
- <title>Index &ndash; {{ .global.forge_title -}}</title>
+ <title>Index &ndash; {{ .BaseData.Global.ForgeTitle -}}</title>
</head>
<body class="index">
{{- template "header" . -}}
@@ -24,7 +24,7 @@
</tr>
</thead>
<tbody>
- {{- range .groups -}}
+ {{- range .Groups -}}
<tr>
<td>
<a href="{{- .Name | path_escape -}}/">{{- .Name -}}</a>
@@ -47,11 +47,11 @@
<tbody>
<tr>
<th scope="row">SSH public key</th>
- <td><code class="breakable">{{- .global.server_public_key_string -}}</code></td>
+ <td><code class="breakable">{{- .BaseData.Global.SSHPubkey -}}</code></td>
</tr>
<tr>
<th scope="row">SSH fingerprint</th>
- <td><code class="breakable">{{- .global.server_public_key_fingerprint -}}</code></td>
+ <td><code class="breakable">{{- .BaseData.Global.SSHFingerprint -}}</code></td>
</tr>
</tbody>
</table>