WIP Twilio

This commit is contained in:
binwiederhier 2023-05-12 21:47:41 -04:00
parent 214efbde36
commit cea434a57c
34 changed files with 311 additions and 143 deletions

View file

@ -455,6 +455,8 @@ func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visit
return s.ensureUser(s.withAccountSync(s.handleAccountPhoneNumberAdd))(w, r, v)
} else if r.Method == http.MethodPost && r.URL.Path == apiAccountPhonePath {
return s.ensureUser(s.withAccountSync(s.handleAccountPhoneNumberVerify))(w, r, v)
} else if r.Method == http.MethodDelete && r.URL.Path == apiAccountPhonePath {
return s.ensureUser(s.withAccountSync(s.handleAccountPhoneNumberDelete))(w, r, v)
} else if r.Method == http.MethodGet && r.URL.Path == apiStatsPath {
return s.handleStats(w, r, v)
} else if r.Method == http.MethodGet && r.URL.Path == apiTiersPath {
@ -692,6 +694,9 @@ func (s *Server) handlePublishInternal(r *http.Request, v *visitor) (*message, e
} else if call != "" && !vrate.CallAllowed() {
return nil, errHTTPTooManyRequestsLimitCalls.With(t)
}
// FIXME check allowed phone numbers
if m.PollID != "" {
m = newPollRequestMessage(t.ID, m.PollID)
}

View file

@ -146,13 +146,7 @@ func (s *Server) handleAccountGet(w http.ResponseWriter, r *http.Request, v *vis
return err
}
if len(phoneNumbers) > 0 {
response.PhoneNumbers = make([]*apiAccountPhoneNumberResponse, 0)
for _, p := range phoneNumbers {
response.PhoneNumbers = append(response.PhoneNumbers, &apiAccountPhoneNumberResponse{
Number: p.Number,
Verified: p.Verified,
})
}
response.PhoneNumbers = phoneNumbers
}
} else {
response.Username = user.Everyone
@ -542,19 +536,15 @@ func (s *Server) handleAccountPhoneNumberAdd(w http.ResponseWriter, r *http.Requ
} else if u.IsUser() && u.Tier.CallLimit == 0 {
return errHTTPUnauthorized
}
// Actually add the unverified number, and send verification
logvr(v, r).
Tag(tagAccount).
Fields(log.Context{
"number": req.Number,
}).
Debug("Adding phone number, and sending verification")
if err := s.userManager.AddPhoneNumber(u.ID, req.Number); err != nil {
if err == user.ErrPhoneNumberExists {
return errHTTPConflictPhoneNumberExists
}
// Check if phone number exists
phoneNumbers, err := s.userManager.PhoneNumbers(u.ID)
if err != nil {
return err
} else if util.Contains(phoneNumbers, req.Number) {
return errHTTPConflictPhoneNumberExists
}
// Actually add the unverified number, and send verification
logvr(v, r).Tag(tagAccount).Field("phone_number", req.Number).Debug("Sending phone number verification")
if err := s.verifyPhone(v, r, req.Number); err != nil {
return err
}
@ -570,31 +560,27 @@ func (s *Server) handleAccountPhoneNumberVerify(w http.ResponseWriter, r *http.R
if !phoneNumberRegex.MatchString(req.Number) {
return errHTTPBadRequestPhoneNumberInvalid
}
// Get phone numbers, and check if it's in the list
phoneNumbers, err := s.userManager.PhoneNumbers(u.ID)
if err != nil {
return err
}
found := false
for _, phoneNumber := range phoneNumbers {
if phoneNumber.Number == req.Number && !phoneNumber.Verified {
found = true
break
}
}
if !found {
return errHTTPBadRequestPhoneNumberInvalid
}
if err := s.checkVerifyPhone(v, r, req.Number, req.Code); err != nil {
return err
}
logvr(v, r).
Tag(tagAccount).
Fields(log.Context{
"number": req.Number,
}).
Debug("Marking phone number as verified")
if err := s.userManager.MarkPhoneNumberVerified(u.ID, req.Number); err != nil {
logvr(v, r).Tag(tagAccount).Field("phone_number", req.Number).Debug("Adding phone number as verified")
if err := s.userManager.AddPhoneNumber(u.ID, req.Number); err != nil {
return err
}
return s.writeJSON(w, newSuccessResponse())
}
func (s *Server) handleAccountPhoneNumberDelete(w http.ResponseWriter, r *http.Request, v *visitor) error {
u := v.User()
req, err := readJSONWithLimit[apiAccountPhoneNumberRequest](r.Body, jsonBodyBytesLimit, false)
if err != nil {
return err
}
if !phoneNumberRegex.MatchString(req.Number) {
return errHTTPBadRequestPhoneNumberInvalid
}
logvr(v, r).Tag(tagAccount).Field("phone_number", req.Number).Debug("Deleting phone number")
if err := s.userManager.DeletePhoneNumber(u.ID, req.Number); err != nil {
return err
}
return s.writeJSON(w, newSuccessResponse())

View file

@ -282,11 +282,6 @@ type apiAccountPhoneNumberRequest struct {
Code string `json:"code,omitempty"` // Only supplied in "verify" call
}
type apiAccountPhoneNumberResponse struct {
Number string `json:"number"`
Verified bool `json:"verified"`
}
type apiAccountTier struct {
Code string `json:"code"`
Name string `json:"name"`
@ -336,19 +331,19 @@ type apiAccountBilling struct {
}
type apiAccountResponse struct {
Username string `json:"username"`
Role string `json:"role,omitempty"`
SyncTopic string `json:"sync_topic,omitempty"`
Language string `json:"language,omitempty"`
Notification *user.NotificationPrefs `json:"notification,omitempty"`
Subscriptions []*user.Subscription `json:"subscriptions,omitempty"`
Reservations []*apiAccountReservation `json:"reservations,omitempty"`
Tokens []*apiAccountTokenResponse `json:"tokens,omitempty"`
PhoneNumbers []*apiAccountPhoneNumberResponse `json:"phone_numbers,omitempty"`
Tier *apiAccountTier `json:"tier,omitempty"`
Limits *apiAccountLimits `json:"limits,omitempty"`
Stats *apiAccountStats `json:"stats,omitempty"`
Billing *apiAccountBilling `json:"billing,omitempty"`
Username string `json:"username"`
Role string `json:"role,omitempty"`
SyncTopic string `json:"sync_topic,omitempty"`
Language string `json:"language,omitempty"`
Notification *user.NotificationPrefs `json:"notification,omitempty"`
Subscriptions []*user.Subscription `json:"subscriptions,omitempty"`
Reservations []*apiAccountReservation `json:"reservations,omitempty"`
Tokens []*apiAccountTokenResponse `json:"tokens,omitempty"`
PhoneNumbers []string `json:"phone_numbers,omitempty"`
Tier *apiAccountTier `json:"tier,omitempty"`
Limits *apiAccountLimits `json:"limits,omitempty"`
Stats *apiAccountStats `json:"stats,omitempty"`
Billing *apiAccountBilling `json:"billing,omitempty"`
}
type apiAccountReservationRequest struct {