Avoid redundant OAuth queries when not signed in
If you aren't signed in, you don't have an auth token. When you don't have an auth token, React was sending the headers "Authorization: Bearer null" This caused 5 Doorkeeper token lookups using WHERE "oauth_access_tokens"."token" = 'null' on the Explore page (the root of the app when not signed in).
This commit is contained in:
parent
7bfe6c7709
commit
92c9092abd
4
Gemfile
4
Gemfile
|
@ -30,7 +30,9 @@ gem 'charlock_holmes', '~> 0.7.6'
|
||||||
gem 'iso-639'
|
gem 'iso-639'
|
||||||
gem 'chewy', '~> 5.0'
|
gem 'chewy', '~> 5.0'
|
||||||
gem 'cld3', '~> 3.2.4'
|
gem 'cld3', '~> 3.2.4'
|
||||||
gem 'devise', '~> 4.6'
|
git 'https://github.com/freespeech4ever/devise.git', branch: 'gab2' do
|
||||||
|
gem 'devise'
|
||||||
|
end
|
||||||
gem 'devise-two-factor', '~> 3.0'
|
gem 'devise-two-factor', '~> 3.0'
|
||||||
|
|
||||||
group :pam_authentication, optional: true do
|
group :pam_authentication, optional: true do
|
||||||
|
|
20
Gemfile.lock
20
Gemfile.lock
|
@ -1,3 +1,15 @@
|
||||||
|
GIT
|
||||||
|
remote: https://github.com/freespeech4ever/devise.git
|
||||||
|
revision: 4009905531f28ebd7ecab22f898b5d6180eefb4a
|
||||||
|
branch: gab2
|
||||||
|
specs:
|
||||||
|
devise (4.7.3)
|
||||||
|
bcrypt (~> 3.0)
|
||||||
|
orm_adapter (~> 0.1)
|
||||||
|
railties (>= 4.1.0)
|
||||||
|
responders
|
||||||
|
warden (~> 1.2.3)
|
||||||
|
|
||||||
GIT
|
GIT
|
||||||
remote: https://github.com/rtomayko/posix-spawn
|
remote: https://github.com/rtomayko/posix-spawn
|
||||||
revision: 58465d2e213991f8afb13b984854a49fcdcc980c
|
revision: 58465d2e213991f8afb13b984854a49fcdcc980c
|
||||||
|
@ -183,12 +195,6 @@ GEM
|
||||||
rake (> 10, < 14)
|
rake (> 10, < 14)
|
||||||
ruby-statistics (>= 2.1)
|
ruby-statistics (>= 2.1)
|
||||||
thor (>= 0.19, < 2)
|
thor (>= 0.19, < 2)
|
||||||
devise (4.7.3)
|
|
||||||
bcrypt (~> 3.0)
|
|
||||||
orm_adapter (~> 0.1)
|
|
||||||
railties (>= 4.1.0)
|
|
||||||
responders
|
|
||||||
warden (~> 1.2.3)
|
|
||||||
devise-two-factor (3.1.0)
|
devise-two-factor (3.1.0)
|
||||||
activesupport (< 6.1)
|
activesupport (< 6.1)
|
||||||
attr_encrypted (>= 1.3, < 4, != 2)
|
attr_encrypted (>= 1.3, < 4, != 2)
|
||||||
|
@ -696,7 +702,7 @@ DEPENDENCIES
|
||||||
climate_control (~> 0.2)
|
climate_control (~> 0.2)
|
||||||
concurrent-ruby
|
concurrent-ruby
|
||||||
derailed_benchmarks
|
derailed_benchmarks
|
||||||
devise (~> 4.6)
|
devise!
|
||||||
devise-two-factor (~> 3.0)
|
devise-two-factor (~> 3.0)
|
||||||
devise_pam_authenticatable2 (~> 9.2)
|
devise_pam_authenticatable2 (~> 9.2)
|
||||||
doorkeeper (~> 5.1)
|
doorkeeper (~> 5.1)
|
||||||
|
|
|
@ -51,7 +51,7 @@ export const LIST_ADDER_LISTS_FETCH_SUCCESS = 'LIST_ADDER_LISTS_FETCH_SUCCESS'
|
||||||
export const LIST_ADDER_LISTS_FETCH_FAIL = 'LIST_ADDER_LISTS_FETCH_FAIL'
|
export const LIST_ADDER_LISTS_FETCH_FAIL = 'LIST_ADDER_LISTS_FETCH_FAIL'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const fetchList = (id) => (dispatch, getState) => {
|
export const fetchList = (id) => (dispatch, getState) => {
|
||||||
if (!me) return
|
if (!me) return
|
||||||
|
@ -82,7 +82,7 @@ const fetchListFail = (id, error) => ({
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const fetchLists = () => (dispatch, getState) => {
|
export const fetchLists = () => (dispatch, getState) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
@ -109,12 +109,12 @@ const fetchListsSuccess = (lists) => ({
|
||||||
|
|
||||||
const fetchListsFail = (error) => ({
|
const fetchListsFail = (error) => ({
|
||||||
type: LISTS_FETCH_FAIL,
|
type: LISTS_FETCH_FAIL,
|
||||||
showToast: true,
|
showToast: false,
|
||||||
error,
|
error,
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const submitListEditor = (shouldReset) => (dispatch, getState) => {
|
export const submitListEditor = (shouldReset) => (dispatch, getState) => {
|
||||||
const listId = getState().getIn(['listEditor', 'listId'])
|
const listId = getState().getIn(['listEditor', 'listId'])
|
||||||
|
@ -128,7 +128,7 @@ export const submitListEditor = (shouldReset) => (dispatch, getState) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const setupListEditor = (listId) => (dispatch, getState) => {
|
export const setupListEditor = (listId) => (dispatch, getState) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
@ -140,7 +140,7 @@ export const setupListEditor = (listId) => (dispatch, getState) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const changeListEditorTitle = (value) => ({
|
export const changeListEditorTitle = (value) => ({
|
||||||
type: LIST_EDITOR_TITLE_CHANGE,
|
type: LIST_EDITOR_TITLE_CHANGE,
|
||||||
|
@ -148,7 +148,7 @@ export const changeListEditorTitle = (value) => ({
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const createList = (title, shouldReset) => (dispatch, getState) => {
|
export const createList = (title, shouldReset) => (dispatch, getState) => {
|
||||||
if (!me) return
|
if (!me) return
|
||||||
|
@ -181,7 +181,7 @@ export const createListFail = (error) => ({
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const updateList = (id, title, shouldReset) => (dispatch, getState) => {
|
export const updateList = (id, title, shouldReset) => (dispatch, getState) => {
|
||||||
if (!me) return
|
if (!me) return
|
||||||
|
@ -220,7 +220,7 @@ export const resetListEditor = () => ({
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const deleteList = (id) => (dispatch, getState) => {
|
export const deleteList = (id) => (dispatch, getState) => {
|
||||||
if (!me) return
|
if (!me) return
|
||||||
|
@ -251,7 +251,7 @@ export const deleteListFail = (id, error) => ({
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const fetchListAccounts = (listId) => (dispatch, getState) => {
|
export const fetchListAccounts = (listId) => (dispatch, getState) => {
|
||||||
if (!me) return
|
if (!me) return
|
||||||
|
@ -284,7 +284,7 @@ export const fetchListAccountsFail = (id, error) => ({
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const fetchListSuggestions = (q) => (dispatch, getState) => {
|
export const fetchListSuggestions = (q) => (dispatch, getState) => {
|
||||||
if (!me) return
|
if (!me) return
|
||||||
|
@ -303,7 +303,7 @@ export const fetchListSuggestions = (q) => (dispatch, getState) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
const fetchListSuggestionsReady = (query, accounts) => ({
|
const fetchListSuggestionsReady = (query, accounts) => ({
|
||||||
type: LIST_EDITOR_SUGGESTIONS_READY,
|
type: LIST_EDITOR_SUGGESTIONS_READY,
|
||||||
|
@ -312,14 +312,14 @@ const fetchListSuggestionsReady = (query, accounts) => ({
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const clearListSuggestions = () => ({
|
export const clearListSuggestions = () => ({
|
||||||
type: LIST_EDITOR_SUGGESTIONS_CLEAR,
|
type: LIST_EDITOR_SUGGESTIONS_CLEAR,
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const changeListSuggestions = (value) => ({
|
export const changeListSuggestions = (value) => ({
|
||||||
type: LIST_EDITOR_SUGGESTIONS_CHANGE,
|
type: LIST_EDITOR_SUGGESTIONS_CHANGE,
|
||||||
|
@ -327,14 +327,14 @@ export const changeListSuggestions = (value) => ({
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const addToListEditor = accountId => (dispatch, getState) => {
|
export const addToListEditor = accountId => (dispatch, getState) => {
|
||||||
dispatch(addToList(getState().getIn(['listEditor', 'listId']), accountId))
|
dispatch(addToList(getState().getIn(['listEditor', 'listId']), accountId))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const addToList = (listId, accountId) => (dispatch, getState) => {
|
export const addToList = (listId, accountId) => (dispatch, getState) => {
|
||||||
if (!me) return
|
if (!me) return
|
||||||
|
@ -368,14 +368,14 @@ const addToListFail = (listId, accountId, error) => ({
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const removeFromListEditor = accountId => (dispatch, getState) => {
|
export const removeFromListEditor = accountId => (dispatch, getState) => {
|
||||||
dispatch(removeFromList(getState().getIn(['listEditor', 'listId']), accountId))
|
dispatch(removeFromList(getState().getIn(['listEditor', 'listId']), accountId))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const removeFromList = (listId, accountId) => (dispatch, getState) => {
|
export const removeFromList = (listId, accountId) => (dispatch, getState) => {
|
||||||
if (!me) return
|
if (!me) return
|
||||||
|
@ -409,14 +409,14 @@ const removeFromListFail = (listId, accountId, error) => ({
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const resetListAdder = () => ({
|
export const resetListAdder = () => ({
|
||||||
type: LIST_ADDER_RESET,
|
type: LIST_ADDER_RESET,
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const setupListAdder = accountId => (dispatch, getState) => {
|
export const setupListAdder = accountId => (dispatch, getState) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
@ -428,7 +428,7 @@ export const setupListAdder = accountId => (dispatch, getState) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const fetchAccountLists = (accountId) => (dispatch, getState) => {
|
export const fetchAccountLists = (accountId) => (dispatch, getState) => {
|
||||||
if (!me) return
|
if (!me) return
|
||||||
|
@ -459,14 +459,14 @@ const fetchAccountListsFail = (id, error) => ({
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const addToListAdder = (listId) => (dispatch, getState) => {
|
export const addToListAdder = (listId) => (dispatch, getState) => {
|
||||||
dispatch(addToList(listId, getState().getIn(['listAdder', 'accountId'])))
|
dispatch(addToList(listId, getState().getIn(['listAdder', 'accountId'])))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const removeFromListAdder = (listId) => (dispatch, getState) => {
|
export const removeFromListAdder = (listId) => (dispatch, getState) => {
|
||||||
dispatch(removeFromList(listId, getState().getIn(['listAdder', 'accountId'])))
|
dispatch(removeFromList(listId, getState().getIn(['listAdder', 'accountId'])))
|
||||||
|
|
|
@ -25,16 +25,20 @@ function setCSRFHeader() {
|
||||||
|
|
||||||
ready(setCSRFHeader);
|
ready(setCSRFHeader);
|
||||||
|
|
||||||
export default getState => axios.create({
|
export default getState => {
|
||||||
headers: Object.assign(csrfHeader, getState ? {
|
const authToken = getState ? getState().getIn(['meta', 'access_token'], '') : null;
|
||||||
'Authorization': `Bearer ${getState().getIn(['meta', 'access_token'], '')}`,
|
|
||||||
} : {}),
|
|
||||||
|
|
||||||
transformResponse: [function (data) {
|
return axios.create({
|
||||||
try {
|
headers: Object.assign(csrfHeader, authToken ? {
|
||||||
return JSON.parse(data);
|
'Authorization': `Bearer ${authToken}}`,
|
||||||
} catch (Exception) {
|
} : {}),
|
||||||
return data;
|
|
||||||
}
|
transformResponse: [function (data) {
|
||||||
}],
|
try {
|
||||||
});
|
return JSON.parse(data);
|
||||||
|
} catch (Exception) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
@ -15,11 +15,6 @@ class AccountModerationNote < ApplicationRecord
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
belongs_to :target_account, class_name: 'Account'
|
belongs_to :target_account, class_name: 'Account'
|
||||||
|
|
||||||
connects_to database: {
|
|
||||||
writing: :master,
|
|
||||||
reading: :master
|
|
||||||
}
|
|
||||||
|
|
||||||
scope :latest, -> { reorder('created_at DESC') }
|
scope :latest, -> { reorder('created_at DESC') }
|
||||||
|
|
||||||
validates :content, presence: true, length: { maximum: 500 }
|
validates :content, presence: true, length: { maximum: 500 }
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
class AccountVerificationRequest < ApplicationRecord
|
class AccountVerificationRequest < ApplicationRecord
|
||||||
|
|
||||||
connects_to database: {
|
connects_to database: {
|
||||||
writing: :master,
|
writing: :primary,
|
||||||
reading: :master
|
reading: :primary
|
||||||
}
|
}
|
||||||
|
|
||||||
LIMIT = 4.megabytes
|
LIMIT = 4.megabytes
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
class ApplicationRecord < ActiveRecord::Base
|
class ApplicationRecord < ActiveRecord::Base
|
||||||
connects_to database: {
|
connects_to database: {
|
||||||
writing: :master,
|
writing: :primary,
|
||||||
reading: :slave1
|
reading: :slave1
|
||||||
}
|
}
|
||||||
self.abstract_class = true
|
self.abstract_class = true
|
||||||
|
|
|
@ -29,11 +29,6 @@ class Report < ApplicationRecord
|
||||||
|
|
||||||
validates :comment, length: { maximum: 1000 }
|
validates :comment, length: { maximum: 1000 }
|
||||||
|
|
||||||
connects_to database: {
|
|
||||||
writing: :master,
|
|
||||||
reading: :master
|
|
||||||
}
|
|
||||||
|
|
||||||
def local?
|
def local?
|
||||||
false # Force uri_for to use uri attribute
|
false # Force uri_for to use uri attribute
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,9 +19,4 @@ class ReportNote < ApplicationRecord
|
||||||
|
|
||||||
validates :content, presence: true, length: { maximum: 500 }
|
validates :content, presence: true, length: { maximum: 500 }
|
||||||
|
|
||||||
connects_to database: {
|
|
||||||
writing: :master,
|
|
||||||
reading: :master
|
|
||||||
}
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,11 +23,6 @@ class SessionActivation < ApplicationRecord
|
||||||
to: :access_token,
|
to: :access_token,
|
||||||
allow_nil: true
|
allow_nil: true
|
||||||
|
|
||||||
connects_to database: {
|
|
||||||
writing: :master,
|
|
||||||
reading: :master
|
|
||||||
}
|
|
||||||
|
|
||||||
def detection
|
def detection
|
||||||
@detection ||= Browser.new(user_agent)
|
@detection ||= Browser.new(user_agent)
|
||||||
end
|
end
|
||||||
|
@ -45,7 +40,9 @@ class SessionActivation < ApplicationRecord
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def active?(id)
|
def active?(id)
|
||||||
id && where(session_id: id).exists?
|
ActiveRecord::Base.connected_to(role: :writing) do
|
||||||
|
id && where(session_id: id).exists?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def activate(**options)
|
def activate(**options)
|
||||||
|
@ -61,19 +58,16 @@ class SessionActivation < ApplicationRecord
|
||||||
|
|
||||||
def deactivate(id)
|
def deactivate(id)
|
||||||
return unless id
|
return unless id
|
||||||
ActiveRecord::Base.connected_to(role: :writing) do
|
where(session_id: id).destroy_all
|
||||||
conn = ActiveRecord::Base.connection
|
|
||||||
conn.exec_query "delete from session_activations where session_id = '#{id}'"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def purge_old
|
def purge_old
|
||||||
order('created_at desc').offset(Rails.configuration.x.max_session_activations).destroy_all
|
order('created_at desc').offset(Rails.configuration.x.max_session_activations).destroy_all
|
||||||
end
|
end
|
||||||
|
|
||||||
def exclusive(id)
|
#def exclusive(id)
|
||||||
where('session_id != ?', id).destroy_all
|
# where('session_id != ?', id).destroy_all
|
||||||
end
|
#end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -93,6 +87,5 @@ class SessionActivation < ApplicationRecord
|
||||||
expires_in: Doorkeeper.configuration.access_token_expires_in,
|
expires_in: Doorkeeper.configuration.access_token_expires_in,
|
||||||
use_refresh_token: Doorkeeper.configuration.refresh_token_enabled?)
|
use_refresh_token: Doorkeeper.configuration.refresh_token_enabled?)
|
||||||
end
|
end
|
||||||
self.access_token
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@ default: &default
|
||||||
prepared_statements: <%= ENV['PREPARED_STATEMENTS'] || 'false' %>
|
prepared_statements: <%= ENV['PREPARED_STATEMENTS'] || 'false' %>
|
||||||
|
|
||||||
development:
|
development:
|
||||||
master:
|
primary:
|
||||||
<<: *default
|
<<: *default
|
||||||
url: <%= ENV['DB_MASTER_URL'] %>
|
url: <%= ENV['DB_MASTER_URL'] %>
|
||||||
slave1:
|
slave1:
|
||||||
|
@ -39,7 +39,7 @@ test:
|
||||||
# port: <%= ENV['DB_PORT'] || 5432 %>
|
# port: <%= ENV['DB_PORT'] || 5432 %>
|
||||||
# prepared_statements: <%= ENV['PREPARED_STATEMENTS'] || 'true' %>
|
# prepared_statements: <%= ENV['PREPARED_STATEMENTS'] || 'true' %>
|
||||||
production:
|
production:
|
||||||
master:
|
primary:
|
||||||
<<: *default
|
<<: *default
|
||||||
url: <%= ENV['DB_MASTER_URL'] %>
|
url: <%= ENV['DB_MASTER_URL'] %>
|
||||||
slave1:
|
slave1:
|
||||||
|
|
|
@ -76,6 +76,14 @@ module Devise
|
||||||
end
|
end
|
||||||
|
|
||||||
Devise.setup do |config|
|
Devise.setup do |config|
|
||||||
|
|
||||||
|
config.warden_hook_save_wrapper = Proc.new do |hook|
|
||||||
|
# ensure the writable connection is used to avoid read-only write errors
|
||||||
|
ApplicationRecord.connected_to(role: :writing) do
|
||||||
|
hook.call
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
config.warden do |manager|
|
config.warden do |manager|
|
||||||
manager.default_strategies(scope: :user).unshift :ldap_authenticatable if Devise.ldap_authentication
|
manager.default_strategies(scope: :user).unshift :ldap_authenticatable if Devise.ldap_authentication
|
||||||
manager.default_strategies(scope: :user).unshift :pam_authenticatable if Devise.pam_authentication
|
manager.default_strategies(scope: :user).unshift :pam_authenticatable if Devise.pam_authentication
|
||||||
|
|
|
@ -7,6 +7,13 @@ Devise.setup do |config|
|
||||||
options = {}
|
options = {}
|
||||||
options[:redirect_at_sign_in] = ENV['OAUTH_REDIRECT_AT_SIGN_IN'] == 'true'
|
options[:redirect_at_sign_in] = ENV['OAUTH_REDIRECT_AT_SIGN_IN'] == 'true'
|
||||||
|
|
||||||
|
config.warden_hook_save_wrapper = Proc.new do |hook|
|
||||||
|
# ensure the writable connection is used to avoid read-only write errors
|
||||||
|
ApplicationRecord.connected_to(role: :writing) do
|
||||||
|
hook.call
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# CAS strategy
|
# CAS strategy
|
||||||
if ENV['CAS_ENABLED'] == 'true'
|
if ENV['CAS_ENABLED'] == 'true'
|
||||||
cas_options = options
|
cas_options = options
|
||||||
|
|
Loading…
Reference in New Issue