package site import ( "context" "fmt" "time" "github.com/gocql/gocql" "go.uber.org/zap" domainsite "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/domain/site" "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/security/apikey" ) // RotateAPIKeyUseCase handles API key rotation // DEPRECATED: This usecase is too fat and violates Clean Architecture. // Use the service layer (service/site/rotate_apikey.go) which orchestrates // focused usecases: GetSiteUseCase, GenerateAPIKeyUseCase, UpdateSiteAPIKeyUseCase, UpdateSiteToRepoUseCase. // This will be removed after migration is complete. type RotateAPIKeyUseCase struct { repo domainsite.Repository apiKeyGen apikey.Generator apiKeyHasher apikey.Hasher logger *zap.Logger } // ProvideRotateAPIKeyUseCase creates a new RotateAPIKeyUseCase func ProvideRotateAPIKeyUseCase( repo domainsite.Repository, apiKeyGen apikey.Generator, apiKeyHasher apikey.Hasher, logger *zap.Logger, ) *RotateAPIKeyUseCase { return &RotateAPIKeyUseCase{ repo: repo, apiKeyGen: apiKeyGen, apiKeyHasher: apiKeyHasher, logger: logger, } } // RotateAPIKeyInput is the input for rotating an API key type RotateAPIKeyInput struct { SiteID string } // RotateAPIKeyOutput is the output after rotating an API key type RotateAPIKeyOutput struct { NewAPIKey string `json:"new_api_key"` OldKeyLastFour string `json:"old_key_last_four"` RotatedAt time.Time `json:"rotated_at"` } // Execute rotates a site's API key func (uc *RotateAPIKeyUseCase) Execute(ctx context.Context, tenantID gocql.UUID, input *RotateAPIKeyInput) (*RotateAPIKeyOutput, error) { siteID, err := gocql.ParseUUID(input.SiteID) if err != nil { return nil, err } // Get current site site, err := uc.repo.GetByID(ctx, tenantID, siteID) if err != nil { uc.logger.Error("failed to get site", zap.Error(err)) return nil, err } // Store old key info oldKeyLastFour := site.APIKeyLastFour // Generate new API key newAPIKey, err := uc.apiKeyGen.Generate() if err != nil { uc.logger.Error("failed to generate new API key", zap.Error(err)) return nil, fmt.Errorf("failed to generate API key: %w", err) } // Hash new key newKeyHash := uc.apiKeyHasher.Hash(newAPIKey) newKeyPrefix := apikey.ExtractPrefix(newAPIKey) newKeyLastFour := apikey.ExtractLastFour(newAPIKey) // Update site with new key site.APIKeyHash = newKeyHash site.APIKeyPrefix = newKeyPrefix site.APIKeyLastFour = newKeyLastFour site.UpdatedAt = time.Now() // Update in repository (all 4 tables) if err := uc.repo.Update(ctx, site); err != nil { uc.logger.Error("failed to update site with new API key", zap.Error(err)) return nil, err } rotatedAt := time.Now() uc.logger.Info("API key rotated successfully", zap.String("site_id", siteID.String()), zap.String("old_key_last_four", oldKeyLastFour)) return &RotateAPIKeyOutput{ NewAPIKey: newAPIKey, // PLAINTEXT - only shown once! OldKeyLastFour: oldKeyLastFour, RotatedAt: rotatedAt, }, nil }