package service import ( "context" "crypto/rand" "encoding/hex" "encoding/json" "github.com/calendarapi/internal/middleware" "github.com/calendarapi/internal/models" "github.com/calendarapi/internal/repository" "github.com/calendarapi/internal/utils" "github.com/google/uuid" ) type APIKeyService struct { queries *repository.Queries } func NewAPIKeyService(queries *repository.Queries) *APIKeyService { return &APIKeyService{queries: queries} } func (s *APIKeyService) Create(ctx context.Context, userID uuid.UUID, name string, scopes map[string][]string) (*models.APIKeyResponse, error) { if name == "" { return nil, models.NewValidationError("name is required") } rawToken := make([]byte, 32) if _, err := rand.Read(rawToken); err != nil { return nil, models.ErrInternal } token := hex.EncodeToString(rawToken) hash := middleware.SHA256Hash(token) scopesJSON, err := json.Marshal(scopes) if err != nil { return nil, models.ErrInternal } keyID := uuid.New() key, err := s.queries.CreateAPIKey(ctx, repository.CreateAPIKeyParams{ ID: utils.ToPgUUID(keyID), UserID: utils.ToPgUUID(userID), Name: name, KeyHash: hash, Scopes: scopesJSON, }) if err != nil { return nil, models.ErrInternal } return &models.APIKeyResponse{ ID: utils.FromPgUUID(key.ID), Name: key.Name, CreatedAt: utils.FromPgTimestamptz(key.CreatedAt), Token: token, }, nil } func (s *APIKeyService) List(ctx context.Context, userID uuid.UUID) ([]models.APIKeyResponse, error) { keys, err := s.queries.ListAPIKeysByUser(ctx, utils.ToPgUUID(userID)) if err != nil { return nil, models.ErrInternal } result := make([]models.APIKeyResponse, len(keys)) for i, k := range keys { result[i] = models.APIKeyResponse{ ID: utils.FromPgUUID(k.ID), Name: k.Name, CreatedAt: utils.FromPgTimestamptz(k.CreatedAt), RevokedAt: utils.FromPgTimestamptzPtr(k.RevokedAt), } } return result, nil } func (s *APIKeyService) Revoke(ctx context.Context, userID uuid.UUID, keyID uuid.UUID) error { return s.queries.RevokeAPIKey(ctx, repository.RevokeAPIKeyParams{ ID: utils.ToPgUUID(keyID), UserID: utils.ToPgUUID(userID), }) }