package store import ( "context" "fmt" "time" "github.com/jackc/pgx/v5" ) // ChannelError is a channel that returned HTTP 404 on a discovery pass. type ChannelError struct { ChannelID string ChannelName string FirstSeen time.Time LastSeen time.Time } // UpsertChannelError records (or refreshes) a 404 channel for the current user. // Called by the runner inside a withUser scope; RLS guards user isolation. func (s *Store) UpsertChannelError(ctx context.Context, userID, channelID, channelName string) error { return s.withUser(ctx, userID, func(tx pgx.Tx) error { _, err := tx.Exec(ctx, ` INSERT INTO channel_errors (user_id, channel_id, channel_name) VALUES ($1, $2, $3) ON CONFLICT (user_id, channel_id) DO UPDATE SET channel_name = EXCLUDED.channel_name, last_seen = now()`, userID, channelID, channelName) if err != nil { return fmt.Errorf("store: upsert channel error: %w", err) } return nil }) } // ListChannelErrors returns all 404-flagged channels for the user, newest first. func (s *Store) ListChannelErrors(ctx context.Context, userID string) ([]ChannelError, error) { var out []ChannelError err := s.withUser(ctx, userID, func(tx pgx.Tx) error { rows, err := tx.Query(ctx, ` SELECT channel_id, channel_name, first_seen, last_seen FROM channel_errors WHERE user_id = $1 ORDER BY last_seen DESC`, userID) if err != nil { return fmt.Errorf("store: list channel errors: %w", err) } defer rows.Close() for rows.Next() { var ce ChannelError if err := rows.Scan(&ce.ChannelID, &ce.ChannelName, &ce.FirstSeen, &ce.LastSeen); err != nil { return fmt.Errorf("store: scan channel error: %w", err) } out = append(out, ce) } return rows.Err() }) return out, err }