From 264be94427f1d05aef350d0717e53878e8a9e17f Mon Sep 17 00:00:00 2001 From: Iris Lightshard Date: Mon, 9 Dec 2024 10:35:44 -0700 Subject: [PATCH] fix bonks on timeline, flesh out HonkAdapter.Do and add an API wrapper --- adapter/honk.go | 70 +++++++++++++++++++++++++++++++++++++--- cli/cli.go | 10 ++++++ frontend/ts/websocket.ts | 4 +-- server/api.go | 63 ++++++++++++++++++++++++++++-------- 4 files changed, 128 insertions(+), 19 deletions(-) diff --git a/adapter/honk.go b/adapter/honk.go index 65e86c0..d416346 100644 --- a/adapter/honk.go +++ b/adapter/honk.go @@ -1,10 +1,13 @@ package adapter import ( + "bytes" "encoding/json" "errors" "fmt" . "forge.lightcrystal.systems/nilix/underbbs/models" + "io" + "mime/multipart" "net/http" "net/url" "os" @@ -111,7 +114,9 @@ func (self *HonkAdapter) Stop() { } func (self *HonkAdapter) Subscribe(filter string, target *string) []error { - + if self.isAnonymous() { + return nil + } if self.stop != nil { close(self.stop) } @@ -199,7 +204,8 @@ func (self *HonkAdapter) toMsg(h honk, target *string) Message { } if h.Oonker != "" { r := fmt.Sprintf("%s/bonk/%d", h.Honker, h.ID) - msg.Renoter = &h.Oonker + fmt.Println(r) + msg.Renoter = &h.Honker msg.RenoteId = &r msg.RenoteTime = &tt } @@ -223,14 +229,70 @@ func (self *HonkAdapter) Fetch(etype string, ids []string) error { return aaa.Fetch(etype, ids) } +func (self *HonkAdapter) donk(data map[string]string) error { + var b bytes.Buffer + w := multipart.NewWriter(&b) + fw, err := w.CreateFormField("action") + io.Copy(fw, strings.NewReader("honk")) + fw, err = w.CreateFormField("token") + io.Copy(fw, strings.NewReader(self.token)) + for k, v := range data { + if k == "file" { + if fw, err = w.CreateFormFile("donk", "donk"); err != nil { + return err + } + } else { + fieldName := "noise" + switch k { + case "desc": + fieldName = "donkdesc" + case "content": + fieldName = "noise" + } + if fw, err = w.CreateFormField(fieldName); err != nil { + return err + } + } + if _, err := io.Copy(fw, strings.NewReader(v)); err != nil { + return err + } + } + w.Close() + + req, err := http.NewRequest("POST", self.server+"/api", &b) + if err != nil { + return err + } + + req.Header.Set("Content-Type", w.FormDataContentType()) + c := http.Client{} + res, err := c.Do(req) + if err != nil { + return err + } + if res.StatusCode < 400 { + return nil + } else { + return errors.New(fmt.Sprintf("status: %d", res.StatusCode)) + } +} + func (self *HonkAdapter) Do(action string, data map[string]string) error { + if self.isAnonymous() { + return nil + } switch action { case "post": - res, err := http.PostForm(self.server+"/api", url.Values{ + honkForm := url.Values{ "action": []string{"honk"}, "token": []string{self.token}, "noise": []string{data["content"]}, - }) + } + _, exists := data["file"] + if exists { + return self.donk(data) + } + res, err := http.PostForm(self.server+"/api", honkForm) if err != nil { return err } diff --git a/cli/cli.go b/cli/cli.go index 0421c8a..f7754ad 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -105,6 +105,16 @@ func Process(args ...string) error { aa := strings.Split(a, "=") k := aa[0] v := strings.Join(aa[1:], "=") + if k == "file" { + b, err := ioutil.ReadFile(v) + if err != nil { + return err + } + v = string(b) + if err != nil { + return err + } + } data[k] = v } } diff --git a/frontend/ts/websocket.ts b/frontend/ts/websocket.ts index 3686a50..31fd876 100644 --- a/frontend/ts/websocket.ts +++ b/frontend/ts/websocket.ts @@ -89,11 +89,11 @@ export class DatagramSocket { }); } if (data.target) { + console.log("data has target: " + data.target); let e = document.querySelector(`underbbs-timeline#${data.target}[data-adapter="${data.adapter}"]`); if (e) { - console.log("setting latest...") - e.setAttribute("data-latest", data.id); + e.setAttribute("data-latest", data.renoteId ?? data.id); } } } diff --git a/server/api.go b/server/api.go index 6933c65..55cd93f 100644 --- a/server/api.go +++ b/server/api.go @@ -1,6 +1,7 @@ package server import ( + "encoding/base64" "encoding/json" "errors" "fmt" @@ -15,6 +16,13 @@ import ( "strings" ) +type doRequest struct { + Action string `json:"action"` + Content *string `json:"content,omitempty"` + File *string `json:"file,omitempty"` + Desc *string `json:"desc,omitempty"` +} + func getSubscriberKey(req *http.Request) (string, error) { authHeader := req.Header.Get("Authorization") if strings.HasPrefix(authHeader, "Bearer ") { @@ -114,13 +122,6 @@ func apiConfigureAdapters(next http.Handler, subscribers map[*Subscriber][]adapt }) } -func apiGetAdapters(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(201) - next.ServeHTTP(w, req) - }) -} - type subscribeParams struct { Filter string `json:"filter"` Target *string `json:"target,omitempty"` @@ -203,6 +204,43 @@ func apiAdapterFetch(next http.Handler, subscribers map[*Subscriber][]adapter.Ad }) } +func apiAdapterDo(next http.Handler, subscribers map[*Subscriber][]adapter.Adapter) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + authHeader := req.Header.Get("Authorization") + if strings.HasPrefix(authHeader, "Bearer ") { + subscriberKey := strings.Split(authHeader, "Bearer ")[1] + s := getSubscriberByKey(subscriberKey, subscribers) + if s != nil { + apiParams := req.Context().Value("params").(map[string]string) + for _, a := range subscribers[s] { + if a.Name() == apiParams["adapter_id"] { + // request body is json + // if we have a `file`, it needs to be transformed from base64 to standard bytes/string + doReq := map[string]string{} + err := json.NewDecoder(req.Body).Decode(&doReq) + if err != nil { + w.WriteHeader(422) + return + } + if f, exists := doReq["file"]; exists { + rawFile, err := base64.StdEncoding.DecodeString(f) + if err != nil { + w.WriteHeader(500) + return + } + doReq["file"] = string(rawFile) + } + a.Do(doReq["action"], doReq) + next.ServeHTTP(w, req) + return + } + } + } + } + w.WriteHeader(http.StatusUnauthorized) + }) +} + func (self *BBSServer) apiMux() http.Handler { errTemplate, err := template.New("err").Parse("{{ $params := (.Context).Value \"params\" }}

ERROR {{ $params.ErrorCode }}

{{ $params.ErrorMessage }}

") if err != nil { @@ -212,17 +250,11 @@ func (self *BBSServer) apiMux() http.Handler { Fallback: *errTemplate, } - // adapters (POST & GET) rtr.Post("/adapters", ProtectWithSubscriberKey( apiConfigureAdapters(renderer.JSON("data"), self.subscribers), self.subscribers, )) - rtr.Get("/adapters", ProtectWithSubscriberKey( - apiGetAdapters(renderer.JSON("data")), - self.subscribers, - )) - // adapters/:name/subscribe rtr.Post(`/adapters/(?P\S+)/subscribe`, ProtectWithSubscriberKey( apiAdapterSubscribe(renderer.JSON("data"), self.subscribers), self.subscribers, @@ -233,5 +265,10 @@ func (self *BBSServer) apiMux() http.Handler { self.subscribers, )) + rtr.Post(`adapters/(?P\S+)/do`, ProtectWithSubscriberKey( + apiAdapterDo(renderer.JSON("data"), self.subscribers), + self.subscribers, + )) + return http.HandlerFunc(rtr.ServeHTTP) }