From 2a70399f3896322c7a36b42c60975b99be0c78a7 Mon Sep 17 00:00:00 2001 From: Ali Date: Sat, 16 May 2026 18:38:19 +0200 Subject: [PATCH 01/15] Update XDateTimeType.go --- modules/fireback/XDateTimeType.go | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/modules/fireback/XDateTimeType.go b/modules/fireback/XDateTimeType.go index 7c571e6d..a3bd6ac3 100644 --- a/modules/fireback/XDateTimeType.go +++ b/modules/fireback/XDateTimeType.go @@ -13,6 +13,7 @@ import ( ptime "github.com/yaa110/go-persian-calendar" "gorm.io/gorm" "gorm.io/gorm/clause" + "gorm.io/gorm/schema" ) type XDateTime string @@ -158,13 +159,24 @@ func (date XDateTime) Value() (driver.Value, error) { if date == "" { return nil, nil } - return string(date), nil + t, err := time.Parse(time.RFC3339, string(date)) + if err != nil { + return nil, err + } + + return t, nil } -// GormDataType gorm common data type -func (date XDateTime) GormDataType() string { - return "datetime" +func (date XDateTime) GormDBDataType(db *gorm.DB, field *schema.Field) string { + switch db.Dialector.Name() { + case "postgres": + return "timestamptz" + case "mysql": + return "datetime" + default: + return "datetime" + } } func (js XDateTime) GormValue(ctx context.Context, db *gorm.DB) clause.Expr { @@ -173,6 +185,12 @@ func (js XDateTime) GormValue(ctx context.Context, db *gorm.DB) clause.Expr { } switch db.Dialector.Name() { + case "postgres": + t, err := time.Parse(time.RFC3339, string(js)) + if err == nil { + return gorm.Expr("?", t) + } + case "mysql": parsedTime, err := time.Parse(time.RFC3339, string(js)) if err == nil { From cd045741e913490d7d39c313f4e5a4a53bbde490 Mon Sep 17 00:00:00 2001 From: Ali Date: Sat, 16 May 2026 18:38:24 +0200 Subject: [PATCH 02/15] Update CoreUtils.go --- modules/fireback/CoreUtils.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/fireback/CoreUtils.go b/modules/fireback/CoreUtils.go index 90d35b2f..54f0c08d 100644 --- a/modules/fireback/CoreUtils.go +++ b/modules/fireback/CoreUtils.go @@ -497,9 +497,7 @@ func askProjectDatabase(projectName string) (Database, error) { DATABASE_TYPE_SQLITE_MEMORY, DATABASE_TYPE_MYSQL, DATABASE_TYPE_MARIADB, - // Postgres is not well tested yet, we are not adding production ready - // features anymore in fireback at all. - // DATABASE_TYPE_POSTGRES, + DATABASE_TYPE_POSTGRES, }, } From 8b5bb86652ebca59035481a40cd3959bc85f79a5 Mon Sep 17 00:00:00 2001 From: Ali Date: Sat, 16 May 2026 18:39:12 +0200 Subject: [PATCH 03/15] Changed the generated code, and it works. Needs to be moved into the code gen, and requires transaction --- modules/abac/AppMenuEntity.dyno.go | 55 +++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/modules/abac/AppMenuEntity.dyno.go b/modules/abac/AppMenuEntity.dyno.go index 588544d2..20cd69ae 100644 --- a/modules/abac/AppMenuEntity.dyno.go +++ b/modules/abac/AppMenuEntity.dyno.go @@ -8,6 +8,9 @@ package abac import ( "encoding/json" "fmt" + "log" + "strings" + "github.com/gin-gonic/gin" jsoniter "github.com/json-iterator/go" "github.com/schollz/progressbar/v3" @@ -15,19 +18,19 @@ import ( "github.com/torabian/fireback/modules/fireback" "gorm.io/gorm" "gorm.io/gorm/clause" - "log" - "strings" + //queries github.com/torabian/fireback - modules/abac" "context" "embed" + reflect "reflect" + "time" + "github.com/torabian/emi/emigo" metas "github.com/torabian/fireback/modules/abac/metas" mocks "github.com/torabian/fireback/modules/abac/mocks/AppMenu" seeders "github.com/torabian/fireback/modules/abac/seeders/AppMenu" "github.com/urfave/cli/v3" "gopkg.in/yaml.v2" - reflect "reflect" - "time" ) var appMenuSeedersFs = &seeders.ViewsFs @@ -250,9 +253,9 @@ var AppMenuEntityMetaConfig map[string]int64 = map[string]int64{} var AppMenuEntityJsonSchema = fireback.ExtractEntityFields(reflect.ValueOf(&AppMenuEntity{})) type AppMenuEntityPolyglot struct { - LinkerId string `gorm:"uniqueId;not null;size:100;" json:"linkerId,omitempty" yaml:"linkerId,omitempty" xml:"linkerId,omitempty"` - LanguageId string `gorm:"uniqueId;not null;size:100;" json:"languageId,omitempty" xml:"languageId,omitempty" yaml:"languageId,omitempty"` - Label string `yaml:"label,omitempty" xml:"label,omitempty" json:"label,omitempty"` + LinkerId string `gorm:"not null;size:100;uniqueIndex:idx_polyglot" json:"linkerId,omitempty" yaml:"linkerId,omitempty" xml:"linkerId,omitempty"` + LanguageId string `gorm:"not null;size:100;uniqueIndex:idx_polyglot" json:"languageId,omitempty" xml:"languageId,omitempty" yaml:"languageId,omitempty"` + Label string `json:"label,omitempty" yaml:"label,omitempty"` } func entityAppMenuFormatter(dto *AppMenuEntity, query fireback.QueryDSL) { @@ -400,11 +403,13 @@ func AppMenuRecursiveAddUniqueId(dto *AppMenuEntity, query fireback.QueryDSL) { /* * - Batch inserts, do not have all features that create - operation does. Use it with unnormalized content, - or read the source code carefully. - This is not marked as an action, because it should not be available publicly - at this moment. + + Batch inserts, do not have all features that create + operation does. Use it with unnormalized content, + or read the source code carefully. + This is not marked as an action, because it should not be available publicly + at this moment. + * */ func AppMenuMultiInsertFn(dtos []*AppMenuEntity, query fireback.QueryDSL) ([]*AppMenuEntity, *fireback.IError) { @@ -461,7 +466,31 @@ func AppMenuActionCreateFn(dto *AppMenuEntity, query fireback.QueryDSL) (*AppMen dbref = query.Tx } query.Tx = dbref - err := dbref.Create(&dto).Error + + err := dbref.Omit("Translations").Create(&dto).Error + if err != nil { + err := fireback.GormErrorToIError(err) + return nil, err + } + + if len(dto.Translations) > 0 { + k, _ := json.MarshalIndent(dto.Translations, "", " ") + fmt.Println(string(k)) + + for _, tr := range dto.Translations { + tr.LinkerId = dto.UniqueId + } + err = dbref. + Clauses(clause.OnConflict{ + Columns: []clause.Column{ + {Name: "linker_id"}, + {Name: "language_id"}, + }, + DoUpdates: clause.AssignmentColumns([]string{"label"}), + }). + Create(&dto.Translations).Error + } + if err != nil { err := fireback.GormErrorToIError(err) return nil, err From 9b6935605cc9bae85e2f70a3ab86d6222c806a4e Mon Sep 17 00:00:00 2001 From: Ali Date: Sat, 16 May 2026 18:48:27 +0200 Subject: [PATCH 04/15] Made it transation --- modules/abac/AppMenuEntity.dyno.go | 70 ++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/modules/abac/AppMenuEntity.dyno.go b/modules/abac/AppMenuEntity.dyno.go index 20cd69ae..33a8d6ee 100644 --- a/modules/abac/AppMenuEntity.dyno.go +++ b/modules/abac/AppMenuEntity.dyno.go @@ -459,41 +459,63 @@ func AppMenuActionCreateFn(dto *AppMenuEntity, query fireback.QueryDSL) (*AppMen // 2. Append the necessary information about user, workspace AppMenuEntityBeforeCreateAppend(dto, query) // 4. Create the entity - var dbref *gorm.DB = nil - if query.Tx == nil { - dbref = fireback.GetDbRef() - } else { + var dbref *gorm.DB + + if query.Tx != nil { dbref = query.Tx + } else { + dbref = fireback.GetDbRef() } - query.Tx = dbref - err := dbref.Omit("Translations").Create(&dto).Error - if err != nil { - err := fireback.GormErrorToIError(err) - return nil, err - } + err := dbref.Transaction(func(tx *gorm.DB) error { - if len(dto.Translations) > 0 { - k, _ := json.MarshalIndent(dto.Translations, "", " ") - fmt.Println(string(k)) + query.Tx = tx - for _, tr := range dto.Translations { - tr.LinkerId = dto.UniqueId - } - err = dbref. + // create parent + if err := tx. + Omit("Translations"). Clauses(clause.OnConflict{ Columns: []clause.Column{ - {Name: "linker_id"}, - {Name: "language_id"}, + {Name: "unique_id"}, }, - DoUpdates: clause.AssignmentColumns([]string{"label"}), + DoUpdates: clause.AssignmentColumns([]string{ + "label", + "href", + "icon", + "active_matcher", + }), }). - Create(&dto.Translations).Error - } + Create(&dto).Error; err != nil { + return err + } + + // create translations + if len(dto.Translations) > 0 { + + for _, tr := range dto.Translations { + tr.LinkerId = dto.UniqueId + } + + if err := tx. + Clauses(clause.OnConflict{ + Columns: []clause.Column{ + {Name: "linker_id"}, + {Name: "language_id"}, + }, + DoUpdates: clause.AssignmentColumns([]string{ + "label", + }), + }). + Create(&dto.Translations).Error; err != nil { + return err + } + } + + return nil + }) if err != nil { - err := fireback.GormErrorToIError(err) - return nil, err + return nil, fireback.GormErrorToIError(err) } // 5. Create sub entities, objects or arrays, association to other entities AppMenuAssociationCreate(dto, query) From 802212ceb3af3d6ae7ccfd30f16801a2effd025a Mon Sep 17 00:00:00 2001 From: Ali Date: Sat, 16 May 2026 23:18:00 +0200 Subject: [PATCH 05/15] Postgres --- modules/abac/AppMenuEntity.dyno.go | 51 +++++++---------- modules/abac/TimezoneGroupEntity.dyno.go | 40 ++++++++++++- modules/abac/WorkspaceTypeEntity.dyno.go | 40 ++++++++++++- modules/abac/queries/AppMenuCte.vsql | 2 +- modules/abac/queries/WorkspaceCte.vsql | 2 +- modules/fireback/CapabilityEntity.dyno.go | 40 ++++++++++++- modules/fireback/CrudCoreActions.go | 16 +++++- .../codegen/firebackgo/SharedSnippets.tpl | 56 ++++++++++++++++++- .../codegen/firebackgo/SqlCteQuery.tpl | 4 +- 9 files changed, 205 insertions(+), 46 deletions(-) diff --git a/modules/abac/AppMenuEntity.dyno.go b/modules/abac/AppMenuEntity.dyno.go index 33a8d6ee..a6eb59d7 100644 --- a/modules/abac/AppMenuEntity.dyno.go +++ b/modules/abac/AppMenuEntity.dyno.go @@ -8,9 +8,6 @@ package abac import ( "encoding/json" "fmt" - "log" - "strings" - "github.com/gin-gonic/gin" jsoniter "github.com/json-iterator/go" "github.com/schollz/progressbar/v3" @@ -18,19 +15,19 @@ import ( "github.com/torabian/fireback/modules/fireback" "gorm.io/gorm" "gorm.io/gorm/clause" - + "log" + "strings" //queries github.com/torabian/fireback - modules/abac" "context" "embed" - reflect "reflect" - "time" - "github.com/torabian/emi/emigo" metas "github.com/torabian/fireback/modules/abac/metas" mocks "github.com/torabian/fireback/modules/abac/mocks/AppMenu" seeders "github.com/torabian/fireback/modules/abac/seeders/AppMenu" "github.com/urfave/cli/v3" "gopkg.in/yaml.v2" + reflect "reflect" + "time" ) var appMenuSeedersFs = &seeders.ViewsFs @@ -253,9 +250,9 @@ var AppMenuEntityMetaConfig map[string]int64 = map[string]int64{} var AppMenuEntityJsonSchema = fireback.ExtractEntityFields(reflect.ValueOf(&AppMenuEntity{})) type AppMenuEntityPolyglot struct { - LinkerId string `gorm:"not null;size:100;uniqueIndex:idx_polyglot" json:"linkerId,omitempty" yaml:"linkerId,omitempty" xml:"linkerId,omitempty"` - LanguageId string `gorm:"not null;size:100;uniqueIndex:idx_polyglot" json:"languageId,omitempty" xml:"languageId,omitempty" yaml:"languageId,omitempty"` - Label string `json:"label,omitempty" yaml:"label,omitempty"` + LinkerId string `gorm:"uniqueId;not null;size:100;" json:"linkerId,omitempty" yaml:"linkerId,omitempty" xml:"linkerId,omitempty"` + LanguageId string `gorm:"uniqueId;not null;size:100;" json:"languageId,omitempty" xml:"languageId,omitempty" yaml:"languageId,omitempty"` + Label string `yaml:"label,omitempty" xml:"label,omitempty" json:"label,omitempty"` } func entityAppMenuFormatter(dto *AppMenuEntity, query fireback.QueryDSL) { @@ -403,13 +400,11 @@ func AppMenuRecursiveAddUniqueId(dto *AppMenuEntity, query fireback.QueryDSL) { /* * - - Batch inserts, do not have all features that create - operation does. Use it with unnormalized content, - or read the source code carefully. - This is not marked as an action, because it should not be available publicly - at this moment. - + Batch inserts, do not have all features that create + operation does. Use it with unnormalized content, + or read the source code carefully. + This is not marked as an action, because it should not be available publicly + at this moment. * */ func AppMenuMultiInsertFn(dtos []*AppMenuEntity, query fireback.QueryDSL) ([]*AppMenuEntity, *fireback.IError) { @@ -459,19 +454,15 @@ func AppMenuActionCreateFn(dto *AppMenuEntity, query fireback.QueryDSL) (*AppMen // 2. Append the necessary information about user, workspace AppMenuEntityBeforeCreateAppend(dto, query) // 4. Create the entity - var dbref *gorm.DB - - if query.Tx != nil { - dbref = query.Tx - } else { + var dbref *gorm.DB = nil + if query.Tx == nil { dbref = fireback.GetDbRef() + } else { + dbref = query.Tx } - + query.Tx = dbref err := dbref.Transaction(func(tx *gorm.DB) error { - query.Tx = tx - - // create parent if err := tx. Omit("Translations"). Clauses(clause.OnConflict{ @@ -488,14 +479,11 @@ func AppMenuActionCreateFn(dto *AppMenuEntity, query fireback.QueryDSL) (*AppMen Create(&dto).Error; err != nil { return err } - // create translations if len(dto.Translations) > 0 { - for _, tr := range dto.Translations { tr.LinkerId = dto.UniqueId } - if err := tx. Clauses(clause.OnConflict{ Columns: []clause.Column{ @@ -510,12 +498,11 @@ func AppMenuActionCreateFn(dto *AppMenuEntity, query fireback.QueryDSL) (*AppMen return err } } - return nil }) - if err != nil { - return nil, fireback.GormErrorToIError(err) + err := fireback.GormErrorToIError(err) + return nil, err } // 5. Create sub entities, objects or arrays, association to other entities AppMenuAssociationCreate(dto, query) diff --git a/modules/abac/TimezoneGroupEntity.dyno.go b/modules/abac/TimezoneGroupEntity.dyno.go index 3c79efa3..9fdc12c2 100644 --- a/modules/abac/TimezoneGroupEntity.dyno.go +++ b/modules/abac/TimezoneGroupEntity.dyno.go @@ -420,7 +420,45 @@ func TimezoneGroupActionCreateFn(dto *TimezoneGroupEntity, query fireback.QueryD dbref = query.Tx } query.Tx = dbref - err := dbref.Create(&dto).Error + err := dbref.Transaction(func(tx *gorm.DB) error { + query.Tx = tx + if err := tx. + Omit("Translations"). + Clauses(clause.OnConflict{ + Columns: []clause.Column{ + {Name: "unique_id"}, + }, + DoUpdates: clause.AssignmentColumns([]string{ + "label", + "href", + "icon", + "active_matcher", + }), + }). + Create(&dto).Error; err != nil { + return err + } + // create translations + if len(dto.Translations) > 0 { + for _, tr := range dto.Translations { + tr.LinkerId = dto.UniqueId + } + if err := tx. + Clauses(clause.OnConflict{ + Columns: []clause.Column{ + {Name: "linker_id"}, + {Name: "language_id"}, + }, + DoUpdates: clause.AssignmentColumns([]string{ + "label", + }), + }). + Create(&dto.Translations).Error; err != nil { + return err + } + } + return nil + }) if err != nil { err := fireback.GormErrorToIError(err) return nil, err diff --git a/modules/abac/WorkspaceTypeEntity.dyno.go b/modules/abac/WorkspaceTypeEntity.dyno.go index cceeeb66..1bccc841 100644 --- a/modules/abac/WorkspaceTypeEntity.dyno.go +++ b/modules/abac/WorkspaceTypeEntity.dyno.go @@ -456,7 +456,45 @@ func WorkspaceTypeActionCreateFn(dto *WorkspaceTypeEntity, query fireback.QueryD dbref = query.Tx } query.Tx = dbref - err := dbref.Create(&dto).Error + err := dbref.Transaction(func(tx *gorm.DB) error { + query.Tx = tx + if err := tx. + Omit("Translations"). + Clauses(clause.OnConflict{ + Columns: []clause.Column{ + {Name: "unique_id"}, + }, + DoUpdates: clause.AssignmentColumns([]string{ + "label", + "href", + "icon", + "active_matcher", + }), + }). + Create(&dto).Error; err != nil { + return err + } + // create translations + if len(dto.Translations) > 0 { + for _, tr := range dto.Translations { + tr.LinkerId = dto.UniqueId + } + if err := tx. + Clauses(clause.OnConflict{ + Columns: []clause.Column{ + {Name: "linker_id"}, + {Name: "language_id"}, + }, + DoUpdates: clause.AssignmentColumns([]string{ + "label", + }), + }). + Create(&dto.Translations).Error; err != nil { + return err + } + } + return nil + }) if err != nil { err := fireback.GormErrorToIError(err) return nil, err diff --git a/modules/abac/queries/AppMenuCte.vsql b/modules/abac/queries/AppMenuCte.vsql index 4b87c48a..43bfa995 100644 --- a/modules/abac/queries/AppMenuCte.vsql +++ b/modules/abac/queries/AppMenuCte.vsql @@ -34,7 +34,7 @@ and app_menu_entity_polyglots.language_id = '(language)' {{ end }} {{ end }} -{{ if .IsMysql }} +{{ if or .IsMysql .IsPostgres }} {{ if .IsCounter }} select count(*) total_items diff --git a/modules/abac/queries/WorkspaceCte.vsql b/modules/abac/queries/WorkspaceCte.vsql index 243c74b8..2101b633 100644 --- a/modules/abac/queries/WorkspaceCte.vsql +++ b/modules/abac/queries/WorkspaceCte.vsql @@ -30,7 +30,7 @@ FROM workspace_entities_cte {{ end }} {{ end }} -{{ if .IsMysql }} +{{ if or .IsMysql .IsPostgres }} {{ if .IsCounter }} select count(*) total_items diff --git a/modules/fireback/CapabilityEntity.dyno.go b/modules/fireback/CapabilityEntity.dyno.go index 94601513..49bc7c85 100644 --- a/modules/fireback/CapabilityEntity.dyno.go +++ b/modules/fireback/CapabilityEntity.dyno.go @@ -426,7 +426,45 @@ func CapabilityActionCreateFn(dto *CapabilityEntity, query QueryDSL) (*Capabilit dbref = query.Tx } query.Tx = dbref - err := dbref.Create(&dto).Error + err := dbref.Transaction(func(tx *gorm.DB) error { + query.Tx = tx + if err := tx. + Omit("Translations"). + Clauses(clause.OnConflict{ + Columns: []clause.Column{ + {Name: "unique_id"}, + }, + DoUpdates: clause.AssignmentColumns([]string{ + "label", + "href", + "icon", + "active_matcher", + }), + }). + Create(&dto).Error; err != nil { + return err + } + // create translations + if len(dto.Translations) > 0 { + for _, tr := range dto.Translations { + tr.LinkerId = dto.UniqueId + } + if err := tx. + Clauses(clause.OnConflict{ + Columns: []clause.Column{ + {Name: "linker_id"}, + {Name: "language_id"}, + }, + DoUpdates: clause.AssignmentColumns([]string{ + "label", + }), + }). + Create(&dto.Translations).Error; err != nil { + return err + } + } + return nil + }) if err != nil { err := GormErrorToIError(err) return nil, err diff --git a/modules/fireback/CrudCoreActions.go b/modules/fireback/CrudCoreActions.go index 6c6873c2..65a78688 100644 --- a/modules/fireback/CrudCoreActions.go +++ b/modules/fireback/CrudCoreActions.go @@ -266,9 +266,10 @@ func UnsafeQuerySqlFromFs[T any](fsRef *embed.FS, queryName string, query QueryD } type VSqlContext struct { - IsMysql bool - IsSqlite bool - IsCounter bool + IsMysql bool + IsSqlite bool + IsPostgres bool + IsCounter bool } func ContextAwareVSqlOperation[T any](refl reflect.Value, fsRef *embed.FS, queryName string, query QueryDSL, values ...interface{}) ([]*T, *QueryResultMeta, *IError) { @@ -309,6 +310,10 @@ func ContextAwareVSqlOperation[T any](refl reflect.Value, fsRef *embed.FS, query ctx.IsSqlite = true } + if vendor == "postgres" { + ctx.IsPostgres = true + } + var output bytes.Buffer tmpl, err := template.New("example").Parse(content) if err != nil { @@ -338,6 +343,11 @@ func ContextAwareVSqlOperation[T any](refl reflect.Value, fsRef *embed.FS, query ctx.IsSqlite = true } + if vendor == "postgres" { + + ctx.IsPostgres = true + } + var output bytes.Buffer tmpl, err := template.New("example").Parse(content) if err != nil { diff --git a/modules/fireback/codegen/firebackgo/SharedSnippets.tpl b/modules/fireback/codegen/firebackgo/SharedSnippets.tpl index eaae6e93..513202d0 100644 --- a/modules/fireback/codegen/firebackgo/SharedSnippets.tpl +++ b/modules/fireback/codegen/firebackgo/SharedSnippets.tpl @@ -688,12 +688,62 @@ func {{ .e.Upper }}ActionCreateFn(dto *{{ .e.EntityName }}, query {{ .wsprefix } } query.Tx = dbref; - err := dbref.Create(&dto).Error - if err != nil { + + + {{ if .e.HasTranslations }} + err := dbref.Transaction(func(tx *gorm.DB) error { + query.Tx = tx + + if err := tx. + Omit("Translations"). + Clauses(clause.OnConflict{ + Columns: []clause.Column{ + {Name: "unique_id"}, + }, + DoUpdates: clause.AssignmentColumns([]string{ + "label", + "href", + "icon", + "active_matcher", + }), + }). + Create(&dto).Error; err != nil { + return err + } + + // create translations + if len(dto.Translations) > 0 { + + for _, tr := range dto.Translations { + tr.LinkerId = dto.UniqueId + } + + if err := tx. + Clauses(clause.OnConflict{ + Columns: []clause.Column{ + {Name: "linker_id"}, + {Name: "language_id"}, + }, + DoUpdates: clause.AssignmentColumns([]string{ + "label", + }), + }). + Create(&dto.Translations).Error; err != nil { + return err + } + } + + return nil + }) + + {{ else }} + err := dbref.Create(&dto).Error + {{ end }} + if err != nil { err := {{ .wsprefix }}GormErrorToIError(err) return nil, err } - + // 5. Create sub entities, objects or arrays, association to other entities {{ .e.Upper }}AssociationCreate(dto, query) diff --git a/modules/fireback/codegen/firebackgo/SqlCteQuery.tpl b/modules/fireback/codegen/firebackgo/SqlCteQuery.tpl index a9b82d8e..96fda6d2 100644 --- a/modules/fireback/codegen/firebackgo/SqlCteQuery.tpl +++ b/modules/fireback/codegen/firebackgo/SqlCteQuery.tpl @@ -40,9 +40,7 @@ {{"{{"}} end {{"}}"}} {{"{{"}} end {{"}}"}} - - -{{"{{"}} if .IsMysql {{"}}"}} +{{"{{"}} if or .IsMysql .IsPostgres {{"}}"}} {{"{{"}} if .IsCounter {{"}}"}} select count(*) total_items From 6ea7bbf0d4e414ecc91ff6b82f1ef4794acb890f Mon Sep 17 00:00:00 2001 From: Ali Date: Sat, 16 May 2026 23:21:40 +0200 Subject: [PATCH 06/15] Postgres --- .github/workflows/cypress-test.yml | 71 ++++++++++++++++++++-------- .github/workflows/fireback-build.yml | 7 +++ 2 files changed, 57 insertions(+), 21 deletions(-) diff --git a/.github/workflows/cypress-test.yml b/.github/workflows/cypress-test.yml index 9e95bec2..a375f055 100644 --- a/.github/workflows/cypress-test.yml +++ b/.github/workflows/cypress-test.yml @@ -10,6 +10,7 @@ on: jobs: cypress-run: runs-on: ubuntu-latest + services: mysql: image: mysql:8 @@ -18,15 +19,30 @@ jobs: MYSQL_DATABASE: fireback_test ports: - 3306:3306 - options: --health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -proot" --health-interval=10s --health-timeout=5s --health-retries=10 + options: >- + --health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -proot" + --health-interval=10s + --health-timeout=5s + --health-retries=10 + + postgres: + image: postgres:16 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: fireback_test + ports: + - 5432:5432 + options: >- + --health-cmd="pg_isready -U postgres -d fireback_test" + --health-interval=10s + --health-timeout=5s + --health-retries=10 steps: - name: Checkout code uses: actions/checkout@v4 - - name: Print pwd - run: pwd - - name: Set up Go uses: actions/setup-go@v4 with: @@ -38,7 +54,7 @@ jobs: node-version: 18 - name: Install dependencies - run: cd e2e && npm install + run: cd e2e && npm install - uses: actions/download-artifact@master with: @@ -48,27 +64,40 @@ jobs: - name: Install Fireback run: sudo dpkg -i artifacts-ubuntu/fireback-server-all/fireback-amd64.deb - # - name: Setup Database - # run: | - # if [[ "${{ inputs.DB_TYPE }}" == "mysql" ]]; then - # fireback config db-dsn set root:root@tcp(localhost:3306)/fireback_test?charset=utf8mb4&parseTime=True&loc=Local - # fireback config db-vendor set mysql - # else - # fireback config db-name /tmp/database.db && \ - # fireback config db-vendor set sqlite - # fi - # fireback migration apply + - name: Setup Database + run: | + if [[ "${{ inputs.DB_TYPE }}" == "mysql" ]]; then + fireback config db-dsn set "root:root@tcp(localhost:3306)/fireback_test?charset=utf8mb4&parseTime=True&loc=Local" + fireback config db-vendor set mysql - # - name: Add admin account - # run: fireback auth --in-root=true --value admin --workspace-type-id root --type email --password admin --first-name Ali --last-name Torabi - # - name: Check the passport - # run: fireback passport check-passport-methods + elif [[ "${{ inputs.DB_TYPE }}" == "postgres" ]]; then - + fireback config db-dsn set "host=localhost user=postgres password=postgres dbname=fireback_test port=5432 sslmode=disable TimeZone=UTC" + fireback config db-vendor set postgres + + else + + fireback config db-name /tmp/database.db + fireback config db-vendor set sqlite + + fi + + fireback migration apply + + - name: Add admin account + run: | + fireback auth \ + --in-root=true \ + --value admin \ + --workspace-type-id root \ + --type email \ + --password admin \ + --first-name Ali \ + --last-name Torabi - name: Run Cypress tests - run: cd e2e && DB_TYPE=${{ inputs.DB_TYPE }} npm test + run: cd e2e && DB_TYPE=${{ inputs.DB_TYPE }} npm test - name: Change the fireback replacement to github actions run: cd e2e/samples/fireback-data-types && sed -i 's|/Users/ali/work/fireback|/home/runner/work/fireback/fireback|' go.mod diff --git a/.github/workflows/fireback-build.yml b/.github/workflows/fireback-build.yml index 3a172bf8..c6ce1435 100644 --- a/.github/workflows/fireback-build.yml +++ b/.github/workflows/fireback-build.yml @@ -638,6 +638,13 @@ jobs: uses: ./.github/workflows/cypress-test.yml with: DB_TYPE: mysql + + run-cypress-postgres: + needs: + - build-ubuntu + uses: ./.github/workflows/cypress-test.yml + with: + DB_TYPE: postgres run-cypress-sqlite: needs: From 378d621996798cb91128e5736b3dcda2d176aa8f Mon Sep 17 00:00:00 2001 From: Ali Date: Sat, 16 May 2026 23:29:38 +0200 Subject: [PATCH 07/15] Update cypress-test.yml --- .github/workflows/cypress-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cypress-test.yml b/.github/workflows/cypress-test.yml index a375f055..182eefc2 100644 --- a/.github/workflows/cypress-test.yml +++ b/.github/workflows/cypress-test.yml @@ -78,7 +78,7 @@ jobs: else - fireback config db-name /tmp/database.db + fireback config db-name set /tmp/database.db fireback config db-vendor set sqlite fi From caca0ddd18982887fc34e154a6cefbd6392b16c3 Mon Sep 17 00:00:00 2001 From: Ali Date: Sat, 16 May 2026 23:38:35 +0200 Subject: [PATCH 08/15] Revert --- .github/workflows/cypress-test.yml | 58 +++--------------------------- 1 file changed, 5 insertions(+), 53 deletions(-) diff --git a/.github/workflows/cypress-test.yml b/.github/workflows/cypress-test.yml index 182eefc2..0fdbd8ec 100644 --- a/.github/workflows/cypress-test.yml +++ b/.github/workflows/cypress-test.yml @@ -10,7 +10,6 @@ on: jobs: cypress-run: runs-on: ubuntu-latest - services: mysql: image: mysql:8 @@ -19,30 +18,15 @@ jobs: MYSQL_DATABASE: fireback_test ports: - 3306:3306 - options: >- - --health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -proot" - --health-interval=10s - --health-timeout=5s - --health-retries=10 - - postgres: - image: postgres:16 - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: fireback_test - ports: - - 5432:5432 - options: >- - --health-cmd="pg_isready -U postgres -d fireback_test" - --health-interval=10s - --health-timeout=5s - --health-retries=10 + options: --health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -proot" --health-interval=10s --health-timeout=5s --health-retries=10 steps: - name: Checkout code uses: actions/checkout@v4 + - name: Print pwd + run: pwd + - name: Set up Go uses: actions/setup-go@v4 with: @@ -54,7 +38,7 @@ jobs: node-version: 18 - name: Install dependencies - run: cd e2e && npm install + run: cd e2e && npm install - uses: actions/download-artifact@master with: @@ -64,38 +48,6 @@ jobs: - name: Install Fireback run: sudo dpkg -i artifacts-ubuntu/fireback-server-all/fireback-amd64.deb - - name: Setup Database - run: | - if [[ "${{ inputs.DB_TYPE }}" == "mysql" ]]; then - - fireback config db-dsn set "root:root@tcp(localhost:3306)/fireback_test?charset=utf8mb4&parseTime=True&loc=Local" - fireback config db-vendor set mysql - - elif [[ "${{ inputs.DB_TYPE }}" == "postgres" ]]; then - - fireback config db-dsn set "host=localhost user=postgres password=postgres dbname=fireback_test port=5432 sslmode=disable TimeZone=UTC" - fireback config db-vendor set postgres - - else - - fireback config db-name set /tmp/database.db - fireback config db-vendor set sqlite - - fi - - fireback migration apply - - - name: Add admin account - run: | - fireback auth \ - --in-root=true \ - --value admin \ - --workspace-type-id root \ - --type email \ - --password admin \ - --first-name Ali \ - --last-name Torabi - - name: Run Cypress tests run: cd e2e && DB_TYPE=${{ inputs.DB_TYPE }} npm test From 50a87338f41adc7790e06cd14348c36fb168370f Mon Sep 17 00:00:00 2001 From: Ali Date: Sat, 16 May 2026 23:41:21 +0200 Subject: [PATCH 09/15] Update --- .github/workflows/cypress-test.yml | 14 ++++++++++++++ .../tests/cypress.config.js | 19 +++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cypress-test.yml b/.github/workflows/cypress-test.yml index 0fdbd8ec..0e38ced5 100644 --- a/.github/workflows/cypress-test.yml +++ b/.github/workflows/cypress-test.yml @@ -19,6 +19,20 @@ jobs: ports: - 3306:3306 options: --health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -proot" --health-interval=10s --health-timeout=5s --health-retries=10 + + postgres: + image: postgres:16 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: fireback_test + ports: + - 5432:5432 + options: >- + --health-cmd="pg_isready -U postgres -d fireback_test" + --health-interval=10s + --health-timeout=5s + --health-retries=10 steps: - name: Checkout code diff --git a/e2e/samples/fireback-data-types/tests/cypress.config.js b/e2e/samples/fireback-data-types/tests/cypress.config.js index 7ced31e4..509c6cf5 100644 --- a/e2e/samples/fireback-data-types/tests/cypress.config.js +++ b/e2e/samples/fireback-data-types/tests/cypress.config.js @@ -61,7 +61,7 @@ module.exports = defineConfig({ await execAsync(`${BINARY} config db-vendor set mysql`, CWD); await execAsync( `${BINARY} config db-dsn set "root:root@tcp(localhost:3306)/fireback_test?charset=utf8mb4&parseTime=True&loc=Local"`, - CWD + CWD, ); await execAsync(`${BINARY} migration apply`, CWD); @@ -69,11 +69,26 @@ module.exports = defineConfig({ } catch (err) { console.error("setup mysql failed:", err); } + } else if (vendor === "postgres") { + try { + await execAsync(`${BINARY} config db-vendor set postgres`, CWD); + + await execAsync( + `${BINARY} config db-dsn set "host=localhost user=postgres password=postgres dbname=fireback_test port=5432 sslmode=disable TimeZone=UTC"`, + CWD, + ); + + await execAsync(`${BINARY} migration apply`, CWD); + + return true; + } catch (err) { + console.error("setup postgres failed:", err); + } } else { await execAsync(`${BINARY} config db-vendor set sqlite`, CWD); await execAsync( `${BINARY} config db-name set /tmp/${dbname}.db`, - CWD + CWD, ); await execAsync(`${BINARY} migration apply`, CWD); From 987eb6e19416d161eb91888190af595c645602e4 Mon Sep 17 00:00:00 2001 From: Ali Date: Sat, 16 May 2026 23:49:10 +0200 Subject: [PATCH 10/15] Change --- e2e/cypress.config.js | 19 +++++++++++++++++-- .../fireback-payment/tests/cypress.config.js | 19 +++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/e2e/cypress.config.js b/e2e/cypress.config.js index 93a47292..fa9d3325 100644 --- a/e2e/cypress.config.js +++ b/e2e/cypress.config.js @@ -62,7 +62,7 @@ module.exports = defineConfig({ await execAsync(`${BINARY} config db-vendor set mysql`, CWD); await execAsync( `${BINARY} config db-dsn set "root:root@tcp(localhost:3306)/fireback_test?charset=utf8mb4&parseTime=True&loc=Local"`, - CWD + CWD, ); await execAsync(`${BINARY} migration apply`, CWD); @@ -70,11 +70,26 @@ module.exports = defineConfig({ } catch (err) { console.error("setup mysql failed:", err); } + } else if (vendor === "postgres") { + try { + await execAsync(`${BINARY} config db-vendor set postgres`, CWD); + + await execAsync( + `${BINARY} config db-dsn set "host=localhost user=postgres password=postgres dbname=fireback_test port=5432 sslmode=disable TimeZone=UTC"`, + CWD, + ); + + await execAsync(`${BINARY} migration apply`, CWD); + + return true; + } catch (err) { + console.error("setup postgres failed:", err); + } } else { await execAsync(`${BINARY} config db-vendor set sqlite`, CWD); await execAsync( `${BINARY} config db-name set /tmp/${dbname}.db`, - CWD + CWD, ); await execAsync(`${BINARY} migration apply`, CWD); diff --git a/e2e/samples/fireback-payment/tests/cypress.config.js b/e2e/samples/fireback-payment/tests/cypress.config.js index 6266102d..beaab243 100644 --- a/e2e/samples/fireback-payment/tests/cypress.config.js +++ b/e2e/samples/fireback-payment/tests/cypress.config.js @@ -61,7 +61,7 @@ module.exports = defineConfig({ await execAsync(`${BINARY} config db-vendor set mysql`, CWD); await execAsync( `${BINARY} config db-dsn set "root:root@tcp(localhost:3306)/fireback_test?charset=utf8mb4&parseTime=True&loc=Local"`, - CWD + CWD, ); await execAsync(`${BINARY} migration apply`, CWD); @@ -69,11 +69,26 @@ module.exports = defineConfig({ } catch (err) { console.error("setup mysql failed:", err); } + } else if (vendor === "postgres") { + try { + await execAsync(`${BINARY} config db-vendor set postgres`, CWD); + + await execAsync( + `${BINARY} config db-dsn set "host=localhost user=postgres password=postgres dbname=fireback_test port=5432 sslmode=disable TimeZone=UTC"`, + CWD, + ); + + await execAsync(`${BINARY} migration apply`, CWD); + + return true; + } catch (err) { + console.error("setup postgres failed:", err); + } } else { await execAsync(`${BINARY} config db-vendor set sqlite`, CWD); await execAsync( `${BINARY} config db-name set /tmp/${dbname}.db`, - CWD + CWD, ); await execAsync(`${BINARY} migration apply`, CWD); From ba46e3c9f23f3771470331344fbb3693504272a6 Mon Sep 17 00:00:00 2001 From: Ali Date: Sun, 17 May 2026 00:01:14 +0200 Subject: [PATCH 11/15] Fix the generated code. --- modules/abac/AppMenuEntity.dyno.go | 11 ----------- modules/abac/TimezoneGroupEntity.dyno.go | 13 +------------ modules/abac/WorkspaceTypeEntity.dyno.go | 14 ++------------ modules/fireback/CapabilityEntity.dyno.go | 13 +------------ .../codegen/firebackgo/SharedSnippets.tpl | 17 +++++------------ 5 files changed, 9 insertions(+), 59 deletions(-) diff --git a/modules/abac/AppMenuEntity.dyno.go b/modules/abac/AppMenuEntity.dyno.go index a6eb59d7..d91dedba 100644 --- a/modules/abac/AppMenuEntity.dyno.go +++ b/modules/abac/AppMenuEntity.dyno.go @@ -465,17 +465,6 @@ func AppMenuActionCreateFn(dto *AppMenuEntity, query fireback.QueryDSL) (*AppMen query.Tx = tx if err := tx. Omit("Translations"). - Clauses(clause.OnConflict{ - Columns: []clause.Column{ - {Name: "unique_id"}, - }, - DoUpdates: clause.AssignmentColumns([]string{ - "label", - "href", - "icon", - "active_matcher", - }), - }). Create(&dto).Error; err != nil { return err } diff --git a/modules/abac/TimezoneGroupEntity.dyno.go b/modules/abac/TimezoneGroupEntity.dyno.go index 9fdc12c2..cc344a97 100644 --- a/modules/abac/TimezoneGroupEntity.dyno.go +++ b/modules/abac/TimezoneGroupEntity.dyno.go @@ -424,17 +424,6 @@ func TimezoneGroupActionCreateFn(dto *TimezoneGroupEntity, query fireback.QueryD query.Tx = tx if err := tx. Omit("Translations"). - Clauses(clause.OnConflict{ - Columns: []clause.Column{ - {Name: "unique_id"}, - }, - DoUpdates: clause.AssignmentColumns([]string{ - "label", - "href", - "icon", - "active_matcher", - }), - }). Create(&dto).Error; err != nil { return err } @@ -450,7 +439,7 @@ func TimezoneGroupActionCreateFn(dto *TimezoneGroupEntity, query fireback.QueryD {Name: "language_id"}, }, DoUpdates: clause.AssignmentColumns([]string{ - "label", + "title", }), }). Create(&dto.Translations).Error; err != nil { diff --git a/modules/abac/WorkspaceTypeEntity.dyno.go b/modules/abac/WorkspaceTypeEntity.dyno.go index 1bccc841..0864960b 100644 --- a/modules/abac/WorkspaceTypeEntity.dyno.go +++ b/modules/abac/WorkspaceTypeEntity.dyno.go @@ -460,17 +460,6 @@ func WorkspaceTypeActionCreateFn(dto *WorkspaceTypeEntity, query fireback.QueryD query.Tx = tx if err := tx. Omit("Translations"). - Clauses(clause.OnConflict{ - Columns: []clause.Column{ - {Name: "unique_id"}, - }, - DoUpdates: clause.AssignmentColumns([]string{ - "label", - "href", - "icon", - "active_matcher", - }), - }). Create(&dto).Error; err != nil { return err } @@ -486,7 +475,8 @@ func WorkspaceTypeActionCreateFn(dto *WorkspaceTypeEntity, query fireback.QueryD {Name: "language_id"}, }, DoUpdates: clause.AssignmentColumns([]string{ - "label", + "title", + "description", }), }). Create(&dto.Translations).Error; err != nil { diff --git a/modules/fireback/CapabilityEntity.dyno.go b/modules/fireback/CapabilityEntity.dyno.go index 49bc7c85..7b4b805f 100644 --- a/modules/fireback/CapabilityEntity.dyno.go +++ b/modules/fireback/CapabilityEntity.dyno.go @@ -430,17 +430,6 @@ func CapabilityActionCreateFn(dto *CapabilityEntity, query QueryDSL) (*Capabilit query.Tx = tx if err := tx. Omit("Translations"). - Clauses(clause.OnConflict{ - Columns: []clause.Column{ - {Name: "unique_id"}, - }, - DoUpdates: clause.AssignmentColumns([]string{ - "label", - "href", - "icon", - "active_matcher", - }), - }). Create(&dto).Error; err != nil { return err } @@ -456,7 +445,7 @@ func CapabilityActionCreateFn(dto *CapabilityEntity, query QueryDSL) (*Capabilit {Name: "language_id"}, }, DoUpdates: clause.AssignmentColumns([]string{ - "label", + "description", }), }). Create(&dto.Translations).Error; err != nil { diff --git a/modules/fireback/codegen/firebackgo/SharedSnippets.tpl b/modules/fireback/codegen/firebackgo/SharedSnippets.tpl index 513202d0..b94c0436 100644 --- a/modules/fireback/codegen/firebackgo/SharedSnippets.tpl +++ b/modules/fireback/codegen/firebackgo/SharedSnippets.tpl @@ -696,17 +696,6 @@ func {{ .e.Upper }}ActionCreateFn(dto *{{ .e.EntityName }}, query {{ .wsprefix } if err := tx. Omit("Translations"). - Clauses(clause.OnConflict{ - Columns: []clause.Column{ - {Name: "unique_id"}, - }, - DoUpdates: clause.AssignmentColumns([]string{ - "label", - "href", - "icon", - "active_matcher", - }), - }). Create(&dto).Error; err != nil { return err } @@ -725,7 +714,11 @@ func {{ .e.Upper }}ActionCreateFn(dto *{{ .e.EntityName }}, query {{ .wsprefix } {Name: "language_id"}, }, DoUpdates: clause.AssignmentColumns([]string{ - "label", + {{ range .e.CompleteFields }} + {{ if .Translate }} + "{{.Name}}", + {{ end }} + {{ end }} }), }). Create(&dto.Translations).Error; err != nil { From 0dea04deeb195d1c25e92bcaf91d67502ed1f24a Mon Sep 17 00:00:00 2001 From: Ali Date: Sun, 17 May 2026 00:12:59 +0200 Subject: [PATCH 12/15] Fix the dialect --- modules/abac/AppMenuEntity.dyno.go | 8 +++++--- modules/abac/TimezoneGroupEntity.dyno.go | 8 +++++--- modules/abac/WorkspaceTypeEntity.dyno.go | 8 +++++--- modules/fireback/CapabilityEntity.dyno.go | 8 +++++--- modules/fireback/codegen/firebackgo/SharedSnippets.tpl | 9 ++++++--- 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/modules/abac/AppMenuEntity.dyno.go b/modules/abac/AppMenuEntity.dyno.go index d91dedba..407f3f21 100644 --- a/modules/abac/AppMenuEntity.dyno.go +++ b/modules/abac/AppMenuEntity.dyno.go @@ -473,8 +473,8 @@ func AppMenuActionCreateFn(dto *AppMenuEntity, query fireback.QueryDSL) (*AppMen for _, tr := range dto.Translations { tr.LinkerId = dto.UniqueId } - if err := tx. - Clauses(clause.OnConflict{ + if tx.Dialector.Name() == "postgres" { + tx = tx.Clauses(clause.OnConflict{ Columns: []clause.Column{ {Name: "linker_id"}, {Name: "language_id"}, @@ -482,7 +482,9 @@ func AppMenuActionCreateFn(dto *AppMenuEntity, query fireback.QueryDSL) (*AppMen DoUpdates: clause.AssignmentColumns([]string{ "label", }), - }). + }) + } + if err := tx. Create(&dto.Translations).Error; err != nil { return err } diff --git a/modules/abac/TimezoneGroupEntity.dyno.go b/modules/abac/TimezoneGroupEntity.dyno.go index cc344a97..a058a962 100644 --- a/modules/abac/TimezoneGroupEntity.dyno.go +++ b/modules/abac/TimezoneGroupEntity.dyno.go @@ -432,8 +432,8 @@ func TimezoneGroupActionCreateFn(dto *TimezoneGroupEntity, query fireback.QueryD for _, tr := range dto.Translations { tr.LinkerId = dto.UniqueId } - if err := tx. - Clauses(clause.OnConflict{ + if tx.Dialector.Name() == "postgres" { + tx = tx.Clauses(clause.OnConflict{ Columns: []clause.Column{ {Name: "linker_id"}, {Name: "language_id"}, @@ -441,7 +441,9 @@ func TimezoneGroupActionCreateFn(dto *TimezoneGroupEntity, query fireback.QueryD DoUpdates: clause.AssignmentColumns([]string{ "title", }), - }). + }) + } + if err := tx. Create(&dto.Translations).Error; err != nil { return err } diff --git a/modules/abac/WorkspaceTypeEntity.dyno.go b/modules/abac/WorkspaceTypeEntity.dyno.go index 0864960b..2da39d84 100644 --- a/modules/abac/WorkspaceTypeEntity.dyno.go +++ b/modules/abac/WorkspaceTypeEntity.dyno.go @@ -468,8 +468,8 @@ func WorkspaceTypeActionCreateFn(dto *WorkspaceTypeEntity, query fireback.QueryD for _, tr := range dto.Translations { tr.LinkerId = dto.UniqueId } - if err := tx. - Clauses(clause.OnConflict{ + if tx.Dialector.Name() == "postgres" { + tx = tx.Clauses(clause.OnConflict{ Columns: []clause.Column{ {Name: "linker_id"}, {Name: "language_id"}, @@ -478,7 +478,9 @@ func WorkspaceTypeActionCreateFn(dto *WorkspaceTypeEntity, query fireback.QueryD "title", "description", }), - }). + }) + } + if err := tx. Create(&dto.Translations).Error; err != nil { return err } diff --git a/modules/fireback/CapabilityEntity.dyno.go b/modules/fireback/CapabilityEntity.dyno.go index 7b4b805f..c9d70b25 100644 --- a/modules/fireback/CapabilityEntity.dyno.go +++ b/modules/fireback/CapabilityEntity.dyno.go @@ -438,8 +438,8 @@ func CapabilityActionCreateFn(dto *CapabilityEntity, query QueryDSL) (*Capabilit for _, tr := range dto.Translations { tr.LinkerId = dto.UniqueId } - if err := tx. - Clauses(clause.OnConflict{ + if tx.Dialector.Name() == "postgres" { + tx = tx.Clauses(clause.OnConflict{ Columns: []clause.Column{ {Name: "linker_id"}, {Name: "language_id"}, @@ -447,7 +447,9 @@ func CapabilityActionCreateFn(dto *CapabilityEntity, query QueryDSL) (*Capabilit DoUpdates: clause.AssignmentColumns([]string{ "description", }), - }). + }) + } + if err := tx. Create(&dto.Translations).Error; err != nil { return err } diff --git a/modules/fireback/codegen/firebackgo/SharedSnippets.tpl b/modules/fireback/codegen/firebackgo/SharedSnippets.tpl index b94c0436..4947dd88 100644 --- a/modules/fireback/codegen/firebackgo/SharedSnippets.tpl +++ b/modules/fireback/codegen/firebackgo/SharedSnippets.tpl @@ -707,8 +707,8 @@ func {{ .e.Upper }}ActionCreateFn(dto *{{ .e.EntityName }}, query {{ .wsprefix } tr.LinkerId = dto.UniqueId } - if err := tx. - Clauses(clause.OnConflict{ + if tx.Dialector.Name() == "postgres" { + tx = tx.Clauses(clause.OnConflict{ Columns: []clause.Column{ {Name: "linker_id"}, {Name: "language_id"}, @@ -720,7 +720,10 @@ func {{ .e.Upper }}ActionCreateFn(dto *{{ .e.EntityName }}, query {{ .wsprefix } {{ end }} {{ end }} }), - }). + }) + } + + if err := tx. Create(&dto.Translations).Error; err != nil { return err } From 63945bb1624c13b0cc9a689fd825c0490d582686 Mon Sep 17 00:00:00 2001 From: Ali Date: Sun, 17 May 2026 01:00:07 +0200 Subject: [PATCH 13/15] Update login.cy.js --- e2e/cypress/e2e/login.cy.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/e2e/cypress/e2e/login.cy.js b/e2e/cypress/e2e/login.cy.js index f50da8eb..dfc8945f 100644 --- a/e2e/cypress/e2e/login.cy.js +++ b/e2e/cypress/e2e/login.cy.js @@ -19,7 +19,7 @@ describe("Logging in with the signin", () => { it("get the data of the public", () => { cy.request( "GET", - "http://localhost:7793/passports/available-methods" + "http://localhost:7793/passports/available-methods", ).then((response) => { cy.task("log", response.body); expect(response.body.data.item.email).to.equal(false); @@ -38,7 +38,7 @@ describe("Logging in with the signin", () => { it("get the data of the public", () => { cy.request( "GET", - "http://localhost:7793/passports/available-methods" + "http://localhost:7793/passports/available-methods", ).then((response) => { cy.task("log", response.body); expect(response.body.data.item.email).to.equal(true); @@ -63,7 +63,7 @@ describe("Logging in with the signin", () => { it("should be able to create a role in order to assign it into the workspace type.", () => { cy.task( "exec", - ` role c --name testagentrole --capabilities "root.*"` + ` role c --name testagentrole --capabilities "root.*"`, ).then((res) => { console.log((roleId = JSON.parse(res).uniqueId)); }); @@ -72,7 +72,7 @@ describe("Logging in with the signin", () => { it("should be able to create a workspace name", () => { cy.task( "exec", - ` ws type c --title customer --slug customer --role-id ${roleId}` + ` ws type c --title customer --slug customer --role-id ${roleId}`, ); }); @@ -127,6 +127,7 @@ describe("Logging in with the signin", () => { it("should be able to generate back the menu items from seeder.", () => { cy.task("exec", `misc appmenu ssync`).then((content) => { + console.log(100, content); let countFilesImported = 0; for (const line of content.split("\n")) { if (line.startsWith("Success")) { @@ -184,7 +185,7 @@ describe("Logging in with the signin", () => { cy.task( "exec", - `misc appmenu q --query "unique_id = ${item.uniqueId}"` + `misc appmenu q --query "unique_id = ${item.uniqueId}"`, ).then((content) => { const res = JSON.parse(content); console.log(res); From 4446578c9f977a2548a846a28843c3fe135e16ab Mon Sep 17 00:00:00 2001 From: anonymouse Date: Sun, 17 May 2026 13:07:17 +0200 Subject: [PATCH 14/15] Fix the postgres --- e2e/cypress.config.js | 8 +++++++- e2e/cypress/e2e/login.cy.js | 5 +++-- e2e/samples/fireback-data-types/go.sum | 2 +- e2e/samples/fireback-data-types/tests/cypress.config.js | 2 +- e2e/samples/fireback-payment/go.sum | 2 +- e2e/samples/fireback-payment/tests/cypress.config.js | 2 +- go.sum | 2 +- modules/abac/AppMenuEntity.dyno.go | 4 ++-- modules/abac/TimezoneGroupEntity.dyno.go | 4 ++-- modules/abac/WorkspaceTypeEntity.dyno.go | 4 ++-- modules/fireback/CapabilityEntity.dyno.go | 4 ++-- modules/fireback/codegen/firebackgo/SharedSnippets.tpl | 6 ++++-- 12 files changed, 27 insertions(+), 18 deletions(-) diff --git a/e2e/cypress.config.js b/e2e/cypress.config.js index fa9d3325..62d204e8 100644 --- a/e2e/cypress.config.js +++ b/e2e/cypress.config.js @@ -5,7 +5,7 @@ let firebackProcess; // Store the Fireback process reference let BINARY = "/home/ali/work/fireback/app"; let CWD = "/home/ali/work/fireback"; -const PORT = 7793; +const PORT = 7794; let DB_VENDOR = "sqlite"; const isGitHubActions = !!process.env.GITHUB_ACTIONS; @@ -72,8 +72,14 @@ module.exports = defineConfig({ } } else if (vendor === "postgres") { try { + console.log("Using postgres"); await execAsync(`${BINARY} config db-vendor set postgres`, CWD); + const dbName = "fireback_test"; + + // Drop and recreate database + console.log( + await execAsync( await execAsync( `${BINARY} config db-dsn set "host=localhost user=postgres password=postgres dbname=fireback_test port=5432 sslmode=disable TimeZone=UTC"`, CWD, diff --git a/e2e/cypress/e2e/login.cy.js b/e2e/cypress/e2e/login.cy.js index dfc8945f..e5b5e6a0 100644 --- a/e2e/cypress/e2e/login.cy.js +++ b/e2e/cypress/e2e/login.cy.js @@ -19,7 +19,7 @@ describe("Logging in with the signin", () => { it("get the data of the public", () => { cy.request( "GET", - "http://localhost:7793/passports/available-methods", + "http://localhost:7794/passports/available-methods", ).then((response) => { cy.task("log", response.body); expect(response.body.data.item.email).to.equal(false); @@ -38,7 +38,7 @@ describe("Logging in with the signin", () => { it("get the data of the public", () => { cy.request( "GET", - "http://localhost:7793/passports/available-methods", + "http://localhost:7794/passports/available-methods", ).then((response) => { cy.task("log", response.body); expect(response.body.data.item.email).to.equal(true); @@ -65,6 +65,7 @@ describe("Logging in with the signin", () => { "exec", ` role c --name testagentrole --capabilities "root.*"`, ).then((res) => { + console.log("Role created:", res) console.log((roleId = JSON.parse(res).uniqueId)); }); }); diff --git a/e2e/samples/fireback-data-types/go.sum b/e2e/samples/fireback-data-types/go.sum index 53b08510..c8a9d324 100644 --- a/e2e/samples/fireback-data-types/go.sum +++ b/e2e/samples/fireback-data-types/go.sum @@ -111,7 +111,7 @@ cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2b cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987794e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= github.com/474420502/gcurl v1.2.1 h1:Z+bAiOWdW7hbvTuBKxzhNuCevvvLlHDvk3hoSwHD2RA= diff --git a/e2e/samples/fireback-data-types/tests/cypress.config.js b/e2e/samples/fireback-data-types/tests/cypress.config.js index 509c6cf5..848fb7f4 100644 --- a/e2e/samples/fireback-data-types/tests/cypress.config.js +++ b/e2e/samples/fireback-data-types/tests/cypress.config.js @@ -5,7 +5,7 @@ let firebackProcess; // Store the Fireback process reference let BINARY = "/Users/ali/work/fireback/e2e/samples/fireback-data-types/app"; let CWD = "/Users/ali/work/fireback/e2e/samples/fireback-data-types"; -const PORT = 7793; +const PORT = 7794; let DB_VENDOR = "sqlite"; const isGitHubActions = !!process.env.GITHUB_ACTIONS; diff --git a/e2e/samples/fireback-payment/go.sum b/e2e/samples/fireback-payment/go.sum index b7f20e42..e76b7788 100644 --- a/e2e/samples/fireback-payment/go.sum +++ b/e2e/samples/fireback-payment/go.sum @@ -111,7 +111,7 @@ cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2b cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987794e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= diff --git a/e2e/samples/fireback-payment/tests/cypress.config.js b/e2e/samples/fireback-payment/tests/cypress.config.js index beaab243..5259bcf4 100644 --- a/e2e/samples/fireback-payment/tests/cypress.config.js +++ b/e2e/samples/fireback-payment/tests/cypress.config.js @@ -5,7 +5,7 @@ let firebackProcess; // Store the Fireback process reference let BINARY = "/Users/ali/work/fireback/e2e/samples/fireback-payment/app"; let CWD = "/Users/ali/work/fireback/e2e/samples/fireback-payment"; -const PORT = 7793; +const PORT = 7794; let DB_VENDOR = "sqlite"; const isGitHubActions = !!process.env.GITHUB_ACTIONS; diff --git a/go.sum b/go.sum index fef937a4..1b64297e 100644 --- a/go.sum +++ b/go.sum @@ -111,7 +111,7 @@ cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2b cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987794e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= github.com/474420502/gcurl v1.2.1 h1:Z+bAiOWdW7hbvTuBKxzhNuCevvvLlHDvk3hoSwHD2RA= diff --git a/modules/abac/AppMenuEntity.dyno.go b/modules/abac/AppMenuEntity.dyno.go index 407f3f21..82a3adf2 100644 --- a/modules/abac/AppMenuEntity.dyno.go +++ b/modules/abac/AppMenuEntity.dyno.go @@ -250,8 +250,8 @@ var AppMenuEntityMetaConfig map[string]int64 = map[string]int64{} var AppMenuEntityJsonSchema = fireback.ExtractEntityFields(reflect.ValueOf(&AppMenuEntity{})) type AppMenuEntityPolyglot struct { - LinkerId string `gorm:"uniqueId;not null;size:100;" json:"linkerId,omitempty" yaml:"linkerId,omitempty" xml:"linkerId,omitempty"` - LanguageId string `gorm:"uniqueId;not null;size:100;" json:"languageId,omitempty" xml:"languageId,omitempty" yaml:"languageId,omitempty"` + LinkerId string `gorm:"not null;index:idx_linker_language_appMenu_AppMenuEntityPolyglot,unique" json:"linkerId,omitempty" yaml:"linkerId,omitempty" xml:"linkerId,omitempty"` + LanguageId string `gorm:"not null;index:idx_linker_language_appMenu_AppMenuEntityPolyglot,unique" json:"languageId,omitempty" xml:"languageId,omitempty" yaml:"languageId,omitempty"` Label string `yaml:"label,omitempty" xml:"label,omitempty" json:"label,omitempty"` } diff --git a/modules/abac/TimezoneGroupEntity.dyno.go b/modules/abac/TimezoneGroupEntity.dyno.go index a058a962..782adc46 100644 --- a/modules/abac/TimezoneGroupEntity.dyno.go +++ b/modules/abac/TimezoneGroupEntity.dyno.go @@ -213,8 +213,8 @@ var TimezoneGroupEntityMetaConfig map[string]int64 = map[string]int64{} var TimezoneGroupEntityJsonSchema = fireback.ExtractEntityFields(reflect.ValueOf(&TimezoneGroupEntity{})) type TimezoneGroupEntityPolyglot struct { - LinkerId string `gorm:"uniqueId;not null;size:100;" json:"linkerId,omitempty" yaml:"linkerId,omitempty" xml:"linkerId,omitempty"` - LanguageId string `gorm:"uniqueId;not null;size:100;" json:"languageId,omitempty" xml:"languageId,omitempty" yaml:"languageId,omitempty"` + LinkerId string `gorm:"not null;index:idx_linker_language_timezoneGroup_TimezoneGroupEntityPolyglot,unique" json:"linkerId,omitempty" yaml:"linkerId,omitempty" xml:"linkerId,omitempty"` + LanguageId string `gorm:"not null;index:idx_linker_language_timezoneGroup_TimezoneGroupEntityPolyglot,unique" json:"languageId,omitempty" xml:"languageId,omitempty" yaml:"languageId,omitempty"` Title string `yaml:"title,omitempty" xml:"title,omitempty" json:"title,omitempty"` } diff --git a/modules/abac/WorkspaceTypeEntity.dyno.go b/modules/abac/WorkspaceTypeEntity.dyno.go index 2da39d84..f1a3a5e3 100644 --- a/modules/abac/WorkspaceTypeEntity.dyno.go +++ b/modules/abac/WorkspaceTypeEntity.dyno.go @@ -235,8 +235,8 @@ var WorkspaceTypeEntityMetaConfig map[string]int64 = map[string]int64{} var WorkspaceTypeEntityJsonSchema = fireback.ExtractEntityFields(reflect.ValueOf(&WorkspaceTypeEntity{})) type WorkspaceTypeEntityPolyglot struct { - LinkerId string `gorm:"uniqueId;not null;size:100;" json:"linkerId,omitempty" yaml:"linkerId,omitempty" xml:"linkerId,omitempty"` - LanguageId string `gorm:"uniqueId;not null;size:100;" json:"languageId,omitempty" xml:"languageId,omitempty" yaml:"languageId,omitempty"` + LinkerId string `gorm:"not null;index:idx_linker_language_workspaceType_WorkspaceTypeEntityPolyglot,unique" json:"linkerId,omitempty" yaml:"linkerId,omitempty" xml:"linkerId,omitempty"` + LanguageId string `gorm:"not null;index:idx_linker_language_workspaceType_WorkspaceTypeEntityPolyglot,unique" json:"languageId,omitempty" xml:"languageId,omitempty" yaml:"languageId,omitempty"` Title string `yaml:"title,omitempty" xml:"title,omitempty" json:"title,omitempty"` Description string `yaml:"description,omitempty" xml:"description,omitempty" json:"description,omitempty"` } diff --git a/modules/fireback/CapabilityEntity.dyno.go b/modules/fireback/CapabilityEntity.dyno.go index c9d70b25..3a403c18 100644 --- a/modules/fireback/CapabilityEntity.dyno.go +++ b/modules/fireback/CapabilityEntity.dyno.go @@ -218,8 +218,8 @@ var CapabilityEntityMetaConfig map[string]int64 = map[string]int64{} var CapabilityEntityJsonSchema = ExtractEntityFields(reflect.ValueOf(&CapabilityEntity{})) type CapabilityEntityPolyglot struct { - LinkerId string `gorm:"uniqueId;not null;size:100;" json:"linkerId,omitempty" yaml:"linkerId,omitempty" xml:"linkerId,omitempty"` - LanguageId string `gorm:"uniqueId;not null;size:100;" json:"languageId,omitempty" xml:"languageId,omitempty" yaml:"languageId,omitempty"` + LinkerId string `gorm:"not null;index:idx_linker_language_capability_CapabilityEntityPolyglot,unique" json:"linkerId,omitempty" yaml:"linkerId,omitempty" xml:"linkerId,omitempty"` + LanguageId string `gorm:"not null;index:idx_linker_language_capability_CapabilityEntityPolyglot,unique" json:"languageId,omitempty" xml:"languageId,omitempty" yaml:"languageId,omitempty"` Description string `yaml:"description,omitempty" xml:"description,omitempty" json:"description,omitempty"` } diff --git a/modules/fireback/codegen/firebackgo/SharedSnippets.tpl b/modules/fireback/codegen/firebackgo/SharedSnippets.tpl index 4947dd88..6657aa5f 100644 --- a/modules/fireback/codegen/firebackgo/SharedSnippets.tpl +++ b/modules/fireback/codegen/firebackgo/SharedSnippets.tpl @@ -183,8 +183,10 @@ import "{{ $key}}" {{ if .e.HasTranslations }} type {{ .e.PolyglotName}} struct { - LinkerId string `gorm:"uniqueId;not null;size:100;" json:"linkerId,omitempty" yaml:"linkerId,omitempty" xml:"linkerId,omitempty"` - LanguageId string `gorm:"uniqueId;not null;size:100;" json:"languageId,omitempty" xml:"languageId,omitempty" yaml:"languageId,omitempty"` + LinkerId string `gorm:"not null;index:idx_linker_language_{{.e.Name}}_{{.e.PolyglotName}},unique" json:"linkerId,omitempty" yaml:"linkerId,omitempty" xml:"linkerId,omitempty"` + LanguageId string `gorm:"not null;index:idx_linker_language_{{.e.Name}}_{{.e.PolyglotName}},unique" json:"languageId,omitempty" xml:"languageId,omitempty" yaml:"languageId,omitempty"` + + {{ range .e.CompleteFields }} {{ if .Translate }} From 0698c3de72d6b2ffa5a413396368d7673d90c717 Mon Sep 17 00:00:00 2001 From: anonymouse Date: Sun, 17 May 2026 13:07:23 +0200 Subject: [PATCH 15/15] Update cypress.config.js --- e2e/cypress.config.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/e2e/cypress.config.js b/e2e/cypress.config.js index 62d204e8..d723a403 100644 --- a/e2e/cypress.config.js +++ b/e2e/cypress.config.js @@ -80,8 +80,18 @@ module.exports = defineConfig({ // Drop and recreate database console.log( await execAsync( + `PGPASSWORD=postgres psql -U postgres -h localhost -p 5432 -d postgres -c "DROP DATABASE IF EXISTS ${dbName};"`, + CWD, + ), + ); + + await execAsync( + `PGPASSWORD=postgres psql -U postgres -h localhost -p 5432 -d postgres -c "CREATE DATABASE ${dbName};"`, + CWD, + ); + await execAsync( - `${BINARY} config db-dsn set "host=localhost user=postgres password=postgres dbname=fireback_test port=5432 sslmode=disable TimeZone=UTC"`, + `${BINARY} config db-dsn set "host=localhost user=postgres password=postgres dbname=${dbName} port=5432 sslmode=disable TimeZone=UTC"`, CWD, );