connections returns the configured app connections the viewer may see, merged with their templates: public ones to everyone, member ones to members, moderators and the owner, owner ones to the owner. Connections whose feature is off are left out.
- commit
- 591f26270893b3a17c7fb23aa569adc46323946e
- parent
- 5df27560c504297b6e3122447c82686e28298194
- author
- Dami <dami.etoile@gmail.com>
| A | internal/ | 42 | +++++++++++++++++++++++ |
| M | internal/ | 56 | +++++++++++++++++++++++++++++- |
@@ -0,0 +1,42 @@
1+package daemon2+3+import (4+ "context"5+ "encoding/json"6+ "strings"7+ "testing"8+)9+10+func TestVisibleConnectionsFollowViewerRole(t *testing.T) {11+ ctx := context.Background()12+ _, tenant := testTenant(t)13+ rows := `[{"template":"notes","visibility":"public"},{"template":"bookmarks","visibility":"members"},{"template":"relay-page","visibility":"owner"},{"template":"nope","visibility":"public"}]`14+ if _, err := tenant.Execute(ctx, tenant.Policy().Owner, "setconnections", []json.RawMessage{json.RawMessage(rows)}); err != nil {15+ t.Fatal(err)16+ }17+ names := func(actor string) string {18+ value, err := tenant.Execute(ctx, actor, "connections", nil)19+ if err != nil {20+ t.Fatal(err)21+ }22+ raw, _ := json.Marshal(value)23+ var list []map[string]any24+ _ = json.Unmarshal(raw, &list)25+ out := []string{}26+ for _, item := range list {27+ out = append(out, item["template"].(string))28+ }29+ return strings.Join(out, ",")30+ }31+ if got := names(""); got != "notes" {32+ t.Fatalf("guest sees %q", got)33+ }34+ if got := names(tenant.Policy().Owner); got != "notes,bookmarks,relay-page" {35+ t.Fatalf("owner sees %q", got)36+ }37+ value, _ := tenant.Execute(ctx, "", "connections", nil)38+ raw, _ := json.Marshal(value)39+ if !strings.Contains(string(raw), "jumble.social") || !strings.Contains(string(raw), `"links"`) {40+ t.Fatalf("connection is missing its template links: %s", raw)41+ }42+}@@ -110,7 +110,7 @@ func managementAdapterMethod(method string) bool {
110110 // are intentionally separate from the NIP-86 management methods so a public111111 // page cannot accidentally gain access to privileged search or export APIs.112112 func PublicMethods() []string {113- return []string{"queryevents", "eventdetail", "feed", "listview", "claiminvite"}113+ return []string{"queryevents", "eventdetail", "feed", "listview", "claiminvite", "connections"}114114 }115115 116116 func publicManagementMethod(method string) bool {@@ -151,6 +151,9 @@ func roleName(role string) string {
151151 }152152 153153 func (t *Tenant) executePublicManagement(ctx context.Context, actor, method string, params []json.RawMessage) (any, bool, error) {154+ if method == "connections" {155+ return t.visibleConnections(ctx, actor)156+ }154157 if method == "claiminvite" {155158 if strings.TrimSpace(actor) == "" {156159 return nil, true, errors.New("auth-required: claiminvite")@@ -487,6 +490,57 @@ func featureEnabled(p policy.Policy, name string) bool {
487490 }488491 }489492 493+// visibleConnections lists configured connections the viewer may see, each494+// merged with its template so pages can render titles, links and QR codes.495+// Visibility "public" shows to everyone, "members" or "auth" to members,496+// moderators and the owner, and "owner" to the owner alone. Connections497+// whose feature is off are left out.498+func (t *Tenant) visibleConnections(ctx context.Context, actor string) (any, bool, error) {499+ rows, _, err := t.connections(ctx)500+ if err != nil {501+ return nil, true, err502+ }503+ role := ""504+ if strings.TrimSpace(actor) != "" {505+ if role, err = t.community.Role(ctx, actor); err != nil {506+ return nil, true, err507+ }508+ }509+ catalog := map[string]templates.Connection{}510+ for _, c := range templates.Connections() {511+ catalog[c.Name] = c512+ }513+ p := t.Policy()514+ result := make([]map[string]any, 0)515+ for _, row := range rows.([]map[string]any) {516+ name, _ := row["template"].(string)517+ if name == "" {518+ name, _ = row["name"].(string)519+ }520+ c, ok := catalog[name]521+ if !ok || (c.Feature != "" && !featureEnabled(p, c.Feature)) {522+ continue523+ }524+ visibility, _ := row["visibility"].(string)525+ if visibility == "" {526+ visibility = c.Visibility527+ }528+ switch visibility {529+ case "public":530+ case "members", "member", "auth":531+ if role != "member" && role != "moderator" && role != "owner" {532+ continue533+ }534+ default:535+ if role != "owner" {536+ continue537+ }538+ }539+ result = append(result, map[string]any{"template": c.Name, "title": c.Title, "about": c.About, "app": c.App, "where": c.Where, "icon": c.Icon, "feature": c.Feature, "visibility": visibility, "qr": c.QR, "inputs": c.Inputs, "links": c.Links})540+ }541+ return result, true, nil542+}543+490544 func (t *Tenant) connections(ctx context.Context) (any, bool, error) {491545 rows, err := t.store.DB().QueryContext(ctx, `SELECT value FROM config_connections ORDER BY position`)492546 if err != nil {