diff --git a/knotmirror/adminpage.go b/knotmirror/adminpage.go index 7db562e1..6fa3d477 100644 --- a/knotmirror/adminpage.go +++ b/knotmirror/adminpage.go @@ -15,6 +15,7 @@ import ( "github.com/bluesky-social/indigo/atproto/syntax" "github.com/go-chi/chi/v5" "tangled.org/core/appview/pagination" + "tangled.org/core/idresolver" "tangled.org/core/knotmirror/db" "tangled.org/core/knotmirror/models" "tangled.org/core/knotmirror/xrpc" @@ -30,14 +31,16 @@ type AdminServer struct { resyncer *Resyncer xrpc *xrpc.Xrpc logger *slog.Logger + resolver *idresolver.Resolver } -func NewAdminServer(l *slog.Logger, database *sql.DB, resyncer *Resyncer, x *xrpc.Xrpc) *AdminServer { +func NewAdminServer(l *slog.Logger, database *sql.DB, resyncer *Resyncer, x *xrpc.Xrpc, resolver *idresolver.Resolver) *AdminServer { return &AdminServer{ db: database, resyncer: resyncer, xrpc: x, logger: l, + resolver: resolver, } } @@ -103,13 +106,30 @@ func (s *AdminServer) handleRepos() http.HandlerFunc { } var ( - did = r.URL.Query().Get("did") - knot = r.URL.Query().Get("knot") - state = r.URL.Query().Get("state") + didInput = r.URL.Query().Get("did") + knot = r.URL.Query().Get("knot") + state = r.URL.Query().Get("state") name = r.URL.Query().Get("name") ) - repos, err := db.ListRepos(r.Context(), s.db, page, did, knot, state) + did := didInput + if didInput != "" { + if _, err := syntax.ParseDID(didInput); err != nil { + // treat as a handle and resolve to DID + handle, herr := syntax.ParseHandle(didInput) + if herr != nil { + http.Error(w, fmt.Sprintf("invalid DID or handle: %s", didInput), http.StatusBadRequest) + return + } + resolved, rerr := s.resolver.ResolveHandle(r.Context(), handle.Normalize()) + if rerr != nil { + http.Error(w, fmt.Sprintf("could not resolve handle %q: %s", didInput, rerr), http.StatusBadRequest) + return + } + did = resolved.String() + } + } + repos, err := db.ListRepos(r.Context(), s.db, page, did, knot, state, name) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) diff --git a/knotmirror/knotmirror.go b/knotmirror/knotmirror.go index 567396ba..6c004c98 100644 --- a/knotmirror/knotmirror.go +++ b/knotmirror/knotmirror.go @@ -33,7 +33,11 @@ func Run(ctx context.Context, cfg *config.Config) error { rdb := redis.NewClient(&redis.Options{Addr: cfg.RedisAddr}) - resolver := idresolver.DefaultResolver(cfg.PlcUrl) + resolver, err := idresolver.RedisResolver("redis://"+cfg.RedisAddr, cfg.PlcUrl) + if err != nil { + logger.Error("failed to create redis resolver for admin, falling back to default", "err", err) + resolver = idresolver.DefaultResolver(cfg.PlcUrl) + } // NOTE: using plain git-cli for clone/fetch as go-git is too memory-intensive. gitm := NewCliGitMirrorManager(cfg.GitRepoBasePath, cfg.KnotUseSSL) @@ -56,7 +60,7 @@ func Run(ctx context.Context, cfg *config.Config) error { crawler := NewCrawler(logger, db) resyncer := NewResyncer(logger, db, gitm, cfg) xrpc := xrpc.New(logger, cfg, db, rdb, resolver, knotstream) - adminpage := NewAdminServer(logger, db, resyncer, xrpc) + adminpage := NewAdminServer(logger, db, resyncer, xrpc, resolver) // maintain repository list with tap // NOTE: this can be removed once we introduce did-for-repo because then we can just listen to KnotStream for #identity events. diff --git a/knotmirror/templates/repos.html b/knotmirror/templates/repos.html index 83e1f707..9fb3ebef 100644 --- a/knotmirror/templates/repos.html +++ b/knotmirror/templates/repos.html @@ -10,7 +10,7 @@ hx-swap="outerHTML" hx-trigger="input delay:300ms from:input, change from:select, every 10s" > - +