Merge branch 'master' into glitch-soc/merge-upstream
This commit is contained in:
		
						commit
						c32625495f
					
				
					 14 changed files with 72 additions and 30 deletions
				
			
		|  | @ -27,7 +27,7 @@ module Admin | ||||||
|           ips       = [] |           ips       = [] | ||||||
| 
 | 
 | ||||||
|           Resolv::DNS.open do |dns| |           Resolv::DNS.open do |dns| | ||||||
|             dns.timeouts = 1 |             dns.timeouts = 5 | ||||||
| 
 | 
 | ||||||
|             hostnames = dns.getresources(@email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s } |             hostnames = dns.getresources(@email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -99,6 +99,28 @@ function main() { | ||||||
|       new Rellax('.parallax', { speed: -1 }); |       new Rellax('.parallax', { speed: -1 }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     delegate(document, '#registration_user_password_confirmation,#registration_user_password', 'input', () => { | ||||||
|  |       const password = document.getElementById('registration_user_password'); | ||||||
|  |       const confirmation = document.getElementById('registration_user_password_confirmation'); | ||||||
|  |       if (password.value && password.value !== confirmation.value) { | ||||||
|  |         confirmation.setCustomValidity((new IntlMessageFormat(messages['password_confirmation.mismatching'] || 'Password confirmation does not match', locale)).format()); | ||||||
|  |       } else { | ||||||
|  |         confirmation.setCustomValidity(''); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     delegate(document, '#user_password,#user_password_confirmation', 'input', () => { | ||||||
|  |       const password = document.getElementById('user_password'); | ||||||
|  |       const confirmation = document.getElementById('user_password_confirmation'); | ||||||
|  |       if (!confirmation) return; | ||||||
|  | 
 | ||||||
|  |       if (password.value && password.value !== confirmation.value) { | ||||||
|  |         confirmation.setCustomValidity((new IntlMessageFormat(messages['password_confirmation.mismatching'] || 'Password confirmation does not match', locale)).format()); | ||||||
|  |       } else { | ||||||
|  |         confirmation.setCustomValidity(''); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original')); |     delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original')); | ||||||
|     delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static')); |     delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static')); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -364,7 +364,8 @@ code { | ||||||
|       box-shadow: none; |       box-shadow: none; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     &:focus:invalid:not(:placeholder-shown) { |     &:focus:invalid:not(:placeholder-shown), | ||||||
|  |     &:required:invalid:not(:placeholder-shown) { | ||||||
|       border-color: lighten($error-red, 12%); |       border-color: lighten($error-red, 12%); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ class BlacklistedEmailValidator < ActiveModel::Validator | ||||||
| 
 | 
 | ||||||
|     @email = user.email |     @email = user.email | ||||||
| 
 | 
 | ||||||
|     user.errors.add(:email, I18n.t('users.invalid_email')) if blocked_email? |     user.errors.add(:email, I18n.t('users.blocked_email_provider')) if blocked_email? | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   private |   private | ||||||
|  |  | ||||||
|  | @ -4,22 +4,38 @@ require 'resolv' | ||||||
| 
 | 
 | ||||||
| class EmailMxValidator < ActiveModel::Validator | class EmailMxValidator < ActiveModel::Validator | ||||||
|   def validate(user) |   def validate(user) | ||||||
|     user.errors.add(:email, I18n.t('users.invalid_email')) if invalid_mx?(user.email) |     domain = get_domain(user.email) | ||||||
|  | 
 | ||||||
|  |     if domain.nil? | ||||||
|  |       user.errors.add(:email, I18n.t('users.invalid_email')) | ||||||
|  |     else | ||||||
|  |       ips, hostnames = resolve_mx(domain) | ||||||
|  |       if ips.empty? | ||||||
|  |         user.errors.add(:email, I18n.t('users.invalid_email_mx')) | ||||||
|  |       elsif on_blacklist?(hostnames + ips) | ||||||
|  |         user.errors.add(:email, I18n.t('users.blocked_email_provider')) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   private |   private | ||||||
| 
 | 
 | ||||||
|   def invalid_mx?(value) |   def get_domain(value) | ||||||
|     _, domain = value.split('@', 2) |     _, domain = value.split('@', 2) | ||||||
| 
 | 
 | ||||||
|     return true if domain.nil? |     return nil if domain.nil? | ||||||
| 
 | 
 | ||||||
|     domain    = TagManager.instance.normalize_domain(domain) |     TagManager.instance.normalize_domain(domain) | ||||||
|  |   rescue Addressable::URI::InvalidURIError | ||||||
|  |     nil | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def resolve_mx(domain) | ||||||
|     hostnames = [] |     hostnames = [] | ||||||
|     ips       = [] |     ips       = [] | ||||||
| 
 | 
 | ||||||
|     Resolv::DNS.open do |dns| |     Resolv::DNS.open do |dns| | ||||||
|       dns.timeouts = 1 |       dns.timeouts = 5 | ||||||
| 
 | 
 | ||||||
|       hostnames = dns.getresources(domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s } |       hostnames = dns.getresources(domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s } | ||||||
| 
 | 
 | ||||||
|  | @ -29,9 +45,7 @@ class EmailMxValidator < ActiveModel::Validator | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     ips.empty? || on_blacklist?(hostnames + ips) |     [ips, hostnames] | ||||||
|   rescue Addressable::URI::InvalidURIError |  | ||||||
|     true |  | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def on_blacklist?(values) |   def on_blacklist?(values) | ||||||
|  |  | ||||||
|  | @ -1,13 +1,13 @@ | ||||||
| .simple_form__overlay-area{ class: (closed_registrations? && @instance_presenter.closed_registrations_message.present?) ? 'simple_form__overlay-area__blurred' : '' } | .simple_form__overlay-area{ class: (closed_registrations? && @instance_presenter.closed_registrations_message.present?) ? 'simple_form__overlay-area__blurred' : '' } | ||||||
|   = simple_form_for(new_user, url: user_registration_path, namespace: 'registration') do |f| |   = simple_form_for(new_user, url: user_registration_path, namespace: 'registration', html: { novalidate: false }) do |f| | ||||||
|     %p.lead= t('about.federation_hint_html', instance: content_tag(:strong, site_hostname)) |     %p.lead= t('about.federation_hint_html', instance: content_tag(:strong, site_hostname)) | ||||||
| 
 | 
 | ||||||
|     .fields-group |     .fields-group | ||||||
|       = f.simple_fields_for :account do |account_fields| |       = f.simple_fields_for :account do |account_fields| | ||||||
|         = account_fields.input :username, wrapper: :with_label, label: false, required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', placeholder: t('simple_form.labels.defaults.username') }, append: "@#{site_hostname}", hint: false, disabled: closed_registrations? |         = account_fields.input :username, wrapper: :with_label, label: false, required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', placeholder: t('simple_form.labels.defaults.username'), pattern: '[a-z0-9_]+', maxlength: 30 }, append: "@#{site_hostname}", hint: false, disabled: closed_registrations? | ||||||
| 
 | 
 | ||||||
|       = f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations? |       = f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations? | ||||||
|       = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations? |       = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off', :minlength => User.password_length.first, :maxlength => User.password_length.last }, hint: false, disabled: closed_registrations? | ||||||
|       = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations? |       = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations? | ||||||
| 
 | 
 | ||||||
|     - if approved_registrations? |     - if approved_registrations? | ||||||
|  | @ -16,7 +16,7 @@ | ||||||
|           = invite_request_fields.input :text, as: :text, wrapper: :with_block_label, required: false |           = invite_request_fields.input :text, as: :text, wrapper: :with_block_label, required: false | ||||||
| 
 | 
 | ||||||
|     .fields-group |     .fields-group | ||||||
|       = f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), disabled: closed_registrations? |       = f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), required: true, disabled: closed_registrations? | ||||||
| 
 | 
 | ||||||
|     .actions |     .actions | ||||||
|       = f.button :button, sign_up_message, type: :submit, class: 'button button-primary', disabled: closed_registrations? |       = f.button :button, sign_up_message, type: :submit, class: 'button button-primary', disabled: closed_registrations? | ||||||
|  |  | ||||||
|  | @ -1,14 +1,14 @@ | ||||||
| - content_for :page_title do | - content_for :page_title do | ||||||
|   = t('auth.set_new_password') |   = t('auth.set_new_password') | ||||||
| 
 | 
 | ||||||
| = simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| | = simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, novalidate: false }) do |f| | ||||||
|   = render 'shared/error_messages', object: resource |   = render 'shared/error_messages', object: resource | ||||||
| 
 | 
 | ||||||
|   - if !use_seamless_external_login? || resource.encrypted_password.present? |   - if !use_seamless_external_login? || resource.encrypted_password.present? | ||||||
|     = f.input :reset_password_token, as: :hidden |     = f.input :reset_password_token, as: :hidden | ||||||
| 
 | 
 | ||||||
|     .fields-group |     .fields-group | ||||||
|       = f.input :password, wrapper: :with_label, autofocus: true, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off' }, required: true |       = f.input :password, wrapper: :with_label, autofocus: true, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off', :minlength => User.password_length.first, :maxlength => User.password_length.last }, required: true | ||||||
|     .fields-group |     .fields-group | ||||||
|       = f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' }, required: true |       = f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' }, required: true | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
| 
 | 
 | ||||||
| %h3= t('auth.security') | %h3= t('auth.security') | ||||||
| 
 | 
 | ||||||
| = simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, class: 'auth_edit' }) do |f| | = simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, class: 'auth_edit', novalidate: false }) do |f| | ||||||
|   = render 'shared/error_messages', object: resource |   = render 'shared/error_messages', object: resource | ||||||
| 
 | 
 | ||||||
|   - if !use_seamless_external_login? || resource.encrypted_password.present? |   - if !use_seamless_external_login? || resource.encrypted_password.present? | ||||||
|  | @ -17,7 +17,7 @@ | ||||||
| 
 | 
 | ||||||
|     .fields-row |     .fields-row | ||||||
|       .fields-row__column.fields-group.fields-row__column-6 |       .fields-row__column.fields-group.fields-row__column-6 | ||||||
|         = f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off' }, hint: t('simple_form.hints.defaults.password'), disabled: current_account.suspended? |         = f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off', :minlength => User.password_length.first, :maxlength => User.password_length.last }, hint: t('simple_form.hints.defaults.password'), disabled: current_account.suspended? | ||||||
|       .fields-row__column.fields-group.fields-row__column-6 |       .fields-row__column.fields-group.fields-row__column-6 | ||||||
|         = f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' }, disabled: current_account.suspended? |         = f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' }, disabled: current_account.suspended? | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ | ||||||
| - content_for :header_tags do | - content_for :header_tags do | ||||||
|   = render partial: 'shared/og', locals: { description: description_for_sign_up } |   = render partial: 'shared/og', locals: { description: description_for_sign_up } | ||||||
| 
 | 
 | ||||||
| = simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| | = simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { novalidate: false }) do |f| | ||||||
|   = render 'shared/error_messages', object: resource |   = render 'shared/error_messages', object: resource | ||||||
| 
 | 
 | ||||||
