mirror of
https://github.com/pocketbase/pocketbase.git
synced 2024-12-12 05:24:27 +02:00
90 lines
2.8 KiB
Go
90 lines
2.8 KiB
Go
|
package apis
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
"time"
|
||
|
|
||
|
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||
|
"github.com/go-ozzo/ozzo-validation/v4/is"
|
||
|
"github.com/pocketbase/pocketbase/core"
|
||
|
"github.com/pocketbase/pocketbase/mails"
|
||
|
"github.com/pocketbase/pocketbase/tools/routine"
|
||
|
)
|
||
|
|
||
|
func recordRequestVerification(e *core.RequestEvent) error {
|
||
|
collection, err := findAuthCollection(e)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if collection.Name == core.CollectionNameSuperusers {
|
||
|
return e.BadRequestError("All superusers are verified by default.", nil)
|
||
|
}
|
||
|
|
||
|
form := new(recordRequestVerificationForm)
|
||
|
if err = e.BindBody(form); err != nil {
|
||
|
return firstApiError(err, e.BadRequestError("An error occurred while loading the submitted data.", err))
|
||
|
}
|
||
|
if err = form.validate(); err != nil {
|
||
|
return firstApiError(err, e.BadRequestError("An error occurred while validating the submitted data.", err))
|
||
|
}
|
||
|
|
||
|
record, err := e.App.FindAuthRecordByEmail(collection, form.Email)
|
||
|
if err != nil {
|
||
|
// eagerly write 204 response as a very basic measure against emails enumeration
|
||
|
e.NoContent(http.StatusNoContent)
|
||
|
return fmt.Errorf("failed to fetch %s record with email %s: %w", collection.Name, form.Email, err)
|
||
|
}
|
||
|
|
||
|
resendKey := getVerificationResendKey(record)
|
||
|
if !record.Verified() && e.App.Store().Has(resendKey) {
|
||
|
// eagerly write 204 response as a very basic measure against emails enumeration
|
||
|
e.NoContent(http.StatusNoContent)
|
||
|
return errors.New("try again later - you've already requested a verification email")
|
||
|
}
|
||
|
|
||
|
event := new(core.RecordRequestVerificationRequestEvent)
|
||
|
event.RequestEvent = e
|
||
|
event.Collection = collection
|
||
|
event.Record = record
|
||
|
|
||
|
return e.App.OnRecordRequestVerificationRequest().Trigger(event, func(e *core.RecordRequestVerificationRequestEvent) error {
|
||
|
if e.Record.Verified() {
|
||
|
return e.NoContent(http.StatusNoContent)
|
||
|
}
|
||
|
|
||
|
// run in background because we don't need to show the result to the client
|
||
|
app := e.App
|
||
|
routine.FireAndForget(func() {
|
||
|
if err := mails.SendRecordVerification(app, e.Record); err != nil {
|
||
|
app.Logger().Error("Failed to send verification email", "error", err)
|
||
|
}
|
||
|
|
||
|
app.Store().Set(resendKey, struct{}{})
|
||
|
time.AfterFunc(2*time.Minute, func() {
|
||
|
app.Store().Remove(resendKey)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
return e.NoContent(http.StatusNoContent)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// -------------------------------------------------------------------
|
||
|
|
||
|
type recordRequestVerificationForm struct {
|
||
|
Email string `form:"email" json:"email"`
|
||
|
}
|
||
|
|
||
|
func (form *recordRequestVerificationForm) validate() error {
|
||
|
return validation.ValidateStruct(form,
|
||
|
validation.Field(&form.Email, validation.Required, validation.Length(1, 255), is.EmailFormat),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
func getVerificationResendKey(record *core.Record) string {
|
||
|
return "@limitVerificationEmail_" + record.Collection().Id + record.Id
|
||
|
}
|