aboutsummaryrefslogtreecommitdiff
path: root/http_handle_repo_index.go
blob: 9dc3ea6f60fc093e8598fbfba07f6e3200232e58 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>

package main

import (
	"net/http"
	"strings"
	"time"

	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	"github.com/go-git/go-git/v5/plumbing/object"
	"github.com/go-git/go-git/v5/plumbing/storer"
)

// httpHandleRepoIndex provides the front page of a repo.
func httpHandleRepoIndex(writer http.ResponseWriter, _ *http.Request, params map[string]any) {
	var repo *git.Repository
	var repoName string
	var groupPath []string
	var refHash plumbing.Hash
	var refHashSlice []byte
	var err error
	var commitObj *object.Commit
	var tree *object.Tree
	var notes []string
	var branches []string
	var branchesIter storer.ReferenceIter
	var commits []commitDisplay

	repo, repoName, groupPath = params["repo"].(*git.Repository), params["repo_name"].(string), params["group_path"].([]string)

	if strings.Contains(repoName, "\n") || sliceContainsNewlines(groupPath) {
		notes = append(notes, "Path contains newlines; HTTP Git access impossible")
	}

	refHash, err = getRefHash(repo, params["ref_type"].(string), params["ref_name"].(string))
	if err != nil {
		goto no_ref
	}
	refHashSlice = refHash[:]

	branchesIter, err = repo.Branches()
	if err == nil {
		_ = branchesIter.ForEach(func(branch *plumbing.Reference) error {
			branches = append(branches, branch.Name().Short())
			return nil
		})
	}
	params["branches"] = branches

	if value, found := indexCommitsDisplayCache.Get(refHashSlice); found {
		if value != nil {
			commits = value
		} else {
			goto readme
		}
	} else {
		start := time.Now()
		commits, err = getRecentCommitsDisplay(repo, refHash, 5)
		if err != nil {
			commits = nil
		}
		cost := time.Since(start).Nanoseconds()
		indexCommitsDisplayCache.Set(refHashSlice, commits, cost)
		if err != nil {
			goto readme
		}
	}

	params["commits"] = commits

readme:

	if value, found := treeReadmeCache.Get(refHashSlice); found {
		params["files"] = value.DisplayTree
		params["readme_filename"] = value.ReadmeFilename
		params["readme"] = value.ReadmeRendered
	} else {
		start := time.Now()
		if commitObj, err = repo.CommitObject(refHash); err != nil {
			goto no_ref
		}

		if tree, err = commitObj.Tree(); err != nil {
			goto no_ref
		}
		displayTree := makeDisplayTree(tree)
		readmeFilename, readmeRendered := renderReadmeAtTree(tree)
		cost := time.Since(start).Nanoseconds()

		params["files"] = displayTree
		params["readme_filename"] = readmeFilename
		params["readme"] = readmeRendered

		entry := treeReadmeCacheEntry{
			DisplayTree:    displayTree,
			ReadmeFilename: readmeFilename,
			ReadmeRendered: readmeRendered,
		}
		treeReadmeCache.Set(refHashSlice, entry, cost)
	}

no_ref:

	params["http_clone_url"] = genHTTPRemoteURL(groupPath, repoName)
	params["ssh_clone_url"] = genSSHRemoteURL(groupPath, repoName)
	params["notes"] = notes

	renderTemplate(writer, "repo_index", params)
}