repo_description: Open Source realtime backend in 1 file
repo_topics: authentication backend golang realtime
code: package core
  
  import (
  	"context"
  	"errors"
  	"time"
  
  	"github.com/pocketbase/pocketbase/tools/types"
  )
  
  const CollectionNameOTPs = "_otps"
  
  var (
  	_ Model        = (*OTP)(nil)
  	_ PreValidator = (*OTP)(nil)
  	_ RecordProxy  = (*OTP)(nil)
  )
  
  // OTP defines a Record proxy for working with the otps collection.
  type OTP struct {
  	*Record
  }
  
  // NewOTP instantiates and returns a new blank *OTP model.
  //
  // Example usage:
  //
  //	otp := core.NewOTP(app)
  //	otp.SetRecordRef(user.Id)
  //	otp.SetCollectionRef(user.Collection().Id)
  //	otp.SetPassword(security.RandomStringWithAlphabet(6, "1234567890"))
  //	app.Save(otp)
  func NewOTP(app App) *OTP {
  	m := &OTP{}
  
  	c, err := app.FindCachedCollectionByNameOrId(CollectionNameOTPs)
  	if err != nil {
  		// this is just to make tests easier since otp is a system collection and it is expected to be always accessible
  		// (note: the loaded record is further checked on OTP.PreValidate())
  		c = NewBaseCollection("__invalid__")
  	}
  
  	m.Record = NewRecord(c)
  
  	return m
  }
  
  // PreValidate implements the [PreValidator] interface and checks
  // whether the proxy is properly loaded.
  func (m *OTP) PreValidate(ctx context.Context, app App) error {
  	if m.Record == nil || m.Record.Collection().Name != CollectionNameOTPs {
  		return errors.New("missing or invalid otp ProxyRecord")
  	}
  
  	return nil
  }
  
  // ProxyRecord returns the proxied Record model.
  func (m *OTP) ProxyRecord() *Record {
  	return m.Record
  }
  
  // SetProxyRecord loads the specified record model into the current proxy.
  func (m *OTP) SetProxyRecord(record *Record) {
  	m.Record = record
  }
  
  // CollectionRef returns the "collectionRef" field value.
  func (m *OTP) CollectionRef() string {
  	return m.GetString("collectionRef")
  }
  
  // SetCollectionRef updates the "collectionRef" record field value.
  func (m *OTP) SetCollectionRef(collectionId string) {
  	m.Set("collectionRef", collectionId)
  }
  
  // RecordRef returns the "recordRef" record field value.
  func (m *OTP) RecordRef() string {
  	return m.GetString("recordRef")
  }
  
  // SetRecordRef updates the "recordRef" record field value.
  func (m *OTP) SetRecordRef(recordId string) {
  	m.Set("recordRef", recordId)
  }
  
  // SentTo returns the "sentTo" record field value.
  //
  // It could be any string value (email, phone, message app id, etc.)
  // and usually is used as part of the auth flow to update the verified
  // user state in case for example the sentTo value matches with the user record email.
  func (m *OTP) SentTo() string {
  	return m.GetString("sentTo")
  }
  
  // SetSentTo updates the "sentTo" record field value.
  func (m *OTP) SetSentTo(val string) {
  	m.Set("sentTo", val)
  }
  
  // Created returns the "created" record field value.
  func (m *OTP) Created() types.DateTime {
  	return m.GetDateTime("created")
  }
  
  // Updated returns the "updated" record field value.
  func (m *OTP) Updated() types.DateTime {
  	return m.GetDateTime("updated")
  }
  
  // HasExpired checks if the otp is expired, aka. whether it has been
  // more than maxElapsed time since its creation.
  func (m *OTP) HasExpired(maxElapsed time.Duration) bool {
  	return time.Since(m.Created().Time()) > maxElapsed
  }
  
  func (app *BaseApp) registerOTPHooks() {
  	recordRefHooks[*OTP](app, CollectionNameOTPs, CollectionTypeAuth)
  
  	// run on every hour to cleanup expired otp sessions
  	app.Cron().Add("__pbOTPCleanup__", "0 * * * *", func() {
  		if err := app.DeleteExpiredOTPs(); err != nil {
  			app.Logger().Warn("Failed to delete expired OTP sessions", "error", err)
  		}
  	})
  }
  