package daemon import ( "embed" "html/template" "log" "net/http" "os" "path/filepath" "sort" "strings" "simplegit/state" ) type indexHandler struct { store *state.LocalState reporoot string sshAddr string } type repoClone struct { Name string HTTPURL string SSHURL string } //go:embed index.html var indexFS embed.FS var indexTmpl = template.Must(template.ParseFS(indexFS, "index.html")) func (h *indexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { paths, err := h.listPublicRepos() if err != nil { log.Printf("index: list public repos: %v", err) } hasSSH := h.sshAddr != "" scheme := "http" if r.TLS != nil { scheme = "https" } if p := r.Header.Get("X-Forwarded-Proto"); p != "" { scheme = p } httpBase := scheme + "://" + r.Host repos := make([]repoClone, 0, len(paths)) for _, p := range paths { rc := repoClone{Name: p, HTTPURL: httpBase + "/" + p} if hasSSH { rc.SSHURL = "ssh://git@" + h.sshAddr + "/" + p } repos = append(repos, rc) } w.Header().Set("Content-Type", "text/html; charset=utf-8") _ = indexTmpl.Execute(w, struct { Repos []repoClone SSH bool }{Repos: repos, SSH: hasSSH}) } func (h *indexHandler) listPublicRepos() ([]string, error) { if eng := h.store.Engine(); eng != nil { var repos []struct { state.Repo `xorm:"extends"` NSName string `xorm:"ns_name"` } if err := eng.Table("repo").Select("repo.*, namespace.name AS ns_name"). Join("INNER", "namespace", "namespace.id = repo.namespace_id").Find(&repos); err != nil { return nil, err } out := make([]string, 0, len(repos)) for _, r := range repos { if r.IsPrivate { continue } out = append(out, r.NSName+"/"+r.Name+".git") } sort.Strings(out) return out, nil } return h.scanDiskRepos() } func (h *indexHandler) scanDiskRepos() ([]string, error) { var out []string err := filepath.WalkDir(h.reporoot, func(path string, d os.DirEntry, err error) error { if err != nil { return err } if !d.IsDir() || !strings.HasSuffix(d.Name(), ".git") { return nil } rel, err := filepath.Rel(h.reporoot, path) if err != nil { return err } out = append(out, filepath.ToSlash(rel)) return filepath.SkipDir }) sort.Strings(out) return out, err }