Goit

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

AuthorJakob Wakeling <[email protected]>
Date2023-11-16 10:06:56
Commitb4de62691eccd59541652dc3c7b00c6da85e85b3
Parent35fbf5f73281709d10188c18c0ffe35025f8213f

Implement file downloading

Diffstat

M res/repo/tree.html | 2 +-
M src/main.go | 1 +
A src/repo/download.go | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
M src/repo/file.go | 2 ++
M src/repo/raw.go | 2 ++

5 files changed, 91 insertions, 1 deletions

diff --git a/res/repo/tree.html b/res/repo/tree.html
index 5bf72d1..77bfb3b 100644
--- a/res/repo/tree.html
+++ b/res/repo/tree.html
@@ -24,7 +24,7 @@
 									<a href="/{{$.Name}}/log/{{.RawPath}}">log</a>
 									blame
 									<a href="/{{$.Name}}/raw/{{.RawPath}}">raw</a>
-									download
+									<a href="/{{$.Name}}/download/{{.RawPath}}">download</a>
 								{{end}}
 							</td>
 						</tr>
diff --git a/src/main.go b/src/main.go
index d59bdeb..00cedf2 100644
--- a/src/main.go
+++ b/src/main.go
@@ -101,6 +101,7 @@ func main() {
 	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}/download/{path:.*}").Methods("GET").HandlerFunc(repo.HandleDownload)
 	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/src/repo/download.go b/src/repo/download.go
new file mode 100644
index 0000000..db538be
--- /dev/null
+++ b/src/repo/download.go
@@ -0,0 +1,85 @@
+package repo
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"log"
+	"net/http"
+	"path/filepath"
+
+	"github.com/Jamozed/Goit/src/goit"
+	"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 HandleDownload(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
+	}
+
+	path := 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, true))
+	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(path)
+	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 {
+		w.Header().Set("Content-Disposition", "attachement; filename="+filepath.Base(path))
+		w.Header().Set("Content-Length", fmt.Sprint(file.Size))
+
+		if _, err := io.Copy(w, rc); err != nil {
+			log.Println("[/repo/download]", err.Error())
+		}
+
+		rc.Close()
+	}
+}
diff --git a/src/repo/file.go b/src/repo/file.go
index da2651c..440ad1d 100644
--- a/src/repo/file.go
+++ b/src/repo/file.go
@@ -117,6 +117,8 @@ func HandleFile(w http.ResponseWriter, r *http.Request) {
 			data.Body = string(append(buf, buf2...))
 			data.Lines = strings.Split(data.Body, "\n")
 		}
+
+		rc.Close()
 	}
 
 	if err := goit.Tmpl.ExecuteTemplate(w, "repo/file", data); err != nil {
diff --git a/src/repo/raw.go b/src/repo/raw.go
index d0b5430..6eb55ac 100644
--- a/src/repo/raw.go
+++ b/src/repo/raw.go
@@ -83,5 +83,7 @@ func HandleRaw(w http.ResponseWriter, r *http.Request) {
 			goit.HttpError(w, http.StatusInternalServerError)
 			return
 		}
+
+		rc.Close()
 	}
 }