fix bonks on timeline, flesh out HonkAdapter.Do and add an API wrapper

This commit is contained in:
Iris Lightshard 2024-12-09 10:35:44 -07:00
parent ab8249e0bf
commit 264be94427
Signed by: Iris Lightshard
GPG key ID: 688407174966CAF3
4 changed files with 128 additions and 19 deletions

View file

@ -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
}

View file

@ -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
}
}

View file

@ -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);
}
}
}

View file

@ -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\" }}<html><body><h1>ERROR {{ $params.ErrorCode }}</h1><p class='error'>{{ $params.ErrorMessage }}</p></body></html>")
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<adapter_id>\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<adapter-id>\S+)/do`, ProtectWithSubscriberKey(
apiAdapterDo(renderer.JSON("data"), self.subscribers),
self.subscribers,
))
return http.HandlerFunc(rtr.ServeHTTP)
}