|   - if @invite.present? && @invite.autofollow? |   - if @invite.present? && @invite.autofollow? | ||||||
|  | @ -14,13 +14,13 @@ | ||||||
| 
 | 
 | ||||||
|   = f.simple_fields_for :account do |ff| |   = f.simple_fields_for :account do |ff| | ||||||
|     .fields-group |     .fields-group | ||||||
|       = ff.input :username, wrapper: :with_label, autofocus: true, label: t('simple_form.labels.defaults.username'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off' }, append: "@#{site_hostname}", hint: t('simple_form.hints.defaults.username', domain: site_hostname) |       = ff.input :username, wrapper: :with_label, autofocus: true, label: t('simple_form.labels.defaults.username'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', pattern: '[a-z0-9_]+', maxlength: 30 }, append: "@#{site_hostname}", hint: t('simple_form.hints.defaults.username', domain: site_hostname) | ||||||
| 
 | 
 | ||||||
|   .fields-group |   .fields-group | ||||||
|     = f.input :email, wrapper: :with_label, label: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' } |     = f.input :email, wrapper: :with_label, label: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' } | ||||||
| 
 | 
 | ||||||
|   .fields-group |   .fields-group | ||||||
|     = f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' } |     = f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off', :minlength => User.password_length.first, :maxlength => User.password_length.last } | ||||||
| 
 | 
 | ||||||
