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