From e551282f57154b4511e2388953131443ee4cdf9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Tue, 18 Aug 2026 15:53:14 -0600 Subject: [PATCH 1/2] feat[backend](tenants): added events and purge operations on events --- backend/modules/audit/domain/event_type.go | 4 ++++ backend/pkg/eventstore/eventstore.go | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/backend/modules/audit/domain/event_type.go b/backend/modules/audit/domain/event_type.go index f11defcc7..5f2861dc3 100644 --- a/backend/modules/audit/domain/event_type.go +++ b/backend/modules/audit/domain/event_type.go @@ -124,6 +124,10 @@ const ( TENANT_UPDATE_SUCCESS ApplicationEventType = "TENANT_UPDATE_SUCCESS" TENANT_TERMINATE_ATTEMPT ApplicationEventType = "TENANT_TERMINATE_ATTEMPT" TENANT_TERMINATE_SUCCESS ApplicationEventType = "TENANT_TERMINATE_SUCCESS" + TENANT_REACTIVATE_ATTEMPT ApplicationEventType = "TENANT_REACTIVATE_ATTEMPT" + TENANT_REACTIVATE_SUCCESS ApplicationEventType = "TENANT_REACTIVATE_SUCCESS" + TENANT_PURGE_ATTEMPT ApplicationEventType = "TENANT_PURGE_ATTEMPT" + TENANT_PURGE_SUCCESS ApplicationEventType = "TENANT_PURGE_SUCCESS" TENANT_SUPPORT_ACCESS_ATTEMPT ApplicationEventType = "TENANT_SUPPORT_ACCESS_ATTEMPT" TENANT_SUPPORT_ACCESS_SUCCESS ApplicationEventType = "TENANT_SUPPORT_ACCESS_SUCCESS" diff --git a/backend/pkg/eventstore/eventstore.go b/backend/pkg/eventstore/eventstore.go index 8db642979..0dd12b429 100644 --- a/backend/pkg/eventstore/eventstore.go +++ b/backend/pkg/eventstore/eventstore.go @@ -1,6 +1,8 @@ package eventstore import ( + "context" + "fmt" "time" chdriver "github.com/ClickHouse/clickhouse-go/v2" @@ -69,4 +71,17 @@ func (s *Store) TableName(d store.Dataset) string { return db + "." + name } +// PurgeTenant deletes all rows scoped to the given tenant across every +// dataset table. ClickHouse mutation, not synchronous — the ALTER returns +// once accepted and the rows disappear as the mutation runs. +func (s *Store) PurgeTenant(ctx context.Context, tenantID string) error { + for _, d := range []store.Dataset{DatasetLogs, DatasetAlerts, DatasetStats} { + sql := fmt.Sprintf("ALTER TABLE %s DELETE WHERE tenantId = ?", s.TableName(d)) + if err := s.Conn.Exec(ctx, sql, tenantID); err != nil { + return fmt.Errorf("purge %s: %w", d, err) + } + } + return nil +} + func SupportsTextSearch(d store.Dataset) bool { return d == DatasetLogs } From 7fe3e36a1f976b3947d02161167a3d36e7537f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Tue, 18 Aug 2026 16:02:28 -0600 Subject: [PATCH 2/2] feat[backend](tenants): added tenant valid uuid verification and used clickhouse friendly syntax to delete operations --- backend/pkg/eventstore/eventstore.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/backend/pkg/eventstore/eventstore.go b/backend/pkg/eventstore/eventstore.go index 0dd12b429..1727e786f 100644 --- a/backend/pkg/eventstore/eventstore.go +++ b/backend/pkg/eventstore/eventstore.go @@ -7,6 +7,7 @@ import ( chdriver "github.com/ClickHouse/clickhouse-go/v2" "github.com/ClickHouse/clickhouse-go/v2/lib/driver" + "github.com/google/uuid" "github.com/threatwinds/go-sdk/store" "github.com/threatwinds/go-sdk/store/clickhouse" @@ -74,10 +75,17 @@ func (s *Store) TableName(d store.Dataset) string { // PurgeTenant deletes all rows scoped to the given tenant across every // dataset table. ClickHouse mutation, not synchronous — the ALTER returns // once accepted and the rows disappear as the mutation runs. +// +// tenantID must be a UUID string (caller passes uuid.UUID.String()); it is +// inlined into the SQL because ClickHouse does not bind parameters inside +// ALTER ... DELETE mutation expressions. The UUID shape makes inlining safe. func (s *Store) PurgeTenant(ctx context.Context, tenantID string) error { + if _, err := uuid.Parse(tenantID); err != nil { + return fmt.Errorf("purge: invalid tenant id: %w", err) + } for _, d := range []store.Dataset{DatasetLogs, DatasetAlerts, DatasetStats} { - sql := fmt.Sprintf("ALTER TABLE %s DELETE WHERE tenantId = ?", s.TableName(d)) - if err := s.Conn.Exec(ctx, sql, tenantID); err != nil { + sql := fmt.Sprintf("ALTER TABLE %s DELETE WHERE tenantId = '%s'", s.TableName(d), tenantID) + if err := s.Conn.Exec(ctx, sql); err != nil { return fmt.Errorf("purge %s: %w", d, err) } }