|
|
|
@ -45,6 +45,45 @@ func (s *server) defaultErrorHandler(err error, c echo.Context) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *server) response(c echo.Context, status int, data interface{}) error {
|
|
|
|
|
return c.JSON(status, data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *server) responseOK(c echo.Context, data interface{}) error {
|
|
|
|
|
return s.response(c, http.StatusOK, data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *server) responseCreated(c echo.Context, data interface{}) error {
|
|
|
|
|
return s.response(c, http.StatusCreated, data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *server) responseNoContent(c echo.Context) error {
|
|
|
|
|
return c.NoContent(http.StatusNoContent)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *server) error(c echo.Context, status int, err error) error {
|
|
|
|
|
type genericError struct {
|
|
|
|
|
Error string `json:"error"`
|
|
|
|
|
}
|
|
|
|
|
return c.JSON(status, genericError{Error: err.Error()})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *server) errorBadRequest(c echo.Context, err error) error {
|
|
|
|
|
return s.error(c, http.StatusBadRequest, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *server) errorNotFound(c echo.Context, err error) error {
|
|
|
|
|
s.logger.Warn("client error", zap.Error(err))
|
|
|
|
|
return s.error(c, http.StatusNotFound, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *server) errorInternal(c echo.Context, err error) error {
|
|
|
|
|
s.logger.Error("server error", zap.Error(err))
|
|
|
|
|
// Overwrite error so not to leak internal errors to clients
|
|
|
|
|
err = fmt.Errorf("internal server error")
|
|
|
|
|
return s.error(c, http.StatusInternalServerError, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *server) handleInitMultisig() echo.HandlerFunc {
|
|
|
|
|
type member struct{}
|
|
|
|
|
|
|
|
|
@ -74,9 +113,7 @@ func (s *server) handleInitMultisig() echo.HandlerFunc {
|
|
|
|
|
in := &input{}
|
|
|
|
|
|
|
|
|
|
if err := c.Bind(in); err != nil {
|
|
|
|
|
return c.JSON(http.StatusBadRequest, map[string]interface{}{
|
|
|
|
|
"error": err.Error(),
|
|
|
|
|
})
|
|
|
|
|
return s.errorBadRequest(c, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: validate input!
|
|
|
|
@ -84,17 +121,13 @@ func (s *server) handleInitMultisig() echo.HandlerFunc {
|
|
|
|
|
// Check if input contains manager's username
|
|
|
|
|
managerNickname := s.config.ManagementCredentials.Username()
|
|
|
|
|
if _, ok := in.Members[managerNickname]; ok {
|
|
|
|
|
return c.JSON(http.StatusBadRequest, map[string]interface{}{
|
|
|
|
|
"error": fmt.Sprintf("username %s already taken", managerNickname),
|
|
|
|
|
})
|
|
|
|
|
return s.errorBadRequest(c, fmt.Errorf("username `%s` already taken", managerNickname))
|
|
|
|
|
}
|
|
|
|
|
in.Members[managerNickname] = member{}
|
|
|
|
|
|
|
|
|
|
topic, err := uuid.GenerateUUID()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return c.JSON(http.StatusInternalServerError, map[string]interface{}{
|
|
|
|
|
"error": "failed to generate UUID",
|
|
|
|
|
})
|
|
|
|
|
return s.errorInternal(c, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memberSecrets := map[string]string{}
|
|
|
|
@ -102,9 +135,7 @@ func (s *server) handleInitMultisig() echo.HandlerFunc {
|
|
|
|
|
// TODO: put password length in config.
|
|
|
|
|
token, err := password.Generate(18, 6, 0, false, false)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return c.JSON(http.StatusInternalServerError, map[string]interface{}{
|
|
|
|
|
"error": "failed to generate a token",
|
|
|
|
|
})
|
|
|
|
|
return s.errorInternal(c, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
member := members.Member{
|
|
|
|
@ -121,9 +152,7 @@ func (s *server) handleInitMultisig() echo.HandlerFunc {
|
|
|
|
|
}
|
|
|
|
|
encoded, err := encodeSecret(secret)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return c.JSON(http.StatusInternalServerError, map[string]interface{}{
|
|
|
|
|
"error": "failed to encode secret data",
|
|
|
|
|
})
|
|
|
|
|
return s.errorInternal(c, err)
|
|
|
|
|
}
|
|
|
|
|
memberSecrets[nickname] = encoded
|
|
|
|
|
}
|
|
|
|
@ -136,7 +165,7 @@ func (s *server) handleInitMultisig() echo.HandlerFunc {
|
|
|
|
|
Secrets: memberSecrets,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return c.JSON(http.StatusCreated, out)
|
|
|
|
|
return s.responseCreated(c, out)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -152,19 +181,21 @@ func (s *server) handleListMessages() echo.HandlerFunc {
|
|
|
|
|
topic := c.Param("topic")
|
|
|
|
|
offset, err := offsetToNumber(c.QueryParam("offset"))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return c.JSON(http.StatusBadRequest, map[string]interface{}{
|
|
|
|
|
"error": "offset is not a number",
|
|
|
|
|
})
|
|
|
|
|
return s.errorBadRequest(c, fmt.Errorf("offset is not a number"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
messages, err := s.messageLog.List(topic, int(offset))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return c.JSON(http.StatusNotFound, map[string]interface{}{
|
|
|
|
|
"error": err.Error(),
|
|
|
|
|
})
|
|
|
|
|
switch err.(type) {
|
|
|
|
|
case *messagelog.ErrInvalidMessage:
|
|
|
|
|
return s.errorBadRequest(c, err)
|
|
|
|
|
case *messagelog.ErrTopicNotFound:
|
|
|
|
|
return s.errorNotFound(c, err)
|
|
|
|
|
}
|
|
|
|
|
return s.errorInternal(c, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return c.JSON(http.StatusOK, messages)
|
|
|
|
|
return s.responseOK(c, messages)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -173,27 +204,27 @@ func (s *server) handlePushMessage() echo.HandlerFunc {
|
|
|
|
|
message := messagelog.Message{}
|
|
|
|
|
|
|
|
|
|
if err := c.Bind(&message); err != nil {
|
|
|
|
|
return c.JSON(http.StatusBadRequest, map[string]interface{}{
|
|
|
|
|
"error": "invalid message format",
|
|
|
|
|
})
|
|
|
|
|
return s.errorBadRequest(c, fmt.Errorf("invalid message format"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
member, ok := c.Get("member").(members.Member)
|
|
|
|
|
if !ok {
|
|
|
|
|
return c.JSON(http.StatusInternalServerError, map[string]interface{}{
|
|
|
|
|
"error": "internal server error",
|
|
|
|
|
})
|
|
|
|
|
return s.errorInternal(c, fmt.Errorf("missing member information from the middleware"))
|
|
|
|
|
}
|
|
|
|
|
message.Sender = member.Nickname
|
|
|
|
|
|
|
|
|
|
topic := c.Param("topic")
|
|
|
|
|
if err := s.messageLog.Push(topic, message); err != nil {
|
|
|
|
|
return c.JSON(http.StatusNotFound, map[string]interface{}{
|
|
|
|
|
"error": err.Error(),
|
|
|
|
|
})
|
|
|
|
|
switch err.(type) {
|
|
|
|
|
case *messagelog.ErrInvalidMessage:
|
|
|
|
|
return s.errorBadRequest(c, err)
|
|
|
|
|
case *messagelog.ErrTopicNotFound:
|
|
|
|
|
return s.errorNotFound(c, err)
|
|
|
|
|
}
|
|
|
|
|
return s.errorInternal(c, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return c.String(http.StatusNoContent, "")
|
|
|
|
|
return s.responseNoContent(c)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|