backend for xcvr appview
1package recordmanager
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 atoauth "github.com/bluesky-social/indigo/atproto/auth/oauth"
8 "github.com/bluesky-social/indigo/atproto/syntax"
9 "rvcx/internal/atputils"
10 "rvcx/internal/lex"
11 "rvcx/internal/oauth"
12 "rvcx/internal/types"
13 "time"
14)
15
16func (rm *RecordManager) AcceptChannel(c *types.Channel, ctx context.Context) error {
17 wasNew, err := rm.storeChannel(c, ctx)
18 if err != nil {
19 return errors.New("failed to store channel: " + err.Error())
20 }
21 if !wasNew {
22 return nil
23 }
24 err = rm.initChannel(c)
25 if err != nil {
26 return errors.New("failed to initialize channel: " + err.Error())
27 }
28 return nil
29}
30
31func (rm *RecordManager) AcceptChannelUpdate(c *types.Channel, ctx context.Context) error {
32 err := rm.updateChanneldb(c, ctx)
33 if err != nil {
34 return errors.New("failed to update channel: " + err.Error())
35 }
36 err = rm.updateChannelmodel(c)
37 if err != nil {
38 return errors.New("failed to update channel model: " + err.Error())
39 }
40 return nil
41}
42
43func (rm *RecordManager) AcceptChannelDelete(uri string, ctx context.Context) error {
44 err := rm.db.DeleteChannel(uri, ctx)
45 if err != nil {
46 return errors.New("failed to delete channel: " + err.Error())
47 }
48 return rm.broadcaster.DeleteChannel(uri)
49}
50
51func (rm *RecordManager) PostMyChannel(ctx context.Context, pcr *types.PostChannelRequest) (did string, uri string, err error) {
52 return rm.postchannelflow(rm.createMyChannel(), ctx, pcr)
53}
54
55func (rm *RecordManager) PostChannel(cs *atoauth.ClientSession, ctx context.Context, pcr *types.PostChannelRequest) (did string, uri string, err error) {
56 return rm.postchannelflow(rm.createChannel(cs), ctx, pcr)
57}
58
59func (rm *RecordManager) DeleteChannel(cs *atoauth.ClientSession, rkey string, ctx context.Context) error {
60 err := oauth.DeleteXCVRChannel(cs, rkey, ctx)
61 if err != nil {
62 return errors.New("failed to delete channel: " + err.Error())
63 }
64 return rm.AcceptChannelDelete(fmt.Sprintf("at://%s/org.xcvr.feed.channel/%s", cs.Data.AccountDID.String(), rkey), ctx)
65}
66
67func (rm *RecordManager) postchannelflow(f func(*lex.ChannelRecord, *time.Time, context.Context) (*types.Channel, error), ctx context.Context, pcr *types.PostChannelRequest) (did string, uri string, err error) {
68 lcr, now, err := rm.validateChannel(pcr)
69 if err != nil {
70 err = errors.New("couldn't validate channel: " + err.Error())
71 return
72 }
73 channel, err := f(lcr, now, ctx)
74 if err != nil {
75 err = errors.New("couldn't create channel: " + err.Error())
76 return
77 }
78 wasNew, err := rm.storeChannel(channel, ctx)
79 if err != nil {
80 err = errors.New("couldn't store channel: " + err.Error())
81 return
82 }
83 if !wasNew {
84 did = channel.DID
85 uri = channel.URI
86 return
87 }
88 err = rm.initChannel(channel)
89 if err != nil {
90 err = errors.New("couldn't init channel: " + err.Error())
91 return
92 }
93 did = channel.DID
94 uri = channel.URI
95 return
96}
97
98func (rm *RecordManager) storeChannel(c *types.Channel, ctx context.Context) (wasNew bool, err error) {
99 return rm.db.StoreChannel(c, ctx)
100}
101
102func (rm *RecordManager) initChannel(c *types.Channel) error {
103 return rm.broadcaster.AddChannel(c)
104}
105
106func (rm *RecordManager) updateChanneldb(c *types.Channel, ctx context.Context) error {
107 return rm.db.UpdateChannel(c, ctx)
108}
109
110func (rm *RecordManager) updateChannelmodel(c *types.Channel) error {
111 return rm.broadcaster.UpdateChannel(c)
112}
113
114func (rm *RecordManager) createChannel(cs *atoauth.ClientSession) func(*lex.ChannelRecord, *time.Time, context.Context) (*types.Channel, error) {
115 return func(lcr *lex.ChannelRecord, now *time.Time, ctx context.Context) (*types.Channel, error) {
116 uri, cid, err := oauth.CreateXCVRChannel(cs, lcr, ctx)
117 if err != nil {
118 return nil, errors.New("something bad probs happened when posting a channel " + err.Error())
119 }
120 channel := types.Channel{
121 URI: uri,
122 CID: cid,
123 DID: cs.Data.AccountDID.String(),
124 Host: lcr.Host,
125 Title: lcr.Title,
126 Topic: lcr.Topic,
127 CreatedAt: *now,
128 IndexedAt: time.Now(),
129 }
130 return &channel, nil
131 }
132}
133
134func (rm *RecordManager) createMyChannel() func(*lex.ChannelRecord, *time.Time, context.Context) (*types.Channel, error) {
135 return func(lcr *lex.ChannelRecord, now *time.Time, ctx context.Context) (*types.Channel, error) {
136 cid, uri, err := rm.myClient.CreateXCVRChannel(lcr, ctx)
137 if err != nil {
138 return nil, errors.New("something bad probs happened when posting a channel " + err.Error())
139 }
140 channel := types.Channel{
141 URI: uri,
142 CID: cid,
143 DID: atputils.GetMyDid(),
144 Host: lcr.Host,
145 Title: lcr.Title,
146 Topic: lcr.Topic,
147 CreatedAt: *now,
148 IndexedAt: time.Now(),
149 }
150 return &channel, nil
151 }
152}
153
154func (rm *RecordManager) validateChannel(cr *types.PostChannelRequest) (*lex.ChannelRecord, *time.Time, error) {
155 var lcr lex.ChannelRecord
156 if cr.Title == "" || atputils.ValidateGraphemesAndLength(cr.Title, 64, 640) {
157 return nil, nil, errors.New("title empty or too long")
158 }
159 lcr.Title = cr.Title
160 if cr.Host == "" {
161 return nil, nil, errors.New("no host")
162 }
163 lcr.Host = cr.Host
164 if cr.Topic != nil {
165 if atputils.ValidateGraphemesAndLength(*cr.Topic, 256, 2560) {
166 return nil, nil, errors.New("topic too long")
167 }
168 lcr.Topic = cr.Topic
169 }
170
171 dtn := syntax.DatetimeNow()
172 lcr.CreatedAt = dtn.String()
173 time := dtn.Time()
174 return &lcr, &time, nil
175}