Goit

Simple and lightweight Git web server
Mirror of https://github.com/Jamozed/Goit
git clone http://git.omkov.net/Goit
Log | Tree | Refs | README | Download

AuthorJakob Wakeling <[email protected]>
Date2023-11-03 10:00:26
Commitd314664e5432e372cc8908e277f84fc373799af6
Parent7974d70ab064a04f872f70f1bc94555ca333517b

Implement raw file viewing

Diffstat

M main.go | 1 +
M res/repo/tree.html | 8 +++++++-
A src/repo/raw.go | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
M src/repo/tree.go | 9 +++++----

4 files changed, 100 insertions, 5 deletions

diff --git a/main.go b/main.go
index 61921ad..57acc0c 100644
--- a/main.go
+++ b/main.go
@@ -51,6 +51,7 @@ func main() {
 	h.Path("/{repo}/tree").Methods("GET").HandlerFunc(repo.HandleTree)
 	h.Path("/{repo}/tree/{path:.*}").Methods("GET").HandlerFunc(repo.HandleTree)
 	h.Path("/{repo}/file/{path:.*}").Methods("GET").HandlerFunc(repo.HandleFile)
+	h.Path("/{repo}/raw/{path:.*}").Methods("GET").HandlerFunc(repo.HandleRaw)
 	h.Path("/{repo}/refs").Methods("GET").HandlerFunc(repo.HandleRefs)
 	h.Path("/{repo}/edit").Methods("GET", "POST").HandlerFunc(repo.HandleEdit)
 	h.Path("/{repo}/info/refs").Methods("GET").HandlerFunc(goit.HandleInfoRefs)
diff --git a/res/repo/tree.html b/res/repo/tree.html
index f3c8eae..8c2d7d7 100644
--- a/res/repo/tree.html
+++ b/res/repo/tree.html
@@ -19,7 +19,13 @@
 							<td>{{.Mode}}</td>
 							<td><a href="/{{$.Name}}/{{.Path}}">{{.Name}}</a></td>
 							<td align="right" {{if .B}}style="padding-right: calc(2ch + 0.4em);"{{end}}>{{.Size}}</td>
-							<td>log blame raw download</td>
+							<td>
+								{{if .RawPath}}
+									log blame
+									<a href="/{{$.Name}}/raw/{{.RawPath}}">raw</a>
+									download
+								{{end}}
+							</td>
 						</tr>
 					{{end}}
 				{{else}}
diff --git a/src/repo/raw.go b/src/repo/raw.go
new file mode 100644
index 0000000..5a16a31
--- /dev/null
+++ b/src/repo/raw.go
@@ -0,0 +1,87 @@
+package repo
+
+import (
+	"errors"
+	"io"
+	"log"
+	"net/http"
+
+	goit "github.com/Jamozed/Goit/src"
+	"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/gorilla/mux"
+)
+
+func HandleRaw(w http.ResponseWriter, r *http.Request) {
+	auth, user, err := goit.Auth(w, r, true)
+	if err != nil {
+		log.Println("[repo/raw]", err.Error())
+		goit.HttpError(w, http.StatusInternalServerError)
+		return
+	}
+
+	treepath := mux.Vars(r)["path"]
+
+	repo, err := goit.GetRepoByName(mux.Vars(r)["repo"])
+	if err != nil {
+		goit.HttpError(w, http.StatusInternalServerError)
+		return
+	} else if repo == nil || (repo.IsPrivate && (!auth || user.Id != repo.OwnerId)) {
+		goit.HttpError(w, http.StatusNotFound)
+		return
+	}
+
+	gr, err := git.PlainOpen(goit.RepoPath(repo.Name))
+	if err != nil {
+		log.Println("[/repo/file]", err.Error())
+		goit.HttpError(w, http.StatusInternalServerError)
+		return
+	}
+
+	ref, err := gr.Head()
+	if errors.Is(err, plumbing.ErrReferenceNotFound) {
+		goit.HttpError(w, http.StatusNotFound)
+		return
+	} else if err != nil {
+		log.Println("[/repo/file]", err.Error())
+		goit.HttpError(w, http.StatusInternalServerError)
+		return
+	}
+
+	commit, err := gr.CommitObject(ref.Hash())
+	if err != nil {
+		log.Println("[/repo/file]", err.Error())
+		goit.HttpError(w, http.StatusInternalServerError)
+		return
+	}
+
+	file, err := commit.File(treepath)
+	if errors.Is(err, object.ErrFileNotFound) {
+		goit.HttpError(w, http.StatusNotFound)
+		return
+	} else if err != nil {
+		log.Println("[/repo/file]", err.Error())
+		goit.HttpError(w, http.StatusInternalServerError)
+		return
+	}
+
+	if rc, err := file.Blob.Reader(); err != nil {
+		log.Println("[/repo/file]", err.Error())
+		goit.HttpError(w, http.StatusInternalServerError)
+		return
+	} else {
+		buf := make([]byte, min(file.Size, (10*1024*1024)))
+		if _, err := rc.Read(buf); err != nil && !errors.Is(err, io.EOF) {
+			log.Println("[/repo/file]", err.Error())
+			goit.HttpError(w, http.StatusInternalServerError)
+			return
+		}
+
+		if _, err := w.Write(buf); err != nil {
+			log.Println("[/repo/file]", err.Error())
+			goit.HttpError(w, http.StatusInternalServerError)
+			return
+		}
+	}
+}
diff --git a/src/repo/tree.go b/src/repo/tree.go
index 7fd9c27..9c78912 100644
--- a/src/repo/tree.go
+++ b/src/repo/tree.go
@@ -31,8 +31,8 @@ func HandleTree(w http.ResponseWriter, r *http.Request) {
 	}
 
 	type row struct {
-		Mode, Name, Path, Size string
-		B                      bool
+		Mode, Name, Path, RawPath, Size string
+		B                               bool
 	}
 	data := struct {
 		Title, Name, Description, Url string
@@ -105,7 +105,7 @@ func HandleTree(w http.ResponseWriter, r *http.Request) {
 		})
 
 		for _, v := range tree.Entries {
-			var fpath, size string
+			var fpath, rpath, size string
 
 			if v.Mode&0o40000 == 0 {
 				file, err := tree.File(v.Name)
@@ -116,6 +116,7 @@ func HandleTree(w http.ResponseWriter, r *http.Request) {
 				}
 
 				fpath = path.Join("file", treepath, v.Name)
+				rpath = path.Join(treepath, v.Name)
 				size = humanize.IBytes(uint64(file.Size))
 			} else {
 				var dirSize uint64
@@ -141,7 +142,7 @@ func HandleTree(w http.ResponseWriter, r *http.Request) {
 			}
 
 			data.Files = append(data.Files, row{
-				Mode: util.ModeString(uint32(v.Mode)), Name: v.Name, Path: fpath, Size: size,
+				Mode: util.ModeString(uint32(v.Mode)), Name: v.Name, Path: fpath, RawPath: rpath, Size: size,
 				B: util.If(strings.HasSuffix(size, " B"), true, false),
 			})
 		}