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..1727e786f 100644 --- a/backend/pkg/eventstore/eventstore.go +++ b/backend/pkg/eventstore/eventstore.go @@ -1,10 +1,13 @@ package eventstore import ( + "context" + "fmt" "time" 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" @@ -69,4 +72,24 @@ 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. +// +// 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'", s.TableName(d), tenantID) + if err := s.Conn.Exec(ctx, sql); err != nil { + return fmt.Errorf("purge %s: %w", d, err) + } + } + return nil +} + func SupportsTextSearch(d store.Dataset) bool { return d == DatasetLogs }