forked from tangled.org/core
Monorepo for Tangled

appview: repo: refactor into its own package

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.sh>

anirudh.fi 32ef8cbb 26228e3f

verified
Changed files
+460 -390
appview
+16 -2
appview/pulls/pulls.go
··· 43 43 posthog posthog.Client 44 44 } 45 45 46 - func New(oauth *oauth.OAuth, repoResolver *reporesolver.RepoResolver, pages *pages.Pages, resolver *idresolver.Resolver, db *db.DB, config *appview.Config) *Pulls { 47 - return &Pulls{oauth: oauth, repoResolver: repoResolver, pages: pages, idResolver: resolver, db: db, config: config} 46 + func New( 47 + oauth *oauth.OAuth, 48 + repoResolver *reporesolver.RepoResolver, 49 + pages *pages.Pages, 50 + resolver *idresolver.Resolver, 51 + db *db.DB, 52 + config *appview.Config, 53 + ) *Pulls { 54 + return &Pulls{ 55 + oauth: oauth, 56 + repoResolver: repoResolver, 57 + pages: pages, 58 + idResolver: resolver, 59 + db: db, 60 + config: config, 61 + } 48 62 } 49 63 50 64 // htmx fragment
+100
appview/repo/router.go
··· 1 + package repo 2 + 3 + import ( 4 + "net/http" 5 + 6 + "github.com/go-chi/chi/v5" 7 + "tangled.sh/tangled.sh/core/appview/middleware" 8 + ) 9 + 10 + func (rp *Repo) Router(mw *middleware.Middleware) http.Handler { 11 + r := chi.NewRouter() 12 + r.Get("/", rp.RepoIndex) 13 + r.Get("/commits/{ref}", rp.RepoLog) 14 + r.Route("/tree/{ref}", func(r chi.Router) { 15 + r.Get("/", rp.RepoIndex) 16 + r.Get("/*", rp.RepoTree) 17 + }) 18 + r.Get("/commit/{ref}", rp.RepoCommit) 19 + r.Get("/branches", rp.RepoBranches) 20 + r.Route("/tags", func(r chi.Router) { 21 + r.Get("/", rp.RepoTags) 22 + r.Route("/{tag}", func(r chi.Router) { 23 + r.Use(middleware.AuthMiddleware(rp.oauth)) 24 + // require auth to download for now 25 + r.Get("/download/{file}", rp.DownloadArtifact) 26 + 27 + // require repo:push to upload or delete artifacts 28 + // 29 + // additionally: only the uploader can truly delete an artifact 30 + // (record+blob will live on their pds) 31 + r.Group(func(r chi.Router) { 32 + r.With(mw.RepoPermissionMiddleware("repo:push")) 33 + r.Post("/upload", rp.AttachArtifact) 34 + r.Delete("/{file}", rp.DeleteArtifact) 35 + }) 36 + }) 37 + }) 38 + r.Get("/blob/{ref}/*", rp.RepoBlob) 39 + r.Get("/raw/{ref}/*", rp.RepoBlobRaw) 40 + 41 + r.Route("/issues", func(r chi.Router) { 42 + r.With(middleware.Paginate).Get("/", rp.RepoIssues) 43 + r.Get("/{issue}", rp.RepoSingleIssue) 44 + 45 + r.Group(func(r chi.Router) { 46 + r.Use(middleware.AuthMiddleware(rp.oauth)) 47 + r.Get("/new", rp.NewIssue) 48 + r.Post("/new", rp.NewIssue) 49 + r.Post("/{issue}/comment", rp.NewIssueComment) 50 + r.Route("/{issue}/comment/{comment_id}/", func(r chi.Router) { 51 + r.Get("/", rp.IssueComment) 52 + r.Delete("/", rp.DeleteIssueComment) 53 + r.Get("/edit", rp.EditIssueComment) 54 + r.Post("/edit", rp.EditIssueComment) 55 + }) 56 + r.Post("/{issue}/close", rp.CloseIssue) 57 + r.Post("/{issue}/reopen", rp.ReopenIssue) 58 + }) 59 + }) 60 + 61 + r.Route("/fork", func(r chi.Router) { 62 + r.Use(middleware.AuthMiddleware(rp.oauth)) 63 + r.Get("/", rp.ForkRepo) 64 + r.Post("/", rp.ForkRepo) 65 + r.With(mw.RepoPermissionMiddleware("repo:owner")).Route("/sync", func(r chi.Router) { 66 + r.Post("/", rp.SyncRepoFork) 67 + }) 68 + }) 69 + 70 + r.Route("/compare", func(r chi.Router) { 71 + r.Get("/", rp.RepoCompareNew) // start an new comparison 72 + 73 + // we have to wildcard here since we want to support GitHub's compare syntax 74 + // /compare/{ref1}...{ref2} 75 + // for example: 76 + // /compare/master...some/feature 77 + // /compare/master...example.com:another/feature <- this is a fork 78 + r.Get("/{base}/{head}", rp.RepoCompare) 79 + r.Get("/*", rp.RepoCompare) 80 + }) 81 + 82 + // settings routes, needs auth 83 + r.Group(func(r chi.Router) { 84 + r.Use(middleware.AuthMiddleware(rp.oauth)) 85 + // repo description can only be edited by owner 86 + r.With(mw.RepoPermissionMiddleware("repo:owner")).Route("/description", func(r chi.Router) { 87 + r.Put("/", rp.RepoDescription) 88 + r.Get("/", rp.RepoDescription) 89 + r.Get("/edit", rp.RepoDescriptionEdit) 90 + }) 91 + r.With(mw.RepoPermissionMiddleware("repo:settings")).Route("/settings", func(r chi.Router) { 92 + r.Get("/", rp.RepoSettings) 93 + r.With(mw.RepoPermissionMiddleware("repo:invite")).Put("/collaborator", rp.AddCollaborator) 94 + r.With(mw.RepoPermissionMiddleware("repo:delete")).Delete("/delete", rp.DeleteRepo) 95 + r.Put("/branches/default", rp.SetDefaultBranch) 96 + }) 97 + }) 98 + 99 + return r 100 + }
+38 -38
appview/state/artifact.go appview/repo/artifact.go
··· 1 - package state 1 + package repo 2 2 3 3 import ( 4 4 "fmt" ··· 23 23 ) 24 24 25 25 // TODO: proper statuses here on early exit 26 - func (s *State) AttachArtifact(w http.ResponseWriter, r *http.Request) { 27 - user := s.oauth.GetUser(r) 26 + func (rp *Repo) AttachArtifact(w http.ResponseWriter, r *http.Request) { 27 + user := rp.oauth.GetUser(r) 28 28 tagParam := chi.URLParam(r, "tag") 29 - f, err := s.repoResolver.Resolve(r) 29 + f, err := rp.repoResolver.Resolve(r) 30 30 if err != nil { 31 31 log.Println("failed to get repo and knot", err) 32 - s.pages.Notice(w, "upload", "failed to upload artifact, error in repo resolution") 32 + rp.pages.Notice(w, "upload", "failed to upload artifact, error in repo resolution") 33 33 return 34 34 } 35 35 36 - tag, err := s.resolveTag(f, tagParam) 36 + tag, err := rp.resolveTag(f, tagParam) 37 37 if err != nil { 38 38 log.Println("failed to resolve tag", err) 39 - s.pages.Notice(w, "upload", "failed to upload artifact, error in tag resolution") 39 + rp.pages.Notice(w, "upload", "failed to upload artifact, error in tag resolution") 40 40 return 41 41 } 42 42 43 43 file, handler, err := r.FormFile("artifact") 44 44 if err != nil { 45 45 log.Println("failed to upload artifact", err) 46 - s.pages.Notice(w, "upload", "failed to upload artifact") 46 + rp.pages.Notice(w, "upload", "failed to upload artifact") 47 47 return 48 48 } 49 49 defer file.Close() 50 50 51 - client, err := s.oauth.AuthorizedClient(r) 51 + client, err := rp.oauth.AuthorizedClient(r) 52 52 if err != nil { 53 53 log.Println("failed to get authorized client", err) 54 - s.pages.Notice(w, "upload", "failed to get authorized client") 54 + rp.pages.Notice(w, "upload", "failed to get authorized client") 55 55 return 56 56 } 57 57 58 58 uploadBlobResp, err := client.RepoUploadBlob(r.Context(), file) 59 59 if err != nil { 60 60 log.Println("failed to upload blob", err) 61 - s.pages.Notice(w, "upload", "Failed to upload blob to your PDS. Try again later.") 61 + rp.pages.Notice(w, "upload", "Failed to upload blob to your PDS. Try again later.") 62 62 return 63 63 } 64 64 ··· 83 83 }) 84 84 if err != nil { 85 85 log.Println("failed to create record", err) 86 - s.pages.Notice(w, "upload", "Failed to create artifact record. Try again later.") 86 + rp.pages.Notice(w, "upload", "Failed to create artifact record. Try again later.") 87 87 return 88 88 } 89 89 90 90 log.Println(putRecordResp.Uri) 91 91 92 - tx, err := s.db.BeginTx(r.Context(), nil) 92 + tx, err := rp.db.BeginTx(r.Context(), nil) 93 93 if err != nil { 94 94 log.Println("failed to start tx") 95 - s.pages.Notice(w, "upload", "Failed to create artifact. Try again later.") 95 + rp.pages.Notice(w, "upload", "Failed to create artifact. Try again later.") 96 96 return 97 97 } 98 98 defer tx.Rollback() ··· 112 112 err = db.AddArtifact(tx, artifact) 113 113 if err != nil { 114 114 log.Println("failed to add artifact record to db", err) 115 - s.pages.Notice(w, "upload", "Failed to create artifact. Try again later.") 115 + rp.pages.Notice(w, "upload", "Failed to create artifact. Try again later.") 116 116 return 117 117 } 118 118 119 119 err = tx.Commit() 120 120 if err != nil { 121 121 log.Println("failed to add artifact record to db") 122 - s.pages.Notice(w, "upload", "Failed to create artifact. Try again later.") 122 + rp.pages.Notice(w, "upload", "Failed to create artifact. Try again later.") 123 123 return 124 124 } 125 125 126 - s.pages.RepoArtifactFragment(w, pages.RepoArtifactParams{ 126 + rp.pages.RepoArtifactFragment(w, pages.RepoArtifactParams{ 127 127 LoggedInUser: user, 128 128 RepoInfo: f.RepoInfo(user), 129 129 Artifact: artifact, ··· 131 131 } 132 132 133 133 // TODO: proper statuses here on early exit 134 - func (s *State) DownloadArtifact(w http.ResponseWriter, r *http.Request) { 134 + func (rp *Repo) DownloadArtifact(w http.ResponseWriter, r *http.Request) { 135 135 tagParam := chi.URLParam(r, "tag") 136 136 filename := chi.URLParam(r, "file") 137 - f, err := s.repoResolver.Resolve(r) 137 + f, err := rp.repoResolver.Resolve(r) 138 138 if err != nil { 139 139 log.Println("failed to get repo and knot", err) 140 140 return 141 141 } 142 142 143 - tag, err := s.resolveTag(f, tagParam) 143 + tag, err := rp.resolveTag(f, tagParam) 144 144 if err != nil { 145 145 log.Println("failed to resolve tag", err) 146 - s.pages.Notice(w, "upload", "failed to upload artifact, error in tag resolution") 146 + rp.pages.Notice(w, "upload", "failed to upload artifact, error in tag resolution") 147 147 return 148 148 } 149 149 150 - client, err := s.oauth.AuthorizedClient(r) 150 + client, err := rp.oauth.AuthorizedClient(r) 151 151 if err != nil { 152 152 log.Println("failed to get authorized client", err) 153 153 return 154 154 } 155 155 156 156 artifacts, err := db.GetArtifact( 157 - s.db, 157 + rp.db, 158 158 db.FilterEq("repo_at", f.RepoAt), 159 159 db.FilterEq("tag", tag.Tag.Hash[:]), 160 160 db.FilterEq("name", filename), ··· 181 181 } 182 182 183 183 // TODO: proper statuses here on early exit 184 - func (s *State) DeleteArtifact(w http.ResponseWriter, r *http.Request) { 185 - user := s.oauth.GetUser(r) 184 + func (rp *Repo) DeleteArtifact(w http.ResponseWriter, r *http.Request) { 185 + user := rp.oauth.GetUser(r) 186 186 tagParam := chi.URLParam(r, "tag") 187 187 filename := chi.URLParam(r, "file") 188 - f, err := s.repoResolver.Resolve(r) 188 + f, err := rp.repoResolver.Resolve(r) 189 189 if err != nil { 190 190 log.Println("failed to get repo and knot", err) 191 191 return 192 192 } 193 193 194 - client, _ := s.oauth.AuthorizedClient(r) 194 + client, _ := rp.oauth.AuthorizedClient(r) 195 195 196 196 tag := plumbing.NewHash(tagParam) 197 197 198 198 artifacts, err := db.GetArtifact( 199 - s.db, 199 + rp.db, 200 200 db.FilterEq("repo_at", f.RepoAt), 201 201 db.FilterEq("tag", tag[:]), 202 202 db.FilterEq("name", filename), 203 203 ) 204 204 if err != nil { 205 205 log.Println("failed to get artifacts", err) 206 - s.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.") 206 + rp.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.") 207 207 return 208 208 } 209 209 if len(artifacts) != 1 { 210 - s.pages.Notice(w, "remove", "Unable to find artifact.") 210 + rp.pages.Notice(w, "remove", "Unable to find artifact.") 211 211 return 212 212 } 213 213 ··· 215 215 216 216 if user.Did != artifact.Did { 217 217 log.Println("user not authorized to delete artifact", err) 218 - s.pages.Notice(w, "remove", "Unauthorized deletion of artifact.") 218 + rp.pages.Notice(w, "remove", "Unauthorized deletion of artifact.") 219 219 return 220 220 } 221 221 ··· 226 226 }) 227 227 if err != nil { 228 228 log.Println("failed to get blob from pds", err) 229 - s.pages.Notice(w, "remove", "Failed to remove blob from PDS.") 229 + rp.pages.Notice(w, "remove", "Failed to remove blob from PDS.") 230 230 return 231 231 } 232 232 233 - tx, err := s.db.BeginTx(r.Context(), nil) 233 + tx, err := rp.db.BeginTx(r.Context(), nil) 234 234 if err != nil { 235 235 log.Println("failed to start tx") 236 - s.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.") 236 + rp.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.") 237 237 return 238 238 } 239 239 defer tx.Rollback() ··· 245 245 ) 246 246 if err != nil { 247 247 log.Println("failed to remove artifact record from db", err) 248 - s.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.") 248 + rp.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.") 249 249 return 250 250 } 251 251 252 252 err = tx.Commit() 253 253 if err != nil { 254 254 log.Println("failed to remove artifact record from db") 255 - s.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.") 255 + rp.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.") 256 256 return 257 257 } 258 258 259 259 w.Write([]byte{}) 260 260 } 261 261 262 - func (s *State) resolveTag(f *reporesolver.ResolvedRepo, tagParam string) (*types.TagReference, error) { 262 + func (rp *Repo) resolveTag(f *reporesolver.ResolvedRepo, tagParam string) (*types.TagReference, error) { 263 263 tagParam, err := url.QueryUnescape(tagParam) 264 264 if err != nil { 265 265 return nil, err 266 266 } 267 267 268 - us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev) 268 + us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev) 269 269 if err != nil { 270 270 return nil, err 271 271 }
+295 -261
appview/state/repo.go appview/repo/repo.go
··· 1 - package state 1 + package repo 2 2 3 3 import ( 4 4 "database/sql" ··· 19 19 "tangled.sh/tangled.sh/core/api/tangled" 20 20 "tangled.sh/tangled.sh/core/appview" 21 21 "tangled.sh/tangled.sh/core/appview/db" 22 + "tangled.sh/tangled.sh/core/appview/idresolver" 22 23 "tangled.sh/tangled.sh/core/appview/oauth" 23 24 "tangled.sh/tangled.sh/core/appview/pages" 24 25 "tangled.sh/tangled.sh/core/appview/pages/markup" ··· 27 28 "tangled.sh/tangled.sh/core/appview/reporesolver" 28 29 "tangled.sh/tangled.sh/core/knotclient" 29 30 "tangled.sh/tangled.sh/core/patchutil" 31 + "tangled.sh/tangled.sh/core/rbac" 30 32 "tangled.sh/tangled.sh/core/types" 31 33 32 34 "github.com/bluesky-social/indigo/atproto/data" ··· 39 41 lexutil "github.com/bluesky-social/indigo/lex/util" 40 42 ) 41 43 42 - func (s *State) RepoIndex(w http.ResponseWriter, r *http.Request) { 44 + type Repo struct { 45 + repoResolver *reporesolver.RepoResolver 46 + idResolver *idresolver.Resolver 47 + config *appview.Config 48 + oauth *oauth.OAuth 49 + pages *pages.Pages 50 + db *db.DB 51 + enforcer *rbac.Enforcer 52 + posthog posthog.Client 53 + } 54 + 55 + func New( 56 + oauth *oauth.OAuth, 57 + repoResolver *reporesolver.RepoResolver, 58 + pages *pages.Pages, 59 + idResolver *idresolver.Resolver, 60 + db *db.DB, 61 + config *appview.Config, 62 + posthog posthog.Client, 63 + enforcer *rbac.Enforcer, 64 + ) *Repo { 65 + return &Repo{oauth: oauth, 66 + repoResolver: repoResolver, 67 + pages: pages, 68 + idResolver: idResolver, 69 + config: config, 70 + db: db, 71 + posthog: posthog, 72 + enforcer: enforcer, 73 + } 74 + } 75 + 76 + func (rp *Repo) RepoIndex(w http.ResponseWriter, r *http.Request) { 43 77 ref := chi.URLParam(r, "ref") 44 - f, err := s.repoResolver.Resolve(r) 78 + f, err := rp.repoResolver.Resolve(r) 45 79 if err != nil { 46 80 log.Println("failed to fully resolve repo", err) 47 81 return 48 82 } 49 83 50 - us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev) 84 + us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev) 51 85 if err != nil { 52 86 log.Printf("failed to create unsigned client for %s", f.Knot) 53 - s.pages.Error503(w) 87 + rp.pages.Error503(w) 54 88 return 55 89 } 56 90 57 91 result, err := us.Index(f.OwnerDid(), f.RepoName, ref) 58 92 if err != nil { 59 - s.pages.Error503(w) 93 + rp.pages.Error503(w) 60 94 log.Println("failed to reach knotserver", err) 61 95 return 62 96 } ··· 107 141 108 142 emails := uniqueEmails(commitsTrunc) 109 143 110 - user := s.oauth.GetUser(r) 144 + user := rp.oauth.GetUser(r) 111 145 repoInfo := f.RepoInfo(user) 112 146 113 - secret, err := db.GetRegistrationKey(s.db, f.Knot) 147 + secret, err := db.GetRegistrationKey(rp.db, f.Knot) 114 148 if err != nil { 115 149 log.Printf("failed to get registration key for %s: %s", f.Knot, err) 116 - s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 150 + rp.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 117 151 } 118 152 119 - signedClient, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev) 153 + signedClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev) 120 154 if err != nil { 121 155 log.Printf("failed to create signed client for %s: %s", f.Knot, err) 122 156 return ··· 124 158 125 159 var forkInfo *types.ForkInfo 126 160 if user != nil && (repoInfo.Roles.IsOwner() || repoInfo.Roles.IsCollaborator()) { 127 - forkInfo, err = getForkInfo(repoInfo, s, f, user, signedClient) 161 + forkInfo, err = getForkInfo(repoInfo, rp, f, user, signedClient) 128 162 if err != nil { 129 163 log.Printf("Failed to fetch fork information: %v", err) 130 164 return ··· 137 171 // non-fatal 138 172 } 139 173 140 - s.pages.RepoIndexPage(w, pages.RepoIndexParams{ 174 + rp.pages.RepoIndexPage(w, pages.RepoIndexParams{ 141 175 LoggedInUser: user, 142 176 RepoInfo: repoInfo, 143 177 TagMap: tagMap, ··· 146 180 TagsTrunc: tagsTrunc, 147 181 ForkInfo: forkInfo, 148 182 BranchesTrunc: branchesTrunc, 149 - EmailToDidOrHandle: EmailToDidOrHandle(s, emails), 183 + EmailToDidOrHandle: EmailToDidOrHandle(rp, emails), 150 184 Languages: repoLanguages, 151 185 }) 152 186 return ··· 154 188 155 189 func getForkInfo( 156 190 repoInfo repoinfo.RepoInfo, 157 - s *State, 191 + rp *Repo, 158 192 f *reporesolver.ResolvedRepo, 159 193 user *oauth.User, 160 194 signedClient *knotclient.SignedClient, ··· 173 207 return &forkInfo, nil 174 208 } 175 209 176 - us, err := knotclient.NewUnsignedClient(repoInfo.Source.Knot, s.config.Core.Dev) 210 + us, err := knotclient.NewUnsignedClient(repoInfo.Source.Knot, rp.config.Core.Dev) 177 211 if err != nil { 178 212 log.Printf("failed to create unsigned client for %s", repoInfo.Source.Knot) 179 213 return nil, err ··· 216 250 return &forkInfo, nil 217 251 } 218 252 219 - func (s *State) RepoLog(w http.ResponseWriter, r *http.Request) { 220 - f, err := s.repoResolver.Resolve(r) 253 + func (rp *Repo) RepoLog(w http.ResponseWriter, r *http.Request) { 254 + f, err := rp.repoResolver.Resolve(r) 221 255 if err != nil { 222 256 log.Println("failed to fully resolve repo", err) 223 257 return ··· 233 267 234 268 ref := chi.URLParam(r, "ref") 235 269 236 - us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev) 270 + us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev) 237 271 if err != nil { 238 272 log.Println("failed to create unsigned client", err) 239 273 return ··· 260 294 tagMap[hash] = append(tagMap[hash], tag.Name) 261 295 } 262 296 263 - user := s.oauth.GetUser(r) 264 - s.pages.RepoLog(w, pages.RepoLogParams{ 297 + user := rp.oauth.GetUser(r) 298 + rp.pages.RepoLog(w, pages.RepoLogParams{ 265 299 LoggedInUser: user, 266 300 TagMap: tagMap, 267 301 RepoInfo: f.RepoInfo(user), 268 302 RepoLogResponse: *repolog, 269 - EmailToDidOrHandle: EmailToDidOrHandle(s, uniqueEmails(repolog.Commits)), 303 + EmailToDidOrHandle: EmailToDidOrHandle(rp, uniqueEmails(repolog.Commits)), 270 304 }) 271 305 return 272 306 } 273 307 274 - func (s *State) RepoDescriptionEdit(w http.ResponseWriter, r *http.Request) { 275 - f, err := s.repoResolver.Resolve(r) 308 + func (rp *Repo) RepoDescriptionEdit(w http.ResponseWriter, r *http.Request) { 309 + f, err := rp.repoResolver.Resolve(r) 276 310 if err != nil { 277 311 log.Println("failed to get repo and knot", err) 278 312 w.WriteHeader(http.StatusBadRequest) 279 313 return 280 314 } 281 315 282 - user := s.oauth.GetUser(r) 283 - s.pages.EditRepoDescriptionFragment(w, pages.RepoDescriptionParams{ 316 + user := rp.oauth.GetUser(r) 317 + rp.pages.EditRepoDescriptionFragment(w, pages.RepoDescriptionParams{ 284 318 RepoInfo: f.RepoInfo(user), 285 319 }) 286 320 return 287 321 } 288 322 289 - func (s *State) RepoDescription(w http.ResponseWriter, r *http.Request) { 290 - f, err := s.repoResolver.Resolve(r) 323 + func (rp *Repo) RepoDescription(w http.ResponseWriter, r *http.Request) { 324 + f, err := rp.repoResolver.Resolve(r) 291 325 if err != nil { 292 326 log.Println("failed to get repo and knot", err) 293 327 w.WriteHeader(http.StatusBadRequest) ··· 302 336 return 303 337 } 304 338 305 - user := s.oauth.GetUser(r) 339 + user := rp.oauth.GetUser(r) 306 340 307 341 switch r.Method { 308 342 case http.MethodGet: 309 - s.pages.RepoDescriptionFragment(w, pages.RepoDescriptionParams{ 343 + rp.pages.RepoDescriptionFragment(w, pages.RepoDescriptionParams{ 310 344 RepoInfo: f.RepoInfo(user), 311 345 }) 312 346 return 313 347 case http.MethodPut: 314 - user := s.oauth.GetUser(r) 348 + user := rp.oauth.GetUser(r) 315 349 newDescription := r.FormValue("description") 316 - client, err := s.oauth.AuthorizedClient(r) 350 + client, err := rp.oauth.AuthorizedClient(r) 317 351 if err != nil { 318 352 log.Println("failed to get client") 319 - s.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") 353 + rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") 320 354 return 321 355 } 322 356 323 357 // optimistic update 324 - err = db.UpdateDescription(s.db, string(repoAt), newDescription) 358 + err = db.UpdateDescription(rp.db, string(repoAt), newDescription) 325 359 if err != nil { 326 360 log.Println("failed to perferom update-description query", err) 327 - s.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") 361 + rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") 328 362 return 329 363 } 330 364 ··· 334 368 ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoNSID, user.Did, rkey) 335 369 if err != nil { 336 370 // failed to get record 337 - s.pages.Notice(w, "repo-notice", "Failed to update description, no record found on PDS.") 371 + rp.pages.Notice(w, "repo-notice", "Failed to update description, no record found on PDS.") 338 372 return 339 373 } 340 374 _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ ··· 356 390 if err != nil { 357 391 log.Println("failed to perferom update-description query", err) 358 392 // failed to get record 359 - s.pages.Notice(w, "repo-notice", "Failed to update description, unable to save to PDS.") 393 + rp.pages.Notice(w, "repo-notice", "Failed to update description, unable to save to PDS.") 360 394 return 361 395 } 362 396 363 397 newRepoInfo := f.RepoInfo(user) 364 398 newRepoInfo.Description = newDescription 365 399 366 - s.pages.RepoDescriptionFragment(w, pages.RepoDescriptionParams{ 400 + rp.pages.RepoDescriptionFragment(w, pages.RepoDescriptionParams{ 367 401 RepoInfo: newRepoInfo, 368 402 }) 369 403 return 370 404 } 371 405 } 372 406 373 - func (s *State) RepoCommit(w http.ResponseWriter, r *http.Request) { 374 - f, err := s.repoResolver.Resolve(r) 407 + func (rp *Repo) RepoCommit(w http.ResponseWriter, r *http.Request) { 408 + f, err := rp.repoResolver.Resolve(r) 375 409 if err != nil { 376 410 log.Println("failed to fully resolve repo", err) 377 411 return 378 412 } 379 413 ref := chi.URLParam(r, "ref") 380 414 protocol := "http" 381 - if !s.config.Core.Dev { 415 + if !rp.config.Core.Dev { 382 416 protocol = "https" 383 417 } 384 418 385 419 if !plumbing.IsHash(ref) { 386 - s.pages.Error404(w) 420 + rp.pages.Error404(w) 387 421 return 388 422 } 389 423 ··· 406 440 return 407 441 } 408 442 409 - user := s.oauth.GetUser(r) 410 - s.pages.RepoCommit(w, pages.RepoCommitParams{ 443 + user := rp.oauth.GetUser(r) 444 + rp.pages.RepoCommit(w, pages.RepoCommitParams{ 411 445 LoggedInUser: user, 412 446 RepoInfo: f.RepoInfo(user), 413 447 RepoCommitResponse: result, 414 - EmailToDidOrHandle: EmailToDidOrHandle(s, []string{result.Diff.Commit.Author.Email}), 448 + EmailToDidOrHandle: EmailToDidOrHandle(rp, []string{result.Diff.Commit.Author.Email}), 415 449 }) 416 450 return 417 451 } 418 452 419 - func (s *State) RepoTree(w http.ResponseWriter, r *http.Request) { 420 - f, err := s.repoResolver.Resolve(r) 453 + func (rp *Repo) RepoTree(w http.ResponseWriter, r *http.Request) { 454 + f, err := rp.repoResolver.Resolve(r) 421 455 if err != nil { 422 456 log.Println("failed to fully resolve repo", err) 423 457 return ··· 426 460 ref := chi.URLParam(r, "ref") 427 461 treePath := chi.URLParam(r, "*") 428 462 protocol := "http" 429 - if !s.config.Core.Dev { 463 + if !rp.config.Core.Dev { 430 464 protocol = "https" 431 465 } 432 466 resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/tree/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, treePath)) ··· 455 489 return 456 490 } 457 491 458 - user := s.oauth.GetUser(r) 492 + user := rp.oauth.GetUser(r) 459 493 460 494 var breadcrumbs [][]string 461 495 breadcrumbs = append(breadcrumbs, []string{f.RepoName, fmt.Sprintf("/%s/tree/%s", f.OwnerSlashRepo(), ref)}) ··· 468 502 baseTreeLink := path.Join(f.OwnerSlashRepo(), "tree", ref, treePath) 469 503 baseBlobLink := path.Join(f.OwnerSlashRepo(), "blob", ref, treePath) 470 504 471 - s.pages.RepoTree(w, pages.RepoTreeParams{ 505 + rp.pages.RepoTree(w, pages.RepoTreeParams{ 472 506 LoggedInUser: user, 473 507 BreadCrumbs: breadcrumbs, 474 508 BaseTreeLink: baseTreeLink, ··· 479 513 return 480 514 } 481 515 482 - func (s *State) RepoTags(w http.ResponseWriter, r *http.Request) { 483 - f, err := s.repoResolver.Resolve(r) 516 + func (rp *Repo) RepoTags(w http.ResponseWriter, r *http.Request) { 517 + f, err := rp.repoResolver.Resolve(r) 484 518 if err != nil { 485 519 log.Println("failed to get repo and knot", err) 486 520 return 487 521 } 488 522 489 - us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev) 523 + us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev) 490 524 if err != nil { 491 525 log.Println("failed to create unsigned client", err) 492 526 return ··· 498 532 return 499 533 } 500 534 501 - artifacts, err := db.GetArtifact(s.db, db.FilterEq("repo_at", f.RepoAt)) 535 + artifacts, err := db.GetArtifact(rp.db, db.FilterEq("repo_at", f.RepoAt)) 502 536 if err != nil { 503 537 log.Println("failed grab artifacts", err) 504 538 return ··· 526 560 } 527 561 } 528 562 529 - user := s.oauth.GetUser(r) 530 - s.pages.RepoTags(w, pages.RepoTagsParams{ 563 + user := rp.oauth.GetUser(r) 564 + rp.pages.RepoTags(w, pages.RepoTagsParams{ 531 565 LoggedInUser: user, 532 566 RepoInfo: f.RepoInfo(user), 533 567 RepoTagsResponse: *result, ··· 537 571 return 538 572 } 539 573 540 - func (s *State) RepoBranches(w http.ResponseWriter, r *http.Request) { 541 - f, err := s.repoResolver.Resolve(r) 574 + func (rp *Repo) RepoBranches(w http.ResponseWriter, r *http.Request) { 575 + f, err := rp.repoResolver.Resolve(r) 542 576 if err != nil { 543 577 log.Println("failed to get repo and knot", err) 544 578 return 545 579 } 546 580 547 - us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev) 581 + us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev) 548 582 if err != nil { 549 583 log.Println("failed to create unsigned client", err) 550 584 return ··· 573 607 return strings.Compare(a.Name, b.Name) * -1 574 608 }) 575 609 576 - user := s.oauth.GetUser(r) 577 - s.pages.RepoBranches(w, pages.RepoBranchesParams{ 610 + user := rp.oauth.GetUser(r) 611 + rp.pages.RepoBranches(w, pages.RepoBranchesParams{ 578 612 LoggedInUser: user, 579 613 RepoInfo: f.RepoInfo(user), 580 614 RepoBranchesResponse: *result, ··· 582 616 return 583 617 } 584 618 585 - func (s *State) RepoBlob(w http.ResponseWriter, r *http.Request) { 586 - f, err := s.repoResolver.Resolve(r) 619 + func (rp *Repo) RepoBlob(w http.ResponseWriter, r *http.Request) { 620 + f, err := rp.repoResolver.Resolve(r) 587 621 if err != nil { 588 622 log.Println("failed to get repo and knot", err) 589 623 return ··· 592 626 ref := chi.URLParam(r, "ref") 593 627 filePath := chi.URLParam(r, "*") 594 628 protocol := "http" 595 - if !s.config.Core.Dev { 629 + if !rp.config.Core.Dev { 596 630 protocol = "https" 597 631 } 598 632 resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath)) ··· 630 664 showRendered = r.URL.Query().Get("code") != "true" 631 665 } 632 666 633 - user := s.oauth.GetUser(r) 634 - s.pages.RepoBlob(w, pages.RepoBlobParams{ 667 + user := rp.oauth.GetUser(r) 668 + rp.pages.RepoBlob(w, pages.RepoBlobParams{ 635 669 LoggedInUser: user, 636 670 RepoInfo: f.RepoInfo(user), 637 671 RepoBlobResponse: result, ··· 642 676 return 643 677 } 644 678 645 - func (s *State) RepoBlobRaw(w http.ResponseWriter, r *http.Request) { 646 - f, err := s.repoResolver.Resolve(r) 679 + func (rp *Repo) RepoBlobRaw(w http.ResponseWriter, r *http.Request) { 680 + f, err := rp.repoResolver.Resolve(r) 647 681 if err != nil { 648 682 log.Println("failed to get repo and knot", err) 649 683 return ··· 653 687 filePath := chi.URLParam(r, "*") 654 688 655 689 protocol := "http" 656 - if !s.config.Core.Dev { 690 + if !rp.config.Core.Dev { 657 691 protocol = "https" 658 692 } 659 693 resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath)) ··· 686 720 return 687 721 } 688 722 689 - func (s *State) AddCollaborator(w http.ResponseWriter, r *http.Request) { 690 - f, err := s.repoResolver.Resolve(r) 723 + func (rp *Repo) AddCollaborator(w http.ResponseWriter, r *http.Request) { 724 + f, err := rp.repoResolver.Resolve(r) 691 725 if err != nil { 692 726 log.Println("failed to get repo and knot", err) 693 727 return ··· 699 733 return 700 734 } 701 735 702 - collaboratorIdent, err := s.idResolver.ResolveIdent(r.Context(), collaborator) 736 + collaboratorIdent, err := rp.idResolver.ResolveIdent(r.Context(), collaborator) 703 737 if err != nil { 704 738 w.Write([]byte("failed to resolve collaborator did to a handle")) 705 739 return ··· 708 742 709 743 // TODO: create an atproto record for this 710 744 711 - secret, err := db.GetRegistrationKey(s.db, f.Knot) 745 + secret, err := db.GetRegistrationKey(rp.db, f.Knot) 712 746 if err != nil { 713 747 log.Printf("no key found for domain %s: %s\n", f.Knot, err) 714 748 return 715 749 } 716 750 717 - ksClient, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev) 751 + ksClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev) 718 752 if err != nil { 719 753 log.Println("failed to create client to ", f.Knot) 720 754 return ··· 731 765 return 732 766 } 733 767 734 - tx, err := s.db.BeginTx(r.Context(), nil) 768 + tx, err := rp.db.BeginTx(r.Context(), nil) 735 769 if err != nil { 736 770 log.Println("failed to start tx") 737 771 w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err))) ··· 739 773 } 740 774 defer func() { 741 775 tx.Rollback() 742 - err = s.enforcer.E.LoadPolicy() 776 + err = rp.enforcer.E.LoadPolicy() 743 777 if err != nil { 744 778 log.Println("failed to rollback policies") 745 779 } 746 780 }() 747 781 748 - err = s.enforcer.AddCollaborator(collaboratorIdent.DID.String(), f.Knot, f.DidSlashRepo()) 782 + err = rp.enforcer.AddCollaborator(collaboratorIdent.DID.String(), f.Knot, f.DidSlashRepo()) 749 783 if err != nil { 750 784 w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err))) 751 785 return 752 786 } 753 787 754 - err = db.AddCollaborator(s.db, collaboratorIdent.DID.String(), f.OwnerDid(), f.RepoName, f.Knot) 788 + err = db.AddCollaborator(rp.db, collaboratorIdent.DID.String(), f.OwnerDid(), f.RepoName, f.Knot) 755 789 if err != nil { 756 790 w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err))) 757 791 return ··· 764 798 return 765 799 } 766 800 767 - err = s.enforcer.E.SavePolicy() 801 + err = rp.enforcer.E.SavePolicy() 768 802 if err != nil { 769 803 log.Println("failed to update ACLs", err) 770 804 http.Error(w, err.Error(), http.StatusInternalServerError) ··· 775 809 776 810 } 777 811 778 - func (s *State) DeleteRepo(w http.ResponseWriter, r *http.Request) { 779 - user := s.oauth.GetUser(r) 812 + func (rp *Repo) DeleteRepo(w http.ResponseWriter, r *http.Request) { 813 + user := rp.oauth.GetUser(r) 780 814 781 - f, err := s.repoResolver.Resolve(r) 815 + f, err := rp.repoResolver.Resolve(r) 782 816 if err != nil { 783 817 log.Println("failed to get repo and knot", err) 784 818 return 785 819 } 786 820 787 821 // remove record from pds 788 - xrpcClient, err := s.oauth.AuthorizedClient(r) 822 + xrpcClient, err := rp.oauth.AuthorizedClient(r) 789 823 if err != nil { 790 824 log.Println("failed to get authorized client", err) 791 825 return ··· 798 832 }) 799 833 if err != nil { 800 834 log.Printf("failed to delete record: %s", err) 801 - s.pages.Notice(w, "settings-delete", "Failed to delete repository from PDS.") 835 + rp.pages.Notice(w, "settings-delete", "Failed to delete repository from PDS.") 802 836 return 803 837 } 804 838 log.Println("removed repo record ", f.RepoAt.String()) 805 839 806 - secret, err := db.GetRegistrationKey(s.db, f.Knot) 840 + secret, err := db.GetRegistrationKey(rp.db, f.Knot) 807 841 if err != nil { 808 842 log.Printf("no key found for domain %s: %s\n", f.Knot, err) 809 843 return 810 844 } 811 845 812 - ksClient, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev) 846 + ksClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev) 813 847 if err != nil { 814 848 log.Println("failed to create client to ", f.Knot) 815 849 return ··· 827 861 log.Println("removed repo from knot ", f.Knot) 828 862 } 829 863 830 - tx, err := s.db.BeginTx(r.Context(), nil) 864 + tx, err := rp.db.BeginTx(r.Context(), nil) 831 865 if err != nil { 832 866 log.Println("failed to start tx") 833 867 w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err))) ··· 835 869 } 836 870 defer func() { 837 871 tx.Rollback() 838 - err = s.enforcer.E.LoadPolicy() 872 + err = rp.enforcer.E.LoadPolicy() 839 873 if err != nil { 840 874 log.Println("failed to rollback policies") 841 875 } 842 876 }() 843 877 844 878 // remove collaborator RBAC 845 - repoCollaborators, err := s.enforcer.E.GetImplicitUsersForResourceByDomain(f.DidSlashRepo(), f.Knot) 879 + repoCollaborators, err := rp.enforcer.E.GetImplicitUsersForResourceByDomain(f.DidSlashRepo(), f.Knot) 846 880 if err != nil { 847 - s.pages.Notice(w, "settings-delete", "Failed to remove collaborators") 881 + rp.pages.Notice(w, "settings-delete", "Failed to remove collaborators") 848 882 return 849 883 } 850 884 for _, c := range repoCollaborators { 851 885 did := c[0] 852 - s.enforcer.RemoveCollaborator(did, f.Knot, f.DidSlashRepo()) 886 + rp.enforcer.RemoveCollaborator(did, f.Knot, f.DidSlashRepo()) 853 887 } 854 888 log.Println("removed collaborators") 855 889 856 890 // remove repo RBAC 857 - err = s.enforcer.RemoveRepo(f.OwnerDid(), f.Knot, f.DidSlashRepo()) 891 + err = rp.enforcer.RemoveRepo(f.OwnerDid(), f.Knot, f.DidSlashRepo()) 858 892 if err != nil { 859 - s.pages.Notice(w, "settings-delete", "Failed to update RBAC rules") 893 + rp.pages.Notice(w, "settings-delete", "Failed to update RBAC rules") 860 894 return 861 895 } 862 896 863 897 // remove repo from db 864 898 err = db.RemoveRepo(tx, f.OwnerDid(), f.RepoName) 865 899 if err != nil { 866 - s.pages.Notice(w, "settings-delete", "Failed to update appview") 900 + rp.pages.Notice(w, "settings-delete", "Failed to update appview") 867 901 return 868 902 } 869 903 log.Println("removed repo from db") ··· 875 909 return 876 910 } 877 911 878 - err = s.enforcer.E.SavePolicy() 912 + err = rp.enforcer.E.SavePolicy() 879 913 if err != nil { 880 914 log.Println("failed to update ACLs", err) 881 915 http.Error(w, err.Error(), http.StatusInternalServerError) 882 916 return 883 917 } 884 918 885 - s.pages.HxRedirect(w, fmt.Sprintf("/%s", f.OwnerDid())) 919 + rp.pages.HxRedirect(w, fmt.Sprintf("/%s", f.OwnerDid())) 886 920 } 887 921 888 - func (s *State) SetDefaultBranch(w http.ResponseWriter, r *http.Request) { 889 - f, err := s.repoResolver.Resolve(r) 922 + func (rp *Repo) SetDefaultBranch(w http.ResponseWriter, r *http.Request) { 923 + f, err := rp.repoResolver.Resolve(r) 890 924 if err != nil { 891 925 log.Println("failed to get repo and knot", err) 892 926 return ··· 898 932 return 899 933 } 900 934 901 - secret, err := db.GetRegistrationKey(s.db, f.Knot) 935 + secret, err := db.GetRegistrationKey(rp.db, f.Knot) 902 936 if err != nil { 903 937 log.Printf("no key found for domain %s: %s\n", f.Knot, err) 904 938 return 905 939 } 906 940 907 - ksClient, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev) 941 + ksClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev) 908 942 if err != nil { 909 943 log.Println("failed to create client to ", f.Knot) 910 944 return ··· 917 951 } 918 952 919 953 if ksResp.StatusCode != http.StatusNoContent { 920 - s.pages.Notice(w, "repo-settings", "Failed to set default branch. Try again later.") 954 + rp.pages.Notice(w, "repo-settings", "Failed to set default branch. Try again later.") 921 955 return 922 956 } 923 957 924 958 w.Write([]byte(fmt.Sprint("default branch set to: ", branch))) 925 959 } 926 960 927 - func (s *State) RepoSettings(w http.ResponseWriter, r *http.Request) { 928 - f, err := s.repoResolver.Resolve(r) 961 + func (rp *Repo) RepoSettings(w http.ResponseWriter, r *http.Request) { 962 + f, err := rp.repoResolver.Resolve(r) 929 963 if err != nil { 930 964 log.Println("failed to get repo and knot", err) 931 965 return ··· 934 968 switch r.Method { 935 969 case http.MethodGet: 936 970 // for now, this is just pubkeys 937 - user := s.oauth.GetUser(r) 971 + user := rp.oauth.GetUser(r) 938 972 repoCollaborators, err := f.Collaborators(r.Context()) 939 973 if err != nil { 940 974 log.Println("failed to get collaborators", err) ··· 942 976 943 977 isCollaboratorInviteAllowed := false 944 978 if user != nil { 945 - ok, err := s.enforcer.IsCollaboratorInviteAllowed(user.Did, f.Knot, f.DidSlashRepo()) 979 + ok, err := rp.enforcer.IsCollaboratorInviteAllowed(user.Did, f.Knot, f.DidSlashRepo()) 946 980 if err == nil && ok { 947 981 isCollaboratorInviteAllowed = true 948 982 } 949 983 } 950 984 951 - us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev) 985 + us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev) 952 986 if err != nil { 953 987 log.Println("failed to create unsigned client", err) 954 988 return ··· 960 994 return 961 995 } 962 996 963 - s.pages.RepoSettings(w, pages.RepoSettingsParams{ 997 + rp.pages.RepoSettings(w, pages.RepoSettingsParams{ 964 998 LoggedInUser: user, 965 999 RepoInfo: f.RepoInfo(user), 966 1000 Collaborators: repoCollaborators, ··· 970 1004 } 971 1005 } 972 1006 973 - func (s *State) RepoSingleIssue(w http.ResponseWriter, r *http.Request) { 974 - user := s.oauth.GetUser(r) 975 - f, err := s.repoResolver.Resolve(r) 1007 + func (rp *Repo) RepoSingleIssue(w http.ResponseWriter, r *http.Request) { 1008 + user := rp.oauth.GetUser(r) 1009 + f, err := rp.repoResolver.Resolve(r) 976 1010 if err != nil { 977 1011 log.Println("failed to get repo and knot", err) 978 1012 return ··· 986 1020 return 987 1021 } 988 1022 989 - issue, comments, err := db.GetIssueWithComments(s.db, f.RepoAt, issueIdInt) 1023 + issue, comments, err := db.GetIssueWithComments(rp.db, f.RepoAt, issueIdInt) 990 1024 if err != nil { 991 1025 log.Println("failed to get issue and comments", err) 992 - s.pages.Notice(w, "issues", "Failed to load issue. Try again later.") 1026 + rp.pages.Notice(w, "issues", "Failed to load issue. Try again later.") 993 1027 return 994 1028 } 995 1029 996 - issueOwnerIdent, err := s.idResolver.ResolveIdent(r.Context(), issue.OwnerDid) 1030 + issueOwnerIdent, err := rp.idResolver.ResolveIdent(r.Context(), issue.OwnerDid) 997 1031 if err != nil { 998 1032 log.Println("failed to resolve issue owner", err) 999 1033 } ··· 1002 1036 for i, comment := range comments { 1003 1037 identsToResolve[i] = comment.OwnerDid 1004 1038 } 1005 - resolvedIds := s.idResolver.ResolveIdents(r.Context(), identsToResolve) 1039 + resolvedIds := rp.idResolver.ResolveIdents(r.Context(), identsToResolve) 1006 1040 didHandleMap := make(map[string]string) 1007 1041 for _, identity := range resolvedIds { 1008 1042 if !identity.Handle.IsInvalidHandle() { ··· 1012 1046 } 1013 1047 } 1014 1048 1015 - s.pages.RepoSingleIssue(w, pages.RepoSingleIssueParams{ 1049 + rp.pages.RepoSingleIssue(w, pages.RepoSingleIssueParams{ 1016 1050 LoggedInUser: user, 1017 1051 RepoInfo: f.RepoInfo(user), 1018 1052 Issue: *issue, ··· 1024 1058 1025 1059 } 1026 1060 1027 - func (s *State) CloseIssue(w http.ResponseWriter, r *http.Request) { 1028 - user := s.oauth.GetUser(r) 1029 - f, err := s.repoResolver.Resolve(r) 1061 + func (rp *Repo) CloseIssue(w http.ResponseWriter, r *http.Request) { 1062 + user := rp.oauth.GetUser(r) 1063 + f, err := rp.repoResolver.Resolve(r) 1030 1064 if err != nil { 1031 1065 log.Println("failed to get repo and knot", err) 1032 1066 return ··· 1040 1074 return 1041 1075 } 1042 1076 1043 - issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt) 1077 + issue, err := db.GetIssue(rp.db, f.RepoAt, issueIdInt) 1044 1078 if err != nil { 1045 1079 log.Println("failed to get issue", err) 1046 - s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") 1080 + rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") 1047 1081 return 1048 1082 } 1049 1083 ··· 1061 1095 1062 1096 closed := tangled.RepoIssueStateClosed 1063 1097 1064 - client, err := s.oauth.AuthorizedClient(r) 1098 + client, err := rp.oauth.AuthorizedClient(r) 1065 1099 if err != nil { 1066 1100 log.Println("failed to get authorized client", err) 1067 1101 return ··· 1080 1114 1081 1115 if err != nil { 1082 1116 log.Println("failed to update issue state", err) 1083 - s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") 1117 + rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") 1084 1118 return 1085 1119 } 1086 1120 1087 - err = db.CloseIssue(s.db, f.RepoAt, issueIdInt) 1121 + err = db.CloseIssue(rp.db, f.RepoAt, issueIdInt) 1088 1122 if err != nil { 1089 1123 log.Println("failed to close issue", err) 1090 - s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") 1124 + rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") 1091 1125 return 1092 1126 } 1093 1127 1094 - s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueIdInt)) 1128 + rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueIdInt)) 1095 1129 return 1096 1130 } else { 1097 1131 log.Println("user is not permitted to close issue") ··· 1100 1134 } 1101 1135 } 1102 1136 1103 - func (s *State) ReopenIssue(w http.ResponseWriter, r *http.Request) { 1104 - user := s.oauth.GetUser(r) 1105 - f, err := s.repoResolver.Resolve(r) 1137 + func (rp *Repo) ReopenIssue(w http.ResponseWriter, r *http.Request) { 1138 + user := rp.oauth.GetUser(r) 1139 + f, err := rp.repoResolver.Resolve(r) 1106 1140 if err != nil { 1107 1141 log.Println("failed to get repo and knot", err) 1108 1142 return ··· 1116 1150 return 1117 1151 } 1118 1152 1119 - issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt) 1153 + issue, err := db.GetIssue(rp.db, f.RepoAt, issueIdInt) 1120 1154 if err != nil { 1121 1155 log.Println("failed to get issue", err) 1122 - s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") 1156 + rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") 1123 1157 return 1124 1158 } 1125 1159 ··· 1133 1167 isIssueOwner := user.Did == issue.OwnerDid 1134 1168 1135 1169 if isCollaborator || isIssueOwner { 1136 - err := db.ReopenIssue(s.db, f.RepoAt, issueIdInt) 1170 + err := db.ReopenIssue(rp.db, f.RepoAt, issueIdInt) 1137 1171 if err != nil { 1138 1172 log.Println("failed to reopen issue", err) 1139 - s.pages.Notice(w, "issue-action", "Failed to reopen issue. Try again later.") 1173 + rp.pages.Notice(w, "issue-action", "Failed to reopen issue. Try again later.") 1140 1174 return 1141 1175 } 1142 - s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueIdInt)) 1176 + rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueIdInt)) 1143 1177 return 1144 1178 } else { 1145 1179 log.Println("user is not the owner of the repo") ··· 1148 1182 } 1149 1183 } 1150 1184 1151 - func (s *State) NewIssueComment(w http.ResponseWriter, r *http.Request) { 1152 - user := s.oauth.GetUser(r) 1153 - f, err := s.repoResolver.Resolve(r) 1185 + func (rp *Repo) NewIssueComment(w http.ResponseWriter, r *http.Request) { 1186 + user := rp.oauth.GetUser(r) 1187 + f, err := rp.repoResolver.Resolve(r) 1154 1188 if err != nil { 1155 1189 log.Println("failed to get repo and knot", err) 1156 1190 return ··· 1168 1202 case http.MethodPost: 1169 1203 body := r.FormValue("body") 1170 1204 if body == "" { 1171 - s.pages.Notice(w, "issue", "Body is required") 1205 + rp.pages.Notice(w, "issue", "Body is required") 1172 1206 return 1173 1207 } 1174 1208 1175 1209 commentId := mathrand.IntN(1000000) 1176 1210 rkey := appview.TID() 1177 1211 1178 - err := db.NewIssueComment(s.db, &db.Comment{ 1212 + err := db.NewIssueComment(rp.db, &db.Comment{ 1179 1213 OwnerDid: user.Did, 1180 1214 RepoAt: f.RepoAt, 1181 1215 Issue: issueIdInt, ··· 1185 1219 }) 1186 1220 if err != nil { 1187 1221 log.Println("failed to create comment", err) 1188 - s.pages.Notice(w, "issue-comment", "Failed to create comment.") 1222 + rp.pages.Notice(w, "issue-comment", "Failed to create comment.") 1189 1223 return 1190 1224 } 1191 1225 1192 1226 createdAt := time.Now().Format(time.RFC3339) 1193 1227 commentIdInt64 := int64(commentId) 1194 1228 ownerDid := user.Did 1195 - issueAt, err := db.GetIssueAt(s.db, f.RepoAt, issueIdInt) 1229 + issueAt, err := db.GetIssueAt(rp.db, f.RepoAt, issueIdInt) 1196 1230 if err != nil { 1197 1231 log.Println("failed to get issue at", err) 1198 - s.pages.Notice(w, "issue-comment", "Failed to create comment.") 1232 + rp.pages.Notice(w, "issue-comment", "Failed to create comment.") 1199 1233 return 1200 1234 } 1201 1235 1202 1236 atUri := f.RepoAt.String() 1203 - client, err := s.oauth.AuthorizedClient(r) 1237 + client, err := rp.oauth.AuthorizedClient(r) 1204 1238 if err != nil { 1205 1239 log.Println("failed to get authorized client", err) 1206 - s.pages.Notice(w, "issue-comment", "Failed to create comment.") 1240 + rp.pages.Notice(w, "issue-comment", "Failed to create comment.") 1207 1241 return 1208 1242 } 1209 1243 _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ ··· 1223 1257 }) 1224 1258 if err != nil { 1225 1259 log.Println("failed to create comment", err) 1226 - s.pages.Notice(w, "issue-comment", "Failed to create comment.") 1260 + rp.pages.Notice(w, "issue-comment", "Failed to create comment.") 1227 1261 return 1228 1262 } 1229 1263 1230 - s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", f.OwnerSlashRepo(), issueIdInt, commentId)) 1264 + rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", f.OwnerSlashRepo(), issueIdInt, commentId)) 1231 1265 return 1232 1266 } 1233 1267 } 1234 1268 1235 - func (s *State) IssueComment(w http.ResponseWriter, r *http.Request) { 1236 - user := s.oauth.GetUser(r) 1237 - f, err := s.repoResolver.Resolve(r) 1269 + func (rp *Repo) IssueComment(w http.ResponseWriter, r *http.Request) { 1270 + user := rp.oauth.GetUser(r) 1271 + f, err := rp.repoResolver.Resolve(r) 1238 1272 if err != nil { 1239 1273 log.Println("failed to get repo and knot", err) 1240 1274 return ··· 1256 1290 return 1257 1291 } 1258 1292 1259 - issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt) 1293 + issue, err := db.GetIssue(rp.db, f.RepoAt, issueIdInt) 1260 1294 if err != nil { 1261 1295 log.Println("failed to get issue", err) 1262 - s.pages.Notice(w, "issues", "Failed to load issue. Try again later.") 1296 + rp.pages.Notice(w, "issues", "Failed to load issue. Try again later.") 1263 1297 return 1264 1298 } 1265 1299 1266 - comment, err := db.GetComment(s.db, f.RepoAt, issueIdInt, commentIdInt) 1300 + comment, err := db.GetComment(rp.db, f.RepoAt, issueIdInt, commentIdInt) 1267 1301 if err != nil { 1268 1302 http.Error(w, "bad comment id", http.StatusBadRequest) 1269 1303 return 1270 1304 } 1271 1305 1272 - identity, err := s.idResolver.ResolveIdent(r.Context(), comment.OwnerDid) 1306 + identity, err := rp.idResolver.ResolveIdent(r.Context(), comment.OwnerDid) 1273 1307 if err != nil { 1274 1308 log.Println("failed to resolve did") 1275 1309 return ··· 1282 1316 didHandleMap[identity.DID.String()] = identity.DID.String() 1283 1317 } 1284 1318 1285 - s.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{ 1319 + rp.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{ 1286 1320 LoggedInUser: user, 1287 1321 RepoInfo: f.RepoInfo(user), 1288 1322 DidHandleMap: didHandleMap, ··· 1291 1325 }) 1292 1326 } 1293 1327 1294 - func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) { 1295 - user := s.oauth.GetUser(r) 1296 - f, err := s.repoResolver.Resolve(r) 1328 + func (rp *Repo) EditIssueComment(w http.ResponseWriter, r *http.Request) { 1329 + user := rp.oauth.GetUser(r) 1330 + f, err := rp.repoResolver.Resolve(r) 1297 1331 if err != nil { 1298 1332 log.Println("failed to get repo and knot", err) 1299 1333 return ··· 1315 1349 return 1316 1350 } 1317 1351 1318 - issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt) 1352 + issue, err := db.GetIssue(rp.db, f.RepoAt, issueIdInt) 1319 1353 if err != nil { 1320 1354 log.Println("failed to get issue", err) 1321 - s.pages.Notice(w, "issues", "Failed to load issue. Try again later.") 1355 + rp.pages.Notice(w, "issues", "Failed to load issue. Try again later.") 1322 1356 return 1323 1357 } 1324 1358 1325 - comment, err := db.GetComment(s.db, f.RepoAt, issueIdInt, commentIdInt) 1359 + comment, err := db.GetComment(rp.db, f.RepoAt, issueIdInt, commentIdInt) 1326 1360 if err != nil { 1327 1361 http.Error(w, "bad comment id", http.StatusBadRequest) 1328 1362 return ··· 1335 1369 1336 1370 switch r.Method { 1337 1371 case http.MethodGet: 1338 - s.pages.EditIssueCommentFragment(w, pages.EditIssueCommentParams{ 1372 + rp.pages.EditIssueCommentFragment(w, pages.EditIssueCommentParams{ 1339 1373 LoggedInUser: user, 1340 1374 RepoInfo: f.RepoInfo(user), 1341 1375 Issue: issue, ··· 1344 1378 case http.MethodPost: 1345 1379 // extract form value 1346 1380 newBody := r.FormValue("body") 1347 - client, err := s.oauth.AuthorizedClient(r) 1381 + client, err := rp.oauth.AuthorizedClient(r) 1348 1382 if err != nil { 1349 1383 log.Println("failed to get authorized client", err) 1350 - s.pages.Notice(w, "issue-comment", "Failed to create comment.") 1384 + rp.pages.Notice(w, "issue-comment", "Failed to create comment.") 1351 1385 return 1352 1386 } 1353 1387 rkey := comment.Rkey 1354 1388 1355 1389 // optimistic update 1356 1390 edited := time.Now() 1357 - err = db.EditComment(s.db, comment.RepoAt, comment.Issue, comment.CommentId, newBody) 1391 + err = db.EditComment(rp.db, comment.RepoAt, comment.Issue, comment.CommentId, newBody) 1358 1392 if err != nil { 1359 1393 log.Println("failed to perferom update-description query", err) 1360 - s.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") 1394 + rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") 1361 1395 return 1362 1396 } 1363 1397 ··· 1368 1402 if err != nil { 1369 1403 // failed to get record 1370 1404 log.Println(err, rkey) 1371 - s.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.") 1405 + rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.") 1372 1406 return 1373 1407 } 1374 1408 value, _ := ex.Value.MarshalJSON() // we just did get record; it is valid json ··· 1408 1442 comment.Edited = &edited 1409 1443 1410 1444 // return new comment body with htmx 1411 - s.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{ 1445 + rp.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{ 1412 1446 LoggedInUser: user, 1413 1447 RepoInfo: f.RepoInfo(user), 1414 1448 DidHandleMap: didHandleMap, ··· 1421 1455 1422 1456 } 1423 1457 1424 - func (s *State) DeleteIssueComment(w http.ResponseWriter, r *http.Request) { 1425 - user := s.oauth.GetUser(r) 1426 - f, err := s.repoResolver.Resolve(r) 1458 + func (rp *Repo) DeleteIssueComment(w http.ResponseWriter, r *http.Request) { 1459 + user := rp.oauth.GetUser(r) 1460 + f, err := rp.repoResolver.Resolve(r) 1427 1461 if err != nil { 1428 1462 log.Println("failed to get repo and knot", err) 1429 1463 return ··· 1437 1471 return 1438 1472 } 1439 1473 1440 - issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt) 1474 + issue, err := db.GetIssue(rp.db, f.RepoAt, issueIdInt) 1441 1475 if err != nil { 1442 1476 log.Println("failed to get issue", err) 1443 - s.pages.Notice(w, "issues", "Failed to load issue. Try again later.") 1477 + rp.pages.Notice(w, "issues", "Failed to load issue. Try again later.") 1444 1478 return 1445 1479 } 1446 1480 ··· 1452 1486 return 1453 1487 } 1454 1488 1455 - comment, err := db.GetComment(s.db, f.RepoAt, issueIdInt, commentIdInt) 1489 + comment, err := db.GetComment(rp.db, f.RepoAt, issueIdInt, commentIdInt) 1456 1490 if err != nil { 1457 1491 http.Error(w, "bad comment id", http.StatusBadRequest) 1458 1492 return ··· 1470 1504 1471 1505 // optimistic deletion 1472 1506 deleted := time.Now() 1473 - err = db.DeleteComment(s.db, f.RepoAt, issueIdInt, commentIdInt) 1507 + err = db.DeleteComment(rp.db, f.RepoAt, issueIdInt, commentIdInt) 1474 1508 if err != nil { 1475 1509 log.Println("failed to delete comment") 1476 - s.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "failed to delete comment") 1510 + rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "failed to delete comment") 1477 1511 return 1478 1512 } 1479 1513 1480 1514 // delete from pds 1481 1515 if comment.Rkey != "" { 1482 - client, err := s.oauth.AuthorizedClient(r) 1516 + client, err := rp.oauth.AuthorizedClient(r) 1483 1517 if err != nil { 1484 1518 log.Println("failed to get authorized client", err) 1485 - s.pages.Notice(w, "issue-comment", "Failed to delete comment.") 1519 + rp.pages.Notice(w, "issue-comment", "Failed to delete comment.") 1486 1520 return 1487 1521 } 1488 1522 _, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ ··· 1503 1537 comment.Deleted = &deleted 1504 1538 1505 1539 // htmx fragment of comment after deletion 1506 - s.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{ 1540 + rp.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{ 1507 1541 LoggedInUser: user, 1508 1542 RepoInfo: f.RepoInfo(user), 1509 1543 DidHandleMap: didHandleMap, ··· 1513 1547 return 1514 1548 } 1515 1549 1516 - func (s *State) RepoIssues(w http.ResponseWriter, r *http.Request) { 1550 + func (rp *Repo) RepoIssues(w http.ResponseWriter, r *http.Request) { 1517 1551 params := r.URL.Query() 1518 1552 state := params.Get("state") 1519 1553 isOpen := true ··· 1532 1566 page = pagination.FirstPage() 1533 1567 } 1534 1568 1535 - user := s.oauth.GetUser(r) 1536 - f, err := s.repoResolver.Resolve(r) 1569 + user := rp.oauth.GetUser(r) 1570 + f, err := rp.repoResolver.Resolve(r) 1537 1571 if err != nil { 1538 1572 log.Println("failed to get repo and knot", err) 1539 1573 return 1540 1574 } 1541 1575 1542 - issues, err := db.GetIssues(s.db, f.RepoAt, isOpen, page) 1576 + issues, err := db.GetIssues(rp.db, f.RepoAt, isOpen, page) 1543 1577 if err != nil { 1544 1578 log.Println("failed to get issues", err) 1545 - s.pages.Notice(w, "issues", "Failed to load issues. Try again later.") 1579 + rp.pages.Notice(w, "issues", "Failed to load issues. Try again later.") 1546 1580 return 1547 1581 } 1548 1582 ··· 1550 1584 for i, issue := range issues { 1551 1585 identsToResolve[i] = issue.OwnerDid 1552 1586 } 1553 - resolvedIds := s.idResolver.ResolveIdents(r.Context(), identsToResolve) 1587 + resolvedIds := rp.idResolver.ResolveIdents(r.Context(), identsToResolve) 1554 1588 didHandleMap := make(map[string]string) 1555 1589 for _, identity := range resolvedIds { 1556 1590 if !identity.Handle.IsInvalidHandle() { ··· 1560 1594 } 1561 1595 } 1562 1596 1563 - s.pages.RepoIssues(w, pages.RepoIssuesParams{ 1564 - LoggedInUser: s.oauth.GetUser(r), 1597 + rp.pages.RepoIssues(w, pages.RepoIssuesParams{ 1598 + LoggedInUser: rp.oauth.GetUser(r), 1565 1599 RepoInfo: f.RepoInfo(user), 1566 1600 Issues: issues, 1567 1601 DidHandleMap: didHandleMap, ··· 1571 1605 return 1572 1606 } 1573 1607 1574 - func (s *State) NewIssue(w http.ResponseWriter, r *http.Request) { 1575 - user := s.oauth.GetUser(r) 1608 + func (rp *Repo) NewIssue(w http.ResponseWriter, r *http.Request) { 1609 + user := rp.oauth.GetUser(r) 1576 1610 1577 - f, err := s.repoResolver.Resolve(r) 1611 + f, err := rp.repoResolver.Resolve(r) 1578 1612 if err != nil { 1579 1613 log.Println("failed to get repo and knot", err) 1580 1614 return ··· 1582 1616 1583 1617 switch r.Method { 1584 1618 case http.MethodGet: 1585 - s.pages.RepoNewIssue(w, pages.RepoNewIssueParams{ 1619 + rp.pages.RepoNewIssue(w, pages.RepoNewIssueParams{ 1586 1620 LoggedInUser: user, 1587 1621 RepoInfo: f.RepoInfo(user), 1588 1622 }) ··· 1591 1625 body := r.FormValue("body") 1592 1626 1593 1627 if title == "" || body == "" { 1594 - s.pages.Notice(w, "issues", "Title and body are required") 1628 + rp.pages.Notice(w, "issues", "Title and body are required") 1595 1629 return 1596 1630 } 1597 1631 1598 - tx, err := s.db.BeginTx(r.Context(), nil) 1632 + tx, err := rp.db.BeginTx(r.Context(), nil) 1599 1633 if err != nil { 1600 - s.pages.Notice(w, "issues", "Failed to create issue, try again later") 1634 + rp.pages.Notice(w, "issues", "Failed to create issue, try again later") 1601 1635 return 1602 1636 } 1603 1637 ··· 1609 1643 }) 1610 1644 if err != nil { 1611 1645 log.Println("failed to create issue", err) 1612 - s.pages.Notice(w, "issues", "Failed to create issue.") 1646 + rp.pages.Notice(w, "issues", "Failed to create issue.") 1613 1647 return 1614 1648 } 1615 1649 1616 - issueId, err := db.GetIssueId(s.db, f.RepoAt) 1650 + issueId, err := db.GetIssueId(rp.db, f.RepoAt) 1617 1651 if err != nil { 1618 1652 log.Println("failed to get issue id", err) 1619 - s.pages.Notice(w, "issues", "Failed to create issue.") 1653 + rp.pages.Notice(w, "issues", "Failed to create issue.") 1620 1654 return 1621 1655 } 1622 1656 1623 - client, err := s.oauth.AuthorizedClient(r) 1657 + client, err := rp.oauth.AuthorizedClient(r) 1624 1658 if err != nil { 1625 1659 log.Println("failed to get authorized client", err) 1626 - s.pages.Notice(w, "issues", "Failed to create issue.") 1660 + rp.pages.Notice(w, "issues", "Failed to create issue.") 1627 1661 return 1628 1662 } 1629 1663 atUri := f.RepoAt.String() ··· 1643 1677 }) 1644 1678 if err != nil { 1645 1679 log.Println("failed to create issue", err) 1646 - s.pages.Notice(w, "issues", "Failed to create issue.") 1680 + rp.pages.Notice(w, "issues", "Failed to create issue.") 1647 1681 return 1648 1682 } 1649 1683 1650 - err = db.SetIssueAt(s.db, f.RepoAt, issueId, resp.Uri) 1684 + err = db.SetIssueAt(rp.db, f.RepoAt, issueId, resp.Uri) 1651 1685 if err != nil { 1652 1686 log.Println("failed to set issue at", err) 1653 - s.pages.Notice(w, "issues", "Failed to create issue.") 1687 + rp.pages.Notice(w, "issues", "Failed to create issue.") 1654 1688 return 1655 1689 } 1656 1690 1657 - if !s.config.Core.Dev { 1658 - err = s.posthog.Enqueue(posthog.Capture{ 1691 + if !rp.config.Core.Dev { 1692 + err = rp.posthog.Enqueue(posthog.Capture{ 1659 1693 DistinctId: user.Did, 1660 1694 Event: "new_issue", 1661 1695 Properties: posthog.Properties{"repo_at": f.RepoAt.String(), "issue_id": issueId}, ··· 1665 1699 } 1666 1700 } 1667 1701 1668 - s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueId)) 1702 + rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueId)) 1669 1703 return 1670 1704 } 1671 1705 } 1672 1706 1673 - func (s *State) SyncRepoFork(w http.ResponseWriter, r *http.Request) { 1674 - user := s.oauth.GetUser(r) 1675 - f, err := s.repoResolver.Resolve(r) 1707 + func (rp *Repo) SyncRepoFork(w http.ResponseWriter, r *http.Request) { 1708 + user := rp.oauth.GetUser(r) 1709 + f, err := rp.repoResolver.Resolve(r) 1676 1710 if err != nil { 1677 1711 log.Printf("failed to resolve source repo: %v", err) 1678 1712 return ··· 1680 1714 1681 1715 switch r.Method { 1682 1716 case http.MethodPost: 1683 - secret, err := db.GetRegistrationKey(s.db, f.Knot) 1717 + secret, err := db.GetRegistrationKey(rp.db, f.Knot) 1684 1718 if err != nil { 1685 - s.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", f.Knot)) 1719 + rp.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %rp.", f.Knot)) 1686 1720 return 1687 1721 } 1688 1722 1689 - client, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev) 1723 + client, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev) 1690 1724 if err != nil { 1691 - s.pages.Notice(w, "repo", "Failed to reach knot server.") 1725 + rp.pages.Notice(w, "repo", "Failed to reach knot server.") 1692 1726 return 1693 1727 } 1694 1728 1695 1729 var uri string 1696 - if s.config.Core.Dev { 1730 + if rp.config.Core.Dev { 1697 1731 uri = "http" 1698 1732 } else { 1699 1733 uri = "https" ··· 1703 1737 1704 1738 _, err = client.SyncRepoFork(user.Did, forkSourceUrl, forkName, f.Ref) 1705 1739 if err != nil { 1706 - s.pages.Notice(w, "repo", "Failed to sync repository fork.") 1740 + rp.pages.Notice(w, "repo", "Failed to sync repository fork.") 1707 1741 return 1708 1742 } 1709 1743 1710 - s.pages.HxRefresh(w) 1744 + rp.pages.HxRefresh(w) 1711 1745 return 1712 1746 } 1713 1747 } 1714 1748 1715 - func (s *State) ForkRepo(w http.ResponseWriter, r *http.Request) { 1716 - user := s.oauth.GetUser(r) 1717 - f, err := s.repoResolver.Resolve(r) 1749 + func (rp *Repo) ForkRepo(w http.ResponseWriter, r *http.Request) { 1750 + user := rp.oauth.GetUser(r) 1751 + f, err := rp.repoResolver.Resolve(r) 1718 1752 if err != nil { 1719 1753 log.Printf("failed to resolve source repo: %v", err) 1720 1754 return ··· 1722 1756 1723 1757 switch r.Method { 1724 1758 case http.MethodGet: 1725 - user := s.oauth.GetUser(r) 1726 - knots, err := s.enforcer.GetDomainsForUser(user.Did) 1759 + user := rp.oauth.GetUser(r) 1760 + knots, err := rp.enforcer.GetDomainsForUser(user.Did) 1727 1761 if err != nil { 1728 - s.pages.Notice(w, "repo", "Invalid user account.") 1762 + rp.pages.Notice(w, "repo", "Invalid user account.") 1729 1763 return 1730 1764 } 1731 1765 1732 - s.pages.ForkRepo(w, pages.ForkRepoParams{ 1766 + rp.pages.ForkRepo(w, pages.ForkRepoParams{ 1733 1767 LoggedInUser: user, 1734 1768 Knots: knots, 1735 1769 RepoInfo: f.RepoInfo(user), ··· 1739 1773 1740 1774 knot := r.FormValue("knot") 1741 1775 if knot == "" { 1742 - s.pages.Notice(w, "repo", "Invalid form submission&mdash;missing knot domain.") 1776 + rp.pages.Notice(w, "repo", "Invalid form submission&mdash;missing knot domain.") 1743 1777 return 1744 1778 } 1745 1779 1746 - ok, err := s.enforcer.E.Enforce(user.Did, knot, knot, "repo:create") 1780 + ok, err := rp.enforcer.E.Enforce(user.Did, knot, knot, "repo:create") 1747 1781 if err != nil || !ok { 1748 - s.pages.Notice(w, "repo", "You do not have permission to create a repo in this knot.") 1782 + rp.pages.Notice(w, "repo", "You do not have permission to create a repo in this knot.") 1749 1783 return 1750 1784 } 1751 1785 ··· 1753 1787 1754 1788 // this check is *only* to see if the forked repo name already exists 1755 1789 // in the user's account. 1756 - existingRepo, err := db.GetRepo(s.db, user.Did, f.RepoName) 1790 + existingRepo, err := db.GetRepo(rp.db, user.Did, f.RepoName) 1757 1791 if err != nil { 1758 1792 if errors.Is(err, sql.ErrNoRows) { 1759 1793 // no existing repo with this name found, we can use the name as is 1760 1794 } else { 1761 1795 log.Println("error fetching existing repo from db", err) 1762 - s.pages.Notice(w, "repo", "Failed to fork this repository. Try again later.") 1796 + rp.pages.Notice(w, "repo", "Failed to fork this repository. Try again later.") 1763 1797 return 1764 1798 } 1765 1799 } else if existingRepo != nil { 1766 1800 // repo with this name already exists, append random string 1767 1801 forkName = fmt.Sprintf("%s-%s", forkName, randomString(3)) 1768 1802 } 1769 - secret, err := db.GetRegistrationKey(s.db, knot) 1803 + secret, err := db.GetRegistrationKey(rp.db, knot) 1770 1804 if err != nil { 1771 - s.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", knot)) 1805 + rp.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %rp.", knot)) 1772 1806 return 1773 1807 } 1774 1808 1775 - client, err := knotclient.NewSignedClient(knot, secret, s.config.Core.Dev) 1809 + client, err := knotclient.NewSignedClient(knot, secret, rp.config.Core.Dev) 1776 1810 if err != nil { 1777 - s.pages.Notice(w, "repo", "Failed to reach knot server.") 1811 + rp.pages.Notice(w, "repo", "Failed to reach knot server.") 1778 1812 return 1779 1813 } 1780 1814 1781 1815 var uri string 1782 - if s.config.Core.Dev { 1816 + if rp.config.Core.Dev { 1783 1817 uri = "http" 1784 1818 } else { 1785 1819 uri = "https" ··· 1796 1830 Source: sourceAt, 1797 1831 } 1798 1832 1799 - tx, err := s.db.BeginTx(r.Context(), nil) 1833 + tx, err := rp.db.BeginTx(r.Context(), nil) 1800 1834 if err != nil { 1801 1835 log.Println(err) 1802 - s.pages.Notice(w, "repo", "Failed to save repository information.") 1836 + rp.pages.Notice(w, "repo", "Failed to save repository information.") 1803 1837 return 1804 1838 } 1805 1839 defer func() { 1806 1840 tx.Rollback() 1807 - err = s.enforcer.E.LoadPolicy() 1841 + err = rp.enforcer.E.LoadPolicy() 1808 1842 if err != nil { 1809 1843 log.Println("failed to rollback policies") 1810 1844 } ··· 1812 1846 1813 1847 resp, err := client.ForkRepo(user.Did, forkSourceUrl, forkName) 1814 1848 if err != nil { 1815 - s.pages.Notice(w, "repo", "Failed to create repository on knot server.") 1849 + rp.pages.Notice(w, "repo", "Failed to create repository on knot server.") 1816 1850 return 1817 1851 } 1818 1852 1819 1853 switch resp.StatusCode { 1820 1854 case http.StatusConflict: 1821 - s.pages.Notice(w, "repo", "A repository with that name already exists.") 1855 + rp.pages.Notice(w, "repo", "A repository with that name already exists.") 1822 1856 return 1823 1857 case http.StatusInternalServerError: 1824 - s.pages.Notice(w, "repo", "Failed to create repository on knot. Try again later.") 1858 + rp.pages.Notice(w, "repo", "Failed to create repository on knot. Try again later.") 1825 1859 case http.StatusNoContent: 1826 1860 // continue 1827 1861 } 1828 1862 1829 - xrpcClient, err := s.oauth.AuthorizedClient(r) 1863 + xrpcClient, err := rp.oauth.AuthorizedClient(r) 1830 1864 if err != nil { 1831 1865 log.Println("failed to get authorized client", err) 1832 - s.pages.Notice(w, "repo", "Failed to create repository.") 1866 + rp.pages.Notice(w, "repo", "Failed to create repository.") 1833 1867 return 1834 1868 } 1835 1869 ··· 1849 1883 }) 1850 1884 if err != nil { 1851 1885 log.Printf("failed to create record: %s", err) 1852 - s.pages.Notice(w, "repo", "Failed to announce repository creation.") 1886 + rp.pages.Notice(w, "repo", "Failed to announce repository creation.") 1853 1887 return 1854 1888 } 1855 1889 log.Println("created repo record: ", atresp.Uri) ··· 1858 1892 err = db.AddRepo(tx, repo) 1859 1893 if err != nil { 1860 1894 log.Println(err) 1861 - s.pages.Notice(w, "repo", "Failed to save repository information.") 1895 + rp.pages.Notice(w, "repo", "Failed to save repository information.") 1862 1896 return 1863 1897 } 1864 1898 1865 1899 // acls 1866 1900 p, _ := securejoin.SecureJoin(user.Did, forkName) 1867 - err = s.enforcer.AddRepo(user.Did, knot, p) 1901 + err = rp.enforcer.AddRepo(user.Did, knot, p) 1868 1902 if err != nil { 1869 1903 log.Println(err) 1870 - s.pages.Notice(w, "repo", "Failed to set up repository permissions.") 1904 + rp.pages.Notice(w, "repo", "Failed to set up repository permissions.") 1871 1905 return 1872 1906 } 1873 1907 ··· 1878 1912 return 1879 1913 } 1880 1914 1881 - err = s.enforcer.E.SavePolicy() 1915 + err = rp.enforcer.E.SavePolicy() 1882 1916 if err != nil { 1883 1917 log.Println("failed to update ACLs", err) 1884 1918 http.Error(w, err.Error(), http.StatusInternalServerError) 1885 1919 return 1886 1920 } 1887 1921 1888 - s.pages.HxLocation(w, fmt.Sprintf("/@%s/%s", user.Handle, forkName)) 1922 + rp.pages.HxLocation(w, fmt.Sprintf("/@%s/%s", user.Handle, forkName)) 1889 1923 return 1890 1924 } 1891 1925 } 1892 1926 1893 - func (s *State) RepoCompareNew(w http.ResponseWriter, r *http.Request) { 1894 - user := s.oauth.GetUser(r) 1895 - f, err := s.repoResolver.Resolve(r) 1927 + func (rp *Repo) RepoCompareNew(w http.ResponseWriter, r *http.Request) { 1928 + user := rp.oauth.GetUser(r) 1929 + f, err := rp.repoResolver.Resolve(r) 1896 1930 if err != nil { 1897 1931 log.Println("failed to get repo and knot", err) 1898 1932 return 1899 1933 } 1900 1934 1901 - us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev) 1935 + us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev) 1902 1936 if err != nil { 1903 1937 log.Printf("failed to create unsigned client for %s", f.Knot) 1904 - s.pages.Error503(w) 1938 + rp.pages.Error503(w) 1905 1939 return 1906 1940 } 1907 1941 1908 1942 result, err := us.Branches(f.OwnerDid(), f.RepoName) 1909 1943 if err != nil { 1910 - s.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 1944 + rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 1911 1945 log.Println("failed to reach knotserver", err) 1912 1946 return 1913 1947 } ··· 1938 1972 1939 1973 tags, err := us.Tags(f.OwnerDid(), f.RepoName) 1940 1974 if err != nil { 1941 - s.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 1975 + rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 1942 1976 log.Println("failed to reach knotserver", err) 1943 1977 return 1944 1978 } 1945 1979 1946 1980 repoinfo := f.RepoInfo(user) 1947 1981 1948 - s.pages.RepoCompareNew(w, pages.RepoCompareNewParams{ 1982 + rp.pages.RepoCompareNew(w, pages.RepoCompareNewParams{ 1949 1983 LoggedInUser: user, 1950 1984 RepoInfo: repoinfo, 1951 1985 Branches: branches, ··· 1955 1989 }) 1956 1990 } 1957 1991 1958 - func (s *State) RepoCompare(w http.ResponseWriter, r *http.Request) { 1959 - user := s.oauth.GetUser(r) 1960 - f, err := s.repoResolver.Resolve(r) 1992 + func (rp *Repo) RepoCompare(w http.ResponseWriter, r *http.Request) { 1993 + user := rp.oauth.GetUser(r) 1994 + f, err := rp.repoResolver.Resolve(r) 1961 1995 if err != nil { 1962 1996 log.Println("failed to get repo and knot", err) 1963 1997 return ··· 1979 2013 1980 2014 if base == "" || head == "" { 1981 2015 log.Printf("invalid comparison") 1982 - s.pages.Error404(w) 2016 + rp.pages.Error404(w) 1983 2017 return 1984 2018 } 1985 2019 1986 - us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev) 2020 + us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev) 1987 2021 if err != nil { 1988 2022 log.Printf("failed to create unsigned client for %s", f.Knot) 1989 - s.pages.Error503(w) 2023 + rp.pages.Error503(w) 1990 2024 return 1991 2025 } 1992 2026 1993 2027 branches, err := us.Branches(f.OwnerDid(), f.RepoName) 1994 2028 if err != nil { 1995 - s.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 2029 + rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 1996 2030 log.Println("failed to reach knotserver", err) 1997 2031 return 1998 2032 } 1999 2033 2000 2034 tags, err := us.Tags(f.OwnerDid(), f.RepoName) 2001 2035 if err != nil { 2002 - s.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 2036 + rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 2003 2037 log.Println("failed to reach knotserver", err) 2004 2038 return 2005 2039 } 2006 2040 2007 2041 formatPatch, err := us.Compare(f.OwnerDid(), f.RepoName, base, head) 2008 2042 if err != nil { 2009 - s.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 2043 + rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 2010 2044 log.Println("failed to compare", err) 2011 2045 return 2012 2046 } ··· 2014 2048 2015 2049 repoinfo := f.RepoInfo(user) 2016 2050 2017 - s.pages.RepoCompare(w, pages.RepoCompareParams{ 2051 + rp.pages.RepoCompare(w, pages.RepoCompareParams{ 2018 2052 LoggedInUser: user, 2019 2053 RepoInfo: repoinfo, 2020 2054 Branches: branches.Branches,
+4 -4
appview/state/repo_util.go appview/repo/repo_util.go
··· 1 - package state 1 + package repo 2 2 3 3 import ( 4 4 "context" ··· 56 56 return 57 57 } 58 58 59 - func EmailToDidOrHandle(s *State, emails []string) map[string]string { 60 - emailToDid, err := db.GetEmailToDid(s.db, emails, true) // only get verified emails for mapping 59 + func EmailToDidOrHandle(r *Repo, emails []string) map[string]string { 60 + emailToDid, err := db.GetEmailToDid(r.db, emails, true) // only get verified emails for mapping 61 61 if err != nil { 62 62 log.Printf("error fetching dids for emails: %v", err) 63 63 return nil ··· 67 67 for _, v := range emailToDid { 68 68 dids = append(dids, v) 69 69 } 70 - resolvedIdents := s.idResolver.ResolveIdents(context.Background(), dids) 70 + resolvedIdents := r.idResolver.ResolveIdents(context.Background(), dids) 71 71 72 72 didHandleMap := make(map[string]string) 73 73 for _, identity := range resolvedIdents {
+7 -85
appview/state/router.go
··· 9 9 "tangled.sh/tangled.sh/core/appview/middleware" 10 10 oauthhandler "tangled.sh/tangled.sh/core/appview/oauth/handler" 11 11 "tangled.sh/tangled.sh/core/appview/pulls" 12 + "tangled.sh/tangled.sh/core/appview/repo" 12 13 "tangled.sh/tangled.sh/core/appview/settings" 13 14 "tangled.sh/tangled.sh/core/appview/state/userutil" 14 15 ) ··· 69 70 r.With(mw.ResolveRepo()).Route("/{repo}", func(r chi.Router) { 70 71 r.Use(mw.GoImport()) 71 72 72 - r.Get("/", s.RepoIndex) 73 - r.Get("/commits/{ref}", s.RepoLog) 74 - r.Route("/tree/{ref}", func(r chi.Router) { 75 - r.Get("/", s.RepoIndex) 76 - r.Get("/*", s.RepoTree) 77 - }) 78 - r.Get("/commit/{ref}", s.RepoCommit) 79 - r.Get("/branches", s.RepoBranches) 80 - r.Route("/tags", func(r chi.Router) { 81 - r.Get("/", s.RepoTags) 82 - r.Route("/{tag}", func(r chi.Router) { 83 - r.Use(middleware.AuthMiddleware(s.oauth)) 84 - // require auth to download for now 85 - r.Get("/download/{file}", s.DownloadArtifact) 86 - 87 - // require repo:push to upload or delete artifacts 88 - // 89 - // additionally: only the uploader can truly delete an artifact 90 - // (record+blob will live on their pds) 91 - r.Group(func(r chi.Router) { 92 - r.With(mw.RepoPermissionMiddleware("repo:push")) 93 - r.Post("/upload", s.AttachArtifact) 94 - r.Delete("/{file}", s.DeleteArtifact) 95 - }) 96 - }) 97 - }) 98 - r.Get("/blob/{ref}/*", s.RepoBlob) 99 - r.Get("/raw/{ref}/*", s.RepoBlobRaw) 100 - 101 - r.Route("/issues", func(r chi.Router) { 102 - r.With(middleware.Paginate).Get("/", s.RepoIssues) 103 - r.Get("/{issue}", s.RepoSingleIssue) 104 - 105 - r.Group(func(r chi.Router) { 106 - r.Use(middleware.AuthMiddleware(s.oauth)) 107 - r.Get("/new", s.NewIssue) 108 - r.Post("/new", s.NewIssue) 109 - r.Post("/{issue}/comment", s.NewIssueComment) 110 - r.Route("/{issue}/comment/{comment_id}/", func(r chi.Router) { 111 - r.Get("/", s.IssueComment) 112 - r.Delete("/", s.DeleteIssueComment) 113 - r.Get("/edit", s.EditIssueComment) 114 - r.Post("/edit", s.EditIssueComment) 115 - }) 116 - r.Post("/{issue}/close", s.CloseIssue) 117 - r.Post("/{issue}/reopen", s.ReopenIssue) 118 - }) 119 - }) 120 - 121 - r.Route("/fork", func(r chi.Router) { 122 - r.Use(middleware.AuthMiddleware(s.oauth)) 123 - r.Get("/", s.ForkRepo) 124 - r.Post("/", s.ForkRepo) 125 - r.With(mw.RepoPermissionMiddleware("repo:owner")).Route("/sync", func(r chi.Router) { 126 - r.Post("/", s.SyncRepoFork) 127 - }) 128 - }) 129 - 130 - r.Route("/compare", func(r chi.Router) { 131 - r.Get("/", s.RepoCompareNew) // start an new comparison 132 - 133 - // we have to wildcard here since we want to support GitHub's compare syntax 134 - // /compare/{ref1}...{ref2} 135 - // for example: 136 - // /compare/master...some/feature 137 - // /compare/master...example.com:another/feature <- this is a fork 138 - r.Get("/{base}/{head}", s.RepoCompare) 139 - r.Get("/*", s.RepoCompare) 140 - }) 73 + r.Mount("/", s.RepoRouter(mw)) 141 74 142 75 r.Mount("/pulls", s.PullsRouter(mw)) 143 76 ··· 146 79 r.Post("/git-upload-pack", s.UploadPack) 147 80 r.Post("/git-receive-pack", s.ReceivePack) 148 81 149 - // settings routes, needs auth 150 - r.Group(func(r chi.Router) { 151 - r.Use(middleware.AuthMiddleware(s.oauth)) 152 - // repo description can only be edited by owner 153 - r.With(mw.RepoPermissionMiddleware("repo:owner")).Route("/description", func(r chi.Router) { 154 - r.Put("/", s.RepoDescription) 155 - r.Get("/", s.RepoDescription) 156 - r.Get("/edit", s.RepoDescriptionEdit) 157 - }) 158 - r.With(mw.RepoPermissionMiddleware("repo:settings")).Route("/settings", func(r chi.Router) { 159 - r.Get("/", s.RepoSettings) 160 - r.With(mw.RepoPermissionMiddleware("repo:invite")).Put("/collaborator", s.AddCollaborator) 161 - r.With(mw.RepoPermissionMiddleware("repo:delete")).Delete("/delete", s.DeleteRepo) 162 - r.Put("/branches/default", s.SetDefaultBranch) 163 - }) 164 - }) 165 82 }) 166 83 }) 167 84 ··· 266 183 pulls := pulls.New(s.oauth, s.repoResolver, s.pages, s.idResolver, s.db, s.config) 267 184 return pulls.Router(mw) 268 185 } 186 + 187 + func (s *State) RepoRouter(mw *middleware.Middleware) http.Handler { 188 + repo := repo.New(s.oauth, s.repoResolver, s.pages, s.idResolver, s.db, s.config, s.posthog, s.enforcer) 189 + return repo.Router(mw) 190 + }