Author | Jakob Wakeling <[email protected]> |
Date | 2023-11-07 08:03:04 |
Commit | 08dff9acdd4ef4de7329c7c47778449035488d55 |
Parent | 945fd713f4c81b269b0bb9854f14e362a60aa2af |
Implement live backup functionality
Diffstat
M | .gitignore | | | 1 | + |
M | .vscode/launch.json | | | 3 | ++- |
A | .vscode/tasks.json | | | 11 | +++++++++++ |
M | src/goit/admin.go | | | 2 | +- |
M | src/goit/git.go | | | 4 | ++-- |
M | src/goit/goit.go | | | 166 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
M | src/goit/index.go | | | 2 | +- |
M | src/goit/repo.go | | | 20 | ++++++++++---------- |
M | src/goit/user.go | | | 14 | +++++++------- |
M | src/main.go | | | 108 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | src/repo/commit.go | | | 10 | +++++----- |
M | src/repo/edit.go | | | 8 | ++++---- |
M | src/repo/file.go | | | 8 | ++++---- |
M | src/repo/log.go | | | 8 | ++++---- |
M | src/repo/raw.go | | | 2 | +- |
M | src/repo/refs.go | | | 8 | ++++---- |
M | src/repo/tree.go | | | 15 | ++++++++------- |
17 files changed, 331 insertions, 59 deletions
diff --git a/.gitignore b/.gitignore index 964e84e..f43f734 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /bin/ !/.vscode/launch.json +!/.vscode/tasks.json diff --git a/.vscode/launch.json b/.vscode/launch.json index d743663..17d6e25 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,10 +5,11 @@ "name": "Debug", "type": "go", "request": "launch", - "program": "${workspaceFolder}/main.go", + "program": "${workspaceFolder}/src/main.go", "cwd": "${workspaceFolder}/bin", "mode": "debug", "args": ["--debug"], + "postDebugTask": "clean", }, ], } diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..99110fd --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,11 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "clean", + "type": "shell", + "command": "rm /run/user/$(id -u)/goit-8080.sock", + "presentation": { "reveal": "silent" } + }, + ], +} diff --git a/src/goit/admin.go b/src/goit/admin.go index 1dd9f85..9804e38 100644 --- a/src/goit/admin.go +++ b/src/goit/admin.go @@ -229,7 +229,7 @@ func HandleAdminRepos(w http.ResponseWriter, r *http.Request) { log.Println("[/admin/repos]", err.Error()) } - size, err := util.DirSize(RepoPath(d.Name)) + size, err := util.DirSize(RepoPath(d.Name, true)) if err != nil { log.Println("[/admin/repos]", err.Error()) } diff --git a/src/goit/git.go b/src/goit/git.go index 0d729d2..1c5197d 100644 --- a/src/goit/git.go +++ b/src/goit/git.go @@ -38,7 +38,7 @@ func HandleInfoRefs(w http.ResponseWriter, r *http.Request) { c := NewGitCommand(strings.TrimPrefix(service, "git-"), "--stateless-rpc", "--advertise-refs", ".") c.AddEnv(os.Environ()...) c.AddEnv("GIT_PROTOCOL=version=2") - c.Dir = RepoPath(repo.Name) + c.Dir = RepoPath(repo.Name, true) refs, _, err := c.Run(nil, nil) if err != nil { @@ -165,7 +165,7 @@ func gitHttpRpc(w http.ResponseWriter, r *http.Request, service string, repo *Re c := NewGitCommand(strings.TrimPrefix(service, "git-"), "--stateless-rpc", ".") c.AddEnv(os.Environ()...) - c.Dir = RepoPath(repo.Name) + c.Dir = RepoPath(repo.Name, true) if p := r.Header.Get("Git-Protocol"); p == "version=2" { c.AddEnv("GIT_PROTOCOL=version=2") diff --git a/src/goit/goit.go b/src/goit/goit.go index 1ea91b3..6ae4c24 100644 --- a/src/goit/goit.go +++ b/src/goit/goit.go @@ -5,15 +5,23 @@ package goit import ( + "archive/zip" "database/sql" "encoding/json" "errors" "fmt" + "io" + "io/fs" "log" "os" - "path" + "path/filepath" + "strings" + "time" + "github.com/Jamozed/Goit/src/util" "github.com/adrg/xdg" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/transport" _ "github.com/mattn/go-sqlite3" ) @@ -27,7 +35,7 @@ type Config struct { } var Conf = Config{ - DataPath: path.Join(xdg.DataHome, "goit"), + DataPath: filepath.Join(xdg.DataHome, "goit"), HttpAddr: "", HttpPort: "8080", GitPath: "git", @@ -56,13 +64,13 @@ func Goit(conf string) (err error) { return fmt.Errorf("[Config] %w", err) } - if dat, err := os.ReadFile(path.Join(Conf.DataPath, "favicon.png")); err != nil { + if dat, err := os.ReadFile(filepath.Join(Conf.DataPath, "favicon.png")); err != nil { log.Println("[Favicon]", err.Error()) } else { Favicon = dat } - if db, err = sql.Open("sqlite3", path.Join(Conf.DataPath, "goit.db")); err != nil { + if db, err = sql.Open("sqlite3", filepath.Join(Conf.DataPath, "goit.db")); err != nil { return fmt.Errorf("[Database] %w", err) } @@ -114,7 +122,7 @@ func Goit(conf string) (err error) { } func ConfPath() string { - if p, err := xdg.SearchConfigFile(path.Join("goit", "goit.json")); err != nil { + if p, err := xdg.SearchConfigFile(filepath.Join("goit", "goit.json")); err != nil { log.Println("[Config]", err.Error()) return "" } else { @@ -122,6 +130,150 @@ func ConfPath() string { } } -func RepoPath(name string) string { - return path.Join(Conf.DataPath, "repos", name+".git") +func RepoPath(name string, abs bool) string { + return util.If(abs, filepath.Join(Conf.DataPath, "repos", name+".git"), filepath.Join(name+".git")) +} + +func Backup() error { + data := struct { + Users []User `json:"users"` + Repos []Repo `json:"repos"` + }{} + + bdir := filepath.Join(Conf.DataPath, "backup") + if err := os.MkdirAll(bdir, 0o777); err != nil { + return err + } + + /* Dump users */ + rows, err := db.Query("SELECT id, name, name_full, pass, pass_algo, salt, is_admin FROM users") + if err != nil { + return err + } + + for rows.Next() { + u := User{} + if err := rows.Scan(&u.Id, &u.Name, &u.FullName, &u.Pass, &u.PassAlgo, &u.Salt, &u.IsAdmin); err != nil { + return err + } + + data.Users = append(data.Users, u) + } + rows.Close() + + /* Dump repositories */ + rows, err = db.Query("SELECT id, owner_id, name, description, is_private FROM repos") + if err != nil { + return err + } + + for rows.Next() { + r := Repo{} + if err := rows.Scan(&r.Id, &r.OwnerId, &r.Name, &r.Description, &r.IsPrivate); err != nil { + return err + } + + data.Repos = append(data.Repos, r) + } + rows.Close() + + /* Open an output ZIP file */ + ts := "goit_" + time.Now().UTC().Format("20060102T150405Z") + + zf, err := os.Create(filepath.Join(bdir, ts+".zip")) + if err != nil { + return err + } + defer zf.Close() + + zw := zip.NewWriter(zf) + defer zw.Close() + + /* Copy repositories to ZIP */ + td, err := os.MkdirTemp(os.TempDir(), "goit-") + if err != nil { + return err + } + defer os.RemoveAll(td) + + for _, r := range data.Repos { + cd := filepath.Join(td, RepoPath(r.Name, false)) + + gr, err := git.PlainClone(cd, true, &git.CloneOptions{ + URL: RepoPath(r.Name, true), Mirror: true, + }) + if err != nil { + if errors.Is(err, transport.ErrRepositoryNotFound) { + continue + } + + if errors.Is(err, transport.ErrEmptyRemoteRepository) { + continue + } + + return err + } + + if err := gr.DeleteRemote("origin"); err != nil { + return fmt.Errorf("%s %w", cd, err) + } + + /* Walk duplicated repository and add it to the ZIP */ + if err = filepath.WalkDir(cd, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + info, err := d.Info() + if err != nil { + return err + } + + head, err := zip.FileInfoHeader(info) + if err != nil { + return err + } + + head.Name = filepath.Join(ts, strings.TrimPrefix(path, Conf.DataPath)) + + if d.IsDir() { + head.Name += "/" + } else { + head.Method = zip.Store + } + + w, err := zw.CreateHeader(head) + if err != nil { + return err + } + + if !d.IsDir() { + fi, err := os.Open(path) + if err != nil { + return err + } + + if _, err := io.Copy(w, fi); err != nil { + return err + } + } + + return nil + }); err != nil { + return err + } + + os.RemoveAll(cd) + } + + /* Write database as JSON to ZIP */ + if b, err := json.MarshalIndent(data, "", "\t"); err != nil { + return err + } else if w, err := zw.Create(filepath.Join(ts, "goit.json")); err != nil { + return err + } else if _, err := w.Write(b); err != nil { + return err + } + + return nil } diff --git a/src/goit/index.go b/src/goit/index.go index e487e27..c7f7b1a 100644 --- a/src/goit/index.go +++ b/src/goit/index.go @@ -58,7 +58,7 @@ func HandleIndex(w http.ResponseWriter, r *http.Request) { } var lastCommit string - if gr, err := git.PlainOpen(RepoPath(repo.Name)); err != nil { + if gr, err := git.PlainOpen(RepoPath(repo.Name, true)); err != nil { log.Println("[/]", err.Error()) } else if ref, err := gr.Head(); err != nil { if !errors.Is(err, plumbing.ErrReferenceNotFound) { diff --git a/src/goit/repo.go b/src/goit/repo.go index 15457ff..32c95a0 100644 --- a/src/goit/repo.go +++ b/src/goit/repo.go @@ -15,11 +15,11 @@ import ( ) type Repo struct { - Id int64 - OwnerId int64 - Name string - Description string - IsPrivate bool + Id int64 `json:"id"` + OwnerId int64 `json:"owner_id"` + Name string `json:"name"` + Description string `json:"description"` + IsPrivate bool `json:"is_private"` } func GetRepo(rid int64) (*Repo, error) { @@ -69,13 +69,13 @@ func CreateRepo(repo Repo) error { return err } - if _, err := git.PlainInit(RepoPath(repo.Name), true); err != nil { + if _, err := git.PlainInit(RepoPath(repo.Name, true), true); err != nil { tx.Rollback() return err } if err := tx.Commit(); err != nil { - os.RemoveAll(RepoPath(repo.Name)) + os.RemoveAll(RepoPath(repo.Name, true)) return err } @@ -88,7 +88,7 @@ func DelRepo(rid int64) error { return err } - if err := os.RemoveAll(RepoPath(repo.Name)); err != nil { + if err := os.RemoveAll(RepoPath(repo.Name, true)); err != nil { return err } @@ -133,14 +133,14 @@ func UpdateRepo(rid int64, repo Repo) error { } if repo.Name != old.Name { - if err := os.Rename(RepoPath(old.Name), RepoPath(repo.Name)); err != nil { + if err := os.Rename(RepoPath(old.Name, true), RepoPath(repo.Name, true)); err != nil { tx.Rollback() return err } } if err := tx.Commit(); err != nil { - os.Rename(RepoPath(repo.Name), RepoPath(old.Name)) + os.Rename(RepoPath(repo.Name, true), RepoPath(old.Name, true)) log.Println("[repo/update]", "error while renaming, check repo \""+old.Name+"\"/\""+repo.Name+"\"") return err } diff --git a/src/goit/user.go b/src/goit/user.go index 9c280b4..cbf110c 100644 --- a/src/goit/user.go +++ b/src/goit/user.go @@ -13,13 +13,13 @@ import ( ) type User struct { - Id int64 - Name string - FullName string - Pass []byte - PassAlgo string - Salt []byte - IsAdmin bool + Id int64 `json:"id"` + Name string `json:"name"` + FullName string `json:"name_full"` + Pass []byte `json:"pass"` + PassAlgo string `json:"pass_algo"` + Salt []byte `json:"salt"` + IsAdmin bool `json:"is_admin"` } func HandleUserLogout(w http.ResponseWriter, r *http.Request) { diff --git a/src/main.go b/src/main.go index 75c4ac7..aa662d7 100644 --- a/src/main.go +++ b/src/main.go @@ -5,25 +5,73 @@ package main import ( + "errors" "flag" + "fmt" "log" + "net" "net/http" + "os" + "os/signal" + "path/filepath" "strings" + "sync" "time" "github.com/Jamozed/Goit/res" "github.com/Jamozed/Goit/src/goit" "github.com/Jamozed/Goit/src/repo" "github.com/Jamozed/Goit/src/user" + "github.com/Jamozed/Goit/src/util" + "github.com/adrg/xdg" "github.com/gorilla/mux" ) func main() { + var backup bool + + flag.BoolVar(&backup, "backup", false, "Perform a backup") flag.BoolVar(&goit.Debug, "debug", false, "Enable debug logging") flag.Parse() + if backup /* IPC client */ { + c, err := net.Dial("unix", filepath.Join(xdg.RuntimeDir, "goit-"+goit.Conf.HttpPort+".sock")) + if err != nil { + log.Fatalln(err.Error()) + } + + _, err = c.Write([]byte{0xBA}) + if err != nil { + log.Fatalln(err.Error()) + } + + buf := make([]byte, 512) + n, err := c.Read(buf) + if err != nil { + log.Fatalln(err.Error()) + } + + fmt.Println(string(buf[1:n])) + c.Close() + + os.Exit(util.If(buf[0] == 0x01, -1, 0)) + } + log.Println("Starting Goit", res.Version) + /* Listen for and handle SIGINT */ + stop := make(chan struct{}) + wait := &sync.WaitGroup{} + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + + go func() { + <-c + close(stop) + wait.Wait() + os.Exit(0) + }() + if err := goit.Goit(goit.ConfPath()); err != nil { log.Fatalln(err.Error()) } @@ -71,8 +119,23 @@ func main() { } }() + /* Listen for IPC */ + ipc, err := net.Listen("unix", filepath.Join(xdg.RuntimeDir, "goit-"+goit.Conf.HttpPort+".sock")) + if err != nil { + log.Fatalln("[sock]", err.Error()) + } + + go func() { + defer ipc.Close() + <-stop + }() + + wait.Add(1) + go handleIpc(stop, wait, ipc) + + /* Listen for HTTP on the specified port */ if err := http.ListenAndServe(goit.Conf.HttpAddr+":"+goit.Conf.HttpPort, logHttp(h)); err != nil { - log.Fatalln("[HTTP]", err) + log.Fatalln("[HTTP]", err.Error()) } } @@ -105,3 +168,46 @@ func handleFavicon(w http.ResponseWriter, r *http.Request) { func redirectDotGit(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, strings.TrimSuffix(r.URL.Path, ".git"), http.StatusMovedPermanently) } + +/* Handle IPC messages. */ +func handleIpc(stop chan struct{}, wait *sync.WaitGroup, ipc net.Listener) { + defer wait.Done() + + for { + select { + case <-stop: + return + default: + c, err := ipc.Accept() + if err != nil { + if !errors.Is(err, net.ErrClosed) { + log.Println("[ipc]", err.Error()) + } + continue + } + + c.SetReadDeadline(time.Now().Add(1 * time.Second)) + + buf := make([]byte, 1) + if _, err := c.Read(buf); err != nil { + log.Println("[ipc]", err.Error()) + continue + } + + if buf[0] == 0xBA { + log.Println("[backup] Starting") + if err := goit.Backup(); err != nil { + c.Write(append([]byte{0x01}, []byte(err.Error())...)) + log.Println("[backup]", err.Error()) + } else { + c.Write(append([]byte{0x00}, []byte("SUCCESS")...)) + log.Println("[backup] Success") + } + } else { + c.Write(append([]byte{0x01}, []byte("ILLEGAL")...)) + } + + c.Close() + } + } +} diff --git a/src/repo/commit.go b/src/repo/commit.go index 914ca3c..3ce03b5 100644 --- a/src/repo/commit.go +++ b/src/repo/commit.go @@ -6,7 +6,7 @@ import ( "html/template" "log" "net/http" - "path" + "path/filepath" "strconv" "strings" "time" @@ -52,7 +52,7 @@ func HandleCommit(w http.ResponseWriter, r *http.Request) { Editable: (auth && repo.OwnerId == uid), } - gr, err := git.PlainOpen(goit.RepoPath(repo.Name)) + gr, err := git.PlainOpen(goit.RepoPath(repo.Name, true)) if err != nil { log.Println("[/repo/commit]", err.Error()) goit.HttpError(w, http.StatusInternalServerError) @@ -68,10 +68,10 @@ func HandleCommit(w http.ResponseWriter, r *http.Request) { } } else { if readme, _ := findReadme(gr, ref); readme != "" { - data.Readme = path.Join("/", repo.Name, "file", readme) + data.Readme = filepath.Join("/", repo.Name, "file", readme) } if licence, _ := findLicence(gr, ref); licence != "" { - data.Licence = path.Join("/", repo.Name, "file", licence) + data.Licence = filepath.Join("/", repo.Name, "file", licence) } } @@ -140,7 +140,7 @@ func HandleCommit(w http.ResponseWriter, r *http.Request) { } c := goit.NewGitCommand("diff", "--color=always", "-p", phash, commit.Hash.String()) - c.Dir = goit.RepoPath(repo.Name) + c.Dir = goit.RepoPath(repo.Name, true) out, _, err := c.Run(nil, nil) if err != nil { log.Println("[/repo/commit]", err.Error()) diff --git a/src/repo/edit.go b/src/repo/edit.go index dd92c3b..2afa940 100644 --- a/src/repo/edit.go +++ b/src/repo/edit.go @@ -5,7 +5,7 @@ import ( "fmt" "log" "net/http" - "path" + "path/filepath" "slices" "github.com/Jamozed/Goit/src/goit" @@ -66,7 +66,7 @@ func HandleEdit(w http.ResponseWriter, r *http.Request) { data.Edit.Description = repo.Description data.Edit.IsPrivate = repo.IsPrivate - gr, err := git.PlainOpen(goit.RepoPath(repo.Name)) + gr, err := git.PlainOpen(goit.RepoPath(repo.Name, true)) if err != nil { log.Println("[/repo/edit]", err.Error()) goit.HttpError(w, http.StatusInternalServerError) @@ -82,10 +82,10 @@ func HandleEdit(w http.ResponseWriter, r *http.Request) { if ref != nil { if readme, _ := findReadme(gr, ref); readme != "" { - data.Readme = path.Join("/", repo.Name, "file", readme) + data.Readme = filepath.Join("/", repo.Name, "file", readme) } if licence, _ := findLicence(gr, ref); licence != "" { - data.Licence = path.Join("/", repo.Name, "file", licence) + data.Licence = filepath.Join("/", repo.Name, "file", licence) } } diff --git a/src/repo/file.go b/src/repo/file.go index 18898b1..334a7a7 100644 --- a/src/repo/file.go +++ b/src/repo/file.go @@ -5,7 +5,7 @@ import ( "io" "log" "net/http" - "path" + "path/filepath" "strings" "github.com/Jamozed/Goit/src/goit" @@ -47,7 +47,7 @@ func HandleFile(w http.ResponseWriter, r *http.Request) { Editable: (auth && repo.OwnerId == uid), } - gr, err := git.PlainOpen(goit.RepoPath(repo.Name)) + gr, err := git.PlainOpen(goit.RepoPath(repo.Name, true)) if err != nil { log.Println("[/repo/file]", err.Error()) goit.HttpError(w, http.StatusInternalServerError) @@ -65,10 +65,10 @@ func HandleFile(w http.ResponseWriter, r *http.Request) { } if readme, _ := findReadme(gr, ref); readme != "" { - data.Readme = path.Join("/", repo.Name, "file", readme) + data.Readme = filepath.Join("/", repo.Name, "file", readme) } if licence, _ := findLicence(gr, ref); licence != "" { - data.Licence = path.Join("/", repo.Name, "file", licence) + data.Licence = filepath.Join("/", repo.Name, "file", licence) } commit, err := gr.CommitObject(ref.Hash()) diff --git a/src/repo/log.go b/src/repo/log.go index 001441e..39a5932 100644 --- a/src/repo/log.go +++ b/src/repo/log.go @@ -5,7 +5,7 @@ import ( "fmt" "log" "net/http" - "path" + "path/filepath" "strings" "time" @@ -51,7 +51,7 @@ func HandleLog(w http.ResponseWriter, r *http.Request) { Editable: (auth && repo.OwnerId == uid), } - gr, err := git.PlainOpen(goit.RepoPath(repo.Name)) + gr, err := git.PlainOpen(goit.RepoPath(repo.Name, true)) if err != nil { log.Println("[/repo/log]", err.Error()) goit.HttpError(w, http.StatusInternalServerError) @@ -68,10 +68,10 @@ func HandleLog(w http.ResponseWriter, r *http.Request) { } if readme, _ := findReadme(gr, ref); readme != "" { - data.Readme = path.Join("/", repo.Name, "file", readme) + data.Readme = filepath.Join("/", repo.Name, "file", readme) } if licence, _ := findLicence(gr, ref); licence != "" { - data.Licence = path.Join("/", repo.Name, "file", licence) + data.Licence = filepath.Join("/", repo.Name, "file", licence) } if iter, err := gr.Log(&git.LogOptions{From: ref.Hash()}); err != nil { diff --git a/src/repo/raw.go b/src/repo/raw.go index ca189a2..d0b5430 100644 --- a/src/repo/raw.go +++ b/src/repo/raw.go @@ -32,7 +32,7 @@ func HandleRaw(w http.ResponseWriter, r *http.Request) { return } - gr, err := git.PlainOpen(goit.RepoPath(repo.Name)) + gr, err := git.PlainOpen(goit.RepoPath(repo.Name, true)) if err != nil { log.Println("[/repo/file]", err.Error()) goit.HttpError(w, http.StatusInternalServerError) diff --git a/src/repo/refs.go b/src/repo/refs.go index f5416d0..a42e938 100644 --- a/src/repo/refs.go +++ b/src/repo/refs.go @@ -4,7 +4,7 @@ import ( "errors" "log" "net/http" - "path" + "path/filepath" "strings" "time" @@ -39,7 +39,7 @@ func HandleRefs(w http.ResponseWriter, r *http.Request) { Editable: (auth && repo.OwnerId == uid), } - gr, err := git.PlainOpen(goit.RepoPath(repo.Name)) + gr, err := git.PlainOpen(goit.RepoPath(repo.Name, true)) if err != nil { log.Println("[/repo/refs]", err.Error()) goit.HttpError(w, http.StatusInternalServerError) @@ -55,10 +55,10 @@ func HandleRefs(w http.ResponseWriter, r *http.Request) { } } else { if readme, _ := findReadme(gr, ref); readme != "" { - data.Readme = path.Join("/", repo.Name, "file", readme) + data.Readme = filepath.Join("/", repo.Name, "file", readme) } if licence, _ := findLicence(gr, ref); licence != "" { - data.Licence = path.Join("/", repo.Name, "file", licence) + data.Licence = filepath.Join("/", repo.Name, "file", licence) } } diff --git a/src/repo/tree.go b/src/repo/tree.go index 06a5c98..ee6e003 100644 --- a/src/repo/tree.go +++ b/src/repo/tree.go @@ -5,6 +5,7 @@ import ( "log" "net/http" "path" + "path/filepath" "sort" "strings" @@ -45,7 +46,7 @@ func HandleTree(w http.ResponseWriter, r *http.Request) { Editable: (auth && repo.OwnerId == uid), } - gr, err := git.PlainOpen(goit.RepoPath(repo.Name)) + gr, err := git.PlainOpen(goit.RepoPath(repo.Name, true)) if err != nil { log.Println("[/repo/tree]", err.Error()) goit.HttpError(w, http.StatusInternalServerError) @@ -60,10 +61,10 @@ func HandleTree(w http.ResponseWriter, r *http.Request) { } } else { if readme, _ := findReadme(gr, ref); readme != "" { - data.Readme = path.Join("/", repo.Name, "file", readme) + data.Readme = filepath.Join("/", repo.Name, "file", readme) } if licence, _ := findLicence(gr, ref); licence != "" { - data.Licence = path.Join("/", repo.Name, "file", licence) + data.Licence = filepath.Join("/", repo.Name, "file", licence) } commit, err := gr.CommitObject(ref.Hash()) @@ -82,7 +83,7 @@ func HandleTree(w http.ResponseWriter, r *http.Request) { if treepath != "" { data.Files = append(data.Files, row{ - Mode: "d---------", Name: "..", Path: path.Join("tree", path.Dir(treepath)), + Mode: "d---------", Name: "..", Path: filepath.Join("tree", path.Dir(treepath)), }) tree, err = tree.Tree(treepath) @@ -115,8 +116,8 @@ func HandleTree(w http.ResponseWriter, r *http.Request) { return } - fpath = path.Join("file", treepath, v.Name) - rpath = path.Join(treepath, v.Name) + fpath = filepath.Join("file", treepath, v.Name) + rpath = filepath.Join(treepath, v.Name) size = humanize.IBytes(uint64(file.Size)) } else { var dirSize uint64 @@ -137,7 +138,7 @@ func HandleTree(w http.ResponseWriter, r *http.Request) { return } - fpath = path.Join("tree", treepath, v.Name) + fpath = filepath.Join("tree", treepath, v.Name) size = humanize.IBytes(dirSize) }