backend for xcvr appview
at main 174 lines 3.7 kB view raw
1package model 2 3import ( 4 "context" 5 "errors" 6 "fmt" 7 "net/http" 8 "os" 9 "rvcx/internal/atputils" 10 "rvcx/internal/lex" 11 "rvcx/internal/types" 12 "time" 13 14 "github.com/gorilla/websocket" 15) 16 17type client struct { 18 conn *websocket.Conn 19 bus chan any 20} 21 22func (cm *channelModel) WSHandler(uri string, m *Model) http.HandlerFunc { 23 return func(w http.ResponseWriter, r *http.Request) { 24 upgrader := &websocket.Upgrader{ 25 CheckOrigin: func(r *http.Request) bool { 26 return true 27 }, 28 } 29 conn, err := upgrader.Upgrade(w, r, nil) 30 if err != nil { 31 return 32 } 33 defer conn.Close() 34 35 bus := make(chan any, 10) 36 client := &client{ 37 conn, 38 bus, 39 } 40 cm.clientsmu.Lock() 41 cm.clients[client] = true 42 cm.clientsmu.Unlock() 43 44 client.wsWriter() 45 cm.logger.Deprintln("i am a lex stream wshandler and i am exiting") 46 47 cm.clientsmu.Lock() 48 delete(cm.clients, client) 49 cm.clientsmu.Unlock() 50 } 51} 52 53func (c *client) wsWriter() { 54 ticker := time.NewTicker(15 * time.Second) 55 for { 56 select { 57 case <-ticker.C: 58 err := c.conn.WriteControl(websocket.PingMessage, nil, time.Now().Add(5*time.Second)) 59 if err != nil { 60 return 61 } 62 case e, ok := <-c.bus: 63 if !ok { 64 return 65 } 66 c.conn.WriteJSON(e) 67 } 68 } 69} 70 71// func (cm *channelModel) cleanUp() { 72// cm.clientsmu.Lock() 73// defer cm.clientsmu.Unlock() 74// for cli := range cm.clients { 75// close(cli.bus) 76// } 77// } 78 79func (cm *channelModel) broadcast(a any) { 80 cm.clientsmu.Lock() 81 defer cm.clientsmu.Unlock() 82 for cli := range cm.clients { 83 select { 84 case cli.bus <- a: 85 default: 86 delete(cm.clients, cli) 87 close(cli.bus) 88 } 89 } 90} 91 92func (m *Model) BroadcastSignet(uri string, s *types.Signet) error { 93 cm := m.uriMap[uri] 94 if cm == nil { 95 return errors.New("AAAAAAAAAAA") 96 } 97 ihandle, err := m.store.ResolveDid(s.IssuerDID, context.Background()) 98 if err != nil { 99 ihandle, err = atputils.TryLookupDid(context.Background(), s.IssuerDID) 100 if err != nil { 101 return errors.New("AAAAAAAAAAAAAAAAAAAAA") 102 } 103 go m.store.StoreDidHandle(s.IssuerDID, ihandle, context.Background()) 104 } 105 sv := types.SignetView{ 106 URI: s.URI, 107 Issuer: s.IssuerDID, 108 ChannelURI: s.ChannelURI, 109 LrcId: s.MessageID, 110 Author: s.Author, 111 AuthorHandle: s.AuthorHandle, 112 StartedAt: s.StartedAt, 113 } 114 cm.broadcast(sv) 115 return nil 116} 117 118func (m *Model) BroadcastMessage(uri string, msg *types.Message) error { 119 cm := m.uriMap[uri] 120 if cm == nil { 121 return errors.New("failed to map uri to lsm!") 122 } 123 pv, err := m.store.GetProfileView(msg.DID, context.Background()) 124 if err != nil { 125 return errors.New("failed to get profile view: " + err.Error()) 126 } 127 mv := types.MessageView{ 128 URI: msg.URI, 129 Author: *pv, 130 Body: msg.Body, 131 Nick: msg.Nick, 132 Color: msg.Color, 133 SignetURI: msg.SignetURI, 134 PostedAt: msg.PostedAt, 135 } 136 cm.broadcast(mv) 137 return nil 138} 139 140func (m *Model) BroadcastImage(uri string, media *types.Image) error { 141 cm := m.uriMap[uri] 142 if cm == nil { 143 return errors.New("failed to map uri to lsm!") 144 } 145 pv, err := m.store.GetProfileView(media.DID, context.Background()) 146 if err != nil { 147 return errors.New("failed to get profile view: " + err.Error()) 148 } 149 var ar *lex.AspectRatio 150 if media.Width != nil && media.Height != nil { 151 ar = &lex.AspectRatio{ 152 Width: *media.Width, 153 Height: *media.Height, 154 } 155 } 156 src := fmt.Sprintf("https://%s/xrpc/org.xcvr.lrc.getImage?uri=%s", os.Getenv("MY_IDENTITY"), media.URI) 157 158 img := types.ImageView{ 159 Alt: media.Alt, 160 Src: &src, 161 AspectRatio: ar, 162 } 163 mv := types.MediaView{ 164 URI: media.URI, 165 Author: *pv, 166 Image: &img, 167 Nick: media.Nick, 168 Color: media.Color, 169 SignetURI: media.SignetURI, 170 PostedAt: media.PostedAt, 171 } 172 cm.broadcast(mv) 173 return nil 174}