this repo has no description
1package web
2
3import (
4 "log/slog"
5 "net/http"
6
7 "github.com/go-chi/chi/v5"
8 "tangled.org/core/appview/config"
9 "tangled.org/core/appview/db"
10 "tangled.org/core/appview/indexer"
11 "tangled.org/core/appview/mentions"
12 "tangled.org/core/appview/notify"
13 "tangled.org/core/appview/oauth"
14 "tangled.org/core/appview/pages"
15 isvc "tangled.org/core/appview/service/issue"
16 rsvc "tangled.org/core/appview/service/repo"
17 "tangled.org/core/appview/state"
18 "tangled.org/core/appview/validator"
19 "tangled.org/core/appview/web/handler"
20 "tangled.org/core/appview/web/middleware"
21 "tangled.org/core/idresolver"
22 "tangled.org/core/rbac"
23)
24
25// RouterFromState creates a web router from `state.State`. This exist to
26// bridge between legacy web routers under `State` and new architecture
27func RouterFromState(s *state.State) http.Handler {
28 config, db, enforcer, idResolver, refResolver, indexer, logger, notifier, oauth, pages, validator := s.Expose()
29
30 return Router(
31 logger,
32 config,
33 db,
34 enforcer,
35 idResolver,
36 refResolver,
37 indexer,
38 notifier,
39 oauth,
40 pages,
41 validator,
42 s,
43 )
44}
45
46func Router(
47 // NOTE: put base dependencies (db, idResolver, oauth etc)
48 logger *slog.Logger,
49 config *config.Config,
50 db *db.DB,
51 enforcer *rbac.Enforcer,
52 idResolver *idresolver.Resolver,
53 mentionsResolver *mentions.Resolver,
54 indexer *indexer.Indexer,
55 notifier notify.Notifier,
56 oauth *oauth.OAuth,
57 pages *pages.Pages,
58 validator *validator.Validator,
59 // to use legacy web handlers. will be removed later
60 s *state.State,
61) http.Handler {
62 repo := rsvc.NewService(
63 logger,
64 config,
65 db,
66 enforcer,
67 )
68 issue := isvc.NewService(
69 logger,
70 config,
71 db,
72 enforcer,
73 notifier,
74 idResolver,
75 mentionsResolver,
76 indexer.Issues,
77 validator,
78 )
79
80 i := s.ExposeIssue()
81
82 r := chi.NewRouter()
83
84 mw := s.Middleware()
85 auth := middleware.AuthMiddleware()
86
87 r.Use(middleware.WithLogger(logger))
88 r.Use(middleware.WithSession(oauth))
89
90 r.Use(middleware.Normalize())
91
92 r.Get("/pwa-manifest.json", s.WebAppManifest)
93 r.Get("/robots.txt", s.RobotsTxt)
94
95 r.Handle("/static/*", pages.Static())
96
97 r.Get("/", s.HomeOrTimeline)
98 r.Get("/timeline", s.Timeline)
99 r.Get("/upgradeBanner", s.UpgradeBanner)
100
101 r.Get("/terms", s.TermsOfService)
102 r.Get("/privacy", s.PrivacyPolicy)
103 r.Get("/brand", s.Brand)
104 // special-case handler for serving tangled.org/core
105 r.Get("/core", s.Core())
106
107 r.Get("/login", s.Login)
108 r.Post("/login", s.Login)
109 r.Post("/logout", s.Logout)
110
111 r.Get("/goodfirstissues", s.GoodFirstIssues)
112
113 r.With(auth).Get("/repo/new", s.NewRepo)
114 r.With(auth).Post("/repo/new", s.NewRepo)
115
116 r.With(auth).Post("/follow", s.Follow)
117 r.With(auth).Delete("/follow", s.Follow)
118
119 r.With(auth).Post("/star", s.Star)
120 r.With(auth).Delete("/star", s.Star)
121
122 r.With(auth).Post("/react", s.React)
123 r.With(auth).Delete("/react", s.React)
124
125 r.With(auth).Get("/profile/edit-bio", s.EditBioFragment)
126 r.With(auth).Get("/profile/edit-pins", s.EditPinsFragment)
127 r.With(auth).Post("/profile/bio", s.UpdateProfileBio)
128 r.With(auth).Post("/profile/pins", s.UpdateProfilePins)
129
130 r.Mount("/settings", s.SettingsRouter())
131 r.Mount("/strings", s.StringsRouter(mw))
132 r.Mount("/settings/knots", s.KnotsRouter())
133 r.Mount("/settings/spindles", s.SpindlesRouter())
134 r.Mount("/notifications", s.NotificationsRouter(mw))
135
136 r.Mount("/signup", s.SignupRouter())
137 r.Get("/oauth/client-metadata.json", handler.OauthClientMetadata(oauth))
138 r.Get("/oauth/jwks.json", handler.OauthJwks(oauth))
139 r.Get("/oauth/callback", oauth.Callback)
140
141 // special-case handler. should replace with xrpc later
142 r.Get("/keys/{user}", s.Keys)
143
144 r.HandleFunc("/@*", func(w http.ResponseWriter, r *http.Request) {
145 http.Redirect(w, r, "/"+chi.URLParam(r, "*"), http.StatusFound)
146 })
147
148 r.Route("/{user}", func(r chi.Router) {
149 r.Use(middleware.EnsureDidOrHandle(pages))
150 r.Use(middleware.ResolveIdent(idResolver, pages))
151
152 r.Get("/", s.Profile)
153 r.Get("/feed.atom", s.AtomFeedPage)
154
155 r.Route("/{repo}", func(r chi.Router) {
156 r.Use(middleware.ResolveRepo(db, pages))
157
158 r.Mount("/", s.RepoRouter(mw))
159
160 // /{user}/{repo}/issues/*
161 r.With(middleware.Paginate).Get("/issues", handler.RepoIssues(issue, repo, pages, db))
162 r.With(auth).Get("/issues/new", handler.NewIssue(repo, pages))
163 r.With(auth).Post("/issues/new", handler.NewIssuePost(issue, pages))
164 r.Route("/issues/{issue}", func(r chi.Router) {
165 r.Use(middleware.ResolveIssue(db, pages))
166
167 r.Get("/", handler.Issue(issue, repo, pages, db))
168 r.Get("/opengraph", i.IssueOpenGraphSummary)
169
170 r.With(auth).Delete("/", handler.IssueDelete(issue, pages))
171
172 r.With(auth).Get("/edit", handler.IssueEdit(issue, repo, pages))
173 r.With(auth).Post("/edit", handler.IssueEditPost(issue, pages))
174
175 r.With(auth).Post("/close", handler.CloseIssue(issue, pages))
176 r.With(auth).Post("/reopen", handler.ReopenIssue(issue, pages))
177
178 r.With(auth).Post("/comment", i.NewIssueComment)
179 r.With(auth).Route("/comment/{commentId}/", func(r chi.Router) {
180 r.Get("/", i.IssueComment)
181 r.Delete("/", i.DeleteIssueComment)
182 r.Get("/edit", i.EditIssueComment)
183 r.Post("/edit", i.EditIssueComment)
184 r.Get("/reply", i.ReplyIssueComment)
185 r.Get("/replyPlaceholder", i.ReplyIssueCommentPlaceholder)
186 })
187 })
188
189 r.Mount("/pulls", s.PullsRouter(mw))
190 r.Mount("/pipelines", s.PipelinesRouter())
191 r.Mount("/labels", s.LabelsRouter())
192
193 // These routes get proxied to the knot
194 r.Get("/info/refs", s.InfoRefs)
195 r.Post("/git-upload-pack", s.UploadPack)
196 r.Post("/git-receive-pack", s.ReceivePack)
197 })
198 })
199
200 r.NotFound(func(w http.ResponseWriter, r *http.Request) {
201 pages.Error404(w)
202 })
203
204 return r
205}