|   .fields-group |   .fields-group | ||||||
|     = f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' } |     = f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' } | ||||||
|  | @ -33,7 +33,7 @@ | ||||||
|   = f.input :invite_code, as: :hidden |   = f.input :invite_code, as: :hidden | ||||||
| 
 | 
 | ||||||
|   .fields-group |   .fields-group | ||||||
|     = f.input :agreement, as: :boolean, wrapper: :with_label, label: whitelist_mode? ? t('auth.checkbox_agreement_without_rules_html', terms_path: terms_path) : t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path) |     = f.input :agreement, as: :boolean, wrapper: :with_label, label: whitelist_mode? ? t('auth.checkbox_agreement_without_rules_html', terms_path: terms_path) : t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), required: true | ||||||
| 
 | 
 | ||||||
|   .actions |   .actions | ||||||
|     = f.button :button, @invite.present? ? t('auth.register') : sign_up_message, type: :submit |     = f.button :button, @invite.present? ? t('auth.register') : sign_up_message, type: :submit | ||||||
|  |  | ||||||
|  | @ -17,7 +17,7 @@ | ||||||
|         %span.display-name |         %span.display-name | ||||||
|           %bdi |           %bdi | ||||||
|             %strong.display-name__html.p-name.emojify= display_name(status.account, custom_emojify: true, autoplay: autoplay) |             %strong.display-name__html.p-name.emojify= display_name(status.account, custom_emojify: true, autoplay: autoplay) | ||||||
|             |           = ' ' | ||||||
|           %span.display-name__account |           %span.display-name__account | ||||||
|             = acct(status.account) |             = acct(status.account) | ||||||
|             = fa_icon('lock') if status.account.locked? |             = fa_icon('lock') if status.account.locked? | ||||||
|  |  | ||||||
|  | @ -1325,9 +1325,11 @@ en: | ||||||
|       tips: Tips |       tips: Tips | ||||||
|       title: Welcome aboard, %{name}! |       title: Welcome aboard, %{name}! | ||||||
|   users: |   users: | ||||||
|  |     blocked_email_provider: This e-mail provider isn't allowed | ||||||
|     follow_limit_reached: You cannot follow more than %{limit} people |     follow_limit_reached: You cannot follow more than %{limit} people | ||||||
|     generic_access_help_html: Trouble accessing your account? You may get in touch with %{email} for assistance |     generic_access_help_html: Trouble accessing your account? You may get in touch with %{email} for assistance | ||||||
|     invalid_email: The e-mail address is invalid |     invalid_email: The e-mail address is invalid | ||||||
|  |     invalid_email_mx: The e-mail address does not seem to exist | ||||||
|     invalid_otp_token: Invalid two-factor code |     invalid_otp_token: Invalid two-factor code | ||||||
|     invalid_sign_in_token: Invalid security code |     invalid_sign_in_token: Invalid security code | ||||||
|     otp_lost_help_html: If you lost access to both, you may get in touch with %{email} |     otp_lost_help_html: If you lost access to both, you may get in touch with %{email} | ||||||
|  |  | ||||||
|  | @ -63,7 +63,7 @@ module Mastodon | ||||||
|         ips       = [] |         ips       = [] | ||||||
| 
 | 
 | ||||||
