this repo has no description
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/middleware" 10 oauthhandler "tangled.sh/tangled.sh/core/appview/oauth/handler" 11 "tangled.sh/tangled.sh/core/appview/settings" 12 "tangled.sh/tangled.sh/core/appview/state/userutil" 13) 14 15func (s *State) Router() http.Handler { 16 router := chi.NewRouter() 17 middleware := middleware.New( 18 s.oauth, 19 s.db, 20 s.enforcer, 21 s.repoResolver, 22 s.resolver, 23 s.pages, 24 ) 25 26 router.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) { 27 pat := chi.URLParam(r, "*") 28 if strings.HasPrefix(pat, "did:") || strings.HasPrefix(pat, "@") { 29 s.UserRouter(&middleware).ServeHTTP(w, r) 30 } else { 31 // Check if the first path element is a valid handle without '@' or a flattened DID 32 pathParts := strings.SplitN(pat, "/", 2) 33 if len(pathParts) > 0 { 34 if userutil.IsHandleNoAt(pathParts[0]) { 35 // Redirect to the same path but with '@' prefixed to the handle 36 redirectPath := "@" + pat 37 http.Redirect(w, r, "/"+redirectPath, http.StatusFound) 38 return 39 } else if userutil.IsFlattenedDid(pathParts[0]) { 40 // Redirect to the unflattened DID version 41 unflattenedDid := userutil.UnflattenDid(pathParts[0]) 42 var redirectPath string 43 if len(pathParts) > 1 { 44 redirectPath = unflattenedDid + "/" + pathParts[1] 45 } else { 46 redirectPath = unflattenedDid 47 } 48 http.Redirect(w, r, "/"+redirectPath, http.StatusFound) 49 return 50 } 51 } 52 s.StandardRouter(&middleware).ServeHTTP(w, r) 53 } 54 }) 55 56 return router 57} 58 59func (s *State) UserRouter(mw *middleware.Middleware) http.Handler { 60 r := chi.NewRouter() 61 62 // strip @ from user 63 r.Use(middleware.StripLeadingAt) 64 65 r.With(mw.ResolveIdent()).Route("/{user}", func(r chi.Router) { 66 r.Get("/", s.Profile) 67 68 r.With(mw.ResolveRepo()).Route("/{repo}", func(r chi.Router) { 69 r.Use(mw.GoImport()) 70 71 r.Get("/", s.RepoIndex) 72 r.Get("/commits/{ref}", s.RepoLog) 73 r.Route("/tree/{ref}", func(r chi.Router) { 74 r.Get("/", s.RepoIndex) 75 r.Get("/*", s.RepoTree) 76 }) 77 r.Get("/commit/{ref}", s.RepoCommit) 78 r.Get("/branches", s.RepoBranches) 79 r.Route("/tags", func(r chi.Router) { 80 r.Get("/", s.RepoTags) 81 r.Route("/{tag}", func(r chi.Router) { 82 r.Use(middleware.AuthMiddleware(s.oauth)) 83 // require auth to download for now 84 r.Get("/download/{file}", s.DownloadArtifact) 85 86 // require repo:push to upload or delete artifacts 87 // 88 // additionally: only the uploader can truly delete an artifact 89 // (record+blob will live on their pds) 90 r.Group(func(r chi.Router) { 91 r.With(mw.RepoPermissionMiddleware("repo:push")) 92 r.Post("/upload", s.AttachArtifact) 93 r.Delete("/{file}", s.DeleteArtifact) 94 }) 95 }) 96 }) 97 r.Get("/blob/{ref}/*", s.RepoBlob) 98 r.Get("/raw/{ref}/*", s.RepoBlobRaw) 99 100 r.Route("/issues", func(r chi.Router) { 101 r.With(middleware.Paginate).Get("/", s.RepoIssues) 102 r.Get("/{issue}", s.RepoSingleIssue) 103 104 r.Group(func(r chi.Router) { 105 r.Use(middleware.AuthMiddleware(s.oauth)) 106 r.Get("/new", s.NewIssue) 107 r.Post("/new", s.NewIssue) 108 r.Post("/{issue}/comment", s.NewIssueComment) 109 r.Route("/{issue}/comment/{comment_id}/", func(r chi.Router) { 110 r.Get("/", s.IssueComment) 111 r.Delete("/", s.DeleteIssueComment) 112 r.Get("/edit", s.EditIssueComment) 113 r.Post("/edit", s.EditIssueComment) 114 }) 115 r.Post("/{issue}/close", s.CloseIssue) 116 r.Post("/{issue}/reopen", s.ReopenIssue) 117 }) 118 }) 119 120 r.Route("/fork", func(r chi.Router) { 121 r.Use(middleware.AuthMiddleware(s.oauth)) 122 r.Get("/", s.ForkRepo) 123 r.Post("/", s.ForkRepo) 124 r.With(mw.RepoPermissionMiddleware("repo:owner")).Route("/sync", func(r chi.Router) { 125 r.Post("/", s.SyncRepoFork) 126 }) 127 }) 128 129 r.Route("/compare", func(r chi.Router) { 130 r.Get("/", s.RepoCompareNew) // start an new comparison 131 132 // we have to wildcard here since we want to support GitHub's compare syntax 133 // /compare/{ref1}...{ref2} 134 // for example: 135 // /compare/master...some/feature 136 // /compare/master...example.com:another/feature <- this is a fork 137 r.Get("/{base}/{head}", s.RepoCompare) 138 r.Get("/*", s.RepoCompare) 139 }) 140 141 r.Route("/pulls", func(r chi.Router) { 142 r.Get("/", s.RepoPulls) 143 r.With(middleware.AuthMiddleware(s.oauth)).Route("/new", func(r chi.Router) { 144 r.Get("/", s.NewPull) 145 r.Get("/patch-upload", s.PatchUploadFragment) 146 r.Post("/validate-patch", s.ValidatePatch) 147 r.Get("/compare-branches", s.CompareBranchesFragment) 148 r.Get("/compare-forks", s.CompareForksFragment) 149 r.Get("/fork-branches", s.CompareForksBranchesFragment) 150 r.Post("/", s.NewPull) 151 }) 152 153 r.Route("/{pull}", func(r chi.Router) { 154 r.Use(mw.ResolvePull()) 155 r.Get("/", s.RepoSinglePull) 156 157 r.Route("/round/{round}", func(r chi.Router) { 158 r.Get("/", s.RepoPullPatch) 159 r.Get("/interdiff", s.RepoPullInterdiff) 160 r.Get("/actions", s.PullActions) 161 r.With(middleware.AuthMiddleware(s.oauth)).Route("/comment", func(r chi.Router) { 162 r.Get("/", s.PullComment) 163 r.Post("/", s.PullComment) 164 }) 165 }) 166 167 r.Route("/round/{round}.patch", func(r chi.Router) { 168 r.Get("/", s.RepoPullPatchRaw) 169 }) 170 171 r.Group(func(r chi.Router) { 172 r.Use(middleware.AuthMiddleware(s.oauth)) 173 r.Route("/resubmit", func(r chi.Router) { 174 r.Get("/", s.ResubmitPull) 175 r.Post("/", s.ResubmitPull) 176 }) 177 r.Post("/close", s.ClosePull) 178 r.Post("/reopen", s.ReopenPull) 179 // collaborators only 180 r.Group(func(r chi.Router) { 181 r.Use(mw.RepoPermissionMiddleware("repo:push")) 182 r.Post("/merge", s.MergePull) 183 // maybe lock, etc. 184 }) 185 }) 186 }) 187 }) 188 189 // These routes get proxied to the knot 190 r.Get("/info/refs", s.InfoRefs) 191 r.Post("/git-upload-pack", s.UploadPack) 192 r.Post("/git-receive-pack", s.ReceivePack) 193 194 // settings routes, needs auth 195 r.Group(func(r chi.Router) { 196 r.Use(middleware.AuthMiddleware(s.oauth)) 197 // repo description can only be edited by owner 198 r.With(mw.RepoPermissionMiddleware("repo:owner")).Route("/description", func(r chi.Router) { 199 r.Put("/", s.RepoDescription) 200 r.Get("/", s.RepoDescription) 201 r.Get("/edit", s.RepoDescriptionEdit) 202 }) 203 r.With(mw.RepoPermissionMiddleware("repo:settings")).Route("/settings", func(r chi.Router) { 204 r.Get("/", s.RepoSettings) 205 r.With(mw.RepoPermissionMiddleware("repo:invite")).Put("/collaborator", s.AddCollaborator) 206 r.With(mw.RepoPermissionMiddleware("repo:delete")).Delete("/delete", s.DeleteRepo) 207 r.Put("/branches/default", s.SetDefaultBranch) 208 }) 209 }) 210 }) 211 }) 212 213 r.NotFound(func(w http.ResponseWriter, r *http.Request) { 214 s.pages.Error404(w) 215 }) 216 217 return r 218} 219 220func (s *State) StandardRouter(mw *middleware.Middleware) http.Handler { 221 r := chi.NewRouter() 222 223 r.Handle("/static/*", s.pages.Static()) 224 225 r.Get("/", s.Timeline) 226 227 r.With(middleware.AuthMiddleware(s.oauth)).Post("/logout", s.Logout) 228 229 r.Route("/knots", func(r chi.Router) { 230 r.Use(middleware.AuthMiddleware(s.oauth)) 231 r.Get("/", s.Knots) 232 r.Post("/key", s.RegistrationKey) 233 234 r.Route("/{domain}", func(r chi.Router) { 235 r.Post("/init", s.InitKnotServer) 236 r.Get("/", s.KnotServerInfo) 237 r.Route("/member", func(r chi.Router) { 238 r.Use(mw.KnotOwner()) 239 r.Get("/", s.ListMembers) 240 r.Put("/", s.AddMember) 241 r.Delete("/", s.RemoveMember) 242 }) 243 }) 244 }) 245 246 r.Route("/repo", func(r chi.Router) { 247 r.Route("/new", func(r chi.Router) { 248 r.Use(middleware.AuthMiddleware(s.oauth)) 249 r.Get("/", s.NewRepo) 250 r.Post("/", s.NewRepo) 251 }) 252 // r.Post("/import", s.ImportRepo) 253 }) 254 255 r.With(middleware.AuthMiddleware(s.oauth)).Route("/follow", func(r chi.Router) { 256 r.Post("/", s.Follow) 257 r.Delete("/", s.Follow) 258 }) 259 260 r.With(middleware.AuthMiddleware(s.oauth)).Route("/star", func(r chi.Router) { 261 r.Post("/", s.Star) 262 r.Delete("/", s.Star) 263 }) 264 265 r.Route("/profile", func(r chi.Router) { 266 r.Use(middleware.AuthMiddleware(s.oauth)) 267 r.Get("/edit-bio", s.EditBioFragment) 268 r.Get("/edit-pins", s.EditPinsFragment) 269 r.Post("/bio", s.UpdateProfileBio) 270 r.Post("/pins", s.UpdateProfilePins) 271 }) 272 273 r.Mount("/settings", s.SettingsRouter()) 274 r.Mount("/", s.OAuthRouter()) 275 r.Get("/keys/{user}", s.Keys) 276 277 r.NotFound(func(w http.ResponseWriter, r *http.Request) { 278 s.pages.Error404(w) 279 }) 280 return r 281} 282 283func (s *State) OAuthRouter() http.Handler { 284 oauth := &oauthhandler.OAuthHandler{ 285 Config: s.config, 286 Pages: s.pages, 287 Resolver: s.resolver, 288 Db: s.db, 289 Store: sessions.NewCookieStore([]byte(s.config.Core.CookieSecret)), 290 OAuth: s.oauth, 291 Enforcer: s.enforcer, 292 Posthog: s.posthog, 293 } 294 295 return oauth.Router() 296} 297 298func (s *State) SettingsRouter() http.Handler { 299 settings := &settings.Settings{ 300 Db: s.db, 301 OAuth: s.oauth, 302 Pages: s.pages, 303 Config: s.config, 304 } 305 306 return settings.Router() 307}