clone

a self-hosted multitenant nostr relay with sites, file storage, git hosting, and inbox and outbox delivery

feat/ui-updates
tinyrelay / internal / daemon / repository_maintainers.go
repository_maintainers.go 3.0K
1package daemon23import (4 "context"5 "time"67 gitrelay "github.com/FelineStateMachine/tinyrelay/tinygit"8)910// Repository maintainers. The owner and the keys in an announcement's11// maintainers tag are maintainers by the event itself; an agent becomes one12// through a kind 30392 grant that holds maintain on that exact repository.13// This file is the one place that answers "does this key maintain this14// repository", for state events and pushes through gitrelay, for status15// changes and access checks in the daemon, and for the browse output that16// the repository panel lists.1718// IsMaintainer implements gitrelay.MaintainerSource. A paused, revoked or19// expired grant never counts. Outcomes are recorded under the authorize20// operation; the key itself is never logged or labelled.21func (t *Tenant) IsMaintainer(ctx context.Context, r gitrelay.Repository, pubkey string) bool {22 if pubkey == "" {23 return false24 }25 if pubkey == r.Owner || contains(r.Maintainers, pubkey) {26 return true27 }28 ctx, finish := t.app.telemetry.Start(ctx, "authorize")29 outcome := "unauthorized"30 defer func() { finish(outcome) }()31 grant, ok, err := t.community.AgentGrant(ctx, pubkey)32 if err != nil {33 outcome = "error"34 t.app.telemetry.Logger().WarnContext(ctx, "agent maintainer lookup failed", "error", err)35 return false36 }37 if ok && grant.Maintains(r.Owner, r.Identifier, time.Now().Unix()) {38 outcome = "ok"39 return true40 }41 return false42}4344// repositoryMaintainers lists the owner, the announcement's maintainers and45// every agent whose active grant holds maintain on the repository, in that46// order. A key in the maintainers tag that also holds such a grant is47// reported as an agent so the interface can say so.48func (t *Tenant) repositoryMaintainers(ctx context.Context, r gitrelay.Repository) []gitrelay.Maintainer {49 out := []gitrelay.Maintainer{{PubKey: r.Owner, Role: "owner"}}50 seen := map[string]int{r.Owner: 0}51 for _, pubkey := range r.Maintainers {52 if _, dup := seen[pubkey]; dup {53 continue54 }55 seen[pubkey] = len(out)56 out = append(out, gitrelay.Maintainer{PubKey: pubkey, Role: "maintainer"})57 }58 grants, err := t.community.RepositoryAgents(ctx, r.Owner, r.Identifier, time.Now().Unix())59 if err != nil {60 t.app.telemetry.Logger().WarnContext(ctx, "repository agents lookup failed", "error", err)61 return out62 }63 for _, grant := range grants {64 if index, dup := seen[grant.Agent]; dup {65 if index > 0 {66 out[index].Role, out[index].Name = "agent", grant.Name67 }68 continue69 }70 seen[grant.Agent] = len(out)71 out = append(out, gitrelay.Maintainer{PubKey: grant.Agent, Role: "agent", Name: grant.Name})72 }73 return out74}7576// maintainerKeys returns the keys whose status events count for a77// repository, for use as an authors filter.78func (t *Tenant) maintainerKeys(ctx context.Context, r gitrelay.Repository) []string {79 maintainers := t.repositoryMaintainers(ctx, r)80 keys := make([]string, 0, len(maintainers))81 for _, maintainer := range maintainers {82 keys = append(keys, maintainer.PubKey)83 }84 return keys85}