forked from tangled.org/core
Monorepo for Tangled — https://tangled.org
1package state 2 3import ( 4 "net/http" 5 "strings" 6 7 "github.com/go-chi/chi/v5" 8 "github.com/gorilla/sessions" 9 "tangled.sh/tangled.sh/core/appview/issues" 10 "tangled.sh/tangled.sh/core/appview/middleware" 11 oauthhandler "tangled.sh/tangled.sh/core/appview/oauth/handler" 12 "tangled.sh/tangled.sh/core/appview/pulls" 13 "tangled.sh/tangled.sh/core/appview/repo" 14 "tangled.sh/tangled.sh/core/appview/settings" 15 "tangled.sh/tangled.sh/core/appview/state/userutil" 16) 17 18func (s *State) Router() http.Handler { 19 router := chi.NewRouter() 20 middleware := middleware.New( 21 s.oauth, 22 s.db, 23 s.enforcer, 24 s.repoResolver, 25 s.idResolver, 26 s.pages, 27 ) 28 29 router.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) { 30 pat := chi.URLParam(r, "*") 31 if strings.HasPrefix(pat, "did:") || strings.HasPrefix(pat, "@") { 32 s.UserRouter(&middleware).ServeHTTP(w, r) 33 } else { 34 // Check if the first path element is a valid handle without '@' or a flattened DID 35 pathParts := strings.SplitN(pat, "/", 2) 36 if len(pathParts) > 0 { 37 if userutil.IsHandleNoAt(pathParts[0]) { 38 // Redirect to the same path but with '@' prefixed to the handle 39 redirectPath := "@" + pat 40 http.Redirect(w, r, "/"+redirectPath, http.StatusFound) 41 return 42 } else if userutil.IsFlattenedDid(pathParts[0]) { 43 // Redirect to the unflattened DID version 44 unflattenedDid := userutil.UnflattenDid(pathParts[0]) 45 var redirectPath string 46 if len(pathParts) > 1 { 47 redirectPath = unflattenedDid + "/" + pathParts[1] 48 } else { 49 redirectPath = unflattenedDid 50 } 51 http.Redirect(w, r, "/"+redirectPath, http.StatusFound) 52 return 53 } 54 } 55 s.StandardRouter(&middleware).ServeHTTP(w, r) 56 } 57 }) 58 59 return router 60} 61 62func (s *State) UserRouter(mw *middleware.Middleware) http.Handler { 63 r := chi.NewRouter() 64 65 // strip @ from user 66 r.Use(middleware.StripLeadingAt) 67 68 r.With(mw.ResolveIdent()).Route("/{user}", func(r chi.Router) { 69 r.Get("/", s.Profile) 70 71 r.With(mw.ResolveRepo()).Route("/{repo}", func(r chi.Router) { 72 r.Use(mw.GoImport()) 73 74 r.Mount("/", s.RepoRouter(mw)) 75 r.Mount("/issues", s.IssuesRouter(mw)) 76 r.Mount("/pulls", s.PullsRouter(mw)) 77 78 // These routes get proxied to the knot 79 r.Get("/info/refs", s.InfoRefs) 80 r.Post("/git-upload-pack", s.UploadPack) 81 r.Post("/git-receive-pack", s.ReceivePack) 82 83 }) 84 }) 85 86 r.NotFound(func(w http.ResponseWriter, r *http.Request) { 87 s.pages.Error404(w) 88 }) 89 90 return r 91} 92 93func (s *State) StandardRouter(mw *middleware.Middleware) http.Handler { 94 r := chi.NewRouter() 95 96 r.Handle("/static/*", s.pages.Static()) 97 98 r.Get("/", s.Timeline) 99 100 r.Route("/knots", func(r chi.Router) { 101 r.Use(middleware.AuthMiddleware(s.oauth)) 102 r.Get("/", s.Knots) 103 r.Post("/key", s.RegistrationKey) 104 105 r.Route("/{domain}", func(r chi.Router) { 106 r.Post("/init", s.InitKnotServer) 107 r.Get("/", s.KnotServerInfo) 108 r.Route("/member", func(r chi.Router) { 109 r.Use(mw.KnotOwner()) 110 r.Get("/", s.ListMembers) 111 r.Put("/", s.AddMember) 112 r.Delete("/", s.RemoveMember) 113 }) 114 }) 115 }) 116 117 r.Route("/repo", func(r chi.Router) { 118 r.Route("/new", func(r chi.Router) { 119 r.Use(middleware.AuthMiddleware(s.oauth)) 120 r.Get("/", s.NewRepo) 121 r.Post("/", s.NewRepo) 122 }) 123 // r.Post("/import", s.ImportRepo) 124 }) 125 126 r.With(middleware.AuthMiddleware(s.oauth)).Route("/follow", func(r chi.Router) { 127 r.Post("/", s.Follow) 128 r.Delete("/", s.Follow) 129 }) 130 131 r.With(middleware.AuthMiddleware(s.oauth)).Route("/star", func(r chi.Router) { 132 r.Post("/", s.Star) 133 r.Delete("/", s.Star) 134 }) 135 136 r.Route("/profile", func(r chi.Router) { 137 r.Use(middleware.AuthMiddleware(s.oauth)) 138 r.Get("/edit-bio", s.EditBioFragment) 139 r.Get("/edit-pins", s.EditPinsFragment) 140 r.Post("/bio", s.UpdateProfileBio) 141 r.Post("/pins", s.UpdateProfilePins) 142 }) 143 144 r.Mount("/settings", s.SettingsRouter()) 145 r.Mount("/", s.OAuthRouter()) 146 147 r.Get("/keys/{user}", s.Keys) 148 149 r.NotFound(func(w http.ResponseWriter, r *http.Request) { 150 s.pages.Error404(w) 151 }) 152 return r 153} 154 155func (s *State) OAuthRouter() http.Handler { 156 store := sessions.NewCookieStore([]byte(s.config.Core.CookieSecret)) 157 oauth := oauthhandler.New(s.config, s.pages, s.idResolver, s.db, s.sess, store, s.oauth, s.enforcer, s.posthog) 158 return oauth.Router() 159} 160 161func (s *State) SettingsRouter() http.Handler { 162 settings := &settings.Settings{ 163 Db: s.db, 164 OAuth: s.oauth, 165 Pages: s.pages, 166 Config: s.config, 167 } 168 169 return settings.Router() 170} 171 172func (s *State) IssuesRouter(mw *middleware.Middleware) http.Handler { 173 issues := issues.New(s.oauth, s.repoResolver, s.pages, s.idResolver, s.db, s.config, s.posthog) 174 return issues.Router(mw) 175 176} 177 178func (s *State) PullsRouter(mw *middleware.Middleware) http.Handler { 179 pulls := pulls.New(s.oauth, s.repoResolver, s.pages, s.idResolver, s.db, s.config, s.posthog) 180 return pulls.Router(mw) 181} 182 183func (s *State) RepoRouter(mw *middleware.Middleware) http.Handler { 184 repo := repo.New(s.oauth, s.repoResolver, s.pages, s.idResolver, s.db, s.config, s.posthog, s.enforcer) 185 return repo.Router(mw) 186}