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