diff --git a/appview/pages/pages.go b/appview/pages/pages.go index 04e767f6..0143fd57 100644 --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -85,3 +85,14 @@ type NewRepoParams struct { func NewRepo(w io.Writer, p NewRepoParams) error { return parse("new-repo.html").Execute(w, p) } + +type ProfilePageParams struct { + LoggedInUser *auth.User + UserDid string + UserHandle string + Repos []db.Repo +} + +func ProfilePage(w io.Writer, p ProfilePageParams) error { + return parse("profile.html").Execute(w, p) +} diff --git a/appview/pages/profile.html b/appview/pages/profile.html new file mode 100644 index 00000000..0a3971aa --- /dev/null +++ b/appview/pages/profile.html @@ -0,0 +1,18 @@ +{{define "title"}}{{ or .UserHandle .UserDid }}{{end}} + +{{define "content"}} + back to timeline +

{{ or .UserHandle .UserDid }} profile

+ +

repos

+ +{{end}} diff --git a/appview/state/middleware.go b/appview/state/middleware.go index daa3914f..b0e71315 100644 --- a/appview/state/middleware.go +++ b/appview/state/middleware.go @@ -3,6 +3,7 @@ package state import ( "log" "net/http" + "strings" "time" comatproto "github.com/bluesky-social/indigo/api/atproto" @@ -99,3 +100,13 @@ func RoleMiddleware(s *State, group string) Middleware { }) } } + +func StripLeadingAt(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + path := req.URL.Path + if strings.HasPrefix(path, "/@") { + req.URL.Path = "/" + strings.TrimPrefix(path, "/@") + } + next.ServeHTTP(w, req) + }) +} diff --git a/appview/state/state.go b/appview/state/state.go index f1d7102f..d7e8e1a9 100644 --- a/appview/state/state.go +++ b/appview/state/state.go @@ -538,7 +538,62 @@ func (s *State) AddRepo(w http.ResponseWriter, r *http.Request) { } } +func (s *State) ProfilePage(w http.ResponseWriter, r *http.Request) { + didOrHandle := chi.URLParam(r, "user") + if didOrHandle == "" { + http.Error(w, "Bad request", http.StatusBadRequest) + return + } + + ident, err := auth.ResolveIdent(r.Context(), didOrHandle) + if err != nil { + log.Printf("resolving identity: %s", err) + w.WriteHeader(http.StatusNotFound) + return + } + + repos, err := s.db.GetAllReposByDid(ident.DID.String()) + if err != nil { + log.Printf("getting repos for %s: %s", ident.DID.String(), err) + } + + pages.ProfilePage(w, pages.ProfilePageParams{ + LoggedInUser: s.auth.GetUser(r), + UserDid: ident.DID.String(), + UserHandle: ident.Handle.String(), + Repos: repos, + }) +} + func (s *State) Router() http.Handler { + router := chi.NewRouter() + + router.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) { + pat := chi.URLParam(r, "*") + if strings.HasPrefix(pat, "did:") || strings.HasPrefix(pat, "@") { + s.UserRouter().ServeHTTP(w, r) + } else { + s.StandardRouter().ServeHTTP(w, r) + } + }) + + return router +} + +func (s *State) UserRouter() http.Handler { + r := chi.NewRouter() + + // strip @ from user + r.Use(StripLeadingAt) + + r.Route("/{user}", func(r chi.Router) { + r.Get("/", s.ProfilePage) + }) + + return r +} + +func (s *State) StandardRouter() http.Handler { r := chi.NewRouter() r.Get("/", s.Timeline)