appview: repo: implement generating feed #476

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