forked from
tangled.org/core
fork
Configure Feed
Select the types of activity you want to include in your feed.
this repo has no description
fork
Configure Feed
Select the types of activity you want to include in your feed.
1package state
2
3import (
4 "net/http"
5 "strings"
6
7 "github.com/go-chi/chi/v5"
8 "tangled.sh/tangled.sh/core/appview/middleware"
9 "tangled.sh/tangled.sh/core/appview/settings"
10 "tangled.sh/tangled.sh/core/appview/state/userutil"
11)
12
13func (s *State) Router() http.Handler {
14 router := chi.NewRouter()
15
16 router.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) {
17 pat := chi.URLParam(r, "*")
18 if strings.HasPrefix(pat, "did:") || strings.HasPrefix(pat, "@") {
19 s.UserRouter().ServeHTTP(w, r)
20 } else {
21 // Check if the first path element is a valid handle without '@' or a flattened DID
22 pathParts := strings.SplitN(pat, "/", 2)
23 if len(pathParts) > 0 {
24 if userutil.IsHandleNoAt(pathParts[0]) {
25 // Redirect to the same path but with '@' prefixed to the handle
26 redirectPath := "@" + pat
27 http.Redirect(w, r, "/"+redirectPath, http.StatusFound)
28 return
29 } else if userutil.IsFlattenedDid(pathParts[0]) {
30 // Redirect to the unflattened DID version
31 unflattenedDid := userutil.UnflattenDid(pathParts[0])
32 var redirectPath string
33 if len(pathParts) > 1 {
34 redirectPath = unflattenedDid + "/" + pathParts[1]
35 } else {
36 redirectPath = unflattenedDid
37 }
38 http.Redirect(w, r, "/"+redirectPath, http.StatusFound)
39 return
40 }
41 }
42 s.StandardRouter().ServeHTTP(w, r)
43 }
44 })
45
46 return router
47}
48
49func (s *State) UserRouter() http.Handler {
50 r := chi.NewRouter()
51
52 // strip @ from user
53 r.Use(StripLeadingAt)
54
55 r.With(ResolveIdent(s)).Route("/{user}", func(r chi.Router) {
56 r.Get("/", s.ProfilePage)
57 r.With(ResolveRepo(s)).Route("/{repo}", func(r chi.Router) {
58 r.Get("/", s.RepoIndex)
59 r.Get("/commits/{ref}", s.RepoLog)
60 r.Route("/tree/{ref}", func(r chi.Router) {
61 r.Get("/", s.RepoIndex)
62 r.Get("/*", s.RepoTree)
63 })
64 r.Get("/commit/{ref}", s.RepoCommit)
65 r.Get("/branches", s.RepoBranches)
66 r.Route("/tags", func(r chi.Router) {
67 r.Get("/", s.RepoTags)
68 r.Route("/{tag}", func(r chi.Router) {
69 r.Use(middleware.AuthMiddleware(s.auth))
70 // require auth to download for now
71 r.Get("/download/{file}", s.DownloadArtifact)
72
73 // require repo:push to upload or delete artifacts
74 //
75 // additionally: only the uploader can truly delete an artifact
76 // (record+blob will live on their pds)
77 r.Group(func(r chi.Router) {
78 r.With(RepoPermissionMiddleware(s, "repo:push"))
79 r.Post("/upload", s.AttachArtifact)
80 r.Delete("/{file}", s.DeleteArtifact)
81 })
82 })
83 })
84 r.Get("/blob/{ref}/*", s.RepoBlob)
85 r.Get("/raw/{ref}/*", s.RepoBlobRaw)
86
87 r.Route("/issues", func(r chi.Router) {
88 r.With(middleware.Paginate).Get("/", s.RepoIssues)
89 r.Get("/{issue}", s.RepoSingleIssue)
90
91 r.Group(func(r chi.Router) {
92 r.Use(middleware.AuthMiddleware(s.auth))
93 r.Get("/new", s.NewIssue)
94 r.Post("/new", s.NewIssue)
95 r.Post("/{issue}/comment", s.NewIssueComment)
96 r.Route("/{issue}/comment/{comment_id}/", func(r chi.Router) {
97 r.Get("/", s.IssueComment)
98 r.Delete("/", s.DeleteIssueComment)
99 r.Get("/edit", s.EditIssueComment)
100 r.Post("/edit", s.EditIssueComment)
101 })
102 r.Post("/{issue}/close", s.CloseIssue)
103 r.Post("/{issue}/reopen", s.ReopenIssue)
104 })
105 })
106
107 r.Route("/fork", func(r chi.Router) {
108 r.Use(middleware.AuthMiddleware(s.auth))
109 r.Get("/", s.ForkRepo)
110 r.Post("/", s.ForkRepo)
111 })
112
113 r.Route("/pulls", func(r chi.Router) {
114 r.Get("/", s.RepoPulls)
115 r.With(middleware.AuthMiddleware(s.auth)).Route("/new", func(r chi.Router) {
116 r.Get("/", s.NewPull)
117 r.Get("/patch-upload", s.PatchUploadFragment)
118 r.Post("/validate-patch", s.ValidatePatch)
119 r.Get("/compare-branches", s.CompareBranchesFragment)
120 r.Get("/compare-forks", s.CompareForksFragment)
121 r.Get("/fork-branches", s.CompareForksBranchesFragment)
122 r.Post("/", s.NewPull)
123 })
124
125 r.Route("/{pull}", func(r chi.Router) {
126 r.Use(ResolvePull(s))
127 r.Get("/", s.RepoSinglePull)
128
129 r.Route("/round/{round}", func(r chi.Router) {
130 r.Get("/", s.RepoPullPatch)
131 r.Get("/interdiff", s.RepoPullInterdiff)
132 r.Get("/actions", s.PullActions)
133 r.With(middleware.AuthMiddleware(s.auth)).Route("/comment", func(r chi.Router) {
134 r.Get("/", s.PullComment)
135 r.Post("/", s.PullComment)
136 })
137 })
138
139 r.Route("/round/{round}.patch", func(r chi.Router) {
140 r.Get("/", s.RepoPullPatchRaw)
141 })
142
143 r.Group(func(r chi.Router) {
144 r.Use(middleware.AuthMiddleware(s.auth))
145 r.Route("/resubmit", func(r chi.Router) {
146 r.Get("/", s.ResubmitPull)
147 r.Post("/", s.ResubmitPull)
148 })
149 r.Post("/close", s.ClosePull)
150 r.Post("/reopen", s.ReopenPull)
151 // collaborators only
152 r.Group(func(r chi.Router) {
153 r.Use(RepoPermissionMiddleware(s, "repo:push"))
154 r.Post("/merge", s.MergePull)
155 // maybe lock, etc.
156 })
157 })
158 })
159 })
160
161 // These routes get proxied to the knot
162 r.Get("/info/refs", s.InfoRefs)
163 r.Post("/git-upload-pack", s.UploadPack)
164
165 // settings routes, needs auth
166 r.Group(func(r chi.Router) {
167 r.Use(middleware.AuthMiddleware(s.auth))
168 // repo description can only be edited by owner
169 r.With(RepoPermissionMiddleware(s, "repo:owner")).Route("/description", func(r chi.Router) {
170 r.Put("/", s.RepoDescription)
171 r.Get("/", s.RepoDescription)
172 r.Get("/edit", s.RepoDescriptionEdit)
173 })
174 r.With(RepoPermissionMiddleware(s, "repo:settings")).Route("/settings", func(r chi.Router) {
175 r.Get("/", s.RepoSettings)
176 r.With(RepoPermissionMiddleware(s, "repo:invite")).Put("/collaborator", s.AddCollaborator)
177 r.With(RepoPermissionMiddleware(s, "repo:delete")).Delete("/delete", s.DeleteRepo)
178 r.Put("/branches/default", s.SetDefaultBranch)
179 })
180 })
181 })
182 })
183
184 r.NotFound(func(w http.ResponseWriter, r *http.Request) {
185 s.pages.Error404(w)
186 })
187
188 return r
189}
190
191func (s *State) StandardRouter() http.Handler {
192 r := chi.NewRouter()
193
194 r.Handle("/static/*", s.pages.Static())
195
196 r.Get("/", s.Timeline)
197
198 r.With(middleware.AuthMiddleware(s.auth)).Post("/logout", s.Logout)
199
200 r.Route("/login", func(r chi.Router) {
201 r.Get("/", s.Login)
202 r.Post("/", s.Login)
203 })
204
205 r.Route("/knots", func(r chi.Router) {
206 r.Use(middleware.AuthMiddleware(s.auth))
207 r.Get("/", s.Knots)
208 r.Post("/key", s.RegistrationKey)
209
210 r.Route("/{domain}", func(r chi.Router) {
211 r.Post("/init", s.InitKnotServer)
212 r.Get("/", s.KnotServerInfo)
213 r.Route("/member", func(r chi.Router) {
214 r.Use(KnotOwner(s))
215 r.Get("/", s.ListMembers)
216 r.Put("/", s.AddMember)
217 r.Delete("/", s.RemoveMember)
218 })
219 })
220 })
221
222 r.Route("/repo", func(r chi.Router) {
223 r.Route("/new", func(r chi.Router) {
224 r.Use(middleware.AuthMiddleware(s.auth))
225 r.Get("/", s.NewRepo)
226 r.Post("/", s.NewRepo)
227 })
228 // r.Post("/import", s.ImportRepo)
229 })
230
231 r.With(middleware.AuthMiddleware(s.auth)).Route("/follow", func(r chi.Router) {
232 r.Post("/", s.Follow)
233 r.Delete("/", s.Follow)
234 })
235
236 r.With(middleware.AuthMiddleware(s.auth)).Route("/star", func(r chi.Router) {
237 r.Post("/", s.Star)
238 r.Delete("/", s.Star)
239 })
240
241 r.Mount("/settings", s.SettingsRouter())
242
243 r.Get("/keys/{user}", s.Keys)
244
245 r.NotFound(func(w http.ResponseWriter, r *http.Request) {
246 s.pages.Error404(w)
247 })
248 return r
249}
250
251func (s *State) SettingsRouter() http.Handler {
252 settings := &settings.Settings{
253 Db: s.db,
254 Auth: s.auth,
255 Pages: s.pages,
256 Config: s.config,
257 }
258
259 return settings.Router()
260}