package handler import ( "fmt" "log" "net/http" "os" "strings" "github.com/bluesky-social/indigo/atproto/auth/oauth" "github.com/bluesky-social/indigo/atproto/syntax" "tangled.org/moth11.net/88x31/blobs" "tangled.org/moth11.net/88x31/lex" myoauth "tangled.org/moth11.net/88x31/oauth" "tangled.org/moth11.net/88x31/types" ) func (h *Handler) like(cs *oauth.ClientSession, w http.ResponseWriter, r *http.Request) { if cs == nil { http.Error(w, "like requires auth", http.StatusUnauthorized) return } err := r.ParseForm() if err != nil { http.Error(w, "form failed to parse", http.StatusBadRequest) return } uri := r.FormValue("uri") if uri == "" { http.Error(w, "must provide uri, don't tamper with form", http.StatusBadRequest) return } cid := r.FormValue("cid") if cid == "" { http.Error(w, "must provide a cid, don't tamper with form", http.StatusBadRequest) return } var llr lex.LikeRecord lls := lex.LikeSubject{URI: uri, CID: cid} llr.Subject = lls nowsyn := syntax.DatetimeNow() llr.CreatedAt = nowsyn.String() luri, lcid, err := myoauth.CreateLike(cs, &llr, r.Context()) if err != nil { log.Println(err) http.Error(w, "error creating post", http.StatusInternalServerError) return } var like types.Like like.CID = lcid like.URI = luri like.CreatedAt = nowsyn.Time() like.SubjectCID = cid like.SubjectURI = uri like.DID = cs.Data.AccountDID.String() err = h.db.StoreLike(&like, r.Context()) if err != nil { log.Println(err) http.Error(w, "error storing like", http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/button?uri=%s", uri), http.StatusSeeOther) } func (h *Handler) unlike(cs *oauth.ClientSession, w http.ResponseWriter, r *http.Request) { if cs == nil { http.Error(w, "unlike requires auth", http.StatusUnauthorized) return } err := r.ParseForm() if err != nil { http.Error(w, "form failed to parse", http.StatusBadRequest) return } uri := r.FormValue("uri") if uri == "" { http.Error(w, "must provide uri, don't tamper with form", http.StatusBadRequest) return } cid := r.FormValue("cid") if cid == "" { http.Error(w, "must provide a cid, don't tamper with form", http.StatusBadRequest) return } ll, err := h.db.GetMyLikesFor(uri, cid, cs.Data.AccountDID.String(), r.Context()) if err != nil { log.Println(err) http.Redirect(w, r, fmt.Sprintf("/button?uri=%s", uri), http.StatusSeeOther) return } for _, likeuricid := range ll { arr := strings.Split(likeuricid, " ") if len(arr) != 2 { http.Error(w, "invalid likeuricid", http.StatusInternalServerError) return } likeuri := arr[0] likecid := arr[1] aturi, err := syntax.ParseATURI(likeuri) if err != nil { http.Error(w, "uri doesn't parse, don't tamper with form", http.StatusBadRequest) return } err = myoauth.DeleteLike(cs, aturi.RecordKey().String(), likecid, r.Context()) if err != nil { log.Println(err) http.Error(w, "error deleting like", http.StatusInternalServerError) return } err = h.db.DeleteLike(likeuri, r.Context()) if err != nil { log.Println(err) http.Error(w, "error deleting cached like", http.StatusInternalServerError) return } } http.Redirect(w, r, fmt.Sprintf("/button?uri=%s", uri), http.StatusSeeOther) } func (h *Handler) delete(cs *oauth.ClientSession, w http.ResponseWriter, r *http.Request) { if cs == nil { http.Error(w, "deletion requires auth", http.StatusUnauthorized) return } err := r.ParseForm() if err != nil { http.Error(w, "form failed to parse", http.StatusBadRequest) return } uri := r.FormValue("uri") if uri == "" { http.Error(w, "must provide uri, don't tamper with form", http.StatusBadRequest) return } aturi, err := syntax.ParseATURI(uri) if err != nil { http.Error(w, "uri should parse", http.StatusBadRequest) return } cid := r.FormValue("cid") if cid == "" { http.Error(w, "must provide a cid, don't tamper with form", http.StatusBadRequest) return } ll, err := h.db.GetMyLikesFor(uri, cid, cs.Data.AccountDID.String(), r.Context()) if err == nil { for _, likeuricid := range ll { arr := strings.Split(likeuricid, " ") if len(arr) != 2 { http.Error(w, "invalid likeuricid", http.StatusInternalServerError) return } likeuri := arr[0] likecid := arr[1] aturi, err := syntax.ParseATURI(likeuri) if err != nil { http.Error(w, "uri doesn't parse, don't tamper with form", http.StatusBadRequest) return } err = myoauth.DeleteLike(cs, aturi.RecordKey().String(), likecid, r.Context()) if err != nil { log.Println(err) http.Error(w, "error deleting like", http.StatusInternalServerError) return } err = h.db.DeleteLike(likeuri, r.Context()) if err != nil { log.Println(err) http.Error(w, "error deleting cached like", http.StatusInternalServerError) return } } } err = myoauth.DeleteButton(cs, aturi.RecordKey().String(), cid, r.Context()) if err != nil { log.Println(err) http.Error(w, "failed to delete button", http.StatusInternalServerError) return } err = h.db.DeleteButton(uri, r.Context()) if err != nil { log.Println(err) http.Error(w, "error deleting cached button", http.StatusInternalServerError) return } http.Redirect(w, r, "/", http.StatusSeeOther) } func (h *Handler) upload(cs *oauth.ClientSession, w http.ResponseWriter, r *http.Request) { if cs == nil { http.Error(w, "upload requires auth", http.StatusUnauthorized) return } err := r.ParseMultipartForm(1 << 21) if err != nil { http.Error(w, "form failed to parse", http.StatusBadRequest) return } alt := r.FormValue("alt") title := r.FormValue("title") href := r.FormValue("href") file, fheader, err := r.FormFile("button") if err != nil { http.Error(w, "form lacks button", http.StatusBadRequest) return } defer file.Close() ct := fheader.Header.Get("Content-Type") if !strings.HasPrefix(ct, "image/") { http.Error(w, "button must be image", http.StatusBadRequest) return } blob, err := myoauth.UploadBLOB(cs, file, fheader, r.Context()) if err != nil { http.Error(w, "failed to upload blob", http.StatusInternalServerError) return } var lbr lex.ButtonRecord if blob == nil { http.Error(w, "recieved nil blob", http.StatusInternalServerError) return } lbr.Blob = *blob if alt != "" { lbr.Alt = &alt } if title != "" { lbr.Title = &title } if href != "" { lbr.Href = &href } nowsyn := syntax.DatetimeNow() lbr.PostedAt = nowsyn.String() uri, cid, err := myoauth.CreateButton(cs, &lbr, r.Context()) if err != nil { log.Println(err) http.Error(w, "error creating button", http.StatusInternalServerError) return } var tbr types.Button tbr.Alt = lbr.Alt tbr.Title = lbr.Title tbr.HREF = lbr.Href tbr.BlobCID = blob.Ref.String() tbr.BlobMIME = ct tbr.URI = uri tbr.CID = cid tbr.DID = cs.Data.AccountDID.String() tbr.PostedAt = nowsyn.Time() err = h.db.StoreButton(&tbr, r.Context()) if err != nil { log.Println(err) } http.Redirect(w, r, fmt.Sprintf("/button?uri=%s", uri), http.StatusFound) } func (h *Handler) getButton(w http.ResponseWriter, r *http.Request) { vals := r.URL.Query() var did string var cid string uri := vals.Get("uri") var button *types.Button var err error if uri != "" { button, err = h.db.GetButton(uri, r.Context()) if err == nil { did = button.DID cid = button.BlobCID } } else { http.Error(w, "provide a uri!", http.StatusBadRequest) return } ib, _ := h.db.IsBanned(did, r.Context()) if ib { http.Error(w, "i don't serve banned content", 404) return } imgPath, err := blobs.AddImageToCache(did, cid, r.Context()) if err != nil { http.Error(w, "error adding to cache, likely bad size", http.StatusInternalServerError) return } stats, err := os.Stat(imgPath) if err != nil { http.Error(w, "error recieving from cache, strange...", http.StatusInternalServerError) return } mime := button.BlobMIME w.Header().Add("Content-Type", mime) w.Header().Add("Content-Length", fmt.Sprintf("%d", stats.Size())) w.Header().Add("Cache-Control", "public, max-age=31536000, immutable") img, err := os.Open(imgPath) if err != nil { http.Error(w, "error reading from cache, strange...", http.StatusInternalServerError) return } img.WriteTo(w) }