package server
import (
"context"
"net/http"
"github.com/keys-pub/keys"
"github.com/keys-pub/keys-ext/http/api"
wsapi "github.com/keys-pub/keys-ext/ws/api"
"github.com/keys-pub/keys/dstore"
"github.com/keys-pub/keys/encoding"
"github.com/labstack/echo/v4"
"github.com/pkg/errors"
)
func (s *Server) postDirectMessage(c echo.Context) error {
s.logger.Infof("Server %s %s", c.Request().Method, c.Request().URL.String())
ctx := c.Request().Context()
body, st, err := readBody(c, true, 16*1024)
if err != nil {
return s.ErrResponse(c, st, err)
}
auth, err := s.auth(c, newAuth("Authorization", "sender", body))
if err != nil {
return s.ErrForbidden(c, err)
}
recipient, err := keys.ParseID(c.Param("recipient"))
if err != nil {
return s.ErrBadRequest(c, errors.Errorf("invalid recipient"))
}
follow, err := s.follow(ctx, recipient, auth.KID)
if err != nil {
return s.ErrInternalServer(c, err)
}
if follow == nil {
return s.ErrForbidden(c, errors.Errorf("not authorized to direct"))
}
path := dstore.Path("dms", recipient)
_, idx, err := s.fi.EventsAdd(ctx, path, [][]byte{body})
if err != nil {
return err
}
dt, err := s.loadDirectToken(ctx, recipient)
if err != nil {
return s.ErrInternalServer(c, err)
}
if err := s.notifyDirectMessage(ctx, dt, idx); err != nil {
return err
}
var out struct{}
return JSON(c, http.StatusOK, out)
}
func (s *Server) loadDirectToken(ctx context.Context, user keys.ID) (*api.DirectToken, error) {
var dt api.DirectToken
if _, err := s.fi.Load(ctx, dstore.Path("directs", user), &dt); err != nil {
return nil, err
}
if dt.Token == "" {
dt.User = user
dt.Token = encoding.MustEncode(keys.Rand32()[:], encoding.Base62)
if err := s.fi.Set(ctx, dstore.Path("directs", user), dstore.From(dt), dstore.MergeAll()); err != nil {
return nil, err
}
}
return &dt, nil
}
func (s *Server) notifyDirectMessage(ctx context.Context, dt *api.DirectToken, idx int64) error {
if s.internalKey == nil {
return errors.Errorf("no secret key set")
}
event := &wsapi.Event{
User: dt.User,
Index: idx,
Token: dt.Token,
}
b, err := wsapi.Encrypt(event, s.internalKey)
if err != nil {
return err
}
if err := s.rds.Publish(ctx, wsapi.EventPubSub, b); err != nil {
return err
}
return nil
}
func (s *Server) getDirectMessages(c echo.Context) error {
s.logger.Infof("Server %s %s", c.Request().Method, c.Request().URL.String())
auth, err := s.auth(c, newAuth("Authorization", "recipient", nil))
if err != nil {
return s.ErrForbidden(c, err)
}
limit := 1000
path := dstore.Path("dms", auth.KID)
resp, st, err := s.events(c, path, limit)
if err != nil {
return s.ErrResponse(c, st, err)
}
truncated := false
if len(resp.Events) >= limit {
truncated = true
}
out := &api.Events{
Events: resp.Events,
Index: resp.Index,
Truncated: truncated,
}
return JSON(c, http.StatusOK, out)
}
func (s *Server) getDirectToken(c echo.Context) error {
s.logger.Infof("Server %s %s", c.Request().Method, c.Request().URL.String())
ctx := c.Request().Context()
auth, err := s.auth(c, newAuth("Authorization", "recipient", nil))
if err != nil {
return s.ErrForbidden(c, err)
}
dt, err := s.loadDirectToken(ctx, auth.KID)
if err != nil {
return s.ErrInternalServer(c, err)
}
out := &api.DirectToken{
User: dt.User,
Token: dt.Token,
}
return JSON(c, http.StatusOK, out)
}