6
1
mirror of https://git.mills.io/saltyim/saltyim.git synced 2024-06-16 03:48:24 +00:00

Add support for performing lookups via the broker we were served from (#99)

Depends on #95

Co-authored-by: James Mills <prologic@shortcircuit.net.au>
Reviewed-on: https://git.mills.io/saltyim/saltyim/pulls/99
This commit is contained in:
James Mills 2022-03-31 00:51:38 +00:00
parent 554f927eaa
commit 57a6ff5ec1
8 changed files with 152 additions and 12 deletions

@ -70,7 +70,7 @@ generate: ## Genereate any code required by the build
echo 'Running in debug mode...'; \
fi
PWA_SRCS = $(shell ls *.go) $(shell find ./internal/pwa -type f)
PWA_SRCS = $(shell ls *.go) ./internal/server.go $(shell find ./internal/pwa -type f)
internal/web/app.wasm: $(PWA_SRCS)
@GOARCH=wasm GOOS=js $(GOCMD) build -o ./internal/web/app.wasm ./internal/pwa/

@ -98,6 +98,8 @@ type Client struct {
id *Identity
key *keys.EdX25519Key
cache addrCache
lookup Lookuper
}
// NewClient reeturns a new Salty IM client for sending and receiving
@ -125,10 +127,11 @@ func NewClient(me *Addr, options ...IdentityOption) (*Client, error) {
log.Debugf("Endpoint is %s", me.Endpoint())
return &Client{
me: me,
id: id,
key: id.key,
cache: make(addrCache),
me: me,
id: id,
key: id.key,
cache: make(addrCache),
lookup: &DirectLookup{},
}, nil
}
@ -138,7 +141,7 @@ func (cli *Client) getAddr(user string) (*Addr, error) {
return addr, nil
}
addr, err := LookupAddr(user)
addr, err := cli.lookup.LookupAddr(user)
if err != nil {
return nil, fmt.Errorf("error: failed to lookup user %s: %w", user, err)
}
@ -252,6 +255,12 @@ func (cli *Client) String() string {
return b.String()
}
// SetLookup sets the internal lookup interface to use (Lookuper) for looking
// up Salty Addresses. By default the DirectLookup implementation is used.
func (cli *Client) SetLookup(lookup Lookuper) {
cli.lookup = lookup
}
// Drain drains this user's inbox by simulteneiously reading until empty anda
// subscribing to the inbox for new messages.
func (cli *Client) Drain(ctx context.Context, extraenvs, prehook, posthook string) chan Message {
@ -376,7 +385,7 @@ func (cli *Client) SendToAddr(addr *Addr, msg string) error {
// Register sends a registration request to the service user of a Salty Broker
func (cli *Client) Register() error {
svc, err := LookupAddr(fmt.Sprintf("%s@%s", ServiceUser, cli.Me().Domain))
svc, err := cli.lookup.LookupAddr(fmt.Sprintf("%s@%s", ServiceUser, cli.Me().Domain))
if err != nil {
return fmt.Errorf("error looking up service user on %s: %w", cli.Me().Domain, err)
}

@ -7,8 +7,8 @@ import (
"github.com/mlctrez/goapp-mdc/pkg/button"
"github.com/mlctrez/goapp-mdc/pkg/icon"
"github.com/mlctrez/goapp-mdc/pkg/textfield"
"go.mills.io/saltyim"
"go.mills.io/saltyim/internal/pwa/storage"
"go.mills.io/saltyim/internal/pwa/utils"
)
type NewChat struct {
@ -61,7 +61,7 @@ func (n *NewChat) Render() app.UI {
func (n *NewChat) newChat() func(button app.HTMLButton) {
return func(button app.HTMLButton) {
button.OnClick(func(ctx app.Context, e app.Event) {
addr, err := saltyim.LookupAddr(n.user.Value)
addr, err := utils.LookupAddr(n.user.Value)
if err != nil {
n.dialog.ShowDialog("error", err.Error())
return

@ -107,6 +107,7 @@ func (h *SaltyChat) connect(ctx app.Context) {
h.dialog.ShowDialog("error setting up client", err.Error())
return
}
newClient.SetLookup(&saltyim.ProxyLookup{LookupEndpoint: app.Getenv("LookupEndpoint")})
client = newClient

@ -0,0 +1,41 @@
package utils
import (
"encoding/json"
"io/ioutil"
"net/http"
"github.com/maxence-charriere/go-app/v9/pkg/app"
"go.mills.io/saltyim"
)
// LookupAddr performs a lookup of a Salty Addr directly and if the request
// fails for whatever reason (usuaully due to Cross-Orogin-Resource-Sharing
// policies / CORS) it uses the Salty Broker the PWA was served from
// initially to perform the lookup by proxying the lookup through the broker.
// Why? CORS sucks.
func LookupAddr(user string) (*saltyim.Addr, error) {
addr, err := saltyim.LookupAddr(user)
if err == nil {
return addr, nil
}
// Fallback to proxying the lookup through the broker...
res, err := saltyim.Request(http.MethodGet, app.Getenv("LookupEndpoint")+user, nil, nil)
if err != nil {
return nil, err
}
defer res.Body.Close()
data, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
if err := json.Unmarshal(data, &addr); err != nil {
return nil, err
}
return addr, nil
}

@ -7,6 +7,7 @@ import (
"net/http"
"os/signal"
"path/filepath"
"strings"
"syscall"
"time"
@ -291,6 +292,11 @@ func (s *Server) initRoutes() {
Name: "Salty Chat",
ShortName: "Salty Chat",
Description: "Secure, easy, self-hosted messaging",
Env: map[string]string{
"LookupEndpoint": strings.TrimSuffix(s.config.BaseURL, "/") + "/api/v1/lookup/",
},
Icon: app.Icon{
Large: "/web/favicon-lg.png",
Default: "/web/favicon-ap.png",

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:95a9c74c800295301b1cb5a08cba6679ee5c16bd9f91ac0cdf6c44e808a33df7
size 27991136
oid sha256:6756046e89856a68e31c8dde6a2c1229674064e47dbf815fd87465a193e45e2c
size 28023655

@ -4,6 +4,7 @@ import (
"crypto/sha256"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
@ -13,9 +14,16 @@ import (
)
var (
_ json.Marshaler = (*Addr)(nil)
_ json.Marshaler = (*Addr)(nil)
_ json.Unmarshaler = (*Addr)(nil)
_ Lookuper = (*DirectLookup)(nil)
_ Lookuper = (*ProxyLookup)(nil)
)
type Lookuper interface {
LookupAddr(user string) (*Addr, error)
}
func fetchConfig(addr string) (Config, Capabilities, error) {
// Attempt using hash
res, err := Request(http.MethodGet, addr, nil, nil)
@ -79,6 +87,37 @@ func (a *Addr) MarshalJSON() ([]byte, error) {
})
}
func (a *Addr) UnmarshalJSON(data []byte) error {
res := struct {
Addr string
User string
Domain string
Key string
Endpoint string
}{}
if err := json.Unmarshal(data, &res); err != nil {
return err
}
a.User = res.User
a.Domain = res.Domain
u, err := url.Parse(res.Endpoint)
if err != nil {
return fmt.Errorf("error parsing endpoint %q: %w", res.Endpoint, err)
}
a.endpoint = u
key, err := keys.NewEdX25519PublicKeyFromID(keys.ID(res.Key))
if err != nil {
return fmt.Errorf("error parsing public key %q: %w", res.Key, err)
}
a.key = key
return nil
}
func (a *Addr) String() string {
return fmt.Sprintf("%s@%s", a.User, a.Domain)
}
@ -198,3 +237,47 @@ func LookupAddr(addr string) (*Addr, error) {
}
return a, nil
}
// DirectLookup performs a direct lookup request
type DirectLookup struct{}
// LookupAddr performs a lookup of a Salty Addr directly
func (l *DirectLookup) LookupAddr(user string) (*Addr, error) {
return LookupAddr(user)
}
// ProxyLookup proxies lookup requests through a Salty Broker's /api/v1/lookup endpoint
type ProxyLookup struct {
// LookupEndpoint is the uri of the lookup endpoint of a broker
LookupEndpoint string
}
// LookupAddr performs a lookup of a Salty Addr directly and if the request fails for
// whatever reason (usuaully due to Cross-Orogin-Resource-Sharing policies / CORS) it
// uses the Salty Broker the PWA was served from initially to perform the lookup by
// proxying the lookup through the broker. Why? CORS sucks.
func (l *ProxyLookup) LookupAddr(user string) (*Addr, error) {
addr, err := LookupAddr(user)
if err == nil {
return addr, nil
}
// Fallback to proxying the lookup through the broker...
res, err := Request(http.MethodGet, l.LookupEndpoint+user, nil, nil)
if err != nil {
return nil, err
}
defer res.Body.Close()
data, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
if err := json.Unmarshal(data, &addr); err != nil {
return nil, err
}
return addr, nil
}