appview: repo: implement generating feed #476

merged
opened by ptr.pet targeting master from [deleted fork]: repo-feed
Changed files
+87
appview
+86
appview/repo/repo.go
··· 37 securejoin "github.com/cyphar/filepath-securejoin" 38 "github.com/go-chi/chi/v5" 39 "github.com/go-git/go-git/v5/plumbing" 40 41 comatproto "github.com/bluesky-social/indigo/api/atproto" 42 "github.com/bluesky-social/indigo/atproto/syntax" ··· 269 } 270 } 271 272 func (rp *Repo) RepoCommit(w http.ResponseWriter, r *http.Request) { 273 f, err := rp.repoResolver.Resolve(r) 274 if err != nil {
··· 37 securejoin "github.com/cyphar/filepath-securejoin" 38 "github.com/go-chi/chi/v5" 39 "github.com/go-git/go-git/v5/plumbing" 40 + "github.com/gorilla/feeds" 41 42 comatproto "github.com/bluesky-social/indigo/api/atproto" 43 "github.com/bluesky-social/indigo/atproto/syntax" ··· 270 } 271 } 272 273 + func (rp *Repo) getRepoFeed(ctx context.Context, f *reporesolver.ResolvedRepo) (*feeds.Feed, error) { 274 + const feedLimitPerType = 100 275 + 276 + pulls, err := db.GetAnyPulls(rp.db, f.RepoAt, feedLimitPerType) 277 + if err != nil { 278 + return nil, err 279 + } 280 + 281 + issues, err := db.GetAnyIssues(rp.db, f.RepoAt, feedLimitPerType) 282 + if err != nil { 283 + return nil, err 284 + } 285 + 286 + feed := &feeds.Feed{ 287 + Title: fmt.Sprintf("activity feed for %s", f.OwnerSlashRepo()), 288 + Link: &feeds.Link{Href: fmt.Sprintf("%s/%s", rp.config.Core.AppviewHost, f.OwnerSlashRepo()), Type: "text/html", Rel: "alternate"}, 289 + Items: make([]*feeds.Item, 0), 290 + Updated: time.UnixMilli(0), 291 + } 292 + 293 + for _, pull := range pulls { 294 + owner, err := rp.idResolver.ResolveIdent(ctx, pull.OwnerDid) 295 + if err != nil { 296 + return nil, err 297 + } 298 + item := &feeds.Item{ 299 + Title: fmt.Sprintf("@%s created pull request on %s", owner.Handle, f.OwnerSlashRepo()), 300 + Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d", rp.config.Core.AppviewHost, f.OwnerSlashRepo(), pull.PullId)}, 301 + Created: pull.Created, 302 + Author: &feeds.Author{ 303 + Name: fmt.Sprintf("@%s", owner.Handle), 304 + }, 305 + } 306 + feed.Items = append(feed.Items, item) 307 + } 308 + 309 + for _, issue := range issues { 310 + owner, err := rp.idResolver.ResolveIdent(ctx, issue.OwnerDid) 311 + if err != nil { 312 + return nil, err 313 + } 314 + item := &feeds.Item{ 315 + Title: fmt.Sprintf("@%s opened issue on %s", owner.Handle, f.OwnerSlashRepo()), 316 + Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/issues/%d", rp.config.Core.AppviewHost, f.OwnerSlashRepo(), issue.IssueId)}, 317 + Created: issue.Created, 318 + Author: &feeds.Author{ 319 + Name: fmt.Sprintf("@%s", owner.Handle), 320 + }, 321 + } 322 + feed.Items = append(feed.Items, item) 323 + } 324 + 325 + slices.SortFunc(feed.Items, func(a *feeds.Item, b *feeds.Item) int { 326 + return int(b.Created.UnixMilli()) - int(a.Created.UnixMilli()) 327 + }) 328 + if len(feed.Items) > 0 { 329 + feed.Updated = feed.Items[0].Created 330 + } 331 + 332 + return feed, nil 333 + } 334 + 335 + func (rp *Repo) RepoAtomFeed(w http.ResponseWriter, r *http.Request) { 336 + f, err := rp.repoResolver.Resolve(r) 337 + if err != nil { 338 + log.Println("failed to fully resolve repo", err) 339 + return 340 + } 341 + 342 + feed, err := rp.getRepoFeed(r.Context(), f) 343 + if err != nil { 344 + rp.pages.Error500(w) 345 + return 346 + } 347 + 348 + atom, err := feed.ToAtom() 349 + if err != nil { 350 + rp.pages.Error500(w) 351 + return 352 + } 353 + 354 + w.Header().Set("content-type", "application/atom+xml") 355 + w.Write([]byte(atom)) 356 + } 357 + 358 func (rp *Repo) RepoCommit(w http.ResponseWriter, r *http.Request) { 359 f, err := rp.repoResolver.Resolve(r) 360 if err != nil {
+1
appview/repo/router.go
··· 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)
··· 10 func (rp *Repo) Router(mw *middleware.Middleware) http.Handler { 11 r := chi.NewRouter() 12 r.Get("/", rp.RepoIndex) 13 + r.Get("/feed.atom", rp.RepoAtomFeed) 14 r.Get("/commits/{ref}", rp.RepoLog) 15 r.Route("/tree/{ref}", func(r chi.Router) { 16 r.Get("/", rp.RepoIndex)