You've already forked pocketbase
							
							
				mirror of
				https://github.com/pocketbase/pocketbase.git
				synced 2025-10-31 08:37:38 +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
 | |
| }
 |