Monorepo for Tangled tangled.org
2

Configure Feed

Select the types of activity you want to include in your feed.

1package knotserver 2 3import ( 4 "context" 5 "fmt" 6 "log/slog" 7 "maps" 8 "slices" 9 10 "github.com/bluesky-social/indigo/atproto/syntax" 11 12 "tangled.org/core/knotserver/db" 13 "tangled.org/core/rbac" 14) 15 16const ( 17 collaboratorBackfillMigration = "backfill-collaborators-from-casbin-v1" 18 knotMemberBackfillMigration = "backfill-knot-members-from-casbin-v1" 19) 20 21func BackfillKnotMembers( 22 ctx context.Context, 23 d *db.DB, 24 e *rbac.Enforcer, 25 ownerDid string, 26 logger *slog.Logger, 27) error { 28 l := logger.With("migration", knotMemberBackfillMigration) 29 30 applied, err := d.IsMigrationApplied(knotMemberBackfillMigration) 31 if err != nil { 32 return fmt.Errorf("check migration applied: %w", err) 33 } 34 if applied { 35 return nil 36 } 37 38 owner, err := syntax.ParseDID(ownerDid) 39 if err != nil { 40 return fmt.Errorf("invalid knot owner DID %q: %w", ownerDid, err) 41 } 42 43 members, err := e.GetKnotUsersByRole("server:member", rbac.ThisServer) 44 if err != nil { 45 return fmt.Errorf("list members: %w", err) 46 } 47 owners, err := e.GetKnotUsersByRole("server:owner", rbac.ThisServer) 48 if err != nil { 49 return fmt.Errorf("list owners: %w", err) 50 } 51 52 var rows []db.KnotMember 53 for _, candidate := range members { 54 if slices.Contains(owners, candidate) { 55 continue 56 } 57 subject, err := syntax.ParseDID(candidate) 58 if err != nil { 59 l.Warn("skipping member with invalid DID", "candidate", candidate, "err", err) 60 continue 61 } 62 rows = append(rows, db.KnotMember{Did: owner, Subject: subject}) 63 } 64 65 if err := d.ApplyKnotMemberBackfill(ctx, rows, knotMemberBackfillMigration); err != nil { 66 return fmt.Errorf("apply backfill: %w", err) 67 } 68 69 l.Info("backfilled knot members from casbin", "count", len(rows)) 70 return nil 71} 72 73func BackfillCollaborators( 74 ctx context.Context, 75 d *db.DB, 76 e *rbac.Enforcer, 77 logger *slog.Logger, 78 markApplied bool, 79) error { 80 l := logger.With("migration", collaboratorBackfillMigration) 81 82 applied, err := d.IsMigrationApplied(collaboratorBackfillMigration) 83 if err != nil { 84 return fmt.Errorf("check migration applied: %w", err) 85 } 86 if applied { 87 return nil 88 } 89 90 byRepo, err := e.GetCollaboratorsByRepo(rbac.ThisServer) 91 if err != nil { 92 return fmt.Errorf("list collaborators: %w", err) 93 } 94 95 var rows []db.Collaborator 96 var skipped int 97 for _, repoDid := range slices.Sorted(maps.Keys(byRepo)) { 98 candidates := byRepo[repoDid] 99 100 ownerDid, _, err := d.GetRepoKeyOwner(repoDid) 101 if err != nil { 102 l.Warn("skipping collaborators for unresolvable repo", "repoDid", repoDid, "collaborators", len(candidates), "err", err) 103 skipped += len(candidates) 104 continue 105 } 106 107 repo, err := syntax.ParseDID(repoDid) 108 if err != nil { 109 l.Warn("skipping collaborators for repo with invalid DID", "repoDid", repoDid, "collaborators", len(candidates), "err", err) 110 skipped += len(candidates) 111 continue 112 } 113 owner, err := syntax.ParseDID(ownerDid) 114 if err != nil { 115 l.Warn("skipping collaborators for repo with invalid owner DID", "repoDid", repoDid, "owner", ownerDid, "collaborators", len(candidates), "err", err) 116 skipped += len(candidates) 117 continue 118 } 119 120 for _, candidate := range candidates { 121 subject, err := syntax.ParseDID(candidate) 122 if err != nil { 123 l.Warn("skipping collaborator with invalid DID", "repoDid", repoDid, "candidate", candidate, "err", err) 124 skipped++ 125 continue 126 } 127 rows = append(rows, db.Collaborator{ 128 RepoDid: repo, 129 Subject: subject, 130 AddedBy: owner, 131 }) 132 } 133 } 134 135 if err := d.ApplyCollaboratorBackfill(ctx, rows, collaboratorBackfillMigration, markApplied); err != nil { 136 return fmt.Errorf("apply backfill: %w", err) 137 } 138 139 l.Info("backfilled collaborators from casbin", "count", len(rows), "repos", len(byRepo), "skipped", skipped, "marked", markApplied) 140 return nil 141}