underbbs/server/api.go

160 lines
4.2 KiB
Go

package server
import (
"encoding/json"
"errors"
"forge.lightcrystal.systems/lightcrystal/underbbs/adapter"
"forge.lightcrystal.systems/lightcrystal/underbbs/models"
"hacklab.nilfm.cc/quartzgun/renderer"
"hacklab.nilfm.cc/quartzgun/router"
"hacklab.nilfm.cc/quartzgun/util"
"html/template"
"net/http"
"strings"
)
func getSubscriberKey(req *http.Request) (string, error) {
authHeader := req.Header.Get("Authorization")
if strings.HasPrefix(authHeader, "Bearer ") {
return strings.Split(authHeader, "Bearer ")[1], nil
}
return "", errors.New("No subscriber key")
}
func getSubscriberByKey(key string, subscribers map[*Subscriber][]adapter.Adapter) *Subscriber {
for s, _ := range subscribers {
if s.key == key {
return s
}
}
return nil
}
func setAdaptersForSubscriber(key string, adapters []adapter.Adapter, subscribers map[*Subscriber][]adapter.Adapter) error {
var ptr *Subscriber = nil
for s, _ := range subscribers {
if s.key == key {
ptr = s
}
}
if ptr != nil {
subscribers[ptr] = adapters
return nil
}
return errors.New("subscriber not present in map")
}
func apiConfigureAdapters(next http.Handler, subscribers map[*Subscriber][]adapter.Adapter) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
// get subscriber key
skey, err := getSubscriberKey(req)
if err != nil {
w.WriteHeader(500)
return
}
subscriber := getSubscriberByKey(skey, subscribers)
if subscriber == nil {
w.WriteHeader(404)
return
}
// decode adapter config from request body
settings := make([]models.Settings, 0)
err = json.NewDecoder(req.Body).Decode(&settings)
if err != nil {
w.WriteHeader(400)
next.ServeHTTP(w, req)
return
}
// iterate through settings and create adapters
adapters := make([]adapter.Adapter, 0)
for _, s := range settings {
var a adapter.Adapter
switch s.Protocol {
case "nostr":
a = &adapter.NostrAdapter{}
break
default:
break
}
err := a.Init(s, subscriber.data)
if err != nil {
util.AddContextValue(req, "data", err.Error())
w.WriteHeader(500)
next.ServeHTTP(w, req)
}
adapters = append(adapters, a)
}
// TODO: cancel subscriptions on any existing adapters
// store the adapters in the subscriber map
err = setAdaptersForSubscriber(skey, adapters, subscribers)
if err != nil {
util.AddContextValue(req, "data", err.Error())
w.WriteHeader(500)
next.ServeHTTP(w, req)
}
w.WriteHeader(201)
next.ServeHTTP(w, req)
})
}
func apiGetAdapters(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(201)
next.ServeHTTP(w, req)
})
}
func apiAdapterSubscribe(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(201)
next.ServeHTTP(w, req)
})
}
func ProtectWithSubscriberKey(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]
for s, _ := range subscribers {
if s.key == subscriberKey {
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 {
panic("error template was malformed")
}
rtr := &router.Router{
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<id>\S+)/subscribe`, ProtectWithSubscriberKey(
apiAdapterSubscribe(renderer.JSON("data")),
self.subscribers,
))
return http.HandlerFunc(rtr.ServeHTTP)
}