added WithConfig factory to all forms
This commit is contained in:
parent
b0ca9b2f1b
commit
a426484916
|
@ -10,7 +10,7 @@ import (
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AdminLogin defines an admin email/pass login form.
|
// AdminLogin specifies an admin email/pass login form.
|
||||||
type AdminLogin struct {
|
type AdminLogin struct {
|
||||||
config AdminLoginConfig
|
config AdminLoginConfig
|
||||||
|
|
||||||
|
@ -20,20 +20,20 @@ type AdminLogin struct {
|
||||||
|
|
||||||
// AdminLoginConfig is the [AdminLogin] factory initializer config.
|
// AdminLoginConfig is the [AdminLogin] factory initializer config.
|
||||||
//
|
//
|
||||||
// NB! Dao is a required struct member.
|
// NB! App is a required struct member.
|
||||||
type AdminLoginConfig struct {
|
type AdminLoginConfig struct {
|
||||||
Dao *daos.Dao
|
App core.App
|
||||||
|
TxDao *daos.Dao
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAdminLogin creates a new [AdminLogin] form with initializer
|
// NewAdminLogin creates a new [AdminLogin] form with initializer
|
||||||
// config created from the provided [core.App] instance.
|
// config created from the provided [core.App] instance.
|
||||||
//
|
//
|
||||||
// This factory method is used primarily for convenience (and backward compatibility).
|
|
||||||
// If you want to submit the form as part of another transaction, use
|
// If you want to submit the form as part of another transaction, use
|
||||||
// [NewCollectionUpsertWithConfig] with Dao configured to your txDao.
|
// [NewAdminLoginWithConfig] with explicitly set TxDao.
|
||||||
func NewAdminLogin(app core.App) *AdminLogin {
|
func NewAdminLogin(app core.App) *AdminLogin {
|
||||||
return NewAdminLoginWithConfig(AdminLoginConfig{
|
return NewAdminLoginWithConfig(AdminLoginConfig{
|
||||||
Dao: app.Dao(),
|
App: app,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,8 +42,12 @@ func NewAdminLogin(app core.App) *AdminLogin {
|
||||||
func NewAdminLoginWithConfig(config AdminLoginConfig) *AdminLogin {
|
func NewAdminLoginWithConfig(config AdminLoginConfig) *AdminLogin {
|
||||||
form := &AdminLogin{config: config}
|
form := &AdminLogin{config: config}
|
||||||
|
|
||||||
if form.config.Dao == nil {
|
if form.config.App == nil {
|
||||||
panic("Invalid initializer config.")
|
panic("Missing required config.App instance.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.config.TxDao == nil {
|
||||||
|
form.config.TxDao = form.config.App.Dao()
|
||||||
}
|
}
|
||||||
|
|
||||||
return form
|
return form
|
||||||
|
@ -64,7 +68,7 @@ func (form *AdminLogin) Submit() (*models.Admin, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
admin, err := form.config.Dao.FindAdminByEmail(form.Email)
|
admin, err := form.config.TxDao.FindAdminByEmail(form.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,24 +3,53 @@ package forms
|
||||||
import (
|
import (
|
||||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
"github.com/pocketbase/pocketbase/forms/validators"
|
"github.com/pocketbase/pocketbase/forms/validators"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AdminPasswordResetConfirm defines an admin password reset confirmation form.
|
// AdminPasswordResetConfirm specifies an admin password reset confirmation form.
|
||||||
type AdminPasswordResetConfirm struct {
|
type AdminPasswordResetConfirm struct {
|
||||||
app core.App
|
config AdminPasswordResetConfirmConfig
|
||||||
|
|
||||||
Token string `form:"token" json:"token"`
|
Token string `form:"token" json:"token"`
|
||||||
Password string `form:"password" json:"password"`
|
Password string `form:"password" json:"password"`
|
||||||
PasswordConfirm string `form:"passwordConfirm" json:"passwordConfirm"`
|
PasswordConfirm string `form:"passwordConfirm" json:"passwordConfirm"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAdminPasswordResetConfirm creates new admin password reset confirmation form.
|
// AdminPasswordResetConfirmConfig is the [AdminPasswordResetConfirm] factory initializer config.
|
||||||
|
//
|
||||||
|
// NB! App is required struct member.
|
||||||
|
type AdminPasswordResetConfirmConfig struct {
|
||||||
|
App core.App
|
||||||
|
TxDao *daos.Dao
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAdminPasswordResetConfirm creates a new [AdminPasswordResetConfirm]
|
||||||
|
// form with initializer config created from the provided [core.App] instance.
|
||||||
|
//
|
||||||
|
// If you want to submit the form as part of another transaction, use
|
||||||
|
// [NewAdminPasswordResetConfirmWithConfig] with explicitly set TxDao.
|
||||||
func NewAdminPasswordResetConfirm(app core.App) *AdminPasswordResetConfirm {
|
func NewAdminPasswordResetConfirm(app core.App) *AdminPasswordResetConfirm {
|
||||||
return &AdminPasswordResetConfirm{
|
return NewAdminPasswordResetConfirmWithConfig(AdminPasswordResetConfirmConfig{
|
||||||
app: app,
|
App: app,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAdminPasswordResetConfirmWithConfig creates a new [AdminPasswordResetConfirm]
|
||||||
|
// form with the provided config or panics on invalid configuration.
|
||||||
|
func NewAdminPasswordResetConfirmWithConfig(config AdminPasswordResetConfirmConfig) *AdminPasswordResetConfirm {
|
||||||
|
form := &AdminPasswordResetConfirm{config: config}
|
||||||
|
|
||||||
|
if form.config.App == nil {
|
||||||
|
panic("Missing required config.App instance.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.config.TxDao == nil {
|
||||||
|
form.config.TxDao = form.config.App.Dao()
|
||||||
|
}
|
||||||
|
|
||||||
|
return form
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
||||||
|
@ -38,9 +67,9 @@ func (form *AdminPasswordResetConfirm) checkToken(value any) error {
|
||||||
return nil // nothing to check
|
return nil // nothing to check
|
||||||
}
|
}
|
||||||
|
|
||||||
admin, err := form.app.Dao().FindAdminByToken(
|
admin, err := form.config.TxDao.FindAdminByToken(
|
||||||
v,
|
v,
|
||||||
form.app.Settings().AdminPasswordResetToken.Secret,
|
form.config.App.Settings().AdminPasswordResetToken.Secret,
|
||||||
)
|
)
|
||||||
if err != nil || admin == nil {
|
if err != nil || admin == nil {
|
||||||
return validation.NewError("validation_invalid_token", "Invalid or expired token.")
|
return validation.NewError("validation_invalid_token", "Invalid or expired token.")
|
||||||
|
@ -56,9 +85,9 @@ func (form *AdminPasswordResetConfirm) Submit() (*models.Admin, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
admin, err := form.app.Dao().FindAdminByToken(
|
admin, err := form.config.TxDao.FindAdminByToken(
|
||||||
form.Token,
|
form.Token,
|
||||||
form.app.Settings().AdminPasswordResetToken.Secret,
|
form.config.App.Settings().AdminPasswordResetToken.Secret,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -68,7 +97,7 @@ func (form *AdminPasswordResetConfirm) Submit() (*models.Admin, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := form.app.Dao().SaveAdmin(admin); err != nil {
|
if err := form.config.TxDao.SaveAdmin(admin); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,24 +7,53 @@ import (
|
||||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||||
"github.com/go-ozzo/ozzo-validation/v4/is"
|
"github.com/go-ozzo/ozzo-validation/v4/is"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
"github.com/pocketbase/pocketbase/mails"
|
"github.com/pocketbase/pocketbase/mails"
|
||||||
"github.com/pocketbase/pocketbase/tools/types"
|
"github.com/pocketbase/pocketbase/tools/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AdminPasswordResetRequest defines an admin password reset request form.
|
// AdminPasswordResetRequest specifies an admin password reset request form.
|
||||||
type AdminPasswordResetRequest struct {
|
type AdminPasswordResetRequest struct {
|
||||||
app core.App
|
config AdminPasswordResetRequestConfig
|
||||||
resendThreshold float64
|
|
||||||
|
|
||||||
Email string `form:"email" json:"email"`
|
Email string `form:"email" json:"email"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAdminPasswordResetRequest creates new admin password reset request form.
|
// AdminPasswordResetRequestConfig is the [AdminPasswordResetRequest] factory initializer config.
|
||||||
|
//
|
||||||
|
// NB! App is required struct member.
|
||||||
|
type AdminPasswordResetRequestConfig struct {
|
||||||
|
App core.App
|
||||||
|
TxDao *daos.Dao
|
||||||
|
ResendThreshold float64 // in seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAdminPasswordResetRequest creates a new [AdminPasswordResetRequest]
|
||||||
|
// form with initializer config created from the provided [core.App] instance.
|
||||||
|
//
|
||||||
|
// If you want to submit the form as part of another transaction, use
|
||||||
|
// [NewAdminPasswordResetRequestWithConfig] with explicitly set TxDao.
|
||||||
func NewAdminPasswordResetRequest(app core.App) *AdminPasswordResetRequest {
|
func NewAdminPasswordResetRequest(app core.App) *AdminPasswordResetRequest {
|
||||||
return &AdminPasswordResetRequest{
|
return NewAdminPasswordResetRequestWithConfig(AdminPasswordResetRequestConfig{
|
||||||
app: app,
|
App: app,
|
||||||
resendThreshold: 120, // 2 min
|
ResendThreshold: 120, // 2min
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAdminPasswordResetRequestWithConfig creates a new [AdminPasswordResetRequest]
|
||||||
|
// form with the provided config or panics on invalid configuration.
|
||||||
|
func NewAdminPasswordResetRequestWithConfig(config AdminPasswordResetRequestConfig) *AdminPasswordResetRequest {
|
||||||
|
form := &AdminPasswordResetRequest{config: config}
|
||||||
|
|
||||||
|
if form.config.App == nil {
|
||||||
|
panic("Missing required config.App instance.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.config.TxDao == nil {
|
||||||
|
form.config.TxDao = form.config.App.Dao()
|
||||||
|
}
|
||||||
|
|
||||||
|
return form
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
||||||
|
@ -48,23 +77,23 @@ func (form *AdminPasswordResetRequest) Submit() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
admin, err := form.app.Dao().FindAdminByEmail(form.Email)
|
admin, err := form.config.TxDao.FindAdminByEmail(form.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
lastResetSentAt := admin.LastResetSentAt.Time()
|
lastResetSentAt := admin.LastResetSentAt.Time()
|
||||||
if now.Sub(lastResetSentAt).Seconds() < form.resendThreshold {
|
if now.Sub(lastResetSentAt).Seconds() < form.config.ResendThreshold {
|
||||||
return errors.New("You have already requested a password reset.")
|
return errors.New("You have already requested a password reset.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := mails.SendAdminPasswordReset(form.app, admin); err != nil {
|
if err := mails.SendAdminPasswordReset(form.config.App, admin); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// update last sent timestamp
|
// update last sent timestamp
|
||||||
admin.LastResetSentAt = types.NowDateTime()
|
admin.LastResetSentAt = types.NowDateTime()
|
||||||
|
|
||||||
return form.app.Dao().SaveAdmin(admin)
|
return form.config.TxDao.SaveAdmin(admin)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,21 +23,21 @@ type AdminUpsert struct {
|
||||||
|
|
||||||
// AdminUpsertConfig is the [AdminUpsert] factory initializer config.
|
// AdminUpsertConfig is the [AdminUpsert] factory initializer config.
|
||||||
//
|
//
|
||||||
// NB! Dao is a required struct member.
|
// NB! App is a required struct member.
|
||||||
type AdminUpsertConfig struct {
|
type AdminUpsertConfig struct {
|
||||||
Dao *daos.Dao
|
App core.App
|
||||||
|
TxDao *daos.Dao
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAdminUpsert creates a new [AdminUpsert] form with initializer
|
// NewAdminUpsert creates a new [AdminUpsert] form with initializer
|
||||||
// config created from the provided [core.App] and [models.Admin] instances
|
// config created from the provided [core.App] and [models.Admin] instances
|
||||||
// (for create you could pass a pointer to an empty Admin - `&models.Admin{}`).
|
// (for create you could pass a pointer to an empty Admin - `&models.Admin{}`).
|
||||||
//
|
//
|
||||||
// This factory method is used primarily for convenience (and backward compatibility).
|
|
||||||
// If you want to submit the form as part of another transaction, use
|
// If you want to submit the form as part of another transaction, use
|
||||||
// [NewAdminUpsertWithConfig] with Dao configured to your txDao.
|
// [NewAdminUpsertWithConfig] with explicitly set TxDao.
|
||||||
func NewAdminUpsert(app core.App, admin *models.Admin) *AdminUpsert {
|
func NewAdminUpsert(app core.App, admin *models.Admin) *AdminUpsert {
|
||||||
return NewAdminUpsertWithConfig(AdminUpsertConfig{
|
return NewAdminUpsertWithConfig(AdminUpsertConfig{
|
||||||
Dao: app.Dao(),
|
App: app,
|
||||||
}, admin)
|
}, admin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,10 +50,14 @@ func NewAdminUpsertWithConfig(config AdminUpsertConfig, admin *models.Admin) *Ad
|
||||||
admin: admin,
|
admin: admin,
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.config.Dao == nil || form.admin == nil {
|
if form.config.App == nil || form.admin == nil {
|
||||||
panic("Invalid initializer config or nil upsert model.")
|
panic("Invalid initializer config or nil upsert model.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.config.TxDao == nil {
|
||||||
|
form.config.TxDao = form.config.App.Dao()
|
||||||
|
}
|
||||||
|
|
||||||
// load defaults
|
// load defaults
|
||||||
form.Id = admin.Id
|
form.Id = admin.Id
|
||||||
form.Avatar = admin.Avatar
|
form.Avatar = admin.Avatar
|
||||||
|
@ -100,7 +104,7 @@ func (form *AdminUpsert) Validate() error {
|
||||||
func (form *AdminUpsert) checkUniqueEmail(value any) error {
|
func (form *AdminUpsert) checkUniqueEmail(value any) error {
|
||||||
v, _ := value.(string)
|
v, _ := value.(string)
|
||||||
|
|
||||||
if form.config.Dao.IsAdminEmailUnique(v, form.admin.Id) {
|
if form.config.TxDao.IsAdminEmailUnique(v, form.admin.Id) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +134,6 @@ func (form *AdminUpsert) Submit(interceptors ...InterceptorFunc) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
return runInterceptors(func() error {
|
return runInterceptors(func() error {
|
||||||
return form.config.Dao.SaveAdmin(form.admin)
|
return form.config.TxDao.SaveAdmin(form.admin)
|
||||||
}, interceptors...)
|
}, interceptors...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,21 +33,21 @@ type CollectionUpsert struct {
|
||||||
|
|
||||||
// CollectionUpsertConfig is the [CollectionUpsert] factory initializer config.
|
// CollectionUpsertConfig is the [CollectionUpsert] factory initializer config.
|
||||||
//
|
//
|
||||||
// NB! Dao is a required struct member.
|
// NB! App is a required struct member.
|
||||||
type CollectionUpsertConfig struct {
|
type CollectionUpsertConfig struct {
|
||||||
Dao *daos.Dao
|
App core.App
|
||||||
|
TxDao *daos.Dao
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCollectionUpsert creates a new [CollectionUpsert] form with initializer
|
// NewCollectionUpsert creates a new [CollectionUpsert] form with initializer
|
||||||
// config created from the provided [core.App] and [models.Collection] instances
|
// config created from the provided [core.App] and [models.Collection] instances
|
||||||
// (for create you could pass a pointer to an empty Collection - `&models.Collection{}`).
|
// (for create you could pass a pointer to an empty Collection - `&models.Collection{}`).
|
||||||
//
|
//
|
||||||
// This factory method is used primarily for convenience (and backward compatibility).
|
|
||||||
// If you want to submit the form as part of another transaction, use
|
// If you want to submit the form as part of another transaction, use
|
||||||
// [NewCollectionUpsertWithConfig] with Dao configured to your txDao.
|
// [NewCollectionUpsertWithConfig] with explicitly set TxDao.
|
||||||
func NewCollectionUpsert(app core.App, collection *models.Collection) *CollectionUpsert {
|
func NewCollectionUpsert(app core.App, collection *models.Collection) *CollectionUpsert {
|
||||||
return NewCollectionUpsertWithConfig(CollectionUpsertConfig{
|
return NewCollectionUpsertWithConfig(CollectionUpsertConfig{
|
||||||
Dao: app.Dao(),
|
App: app,
|
||||||
}, collection)
|
}, collection)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,10 +60,14 @@ func NewCollectionUpsertWithConfig(config CollectionUpsertConfig, collection *mo
|
||||||
collection: collection,
|
collection: collection,
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.config.Dao == nil || form.collection == nil {
|
if form.config.App == nil || form.collection == nil {
|
||||||
panic("Invalid initializer config or nil upsert model.")
|
panic("Invalid initializer config or nil upsert model.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.config.TxDao == nil {
|
||||||
|
form.config.TxDao = form.config.App.Dao()
|
||||||
|
}
|
||||||
|
|
||||||
// load defaults
|
// load defaults
|
||||||
form.Id = form.collection.Id
|
form.Id = form.collection.Id
|
||||||
form.Name = form.collection.Name
|
form.Name = form.collection.Name
|
||||||
|
@ -123,11 +127,11 @@ func (form *CollectionUpsert) Validate() error {
|
||||||
func (form *CollectionUpsert) checkUniqueName(value any) error {
|
func (form *CollectionUpsert) checkUniqueName(value any) error {
|
||||||
v, _ := value.(string)
|
v, _ := value.(string)
|
||||||
|
|
||||||
if !form.config.Dao.IsCollectionNameUnique(v, form.collection.Id) {
|
if !form.config.TxDao.IsCollectionNameUnique(v, form.collection.Id) {
|
||||||
return validation.NewError("validation_collection_name_exists", "Collection name must be unique (case insensitive).")
|
return validation.NewError("validation_collection_name_exists", "Collection name must be unique (case insensitive).")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (form.collection.IsNew() || !strings.EqualFold(v, form.collection.Name)) && form.config.Dao.HasTable(v) {
|
if (form.collection.IsNew() || !strings.EqualFold(v, form.collection.Name)) && form.config.TxDao.HasTable(v) {
|
||||||
return validation.NewError("validation_collection_name_table_exists", "The collection name must be also unique table name.")
|
return validation.NewError("validation_collection_name_table_exists", "The collection name must be also unique table name.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +198,7 @@ func (form *CollectionUpsert) checkRule(value any) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
dummy := &models.Collection{Schema: form.Schema}
|
dummy := &models.Collection{Schema: form.Schema}
|
||||||
r := resolvers.NewRecordFieldResolver(form.config.Dao, dummy, nil)
|
r := resolvers.NewRecordFieldResolver(form.config.TxDao, dummy, nil)
|
||||||
|
|
||||||
_, err := search.FilterData(*v).BuildExpr(r)
|
_, err := search.FilterData(*v).BuildExpr(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -239,6 +243,6 @@ func (form *CollectionUpsert) Submit(interceptors ...InterceptorFunc) error {
|
||||||
form.collection.DeleteRule = form.DeleteRule
|
form.collection.DeleteRule = form.DeleteRule
|
||||||
|
|
||||||
return runInterceptors(func() error {
|
return runInterceptors(func() error {
|
||||||
return form.config.Dao.SaveCollection(form.collection)
|
return form.config.TxDao.SaveCollection(form.collection)
|
||||||
}, interceptors...)
|
}, interceptors...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,21 @@ type CollectionsImport struct {
|
||||||
|
|
||||||
// CollectionsImportConfig is the [CollectionsImport] factory initializer config.
|
// CollectionsImportConfig is the [CollectionsImport] factory initializer config.
|
||||||
//
|
//
|
||||||
// NB! Dao is a required struct member.
|
// NB! App is a required struct member.
|
||||||
type CollectionsImportConfig struct {
|
type CollectionsImportConfig struct {
|
||||||
Dao *daos.Dao
|
App core.App
|
||||||
IsDebug bool
|
TxDao *daos.Dao
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCollectionsImport creates a new [CollectionsImport] form with
|
||||||
|
// initializer config created from the provided [core.App] instance.
|
||||||
|
//
|
||||||
|
// If you want to submit the form as part of another transaction, use
|
||||||
|
// [NewCollectionsImportWithConfig] with explicitly set TxDao.
|
||||||
|
func NewCollectionsImport(app core.App) *CollectionsImport {
|
||||||
|
return NewCollectionsImportWithConfig(CollectionsImportConfig{
|
||||||
|
App: app,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCollectionsImportWithConfig creates a new [CollectionsImport]
|
// NewCollectionsImportWithConfig creates a new [CollectionsImport]
|
||||||
|
@ -33,26 +44,17 @@ type CollectionsImportConfig struct {
|
||||||
func NewCollectionsImportWithConfig(config CollectionsImportConfig) *CollectionsImport {
|
func NewCollectionsImportWithConfig(config CollectionsImportConfig) *CollectionsImport {
|
||||||
form := &CollectionsImport{config: config}
|
form := &CollectionsImport{config: config}
|
||||||
|
|
||||||
if form.config.Dao == nil {
|
if form.config.App == nil {
|
||||||
panic("Invalid initializer config.")
|
panic("Missing required config.App instance.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.config.TxDao == nil {
|
||||||
|
form.config.TxDao = form.config.App.Dao()
|
||||||
}
|
}
|
||||||
|
|
||||||
return form
|
return form
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCollectionsImport creates a new [CollectionsImport] form with
|
|
||||||
// initializer config created from the provided [core.App] instance.
|
|
||||||
//
|
|
||||||
// This factory method is used primarily for convenience (and backward compatibility).
|
|
||||||
// If you want to submit the form as part of another transaction, use
|
|
||||||
// [NewCollectionsImportWithConfig] with Dao configured to your txDao.
|
|
||||||
func NewCollectionsImport(app core.App) *CollectionsImport {
|
|
||||||
return NewCollectionsImportWithConfig(CollectionsImportConfig{
|
|
||||||
Dao: app.Dao(),
|
|
||||||
IsDebug: app.IsDebug(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
||||||
func (form *CollectionsImport) Validate() error {
|
func (form *CollectionsImport) Validate() error {
|
||||||
return validation.ValidateStruct(form,
|
return validation.ValidateStruct(form,
|
||||||
|
@ -73,7 +75,7 @@ func (form *CollectionsImport) Submit() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return form.config.Dao.RunInTransaction(func(txDao *daos.Dao) error {
|
return form.config.TxDao.RunInTransaction(func(txDao *daos.Dao) error {
|
||||||
oldCollections := []*models.Collection{}
|
oldCollections := []*models.Collection{}
|
||||||
if err := txDao.CollectionQuery().All(&oldCollections); err != nil {
|
if err := txDao.CollectionQuery().All(&oldCollections); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -95,7 +97,7 @@ func (form *CollectionsImport) Submit() error {
|
||||||
if mappedFormCollections[old.GetId()] == nil {
|
if mappedFormCollections[old.GetId()] == nil {
|
||||||
// delete the collection
|
// delete the collection
|
||||||
if err := txDao.DeleteCollection(old); err != nil {
|
if err := txDao.DeleteCollection(old); err != nil {
|
||||||
if form.config.IsDebug {
|
if form.config.App.IsDebug() {
|
||||||
log.Println("[CollectionsImport] DeleteOthers failure", old.Name, err)
|
log.Println("[CollectionsImport] DeleteOthers failure", old.Name, err)
|
||||||
}
|
}
|
||||||
return validation.Errors{"collections": validation.NewError(
|
return validation.Errors{"collections": validation.NewError(
|
||||||
|
@ -117,7 +119,7 @@ func (form *CollectionsImport) Submit() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := txDao.Save(collection); err != nil {
|
if err := txDao.Save(collection); err != nil {
|
||||||
if form.config.IsDebug {
|
if form.config.App.IsDebug() {
|
||||||
log.Println("[CollectionsImport] Save failure", collection.Name, err)
|
log.Println("[CollectionsImport] Save failure", collection.Name, err)
|
||||||
}
|
}
|
||||||
return validation.Errors{"collections": validation.NewError(
|
return validation.Errors{"collections": validation.NewError(
|
||||||
|
@ -141,7 +143,8 @@ func (form *CollectionsImport) Submit() error {
|
||||||
upsertModel = collection
|
upsertModel = collection
|
||||||
}
|
}
|
||||||
upsertForm := NewCollectionUpsertWithConfig(CollectionUpsertConfig{
|
upsertForm := NewCollectionUpsertWithConfig(CollectionUpsertConfig{
|
||||||
Dao: txDao,
|
App: form.config.App,
|
||||||
|
TxDao: txDao,
|
||||||
}, upsertModel)
|
}, upsertModel)
|
||||||
// load form fields with the refreshed collection state
|
// load form fields with the refreshed collection state
|
||||||
upsertForm.Id = collection.Id
|
upsertForm.Id = collection.Id
|
||||||
|
@ -168,7 +171,7 @@ func (form *CollectionsImport) Submit() error {
|
||||||
for _, collection := range form.Collections {
|
for _, collection := range form.Collections {
|
||||||
oldCollection := mappedOldCollections[collection.GetId()]
|
oldCollection := mappedOldCollections[collection.GetId()]
|
||||||
if err := txDao.SyncRecordTableSchema(collection, oldCollection); err != nil {
|
if err := txDao.SyncRecordTableSchema(collection, oldCollection); err != nil {
|
||||||
if form.config.IsDebug {
|
if form.config.App.IsDebug() {
|
||||||
log.Println("[CollectionsImport] Records table sync failure", collection.Name, err)
|
log.Println("[CollectionsImport] Records table sync failure", collection.Name, err)
|
||||||
}
|
}
|
||||||
return validation.Errors{"collections": validation.NewError(
|
return validation.Errors{"collections": validation.NewError(
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RealtimeSubscribe defines a RealtimeSubscribe request form.
|
// RealtimeSubscribe specifies a RealtimeSubscribe request form.
|
||||||
type RealtimeSubscribe struct {
|
type RealtimeSubscribe struct {
|
||||||
ClientId string `form:"clientId" json:"clientId"`
|
ClientId string `form:"clientId" json:"clientId"`
|
||||||
Subscriptions []string `form:"subscriptions" json:"subscriptions"`
|
Subscriptions []string `form:"subscriptions" json:"subscriptions"`
|
||||||
|
|
|
@ -16,7 +16,6 @@ import (
|
||||||
"github.com/pocketbase/pocketbase/forms/validators"
|
"github.com/pocketbase/pocketbase/forms/validators"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
"github.com/pocketbase/pocketbase/models/schema"
|
"github.com/pocketbase/pocketbase/models/schema"
|
||||||
"github.com/pocketbase/pocketbase/tools/filesystem"
|
|
||||||
"github.com/pocketbase/pocketbase/tools/list"
|
"github.com/pocketbase/pocketbase/tools/list"
|
||||||
"github.com/pocketbase/pocketbase/tools/rest"
|
"github.com/pocketbase/pocketbase/tools/rest"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
|
@ -36,25 +35,21 @@ type RecordUpsert struct {
|
||||||
|
|
||||||
// RecordUpsertConfig is the [RecordUpsert] factory initializer config.
|
// RecordUpsertConfig is the [RecordUpsert] factory initializer config.
|
||||||
//
|
//
|
||||||
// NB! Dao and FilesystemFactory are required struct members.
|
// NB! App is required struct member.
|
||||||
type RecordUpsertConfig struct {
|
type RecordUpsertConfig struct {
|
||||||
Dao *daos.Dao
|
App core.App
|
||||||
FilesystemFactory func() (*filesystem.System, error)
|
TxDao *daos.Dao
|
||||||
IsDebug bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRecordUpsert creates a new [RecordUpsert] form with initializer
|
// NewRecordUpsert creates a new [RecordUpsert] form with initializer
|
||||||
// config created from the provided [core.App] and [models.Record] instances
|
// config created from the provided [core.App] and [models.Record] instances
|
||||||
// (for create you could pass a pointer to an empty Record - `&models.Record{}`).
|
// (for create you could pass a pointer to an empty Record - `&models.Record{}`).
|
||||||
//
|
//
|
||||||
// This factory method is used primarily for convenience (and backward compatibility).
|
|
||||||
// If you want to submit the form as part of another transaction, use
|
// If you want to submit the form as part of another transaction, use
|
||||||
// [NewRecordUpsertWithConfig] with Dao configured to your txDao.
|
// [NewRecordUpsertWithConfig] with explicitly set TxDao.
|
||||||
func NewRecordUpsert(app core.App, record *models.Record) *RecordUpsert {
|
func NewRecordUpsert(app core.App, record *models.Record) *RecordUpsert {
|
||||||
return NewRecordUpsertWithConfig(RecordUpsertConfig{
|
return NewRecordUpsertWithConfig(RecordUpsertConfig{
|
||||||
Dao: app.Dao(),
|
App: app,
|
||||||
FilesystemFactory: app.NewFilesystem,
|
|
||||||
IsDebug: app.IsDebug(),
|
|
||||||
}, record)
|
}, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,12 +64,14 @@ func NewRecordUpsertWithConfig(config RecordUpsertConfig, record *models.Record)
|
||||||
filesToUpload: []*rest.UploadedFile{},
|
filesToUpload: []*rest.UploadedFile{},
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.config.Dao == nil ||
|
if form.config.App == nil || form.record == nil {
|
||||||
form.config.FilesystemFactory == nil ||
|
|
||||||
form.record == nil {
|
|
||||||
panic("Invalid initializer config or nil upsert model.")
|
panic("Invalid initializer config or nil upsert model.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.config.TxDao == nil {
|
||||||
|
form.config.TxDao = form.config.App.Dao()
|
||||||
|
}
|
||||||
|
|
||||||
form.Id = record.Id
|
form.Id = record.Id
|
||||||
|
|
||||||
form.Data = map[string]any{}
|
form.Data = map[string]any{}
|
||||||
|
@ -240,7 +237,7 @@ func (form *RecordUpsert) LoadData(r *http.Request) error {
|
||||||
// check if there are any new uploaded form files
|
// check if there are any new uploaded form files
|
||||||
files, err := rest.FindUploadedFiles(r, key)
|
files, err := rest.FindUploadedFiles(r, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if form.config.IsDebug {
|
if form.config.App.IsDebug() {
|
||||||
log.Printf("%q uploaded file error: %v\n", key, err)
|
log.Printf("%q uploaded file error: %v\n", key, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +285,7 @@ func (form *RecordUpsert) Validate() error {
|
||||||
|
|
||||||
// record data validator
|
// record data validator
|
||||||
dataValidator := validators.NewRecordDataValidator(
|
dataValidator := validators.NewRecordDataValidator(
|
||||||
form.config.Dao,
|
form.config.TxDao,
|
||||||
form.record,
|
form.record,
|
||||||
form.filesToUpload,
|
form.filesToUpload,
|
||||||
)
|
)
|
||||||
|
@ -316,7 +313,7 @@ func (form *RecordUpsert) DrySubmit(callback func(txDao *daos.Dao) error) error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return form.config.Dao.RunInTransaction(func(txDao *daos.Dao) error {
|
return form.config.TxDao.RunInTransaction(func(txDao *daos.Dao) error {
|
||||||
tx, ok := txDao.DB().(*dbx.Tx)
|
tx, ok := txDao.DB().(*dbx.Tx)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("failed to get transaction db")
|
return errors.New("failed to get transaction db")
|
||||||
|
@ -356,7 +353,7 @@ func (form *RecordUpsert) Submit(interceptors ...InterceptorFunc) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
return runInterceptors(func() error {
|
return runInterceptors(func() error {
|
||||||
return form.config.Dao.RunInTransaction(func(txDao *daos.Dao) error {
|
return form.config.TxDao.RunInTransaction(func(txDao *daos.Dao) error {
|
||||||
// persist record model
|
// persist record model
|
||||||
if err := txDao.SaveRecord(form.record); err != nil {
|
if err := txDao.SaveRecord(form.record); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -387,7 +384,7 @@ func (form *RecordUpsert) processFilesToUpload() error {
|
||||||
return errors.New("The record is not persisted yet.")
|
return errors.New("The record is not persisted yet.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fs, err := form.config.FilesystemFactory()
|
fs, err := form.config.App.NewFilesystem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -423,7 +420,7 @@ func (form *RecordUpsert) processFilesToDelete() error {
|
||||||
return errors.New("The record is not persisted yet.")
|
return errors.New("The record is not persisted yet.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fs, err := form.config.FilesystemFactory()
|
fs, err := form.config.App.NewFilesystem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,22 +5,56 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SettingsUpsert defines app settings upsert form.
|
// SettingsUpsert specifies a [core.Settings] upsert (create/update) form.
|
||||||
type SettingsUpsert struct {
|
type SettingsUpsert struct {
|
||||||
*core.Settings
|
*core.Settings
|
||||||
|
|
||||||
app core.App
|
config SettingsUpsertConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSettingsUpsert creates new settings upsert form from the provided app.
|
// SettingsUpsertConfig is the [SettingsUpsert] factory initializer config.
|
||||||
|
//
|
||||||
|
// NB! App is required struct member.
|
||||||
|
type SettingsUpsertConfig struct {
|
||||||
|
App core.App
|
||||||
|
TxDao *daos.Dao
|
||||||
|
TxLogsDao *daos.Dao
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSettingsUpsert creates a new [SettingsUpsert] form with initializer
|
||||||
|
// config created from the provided [core.App] instance.
|
||||||
|
//
|
||||||
|
// If you want to submit the form as part of another transaction, use
|
||||||
|
// [NewSettingsUpsertWithConfig] with explicitly set TxDao.
|
||||||
func NewSettingsUpsert(app core.App) *SettingsUpsert {
|
func NewSettingsUpsert(app core.App) *SettingsUpsert {
|
||||||
form := &SettingsUpsert{app: app}
|
return NewSettingsUpsertWithConfig(SettingsUpsertConfig{
|
||||||
|
App: app,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSettingsUpsertWithConfig creates a new [SettingsUpsert] form
|
||||||
|
// with the provided config or panics on invalid configuration.
|
||||||
|
func NewSettingsUpsertWithConfig(config SettingsUpsertConfig) *SettingsUpsert {
|
||||||
|
form := &SettingsUpsert{config: config}
|
||||||
|
|
||||||
|
if form.config.App == nil {
|
||||||
|
panic("Missing required config.App instance.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.config.TxDao == nil {
|
||||||
|
form.config.TxDao = form.config.App.Dao()
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.config.TxLogsDao == nil {
|
||||||
|
form.config.TxLogsDao = form.config.App.LogsDao()
|
||||||
|
}
|
||||||
|
|
||||||
// load the application settings into the form
|
// load the application settings into the form
|
||||||
form.Settings, _ = app.Settings().Clone()
|
form.Settings, _ = config.App.Settings().Clone()
|
||||||
|
|
||||||
return form
|
return form
|
||||||
}
|
}
|
||||||
|
@ -41,10 +75,10 @@ func (form *SettingsUpsert) Submit(interceptors ...InterceptorFunc) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
encryptionKey := os.Getenv(form.app.EncryptionEnv())
|
encryptionKey := os.Getenv(form.config.App.EncryptionEnv())
|
||||||
|
|
||||||
return runInterceptors(func() error {
|
return runInterceptors(func() error {
|
||||||
saveErr := form.app.Dao().SaveParam(
|
saveErr := form.config.TxDao.SaveParam(
|
||||||
models.ParamAppSettings,
|
models.ParamAppSettings,
|
||||||
form.Settings,
|
form.Settings,
|
||||||
encryptionKey,
|
encryptionKey,
|
||||||
|
@ -54,11 +88,11 @@ func (form *SettingsUpsert) Submit(interceptors ...InterceptorFunc) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// explicitly trigger old logs deletion
|
// explicitly trigger old logs deletion
|
||||||
form.app.LogsDao().DeleteOldRequests(
|
form.config.TxLogsDao.DeleteOldRequests(
|
||||||
time.Now().AddDate(0, 0, -1*form.Settings.Logs.MaxDays),
|
time.Now().AddDate(0, 0, -1*form.Settings.Logs.MaxDays),
|
||||||
)
|
)
|
||||||
|
|
||||||
// merge the application settings with the form ones
|
// merge the application settings with the form ones
|
||||||
return form.app.Settings().Merge(form.Settings)
|
return form.config.App.Settings().Merge(form.Settings)
|
||||||
}, interceptors...)
|
}, interceptors...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,23 +3,53 @@ package forms
|
||||||
import (
|
import (
|
||||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
"github.com/pocketbase/pocketbase/tools/security"
|
"github.com/pocketbase/pocketbase/tools/security"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserEmailChangeConfirm defines a user email change confirmation form.
|
// UserEmailChangeConfirm specifies a user email change confirmation form.
|
||||||
type UserEmailChangeConfirm struct {
|
type UserEmailChangeConfirm struct {
|
||||||
app core.App
|
config UserEmailChangeConfirmConfig
|
||||||
|
|
||||||
Token string `form:"token" json:"token"`
|
Token string `form:"token" json:"token"`
|
||||||
Password string `form:"password" json:"password"`
|
Password string `form:"password" json:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUserEmailChangeConfirm creates new user email change confirmation form.
|
// UserEmailChangeConfirmConfig is the [UserEmailChangeConfirm] factory initializer config.
|
||||||
|
//
|
||||||
|
// NB! App is required struct member.
|
||||||
|
type UserEmailChangeConfirmConfig struct {
|
||||||
|
App core.App
|
||||||
|
TxDao *daos.Dao
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserEmailChangeConfirm creates a new [UserEmailChangeConfirm]
|
||||||
|
// form with initializer config created from the provided [core.App] instance.
|
||||||
|
//
|
||||||
|
// This factory method is used primarily for convenience (and backward compatibility).
|
||||||
|
// If you want to submit the form as part of another transaction, use
|
||||||
|
// [NewUserEmailChangeConfirmWithConfig] with explicitly set TxDao.
|
||||||
func NewUserEmailChangeConfirm(app core.App) *UserEmailChangeConfirm {
|
func NewUserEmailChangeConfirm(app core.App) *UserEmailChangeConfirm {
|
||||||
return &UserEmailChangeConfirm{
|
return NewUserEmailChangeConfirmWithConfig(UserEmailChangeConfirmConfig{
|
||||||
app: app,
|
App: app,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserEmailChangeConfirmWithConfig creates a new [UserEmailChangeConfirm]
|
||||||
|
// form with the provided config or panics on invalid configuration.
|
||||||
|
func NewUserEmailChangeConfirmWithConfig(config UserEmailChangeConfirmConfig) *UserEmailChangeConfirm {
|
||||||
|
form := &UserEmailChangeConfirm{config: config}
|
||||||
|
|
||||||
|
if form.config.App == nil {
|
||||||
|
panic("Missing required config.App instance.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.config.TxDao == nil {
|
||||||
|
form.config.TxDao = form.config.App.Dao()
|
||||||
|
}
|
||||||
|
|
||||||
|
return form
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
||||||
|
@ -73,14 +103,14 @@ func (form *UserEmailChangeConfirm) parseToken(token string) (*models.User, stri
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure that there aren't other users with the new email
|
// ensure that there aren't other users with the new email
|
||||||
if !form.app.Dao().IsUserEmailUnique(newEmail, "") {
|
if !form.config.TxDao.IsUserEmailUnique(newEmail, "") {
|
||||||
return nil, "", validation.NewError("validation_existing_token_email", "The new email address is already registered: "+newEmail)
|
return nil, "", validation.NewError("validation_existing_token_email", "The new email address is already registered: "+newEmail)
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify that the token is not expired and its signiture is valid
|
// verify that the token is not expired and its signature is valid
|
||||||
user, err := form.app.Dao().FindUserByToken(
|
user, err := form.config.TxDao.FindUserByToken(
|
||||||
token,
|
token,
|
||||||
form.app.Settings().UserEmailChangeToken.Secret,
|
form.config.App.Settings().UserEmailChangeToken.Secret,
|
||||||
)
|
)
|
||||||
if err != nil || user == nil {
|
if err != nil || user == nil {
|
||||||
return nil, "", validation.NewError("validation_invalid_token", "Invalid or expired token.")
|
return nil, "", validation.NewError("validation_invalid_token", "Invalid or expired token.")
|
||||||
|
@ -105,7 +135,7 @@ func (form *UserEmailChangeConfirm) Submit() (*models.User, error) {
|
||||||
user.Verified = true
|
user.Verified = true
|
||||||
user.RefreshTokenKey() // invalidate old tokens
|
user.RefreshTokenKey() // invalidate old tokens
|
||||||
|
|
||||||
if err := form.app.Dao().SaveUser(user); err != nil {
|
if err := form.config.TxDao.SaveUser(user); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,24 +4,55 @@ import (
|
||||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||||
"github.com/go-ozzo/ozzo-validation/v4/is"
|
"github.com/go-ozzo/ozzo-validation/v4/is"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
"github.com/pocketbase/pocketbase/mails"
|
"github.com/pocketbase/pocketbase/mails"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserEmailChangeRequest defines a user email change request form.
|
// UserEmailChangeRequest defines a user email change request form.
|
||||||
type UserEmailChangeRequest struct {
|
type UserEmailChangeRequest struct {
|
||||||
app core.App
|
config UserEmailChangeRequestConfig
|
||||||
user *models.User
|
user *models.User
|
||||||
|
|
||||||
NewEmail string `form:"newEmail" json:"newEmail"`
|
NewEmail string `form:"newEmail" json:"newEmail"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUserEmailChangeRequest creates a new user email change request form.
|
// UserEmailChangeRequestConfig is the [UserEmailChangeRequest] factory initializer config.
|
||||||
|
//
|
||||||
|
// NB! App is required struct member.
|
||||||
|
type UserEmailChangeRequestConfig struct {
|
||||||
|
App core.App
|
||||||
|
TxDao *daos.Dao
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserEmailChangeRequest creates a new [UserEmailChangeRequest]
|
||||||
|
// form with initializer config created from the provided [core.App] instance.
|
||||||
|
//
|
||||||
|
// If you want to submit the form as part of another transaction, use
|
||||||
|
// [NewUserEmailChangeConfirmWithConfig] with explicitly set TxDao.
|
||||||
func NewUserEmailChangeRequest(app core.App, user *models.User) *UserEmailChangeRequest {
|
func NewUserEmailChangeRequest(app core.App, user *models.User) *UserEmailChangeRequest {
|
||||||
return &UserEmailChangeRequest{
|
return NewUserEmailChangeRequestWithConfig(UserEmailChangeRequestConfig{
|
||||||
app: app,
|
App: app,
|
||||||
|
}, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserEmailChangeRequestWithConfig creates a new [UserEmailChangeRequest]
|
||||||
|
// form with the provided config or panics on invalid configuration.
|
||||||
|
func NewUserEmailChangeRequestWithConfig(config UserEmailChangeRequestConfig, user *models.User) *UserEmailChangeRequest {
|
||||||
|
form := &UserEmailChangeRequest{
|
||||||
|
config: config,
|
||||||
user: user,
|
user: user,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.config.App == nil {
|
||||||
|
panic("Missing required config.App instance.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.config.TxDao == nil {
|
||||||
|
form.config.TxDao = form.config.App.Dao()
|
||||||
|
}
|
||||||
|
|
||||||
|
return form
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
||||||
|
@ -40,7 +71,7 @@ func (form *UserEmailChangeRequest) Validate() error {
|
||||||
func (form *UserEmailChangeRequest) checkUniqueEmail(value any) error {
|
func (form *UserEmailChangeRequest) checkUniqueEmail(value any) error {
|
||||||
v, _ := value.(string)
|
v, _ := value.(string)
|
||||||
|
|
||||||
if !form.app.Dao().IsUserEmailUnique(v, "") {
|
if !form.config.TxDao.IsUserEmailUnique(v, "") {
|
||||||
return validation.NewError("validation_user_email_exists", "User email already exists.")
|
return validation.NewError("validation_user_email_exists", "User email already exists.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,5 +84,5 @@ func (form *UserEmailChangeRequest) Submit() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return mails.SendUserChangeEmail(form.app, form.user, form.NewEmail)
|
return mails.SendUserChangeEmail(form.config.App, form.user, form.NewEmail)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,21 +4,49 @@ import (
|
||||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||||
"github.com/go-ozzo/ozzo-validation/v4/is"
|
"github.com/go-ozzo/ozzo-validation/v4/is"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserEmailLogin defines a user email/pass login form.
|
// UserEmailLogin specifies a user email/pass login form.
|
||||||
type UserEmailLogin struct {
|
type UserEmailLogin struct {
|
||||||
app core.App
|
config UserEmailLoginConfig
|
||||||
|
|
||||||
Email string `form:"email" json:"email"`
|
Email string `form:"email" json:"email"`
|
||||||
Password string `form:"password" json:"password"`
|
Password string `form:"password" json:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUserEmailLogin creates a new user email/pass login form.
|
// UserEmailLoginConfig is the [UserEmailLogin] factory initializer config.
|
||||||
|
//
|
||||||
|
// NB! App is required struct member.
|
||||||
|
type UserEmailLoginConfig struct {
|
||||||
|
App core.App
|
||||||
|
TxDao *daos.Dao
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserEmailLogin creates a new [UserEmailLogin] form with
|
||||||
|
// initializer config created from the provided [core.App] instance.
|
||||||
|
//
|
||||||
|
// This factory method is used primarily for convenience (and backward compatibility).
|
||||||
|
// If you want to submit the form as part of another transaction, use
|
||||||
|
// [NewUserEmailLoginWithConfig] with explicitly set TxDao.
|
||||||
func NewUserEmailLogin(app core.App) *UserEmailLogin {
|
func NewUserEmailLogin(app core.App) *UserEmailLogin {
|
||||||
form := &UserEmailLogin{
|
return NewUserEmailLoginWithConfig(UserEmailLoginConfig{
|
||||||
app: app,
|
App: app,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserEmailLoginWithConfig creates a new [UserEmailLogin]
|
||||||
|
// form with the provided config or panics on invalid configuration.
|
||||||
|
func NewUserEmailLoginWithConfig(config UserEmailLoginConfig) *UserEmailLogin {
|
||||||
|
form := &UserEmailLogin{config: config}
|
||||||
|
|
||||||
|
if form.config.App == nil {
|
||||||
|
panic("Missing required config.App instance.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.config.TxDao == nil {
|
||||||
|
form.config.TxDao = form.config.App.Dao()
|
||||||
}
|
}
|
||||||
|
|
||||||
return form
|
return form
|
||||||
|
@ -39,7 +67,7 @@ func (form *UserEmailLogin) Submit() (*models.User, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := form.app.Dao().FindUserByEmail(form.Email)
|
user, err := form.config.TxDao.FindUserByEmail(form.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,15 +7,16 @@ import (
|
||||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||||
"github.com/go-ozzo/ozzo-validation/v4/is"
|
"github.com/go-ozzo/ozzo-validation/v4/is"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
"github.com/pocketbase/pocketbase/tools/auth"
|
"github.com/pocketbase/pocketbase/tools/auth"
|
||||||
"github.com/pocketbase/pocketbase/tools/security"
|
"github.com/pocketbase/pocketbase/tools/security"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserOauth2Login defines a user Oauth2 login form.
|
// UserOauth2Login specifies a user Oauth2 login form.
|
||||||
type UserOauth2Login struct {
|
type UserOauth2Login struct {
|
||||||
app core.App
|
config UserOauth2LoginConfig
|
||||||
|
|
||||||
// The name of the OAuth2 client provider (eg. "google")
|
// The name of the OAuth2 client provider (eg. "google")
|
||||||
Provider string `form:"provider" json:"provider"`
|
Provider string `form:"provider" json:"provider"`
|
||||||
|
@ -30,9 +31,39 @@ type UserOauth2Login struct {
|
||||||
RedirectUrl string `form:"redirectUrl" json:"redirectUrl"`
|
RedirectUrl string `form:"redirectUrl" json:"redirectUrl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUserOauth2Login creates a new user Oauth2 login form.
|
// UserOauth2LoginConfig is the [UserOauth2Login] factory initializer config.
|
||||||
|
//
|
||||||
|
// NB! App is required struct member.
|
||||||
|
type UserOauth2LoginConfig struct {
|
||||||
|
App core.App
|
||||||
|
TxDao *daos.Dao
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserOauth2Login creates a new [UserOauth2Login] form with
|
||||||
|
// initializer config created from the provided [core.App] instance.
|
||||||
|
//
|
||||||
|
// If you want to submit the form as part of another transaction, use
|
||||||
|
// [NewUserOauth2LoginWithConfig] with explicitly set TxDao.
|
||||||
func NewUserOauth2Login(app core.App) *UserOauth2Login {
|
func NewUserOauth2Login(app core.App) *UserOauth2Login {
|
||||||
return &UserOauth2Login{app: app}
|
return NewUserOauth2LoginWithConfig(UserOauth2LoginConfig{
|
||||||
|
App: app,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserOauth2LoginWithConfig creates a new [UserOauth2Login]
|
||||||
|
// form with the provided config or panics on invalid configuration.
|
||||||
|
func NewUserOauth2LoginWithConfig(config UserOauth2LoginConfig) *UserOauth2Login {
|
||||||
|
form := &UserOauth2Login{config: config}
|
||||||
|
|
||||||
|
if form.config.App == nil {
|
||||||
|
panic("Missing required config.App instance.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.config.TxDao == nil {
|
||||||
|
form.config.TxDao = form.config.App.Dao()
|
||||||
|
}
|
||||||
|
|
||||||
|
return form
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
||||||
|
@ -48,7 +79,7 @@ func (form *UserOauth2Login) Validate() error {
|
||||||
func (form *UserOauth2Login) checkProviderName(value any) error {
|
func (form *UserOauth2Login) checkProviderName(value any) error {
|
||||||
name, _ := value.(string)
|
name, _ := value.(string)
|
||||||
|
|
||||||
config, ok := form.app.Settings().NamedAuthProviderConfigs()[name]
|
config, ok := form.config.App.Settings().NamedAuthProviderConfigs()[name]
|
||||||
if !ok || !config.Enabled {
|
if !ok || !config.Enabled {
|
||||||
return validation.NewError("validation_invalid_provider", fmt.Sprintf("%q is missing or is not enabled.", name))
|
return validation.NewError("validation_invalid_provider", fmt.Sprintf("%q is missing or is not enabled.", name))
|
||||||
}
|
}
|
||||||
|
@ -68,7 +99,7 @@ func (form *UserOauth2Login) Submit() (*models.User, *auth.AuthUser, error) {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
config := form.app.Settings().NamedAuthProviderConfigs()[form.Provider]
|
config := form.config.App.Settings().NamedAuthProviderConfigs()[form.Provider]
|
||||||
config.SetupProvider(provider)
|
config.SetupProvider(provider)
|
||||||
|
|
||||||
provider.SetRedirectUrl(form.RedirectUrl)
|
provider.SetRedirectUrl(form.RedirectUrl)
|
||||||
|
@ -89,12 +120,12 @@ func (form *UserOauth2Login) Submit() (*models.User, *auth.AuthUser, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// login/register the auth user
|
// login/register the auth user
|
||||||
user, _ := form.app.Dao().FindUserByEmail(authData.Email)
|
user, _ := form.config.TxDao.FindUserByEmail(authData.Email)
|
||||||
if user != nil {
|
if user != nil {
|
||||||
// update the existing user's verified state
|
// update the existing user's verified state
|
||||||
if !user.Verified {
|
if !user.Verified {
|
||||||
user.Verified = true
|
user.Verified = true
|
||||||
if err := form.app.Dao().SaveUser(user); err != nil {
|
if err := form.config.TxDao.SaveUser(user); err != nil {
|
||||||
return nil, authData, err
|
return nil, authData, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,7 +139,10 @@ func (form *UserOauth2Login) Submit() (*models.User, *auth.AuthUser, error) {
|
||||||
|
|
||||||
// create new user
|
// create new user
|
||||||
user = &models.User{Verified: true}
|
user = &models.User{Verified: true}
|
||||||
upsertForm := NewUserUpsert(form.app, user)
|
upsertForm := NewUserUpsertWithConfig(UserUpsertConfig{
|
||||||
|
App: form.config.App,
|
||||||
|
TxDao: form.config.TxDao,
|
||||||
|
}, user)
|
||||||
upsertForm.Email = authData.Email
|
upsertForm.Email = authData.Email
|
||||||
upsertForm.Password = security.RandomString(30)
|
upsertForm.Password = security.RandomString(30)
|
||||||
upsertForm.PasswordConfirm = upsertForm.Password
|
upsertForm.PasswordConfirm = upsertForm.Password
|
||||||
|
@ -118,7 +152,7 @@ func (form *UserOauth2Login) Submit() (*models.User, *auth.AuthUser, error) {
|
||||||
AuthData: authData,
|
AuthData: authData,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := form.app.OnUserBeforeOauth2Register().Trigger(event); err != nil {
|
if err := form.config.App.OnUserBeforeOauth2Register().Trigger(event); err != nil {
|
||||||
return nil, authData, err
|
return nil, authData, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +160,7 @@ func (form *UserOauth2Login) Submit() (*models.User, *auth.AuthUser, error) {
|
||||||
return nil, authData, err
|
return nil, authData, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := form.app.OnUserAfterOauth2Register().Trigger(event); err != nil {
|
if err := form.config.App.OnUserAfterOauth2Register().Trigger(event); err != nil {
|
||||||
return nil, authData, err
|
return nil, authData, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,29 +3,59 @@ package forms
|
||||||
import (
|
import (
|
||||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
"github.com/pocketbase/pocketbase/forms/validators"
|
"github.com/pocketbase/pocketbase/forms/validators"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserPasswordResetConfirm defines a user password reset confirmation form.
|
// UserPasswordResetConfirm specifies a user password reset confirmation form.
|
||||||
type UserPasswordResetConfirm struct {
|
type UserPasswordResetConfirm struct {
|
||||||
app core.App
|
config UserPasswordResetConfirmConfig
|
||||||
|
|
||||||
Token string `form:"token" json:"token"`
|
Token string `form:"token" json:"token"`
|
||||||
Password string `form:"password" json:"password"`
|
Password string `form:"password" json:"password"`
|
||||||
PasswordConfirm string `form:"passwordConfirm" json:"passwordConfirm"`
|
PasswordConfirm string `form:"passwordConfirm" json:"passwordConfirm"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUserPasswordResetConfirm creates new user password reset confirmation form.
|
// UserPasswordResetConfirmConfig is the [UserPasswordResetConfirm]
|
||||||
|
// factory initializer config.
|
||||||
|
//
|
||||||
|
// NB! App is required struct member.
|
||||||
|
type UserPasswordResetConfirmConfig struct {
|
||||||
|
App core.App
|
||||||
|
TxDao *daos.Dao
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserPasswordResetConfirm creates a new [UserPasswordResetConfirm]
|
||||||
|
// form with initializer config created from the provided [core.App] instance.
|
||||||
|
//
|
||||||
|
// If you want to submit the form as part of another transaction, use
|
||||||
|
// [NewUserPasswordResetConfirmWithConfig] with explicitly set TxDao.
|
||||||
func NewUserPasswordResetConfirm(app core.App) *UserPasswordResetConfirm {
|
func NewUserPasswordResetConfirm(app core.App) *UserPasswordResetConfirm {
|
||||||
return &UserPasswordResetConfirm{
|
return NewUserPasswordResetConfirmWithConfig(UserPasswordResetConfirmConfig{
|
||||||
app: app,
|
App: app,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserPasswordResetConfirmWithConfig creates a new [UserPasswordResetConfirm]
|
||||||
|
// form with the provided config or panics on invalid configuration.
|
||||||
|
func NewUserPasswordResetConfirmWithConfig(config UserPasswordResetConfirmConfig) *UserPasswordResetConfirm {
|
||||||
|
form := &UserPasswordResetConfirm{config: config}
|
||||||
|
|
||||||
|
if form.config.App == nil {
|
||||||
|
panic("Missing required config.App instance.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.config.TxDao == nil {
|
||||||
|
form.config.TxDao = form.config.App.Dao()
|
||||||
|
}
|
||||||
|
|
||||||
|
return form
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
||||||
func (form *UserPasswordResetConfirm) Validate() error {
|
func (form *UserPasswordResetConfirm) Validate() error {
|
||||||
minPasswordLength := form.app.Settings().EmailAuth.MinPasswordLength
|
minPasswordLength := form.config.App.Settings().EmailAuth.MinPasswordLength
|
||||||
|
|
||||||
return validation.ValidateStruct(form,
|
return validation.ValidateStruct(form,
|
||||||
validation.Field(&form.Token, validation.Required, validation.By(form.checkToken)),
|
validation.Field(&form.Token, validation.Required, validation.By(form.checkToken)),
|
||||||
|
@ -40,9 +70,9 @@ func (form *UserPasswordResetConfirm) checkToken(value any) error {
|
||||||
return nil // nothing to check
|
return nil // nothing to check
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := form.app.Dao().FindUserByToken(
|
user, err := form.config.TxDao.FindUserByToken(
|
||||||
v,
|
v,
|
||||||
form.app.Settings().UserPasswordResetToken.Secret,
|
form.config.App.Settings().UserPasswordResetToken.Secret,
|
||||||
)
|
)
|
||||||
if err != nil || user == nil {
|
if err != nil || user == nil {
|
||||||
return validation.NewError("validation_invalid_token", "Invalid or expired token.")
|
return validation.NewError("validation_invalid_token", "Invalid or expired token.")
|
||||||
|
@ -58,9 +88,9 @@ func (form *UserPasswordResetConfirm) Submit() (*models.User, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := form.app.Dao().FindUserByToken(
|
user, err := form.config.TxDao.FindUserByToken(
|
||||||
form.Token,
|
form.Token,
|
||||||
form.app.Settings().UserPasswordResetToken.Secret,
|
form.config.App.Settings().UserPasswordResetToken.Secret,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -70,7 +100,7 @@ func (form *UserPasswordResetConfirm) Submit() (*models.User, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := form.app.Dao().SaveUser(user); err != nil {
|
if err := form.config.TxDao.SaveUser(user); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,24 +7,54 @@ import (
|
||||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||||
"github.com/go-ozzo/ozzo-validation/v4/is"
|
"github.com/go-ozzo/ozzo-validation/v4/is"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
"github.com/pocketbase/pocketbase/mails"
|
"github.com/pocketbase/pocketbase/mails"
|
||||||
"github.com/pocketbase/pocketbase/tools/types"
|
"github.com/pocketbase/pocketbase/tools/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserPasswordResetRequest defines a user password reset request form.
|
// UserPasswordResetRequest specifies a user password reset request form.
|
||||||
type UserPasswordResetRequest struct {
|
type UserPasswordResetRequest struct {
|
||||||
app core.App
|
config UserPasswordResetRequestConfig
|
||||||
resendThreshold float64
|
|
||||||
|
|
||||||
Email string `form:"email" json:"email"`
|
Email string `form:"email" json:"email"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUserPasswordResetRequest creates new user password reset request form.
|
// UserPasswordResetRequestConfig is the [UserPasswordResetRequest]
|
||||||
|
// factory initializer config.
|
||||||
|
//
|
||||||
|
// NB! App is required struct member.
|
||||||
|
type UserPasswordResetRequestConfig struct {
|
||||||
|
App core.App
|
||||||
|
TxDao *daos.Dao
|
||||||
|
ResendThreshold float64 // in seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserPasswordResetRequest creates a new [UserPasswordResetRequest]
|
||||||
|
// form with initializer config created from the provided [core.App] instance.
|
||||||
|
//
|
||||||
|
// If you want to submit the form as part of another transaction, use
|
||||||
|
// [NewUserPasswordResetRequestWithConfig] with explicitly set TxDao.
|
||||||
func NewUserPasswordResetRequest(app core.App) *UserPasswordResetRequest {
|
func NewUserPasswordResetRequest(app core.App) *UserPasswordResetRequest {
|
||||||
return &UserPasswordResetRequest{
|
return NewUserPasswordResetRequestWithConfig(UserPasswordResetRequestConfig{
|
||||||
app: app,
|
App: app,
|
||||||
resendThreshold: 120, // 2 min
|
ResendThreshold: 120, // 2 min
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserPasswordResetRequestWithConfig creates a new [UserPasswordResetRequest]
|
||||||
|
// form with the provided config or panics on invalid configuration.
|
||||||
|
func NewUserPasswordResetRequestWithConfig(config UserPasswordResetRequestConfig) *UserPasswordResetRequest {
|
||||||
|
form := &UserPasswordResetRequest{config: config}
|
||||||
|
|
||||||
|
if form.config.App == nil {
|
||||||
|
panic("Missing required config.App instance.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.config.TxDao == nil {
|
||||||
|
form.config.TxDao = form.config.App.Dao()
|
||||||
|
}
|
||||||
|
|
||||||
|
return form
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
||||||
|
@ -48,23 +78,23 @@ func (form *UserPasswordResetRequest) Submit() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := form.app.Dao().FindUserByEmail(form.Email)
|
user, err := form.config.TxDao.FindUserByEmail(form.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
lastResetSentAt := user.LastResetSentAt.Time()
|
lastResetSentAt := user.LastResetSentAt.Time()
|
||||||
if now.Sub(lastResetSentAt).Seconds() < form.resendThreshold {
|
if now.Sub(lastResetSentAt).Seconds() < form.config.ResendThreshold {
|
||||||
return errors.New("You've already requested a password reset.")
|
return errors.New("You've already requested a password reset.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := mails.SendUserPasswordReset(form.app, user); err != nil {
|
if err := mails.SendUserPasswordReset(form.config.App, user); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// update last sent timestamp
|
// update last sent timestamp
|
||||||
user.LastResetSentAt = types.NowDateTime()
|
user.LastResetSentAt = types.NowDateTime()
|
||||||
|
|
||||||
return form.app.Dao().SaveUser(user)
|
return form.config.TxDao.SaveUser(user)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,28 +26,26 @@ type UserUpsert struct {
|
||||||
|
|
||||||
// UserUpsertConfig is the [UserUpsert] factory initializer config.
|
// UserUpsertConfig is the [UserUpsert] factory initializer config.
|
||||||
//
|
//
|
||||||
// NB! Dao and Settings are required struct members.
|
// NB! App is required struct member.
|
||||||
type UserUpsertConfig struct {
|
type UserUpsertConfig struct {
|
||||||
Dao *daos.Dao
|
App core.App
|
||||||
Settings *core.Settings
|
TxDao *daos.Dao
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUserUpsert creates a new [UserUpsert] form with initializer
|
// NewUserUpsert creates a new [UserUpsert] form with initializer
|
||||||
// config created from the provided [core.App] and [models.User] instances
|
// config created from the provided [core.App] instance
|
||||||
// (for create you could pass a pointer to an empty User - `&models.User{}`).
|
// (for create you could pass a pointer to an empty User - `&models.User{}`).
|
||||||
//
|
//
|
||||||
// This factory method is used primarily for convenience (and backward compatibility).
|
|
||||||
// If you want to submit the form as part of another transaction, use
|
// If you want to submit the form as part of another transaction, use
|
||||||
// [NewUserUpsertWithConfig] with Dao configured to your txDao.
|
// [NewUserEmailChangeConfirmWithConfig] with explicitly set TxDao.
|
||||||
func NewUserUpsert(app core.App, user *models.User) *UserUpsert {
|
func NewUserUpsert(app core.App, user *models.User) *UserUpsert {
|
||||||
return NewUserUpsertWithConfig(UserUpsertConfig{
|
return NewUserUpsertWithConfig(UserUpsertConfig{
|
||||||
Dao: app.Dao(),
|
App: app,
|
||||||
Settings: app.Settings(),
|
|
||||||
}, user)
|
}, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUserUpsertWithConfig creates a new [UserUpsert] form
|
// NewUserUpsertWithConfig creates a new [UserUpsert] form with the provided
|
||||||
// with the provided config and [models.User] instance or panics on invalid configuration
|
// config and [models.User] instance or panics on invalid configuration
|
||||||
// (for create you could pass a pointer to an empty User - `&models.User{}`).
|
// (for create you could pass a pointer to an empty User - `&models.User{}`).
|
||||||
func NewUserUpsertWithConfig(config UserUpsertConfig, user *models.User) *UserUpsert {
|
func NewUserUpsertWithConfig(config UserUpsertConfig, user *models.User) *UserUpsert {
|
||||||
form := &UserUpsert{
|
form := &UserUpsert{
|
||||||
|
@ -55,12 +53,14 @@ func NewUserUpsertWithConfig(config UserUpsertConfig, user *models.User) *UserUp
|
||||||
user: user,
|
user: user,
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.config.Dao == nil ||
|
if form.config.App == nil || form.user == nil {
|
||||||
form.config.Settings == nil ||
|
|
||||||
form.user == nil {
|
|
||||||
panic("Invalid initializer config or nil upsert model.")
|
panic("Invalid initializer config or nil upsert model.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.config.TxDao == nil {
|
||||||
|
form.config.TxDao = form.config.App.Dao()
|
||||||
|
}
|
||||||
|
|
||||||
// load defaults
|
// load defaults
|
||||||
form.Id = user.Id
|
form.Id = user.Id
|
||||||
form.Email = user.Email
|
form.Email = user.Email
|
||||||
|
@ -89,7 +89,7 @@ func (form *UserUpsert) Validate() error {
|
||||||
validation.Field(
|
validation.Field(
|
||||||
&form.Password,
|
&form.Password,
|
||||||
validation.When(form.user.IsNew(), validation.Required),
|
validation.When(form.user.IsNew(), validation.Required),
|
||||||
validation.Length(form.config.Settings.EmailAuth.MinPasswordLength, 100),
|
validation.Length(form.config.App.Settings().EmailAuth.MinPasswordLength, 100),
|
||||||
),
|
),
|
||||||
validation.Field(
|
validation.Field(
|
||||||
&form.PasswordConfirm,
|
&form.PasswordConfirm,
|
||||||
|
@ -102,7 +102,7 @@ func (form *UserUpsert) Validate() error {
|
||||||
func (form *UserUpsert) checkUniqueEmail(value any) error {
|
func (form *UserUpsert) checkUniqueEmail(value any) error {
|
||||||
v, _ := value.(string)
|
v, _ := value.(string)
|
||||||
|
|
||||||
if v == "" || form.config.Dao.IsUserEmailUnique(v, form.user.Id) {
|
if v == "" || form.config.TxDao.IsUserEmailUnique(v, form.user.Id) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,8 +116,8 @@ func (form *UserUpsert) checkEmailDomain(value any) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
domain := val[strings.LastIndex(val, "@")+1:]
|
domain := val[strings.LastIndex(val, "@")+1:]
|
||||||
only := form.config.Settings.EmailAuth.OnlyDomains
|
only := form.config.App.Settings().EmailAuth.OnlyDomains
|
||||||
except := form.config.Settings.EmailAuth.ExceptDomains
|
except := form.config.App.Settings().EmailAuth.ExceptDomains
|
||||||
|
|
||||||
// only domains check
|
// only domains check
|
||||||
if len(only) > 0 && !list.ExistInSlice(domain, only) {
|
if len(only) > 0 && !list.ExistInSlice(domain, only) {
|
||||||
|
@ -159,6 +159,6 @@ func (form *UserUpsert) Submit(interceptors ...InterceptorFunc) error {
|
||||||
form.user.Email = form.Email
|
form.user.Email = form.Email
|
||||||
|
|
||||||
return runInterceptors(func() error {
|
return runInterceptors(func() error {
|
||||||
return form.config.Dao.SaveUser(form.user)
|
return form.config.TxDao.SaveUser(form.user)
|
||||||
}, interceptors...)
|
}, interceptors...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,21 +3,51 @@ package forms
|
||||||
import (
|
import (
|
||||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserVerificationConfirm defines a user email confirmation form.
|
// UserVerificationConfirm specifies a user email verification confirmation form.
|
||||||
type UserVerificationConfirm struct {
|
type UserVerificationConfirm struct {
|
||||||
app core.App
|
config UserVerificationConfirmConfig
|
||||||
|
|
||||||
Token string `form:"token" json:"token"`
|
Token string `form:"token" json:"token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUserVerificationConfirm creates a new user email confirmation form.
|
// UserVerificationConfirmConfig is the [UserVerificationConfirm]
|
||||||
|
// factory initializer config.
|
||||||
|
//
|
||||||
|
// NB! App is required struct member.
|
||||||
|
type UserVerificationConfirmConfig struct {
|
||||||
|
App core.App
|
||||||
|
TxDao *daos.Dao
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserVerificationConfirm creates a new [UserVerificationConfirm]
|
||||||
|
// form with initializer config created from the provided [core.App] instance.
|
||||||
|
//
|
||||||
|
// If you want to submit the form as part of another transaction, use
|
||||||
|
// [NewUserVerificationConfirmWithConfig] with explicitly set TxDao.
|
||||||
func NewUserVerificationConfirm(app core.App) *UserVerificationConfirm {
|
func NewUserVerificationConfirm(app core.App) *UserVerificationConfirm {
|
||||||
return &UserVerificationConfirm{
|
return NewUserVerificationConfirmWithConfig(UserVerificationConfirmConfig{
|
||||||
app: app,
|
App: app,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserVerificationConfirmWithConfig creates a new [UserVerificationConfirmConfig]
|
||||||
|
// form with the provided config or panics on invalid configuration.
|
||||||
|
func NewUserVerificationConfirmWithConfig(config UserVerificationConfirmConfig) *UserVerificationConfirm {
|
||||||
|
form := &UserVerificationConfirm{config: config}
|
||||||
|
|
||||||
|
if form.config.App == nil {
|
||||||
|
panic("Missing required config.App instance.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.config.TxDao == nil {
|
||||||
|
form.config.TxDao = form.config.App.Dao()
|
||||||
|
}
|
||||||
|
|
||||||
|
return form
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
||||||
|
@ -33,9 +63,9 @@ func (form *UserVerificationConfirm) checkToken(value any) error {
|
||||||
return nil // nothing to check
|
return nil // nothing to check
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := form.app.Dao().FindUserByToken(
|
user, err := form.config.TxDao.FindUserByToken(
|
||||||
v,
|
v,
|
||||||
form.app.Settings().UserVerificationToken.Secret,
|
form.config.App.Settings().UserVerificationToken.Secret,
|
||||||
)
|
)
|
||||||
if err != nil || user == nil {
|
if err != nil || user == nil {
|
||||||
return validation.NewError("validation_invalid_token", "Invalid or expired token.")
|
return validation.NewError("validation_invalid_token", "Invalid or expired token.")
|
||||||
|
@ -51,9 +81,9 @@ func (form *UserVerificationConfirm) Submit() (*models.User, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := form.app.Dao().FindUserByToken(
|
user, err := form.config.TxDao.FindUserByToken(
|
||||||
form.Token,
|
form.Token,
|
||||||
form.app.Settings().UserVerificationToken.Secret,
|
form.config.App.Settings().UserVerificationToken.Secret,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -65,7 +95,7 @@ func (form *UserVerificationConfirm) Submit() (*models.User, error) {
|
||||||
|
|
||||||
user.Verified = true
|
user.Verified = true
|
||||||
|
|
||||||
if err := form.app.Dao().SaveUser(user); err != nil {
|
if err := form.config.TxDao.SaveUser(user); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,24 +7,54 @@ import (
|
||||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||||
"github.com/go-ozzo/ozzo-validation/v4/is"
|
"github.com/go-ozzo/ozzo-validation/v4/is"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
"github.com/pocketbase/pocketbase/mails"
|
"github.com/pocketbase/pocketbase/mails"
|
||||||
"github.com/pocketbase/pocketbase/tools/types"
|
"github.com/pocketbase/pocketbase/tools/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserVerificationRequest defines a user email verification request form.
|
// UserVerificationRequest defines a user email verification request form.
|
||||||
type UserVerificationRequest struct {
|
type UserVerificationRequest struct {
|
||||||
app core.App
|
config UserVerificationRequestConfig
|
||||||
resendThreshold float64
|
|
||||||
|
|
||||||
Email string `form:"email" json:"email"`
|
Email string `form:"email" json:"email"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUserVerificationRequest creates a new user email verification request form.
|
// UserVerificationRequestConfig is the [UserVerificationRequest]
|
||||||
|
// factory initializer config.
|
||||||
|
//
|
||||||
|
// NB! App is required struct member.
|
||||||
|
type UserVerificationRequestConfig struct {
|
||||||
|
App core.App
|
||||||
|
TxDao *daos.Dao
|
||||||
|
ResendThreshold float64 // in seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserVerificationRequest creates a new [UserVerificationRequest]
|
||||||
|
// form with initializer config created from the provided [core.App] instance.
|
||||||
|
//
|
||||||
|
// If you want to submit the form as part of another transaction, use
|
||||||
|
// [NewUserVerificationRequestWithConfig] with explicitly set TxDao.
|
||||||
func NewUserVerificationRequest(app core.App) *UserVerificationRequest {
|
func NewUserVerificationRequest(app core.App) *UserVerificationRequest {
|
||||||
return &UserVerificationRequest{
|
return NewUserVerificationRequestWithConfig(UserVerificationRequestConfig{
|
||||||
app: app,
|
App: app,
|
||||||
resendThreshold: 120, // 2 min
|
ResendThreshold: 120, // 2 min
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserVerificationRequestWithConfig creates a new [UserVerificationRequest]
|
||||||
|
// form with the provided config or panics on invalid configuration.
|
||||||
|
func NewUserVerificationRequestWithConfig(config UserVerificationRequestConfig) *UserVerificationRequest {
|
||||||
|
form := &UserVerificationRequest{config: config}
|
||||||
|
|
||||||
|
if form.config.App == nil {
|
||||||
|
panic("Missing required config.App instance.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.config.TxDao == nil {
|
||||||
|
form.config.TxDao = form.config.App.Dao()
|
||||||
|
}
|
||||||
|
|
||||||
|
return form
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
||||||
|
@ -48,7 +78,7 @@ func (form *UserVerificationRequest) Submit() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := form.app.Dao().FindUserByEmail(form.Email)
|
user, err := form.config.TxDao.FindUserByEmail(form.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -59,16 +89,16 @@ func (form *UserVerificationRequest) Submit() error {
|
||||||
|
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
lastVerificationSentAt := user.LastVerificationSentAt.Time()
|
lastVerificationSentAt := user.LastVerificationSentAt.Time()
|
||||||
if (now.Sub(lastVerificationSentAt)).Seconds() < form.resendThreshold {
|
if (now.Sub(lastVerificationSentAt)).Seconds() < form.config.ResendThreshold {
|
||||||
return errors.New("A verification email was already sent.")
|
return errors.New("A verification email was already sent.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := mails.SendUserVerification(form.app, user); err != nil {
|
if err := mails.SendUserVerification(form.config.App, user); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// update last sent timestamp
|
// update last sent timestamp
|
||||||
user.LastVerificationSentAt = types.NowDateTime()
|
user.LastVerificationSentAt = types.NowDateTime()
|
||||||
|
|
||||||
return form.app.Dao().SaveUser(user)
|
return form.config.TxDao.SaveUser(user)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue