all repos — legit @ acac8d47d0dd4bab02274f750d22937044bee988

web frontend for git

routes: Add handler to generate tar gz file
Gabriel A. Giovanini mail@gabrielgio.me
Sun, 23 Jun 2024 15:20:47 +0200
commit

acac8d47d0dd4bab02274f750d22937044bee988

parent

86b2bf47ff930778bd73cce1cda916ffad41518b

3 files changed, 68 insertions(+), 0 deletions(-)

jump to
M routes/handler.goroutes/handler.go

@@ -39,6 +39,7 @@ mux.HandleFunc("POST /{name}", d.Multiplex)

mux.HandleFunc("GET /{name}/tree/{ref}/{rest...}", d.RepoTree) mux.HandleFunc("GET /{name}/blob/{ref}/{rest...}", d.FileContent) mux.HandleFunc("GET /{name}/log/{ref}", d.Log) + mux.HandleFunc("GET /{name}/archive/{file}", d.Archive) mux.HandleFunc("GET /{name}/commit/{ref}", d.Diff) mux.HandleFunc("GET /{name}/refs/{$}", d.Refs) mux.HandleFunc("GET /{name}/{rest...}", d.Multiplex)
M routes/routes.goroutes/routes.go

@@ -1,6 +1,7 @@

package routes import ( + "compress/gzip" "fmt" "html/template" "log"

@@ -9,6 +10,7 @@ "os"

"path/filepath" "sort" "strconv" + "strings" "time" "git.icyphox.sh/legit/config"

@@ -233,6 +235,57 @@ } else {

d.showFile(contents, data, w) } return +} + +func (d *deps) Archive(w http.ResponseWriter, r *http.Request) { + name := r.PathValue("name") + if d.isIgnored(name) { + d.Write404(w) + return + } + + file := r.PathValue("file") + + // TODO: extend this to add more files compression (e.g.: xz) + if !strings.HasSuffix(file, ".tar.gz") { + d.Write404(w) + return + } + + ref := strings.TrimSuffix(file, ".tar.gz") + + // This allows the browser to use a proper name for the file when + // downloading + filename := fmt.Sprintf("%s-%s.tar.gz", name, ref) + setContentDisposition(w, filename) + setGZipMIME(w) + + path := filepath.Join(d.c.Repo.ScanPath, name) + gr, err := git.Open(path, ref) + if err != nil { + d.Write404(w) + return + } + + gw := gzip.NewWriter(w) + defer gw.Close() + + prefix := fmt.Sprintf("%s-%s", name, ref) + err = gr.WriteTar(gw, prefix) + if err != nil { + // once we start writing to the body we can't report error anymore + // so we are only left with printing the error. + log.Println(err) + return + } + + err = gw.Flush() + if err != nil { + // once we start writing to the body we can't report error anymore + // so we are only left with printing the error. + log.Println(err) + return + } } func (d *deps) Log(w http.ResponseWriter, r *http.Request) {
M routes/util.goroutes/util.go

@@ -3,6 +3,7 @@

import ( "io/fs" "log" + "net/http" "os" "path/filepath" "strings"

@@ -88,3 +89,16 @@

func (d *deps) category(path string) string { return strings.TrimPrefix(filepath.Dir(strings.TrimPrefix(path, d.c.Repo.ScanPath)), string(os.PathSeparator)) } + +func setContentDisposition(w http.ResponseWriter, name string) { + h := "inline; filename=\"" + name + "\"" + w.Header().Add("Content-Disposition", h) +} + +func setGZipMIME(w http.ResponseWriter) { + setMIME(w, "application/gzip") +} + +func setMIME(w http.ResponseWriter, mime string) { + w.Header().Add("Content-Type", mime) +}