all repos

mugit @ 1db697aa5fe1475e6047e8ab4b21673c8c95cd80

🐮 git server that your cow will love

mugit/internal/handlers/handlers.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
feat: compare refs (#9)..., 1 month ago
1
package handlers
2
3
import (
4
	"errors"
5
	"html/template"
6
	"net/http"
7
	"net/url"
8
	"path/filepath"
9
	"strings"
10
	"time"
11
12
	"olexsmir.xyz/mugit/internal/cache"
13
	"olexsmir.xyz/mugit/internal/config"
14
	"olexsmir.xyz/mugit/internal/git"
15
	"olexsmir.xyz/mugit/internal/humanize"
16
	"olexsmir.xyz/mugit/web"
17
)
18
19
type handlers struct {
20
	c *config.Config
21
	t *template.Template
22
23
	repoListCache cache.Cacher[[]repoList]
24
	readmeCache   cache.Cacher[template.HTML]
25
	diffCache     cache.Cacher[*git.NiceDiff]
26
}
27
28
func InitRoutes(cfg *config.Config) http.Handler {
29
	tmpls := template.Must(template.New("").
30
		Funcs(templateFuncs).
31
		ParseFS(web.TemplatesFS, "*"))
32
	h := handlers{
33
		cfg, tmpls,
34
		cache.NewInMemory[[]repoList](cfg.Cache.HomePage),
35
		cache.NewInMemory[template.HTML](cfg.Cache.Readme),
36
		cache.NewInMemory[*git.NiceDiff](cfg.Cache.Diff),
37
	}
38
39
	mux := http.NewServeMux()
40
	mux.HandleFunc("GET /", h.indexHandler)
41
	mux.HandleFunc("GET /index.xml", h.indexFeedHandler)
42
	mux.HandleFunc("GET /static/{file}", h.serveStaticHandler)
43
	mux.HandleFunc("GET /{name}/{$}", h.repoIndexHandler)
44
	mux.HandleFunc("GET /{name}/info/refs", h.infoRefsHandler)
45
	mux.HandleFunc("POST /{name}/git-upload-pack", h.uploadPackHandler)
46
	mux.HandleFunc("POST /{name}/git-receive-pack", h.receivePackHandler)
47
	mux.HandleFunc("GET /{name}/feed/{$}", h.repoFeedHandler)
48
	mux.HandleFunc("GET /{name}/tree/{ref}/{rest...}", h.repoTreeHandler)
49
	mux.HandleFunc("GET /{name}/blob/{ref}/{rest...}", h.fileContentsHandler)
50
	mux.HandleFunc("GET /{name}/raw/{ref}/{rest...}", h.rawFileContentsHandler)
51
	mux.HandleFunc("GET /{name}/log/{ref}", h.logHandler)
52
	mux.HandleFunc("GET /{name}/commit/{ref}", h.commitHandler)
53
	mux.HandleFunc("GET /{name}/compare/{ref1}/{ref2}", h.compareHandler)
54
	mux.HandleFunc("GET /{name}/refs/{$}", h.refsHandler)
55
	mux.HandleFunc("GET /{name}/archive/{ref}", h.archiveHandler)
56
57
	handler := h.recoverMiddleware(mux)
58
	return h.loggingMiddleware(handler)
59
}
60
61
func (h *handlers) serveStaticHandler(w http.ResponseWriter, r *http.Request) {
62
	f := filepath.Clean(r.PathValue("file"))
63
	http.ServeFileFS(w, r, web.StaticFS, f)
64
}
65
66
// parseRef parses url encoded ref name.
67
// If it fails it falls back to raw provided value.
68
func (h handlers) parseRef(name string) string {
69
	ref, err := url.PathUnescape(name)
70
	if err != nil {
71
		return name
72
	}
73
	return ref
74
}
75
76
var templateFuncs = template.FuncMap{
77
	"inc":             func(n int) int { return n + 1 },
78
	"inc64":           func(n int64) int64 { return n + 1 },
79
	"humanizeTime":    func(t time.Time) string { return t.Format("2006-01-02 15:04:05 MST") },
80
	"humanizeRelTime": humanize.Time,
81
	"urlencode":       url.PathEscape,
82
	"commitSummary":   commitSummary,
83
	"dict":            dict,
84
}
85
86
func commitSummary(commitMsg string) string {
87
	before, after, found := strings.Cut(commitMsg, "\n")
88
	first := strings.TrimSuffix(before, "\r")
89
	if !found {
90
		return first
91
	}
92
93
	// if there is any content after the first newline, indicate it with "..."
94
	after = strings.TrimLeft(after, "\r\n")
95
	if after != "" {
96
		return first + "..."
97
	}
98
99
	return first
100
}
101
102
func dict(v ...any) (map[string]any, error) {
103
	if len(v)%2 != 0 {
104
		return nil, errors.New("dict requires an even number of arguments")
105
	}
106
107
	out := make(map[string]any, len(v)/2)
108
	for i := 0; i < len(v); i += 2 {
109
		key, ok := v[i].(string)
110
		if !ok {
111
			return nil, errors.New("dict keys must be strings")
112
		}
113
		out[key] = v[i+1]
114
	}
115
	return out, nil
116
}