Author | Jakob Wakeling <[email protected]> |
Date | 2023-11-20 10:18:08 |
Commit | 7dd44593815e8cb2019e0757ac1900a3da470755 |
Parent | 1c90d8bf4e04b11a27a31e77762870e7bbde7122 |
Implement repository and directory downloading
Diffstat
M | res/repo/file.html | | | 2 | +- |
M | res/repo/header.html | | | 1 | + |
M | res/repo/tree.html | | | 10 | ++++++---- |
M | src/main.go | | | 1 | + |
M | src/repo/download.go | | | 75 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- |
M | src/repo/log.go | | | 2 | +- |
6 files changed, 78 insertions, 13 deletions
diff --git a/res/repo/file.html b/res/repo/file.html index 271e647..14290c3 100644 --- a/res/repo/file.html +++ b/res/repo/file.html @@ -3,7 +3,7 @@ <body> <header> {{template "repo/header" .}}<hr> - {{.File}} ({{.Size}}) {{.Mode}} + {{.File}} ({{.Size}}) {{.Mode}} <a href="/{{.Name}}/download/{{.File}}">download</a> </header><hr> <main> <table> diff --git a/res/repo/header.html b/res/repo/header.html index 8391023..9f7e1df 100644 --- a/res/repo/header.html +++ b/res/repo/header.html @@ -24,6 +24,7 @@ {{if .Licence}} | <a href="{{.Licence}}">LICENCE</a> {{end}} + | <a href="/{{.Name}}/download">Download</a> {{if .Editable}} | <a href="/{{.Name}}/edit">Edit</a> {{end}} diff --git a/res/repo/tree.html b/res/repo/tree.html index c3a1a6e..5880f9c 100644 --- a/res/repo/tree.html +++ b/res/repo/tree.html @@ -20,10 +20,12 @@ <td><a href="/{{$.Name}}/{{.Path}}">{{.Name}}</a></td> <td align="right" {{if .B}}style="padding-right: calc(2ch + 0.4em);"{{end}}>{{.Size}}</td> <td> - <a href="/{{$.Name}}/log/{{.RawPath}}">log</a> - {{if .IsFile}} - blame - <a href="/{{$.Name}}/raw/{{.RawPath}}">raw</a> + {{if .RawPath}} + <a href="/{{$.Name}}/log/{{.RawPath}}">log</a> + {{if .IsFile}} + blame + <a href="/{{$.Name}}/raw/{{.RawPath}}">raw</a> + {{end}} <a href="/{{$.Name}}/download/{{.RawPath}}">download</a> {{end}} </td> diff --git a/src/main.go b/src/main.go index 00cedf2..fd36b89 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").Methods("GET").HandlerFunc(repo.HandleDownload) 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) diff --git a/src/repo/download.go b/src/repo/download.go index db538be..51d5548 100644 --- a/src/repo/download.go +++ b/src/repo/download.go @@ -1,14 +1,17 @@ package repo import ( + "archive/zip" "errors" "fmt" "io" "log" "net/http" "path/filepath" + "strings" "github.com/Jamozed/Goit/src/goit" + "github.com/Jamozed/Goit/src/util" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" @@ -18,7 +21,7 @@ import ( 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()) + log.Println("[repo/download]", err.Error()) goit.HttpError(w, http.StatusInternalServerError) return } @@ -36,7 +39,7 @@ func HandleDownload(w http.ResponseWriter, r *http.Request) { gr, err := git.PlainOpen(goit.RepoPath(repo.Name, true)) if err != nil { - log.Println("[/repo/file]", err.Error()) + log.Println("[/repo/download]", err.Error()) goit.HttpError(w, http.StatusInternalServerError) return } @@ -46,30 +49,88 @@ func HandleDownload(w http.ResponseWriter, r *http.Request) { goit.HttpError(w, http.StatusNotFound) return } else if err != nil { - log.Println("[/repo/file]", err.Error()) + log.Println("[/repo/download]", err.Error()) goit.HttpError(w, http.StatusInternalServerError) return } commit, err := gr.CommitObject(ref.Hash()) if err != nil { - log.Println("[/repo/file]", err.Error()) + log.Println("[/repo/download]", err.Error()) goit.HttpError(w, http.StatusInternalServerError) return } file, err := commit.File(path) if errors.Is(err, object.ErrFileNotFound) { - goit.HttpError(w, http.StatusNotFound) + /* Possibly a directory, search file tree for prefix */ + var files []string + var zSize uint64 + + iter, err := commit.Files() + if err != nil { + log.Println("[/repo/download]", err.Error()) + goit.HttpError(w, http.StatusInternalServerError) + return + } + + iter.ForEach(func(f *object.File) error { + if path == "" || strings.HasPrefix(f.Name, path+"/") { + files = append(files, f.Name) + zSize += uint64(f.Size) + } + + return nil + }) + + if len(files) == 0 { + goit.HttpError(w, http.StatusNotFound) + return + } + + /* Build and write ZIP of directory */ + w.Header().Set( + "Content-Disposition", "attachment; filename="+util.If(path == "", repo.Name, filepath.Base(path))+".zip", + ) + // w.Header().Set("Content-Length", fmt.Sprint(zSize)) + + z := zip.NewWriter(w) + for _, f := range files { + zh := zip.FileHeader{Name: f, Method: zip.Store} + + zf, err := z.CreateHeader(&zh) + if err != nil { + log.Println("[/repo/download]", err.Error()) + goit.HttpError(w, http.StatusInternalServerError) + } + + if file, err := commit.File(f); err != nil { + log.Println("[/repo/download]", err.Error()) + goit.HttpError(w, http.StatusInternalServerError) + return + } else if rc, err := file.Blob.Reader(); err != nil { + log.Println("[/repo/download]", err.Error()) + goit.HttpError(w, http.StatusInternalServerError) + return + } else { + if _, err := io.Copy(zf, rc); err != nil { + log.Println("[/repo/download]", err.Error()) + } + + rc.Close() + } + } + + z.Close() return } else if err != nil { - log.Println("[/repo/file]", err.Error()) + log.Println("[/repo/download]", err.Error()) goit.HttpError(w, http.StatusInternalServerError) return } if rc, err := file.Blob.Reader(); err != nil { - log.Println("[/repo/file]", err.Error()) + log.Println("[/repo/download]", err.Error()) goit.HttpError(w, http.StatusInternalServerError) return } else { diff --git a/src/repo/log.go b/src/repo/log.go index 1a7b47d..73192b3 100644 --- a/src/repo/log.go +++ b/src/repo/log.go @@ -92,7 +92,7 @@ func HandleLog(w http.ResponseWriter, r *http.Request) { log.Println("[/repo/log]", err.Error()) } else if path != "" { for _, s := range stats { - if strings.HasPrefix(s.Name, path) { + if s.Name == path || strings.HasPrefix(s.Name, path+"/") { files += 1 additions += s.Addition deletions += s.Deletion