|         Resolv::DNS.open do |dns| |         Resolv::DNS.open do |dns| | ||||||
|           dns.timeouts = 1 |           dns.timeouts = 5 | ||||||
|           hostnames = dns.getresources(email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s } |           hostnames = dns.getresources(email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s } | ||||||
| 
 | 
 | ||||||
|           ([email_domain_block.domain] + hostnames).uniq.each do |hostname| |           ([email_domain_block.domain] + hostnames).uniq.each do |hostname| | ||||||
|  |  | ||||||
|  | @ -17,7 +17,7 @@ RSpec.describe BlacklistedEmailValidator, type: :validator do | ||||||
|       let(:blocked_email) { true } |       let(:blocked_email) { true } | ||||||
| 
 | 
 | ||||||
|       it 'calls errors.add' do |       it 'calls errors.add' do | ||||||
|         expect(errors).to have_received(:add).with(:email, I18n.t('users.invalid_email')) |         expect(errors).to have_received(:add).with(:email, I18n.t('users.blocked_email_provider')) | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -25,7 +25,7 @@ RSpec.describe BlacklistedEmailValidator, type: :validator do | ||||||
|       let(:blocked_email) { false } |       let(:blocked_email) { false } | ||||||
| 
 | 
 | ||||||
|       it 'not calls errors.add' do |       it 'not calls errors.add' do | ||||||
|         expect(errors).not_to have_received(:add).with(:email, I18n.t('users.invalid_email')) |         expect(errors).not_to have_received(:add).with(:email, I18n.t('users.blocked_email_provider')) | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  | @ -210,6 +210,7 @@ const startWorker = (workerId) => { | ||||||
|     if (subs[channel].length === 0) { |     if (subs[channel].length === 0) { | ||||||
|       log.verbose(`Unsubscribe ${channel}`); |       log.verbose(`Unsubscribe ${channel}`); | ||||||
|       redisSubscribeClient.unsubscribe(channel); |       redisSubscribeClient.unsubscribe(channel); | ||||||
|  |       delete subs[channel]; | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|  | @ -888,19 +889,21 @@ const startWorker = (workerId) => { | ||||||
|     channelNameToIds(request, channelName, params).then(({ channelIds }) => { |     channelNameToIds(request, channelName, params).then(({ channelIds }) => { | ||||||
|       log.verbose(request.requestId, `Ending stream from ${channelIds.join(', ')} for ${request.accountId}`); |       log.verbose(request.requestId, `Ending stream from ${channelIds.join(', ')} for ${request.accountId}`); | ||||||
| 
 | 
 | ||||||
|       const { listener, stopHeartbeat } = subscriptions[channelIds.join(';')]; |       const subscription = subscriptions[channelIds.join(';')]; | ||||||
| 
 | 
 | ||||||
|       if (!listener) { |       if (!subscription) { | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|  |       const { listener, stopHeartbeat } = subscription; | ||||||
|  | 
 | ||||||
|       channelIds.forEach(channelId => { |       channelIds.forEach(channelId => { | ||||||
|         unsubscribe(`${redisPrefix}${channelId}`, listener); |         unsubscribe(`${redisPrefix}${channelId}`, listener); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       stopHeartbeat(); |       stopHeartbeat(); | ||||||
| 
 | 
 | ||||||
|       subscriptions[channelIds.join(';')] = undefined; |       delete subscriptions[channelIds.join(';')]; | ||||||
|     }).catch(err => { |     }).catch(err => { | ||||||
|       log.verbose(request.requestId, 'Unsubscription error:', err); |       log.verbose(request.requestId, 'Unsubscription error:', err); | ||||||
|       socket.send(JSON.stringify({ error: err.toString() })); |       socket.send(JSON.stringify({ error: err.toString() })); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue