Lindenii Project Forge
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 {} }