Hi… I am well aware that this diff view is very suboptimal. It will be fixed when the refactored server comes along!
Add missing copyright headers
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
package ansiec var ( Black = "\x1b[30m" Red = "\x1b[31m" Green = "\x1b[32m" Yellow = "\x1b[33m" Blue = "\x1b[34m" Magenta = "\x1b[35m" Cyan = "\x1b[36m" White = "\x1b[37m" ) var ( BrightBlack = "\x1b[30;1m" BrightRed = "\x1b[31;1m" BrightGreen = "\x1b[32;1m" BrightYellow = "\x1b[33;1m" BrightBlue = "\x1b[34;1m" BrightMagenta = "\x1b[35;1m" BrightCyan = "\x1b[36;1m" BrightWhite = "\x1b[37;1m" )
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
package ansiec var Reset = "\x1b[0m"
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
package ansiec var ( Bold = "\x1b[1m" Underline = "\x1b[4m" Reversed = "\x1b[7m" Italic = "\x1b[3m" )
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
package git2c
import (
"fmt"
"net"
"git.sr.ht/~sircmpwn/go-bare"
)
type Client struct {
SocketPath string
conn net.Conn
writer *bare.Writer
reader *bare.Reader
}
func NewClient(socketPath string) (*Client, error) {
conn, err := net.Dial("unix", socketPath)
if err != nil {
return nil, fmt.Errorf("git2d connection failed: %w", err)
}
writer := bare.NewWriter(conn)
reader := bare.NewReader(conn)
return &Client{
SocketPath: socketPath,
conn: conn,
writer: writer,
reader: reader,
}, nil
}
func (c *Client) Close() error {
if c.conn != nil {
return c.conn.Close()
}
return nil
}
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
package git2c
import (
"encoding/hex"
"errors"
"fmt"
"io"
)
func (c *Client) Cmd1(repoPath string) ([]Commit, *FilenameContents, error) {
if err := c.writer.WriteData([]byte(repoPath)); err != nil {
return nil, nil, fmt.Errorf("sending repo path failed: %w", err)
}
if err := c.writer.WriteUint(1); err != nil {
return nil, nil, fmt.Errorf("sending command failed: %w", err)
}
status, err := c.reader.ReadUint()
if err != nil {
return nil, nil, fmt.Errorf("reading status failed: %w", err)
}
if status != 0 {
return nil, nil, fmt.Errorf("git2d error: %d", status)
}
// README
readmeRaw, err := c.reader.ReadData()
if err != nil {
readmeRaw = nil
}
readmeFilename := "README.md" // TODO
readme := &FilenameContents{Filename: readmeFilename, Content: readmeRaw}
// Commits
var commits []Commit
for {
id, err := c.reader.ReadData()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return nil, nil, fmt.Errorf("reading commit ID failed: %w", err)
}
title, _ := c.reader.ReadData()
authorName, _ := c.reader.ReadData()
authorEmail, _ := c.reader.ReadData()
authorDate, _ := c.reader.ReadData()
commits = append(commits, Commit{
Hash: hex.EncodeToString(id),
Author: string(authorName),
Email: string(authorEmail),
Date: string(authorDate),
Message: string(title),
})
}
return commits, readme, nil
}
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
package git2c
import (
"errors"
"fmt"
"io"
)
func (c *Client) Cmd2(repoPath, pathSpec string) ([]TreeEntry, string, error) {
if err := c.writer.WriteData([]byte(repoPath)); err != nil {
return nil, "", fmt.Errorf("sending repo path failed: %w", err)
}
if err := c.writer.WriteUint(2); err != nil {
return nil, "", fmt.Errorf("sending command failed: %w", err)
}
if err := c.writer.WriteData([]byte(pathSpec)); err != nil {
return nil, "", fmt.Errorf("sending path failed: %w", err)
}
status, err := c.reader.ReadUint()
if err != nil {
return nil, "", fmt.Errorf("reading status failed: %w", err)
}
switch status {
case 0:
kind, err := c.reader.ReadUint()
if err != nil {
return nil, "", fmt.Errorf("reading object kind failed: %w", err)
}
switch kind {
case 1:
// Tree
count, err := c.reader.ReadUint()
if err != nil {
return nil, "", fmt.Errorf("reading entry count failed: %w", err)
}
var files []TreeEntry
for range count {
typeCode, err := c.reader.ReadUint()
if err != nil {
return nil, "", fmt.Errorf("error reading entry type: %w", err)
}
mode, err := c.reader.ReadUint()
if err != nil {
return nil, "", fmt.Errorf("error reading entry mode: %w", err)
}
size, err := c.reader.ReadUint()
if err != nil {
return nil, "", fmt.Errorf("error reading entry size: %w", err)
}
name, err := c.reader.ReadData()
if err != nil {
return nil, "", fmt.Errorf("error reading entry name: %w", err)
}
files = append(files, TreeEntry{
Name: string(name),
Mode: fmt.Sprintf("%06o", mode),
Size: size,
IsFile: typeCode == 2,
IsSubtree: typeCode == 1,
})
}
return files, "", nil
case 2:
// Blob
content, err := c.reader.ReadData()
if err != nil && !errors.Is(err, io.EOF) {
return nil, "", fmt.Errorf("error reading file content: %w", err)
}
return nil, string(content), nil
default:
return nil, "", fmt.Errorf("unknown kind: %d", kind)
}
case 3:
return nil, "", fmt.Errorf("path not found: %s", pathSpec)
default:
return nil, "", fmt.Errorf("unknown status code: %d", status)
}
}
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
package git2c
type Commit struct {
Hash string
Author string
Email string
Date string
Message string
}
type FilenameContents struct {
Filename string
Content []byte
}
type TreeEntry struct {
Name string
Mode string
Size uint64
IsFile bool
IsSubtree bool
}
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
package misc
import "strings"
func FirstOrPanic[T any](v T, err error) T {
if err != nil {
panic(err)
}
return v
}
// sliceContainsNewlines returns true if and only if the given slice contains
// one or more strings that contains newlines.
func SliceContainsNewlines(s []string) bool {
for _, v := range s {
if strings.Contains(v, "\n") {
return true
}
}
return false
}
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
package render
import (
"bytes"
"html/template"
chromaHTML "github.com/alecthomas/chroma/v2/formatters/html"
chromaLexers "github.com/alecthomas/chroma/v2/lexers"
chromaStyles "github.com/alecthomas/chroma/v2/styles"
)
func Highlight(filename, content string) template.HTML {
lexer := chromaLexers.Match(filename)
if lexer == nil {
lexer = chromaLexers.Fallback
}
iterator, err := lexer.Tokenise(nil, content)
if err != nil {
return template.HTML("<pre>Error tokenizing file: " + err.Error() + "</pre>") //#nosec G203`
}
var buf bytes.Buffer
style := chromaStyles.Get("autumn")
formatter := chromaHTML.New(
chromaHTML.WithClasses(true),
chromaHTML.TabWidth(8),
)
if err := formatter.Format(&buf, style, iterator); err != nil {
return template.HTML("<pre>Error formatting file: " + err.Error() + "</pre>") //#nosec G203
}
return template.HTML(buf.Bytes()) //#nosec G203
}
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
package render
import (
"html"
"html/template"
)
// EscapeHTML just escapes a string and wraps it in [template.HTML].
func EscapeHTML(s string) template.HTML {
return template.HTML(html.EscapeString(s)) //#nosec G203
}
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
package forge
import (
"errors"
"io/fs"
"log"
"log/slog"
"net"
"net/http"
"os"
"os/exec"
"syscall"
"time"
"github.com/jackc/pgx/v5/pgxpool"
"go.lindenii.runxiyu.org/lindenii-common/cmap"
goSSH "golang.org/x/crypto/ssh"
)
type Server struct {
Config Config
// Database serves as the primary Database handle for this entire application.
// Transactions or single reads may be used from it. A [pgxpool.Pool] is
// necessary to safely use pgx concurrently; pgx.Conn, etc. are insufficient.
Database *pgxpool.Pool
SourceHandler http.Handler
StaticHandler http.Handler
IrcSendBuffered chan string
IrcSendDirectChan chan errorBack[string]
// GlobalData is passed as "global" when rendering HTML templates.
GlobalData map[string]any
ServerPubkeyString string
ServerPubkeyFP string
ServerPubkey goSSH.PublicKey
// PackPasses contains hook cookies mapped to their packPass.
PackPasses cmap.Map[string, packPass]
}
func (s *Server) Setup() {
s.SourceHandler = http.StripPrefix(
"/-/source/",
http.FileServer(http.FS(embeddedSourceFS)),
)
staticFS, err := fs.Sub(embeddedResourcesFS, "static")
if err != nil {
panic(err)
}
s.StaticHandler = http.StripPrefix("/-/static/", http.FileServer(http.FS(staticFS)))
s.GlobalData = map[string]any{
"server_public_key_string": &s.ServerPubkeyString,
"server_public_key_fingerprint": &s.ServerPubkeyFP,
"forge_version": VERSION,
// Some other ones are populated after config parsing
}
}
func (s *Server) Run() {
if err := s.deployHooks(); err != nil {
slog.Error("deploying hooks", "error", err)
os.Exit(1)
}
if err := loadTemplates(); err != nil {
slog.Error("loading templates", "error", err)
os.Exit(1)
}
if err := s.deployGit2D(); err != nil {
slog.Error("deploying git2d", "error", err)
os.Exit(1)
}
// Launch Git2D
go func() {
cmd := exec.Command(s.Config.Git.DaemonPath, s.Config.Git.Socket) //#nosec G204
cmd.Stderr = log.Writer()
cmd.Stdout = log.Writer()
if err := cmd.Run(); err != nil {
panic(err)
}
}()
// UNIX socket listener for hooks
{
hooksListener, err := net.Listen("unix", s.Config.Hooks.Socket)
if errors.Is(err, syscall.EADDRINUSE) {
slog.Warn("removing existing socket", "path", s.Config.Hooks.Socket)
if err = syscall.Unlink(s.Config.Hooks.Socket); err != nil {
slog.Error("removing existing socket", "path", s.Config.Hooks.Socket, "error", err)
os.Exit(1)
}
if hooksListener, err = net.Listen("unix", s.Config.Hooks.Socket); err != nil {
slog.Error("listening hooks", "error", err)
os.Exit(1)
}
} else if err != nil {
slog.Error("listening hooks", "error", err)
os.Exit(1)
}
slog.Info("listening hooks on unix", "path", s.Config.Hooks.Socket)
go func() {
if err = s.serveGitHooks(hooksListener); err != nil {
slog.Error("serving hooks", "error", err)
os.Exit(1)
}
}()
}
// UNIX socket listener for LMTP
{
lmtpListener, err := net.Listen("unix", s.Config.LMTP.Socket)
if errors.Is(err, syscall.EADDRINUSE) {
slog.Warn("removing existing socket", "path", s.Config.LMTP.Socket)
if err = syscall.Unlink(s.Config.LMTP.Socket); err != nil {
slog.Error("removing existing socket", "path", s.Config.LMTP.Socket, "error", err)
os.Exit(1)
}
if lmtpListener, err = net.Listen("unix", s.Config.LMTP.Socket); err != nil {
slog.Error("listening LMTP", "error", err)
os.Exit(1)
}
} else if err != nil {
slog.Error("listening LMTP", "error", err)
os.Exit(1)
}
slog.Info("listening LMTP on unix", "path", s.Config.LMTP.Socket)
go func() {
if err = s.serveLMTP(lmtpListener); err != nil {
slog.Error("serving LMTP", "error", err)
os.Exit(1)
}
}()
}
// SSH listener
{
sshListener, err := net.Listen(s.Config.SSH.Net, s.Config.SSH.Addr)
if errors.Is(err, syscall.EADDRINUSE) && s.Config.SSH.Net == "unix" {
slog.Warn("removing existing socket", "path", s.Config.SSH.Addr)
if err = syscall.Unlink(s.Config.SSH.Addr); err != nil {
slog.Error("removing existing socket", "path", s.Config.SSH.Addr, "error", err)
os.Exit(1)
}
if sshListener, err = net.Listen(s.Config.SSH.Net, s.Config.SSH.Addr); err != nil {
slog.Error("listening SSH", "error", err)
os.Exit(1)
}
} else if err != nil {
slog.Error("listening SSH", "error", err)
os.Exit(1)
}
slog.Info("listening SSH on", "net", s.Config.SSH.Net, "addr", s.Config.SSH.Addr)
go func() {
if err = s.serveSSH(sshListener); err != nil {
slog.Error("serving SSH", "error", err)
os.Exit(1)
}
}()
}
// HTTP listener
{
httpListener, err := net.Listen(s.Config.HTTP.Net, s.Config.HTTP.Addr)
if errors.Is(err, syscall.EADDRINUSE) && s.Config.HTTP.Net == "unix" {
slog.Warn("removing existing socket", "path", s.Config.HTTP.Addr)
if err = syscall.Unlink(s.Config.HTTP.Addr); err != nil {
slog.Error("removing existing socket", "path", s.Config.HTTP.Addr, "error", err)
os.Exit(1)
}
if httpListener, err = net.Listen(s.Config.HTTP.Net, s.Config.HTTP.Addr); err != nil {
slog.Error("listening HTTP", "error", err)
os.Exit(1)
}
} else if err != nil {
slog.Error("listening HTTP", "error", err)
os.Exit(1)
}
server := http.Server{
Handler: s,
ReadTimeout: time.Duration(s.Config.HTTP.ReadTimeout) * time.Second,
WriteTimeout: time.Duration(s.Config.HTTP.ReadTimeout) * time.Second,
IdleTimeout: time.Duration(s.Config.HTTP.ReadTimeout) * time.Second,
} //exhaustruct:ignore
slog.Info("listening HTTP on", "net", s.Config.HTTP.Net, "addr", s.Config.HTTP.Addr)
go func() {
if err = server.Serve(httpListener); err != nil && !errors.Is(err, http.ErrServerClosed) {
slog.Error("serving HTTP", "error", err)
os.Exit(1)
}
}()
}
// IRC bot
go s.ircBotLoop()
select {}
}