knotserver: redirect handle-path to did-path #705

open
opened by boltless.me targeting master from push-xpolynpvotzt

fixing issue reported on discord: https://discord.com/channels/1361963801993285692/1361966362653757450/1430609124114301060

knotserver: redirect handle-path to did-path

this will allow handle based url when cloning from knot over https
usually appview will resolve this before redirecting to the knot

this implementation just redirects to resolved path instead of passing
the entire identity for performance. As we won't want to resolve same
identity twice when proxied by appview.

Signed-off-by: Seongmin Lee <git@boltless.me>
Changed files
+25
knotserver
+25
knotserver/router.go
··· 5 "fmt" 6 "log/slog" 7 "net/http" 8 9 "github.com/go-chi/chi/v5" 10 "tangled.org/core/idresolver" ··· 78 }) 79 80 r.Route("/{did}", func(r chi.Router) { 81 r.Route("/{name}", func(r chi.Router) { 82 // routes for git operations 83 r.Get("/info/refs", h.InfoRefs) ··· 114 return xrpc.Router() 115 } 116 117 func (h *Knot) configureOwner() error { 118 cfgOwner := h.c.Server.Owner 119
··· 5 "fmt" 6 "log/slog" 7 "net/http" 8 + "strings" 9 10 "github.com/go-chi/chi/v5" 11 "tangled.org/core/idresolver" ··· 79 }) 80 81 r.Route("/{did}", func(r chi.Router) { 82 + r.Use(h.resolveDidRedirect) 83 r.Route("/{name}", func(r chi.Router) { 84 // routes for git operations 85 r.Get("/info/refs", h.InfoRefs) ··· 116 return xrpc.Router() 117 } 118 119 + func (h *Knot) resolveDidRedirect(next http.Handler) http.Handler { 120 + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 121 + didOrHandle := chi.URLParam(r, "did") 122 + if strings.HasPrefix(didOrHandle, "did:") { 123 + next.ServeHTTP(w, r) 124 + return 125 + } 126 + 127 + trimmed := strings.TrimPrefix(didOrHandle, "@") 128 + id, err := h.resolver.ResolveIdent(r.Context(), trimmed) 129 + if err != nil { 130 + // invalid did or handle 131 + h.l.Error("failed to resolve did/handle", "handle", trimmed, "err", err) 132 + http.Error(w, fmt.Sprintf("failed to resolve did/handle: %s", trimmed), http.StatusInternalServerError) 133 + return 134 + } 135 + 136 + suffix := strings.TrimPrefix(r.URL.Path, "/"+didOrHandle) 137 + newPath := fmt.Sprintf("/%s/%s?%s", id.DID.String(), suffix, r.URL.RawQuery) 138 + http.Redirect(w, r, newPath, http.StatusTemporaryRedirect) 139 + }) 140 + } 141 + 142 func (h *Knot) configureOwner() error { 143 cfgOwner := h.c.Server.Owner 144