+241
-2
appview/middleware/middleware.go
+241
-2
appview/middleware/middleware.go
···
2
2
3
3
import (
4
4
"context"
5
+
"fmt"
5
6
"log"
6
7
"net/http"
8
+
"slices"
7
9
"strconv"
10
+
"strings"
11
+
"time"
8
12
13
+
"github.com/bluesky-social/indigo/atproto/identity"
14
+
"github.com/go-chi/chi/v5"
15
+
"tangled.sh/tangled.sh/core/appview"
16
+
"tangled.sh/tangled.sh/core/appview/db"
9
17
"tangled.sh/tangled.sh/core/appview/oauth"
18
+
"tangled.sh/tangled.sh/core/appview/pages"
10
19
"tangled.sh/tangled.sh/core/appview/pagination"
20
+
"tangled.sh/tangled.sh/core/appview/reporesolver"
21
+
"tangled.sh/tangled.sh/core/rbac"
11
22
)
12
23
13
-
type Middleware func(http.Handler) http.Handler
24
+
type Middleware struct {
25
+
oauth *oauth.OAuth
26
+
db *db.DB
27
+
enforcer rbac.Enforcer
28
+
repoResolver *reporesolver.RepoResolver
29
+
resolver *appview.Resolver
30
+
pages *pages.Pages
31
+
}
32
+
33
+
func New(oauth *oauth.OAuth, db *db.DB, enforcer rbac.Enforcer, repoResolver *reporesolver.RepoResolver, resolver *appview.Resolver, pages *pages.Pages) Middleware {
34
+
return Middleware{
35
+
oauth: oauth,
36
+
db: db,
37
+
enforcer: enforcer,
38
+
repoResolver: repoResolver,
39
+
resolver: resolver,
40
+
pages: pages,
41
+
}
42
+
}
43
+
44
+
type middlewareFunc func(http.Handler) http.Handler
14
45
15
-
func AuthMiddleware(a *oauth.OAuth) Middleware {
46
+
func AuthMiddleware(a *oauth.OAuth) middlewareFunc {
16
47
return func(next http.Handler) http.Handler {
17
48
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
18
49
redirectFunc := func(w http.ResponseWriter, r *http.Request) {
···
71
102
next.ServeHTTP(w, r.WithContext(ctx))
72
103
})
73
104
}
105
+
106
+
func (mw Middleware) knotRoleMiddleware(group string) middlewareFunc {
107
+
return func(next http.Handler) http.Handler {
108
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
109
+
// requires auth also
110
+
actor := mw.oauth.GetUser(r)
111
+
if actor == nil {
112
+
// we need a logged in user
113
+
log.Printf("not logged in, redirecting")
114
+
http.Error(w, "Forbiden", http.StatusUnauthorized)
115
+
return
116
+
}
117
+
domain := chi.URLParam(r, "domain")
118
+
if domain == "" {
119
+
http.Error(w, "malformed url", http.StatusBadRequest)
120
+
return
121
+
}
122
+
123
+
ok, err := mw.enforcer.E.HasGroupingPolicy(actor.Did, group, domain)
124
+
if err != nil || !ok {
125
+
// we need a logged in user
126
+
log.Printf("%s does not have perms of a %s in domain %s", actor.Did, group, domain)
127
+
http.Error(w, "Forbiden", http.StatusUnauthorized)
128
+
return
129
+
}
130
+
131
+
next.ServeHTTP(w, r)
132
+
})
133
+
}
134
+
}
135
+
136
+
func (mw Middleware) KnotOwner() middlewareFunc {
137
+
return mw.knotRoleMiddleware("server:owner")
138
+
}
139
+
140
+
func (mw Middleware) RepoPermissionMiddleware(requiredPerm string) middlewareFunc {
141
+
return func(next http.Handler) http.Handler {
142
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
143
+
// requires auth also
144
+
actor := mw.oauth.GetUser(r)
145
+
if actor == nil {
146
+
// we need a logged in user
147
+
log.Printf("not logged in, redirecting")
148
+
http.Error(w, "Forbiden", http.StatusUnauthorized)
149
+
return
150
+
}
151
+
f, err := mw.repoResolver.Resolve(r)
152
+
if err != nil {
153
+
http.Error(w, "malformed url", http.StatusBadRequest)
154
+
return
155
+
}
156
+
157
+
ok, err := mw.enforcer.E.Enforce(actor.Did, f.Knot, f.DidSlashRepo(), requiredPerm)
158
+
if err != nil || !ok {
159
+
// we need a logged in user
160
+
log.Printf("%s does not have perms of a %s in repo %s", actor.Did, requiredPerm, f.OwnerSlashRepo())
161
+
http.Error(w, "Forbiden", http.StatusUnauthorized)
162
+
return
163
+
}
164
+
165
+
next.ServeHTTP(w, r)
166
+
})
167
+
}
168
+
}
169
+
170
+
func StripLeadingAt(next http.Handler) http.Handler {
171
+
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
172
+
path := req.URL.EscapedPath()
173
+
if strings.HasPrefix(path, "/@") {
174
+
req.URL.RawPath = "/" + strings.TrimPrefix(path, "/@")
175
+
}
176
+
next.ServeHTTP(w, req)
177
+
})
178
+
}
179
+
180
+
func (mw Middleware) ResolveIdent() middlewareFunc {
181
+
excluded := []string{"favicon.ico"}
182
+
183
+
return func(next http.Handler) http.Handler {
184
+
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
185
+
didOrHandle := chi.URLParam(req, "user")
186
+
if slices.Contains(excluded, didOrHandle) {
187
+
next.ServeHTTP(w, req)
188
+
return
189
+
}
190
+
191
+
id, err := mw.resolver.ResolveIdent(req.Context(), didOrHandle)
192
+
if err != nil {
193
+
// invalid did or handle
194
+
log.Println("failed to resolve did/handle:", err)
195
+
w.WriteHeader(http.StatusNotFound)
196
+
return
197
+
}
198
+
199
+
ctx := context.WithValue(req.Context(), "resolvedId", *id)
200
+
201
+
next.ServeHTTP(w, req.WithContext(ctx))
202
+
})
203
+
}
204
+
}
205
+
206
+
func (mw Middleware) ResolveRepo() middlewareFunc {
207
+
return func(next http.Handler) http.Handler {
208
+
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
209
+
repoName := chi.URLParam(req, "repo")
210
+
id, ok := req.Context().Value("resolvedId").(identity.Identity)
211
+
if !ok {
212
+
log.Println("malformed middleware")
213
+
w.WriteHeader(http.StatusInternalServerError)
214
+
return
215
+
}
216
+
217
+
repo, err := db.GetRepo(mw.db, id.DID.String(), repoName)
218
+
if err != nil {
219
+
// invalid did or handle
220
+
log.Println("failed to resolve repo")
221
+
mw.pages.Error404(w)
222
+
return
223
+
}
224
+
225
+
ctx := context.WithValue(req.Context(), "knot", repo.Knot)
226
+
ctx = context.WithValue(ctx, "repoAt", repo.AtUri)
227
+
ctx = context.WithValue(ctx, "repoDescription", repo.Description)
228
+
ctx = context.WithValue(ctx, "repoAddedAt", repo.Created.Format(time.RFC3339))
229
+
next.ServeHTTP(w, req.WithContext(ctx))
230
+
})
231
+
}
232
+
}
233
+
234
+
// middleware that is tacked on top of /{user}/{repo}/pulls/{pull}
235
+
func (mw Middleware) ResolvePull() middlewareFunc {
236
+
return func(next http.Handler) http.Handler {
237
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
238
+
f, err := mw.repoResolver.Resolve(r)
239
+
if err != nil {
240
+
log.Println("failed to fully resolve repo", err)
241
+
http.Error(w, "invalid repo url", http.StatusNotFound)
242
+
return
243
+
}
244
+
245
+
prId := chi.URLParam(r, "pull")
246
+
prIdInt, err := strconv.Atoi(prId)
247
+
if err != nil {
248
+
http.Error(w, "bad pr id", http.StatusBadRequest)
249
+
log.Println("failed to parse pr id", err)
250
+
return
251
+
}
252
+
253
+
pr, err := db.GetPull(mw.db, f.RepoAt, prIdInt)
254
+
if err != nil {
255
+
log.Println("failed to get pull and comments", err)
256
+
return
257
+
}
258
+
259
+
ctx := context.WithValue(r.Context(), "pull", pr)
260
+
261
+
if pr.IsStacked() {
262
+
stack, err := db.GetStack(mw.db, pr.StackId)
263
+
if err != nil {
264
+
log.Println("failed to get stack", err)
265
+
return
266
+
}
267
+
abandonedPulls, err := db.GetAbandonedPulls(mw.db, pr.StackId)
268
+
if err != nil {
269
+
log.Println("failed to get abandoned pulls", err)
270
+
return
271
+
}
272
+
273
+
ctx = context.WithValue(ctx, "stack", stack)
274
+
ctx = context.WithValue(ctx, "abandonedPulls", abandonedPulls)
275
+
}
276
+
277
+
next.ServeHTTP(w, r.WithContext(ctx))
278
+
})
279
+
}
280
+
}
281
+
282
+
// this should serve the go-import meta tag even if the path is technically
283
+
// a 404 like tangled.sh/oppi.li/go-git/v5
284
+
func (mw Middleware) GoImport() middlewareFunc {
285
+
return func(next http.Handler) http.Handler {
286
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
287
+
f, err := mw.repoResolver.Resolve(r)
288
+
if err != nil {
289
+
log.Println("failed to fully resolve repo", err)
290
+
http.Error(w, "invalid repo url", http.StatusNotFound)
291
+
return
292
+
}
293
+
294
+
fullName := f.OwnerHandle() + "/" + f.RepoName
295
+
296
+
if r.Header.Get("User-Agent") == "Go-http-client/1.1" {
297
+
if r.URL.Query().Get("go-get") == "1" {
298
+
html := fmt.Sprintf(
299
+
`<meta name="go-import" content="tangled.sh/%s git https://tangled.sh/%s"/>`,
300
+
fullName,
301
+
fullName,
302
+
)
303
+
w.Header().Set("Content-Type", "text/html")
304
+
w.Write([]byte(html))
305
+
return
306
+
}
307
+
}
308
+
309
+
next.ServeHTTP(w, r)
310
+
})
311
+
}
312
+
}
-226
appview/state/middleware.go
-226
appview/state/middleware.go
···
1
-
package state
2
-
3
-
import (
4
-
"context"
5
-
"fmt"
6
-
"log"
7
-
"net/http"
8
-
"strconv"
9
-
"strings"
10
-
"time"
11
-
12
-
"slices"
13
-
14
-
"github.com/bluesky-social/indigo/atproto/identity"
15
-
"github.com/go-chi/chi/v5"
16
-
"tangled.sh/tangled.sh/core/appview/db"
17
-
"tangled.sh/tangled.sh/core/appview/middleware"
18
-
)
19
-
20
-
func knotRoleMiddleware(s *State, group string) middleware.Middleware {
21
-
return func(next http.Handler) http.Handler {
22
-
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
23
-
// requires auth also
24
-
actor := s.oauth.GetUser(r)
25
-
if actor == nil {
26
-
// we need a logged in user
27
-
log.Printf("not logged in, redirecting")
28
-
http.Error(w, "Forbiden", http.StatusUnauthorized)
29
-
return
30
-
}
31
-
domain := chi.URLParam(r, "domain")
32
-
if domain == "" {
33
-
http.Error(w, "malformed url", http.StatusBadRequest)
34
-
return
35
-
}
36
-
37
-
ok, err := s.enforcer.E.HasGroupingPolicy(actor.Did, group, domain)
38
-
if err != nil || !ok {
39
-
// we need a logged in user
40
-
log.Printf("%s does not have perms of a %s in domain %s", actor.Did, group, domain)
41
-
http.Error(w, "Forbiden", http.StatusUnauthorized)
42
-
return
43
-
}
44
-
45
-
next.ServeHTTP(w, r)
46
-
})
47
-
}
48
-
}
49
-
50
-
func KnotOwner(s *State) middleware.Middleware {
51
-
return knotRoleMiddleware(s, "server:owner")
52
-
}
53
-
54
-
func RepoPermissionMiddleware(s *State, requiredPerm string) middleware.Middleware {
55
-
return func(next http.Handler) http.Handler {
56
-
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
57
-
// requires auth also
58
-
actor := s.oauth.GetUser(r)
59
-
if actor == nil {
60
-
// we need a logged in user
61
-
log.Printf("not logged in, redirecting")
62
-
http.Error(w, "Forbiden", http.StatusUnauthorized)
63
-
return
64
-
}
65
-
f, err := s.repoResolver.Resolve(r)
66
-
if err != nil {
67
-
http.Error(w, "malformed url", http.StatusBadRequest)
68
-
return
69
-
}
70
-
71
-
ok, err := s.enforcer.E.Enforce(actor.Did, f.Knot, f.DidSlashRepo(), requiredPerm)
72
-
if err != nil || !ok {
73
-
// we need a logged in user
74
-
log.Printf("%s does not have perms of a %s in repo %s", actor.Did, requiredPerm, f.OwnerSlashRepo())
75
-
http.Error(w, "Forbiden", http.StatusUnauthorized)
76
-
return
77
-
}
78
-
79
-
next.ServeHTTP(w, r)
80
-
})
81
-
}
82
-
}
83
-
84
-
func StripLeadingAt(next http.Handler) http.Handler {
85
-
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
86
-
path := req.URL.EscapedPath()
87
-
if strings.HasPrefix(path, "/@") {
88
-
req.URL.RawPath = "/" + strings.TrimPrefix(path, "/@")
89
-
}
90
-
next.ServeHTTP(w, req)
91
-
})
92
-
}
93
-
94
-
func ResolveIdent(s *State) middleware.Middleware {
95
-
excluded := []string{"favicon.ico"}
96
-
97
-
return func(next http.Handler) http.Handler {
98
-
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
99
-
didOrHandle := chi.URLParam(req, "user")
100
-
if slices.Contains(excluded, didOrHandle) {
101
-
next.ServeHTTP(w, req)
102
-
return
103
-
}
104
-
105
-
id, err := s.resolver.ResolveIdent(req.Context(), didOrHandle)
106
-
if err != nil {
107
-
// invalid did or handle
108
-
log.Println("failed to resolve did/handle:", err)
109
-
w.WriteHeader(http.StatusNotFound)
110
-
return
111
-
}
112
-
113
-
ctx := context.WithValue(req.Context(), "resolvedId", *id)
114
-
115
-
next.ServeHTTP(w, req.WithContext(ctx))
116
-
})
117
-
}
118
-
}
119
-
120
-
func ResolveRepo(s *State) middleware.Middleware {
121
-
return func(next http.Handler) http.Handler {
122
-
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
123
-
repoName := chi.URLParam(req, "repo")
124
-
id, ok := req.Context().Value("resolvedId").(identity.Identity)
125
-
if !ok {
126
-
log.Println("malformed middleware")
127
-
w.WriteHeader(http.StatusInternalServerError)
128
-
return
129
-
}
130
-
131
-
repo, err := db.GetRepo(s.db, id.DID.String(), repoName)
132
-
if err != nil {
133
-
// invalid did or handle
134
-
log.Println("failed to resolve repo")
135
-
s.pages.Error404(w)
136
-
return
137
-
}
138
-
139
-
ctx := context.WithValue(req.Context(), "knot", repo.Knot)
140
-
ctx = context.WithValue(ctx, "repoAt", repo.AtUri)
141
-
ctx = context.WithValue(ctx, "repoDescription", repo.Description)
142
-
ctx = context.WithValue(ctx, "repoAddedAt", repo.Created.Format(time.RFC3339))
143
-
next.ServeHTTP(w, req.WithContext(ctx))
144
-
})
145
-
}
146
-
}
147
-
148
-
// middleware that is tacked on top of /{user}/{repo}/pulls/{pull}
149
-
func ResolvePull(s *State) middleware.Middleware {
150
-
return func(next http.Handler) http.Handler {
151
-
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
152
-
f, err := s.repoResolver.Resolve(r)
153
-
if err != nil {
154
-
log.Println("failed to fully resolve repo", err)
155
-
http.Error(w, "invalid repo url", http.StatusNotFound)
156
-
return
157
-
}
158
-
159
-
prId := chi.URLParam(r, "pull")
160
-
prIdInt, err := strconv.Atoi(prId)
161
-
if err != nil {
162
-
http.Error(w, "bad pr id", http.StatusBadRequest)
163
-
log.Println("failed to parse pr id", err)
164
-
return
165
-
}
166
-
167
-
pr, err := db.GetPull(s.db, f.RepoAt, prIdInt)
168
-
if err != nil {
169
-
log.Println("failed to get pull and comments", err)
170
-
return
171
-
}
172
-
173
-
ctx := context.WithValue(r.Context(), "pull", pr)
174
-
175
-
if pr.IsStacked() {
176
-
stack, err := db.GetStack(s.db, pr.StackId)
177
-
if err != nil {
178
-
log.Println("failed to get stack", err)
179
-
return
180
-
}
181
-
abandonedPulls, err := db.GetAbandonedPulls(s.db, pr.StackId)
182
-
if err != nil {
183
-
log.Println("failed to get abandoned pulls", err)
184
-
return
185
-
}
186
-
187
-
ctx = context.WithValue(ctx, "stack", stack)
188
-
ctx = context.WithValue(ctx, "abandonedPulls", abandonedPulls)
189
-
}
190
-
191
-
next.ServeHTTP(w, r.WithContext(ctx))
192
-
})
193
-
}
194
-
}
195
-
196
-
// this should serve the go-import meta tag even if the path is technically
197
-
// a 404 like tangled.sh/oppi.li/go-git/v5
198
-
func GoImport(s *State) middleware.Middleware {
199
-
return func(next http.Handler) http.Handler {
200
-
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
201
-
f, err := s.repoResolver.Resolve(r)
202
-
if err != nil {
203
-
log.Println("failed to fully resolve repo", err)
204
-
http.Error(w, "invalid repo url", http.StatusNotFound)
205
-
return
206
-
}
207
-
208
-
fullName := f.OwnerHandle() + "/" + f.RepoName
209
-
210
-
if r.Header.Get("User-Agent") == "Go-http-client/1.1" {
211
-
if r.URL.Query().Get("go-get") == "1" {
212
-
html := fmt.Sprintf(
213
-
`<meta name="go-import" content="tangled.sh/%s git https://tangled.sh/%s"/>`,
214
-
fullName,
215
-
fullName,
216
-
)
217
-
w.Header().Set("Content-Type", "text/html")
218
-
w.Write([]byte(html))
219
-
return
220
-
}
221
-
}
222
-
223
-
next.ServeHTTP(w, r)
224
-
})
225
-
}
226
-
}
+25
-17
appview/state/router.go
+25
-17
appview/state/router.go
···
14
14
15
15
func (s *State) Router() http.Handler {
16
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
+
)
17
25
18
26
router.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) {
19
27
pat := chi.URLParam(r, "*")
20
28
if strings.HasPrefix(pat, "did:") || strings.HasPrefix(pat, "@") {
21
-
s.UserRouter().ServeHTTP(w, r)
29
+
s.UserRouter(&middleware).ServeHTTP(w, r)
22
30
} else {
23
31
// Check if the first path element is a valid handle without '@' or a flattened DID
24
32
pathParts := strings.SplitN(pat, "/", 2)
···
41
49
return
42
50
}
43
51
}
44
-
s.StandardRouter().ServeHTTP(w, r)
52
+
s.StandardRouter(&middleware).ServeHTTP(w, r)
45
53
}
46
54
})
47
55
48
56
return router
49
57
}
50
58
51
-
func (s *State) UserRouter() http.Handler {
59
+
func (s *State) UserRouter(mw *middleware.Middleware) http.Handler {
52
60
r := chi.NewRouter()
53
61
54
62
// strip @ from user
55
-
r.Use(StripLeadingAt)
63
+
r.Use(middleware.StripLeadingAt)
56
64
57
-
r.With(ResolveIdent(s)).Route("/{user}", func(r chi.Router) {
65
+
r.With(mw.ResolveIdent()).Route("/{user}", func(r chi.Router) {
58
66
r.Get("/", s.Profile)
59
67
60
-
r.With(ResolveRepo(s)).Route("/{repo}", func(r chi.Router) {
61
-
r.Use(GoImport(s))
68
+
r.With(mw.ResolveRepo()).Route("/{repo}", func(r chi.Router) {
69
+
r.Use(mw.GoImport())
62
70
63
71
r.Get("/", s.RepoIndex)
64
72
r.Get("/commits/{ref}", s.RepoLog)
···
80
88
// additionally: only the uploader can truly delete an artifact
81
89
// (record+blob will live on their pds)
82
90
r.Group(func(r chi.Router) {
83
-
r.With(RepoPermissionMiddleware(s, "repo:push"))
91
+
r.With(mw.RepoPermissionMiddleware("repo:push"))
84
92
r.Post("/upload", s.AttachArtifact)
85
93
r.Delete("/{file}", s.DeleteArtifact)
86
94
})
···
113
121
r.Use(middleware.AuthMiddleware(s.oauth))
114
122
r.Get("/", s.ForkRepo)
115
123
r.Post("/", s.ForkRepo)
116
-
r.With(RepoPermissionMiddleware(s, "repo:owner")).Route("/sync", func(r chi.Router) {
124
+
r.With(mw.RepoPermissionMiddleware("repo:owner")).Route("/sync", func(r chi.Router) {
117
125
r.Post("/", s.SyncRepoFork)
118
126
})
119
127
})
···
143
151
})
144
152
145
153
r.Route("/{pull}", func(r chi.Router) {
146
-
r.Use(ResolvePull(s))
154
+
r.Use(mw.ResolvePull())
147
155
r.Get("/", s.RepoSinglePull)
148
156
149
157
r.Route("/round/{round}", func(r chi.Router) {
···
170
178
r.Post("/reopen", s.ReopenPull)
171
179
// collaborators only
172
180
r.Group(func(r chi.Router) {
173
-
r.Use(RepoPermissionMiddleware(s, "repo:push"))
181
+
r.Use(mw.RepoPermissionMiddleware("repo:push"))
174
182
r.Post("/merge", s.MergePull)
175
183
// maybe lock, etc.
176
184
})
···
187
195
r.Group(func(r chi.Router) {
188
196
r.Use(middleware.AuthMiddleware(s.oauth))
189
197
// repo description can only be edited by owner
190
-
r.With(RepoPermissionMiddleware(s, "repo:owner")).Route("/description", func(r chi.Router) {
198
+
r.With(mw.RepoPermissionMiddleware("repo:owner")).Route("/description", func(r chi.Router) {
191
199
r.Put("/", s.RepoDescription)
192
200
r.Get("/", s.RepoDescription)
193
201
r.Get("/edit", s.RepoDescriptionEdit)
194
202
})
195
-
r.With(RepoPermissionMiddleware(s, "repo:settings")).Route("/settings", func(r chi.Router) {
203
+
r.With(mw.RepoPermissionMiddleware("repo:settings")).Route("/settings", func(r chi.Router) {
196
204
r.Get("/", s.RepoSettings)
197
-
r.With(RepoPermissionMiddleware(s, "repo:invite")).Put("/collaborator", s.AddCollaborator)
198
-
r.With(RepoPermissionMiddleware(s, "repo:delete")).Delete("/delete", s.DeleteRepo)
205
+
r.With(mw.RepoPermissionMiddleware("repo:invite")).Put("/collaborator", s.AddCollaborator)
206
+
r.With(mw.RepoPermissionMiddleware("repo:delete")).Delete("/delete", s.DeleteRepo)
199
207
r.Put("/branches/default", s.SetDefaultBranch)
200
208
})
201
209
})
···
209
217
return r
210
218
}
211
219
212
-
func (s *State) StandardRouter() http.Handler {
220
+
func (s *State) StandardRouter(mw *middleware.Middleware) http.Handler {
213
221
r := chi.NewRouter()
214
222
215
223
r.Handle("/static/*", s.pages.Static())
···
227
235
r.Post("/init", s.InitKnotServer)
228
236
r.Get("/", s.KnotServerInfo)
229
237
r.Route("/member", func(r chi.Router) {
230
-
r.Use(KnotOwner(s))
238
+
r.Use(mw.KnotOwner())
231
239
r.Get("/", s.ListMembers)
232
240
r.Put("/", s.AddMember)
233
241
r.Delete("/", s.RemoveMember)