clone

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

feat/standalone-relay
tinyrelay / internal / daemon / custom_views_access.go
custom_views_access.go 3.3K
1package daemon23import (4 "context"5 "encoding/json"6 "strconv"7 "strings"8 "time"910 "github.com/FelineStateMachine/tinyrelay/internal/event"11 "github.com/FelineStateMachine/tinyrelay/internal/relay"12 "github.com/FelineStateMachine/tinyrelay/internal/views"13)1415func (t *customViewService) artifactEventVisible(ctx context.Context, e event.Event, s relay.Session) bool {16 if t.records == nil || e.PubKey != t.records.PublicKey() {17 return true18 }19 d := event.Tag(e, "d")20 if !strings.HasPrefix(d, "bind.ws/view/") {21 return true22 }23 var view, hash string24 if err := t.store.DB().QueryRowContext(ctx, `SELECT view,hash FROM custom_view_artifacts WHERE event_id=?`, e.ID).Scan(&view, &hash); err != nil {25 return false26 }27 if d != "bind.ws/view/"+view+"/"+hash {28 return false29 }30 refs := map[string]bool{}31 for _, tag := range e.Tags {32 if len(tag) == 2 && (tag[0] == "e" || tag[0] == "a") {33 refs[tag[1]] = true34 }35 }36 if len(refs) == 0 {37 return false38 }39 sources, ok := t.artifactSourceRows(ctx, view, hash)40 if !ok {41 return false42 }43 for _, source := range sources {44 if !refs[source] || !t.sourceVisible(ctx, source, s, hash) {45 return false46 }47 delete(refs, source)48 }49 return len(sources) > 0 && len(refs) == 050}5152func (t *customViewService) artifactHTTPSourceVisible(ctx context.Context, view, hash string, s relay.Session) bool {53 sources, ok := t.artifactSourceRows(ctx, view, hash)54 if !ok {55 return false56 }57 for _, source := range sources {58 if t.sourceVisible(ctx, source, s, hash) {59 return true60 }61 }62 return false63}6465func (t *customViewService) artifactSourceRows(ctx context.Context, view, hash string) ([]string, bool) {66 rows, err := t.store.DB().QueryContext(ctx, `SELECT source FROM custom_view_sources WHERE view=? AND hash=?`, view, hash)67 if err != nil {68 return nil, false69 }70 defer rows.Close()71 var sources []string72 for rows.Next() {73 var source string74 if rows.Scan(&source) != nil {75 _ = rows.Close()76 return nil, false77 }78 sources = append(sources, source)79 }80 if rows.Err() != nil {81 return nil, false82 }83 return sources, true84}8586func (t *customViewService) sourceVisible(ctx context.Context, source string, s relay.Session, artifactHash string) bool {87 if t.gate == nil {88 return false89 }90 var raw string91 if tag := viewSourceTag(source); tag[0] == "a" {92 parts := strings.SplitN(source, ":", 3)93 if len(parts) != 3 {94 return false95 }96 if err := t.store.DB().QueryRowContext(ctx, `SELECT raw FROM events WHERE kind=? AND pubkey=? AND d=? AND (expires=0 OR expires>?) ORDER BY created_at DESC,id ASC LIMIT 1`, event.KIND_REPO_STATE, parts[1], parts[2], time.Now().Unix()).Scan(&raw); err != nil {97 return false98 }99 } else if err := t.store.DB().QueryRowContext(ctx, `SELECT raw FROM events WHERE id=? AND (expires=0 OR expires>?)`, source, time.Now().Unix()).Scan(&raw); err != nil {100 return false101 }102 var sourceEvent event.Event103 if err := json.Unmarshal([]byte(raw), &sourceEvent); err != nil || !t.gate.CanSee(ctx, sourceEvent, s, nil) {104 return false105 }106 if strings.HasPrefix(source, strconv.Itoa(event.KIND_REPO_STATE)+":") && artifactHash != "" {107 blocks, err := t.sourceBlocks(ctx, sourceEvent)108 if err != nil {109 return false110 }111 for _, block := range blocks {112 if views.Hash(block.Lang, block.Source) == artifactHash {113 return true114 }115 }116 return false117 }118 return true119}