diff options
author | Runxi Yu <me@runxiyu.org> | 2025-08-14 09:30:08 +0800 |
---|---|---|
committer | Runxi Yu <me@runxiyu.org> | 2025-08-14 09:30:08 +0800 |
commit | b48037f4dc602bd35b38c9cba0f50dd0f8033288 (patch) | |
tree | af254558fad42e989b7c4253ad0e9f585a357df9 /forged/internal | |
parent | Revamp placement of package docstrings (diff) | |
download | forge-refactor.tar.gz forge-refactor.tar.zst forge-refactor.zip |
Refactoringrefactor
Diffstat (limited to '')
-rw-r--r-- | forged/internal/config/config.go | 34 | ||||
-rw-r--r-- | forged/internal/database/database.go | 4 | ||||
-rw-r--r-- | forged/internal/hooki/hooki.go | 45 | ||||
-rw-r--r-- | forged/internal/lmtp/config.go | 53 | ||||
-rw-r--r-- | forged/internal/misc/usock.go | 23 | ||||
-rw-r--r-- | forged/internal/server/server.go | 48 |
6 files changed, 187 insertions, 20 deletions
diff --git a/forged/internal/config/config.go b/forged/internal/config/config.go index d3eb8e1..0166390 100644 --- a/forged/internal/config/config.go +++ b/forged/internal/config/config.go @@ -1,8 +1,14 @@ package config import ( + "bufio" + "log/slog" + "os" + "go.lindenii.runxiyu.org/forge/forged/internal/database" + "go.lindenii.runxiyu.org/forge/forged/internal/hooki" "go.lindenii.runxiyu.org/forge/forged/internal/irc" + "go.lindenii.runxiyu.org/forge/forged/internal/scfg" ) type Config struct { @@ -16,11 +22,8 @@ type Config struct { IdleTimeout uint32 `scfg:"idle_timeout"` ReverseProxy bool `scfg:"reverse_proxy"` } `scfg:"http"` - Hooks struct { - Socket string `scfg:"socket"` - Execs string `scfg:"execs"` - } `scfg:"hooks"` - LMTP struct { + Hooks hooki.Config `scfg:"hooks"` + LMTP struct { Socket string `scfg:"socket"` Domain string `scfg:"domain"` MaxSize int64 `scfg:"max_size"` @@ -42,9 +45,28 @@ type Config struct { General struct { Title string `scfg:"title"` } `scfg:"general"` - DB database.Config `scfg:"db"` + DB database.Config `scfg:"db"` Pprof struct { Net string `scfg:"net"` Addr string `scfg:"addr"` } `scfg:"pprof"` } + +func Open(path string) (config Config, err error) { + var configFile *os.File + + if configFile, err = os.Open(path); err != nil { + return config, err + } + defer configFile.Close() + + decoder := scfg.NewDecoder(bufio.NewReader(configFile)) + if err = decoder.Decode(&config); err != nil { + return config, err + } + for _, u := range decoder.UnknownDirectives() { + slog.Warn("unknown configuration directive", "directive", u) + } + + return config, err +} diff --git a/forged/internal/database/database.go b/forged/internal/database/database.go index 6674fe6..f353fe8 100644 --- a/forged/internal/database/database.go +++ b/forged/internal/database/database.go @@ -19,8 +19,8 @@ type Database struct { // Open opens a new database connection pool using the provided connection // string. It returns a Database instance and an error if any occurs. // It is run indefinitely in the background. -func Open(connString string) (Database, error) { - db, err := pgxpool.New(context.Background(), connString) +func Open(ctx context.Context, config Config) (Database, error) { + db, err := pgxpool.New(ctx, config.Conn) return Database{db}, err } diff --git a/forged/internal/hooki/hooki.go b/forged/internal/hooki/hooki.go index ae26846..8e75bae 100644 --- a/forged/internal/hooki/hooki.go +++ b/forged/internal/hooki/hooki.go @@ -1,13 +1,26 @@ package hooki import ( - "go.lindenii.runxiyu.org/forge/forged/internal/cmap" + "fmt" + "net" + "github.com/gliderlabs/ssh" + "go.lindenii.runxiyu.org/forge/forged/internal/cmap" + "go.lindenii.runxiyu.org/forge/forged/internal/misc" ) -type Pool cmap.Map[string, hookinfo] +type Pool struct { + hookMap cmap.Map[string, hookInfo] + socketPath string + executablesPath string +} -type hookinfo struct { +type Config struct { + Socket string `scfg:"socket"` + Execs string `scfg:"execs"` +} + +type hookInfo struct { session ssh.Session pubkey string directAccess bool @@ -19,3 +32,29 @@ type hookinfo struct { repoName string contribReq string } + +func New(config Config) (pool Pool) { + pool.socketPath = config.Socket + pool.executablesPath = config.Execs + return +} + +func (pool *Pool) Run() error { + listener, _, err := misc.ListenUnixSocket(pool.socketPath) + if err != nil { + return fmt.Errorf("listen unix socket for hooks: %w", err) + } + + for { + conn, err := listener.Accept() + if err != nil { + return fmt.Errorf("accept conn: %w", err) + } + + go pool.handleConn(conn) + } +} + +func (pool *Pool) handleConn(conn net.Conn) { + panic("TODO: handle hook connection") +} diff --git a/forged/internal/lmtp/config.go b/forged/internal/lmtp/config.go new file mode 100644 index 0000000..ce22740 --- /dev/null +++ b/forged/internal/lmtp/config.go @@ -0,0 +1,53 @@ +package lmtp + +import ( + "fmt" + "net" + + "go.lindenii.runxiyu.org/forge/forged/internal/misc" +) + +type Pool struct { + socket string + domain string + maxSize int64 + writeTimeout uint32 + readTimeout uint32 +} + +type Config struct { + Socket string `scfg:"socket"` + Domain string `scfg:"domain"` + MaxSize int64 `scfg:"max_size"` + WriteTimeout uint32 `scfg:"write_timeout"` + ReadTimeout uint32 `scfg:"read_timeout"` +} + +func New(config Config) (pool Pool) { + pool.socket = config.Socket + pool.domain = config.Domain + pool.maxSize = config.MaxSize + pool.writeTimeout = config.WriteTimeout + pool.readTimeout = config.ReadTimeout + return pool +} + +func (pool *Pool) Run() error { + listener, _, err := misc.ListenUnixSocket(pool.socket) + if err != nil { + return fmt.Errorf("listen unix socket for LMTP: %w", err) + } + + for { + conn, err := listener.Accept() + if err != nil { + return fmt.Errorf("accept conn: %w", err) + } + + go pool.handleConn(conn) + } +} + +func (pool *Pool) handleConn(conn net.Conn) { + panic("TODO: handle LMTP connection") +} diff --git a/forged/internal/misc/usock.go b/forged/internal/misc/usock.go new file mode 100644 index 0000000..357fa43 --- /dev/null +++ b/forged/internal/misc/usock.go @@ -0,0 +1,23 @@ +package misc + +import ( + "errors" + "fmt" + "net" + "syscall" +) + +func ListenUnixSocket(path string) (listener net.Listener, replaced bool, err error) { + listener, err = net.Listen("unix", path) + if errors.Is(err, syscall.EADDRINUSE) { + replaced = true + if unlinkErr := syscall.Unlink(path); unlinkErr != nil { + return listener, false, fmt.Errorf("remove existing socket %q: %w", path, unlinkErr) + } + listener, err = net.Listen("unix", path) + } + if err != nil { + return listener, replaced, fmt.Errorf("listen on unix socket %q: %w", path, err) + } + return listener, replaced, nil +} diff --git a/forged/internal/server/server.go b/forged/internal/server/server.go index ec875f1..03570c3 100644 --- a/forged/internal/server/server.go +++ b/forged/internal/server/server.go @@ -4,29 +4,59 @@ import ( "context" "fmt" "html/template" + "log" "go.lindenii.runxiyu.org/forge/forged/internal/config" "go.lindenii.runxiyu.org/forge/forged/internal/database" "go.lindenii.runxiyu.org/forge/forged/internal/hooki" + "go.lindenii.runxiyu.org/forge/forged/internal/lmtp" "go.lindenii.runxiyu.org/forge/forged/internal/store" ) type Server struct { config config.Config - database database.Database - stores *store.Set - hookis *hooki.Pool + database database.Database + stores *store.Set + hookPool hooki.Pool + lmtpPool lmtp.Pool templates *template.Template } -func New(ctx context.Context, config config.Config) (*Server, error) { - database, err := database.Open(ctx, config.DB) +func New(ctx context.Context, configPath string) (server *Server, err error) { + server = &Server{} + + server.config, err = config.Open(configPath) if err != nil { - return nil, fmt.Errorf("open database: %w", err) + return server, fmt.Errorf("open config: %w", err) } - return &Server{ - database: database, - }, nil + // TODO: Should this belong here, or in Run()? + server.database, err = database.Open(ctx, server.config.DB) + if err != nil { + return server, fmt.Errorf("open database: %w", err) + } + + return server, nil +} + +func (s *Server) Run() error { + // TODO: Not running git2d because it should be run separately. + // This needs to be documented somewhere, hence a TODO here for now. + + go func() { + s.hookPool = hooki.New(s.config.Hooks) + if err := s.hookPool.Run(); err != nil { + log.Fatalf("run hook pool: %v", err) + } + }() + + go func() { + s.lmtpPool = lmtp.New(s.config.LMTP) + if err := s.lmtpPool.Run(); err != nil { + log.Fatalf("run LMTP pool: %v", err) + } + }() + + return nil } |