package core import ( "context" "net/http" "time" "github.com/pocketbase/pocketbase/tools/auth" "github.com/pocketbase/pocketbase/tools/hook" "github.com/pocketbase/pocketbase/tools/mailer" "github.com/pocketbase/pocketbase/tools/router" "github.com/pocketbase/pocketbase/tools/search" "github.com/pocketbase/pocketbase/tools/subscriptions" "golang.org/x/crypto/acme/autocert" ) type HookTagger interface { HookTags() []string } // ------------------------------------------------------------------- type baseModelEventData struct { Model Model } func (e *baseModelEventData) Tags() []string { if e.Model == nil { return nil } if ht, ok := e.Model.(HookTagger); ok { return ht.HookTags() } return []string{e.Model.TableName()} } // ------------------------------------------------------------------- type baseRecordEventData struct { Record *Record } func (e *baseRecordEventData) Tags() []string { if e.Record == nil { return nil } return e.Record.HookTags() } // ------------------------------------------------------------------- type baseCollectionEventData struct { Collection *Collection } func (e *baseCollectionEventData) Tags() []string { if e.Collection == nil { return nil } tags := make([]string, 0, 2) if e.Collection.Id != "" { tags = append(tags, e.Collection.Id) } if e.Collection.Name != "" { tags = append(tags, e.Collection.Name) } return tags } // ------------------------------------------------------------------- // App events data // ------------------------------------------------------------------- type BootstrapEvent struct { hook.Event App App } type TerminateEvent struct { hook.Event App App IsRestart bool } type BackupEvent struct { hook.Event App App Context context.Context Name string // the name of the backup to create/restore. Exclude []string // list of dir entries to exclude from the backup create/restore. } type ServeEvent struct { hook.Event App App Router *router.Router[*RequestEvent] Server *http.Server CertManager *autocert.Manager } // ------------------------------------------------------------------- // Settings events data // ------------------------------------------------------------------- type SettingsListRequestEvent struct { hook.Event *RequestEvent Settings *Settings } type SettingsUpdateRequestEvent struct { hook.Event *RequestEvent OldSettings *Settings NewSettings *Settings } type SettingsReloadEvent struct { hook.Event App App } // ------------------------------------------------------------------- // Mailer events data // ------------------------------------------------------------------- type MailerEvent struct { hook.Event App App Mailer mailer.Mailer Message *mailer.Message } type MailerRecordEvent struct { MailerEvent baseRecordEventData Meta map[string]any } // ------------------------------------------------------------------- // Model events data // ------------------------------------------------------------------- const ( ModelEventTypeCreate = "create" ModelEventTypeUpdate = "update" ModelEventTypeDelete = "delete" ModelEventTypeValidate = "validate" ) type ModelEvent struct { hook.Event App App baseModelEventData Context context.Context // Could be any of the ModelEventType* constants, like: // - create // - update // - delete // - validate Type string } type ModelErrorEvent struct { ModelEvent Error error } // ------------------------------------------------------------------- // Record events data // ------------------------------------------------------------------- type RecordEvent struct { hook.Event App App baseRecordEventData Context context.Context // Could be any of the ModelEventType* constants, like: // - create // - update // - delete // - validate Type string } type RecordErrorEvent struct { RecordEvent Error error } func syncModelEventWithRecordEvent(me *ModelEvent, re *RecordEvent) { me.App = re.App me.Context = re.Context me.Type = re.Type // @todo enable if after profiling doesn't have significant impact // skip for now to avoid excessive checks and assume that the // Model and the Record fields still points to the same instance // // if _, ok := me.Model.(*Record); ok { // me.Model = re.Record // } else if proxy, ok := me.Model.(RecordProxy); ok { // proxy.SetProxyRecord(re.Record) // } } func newRecordEventFromModelEvent(me *ModelEvent) (*RecordEvent, bool) { record, ok := me.Model.(*Record) if !ok { proxy, ok := me.Model.(RecordProxy) if !ok { return nil, false } record = proxy.ProxyRecord() } re := new(RecordEvent) re.App = me.App re.Context = me.Context re.Type = me.Type re.Record = record return re, true } func newRecordErrorEventFromModelErrorEvent(me *ModelErrorEvent) (*RecordErrorEvent, bool) { recordEvent, ok := newRecordEventFromModelEvent(&me.ModelEvent) if !ok { return nil, false } re := new(RecordErrorEvent) re.RecordEvent = *recordEvent re.Error = me.Error return re, true } func syncModelErrorEventWithRecordErrorEvent(me *ModelErrorEvent, re *RecordErrorEvent) { syncModelEventWithRecordEvent(&me.ModelEvent, &re.RecordEvent) me.Error = re.Error } // ------------------------------------------------------------------- // Collection events data // ------------------------------------------------------------------- type CollectionEvent struct { hook.Event App App baseCollectionEventData Context context.Context // Could be any of the ModelEventType* constants, like: // - create // - update // - delete // - validate Type string } type CollectionErrorEvent struct { CollectionEvent Error error } func syncModelEventWithCollectionEvent(me *ModelEvent, ce *CollectionEvent) { me.App = ce.App me.Context = ce.Context me.Type = ce.Type me.Model = ce.Collection } func newCollectionEventFromModelEvent(me *ModelEvent) (*CollectionEvent, bool) { record, ok := me.Model.(*Collection) if !ok { return nil, false } ce := new(CollectionEvent) ce.App = me.App ce.Context = me.Context ce.Type = me.Type ce.Collection = record return ce, true } func newCollectionErrorEventFromModelErrorEvent(me *ModelErrorEvent) (*CollectionErrorEvent, bool) { collectionevent, ok := newCollectionEventFromModelEvent(&me.ModelEvent) if !ok { return nil, false } ce := new(CollectionErrorEvent) ce.CollectionEvent = *collectionevent ce.Error = me.Error return ce, true } func syncModelErrorEventWithCollectionErrorEvent(me *ModelErrorEvent, ce *CollectionErrorEvent) { syncModelEventWithCollectionEvent(&me.ModelEvent, &ce.CollectionEvent) me.Error = ce.Error } // ------------------------------------------------------------------- // File API events data // ------------------------------------------------------------------- type FileTokenRequestEvent struct { hook.Event *RequestEvent baseRecordEventData Token string } type FileDownloadRequestEvent struct { hook.Event *RequestEvent baseCollectionEventData Record *Record FileField *FileField ServedPath string ServedName string } // ------------------------------------------------------------------- // Collection API events data // ------------------------------------------------------------------- type CollectionsListRequestEvent struct { hook.Event *RequestEvent Collections []*Collection Result *search.Result } type CollectionsImportRequestEvent struct { hook.Event *RequestEvent CollectionsData []map[string]any DeleteMissing bool } type CollectionRequestEvent struct { hook.Event *RequestEvent baseCollectionEventData } // ------------------------------------------------------------------- // Realtime API events data // ------------------------------------------------------------------- type RealtimeConnectRequestEvent struct { hook.Event *RequestEvent Client subscriptions.Client // note: modifying it after the connect has no effect IdleTimeout time.Duration } type RealtimeMessageEvent struct { hook.Event *RequestEvent Client subscriptions.Client Message *subscriptions.Message } type RealtimeSubscribeRequestEvent struct { hook.Event *RequestEvent Client subscriptions.Client Subscriptions []string } // ------------------------------------------------------------------- // Record CRUD API events data // ------------------------------------------------------------------- type RecordsListRequestEvent struct { hook.Event *RequestEvent baseCollectionEventData // @todo consider removing and maybe add as generic to the search.Result? Records []*Record Result *search.Result } type RecordRequestEvent struct { hook.Event *RequestEvent baseCollectionEventData Record *Record } type RecordEnrichEvent struct { hook.Event App App baseRecordEventData RequestInfo *RequestInfo } // ------------------------------------------------------------------- // Auth Record API events data // ------------------------------------------------------------------- type RecordCreateOTPRequestEvent struct { hook.Event *RequestEvent baseCollectionEventData Record *Record Password string } type RecordAuthWithOTPRequestEvent struct { hook.Event *RequestEvent baseCollectionEventData Record *Record OTP *OTP } type RecordAuthRequestEvent struct { hook.Event *RequestEvent baseCollectionEventData Record *Record Token string Meta any AuthMethod string } type RecordAuthWithPasswordRequestEvent struct { hook.Event *RequestEvent baseCollectionEventData Record *Record Identity string IdentityField string Password string } type RecordAuthWithOAuth2RequestEvent struct { hook.Event *RequestEvent baseCollectionEventData ProviderName string ProviderClient auth.Provider Record *Record OAuth2User *auth.AuthUser CreateData map[string]any IsNewRecord bool } type RecordAuthRefreshRequestEvent struct { hook.Event *RequestEvent baseCollectionEventData Record *Record } type RecordRequestPasswordResetRequestEvent struct { hook.Event *RequestEvent baseCollectionEventData Record *Record } type RecordConfirmPasswordResetRequestEvent struct { hook.Event *RequestEvent baseCollectionEventData Record *Record } type RecordRequestVerificationRequestEvent struct { hook.Event *RequestEvent baseCollectionEventData Record *Record } type RecordConfirmVerificationRequestEvent struct { hook.Event *RequestEvent baseCollectionEventData Record *Record } type RecordRequestEmailChangeRequestEvent struct { hook.Event *RequestEvent baseCollectionEventData Record *Record NewEmail string } type RecordConfirmEmailChangeRequestEvent struct { hook.Event *RequestEvent baseCollectionEventData Record *Record NewEmail string }