mirror of
https://github.com/pocketbase/pocketbase.git
synced 2025-02-03 18:02:17 +02:00
updated admin and user upsert forms
This commit is contained in:
parent
51d635bc12
commit
956263d1fc
@ -21,7 +21,7 @@ func BindCollectionApi(app core.App, rg *echo.Group) {
|
|||||||
subGroup.GET("/:collection", api.view)
|
subGroup.GET("/:collection", api.view)
|
||||||
subGroup.PATCH("/:collection", api.update)
|
subGroup.PATCH("/:collection", api.update)
|
||||||
subGroup.DELETE("/:collection", api.delete)
|
subGroup.DELETE("/:collection", api.delete)
|
||||||
subGroup.POST("/import", api.bulkImport)
|
subGroup.PUT("/import", api.bulkImport)
|
||||||
}
|
}
|
||||||
|
|
||||||
type collectionApi struct {
|
type collectionApi struct {
|
||||||
|
@ -4,32 +4,58 @@ 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/forms/validators"
|
"github.com/pocketbase/pocketbase/forms/validators"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AdminUpsert defines an admin upsert (create/update) form.
|
// AdminUpsert specifies a [models.Admin] upsert (create/update) form.
|
||||||
type AdminUpsert struct {
|
type AdminUpsert struct {
|
||||||
app core.App
|
config AdminUpsertConfig
|
||||||
admin *models.Admin
|
admin *models.Admin
|
||||||
isCreate bool
|
|
||||||
|
|
||||||
|
Id string `form:"id" json:"id"`
|
||||||
Avatar int `form:"avatar" json:"avatar"`
|
Avatar int `form:"avatar" json:"avatar"`
|
||||||
Email string `form:"email" json:"email"`
|
Email string `form:"email" json:"email"`
|
||||||
Password string `form:"password" json:"password"`
|
Password string `form:"password" json:"password"`
|
||||||
PasswordConfirm string `form:"passwordConfirm" json:"passwordConfirm"`
|
PasswordConfirm string `form:"passwordConfirm" json:"passwordConfirm"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAdminUpsert creates new upsert form for the provided admin model
|
// AdminUpsertConfig is the [AdminUpsert] factory initializer config.
|
||||||
// (pass an empty admin model instance (`&models.Admin{}`) for create).
|
//
|
||||||
|
// NB! Dao is a required struct member.
|
||||||
|
type AdminUpsertConfig struct {
|
||||||
|
Dao *daos.Dao
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAdminUpsert creates a new [AdminUpsert] form with initializer
|
||||||
|
// config created from the provided [core.App] and [models.Admin] instances
|
||||||
|
// (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
|
||||||
|
// [NewAdminUpsertWithConfig] with Dao configured to your txDao.
|
||||||
func NewAdminUpsert(app core.App, admin *models.Admin) *AdminUpsert {
|
func NewAdminUpsert(app core.App, admin *models.Admin) *AdminUpsert {
|
||||||
|
return NewAdminUpsertWithConfig(AdminUpsertConfig{
|
||||||
|
Dao: app.Dao(),
|
||||||
|
}, admin)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAdminUpsertWithConfig creates a new [AdminUpsert] form
|
||||||
|
// with the provided config and [models.Admin] instance or panics on invalid configuration
|
||||||
|
// (for create you could pass a pointer to an empty Admin - `&models.Admin{}`).
|
||||||
|
func NewAdminUpsertWithConfig(config AdminUpsertConfig, admin *models.Admin) *AdminUpsert {
|
||||||
form := &AdminUpsert{
|
form := &AdminUpsert{
|
||||||
app: app,
|
config: config,
|
||||||
admin: admin,
|
admin: admin,
|
||||||
isCreate: !admin.HasId(),
|
}
|
||||||
|
|
||||||
|
if form.config.Dao == nil || form.admin == nil {
|
||||||
|
panic("Invalid initializer config or nil upsert model.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// load defaults
|
// load defaults
|
||||||
|
form.Id = admin.Id
|
||||||
form.Avatar = admin.Avatar
|
form.Avatar = admin.Avatar
|
||||||
form.Email = admin.Email
|
form.Email = admin.Email
|
||||||
|
|
||||||
@ -39,6 +65,13 @@ func NewAdminUpsert(app core.App, admin *models.Admin) *AdminUpsert {
|
|||||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
||||||
func (form *AdminUpsert) Validate() error {
|
func (form *AdminUpsert) Validate() error {
|
||||||
return validation.ValidateStruct(form,
|
return validation.ValidateStruct(form,
|
||||||
|
validation.Field(
|
||||||
|
&form.Id,
|
||||||
|
validation.When(
|
||||||
|
form.admin.IsNew(),
|
||||||
|
validation.Length(models.DefaultIdLength, models.DefaultIdLength),
|
||||||
|
).Else(validation.In(form.admin.Id)),
|
||||||
|
),
|
||||||
validation.Field(
|
validation.Field(
|
||||||
&form.Avatar,
|
&form.Avatar,
|
||||||
validation.Min(0),
|
validation.Min(0),
|
||||||
@ -53,7 +86,7 @@ func (form *AdminUpsert) Validate() error {
|
|||||||
),
|
),
|
||||||
validation.Field(
|
validation.Field(
|
||||||
&form.Password,
|
&form.Password,
|
||||||
validation.When(form.isCreate, validation.Required),
|
validation.When(form.admin.IsNew(), validation.Required),
|
||||||
validation.Length(10, 100),
|
validation.Length(10, 100),
|
||||||
),
|
),
|
||||||
validation.Field(
|
validation.Field(
|
||||||
@ -67,7 +100,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.app.Dao().IsAdminEmailUnique(v, form.admin.Id) {
|
if form.config.Dao.IsAdminEmailUnique(v, form.admin.Id) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,6 +116,12 @@ func (form *AdminUpsert) Submit(interceptors ...InterceptorFunc) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// custom insertion id can be set only on create
|
||||||
|
if form.admin.IsNew() && form.Id != "" {
|
||||||
|
form.admin.MarkAsNew()
|
||||||
|
form.admin.SetId(form.Id)
|
||||||
|
}
|
||||||
|
|
||||||
form.admin.Avatar = form.Avatar
|
form.admin.Avatar = form.Avatar
|
||||||
form.admin.Email = form.Email
|
form.admin.Email = form.Email
|
||||||
|
|
||||||
@ -91,6 +130,6 @@ func (form *AdminUpsert) Submit(interceptors ...InterceptorFunc) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return runInterceptors(func() error {
|
return runInterceptors(func() error {
|
||||||
return form.app.Dao().SaveAdmin(form.admin)
|
return form.config.Dao.SaveAdmin(form.admin)
|
||||||
}, interceptors...)
|
}, interceptors...)
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,8 @@ type CollectionUpsertConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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{}`).
|
||||||
//
|
//
|
||||||
// This factory method is used primarily for convenience (and backward compatibility).
|
// 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
|
||||||
@ -52,7 +53,7 @@ func NewCollectionUpsert(app core.App, collection *models.Collection) *Collectio
|
|||||||
|
|
||||||
// NewCollectionUpsertWithConfig creates a new [CollectionUpsert] form
|
// NewCollectionUpsertWithConfig creates a new [CollectionUpsert] form
|
||||||
// with the provided config and [models.Collection] instance or panics on invalid configuration
|
// with the provided config and [models.Collection] instance or panics on invalid configuration
|
||||||
// (for create you could pass a pointer to an empty Collection instance - `&models.Collection{}`).
|
// (for create you could pass a pointer to an empty Collection - `&models.Collection{}`).
|
||||||
func NewCollectionUpsertWithConfig(config CollectionUpsertConfig, collection *models.Collection) *CollectionUpsert {
|
func NewCollectionUpsertWithConfig(config CollectionUpsertConfig, collection *models.Collection) *CollectionUpsert {
|
||||||
form := &CollectionUpsert{
|
form := &CollectionUpsert{
|
||||||
config: config,
|
config: config,
|
||||||
@ -88,7 +89,10 @@ func (form *CollectionUpsert) Validate() error {
|
|||||||
return validation.ValidateStruct(form,
|
return validation.ValidateStruct(form,
|
||||||
validation.Field(
|
validation.Field(
|
||||||
&form.Id,
|
&form.Id,
|
||||||
validation.Length(models.DefaultIdLength, models.DefaultIdLength),
|
validation.When(
|
||||||
|
form.collection.IsNew(),
|
||||||
|
validation.Length(models.DefaultIdLength, models.DefaultIdLength),
|
||||||
|
).Else(validation.In(form.collection.Id)),
|
||||||
),
|
),
|
||||||
validation.Field(
|
validation.Field(
|
||||||
&form.System,
|
&form.System,
|
||||||
|
@ -9,40 +9,74 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||||
"github.com/pocketbase/dbx"
|
"github.com/pocketbase/dbx"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
"github.com/pocketbase/pocketbase/daos"
|
"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"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RecordUpsert defines a Record upsert form.
|
// RecordUpsert specifies a [models.Record] upsert (create/update) form.
|
||||||
type RecordUpsert struct {
|
type RecordUpsert struct {
|
||||||
app core.App
|
config RecordUpsertConfig
|
||||||
record *models.Record
|
record *models.Record
|
||||||
|
|
||||||
isCreate bool
|
|
||||||
filesToDelete []string // names list
|
filesToDelete []string // names list
|
||||||
filesToUpload []*rest.UploadedFile
|
filesToUpload []*rest.UploadedFile
|
||||||
|
|
||||||
|
Id string `form:"id" json:"id"`
|
||||||
Data map[string]any `json:"data"`
|
Data map[string]any `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRecordUpsert creates a new Record upsert form.
|
// RecordUpsertConfig is the [RecordUpsert] factory initializer config.
|
||||||
// (pass a new Record model instance (`models.NewRecord(...)`) for create).
|
//
|
||||||
|
// NB! Dao and FilesystemFactory are required struct members.
|
||||||
|
type RecordUpsertConfig struct {
|
||||||
|
Dao *daos.Dao
|
||||||
|
FilesystemFactory func() (*filesystem.System, error)
|
||||||
|
IsDebug bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRecordUpsert creates a new [RecordUpsert] form with initializer
|
||||||
|
// config created from the provided [core.App] and [models.Record] instances
|
||||||
|
// (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
|
||||||
|
// [NewRecordUpsertWithConfig] with Dao configured to your txDao.
|
||||||
func NewRecordUpsert(app core.App, record *models.Record) *RecordUpsert {
|
func NewRecordUpsert(app core.App, record *models.Record) *RecordUpsert {
|
||||||
|
return NewRecordUpsertWithConfig(RecordUpsertConfig{
|
||||||
|
Dao: app.Dao(),
|
||||||
|
FilesystemFactory: app.NewFilesystem,
|
||||||
|
IsDebug: app.IsDebug(),
|
||||||
|
}, record)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRecordUpsertWithConfig creates a new [RecordUpsert] form
|
||||||
|
// with the provided config and [models.Record] instance or panics on invalid configuration
|
||||||
|
// (for create you could pass a pointer to an empty Record - `&models.Record{}`).
|
||||||
|
func NewRecordUpsertWithConfig(config RecordUpsertConfig, record *models.Record) *RecordUpsert {
|
||||||
form := &RecordUpsert{
|
form := &RecordUpsert{
|
||||||
app: app,
|
config: config,
|
||||||
record: record,
|
record: record,
|
||||||
isCreate: !record.HasId(),
|
|
||||||
filesToDelete: []string{},
|
filesToDelete: []string{},
|
||||||
filesToUpload: []*rest.UploadedFile{},
|
filesToUpload: []*rest.UploadedFile{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.config.Dao == nil ||
|
||||||
|
form.config.FilesystemFactory == nil ||
|
||||||
|
form.record == nil {
|
||||||
|
panic("Invalid initializer config or nil upsert model.")
|
||||||
|
}
|
||||||
|
|
||||||
|
form.Id = record.Id
|
||||||
|
|
||||||
form.Data = map[string]any{}
|
form.Data = map[string]any{}
|
||||||
for _, field := range record.Collection().Schema.Fields() {
|
for _, field := range record.Collection().Schema.Fields() {
|
||||||
form.Data[field.Name] = record.GetDataValue(field.Name)
|
form.Data[field.Name] = record.GetDataValue(field.Name)
|
||||||
@ -136,6 +170,10 @@ func (form *RecordUpsert) LoadData(r *http.Request) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if id, ok := requestData["id"]; ok {
|
||||||
|
form.Id = cast.ToString(id)
|
||||||
|
}
|
||||||
|
|
||||||
// extend base data with the extracted one
|
// extend base data with the extracted one
|
||||||
extendedData := form.record.Data()
|
extendedData := form.record.Data()
|
||||||
rawData, err := json.Marshal(requestData)
|
rawData, err := json.Marshal(requestData)
|
||||||
@ -202,7 +240,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.app.IsDebug() {
|
if form.config.IsDebug {
|
||||||
log.Printf("%q uploaded file error: %v\n", key, err)
|
log.Printf("%q uploaded file error: %v\n", key, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,8 +272,23 @@ func (form *RecordUpsert) LoadData(r *http.Request) error {
|
|||||||
|
|
||||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
||||||
func (form *RecordUpsert) Validate() error {
|
func (form *RecordUpsert) Validate() error {
|
||||||
|
// base form fields validator
|
||||||
|
baseFieldsErrors := validation.ValidateStruct(form,
|
||||||
|
validation.Field(
|
||||||
|
&form.Id,
|
||||||
|
validation.When(
|
||||||
|
form.record.IsNew(),
|
||||||
|
validation.Length(models.DefaultIdLength, models.DefaultIdLength),
|
||||||
|
).Else(validation.In(form.record.Id)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if baseFieldsErrors != nil {
|
||||||
|
return baseFieldsErrors
|
||||||
|
}
|
||||||
|
|
||||||
|
// record data validator
|
||||||
dataValidator := validators.NewRecordDataValidator(
|
dataValidator := validators.NewRecordDataValidator(
|
||||||
form.app.Dao(),
|
form.config.Dao,
|
||||||
form.record,
|
form.record,
|
||||||
form.filesToUpload,
|
form.filesToUpload,
|
||||||
)
|
)
|
||||||
@ -252,12 +305,18 @@ func (form *RecordUpsert) DrySubmit(callback func(txDao *daos.Dao) error) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// custom insertion id can be set only on create
|
||||||
|
if form.record.IsNew() && form.Id != "" {
|
||||||
|
form.record.MarkAsNew()
|
||||||
|
form.record.SetId(form.Id)
|
||||||
|
}
|
||||||
|
|
||||||
// bulk load form data
|
// bulk load form data
|
||||||
if err := form.record.Load(form.Data); err != nil {
|
if err := form.record.Load(form.Data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return form.app.Dao().RunInTransaction(func(txDao *daos.Dao) error {
|
return form.config.Dao.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")
|
||||||
@ -285,13 +344,19 @@ func (form *RecordUpsert) Submit(interceptors ...InterceptorFunc) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// custom insertion id can be set only on create
|
||||||
|
if form.record.IsNew() && form.Id != "" {
|
||||||
|
form.record.MarkAsNew()
|
||||||
|
form.record.SetId(form.Id)
|
||||||
|
}
|
||||||
|
|
||||||
// bulk load form data
|
// bulk load form data
|
||||||
if err := form.record.Load(form.Data); err != nil {
|
if err := form.record.Load(form.Data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return runInterceptors(func() error {
|
return runInterceptors(func() error {
|
||||||
return form.app.Dao().RunInTransaction(func(txDao *daos.Dao) error {
|
return form.config.Dao.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
|
||||||
@ -322,7 +387,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.app.NewFilesystem()
|
fs, err := form.config.FilesystemFactory()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -358,7 +423,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.app.NewFilesystem()
|
fs, err := form.config.FilesystemFactory()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -6,33 +6,63 @@ 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/forms/validators"
|
"github.com/pocketbase/pocketbase/forms/validators"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
"github.com/pocketbase/pocketbase/tools/list"
|
"github.com/pocketbase/pocketbase/tools/list"
|
||||||
"github.com/pocketbase/pocketbase/tools/types"
|
"github.com/pocketbase/pocketbase/tools/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserUpsert defines a user upsert (create/update) form.
|
// UserUpsert specifies a [models.User] upsert (create/update) form.
|
||||||
type UserUpsert struct {
|
type UserUpsert struct {
|
||||||
app core.App
|
config UserUpsertConfig
|
||||||
user *models.User
|
user *models.User
|
||||||
isCreate bool
|
|
||||||
|
|
||||||
|
Id string `form:"id" json:"id"`
|
||||||
Email string `form:"email" json:"email"`
|
Email string `form:"email" json:"email"`
|
||||||
Password string `form:"password" json:"password"`
|
Password string `form:"password" json:"password"`
|
||||||
PasswordConfirm string `form:"passwordConfirm" json:"passwordConfirm"`
|
PasswordConfirm string `form:"passwordConfirm" json:"passwordConfirm"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUserUpsert creates new upsert form for the provided user model
|
// UserUpsertConfig is the [UserUpsert] factory initializer config.
|
||||||
// (pass an empty user model instance (`&models.User{}`) for create).
|
//
|
||||||
|
// NB! Dao and Settings are required struct members.
|
||||||
|
type UserUpsertConfig struct {
|
||||||
|
Dao *daos.Dao
|
||||||
|
Settings *core.Settings
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserUpsert creates a new [UserUpsert] form with initializer
|
||||||
|
// config created from the provided [core.App] and [models.User] instances
|
||||||
|
// (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
|
||||||
|
// [NewUserUpsertWithConfig] with Dao configured to your txDao.
|
||||||
func NewUserUpsert(app core.App, user *models.User) *UserUpsert {
|
func NewUserUpsert(app core.App, user *models.User) *UserUpsert {
|
||||||
|
return NewUserUpsertWithConfig(UserUpsertConfig{
|
||||||
|
Dao: app.Dao(),
|
||||||
|
Settings: app.Settings(),
|
||||||
|
}, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserUpsertWithConfig creates a new [UserUpsert] form
|
||||||
|
// with the provided config and [models.User] instance or panics on invalid configuration
|
||||||
|
// (for create you could pass a pointer to an empty User - `&models.User{}`).
|
||||||
|
func NewUserUpsertWithConfig(config UserUpsertConfig, user *models.User) *UserUpsert {
|
||||||
form := &UserUpsert{
|
form := &UserUpsert{
|
||||||
app: app,
|
config: config,
|
||||||
user: user,
|
user: user,
|
||||||
isCreate: !user.HasId(),
|
}
|
||||||
|
|
||||||
|
if form.config.Dao == nil ||
|
||||||
|
form.config.Settings == nil ||
|
||||||
|
form.user == nil {
|
||||||
|
panic("Invalid initializer config or nil upsert model.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// load defaults
|
// load defaults
|
||||||
|
form.Id = user.Id
|
||||||
form.Email = user.Email
|
form.Email = user.Email
|
||||||
|
|
||||||
return form
|
return form
|
||||||
@ -40,9 +70,14 @@ func NewUserUpsert(app core.App, user *models.User) *UserUpsert {
|
|||||||
|
|
||||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
||||||
func (form *UserUpsert) Validate() error {
|
func (form *UserUpsert) Validate() error {
|
||||||
config := form.app.Settings()
|
|
||||||
|
|
||||||
return validation.ValidateStruct(form,
|
return validation.ValidateStruct(form,
|
||||||
|
validation.Field(
|
||||||
|
&form.Id,
|
||||||
|
validation.When(
|
||||||
|
form.user.IsNew(),
|
||||||
|
validation.Length(models.DefaultIdLength, models.DefaultIdLength),
|
||||||
|
).Else(validation.In(form.user.Id)),
|
||||||
|
),
|
||||||
validation.Field(
|
validation.Field(
|
||||||
&form.Email,
|
&form.Email,
|
||||||
validation.Required,
|
validation.Required,
|
||||||
@ -53,12 +88,12 @@ func (form *UserUpsert) Validate() error {
|
|||||||
),
|
),
|
||||||
validation.Field(
|
validation.Field(
|
||||||
&form.Password,
|
&form.Password,
|
||||||
validation.When(form.isCreate, validation.Required),
|
validation.When(form.user.IsNew(), validation.Required),
|
||||||
validation.Length(config.EmailAuth.MinPasswordLength, 100),
|
validation.Length(form.config.Settings.EmailAuth.MinPasswordLength, 100),
|
||||||
),
|
),
|
||||||
validation.Field(
|
validation.Field(
|
||||||
&form.PasswordConfirm,
|
&form.PasswordConfirm,
|
||||||
validation.When(form.isCreate || form.Password != "", validation.Required),
|
validation.When(form.user.IsNew() || form.Password != "", validation.Required),
|
||||||
validation.By(validators.Compare(form.Password)),
|
validation.By(validators.Compare(form.Password)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -67,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.app.Dao().IsUserEmailUnique(v, form.user.Id) {
|
if v == "" || form.config.Dao.IsUserEmailUnique(v, form.user.Id) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,8 +116,8 @@ func (form *UserUpsert) checkEmailDomain(value any) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
domain := val[strings.LastIndex(val, "@")+1:]
|
domain := val[strings.LastIndex(val, "@")+1:]
|
||||||
only := form.app.Settings().EmailAuth.OnlyDomains
|
only := form.config.Settings.EmailAuth.OnlyDomains
|
||||||
except := form.app.Settings().EmailAuth.ExceptDomains
|
except := form.config.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) {
|
||||||
@ -110,7 +145,13 @@ func (form *UserUpsert) Submit(interceptors ...InterceptorFunc) error {
|
|||||||
form.user.SetPassword(form.Password)
|
form.user.SetPassword(form.Password)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !form.isCreate && form.Email != form.user.Email {
|
// custom insertion id can be set only on create
|
||||||
|
if form.user.IsNew() && form.Id != "" {
|
||||||
|
form.user.MarkAsNew()
|
||||||
|
form.user.SetId(form.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !form.user.IsNew() && form.Email != form.user.Email {
|
||||||
form.user.Verified = false
|
form.user.Verified = false
|
||||||
form.user.LastVerificationSentAt = types.DateTime{} // reset
|
form.user.LastVerificationSentAt = types.DateTime{} // reset
|
||||||
}
|
}
|
||||||
@ -118,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.app.Dao().SaveUser(form.user)
|
return form.config.Dao.SaveUser(form.user)
|
||||||
}, interceptors...)
|
}, interceptors...)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user