caddy-git-server

Caddy module that provides a git server.

<!DOCTYPE html>
<html class="h-full bg-gray-100">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.tailwindcss.com"></script>

    <title>caddy-git-server - blob</title>
</head>
<body class="bg-gray-100">
    
    <!-- full-width container with bg -->
    <div class="bg-white shadow-sm">
        <!-- Header -->
        <div class="px-8 pt-2 mb-4 2xl:px-0 max-w-7xl mx-auto">
            <div class="flex flex-wrap justify-between items-center">
                <p class="text-2xl">caddy-git-server</p>
                <p class="font-mono text-xs bg-gray-200 px-2 hidden sm:block rounded-xl"><span class="">git clone </span><span class="font-bold">https://localhost/git/caddy-git-server.git</span></p>
            </div>
            
            <div class="flex flex-wrap justify-between items-center">
                <p class="font-xs text-gray-500">Provides a `git_server` caddy module for serving git repositories.</p>
                
                <select class="px-4">
                    <option>main</option>
                    <option>master</option>
                    <option>trunk</option>
                    <option>reallylongbranchname</option>
                </select>
            </div>
            
            <div class="flex flex-row flex-nowrap justify-start">
                <a href="tree.html"><div class="cursor-pointer px-2 pt-1 hover:border-b-2">Tree</div></a>
                <a href="log.html"><div class="cursor-pointer px-2 pt-1 hover:border-b-2">Log</div></a>
                <div class="cursor-pointer px-2 pt-1 hover:border-b-2">Diff</div>
                <div class="cursor-pointer px-2 pt-1 border-b-2 border-orange-400">Blob</div>
            </div>
        </div>
    </div>

    <!-- max-width content container -->
    <div class="max-w-7xl mx-auto">
        <div class="max-w-full bg-white shadow-sm rounded-sm">
            <p class="mx-4 px-2 py-2 border-b-2 font-medium text-lg">
                caddy_git_server.go
            </p>

            <div class="px-0 py-2 overflow-x-auto">
                <pre><code>package gitserver

import (
	"fmt"
	"io/fs"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/caddyserver/caddy/v2"
	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
	"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
	"github.com/caddyserver/caddy/v2/modules/caddyhttp"
	"github.com/caddyserver/caddy/v2/modules/caddyhttp/fileserver"
	"go.uber.org/zap"
)

func init() {
	caddy.RegisterModule(GitServer{})
	httpcaddyfile.RegisterHandlerDirective("git_server", parseCaddyfile)
}

type GitServer struct {
	// Git http protocol to use: 'dumb' or 'smart' or 'both' (default)
	// Note this doesn't actually do anything currently, only the dumb protocol is implemented.
	Protocol string `json:"protocol,omitempty"`

	// Path to directory containing bare git repos (<repo>.git)
	Root string `json:"root,omitempty"`
	// How far to search in root
	Depth int `json:"depth,omitempty"`

	// Enable repo browser
	Browse        bool   `json:"browse,omitempty"`
	TemplateDir   string `json:"template_dir,omitempty"`
	AssetDir      string `json:"asset_dir,omitempty"`
	ListedDefault bool   `json:"listed_default,omitemty"`

	// If IgnorePrefix is defined we strip it from the URL path
	IgnorePrefix string `json:"ignore_prefix,omitempty"`

	// Mirror a git repo
	// Mirror        bool `json:"mirror,omitempty"`
	// MirrorRemotes []string

	// File server module that serves static git files
	// FileServerRaw json.RawMessage        `json:"file_server,omitempty" caddy:"namespace=http.handlers inline_key=handler"`
	FileServer *fileserver.FileServer `json:"-"`

	// This is a list of relative paths to repositories in the root directory.
	// If set, the IgnorePrefix is stripped
	repositories             []string
	repositoriesLastModified time.Time

	logger *zap.Logger
}

// CaddyModule returns the Caddy module information.
func (GitServer) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:  "http.handlers.git_server",
		New: func() caddy.Module { return new(GitServer) },
	}
}

// Unmarshal caddyfile directive into a GitServer
func (gsrv *GitServer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
	// println("init:", d.Val())
	for d.Next() {
		// println("for:", d.Val(), strings.Join(d.RemainingArgs(), " "))

		// Check if we have optional "browse" on the end of 'git_server' directive
		args := d.RemainingArgs()
		switch len(args) {
		case 0:
		case 1:
			if args[0] != "browse" {
				return d.ArgErr()
			}
			gsrv.Browse = true
		default:
			return d.ArgErr()
		}

		// Loop over remaining options
		for d.NextBlock(0) {
			// println("next:", d.Val())
			switch d.Val() {
			case "protocol":
				if d.NextArg() {
					if d.Val() == "dumb" || d.Val() == "smart" || d.Val() == "both" {
						gsrv.Protocol = d.Val()
					} else {
						return d.ArgErr()
					}
				} else {
					return d.ArgErr()
				}
			case "root":
				if !d.AllArgs(&gsrv.Root) {
					return d.ArgErr()
				}
			case "depth":
				var depth_arg string
				if !d.AllArgs(&depth_arg) {
					return d.ArgErr()
				}
				depth, err := strconv.Atoi(depth_arg)
				if err != nil {
					return d.SyntaxErr("expected number")
				}
				gsrv.Depth = depth
			case "browse":
				gsrv.Browse = true
			case "template_dir":
				if !d.AllArgs(&gsrv.TemplateDir) {
					return d.ArgErr()
				}
				// case "mirror":
				// 	gsrv.Mirror = true
				// 	if d.NextArg() {
				// 		gsrv.MirrorRemotes = append(gsrv.MirrorRemotes, d.Val())
				// 	} else {
				// 		return d.ArgErr()
				// 	}
			case "asset_dir":
				if !d.AllArgs(&gsrv.AssetDir) {
					return d.ArgErr()
				}
				// case "ignore_prefix":
				// 	if !d.AllArgs(&gsrv.IgnorePrefix) {
				// 		return d.ArgErr()
				// 	}
			case "listed_default":
				var listed string
				if !d.AllArgs(&listed) {
					return d.ArgErr()
				}
				listedBool, err := strconv.ParseBool(listed)
				if err != nil {
					return d.SyntaxErr("expected boolean")
				}
				gsrv.ListedDefault = listedBool
			}
		}
	}
	return nil
}</code></pre>

                <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/default.min.css">
                <!-- <link rel="stylesheet" href="/assets/highlight/default.min.css"> -->
                <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
                 <!-- <script src="/assets/highlight/highlight.min.js"></script> -->
                <script src="//cdnjs.cloudflare.com/ajax/libs/highlightjs-line-numbers.js/2.9.0/highlightjs-line-numbers.min.js"></script>

                <!-- and it's easy to individually load additional languages -->
                <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/go.min.js"></script> -->

                <script>hljs.highlightAll(); hljs.initLineNumbersOnLoad()</script>
            </div>
        </div>
    </div>

    <!-- Footer -->
    <div class="mt-4 sticky top-[100vh]">
        <div class="px-3 py-2 text-md text-center">
            Served with
            <img src="cgs-icon.png" class="size-6 inline mx-1">
            <a href="https://git.mckinnon.ninja/caddy-git-server" class="font-medium underline">
                caddy-git-server
            </a>
        </div>
    </div>
</div>
</body>
</html>