package onboarding import ( "context" "errors" "github.com/jackc/pgx/v5" "time" ) func (s *Service) stateAckCanClose(ctx context.Context, tx pgx.Tx, d Device) (bool, error) { var setupID string err := tx.QueryRow(ctx, `SELECT id::text FROM device_setup_session WHERE device_id=$1::uuid AND boot_id=$2::uuid AND closed_at IS NULL ORDER BY created_at DESC LIMIT 1`, d.ID, d.BootID).Scan(&setupID) if errors.Is(err, pgx.ErrNoRows) { return false, nil } if err != nil { return false, err } _, allowed, err := s.closeDelay(ctx, tx, d, setupID) return allowed, err } func (s *Service) closeDelay(ctx context.Context, tx pgx.Tx, d Device, setupID string) (int64, bool, error) { var saved time.Time var web *time.Time err := tx.QueryRow(ctx, `SELECT a.accepted_at,CASE WHEN ss.web_ack_binding_id=b.id AND ss.web_ack_generation=b.generation THEN ss.web_ack_at END FROM device_state_ack a JOIN device_binding b ON b.device_id=a.device_id AND b.generation=a.binding_generation AND b.unbound_at IS NULL JOIN device_setup_session ss ON ss.device_id=b.device_id AND ss.id=$4::uuid WHERE a.device_id=$1::uuid AND a.binding_generation=$2 AND a.boot_id=$3::uuid`, d.ID, d.Generation, d.BootID, setupID).Scan(&saved, &web) if errors.Is(err, pgx.ErrNoRows) { return 30000, false, nil } if err != nil { return 0, false, err } deadline := saved.Add(30 * time.Second) if web != nil { both := saved if web.After(both) { both = *web } if short := both.Add(3 * time.Second); short.Before(deadline) { deadline = short } } delay := deadline.Sub(s.now()).Milliseconds() if delay < 0 { delay = 0 } return delay, delay == 0, nil }