package repo import ( "context" "crypto/sha256" "encoding/hex" "errors" "fmt" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" "github.com/dbiz/cdp/ingestion/ingest/internal/apperr" "github.com/dbiz/cdp/ingestion/ingest/internal/model" ) // WriteKeyRepo loads WriteKey records by their plaintext value. // The plaintext is hashed before the lookup; the DB only stores hashes. type WriteKeyRepo interface { FindByPlaintext(ctx context.Context, plaintext string) (*model.WriteKey, error) MarkUsed(ctx context.Context, id string) error } type writeKeyRepo struct { db *pgxpool.Pool } func NewWriteKeyRepo(db *pgxpool.Pool) WriteKeyRepo { return &writeKeyRepo{db: db} } func hashKey(plaintext string) string { sum := sha256.Sum256([]byte(plaintext)) return hex.EncodeToString(sum[:]) } func (r *writeKeyRepo) FindByPlaintext(ctx context.Context, plaintext string) (*model.WriteKey, error) { const q = ` SELECT id::text, workspace_id::text, source_id::text, key_prefix, COALESCE(label, ''), revoked_at, last_used_at, created_at FROM write_keys WHERE key_hash = $1` row := r.db.QueryRow(ctx, q, hashKey(plaintext)) var k model.WriteKey err := row.Scan(&k.ID, &k.WorkspaceID, &k.SourceID, &k.KeyPrefix, &k.Label, &k.RevokedAt, &k.LastUsedAt, &k.CreatedAt) if errors.Is(err, pgx.ErrNoRows) { return nil, apperr.Unauthorized("invalid write key") } if err != nil { return nil, apperr.Internal(fmt.Errorf("writekey lookup: %w", err)) } return &k, nil } func (r *writeKeyRepo) MarkUsed(ctx context.Context, id string) error { const q = `UPDATE write_keys SET last_used_at = now() WHERE id = $1` _, err := r.db.Exec(ctx, q, id) if err != nil { return apperr.Internal(fmt.Errorf("writekey mark used: %w", err)) } return nil }