tiny 88x31 lexicon for atproto
at main 305 lines 8.3 kB view raw
1package handler 2 3import ( 4 "fmt" 5 "log" 6 "net/http" 7 "os" 8 "strings" 9 10 "github.com/bluesky-social/indigo/atproto/auth/oauth" 11 "github.com/bluesky-social/indigo/atproto/syntax" 12 "tangled.org/moth11.net/88x31/blobs" 13 "tangled.org/moth11.net/88x31/lex" 14 myoauth "tangled.org/moth11.net/88x31/oauth" 15 "tangled.org/moth11.net/88x31/types" 16) 17 18func (h *Handler) like(cs *oauth.ClientSession, w http.ResponseWriter, r *http.Request) { 19 if cs == nil { 20 http.Error(w, "like requires auth", http.StatusUnauthorized) 21 return 22 } 23 err := r.ParseForm() 24 if err != nil { 25 http.Error(w, "form failed to parse", http.StatusBadRequest) 26 return 27 } 28 uri := r.FormValue("uri") 29 if uri == "" { 30 http.Error(w, "must provide uri, don't tamper with form", http.StatusBadRequest) 31 return 32 } 33 cid := r.FormValue("cid") 34 if cid == "" { 35 http.Error(w, "must provide a cid, don't tamper with form", http.StatusBadRequest) 36 return 37 } 38 var llr lex.LikeRecord 39 lls := lex.LikeSubject{URI: uri, CID: cid} 40 llr.Subject = lls 41 nowsyn := syntax.DatetimeNow() 42 llr.CreatedAt = nowsyn.String() 43 luri, lcid, err := myoauth.CreateLike(cs, &llr, r.Context()) 44 if err != nil { 45 log.Println(err) 46 http.Error(w, "error creating post", http.StatusInternalServerError) 47 return 48 } 49 var like types.Like 50 like.CID = lcid 51 like.URI = luri 52 like.CreatedAt = nowsyn.Time() 53 like.SubjectCID = cid 54 like.SubjectURI = uri 55 like.DID = cs.Data.AccountDID.String() 56 err = h.db.StoreLike(&like, r.Context()) 57 if err != nil { 58 log.Println(err) 59 http.Error(w, "error storing like", http.StatusInternalServerError) 60 return 61 } 62 http.Redirect(w, r, fmt.Sprintf("/button?uri=%s", uri), http.StatusSeeOther) 63} 64 65func (h *Handler) unlike(cs *oauth.ClientSession, w http.ResponseWriter, r *http.Request) { 66 if cs == nil { 67 http.Error(w, "unlike requires auth", http.StatusUnauthorized) 68 return 69 } 70 err := r.ParseForm() 71 if err != nil { 72 http.Error(w, "form failed to parse", http.StatusBadRequest) 73 return 74 } 75 uri := r.FormValue("uri") 76 if uri == "" { 77 http.Error(w, "must provide uri, don't tamper with form", http.StatusBadRequest) 78 return 79 } 80 cid := r.FormValue("cid") 81 if cid == "" { 82 http.Error(w, "must provide a cid, don't tamper with form", http.StatusBadRequest) 83 return 84 } 85 86 ll, err := h.db.GetMyLikesFor(uri, cid, cs.Data.AccountDID.String(), r.Context()) 87 if err != nil { 88 log.Println(err) 89 http.Redirect(w, r, fmt.Sprintf("/button?uri=%s", uri), http.StatusSeeOther) 90 return 91 } 92 for _, likeuricid := range ll { 93 arr := strings.Split(likeuricid, " ") 94 if len(arr) != 2 { 95 http.Error(w, "invalid likeuricid", http.StatusInternalServerError) 96 return 97 } 98 likeuri := arr[0] 99 likecid := arr[1] 100 aturi, err := syntax.ParseATURI(likeuri) 101 if err != nil { 102 http.Error(w, "uri doesn't parse, don't tamper with form", http.StatusBadRequest) 103 return 104 } 105 err = myoauth.DeleteLike(cs, aturi.RecordKey().String(), likecid, r.Context()) 106 if err != nil { 107 log.Println(err) 108 http.Error(w, "error deleting like", http.StatusInternalServerError) 109 return 110 } 111 err = h.db.DeleteLike(likeuri, r.Context()) 112 if err != nil { 113 log.Println(err) 114 http.Error(w, "error deleting cached like", http.StatusInternalServerError) 115 return 116 } 117 } 118 http.Redirect(w, r, fmt.Sprintf("/button?uri=%s", uri), http.StatusSeeOther) 119} 120 121func (h *Handler) delete(cs *oauth.ClientSession, w http.ResponseWriter, r *http.Request) { 122 if cs == nil { 123 http.Error(w, "deletion requires auth", http.StatusUnauthorized) 124 return 125 } 126 err := r.ParseForm() 127 if err != nil { 128 http.Error(w, "form failed to parse", http.StatusBadRequest) 129 return 130 } 131 uri := r.FormValue("uri") 132 if uri == "" { 133 http.Error(w, "must provide uri, don't tamper with form", http.StatusBadRequest) 134 return 135 } 136 aturi, err := syntax.ParseATURI(uri) 137 if err != nil { 138 http.Error(w, "uri should parse", http.StatusBadRequest) 139 return 140 } 141 cid := r.FormValue("cid") 142 if cid == "" { 143 http.Error(w, "must provide a cid, don't tamper with form", http.StatusBadRequest) 144 return 145 } 146 ll, err := h.db.GetMyLikesFor(uri, cid, cs.Data.AccountDID.String(), r.Context()) 147 if err == nil { 148 for _, likeuricid := range ll { 149 arr := strings.Split(likeuricid, " ") 150 if len(arr) != 2 { 151 http.Error(w, "invalid likeuricid", http.StatusInternalServerError) 152 return 153 } 154 likeuri := arr[0] 155 likecid := arr[1] 156 aturi, err := syntax.ParseATURI(likeuri) 157 if err != nil { 158 http.Error(w, "uri doesn't parse, don't tamper with form", http.StatusBadRequest) 159 return 160 } 161 err = myoauth.DeleteLike(cs, aturi.RecordKey().String(), likecid, r.Context()) 162 if err != nil { 163 log.Println(err) 164 http.Error(w, "error deleting like", http.StatusInternalServerError) 165 return 166 } 167 err = h.db.DeleteLike(likeuri, r.Context()) 168 if err != nil { 169 log.Println(err) 170 http.Error(w, "error deleting cached like", http.StatusInternalServerError) 171 return 172 } 173 } 174 } 175 err = myoauth.DeleteButton(cs, aturi.RecordKey().String(), cid, r.Context()) 176 if err != nil { 177 log.Println(err) 178 http.Error(w, "failed to delete button", http.StatusInternalServerError) 179 return 180 } 181 err = h.db.DeleteButton(uri, r.Context()) 182 if err != nil { 183 log.Println(err) 184 http.Error(w, "error deleting cached button", http.StatusInternalServerError) 185 return 186 } 187 http.Redirect(w, r, "/", http.StatusSeeOther) 188} 189 190func (h *Handler) upload(cs *oauth.ClientSession, w http.ResponseWriter, r *http.Request) { 191 if cs == nil { 192 http.Error(w, "upload requires auth", http.StatusUnauthorized) 193 return 194 } 195 err := r.ParseMultipartForm(1 << 21) 196 if err != nil { 197 http.Error(w, "form failed to parse", http.StatusBadRequest) 198 return 199 } 200 alt := r.FormValue("alt") 201 title := r.FormValue("title") 202 href := r.FormValue("href") 203 204 file, fheader, err := r.FormFile("button") 205 if err != nil { 206 http.Error(w, "form lacks button", http.StatusBadRequest) 207 return 208 } 209 defer file.Close() 210 ct := fheader.Header.Get("Content-Type") 211 if !strings.HasPrefix(ct, "image/") { 212 http.Error(w, "button must be image", http.StatusBadRequest) 213 return 214 } 215 blob, err := myoauth.UploadBLOB(cs, file, fheader, r.Context()) 216 if err != nil { 217 http.Error(w, "failed to upload blob", http.StatusInternalServerError) 218 return 219 } 220 var lbr lex.ButtonRecord 221 if blob == nil { 222 http.Error(w, "recieved nil blob", http.StatusInternalServerError) 223 return 224 } 225 lbr.Blob = *blob 226 if alt != "" { 227 lbr.Alt = &alt 228 } 229 if title != "" { 230 lbr.Title = &title 231 } 232 if href != "" { 233 lbr.Href = &href 234 } 235 nowsyn := syntax.DatetimeNow() 236 lbr.PostedAt = nowsyn.String() 237 uri, cid, err := myoauth.CreateButton(cs, &lbr, r.Context()) 238 if err != nil { 239 log.Println(err) 240 http.Error(w, "error creating button", http.StatusInternalServerError) 241 return 242 } 243 var tbr types.Button 244 tbr.Alt = lbr.Alt 245 tbr.Title = lbr.Title 246 tbr.HREF = lbr.Href 247 tbr.BlobCID = blob.Ref.String() 248 tbr.BlobMIME = ct 249 tbr.URI = uri 250 tbr.CID = cid 251 tbr.DID = cs.Data.AccountDID.String() 252 tbr.PostedAt = nowsyn.Time() 253 err = h.db.StoreButton(&tbr, r.Context()) 254 if err != nil { 255 log.Println(err) 256 } 257 http.Redirect(w, r, fmt.Sprintf("/button?uri=%s", uri), http.StatusFound) 258} 259 260func (h *Handler) getButton(w http.ResponseWriter, r *http.Request) { 261 vals := r.URL.Query() 262 var did string 263 var cid string 264 uri := vals.Get("uri") 265 var button *types.Button 266 var err error 267 if uri != "" { 268 button, err = h.db.GetButton(uri, r.Context()) 269 if err == nil { 270 did = button.DID 271 cid = button.BlobCID 272 } 273 } else { 274 http.Error(w, "provide a uri!", http.StatusBadRequest) 275 return 276 } 277 ib, _ := h.db.IsBanned(did, r.Context()) 278 if ib { 279 http.Error(w, "i don't serve banned content", 404) 280 return 281 } 282 imgPath, err := blobs.AddImageToCache(did, cid, r.Context()) 283 if err != nil { 284 http.Error(w, "error adding to cache, likely bad size", http.StatusInternalServerError) 285 return 286 } 287 288 stats, err := os.Stat(imgPath) 289 if err != nil { 290 http.Error(w, "error recieving from cache, strange...", http.StatusInternalServerError) 291 return 292 } 293 294 mime := button.BlobMIME 295 w.Header().Add("Content-Type", mime) 296 w.Header().Add("Content-Length", fmt.Sprintf("%d", stats.Size())) 297 w.Header().Add("Cache-Control", "public, max-age=31536000, immutable") 298 299 img, err := os.Open(imgPath) 300 if err != nil { 301 http.Error(w, "error reading from cache, strange...", http.StatusInternalServerError) 302 return 303 } 304 img.WriteTo(w) 305}