From ec4d4d01150ae979896496651bc64e4148d94a06 Mon Sep 17 00:00:00 2001 From: tobi Date: Tue, 20 May 2025 11:47:40 +0200 Subject: [feature] Allow exposing allows, implement `/api/v1/domain_blocks` and `/api/v1/domain_allows` (#4169) - adds config flags `instance-expose-allowlist` and `instance-expose-allowlist-web` to allow instance admins to expose their allowlist via the web + api. - renames `instance-expose-suspended` and `instance-expose-suspended-web` to `instance-expose-blocklist` and `instance-expose-blocklist-web`. - deprecates the `suspended` filter on `/api/v1/instance/peers` endpoint and adds `blocked` and `allowed` filters - adds the `flat` query param to `/api/v1/instance/peers` to allow forcing return of a flat list of domains - implements `/api/v1/instance/domain_blocks` and `/api/v1/instance/domain_allows` endpoints with or without auth depending on config - rejigs the instance about page to include a general section on domain permissions, with block and allow subsections (and appropriate links) Closes https://codeberg.org/superseriousbusiness/gotosocial/issues/3847 Closes https://codeberg.org/superseriousbusiness/gotosocial/issues/4150 Prerequisite to https://codeberg.org/superseriousbusiness/gotosocial/issues/3711 Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4169 Co-authored-by: tobi Co-committed-by: tobi --- internal/processing/instance.go | 145 ++++++++++++++++++++++++++++------------ 1 file changed, 102 insertions(+), 43 deletions(-) (limited to 'internal/processing/instance.go') diff --git a/internal/processing/instance.go b/internal/processing/instance.go index 87fe1e3ef..e1a3785e9 100644 --- a/internal/processing/instance.go +++ b/internal/processing/instance.go @@ -19,8 +19,10 @@ package processing import ( "context" + "errors" "fmt" - "sort" + "slices" + "strings" apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model" "code.superseriousbusiness.org/gotosocial/internal/config" @@ -31,6 +33,7 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/text" "code.superseriousbusiness.org/gotosocial/internal/typeutils" "code.superseriousbusiness.org/gotosocial/internal/util" + "code.superseriousbusiness.org/gotosocial/internal/util/xslices" "code.superseriousbusiness.org/gotosocial/internal/validate" ) @@ -62,70 +65,126 @@ func (p *Processor) InstanceGetV2(ctx context.Context) (*apimodel.InstanceV2, gt return ai, nil } -func (p *Processor) InstancePeersGet(ctx context.Context, includeSuspended bool, includeOpen bool, flat bool) (interface{}, gtserror.WithCode) { - domains := []*apimodel.Domain{} +func (p *Processor) InstancePeersGet( + ctx context.Context, + includeBlocked bool, + includeAllowed bool, + includeOpen bool, + flatten bool, + includeSeverity bool, +) (any, gtserror.WithCode) { + var ( + domainPerms []gtsmodel.DomainPermission + apiDomains []*apimodel.Domain + ) + + if includeBlocked { + blocks, err := p.state.DB.GetDomainBlocks(ctx) + if err != nil && !errors.Is(err, db.ErrNoEntries) { + err := gtserror.Newf("db error getting domain blocks: %w", err) + return nil, gtserror.NewErrorInternalError(err) + } - if includeOpen { - instances, err := p.state.DB.GetInstancePeers(ctx, false) - if err != nil && err != db.ErrNoEntries { - err = fmt.Errorf("error selecting instance peers: %s", err) + for _, block := range blocks { + domainPerms = append(domainPerms, block) + } + + } else if includeAllowed { + allows, err := p.state.DB.GetDomainAllows(ctx) + if err != nil && !errors.Is(err, db.ErrNoEntries) { + err := gtserror.Newf("db error getting domain allows: %w", err) return nil, gtserror.NewErrorInternalError(err) } - for _, i := range instances { - // Domain may be in Punycode, - // de-punify it just in case. - d, err := util.DePunify(i.Domain) - if err != nil { - log.Errorf(ctx, "couldn't depunify domain %s: %s", i.Domain, err) - continue - } + for _, allow := range allows { + domainPerms = append(domainPerms, allow) + } + } - domains = append(domains, &apimodel.Domain{Domain: d}) + for _, domainPerm := range domainPerms { + // Domain may be in Punycode, + // de-punify it just in case. + domain := domainPerm.GetDomain() + depunied, err := util.DePunify(domain) + if err != nil { + log.Errorf(ctx, "couldn't depunify domain %s: %v", domain, err) + continue } + + if util.PtrOrZero(domainPerm.GetObfuscate()) { + // Obfuscate the de-punified version. + depunied = obfuscate(depunied) + } + + apiDomain := &apimodel.Domain{ + Domain: depunied, + Comment: util.Ptr(domainPerm.GetPublicComment()), + } + + if domainPerm.GetType() == gtsmodel.DomainPermissionBlock { + const severity = "suspend" + apiDomain.Severity = severity + suspendedAt := domainPerm.GetCreatedAt() + apiDomain.SuspendedAt = util.FormatISO8601(suspendedAt) + } + + apiDomains = append(apiDomains, apiDomain) } - if includeSuspended { - domainBlocks := []*gtsmodel.DomainBlock{} - if err := p.state.DB.GetAll(ctx, &domainBlocks); err != nil && err != db.ErrNoEntries { + if includeOpen { + instances, err := p.state.DB.GetInstancePeers(ctx, false) + if err != nil && !errors.Is(err, db.ErrNoEntries) { + err = gtserror.Newf("db error getting instance peers: %w", err) return nil, gtserror.NewErrorInternalError(err) } - for _, domainBlock := range domainBlocks { + for _, instance := range instances { // Domain may be in Punycode, // de-punify it just in case. - d, err := util.DePunify(domainBlock.Domain) + domain := instance.Domain + depunied, err := util.DePunify(domain) if err != nil { - log.Errorf(ctx, "couldn't depunify domain %s: %s", domainBlock.Domain, err) + log.Errorf(ctx, "couldn't depunify domain %s: %v", domain, err) continue } - if *domainBlock.Obfuscate { - // Obfuscate the de-punified version. - d = obfuscate(d) - } - - domains = append(domains, &apimodel.Domain{ - Domain: d, - SuspendedAt: util.FormatISO8601(domainBlock.CreatedAt), - Comment: &domainBlock.PublicComment, - }) + apiDomains = append( + apiDomains, + &apimodel.Domain{ + Domain: depunied, + }, + ) } } - sort.Slice(domains, func(i, j int) bool { - return domains[i].Domain < domains[j].Domain - }) - - if flat { - flattened := []string{} - for _, d := range domains { - flattened = append(flattened, d.Domain) - } - return flattened, nil + // Sort a-z. + slices.SortFunc( + apiDomains, + func(a, b *apimodel.Domain) int { + return strings.Compare(a.Domain, b.Domain) + }, + ) + + // Deduplicate. + apiDomains = xslices.DeduplicateFunc( + apiDomains, + func(v *apimodel.Domain) string { + return v.Domain + }, + ) + + if flatten { + // Return just the domains. + return xslices.Gather( + []string{}, + apiDomains, + func(v *apimodel.Domain) string { + return v.Domain + }, + ), nil } - return domains, nil + return apiDomains, nil } func (p *Processor) InstanceGetRules(ctx context.Context) ([]apimodel.InstanceRule, gtserror.WithCode) { -- cgit v1.2.3