From 6e6a97ba2be600afcb8bc833fda1f7766c87c0d7 Mon Sep 17 00:00:00 2001 From: Anirudh Oppiliappan Date: Wed, 29 Jan 2025 09:05:05 +0200 Subject: [PATCH] routes: resolve did to handle in middleware So handles are now purely cosmetic and are resolved to DIDs in the middleware. auth.ResolveIdent seems to add a lot of latency, so we cache the resolved ident for 24 hours. It's still not as perfomant but we can come back to this later. --- routes/git.go | 4 +-- routes/handler.go | 4 ++- routes/middleware/did.go | 61 ++++++++++++++++++++++++++++++++++++++++ routes/routes.go | 61 ++++++++++++++++------------------------ routes/util.go | 23 ++++++++++++--- 5 files changed, 110 insertions(+), 43 deletions(-) create mode 100644 routes/middleware/did.go diff --git a/routes/git.go b/routes/git.go index b6e781b..93cfc3e 100644 --- a/routes/git.go +++ b/routes/git.go @@ -11,7 +11,7 @@ import ( ) func (d *Handle) InfoRefs(w http.ResponseWriter, r *http.Request) { - name := uniqueName(r) + name := displayRepoName(r) name = filepath.Clean(name) repo := filepath.Join(d.c.Repo.ScanPath, name) @@ -32,7 +32,7 @@ func (d *Handle) InfoRefs(w http.ResponseWriter, r *http.Request) { } func (d *Handle) UploadPack(w http.ResponseWriter, r *http.Request) { - name := uniqueName(r) + name := displayRepoName(r) name = filepath.Clean(name) repo := filepath.Join(d.c.Repo.ScanPath, name) diff --git a/routes/handler.go b/routes/handler.go index 483ba2f..377cd8b 100644 --- a/routes/handler.go +++ b/routes/handler.go @@ -9,9 +9,10 @@ import ( _ "github.com/bluesky-social/indigo/xrpc" "github.com/go-chi/chi/v5" "github.com/gorilla/sessions" + "github.com/icyphox/bild/auth" "github.com/icyphox/bild/config" "github.com/icyphox/bild/db" - "github.com/icyphox/bild/routes/auth" + "github.com/icyphox/bild/routes/middleware" "github.com/icyphox/bild/routes/tmpl" ) @@ -77,6 +78,7 @@ func Setup(c *config.Config, db *db.DB) (http.Handler, error) { }) r.Route("/@{user}", func(r chi.Router) { + r.Use(middleware.AddDID) r.Get("/", h.Index) // Repo routes diff --git a/routes/middleware/did.go b/routes/middleware/did.go new file mode 100644 index 0000000..128fd4a --- /dev/null +++ b/routes/middleware/did.go @@ -0,0 +1,61 @@ +package middleware + +import ( + "context" + "log" + "net/http" + "sync" + "time" + + "github.com/bluesky-social/indigo/atproto/identity" + "github.com/icyphox/bild/auth" +) + +type cachedIdent struct { + ident *identity.Identity + expiry time.Time +} + +var ( + identCache = make(map[string]cachedIdent) + cacheMutex sync.RWMutex +) + +// Only use this middleware for routes that require a handle +// /@{user}/... +func AddDID(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + user := r.PathValue("user") + + // Check cache first + cacheMutex.RLock() + if cached, ok := identCache[user]; ok && time.Now().Before(cached.expiry) { + cacheMutex.RUnlock() + ctx := context.WithValue(r.Context(), "did", cached.ident.DID.String()) + r = r.WithContext(ctx) + next.ServeHTTP(w, r) + return + } + cacheMutex.RUnlock() + + // Cache miss - resolve and cache + ident, err := auth.ResolveIdent(r.Context(), user) + if err != nil { + log.Println("error resolving identity", err) + http.Error(w, "error resolving identity", http.StatusNotFound) + return + } + + cacheMutex.Lock() + identCache[user] = cachedIdent{ + ident: ident, + expiry: time.Now().Add(24 * time.Hour), + } + cacheMutex.Unlock() + + ctx := context.WithValue(r.Context(), "did", ident.DID.String()) + r = r.WithContext(ctx) + + next.ServeHTTP(w, r) + }) +} diff --git a/routes/routes.go b/routes/routes.go index 24fc033..3075768 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -22,10 +22,10 @@ import ( "github.com/google/uuid" "github.com/gorilla/sessions" shbild "github.com/icyphox/bild/api/bild" + "github.com/icyphox/bild/auth" "github.com/icyphox/bild/config" "github.com/icyphox/bild/db" "github.com/icyphox/bild/git" - "github.com/icyphox/bild/routes/auth" "github.com/russross/blackfriday/v2" "golang.org/x/crypto/ssh" ) @@ -39,8 +39,8 @@ type Handle struct { } func (h *Handle) Index(w http.ResponseWriter, r *http.Request) { - user := chi.URLParam(r, "user") - path := filepath.Join(h.c.Repo.ScanPath, user) + name := displayRepoName(r) + path := filepath.Join(h.c.Repo.ScanPath, name) dirs, err := os.ReadDir(path) if err != nil { h.Write500(w) @@ -75,7 +75,7 @@ func (h *Handle) Index(w http.ResponseWriter, r *http.Request) { } infos = append(infos, info{ - DisplayName: getDisplayName(name), + DisplayName: trimDotGit(name), Name: name, Desc: getDescription(path), Idle: humanize.Time(c.Author.When), @@ -98,14 +98,13 @@ func (h *Handle) Index(w http.ResponseWriter, r *http.Request) { } func (h *Handle) RepoIndex(w http.ResponseWriter, r *http.Request) { - name := uniqueName(r) + name := displayRepoName(r) if h.isIgnored(name) { h.Write404(w) return } - name = filepath.Clean(name) - path := filepath.Join(h.c.Repo.ScanPath, name) + path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) gr, err := git.Open(path, "") if err != nil { @@ -164,7 +163,7 @@ func (h *Handle) RepoIndex(w http.ResponseWriter, r *http.Request) { data := make(map[string]any) data["name"] = name - data["displayname"] = getDisplayName(name) + data["displayname"] = trimDotGit(name) data["ref"] = mainBranch data["readme"] = readmeContent data["commits"] = commits @@ -182,7 +181,7 @@ func (h *Handle) RepoIndex(w http.ResponseWriter, r *http.Request) { } func (h *Handle) RepoTree(w http.ResponseWriter, r *http.Request) { - name := uniqueName(r) + name := displayRepoName(r) if h.isIgnored(name) { h.Write404(w) return @@ -190,8 +189,7 @@ func (h *Handle) RepoTree(w http.ResponseWriter, r *http.Request) { treePath := chi.URLParam(r, "*") ref := chi.URLParam(r, "ref") - name = filepath.Clean(name) - path := filepath.Join(h.c.Repo.ScanPath, name) + path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) gr, err := git.Open(path, ref) if err != nil { h.Write404(w) @@ -207,7 +205,7 @@ func (h *Handle) RepoTree(w http.ResponseWriter, r *http.Request) { data := make(map[string]any) data["name"] = name - data["displayname"] = getDisplayName(name) + data["displayname"] = trimDotGit(name) data["ref"] = ref data["parent"] = treePath data["desc"] = getDescription(path) @@ -223,7 +221,7 @@ func (h *Handle) FileContent(w http.ResponseWriter, r *http.Request) { raw = rawParam } - name := uniqueName(r) + name := displayRepoName(r) if h.isIgnored(name) { h.Write404(w) @@ -232,8 +230,7 @@ func (h *Handle) FileContent(w http.ResponseWriter, r *http.Request) { treePath := chi.URLParam(r, "*") ref := chi.URLParam(r, "ref") - name = filepath.Clean(name) - path := filepath.Join(h.c.Repo.ScanPath, name) + path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) gr, err := git.Open(path, ref) if err != nil { h.Write404(w) @@ -247,7 +244,7 @@ func (h *Handle) FileContent(w http.ResponseWriter, r *http.Request) { } data := make(map[string]any) data["name"] = name - data["displayname"] = getDisplayName(name) + data["displayname"] = trimDotGit(name) data["ref"] = ref data["desc"] = getDescription(path) data["path"] = treePath @@ -266,7 +263,7 @@ func (h *Handle) FileContent(w http.ResponseWriter, r *http.Request) { } func (h *Handle) Archive(w http.ResponseWriter, r *http.Request) { - name := uniqueName(r) + name := displayRepoName(r) if h.isIgnored(name) { h.Write404(w) return @@ -288,7 +285,7 @@ func (h *Handle) Archive(w http.ResponseWriter, r *http.Request) { setContentDisposition(w, filename) setGZipMIME(w) - path := filepath.Join(h.c.Repo.ScanPath, name) + path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) gr, err := git.Open(path, ref) if err != nil { h.Write404(w) @@ -317,14 +314,14 @@ func (h *Handle) Archive(w http.ResponseWriter, r *http.Request) { } func (h *Handle) Log(w http.ResponseWriter, r *http.Request) { - name := uniqueName(r) + name := displayRepoName(r) if h.isIgnored(name) { h.Write404(w) return } ref := chi.URLParam(r, "ref") - path := filepath.Join(h.c.Repo.ScanPath, name) + path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) gr, err := git.Open(path, ref) if err != nil { h.Write404(w) @@ -342,7 +339,7 @@ func (h *Handle) Log(w http.ResponseWriter, r *http.Request) { data["commits"] = commits data["meta"] = h.c.Meta data["name"] = name - data["displayname"] = getDisplayName(name) + data["displayname"] = trimDotGit(name) data["ref"] = ref data["desc"] = getDescription(path) data["log"] = true @@ -354,14 +351,14 @@ func (h *Handle) Log(w http.ResponseWriter, r *http.Request) { } func (h *Handle) Diff(w http.ResponseWriter, r *http.Request) { - name := uniqueName(r) + name := displayRepoName(r) if h.isIgnored(name) { h.Write404(w) return } ref := chi.URLParam(r, "ref") - path := filepath.Join(h.c.Repo.ScanPath, name) + path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) gr, err := git.Open(path, ref) if err != nil { h.Write404(w) @@ -382,7 +379,7 @@ func (h *Handle) Diff(w http.ResponseWriter, r *http.Request) { data["diff"] = diff.Diff data["meta"] = h.c.Meta data["name"] = name - data["displayname"] = getDisplayName(name) + data["displayname"] = trimDotGit(name) data["ref"] = ref data["desc"] = getDescription(path) @@ -399,7 +396,7 @@ func (h *Handle) Refs(w http.ResponseWriter, r *http.Request) { return } - path := filepath.Join(h.c.Repo.ScanPath, name) + path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) gr, err := git.Open(path, "") if err != nil { h.Write404(w) @@ -423,7 +420,7 @@ func (h *Handle) Refs(w http.ResponseWriter, r *http.Request) { data["meta"] = h.c.Meta data["name"] = name - data["displayname"] = getDisplayName(name) + data["displayname"] = trimDotGit(name) data["branches"] = branches data["tags"] = tags data["desc"] = getDescription(path) @@ -550,28 +547,20 @@ func (h *Handle) NewRepo(w http.ResponseWriter, r *http.Request) { name := r.FormValue("name") description := r.FormValue("description") - repoPath := filepath.Join(h.c.Repo.ScanPath, handle, name) + repoPath := filepath.Join(h.c.Repo.ScanPath, did, name) err := git.InitBare(repoPath) if err != nil { h.WriteOOBNotice(w, "repo", "Error creating repo. Try again later.") return } - // For use by repoguard - didPath := filepath.Join(repoPath, "did") - err = os.WriteFile(didPath, []byte(did), 0644) - if err != nil { - h.WriteOOBNotice(w, "repo", "Error creating repo. Try again later.") - return - } - err = h.db.AddRepo(did, name, description) if err != nil { h.WriteOOBNotice(w, "repo", "Error creating repo. Try again later.") return } - w.Header().Set("HX-Redirect", fmt.Sprintf("/@example.com/%s", name)) + w.Header().Set("HX-Redirect", fmt.Sprintf("/@%s/%s", handle, name)) w.WriteHeader(http.StatusOK) } } diff --git a/routes/util.go b/routes/util.go index b052561..241016a 100644 --- a/routes/util.go +++ b/routes/util.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/go-chi/chi/v5" + "github.com/icyphox/bild/auth" "github.com/icyphox/bild/git" "github.com/microcosm-cc/bluemonday" ) @@ -23,13 +24,27 @@ func isGoModule(gr *git.GitRepo) bool { return err == nil } -func uniqueName(r *http.Request) string { - user := chi.URLParam(r, "user") +func displayRepoName(r *http.Request) string { + user := r.Context().Value("did").(string) name := chi.URLParam(r, "name") - return fmt.Sprintf("%s/%s", user, name) + + handle, err := auth.ResolveIdent(r.Context(), user) + if err != nil { + log.Printf("failed to resolve ident: %s: %s", user, err) + return fmt.Sprintf("%s/%s", user, name) + } + + return fmt.Sprintf("@%s/%s", handle.Handle.String(), name) +} + +func didPath(r *http.Request) string { + did := r.Context().Value("did").(string) + path := filepath.Join(did, chi.URLParam(r, "name")) + filepath.Clean(path) + return path } -func getDisplayName(name string) string { +func trimDotGit(name string) string { return strings.TrimSuffix(name, ".git") } -- 2.51.2