230 lines
5.2 KiB
Go
230 lines
5.2 KiB
Go
|
package route
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"encoding/base64"
|
||
|
"encoding/json"
|
||
|
"io/ioutil"
|
||
|
"net/http"
|
||
|
"strings"
|
||
|
|
||
|
"forge.cadoles.com/Cadoles/fake-sms/internal/command"
|
||
|
"forge.cadoles.com/Cadoles/fake-sms/internal/config"
|
||
|
"github.com/pkg/errors"
|
||
|
"gitlab.com/wpetit/goweb/cqrs"
|
||
|
"gitlab.com/wpetit/goweb/logger"
|
||
|
"gitlab.com/wpetit/goweb/middleware/container"
|
||
|
"gitlab.com/wpetit/goweb/service"
|
||
|
)
|
||
|
|
||
|
type ErrorCode int
|
||
|
|
||
|
const (
|
||
|
ErrorCodeInvalidCommand ErrorCode = 99997
|
||
|
ErrorCodeAuthenticationFailure ErrorCode = 99998
|
||
|
ErrorCodeNotEnoughPrivileges ErrorCode = 99999
|
||
|
ErrorCodeMissingSMSID ErrorCode = 1
|
||
|
ErrorCodeMissingMobilePhoneNumber ErrorCode = 2
|
||
|
ErrorCodeInvalidSMSID ErrorCode = 3
|
||
|
ErrorCodeInvalidMobilePhoneNumber ErrorCode = 4
|
||
|
ErrorCodeInvalidCustomData ErrorCode = 5
|
||
|
ErrorCodeInvalidTimeToSend ErrorCode = 6
|
||
|
ErrorCodeAccountSubscribersLimitExceeded ErrorCode = 7
|
||
|
ErrorCodeMobilePhoneNumberCannotBeSaved ErrorCode = 7
|
||
|
ErrorCodeTransactionalIDCannotBeCreated ErrorCode = 9
|
||
|
ErrorCodeSMSSentLimitExceeded ErrorCode = 10
|
||
|
)
|
||
|
|
||
|
type Command string
|
||
|
|
||
|
const (
|
||
|
CommandTransactionalSMSSend = "TransactionalSms.Send"
|
||
|
)
|
||
|
|
||
|
type PowowRequest struct {
|
||
|
APIKey string `json:"ApiKey"`
|
||
|
Command Command
|
||
|
ResponseFormat string
|
||
|
Payload map[string]interface{}
|
||
|
}
|
||
|
|
||
|
type PowowResponse struct {
|
||
|
Success bool
|
||
|
ErrorCode ErrorCode
|
||
|
}
|
||
|
|
||
|
func handlePowowEntrypoint(w http.ResponseWriter, r *http.Request) {
|
||
|
ctx := r.Context()
|
||
|
|
||
|
ctn := container.Must(ctx)
|
||
|
conf := config.Must(ctn)
|
||
|
|
||
|
data, err := ioutil.ReadAll(r.Body)
|
||
|
if err != nil {
|
||
|
logger.Error(ctx, "could not read body", logger.E(errors.WithStack(err)))
|
||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||
|
|
||
|
return
|
||
|
}
|
||
|
defer r.Body.Close()
|
||
|
|
||
|
pr := &PowowRequest{
|
||
|
Payload: make(map[string]interface{}),
|
||
|
}
|
||
|
|
||
|
if err := json.Unmarshal(data, pr); err != nil {
|
||
|
logger.Error(ctx, "could not parse request", logger.E(errors.WithStack(err)))
|
||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if err := json.Unmarshal(data, &pr.Payload); err != nil {
|
||
|
logger.Error(ctx, "could not parse request payload", logger.E(errors.WithStack(err)))
|
||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Authenticate user
|
||
|
if conf.Powow.APIKey != pr.APIKey {
|
||
|
res := &PowowResponse{
|
||
|
Success: false,
|
||
|
ErrorCode: ErrorCodeAuthenticationFailure,
|
||
|
}
|
||
|
|
||
|
sendPowowResponse(w, res)
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Handle Powow command
|
||
|
switch pr.Command {
|
||
|
case CommandTransactionalSMSSend:
|
||
|
handleTransactionalSMSSend(ctx, ctn, w, pr)
|
||
|
|
||
|
return
|
||
|
default:
|
||
|
res := &PowowResponse{
|
||
|
Success: false,
|
||
|
ErrorCode: ErrorCodeInvalidCommand,
|
||
|
}
|
||
|
|
||
|
sendPowowResponse(w, res)
|
||
|
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Mock https://powow4.iroquois.fr/user/docs/api/#send-transactional-sms
|
||
|
func handleTransactionalSMSSend(ctx context.Context, ctn *service.Container, w http.ResponseWriter, req *PowowRequest) {
|
||
|
bus := cqrs.Must(ctn)
|
||
|
conf := config.Must(ctn)
|
||
|
|
||
|
rawSMSID, exists := req.Payload["SmsID"]
|
||
|
if !exists {
|
||
|
sendPowowResponse(w, &PowowResponse{
|
||
|
ErrorCode: ErrorCodeMissingSMSID,
|
||
|
Success: false,
|
||
|
})
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
smsID, ok := rawSMSID.(float64)
|
||
|
if !ok {
|
||
|
sendPowowResponse(w, &PowowResponse{
|
||
|
ErrorCode: ErrorCodeInvalidSMSID,
|
||
|
Success: false,
|
||
|
})
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if smsID < 0 || int(smsID) > len(conf.Powow.SMS)-1 {
|
||
|
sendPowowResponse(w, &PowowResponse{
|
||
|
ErrorCode: ErrorCodeInvalidSMSID,
|
||
|
Success: false,
|
||
|
})
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
customData := make(map[string]interface{})
|
||
|
|
||
|
rawCustomData, exists := req.Payload["CustomData"]
|
||
|
if exists {
|
||
|
customData, ok = rawCustomData.(map[string]interface{})
|
||
|
if !ok {
|
||
|
sendPowowResponse(w, &PowowResponse{
|
||
|
ErrorCode: ErrorCodeInvalidCustomData,
|
||
|
Success: false,
|
||
|
})
|
||
|
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sms := conf.Powow.SMS[int(smsID)]
|
||
|
|
||
|
body, err := createSMSBody(sms.Content, customData)
|
||
|
if err != nil {
|
||
|
panic(errors.Wrap(err, "could not generate sms body"))
|
||
|
}
|
||
|
|
||
|
req.Payload["_Template"] = sms
|
||
|
|
||
|
storeSMS := &command.StoreSMSRequest{
|
||
|
From: sms.From,
|
||
|
Body: body,
|
||
|
Recipient: req.Payload["MobilePhoneNumber"].(string),
|
||
|
Metadata: req.Payload,
|
||
|
}
|
||
|
|
||
|
_, err = bus.Exec(ctx, storeSMS)
|
||
|
if err != nil {
|
||
|
panic(errors.Wrap(err, "could not store sms"))
|
||
|
}
|
||
|
|
||
|
res := &struct {
|
||
|
PowowResponse
|
||
|
TransactionalID int
|
||
|
}{
|
||
|
PowowResponse: PowowResponse{
|
||
|
Success: true,
|
||
|
ErrorCode: 0,
|
||
|
},
|
||
|
TransactionalID: 0,
|
||
|
}
|
||
|
|
||
|
sendPowowResponse(w, res)
|
||
|
}
|
||
|
|
||
|
func createSMSBody(template string, customData map[string]interface{}) (string, error) {
|
||
|
content := template
|
||
|
|
||
|
for k, v := range customData {
|
||
|
decoded, err := base64.StdEncoding.DecodeString(v.(string))
|
||
|
if err != nil {
|
||
|
return "", errors.WithStack(err)
|
||
|
}
|
||
|
|
||
|
key := "%Subscriber:" + k + "%"
|
||
|
|
||
|
content = strings.ReplaceAll(content, key, string(decoded))
|
||
|
}
|
||
|
|
||
|
return content, nil
|
||
|
}
|
||
|
|
||
|
func sendPowowResponse(w http.ResponseWriter, res interface{}) {
|
||
|
w.Header().Add("Content-Type", "application/json")
|
||
|
|
||
|
encoder := json.NewEncoder(w)
|
||
|
encoder.SetIndent("", " ")
|
||
|
|
||
|
if err := encoder.Encode(res); err != nil {
|
||
|
panic(errors.WithStack(err))
|
||
|
}
|
||
|
}
|