Goit

Simple and lightweight Git web server
git clone http://git.omkov.net/Goit
Log | Tree | Refs | README | Download

Goit/src/repo/blame.go (179 lines, 4.4 KiB) -rw-r--r-- file download

a0ac27c Jakob Wakeling 2024-07-06 23:32:05
0
// Copyright (C) 2023, Jakob Wakeling
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
1
// All rights reserved.
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
2
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
3
package repo
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
4
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
5
import (
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
6
	"errors"
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
7
	"fmt"
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
8
	"html/template"
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
9
	"net/http"
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
10
	"path"
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
11
	"strings"
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
12
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
13
	"github.com/Jamozed/Goit/src/goit"
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
14
	"github.com/Jamozed/Goit/src/util"
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
15
	"github.com/dustin/go-humanize"
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
16
	"github.com/go-chi/chi/v5"
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
17
	"github.com/go-git/go-git/v5"
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
18
	"github.com/go-git/go-git/v5/plumbing"
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
19
	"github.com/go-git/go-git/v5/plumbing/object"
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
20
)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
21
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
22
func HandleBlame(w http.ResponseWriter, r *http.Request) {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
23
	auth, user, err := goit.Auth(w, r, true)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
24
	if err != nil {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
25
		util.PrintFuncError(err)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
26
		goit.HttpError(w, http.StatusInternalServerError)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
27
		return
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
28
	}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
29
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
30
	tpath := chi.URLParam(r, "*")
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
31
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
32
	repo, err := goit.GetRepoByName(chi.URLParam(r, "repo"))
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
33
	if err != nil {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
34
		goit.HttpError(w, http.StatusInternalServerError)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
35
		return
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
36
	} else if repo == nil || !goit.IsVisible(repo, auth, user) {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
37
		goit.HttpError(w, http.StatusNotFound)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
38
		return
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
39
	}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
40
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
41
	type Bline struct {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
42
		Hash, ShortHash, Author, Date, Line string
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
43
		LineHTML                            template.HTML
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
44
	}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
45
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
46
	data := struct {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
47
		HeaderFields
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
48
		Title, Path, LineC, Size, Mode string
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
49
		Blines                         []Bline
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
50
		PathHTML                       template.HTML
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
51
	}{
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
52
		Title:        repo.Name + " - " + tpath,
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
53
		HeaderFields: GetHeaderFields(auth, user, repo, r.Host),
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
54
	}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
55
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
56
	gr, err := git.PlainOpen(goit.RepoPath(repo.Name, true))
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
57
	if err != nil {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
58
		util.PrintFuncError(err)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
59
		goit.HttpError(w, http.StatusInternalServerError)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
60
		return
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
61
	}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
62
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
63
	ref, err := gr.Head()
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
64
	if errors.Is(err, plumbing.ErrReferenceNotFound) {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
65
		goit.HttpError(w, http.StatusNotFound)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
66
		return
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
67
	} else if err != nil {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
68
		util.PrintFuncError(err)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
69
		goit.HttpError(w, http.StatusInternalServerError)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
70
		return
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
71
	}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
72
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
73
	if readme, _ := findPattern(gr, ref, readmePattern); readme != "" {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
74
		data.Readme = path.Join("/", repo.Name, "file", readme)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
75
	}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
76
	if licence, _ := findPattern(gr, ref, licencePattern); licence != "" {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
77
		data.Licence = path.Join("/", repo.Name, "file", licence)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
78
	}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
79
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
80
	commit, err := gr.CommitObject(ref.Hash())
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
81
	if err != nil {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
82
		util.PrintFuncError(err)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
83
		goit.HttpError(w, http.StatusInternalServerError)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
84
		return
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
85
	}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
86
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
87
	file, err := commit.File(tpath)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
88
	if errors.Is(err, object.ErrFileNotFound) {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
89
		goit.HttpError(w, http.StatusNotFound)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
90
		return
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
91
	} else if err != nil {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
92
		util.PrintFuncError(err)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
93
		goit.HttpError(w, http.StatusInternalServerError)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
94
		return
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
95
	}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
96
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
97
	data.Mode = util.ModeString(uint32(file.Mode))
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
98
	data.Path = file.Name
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
99
	data.Size = humanize.IBytes(uint64(file.Size))
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
100
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
101
	parts := strings.Split(file.Name, "/")
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
102
	htmlPath := "<b style=\"padding-left: 0.4rem;\"><a href=\"/" + repo.Name + "/tree\">" + repo.Name + "</a></b>/"
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
103
	dirPath := ""
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
104
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
105
	for i := 0; i < len(parts)-1; i += 1 {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
106
		dirPath = path.Join(dirPath, parts[i])
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
107
		htmlPath += "<a href=\"/" + repo.Name + "/tree/" + dirPath + "\">" + parts[i] + "</a>/"
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
108
	}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
109
	htmlPath += parts[len(parts)-1]
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
110
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
111
	data.PathHTML = template.HTML(htmlPath)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
112
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
113
	ftype, err := goit.GetFileType(file)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
114
	if err != nil {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
115
		util.PrintFuncError(err)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
116
		goit.HttpError(w, http.StatusInternalServerError)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
117
		return
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
118
	}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
119
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
120
	/* Only populate blines for text files */
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
121
	if strings.HasPrefix(ftype, "text") {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
122
		rc, err := file.Blob.Reader()
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
123
		if err != nil {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
124
			util.PrintFuncError(err)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
125
			goit.HttpError(w, http.StatusInternalServerError)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
126
			return
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
127
		}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
128
		defer rc.Close()
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
129
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
130
		buf := make([]byte, min(file.Size, (10*1024*1024)))
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
131
		if _, err := rc.Read(buf); err != nil {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
132
			util.PrintFuncError(err)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
133
			goit.HttpError(w, http.StatusInternalServerError)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
134
			return
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
135
		}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
136
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
137
		body, _, err := Highlight(file.Name, string(buf), true)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
138
		if err != nil {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
139
			util.PrintFuncError(err)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
140
			goit.HttpError(w, http.StatusInternalServerError)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
141
			return
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
142
		}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
143
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
144
		blame, err := git.Blame(commit, tpath)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
145
		if err != nil {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
146
			util.PrintFuncError(err)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
147
			goit.HttpError(w, http.StatusInternalServerError)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
148
			return
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
149
		}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
150
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
151
		htmlLines := strings.Split(body, "\n")
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
152
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
153
		for i, bline := range blame.Lines {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
154
			data.Blines = append(data.Blines, Bline{
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
155
				Hash:      bline.Hash.String(),
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
156
				ShortHash: bline.Hash.String()[:7],
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
157
				Author:    bline.AuthorName,
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
158
				Date:      bline.Date.Format("2006-01-02 15:04:05"),
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
159
				Line:      bline.Text,
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
160
			})
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
161
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
162
			if i < len(htmlLines) {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
163
				htmlLines[i] = strings.TrimPrefix(htmlLines[i], "</span></span>")
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
164
				htmlLines[i] += "</span></span>"
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
165
				data.Blines[i].LineHTML = template.HTML(htmlLines[i])
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
166
			}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
167
		}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
168
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
169
		data.Blines = append(data.Blines, Bline{})
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
170
	}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
171
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
172
	data.LineC = fmt.Sprint(len(data.Blines), " lines")
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
173
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
174
	if err := goit.Tmpl.ExecuteTemplate(w, "repo/blame", data); err != nil {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
175
		util.PrintFuncError(err)
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
176
	}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
177
}
178