Hi… I am well aware that this diff view is very suboptimal. It will be fixed when the refactored server comes along!
Refactoring
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 {
HTTP struct {
Net string `scfg:"net"`
Addr string `scfg:"addr"`
CookieExpiry int `scfg:"cookie_expiry"`
Root string `scfg:"root"`
ReadTimeout uint32 `scfg:"read_timeout"`
WriteTimeout uint32 `scfg:"write_timeout"`
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"`
WriteTimeout uint32 `scfg:"write_timeout"`
ReadTimeout uint32 `scfg:"read_timeout"`
} `scfg:"lmtp"`
Git struct {
RepoDir string `scfg:"repo_dir"`
Socket string `scfg:"socket"`
DaemonPath string `scfg:"daemon_path"`
} `scfg:"git"`
SSH struct {
Net string `scfg:"net"`
Addr string `scfg:"addr"`
Key string `scfg:"key"`
Root string `scfg:"root"`
} `scfg:"ssh"`
IRC irc.Config `scfg:"irc"`
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
}
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
// Package database provides stubs and wrappers for databases.
package database
import (
"context"
"github.com/jackc/pgx/v5/pgxpool"
)
// Database is a wrapper around pgxpool.Pool to provide a common interface for
// other packages in the forge.
type Database struct {
*pgxpool.Pool
}
// 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
}
type Config struct {
Type string `scfg:"type"`
Conn string `scfg:"conn"`
}
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 Config struct {
Socket string `scfg:"socket"`
Execs string `scfg:"execs"`
}
type hookinfo struct {
type hookInfo struct {
session ssh.Session pubkey string directAccess bool repoPath string userID int userType string repoID int groupPath []string 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")
}
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")
}
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
}
package server 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
}
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org> // The main entry point to the Lindenii Forge daemon. package main import (
"context"
"flag"
"go.lindenii.runxiyu.org/forge/forged/internal/unsorted"
"go.lindenii.runxiyu.org/forge/forged/internal/server"
)
func main() {
configPath := flag.String(
"config",
"/etc/lindenii/forge.scfg",
"path to configuration file",
)
flag.Parse()
s, err := unsorted.NewServer(*configPath)
s, err := server.New(context.Background(), *configPath)
if err != nil {
panic(err)
}
panic(s.Run())
}