From c196149fa1a1322a98c65c53fd5d1fe3c5acd917 Mon Sep 17 00:00:00 2001 From: Ronni Skansing Date: Thu, 29 Jan 2026 22:19:25 +0100 Subject: [PATCH] Allow customs params to DNS\nMinor database index optimization Signed-off-by: Ronni Skansing --- backend/database/campaignEvent.go | 25 ++++++++++++++++++++++--- backend/database/factory.go | 10 +++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/backend/database/campaignEvent.go b/backend/database/campaignEvent.go index 7de023b..8ac4baa 100644 --- a/backend/database/campaignEvent.go +++ b/backend/database/campaignEvent.go @@ -5,6 +5,7 @@ import ( "time" "github.com/google/uuid" + "gorm.io/gorm" ) const ( @@ -24,8 +25,8 @@ type CampaignEvent struct { Metadata string `gorm:"not null;default:''"` // has one - CampaignID *uuid.UUID `gorm:"not null;index;type:uuid;"` - EventID *uuid.UUID `gorm:"not null;index;type:uuid;"` + CampaignID *uuid.UUID `gorm:"not null;type:uuid;"` + EventID *uuid.UUID `gorm:"not null;type:uuid;"` // can has one UserAgent string `gorm:";"` @@ -37,7 +38,25 @@ type CampaignEvent struct { RecipientID *uuid.UUID `gorm:"index;type:uuid;"` Recipient *Recipient - CompanyID *uuid.UUID `gorm:"index;type:uuid;index;"` + CompanyID *uuid.UUID `gorm:"type:uuid;"` +} + +// Migrate creates composite index and removes redundant single-column indexes +func (CampaignEvent) Migrate(db *gorm.DB) error { + // create composite index for campaign_id + event_id (used heavily in GetResultStats) + if err := db.Exec(`CREATE INDEX IF NOT EXISTS idx_campaign_events_campaign_event ON campaign_events(campaign_id, event_id)`).Error; err != nil { + return err + } + + // remove redundant single-column indexes that are covered by the composite index + // ignore errors as indexes may not exist on fresh installs + db.Exec(`DROP INDEX IF EXISTS idx_campaign_events_campaign_id`) + db.Exec(`DROP INDEX IF EXISTS idx_campaign_events_event_id`) + + // remove unused company_id index (column is never populated) + db.Exec(`DROP INDEX IF EXISTS idx_campaign_events_company_id`) + + return nil } // RecipientCampaignEvent is a aggregated read-only model diff --git a/backend/database/factory.go b/backend/database/factory.go index d97365e..3c574d2 100644 --- a/backend/database/factory.go +++ b/backend/database/factory.go @@ -2,6 +2,7 @@ package database import ( "fmt" + "strings" "github.com/phishingclub/phishingclub/config" "github.com/phishingclub/phishingclub/errs" @@ -16,9 +17,16 @@ func FromConfig(conf config.Config) (*gorm.DB, error) { switch conf.Database().Engine { case config.DefaultAdministrationUseSqlite: var err error + // determine the correct separator for additional parameters + // use & if user already has query params, otherwise use ? + separator := "?" + if strings.Contains(conf.Database().DSN, "?") { + separator = "&" + } dsn := fmt.Sprintf( - "%s?_journal_mode=WAL&_busy_timeout=5000&_synchronous=NORMAL&_foreign_keys=ON", + "%s%s_journal_mode=WAL&_busy_timeout=5000&_synchronous=NORMAL&_foreign_keys=ON", conf.Database().DSN, + separator, ) db, err = gorm.Open(sqlite.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent),