Pull request: 1163 safesearch http api vol.3
Merge in DNS/adguard-home from 1163-safesearch-1-3 to master Squashed commit of the following: commit f26c5fb4f7a27dc61b10c28d6672d5307796784c Merge:e7a1b885
143616ca
Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Mar 23 18:45:25 2023 +0700 Merge remote-tracking branch 'origin/master' into 1163-safesearch-1-3 # Conflicts: # CHANGELOG.md commite7a1b885d6
Merge:01b73d76
eb5d8a49
Author: Vladislav Abdulmyanov <v.abdulmyanov@adguard.com> Date: Wed Mar 22 13:55:23 2023 +0200 Merge branch '1163-safesearch-1-3' of ssh://bit.adguard.com:7999/dns/adguard-home into 1163-safesearch-1-3 commit01b73d763c
Author: Vladislav Abdulmyanov <v.abdulmyanov@adguard.com> Date: Wed Mar 22 13:52:02 2023 +0200 client: add safe search extended settings to clients commiteb5d8a499a
Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Mar 22 18:50:23 2023 +0700 all: docs commit 2043a8fba7f664ef365ccc5abac14a85035eb4b7 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Mar 22 09:42:50 2023 +0700 all: docs commit bb1d2f6c0252891ccac3d3727eb23288a24d4bda Merge: 95f9fd3dc3edab43
Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Mar 22 09:42:00 2023 +0700 Merge remote-tracking branch 'origin/master' into 1163-safesearch-1-3 commit 95f9fd3dd1e8abcdf1a156e81aff8e52f320f4c3 Author: Vladislav Abdulmyanov <v.abdulmyanov@adguard.com> Date: Tue Mar 21 15:25:39 2023 +0200 client: move to new safe search api commit ac823a911f0d6ab6f1813d11a0ca082d54cc9131 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Mar 20 22:40:29 2023 +0700 all: docs commit aaa287b125c7c7a775b821e0dd272199229a7538 Merge: 16fa703148431f8b
Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Mar 20 22:39:14 2023 +0700 Merge remote-tracking branch 'origin/master' into 1163-safesearch-1-3 commit 16fa7031ab2aec31139ace54ffa0155cde8e9135 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Mar 20 22:39:03 2023 +0700 all: docs commit 498f7d3cbb842eda218b0fd06fc3bb3601b81f80 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Mar 20 18:59:47 2023 +0700 filtering: imp code commit aab7b70e2355ba86577e5156c1d5569b21b4b358 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Mon Mar 20 18:40:18 2023 +0700 filtering: imp code commit d2870a18ffdb1d293993487073912168d6b75a38 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Mar 17 21:57:58 2023 +0700 filtering: imp code commit 868f5d1ed29c3af702114079e7ffe46e136eb901 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Mar 17 19:06:36 2023 +0700 all: imp docs commit f6d70b06ed873684501ce17f647ccf07a85dd50b Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Mar 17 19:05:40 2023 +0700 filtering: imp code commit 7cd9a37dde6262a8cf4f0f13f9946e011cc0e2cf Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Mar 16 14:56:51 2023 +0700 home: imp code commit 84d8817512e47a517ed2880ffa9dde5ffda1d288 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Mar 16 09:39:41 2023 +0700 all: safesearch http api
This commit is contained in:
parent
143616ca6e
commit
df61741f60
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -90,6 +90,17 @@ In this release, the schema version has changed from 17 to 20.
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
||||||
|
- The `POST /control/safesearch/enable` HTTP API is deprecated. Use the new
|
||||||
|
`PUT /control/safesearch/settings` API.
|
||||||
|
- The `POST /control/safesearch/disable` HTTP API is deprecated. Use the new
|
||||||
|
`PUT /control/safesearch/settings` API
|
||||||
|
- The `safesearch_enabled` field is deprecated in the following HTTP APIs:
|
||||||
|
- `GET /control/clients`
|
||||||
|
- `POST /control/clients/add`
|
||||||
|
- `POST /control/clients/update`
|
||||||
|
- `GET /control/clients/find?ip0=...&ip1=...&ip2=...`
|
||||||
|
|
||||||
|
Check `openapi/openapi.yaml` for more details.
|
||||||
- The `GET /control/stats_info` HTTP API; use the new `GET
|
- The `GET /control/stats_info` HTTP API; use the new `GET
|
||||||
/control/stats/config` API instead.
|
/control/stats/config` API instead.
|
||||||
|
|
||||||
|
|
|
@ -167,6 +167,7 @@
|
||||||
"enabled_parental_toast": "Enabled Parental Control",
|
"enabled_parental_toast": "Enabled Parental Control",
|
||||||
"disabled_safe_search_toast": "Disabled Safe Search",
|
"disabled_safe_search_toast": "Disabled Safe Search",
|
||||||
"enabled_save_search_toast": "Enabled Safe Search",
|
"enabled_save_search_toast": "Enabled Safe Search",
|
||||||
|
"updated_save_search_toast": "Safe Search settings updated",
|
||||||
"enabled_table_header": "Enabled",
|
"enabled_table_header": "Enabled",
|
||||||
"name_table_header": "Name",
|
"name_table_header": "Name",
|
||||||
"list_url_table_header": "List URL",
|
"list_url_table_header": "List URL",
|
||||||
|
|
|
@ -24,6 +24,12 @@ import { getFilteringStatus, setRules } from './filtering';
|
||||||
export const toggleSettingStatus = createAction('SETTING_STATUS_TOGGLE');
|
export const toggleSettingStatus = createAction('SETTING_STATUS_TOGGLE');
|
||||||
export const showSettingsFailure = createAction('SETTINGS_FAILURE_SHOW');
|
export const showSettingsFailure = createAction('SETTINGS_FAILURE_SHOW');
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} settingKey = SETTINGS_NAMES
|
||||||
|
* @param {*} status: boolean | SafeSearchConfig
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
export const toggleSetting = (settingKey, status) => async (dispatch) => {
|
export const toggleSetting = (settingKey, status) => async (dispatch) => {
|
||||||
let successMessage = '';
|
let successMessage = '';
|
||||||
try {
|
try {
|
||||||
|
@ -49,14 +55,9 @@ export const toggleSetting = (settingKey, status) => async (dispatch) => {
|
||||||
dispatch(toggleSettingStatus({ settingKey }));
|
dispatch(toggleSettingStatus({ settingKey }));
|
||||||
break;
|
break;
|
||||||
case SETTINGS_NAMES.safesearch:
|
case SETTINGS_NAMES.safesearch:
|
||||||
if (status) {
|
successMessage = 'updated_save_search_toast';
|
||||||
successMessage = 'disabled_safe_search_toast';
|
await apiClient.updateSafesearch(status);
|
||||||
await apiClient.disableSafesearch();
|
dispatch(toggleSettingStatus({ settingKey, value: status }));
|
||||||
} else {
|
|
||||||
successMessage = 'enabled_save_search_toast';
|
|
||||||
await apiClient.enableSafesearch();
|
|
||||||
}
|
|
||||||
dispatch(toggleSettingStatus({ settingKey }));
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -71,7 +72,9 @@ export const initSettingsRequest = createAction('SETTINGS_INIT_REQUEST');
|
||||||
export const initSettingsFailure = createAction('SETTINGS_INIT_FAILURE');
|
export const initSettingsFailure = createAction('SETTINGS_INIT_FAILURE');
|
||||||
export const initSettingsSuccess = createAction('SETTINGS_INIT_SUCCESS');
|
export const initSettingsSuccess = createAction('SETTINGS_INIT_SUCCESS');
|
||||||
|
|
||||||
export const initSettings = (settingsList) => async (dispatch) => {
|
export const initSettings = (settingsList = {
|
||||||
|
safebrowsing: {}, parental: {},
|
||||||
|
}) => async (dispatch) => {
|
||||||
dispatch(initSettingsRequest());
|
dispatch(initSettingsRequest());
|
||||||
try {
|
try {
|
||||||
const safebrowsingStatus = await apiClient.getSafebrowsingStatus();
|
const safebrowsingStatus = await apiClient.getSafebrowsingStatus();
|
||||||
|
@ -80,7 +83,6 @@ export const initSettings = (settingsList) => async (dispatch) => {
|
||||||
const {
|
const {
|
||||||
safebrowsing,
|
safebrowsing,
|
||||||
parental,
|
parental,
|
||||||
safesearch,
|
|
||||||
} = settingsList;
|
} = settingsList;
|
||||||
const newSettingsList = {
|
const newSettingsList = {
|
||||||
safebrowsing: {
|
safebrowsing: {
|
||||||
|
@ -92,8 +94,7 @@ export const initSettings = (settingsList) => async (dispatch) => {
|
||||||
enabled: parentalStatus.enabled,
|
enabled: parentalStatus.enabled,
|
||||||
},
|
},
|
||||||
safesearch: {
|
safesearch: {
|
||||||
...safesearch,
|
...safesearchStatus,
|
||||||
enabled: safesearchStatus.enabled,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
dispatch(initSettingsSuccess({ settingsList: newSettingsList }));
|
dispatch(initSettingsSuccess({ settingsList: newSettingsList }));
|
||||||
|
|
|
@ -208,24 +208,40 @@ class Api {
|
||||||
// Safesearch
|
// Safesearch
|
||||||
SAFESEARCH_STATUS = { path: 'safesearch/status', method: 'GET' };
|
SAFESEARCH_STATUS = { path: 'safesearch/status', method: 'GET' };
|
||||||
|
|
||||||
SAFESEARCH_ENABLE = { path: 'safesearch/enable', method: 'POST' };
|
SAFESEARCH_UPDATE = { path: 'safesearch/settings', method: 'PUT' };
|
||||||
|
|
||||||
SAFESEARCH_DISABLE = { path: 'safesearch/disable', method: 'POST' };
|
|
||||||
|
|
||||||
getSafesearchStatus() {
|
getSafesearchStatus() {
|
||||||
const { path, method } = this.SAFESEARCH_STATUS;
|
const { path, method } = this.SAFESEARCH_STATUS;
|
||||||
return this.makeRequest(path, method);
|
return this.makeRequest(path, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
enableSafesearch() {
|
/**
|
||||||
const { path, method } = this.SAFESEARCH_ENABLE;
|
* interface SafeSearchConfig {
|
||||||
return this.makeRequest(path, method);
|
"enabled": boolean,
|
||||||
|
"bing": boolean,
|
||||||
|
"duckduckgo": boolean,
|
||||||
|
"google": boolean,
|
||||||
|
"pixabay": boolean,
|
||||||
|
"yandex": boolean,
|
||||||
|
"youtube": boolean
|
||||||
|
* }
|
||||||
|
* @param {*} data - SafeSearchConfig
|
||||||
|
* @returns 200 ok
|
||||||
|
*/
|
||||||
|
updateSafesearch(data) {
|
||||||
|
const { path, method } = this.SAFESEARCH_UPDATE;
|
||||||
|
return this.makeRequest(path, method, { data });
|
||||||
}
|
}
|
||||||
|
|
||||||
disableSafesearch() {
|
// enableSafesearch() {
|
||||||
const { path, method } = this.SAFESEARCH_DISABLE;
|
// const { path, method } = this.SAFESEARCH_ENABLE;
|
||||||
return this.makeRequest(path, method);
|
// return this.makeRequest(path, method);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// disableSafesearch() {
|
||||||
|
// const { path, method } = this.SAFESEARCH_DISABLE;
|
||||||
|
// return this.makeRequest(path, method);
|
||||||
|
// }
|
||||||
|
|
||||||
// Language
|
// Language
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { useDispatch, useSelector } from 'react-redux';
|
||||||
import ReactTable from 'react-table';
|
import ReactTable from 'react-table';
|
||||||
|
|
||||||
import { getAllBlockedServices } from '../../../../actions/services';
|
import { getAllBlockedServices } from '../../../../actions/services';
|
||||||
|
import { initSettings } from '../../../../actions';
|
||||||
import {
|
import {
|
||||||
splitByNewLine,
|
splitByNewLine,
|
||||||
countClientsStatistics,
|
countClientsStatistics,
|
||||||
|
@ -38,9 +39,13 @@ const ClientsTable = ({
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const services = useSelector((store) => store?.services);
|
const services = useSelector((store) => store?.services);
|
||||||
|
const globalSettings = useSelector((store) => store?.settings.settingsList) || {};
|
||||||
|
|
||||||
|
const { safesearch } = globalSettings;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(getAllBlockedServices());
|
dispatch(getAllBlockedServices());
|
||||||
|
dispatch(initSettings());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleFormAdd = (values) => {
|
const handleFormAdd = (values) => {
|
||||||
|
@ -107,6 +112,7 @@ const ClientsTable = ({
|
||||||
tags: [],
|
tags: [],
|
||||||
use_global_settings: true,
|
use_global_settings: true,
|
||||||
use_global_blocked_services: true,
|
use_global_blocked_services: true,
|
||||||
|
safe_search: { ...(safesearch || {}) },
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import Select from 'react-select';
|
||||||
import i18n from '../../../i18n';
|
import i18n from '../../../i18n';
|
||||||
import Tabs from '../../ui/Tabs';
|
import Tabs from '../../ui/Tabs';
|
||||||
import Examples from '../Dns/Upstream/Examples';
|
import Examples from '../Dns/Upstream/Examples';
|
||||||
import { toggleAllServices, trimLinesAndRemoveEmpty } from '../../../helpers/helpers';
|
import { toggleAllServices, trimLinesAndRemoveEmpty, captitalizeWords } from '../../../helpers/helpers';
|
||||||
import {
|
import {
|
||||||
renderInputField,
|
renderInputField,
|
||||||
renderGroupField,
|
renderGroupField,
|
||||||
|
@ -40,10 +40,6 @@ const settingsCheckboxes = [
|
||||||
name: 'parental_enabled',
|
name: 'parental_enabled',
|
||||||
placeholder: 'use_adguard_parental',
|
placeholder: 'use_adguard_parental',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'safesearch_enabled',
|
|
||||||
placeholder: 'enforce_safe_search',
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
const validate = (values) => {
|
const validate = (values) => {
|
||||||
const errors = {};
|
const errors = {};
|
||||||
|
@ -139,8 +135,12 @@ let Form = (props) => {
|
||||||
processingUpdating,
|
processingUpdating,
|
||||||
invalid,
|
invalid,
|
||||||
tagsOptions,
|
tagsOptions,
|
||||||
|
initialValues,
|
||||||
} = props;
|
} = props;
|
||||||
const services = useSelector((store) => store?.services);
|
const services = useSelector((store) => store?.services);
|
||||||
|
const { safe_search } = initialValues;
|
||||||
|
const safeSearchServices = { ...safe_search };
|
||||||
|
delete safeSearchServices.enabled;
|
||||||
|
|
||||||
const [activeTabLabel, setActiveTabLabel] = useState('settings');
|
const [activeTabLabel, setActiveTabLabel] = useState('settings');
|
||||||
|
|
||||||
|
@ -163,6 +163,28 @@ let Form = (props) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
<div className="form__group">
|
||||||
|
<Field
|
||||||
|
name="safe_search.enabled"
|
||||||
|
type="checkbox"
|
||||||
|
component={CheckboxField}
|
||||||
|
placeholder={t('enforce_safe_search')}
|
||||||
|
disabled={useGlobalSettings}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='form__group--inner'>
|
||||||
|
{Object.keys(safeSearchServices).map((searchKey) => (
|
||||||
|
<div key={searchKey}>
|
||||||
|
<Field
|
||||||
|
name={`safe_search.${searchKey}`}
|
||||||
|
type="checkbox"
|
||||||
|
component={CheckboxField}
|
||||||
|
placeholder={captitalizeWords(searchKey)}
|
||||||
|
disabled={useGlobalSettings}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>,
|
</div>,
|
||||||
},
|
},
|
||||||
block_services: {
|
block_services: {
|
||||||
|
@ -358,6 +380,7 @@ Form.propTypes = {
|
||||||
processingUpdating: PropTypes.bool.isRequired,
|
processingUpdating: PropTypes.bool.isRequired,
|
||||||
invalid: PropTypes.bool.isRequired,
|
invalid: PropTypes.bool.isRequired,
|
||||||
tagsOptions: PropTypes.array.isRequired,
|
tagsOptions: PropTypes.array.isRequired,
|
||||||
|
initialValues: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
const selector = formValueSelector(FORM_NAME.CLIENT);
|
const selector = formValueSelector(FORM_NAME.CLIENT);
|
||||||
|
|
|
@ -10,7 +10,7 @@ import Checkbox from '../ui/Checkbox';
|
||||||
import Loading from '../ui/Loading';
|
import Loading from '../ui/Loading';
|
||||||
import PageTitle from '../ui/PageTitle';
|
import PageTitle from '../ui/PageTitle';
|
||||||
import Card from '../ui/Card';
|
import Card from '../ui/Card';
|
||||||
import { getObjectKeysSorted } from '../../helpers/helpers';
|
import { getObjectKeysSorted, captitalizeWords } from '../../helpers/helpers';
|
||||||
import './Settings.css';
|
import './Settings.css';
|
||||||
|
|
||||||
const ORDER_KEY = 'order';
|
const ORDER_KEY = 'order';
|
||||||
|
@ -28,12 +28,6 @@ const SETTINGS = {
|
||||||
subtitle: 'use_adguard_parental_hint',
|
subtitle: 'use_adguard_parental_hint',
|
||||||
[ORDER_KEY]: 1,
|
[ORDER_KEY]: 1,
|
||||||
},
|
},
|
||||||
safesearch: {
|
|
||||||
enabled: false,
|
|
||||||
title: 'enforce_safe_search',
|
|
||||||
subtitle: 'enforce_save_search_hint',
|
|
||||||
[ORDER_KEY]: 2,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Settings extends Component {
|
class Settings extends Component {
|
||||||
|
@ -44,7 +38,7 @@ class Settings extends Component {
|
||||||
this.props.getFilteringStatus();
|
this.props.getFilteringStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSettings = (settings) => getObjectKeysSorted(settings, ORDER_KEY)
|
renderSettings = (settings) => getObjectKeysSorted(SETTINGS, ORDER_KEY)
|
||||||
.map((key) => {
|
.map((key) => {
|
||||||
const setting = settings[key];
|
const setting = settings[key];
|
||||||
const { enabled } = setting;
|
const { enabled } = setting;
|
||||||
|
@ -55,6 +49,35 @@ class Settings extends Component {
|
||||||
/>;
|
/>;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
renderSafeSearch = () => {
|
||||||
|
const { settings: { settingsList: { safesearch } } } = this.props;
|
||||||
|
const { enabled } = safesearch || {};
|
||||||
|
const searches = { ...(safesearch || {}) };
|
||||||
|
delete searches.enabled;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Checkbox
|
||||||
|
enabled={enabled}
|
||||||
|
title='enforce_safe_search'
|
||||||
|
subtitle='enforce_save_search_hint'
|
||||||
|
handleChange={({ target: { checked: enabled } }) => this.props.toggleSetting('safesearch', { ...safesearch, enabled })}
|
||||||
|
/>
|
||||||
|
<div className='form__group--inner'>
|
||||||
|
{Object.keys(searches).map((searchKey) => (
|
||||||
|
<Checkbox
|
||||||
|
key={searchKey}
|
||||||
|
enabled={searches[searchKey]}
|
||||||
|
title={captitalizeWords(searchKey)}
|
||||||
|
subtitle=''
|
||||||
|
disabled={!safesearch.enabled}
|
||||||
|
handleChange={({ target: { checked } }) => this.props.toggleSetting('safesearch', { ...safesearch, [searchKey]: checked })}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
settings,
|
settings,
|
||||||
|
@ -92,6 +115,7 @@ class Settings extends Component {
|
||||||
setFiltersConfig={setFiltersConfig}
|
setFiltersConfig={setFiltersConfig}
|
||||||
/>
|
/>
|
||||||
{this.renderSettings(settings.settingsList)}
|
{this.renderSettings(settings.settingsList)}
|
||||||
|
{this.renderSafeSearch()}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -11,13 +11,14 @@ class Checkbox extends Component {
|
||||||
subtitle,
|
subtitle,
|
||||||
enabled,
|
enabled,
|
||||||
handleChange,
|
handleChange,
|
||||||
|
disabled,
|
||||||
t,
|
t,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<div className="form__group form__group--checkbox">
|
<div className="form__group form__group--checkbox">
|
||||||
<label className="checkbox checkbox--settings">
|
<label className="checkbox checkbox--settings">
|
||||||
<span className="checkbox__marker"/>
|
<span className="checkbox__marker"/>
|
||||||
<input type="checkbox" className="checkbox__input" onChange={handleChange} checked={enabled}/>
|
<input type="checkbox" className="checkbox__input" onChange={handleChange} checked={enabled} disabled={disabled}/>
|
||||||
<span className="checkbox__label">
|
<span className="checkbox__label">
|
||||||
<span className="checkbox__label-text">
|
<span className="checkbox__label-text">
|
||||||
<span className="checkbox__label-title">{ t(title) }</span>
|
<span className="checkbox__label-title">{ t(title) }</span>
|
||||||
|
@ -35,6 +36,7 @@ Checkbox.propTypes = {
|
||||||
subtitle: PropTypes.string.isRequired,
|
subtitle: PropTypes.string.isRequired,
|
||||||
enabled: PropTypes.bool.isRequired,
|
enabled: PropTypes.bool.isRequired,
|
||||||
handleChange: PropTypes.func.isRequired,
|
handleChange: PropTypes.func.isRequired,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,11 @@ const settings = handleActions(
|
||||||
},
|
},
|
||||||
[actions.toggleSettingStatus]: (state, { payload }) => {
|
[actions.toggleSettingStatus]: (state, { payload }) => {
|
||||||
const { settingsList } = state;
|
const { settingsList } = state;
|
||||||
const { settingKey } = payload;
|
const { settingKey, value } = payload;
|
||||||
|
|
||||||
const setting = settingsList[settingKey];
|
const setting = settingsList[settingKey];
|
||||||
|
|
||||||
const newSetting = {
|
const newSetting = value || {
|
||||||
...setting,
|
...setting,
|
||||||
enabled: !setting.enabled,
|
enabled: !setting.enabled,
|
||||||
};
|
};
|
||||||
|
|
|
@ -461,6 +461,7 @@ func (d *DNSFilter) RegisterFilteringHandlers() {
|
||||||
registerHTTP(http.MethodPost, "/control/safesearch/enable", d.handleSafeSearchEnable)
|
registerHTTP(http.MethodPost, "/control/safesearch/enable", d.handleSafeSearchEnable)
|
||||||
registerHTTP(http.MethodPost, "/control/safesearch/disable", d.handleSafeSearchDisable)
|
registerHTTP(http.MethodPost, "/control/safesearch/disable", d.handleSafeSearchDisable)
|
||||||
registerHTTP(http.MethodGet, "/control/safesearch/status", d.handleSafeSearchStatus)
|
registerHTTP(http.MethodGet, "/control/safesearch/status", d.handleSafeSearchStatus)
|
||||||
|
registerHTTP(http.MethodPut, "/control/safesearch/settings", d.handleSafeSearchSettings)
|
||||||
|
|
||||||
registerHTTP(http.MethodGet, "/control/rewrite/list", d.handleRewriteList)
|
registerHTTP(http.MethodGet, "/control/rewrite/list", d.handleRewriteList)
|
||||||
registerHTTP(http.MethodPost, "/control/rewrite/add", d.handleRewriteAdd)
|
registerHTTP(http.MethodPost, "/control/rewrite/add", d.handleRewriteAdd)
|
||||||
|
|
|
@ -17,7 +17,7 @@ type SafeSearch interface {
|
||||||
// SafeSearchConfig is a struct with safe search related settings.
|
// SafeSearchConfig is a struct with safe search related settings.
|
||||||
type SafeSearchConfig struct {
|
type SafeSearchConfig struct {
|
||||||
// CustomResolver is the resolver used by safe search.
|
// CustomResolver is the resolver used by safe search.
|
||||||
CustomResolver Resolver `yaml:"-"`
|
CustomResolver Resolver `yaml:"-" json:"-"`
|
||||||
|
|
||||||
// Enabled indicates if safe search is enabled entirely.
|
// Enabled indicates if safe search is enabled entirely.
|
||||||
Enabled bool `yaml:"enabled" json:"enabled"`
|
Enabled bool `yaml:"enabled" json:"enabled"`
|
||||||
|
|
|
@ -1,29 +1,63 @@
|
||||||
package filtering
|
package filtering
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(d.kolyshev): Replace handlers below with the new API.
|
// handleSafeSearchEnable is the handler for POST /control/safesearch/enable
|
||||||
|
// HTTP API.
|
||||||
|
//
|
||||||
|
// Deprecated: Use handleSafeSearchSettings.
|
||||||
func (d *DNSFilter) handleSafeSearchEnable(w http.ResponseWriter, r *http.Request) {
|
func (d *DNSFilter) handleSafeSearchEnable(w http.ResponseWriter, r *http.Request) {
|
||||||
setProtectedBool(&d.confLock, &d.Config.SafeSearchConf.Enabled, true)
|
setProtectedBool(&d.confLock, &d.Config.SafeSearchConf.Enabled, true)
|
||||||
d.Config.ConfigModified()
|
d.Config.ConfigModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleSafeSearchDisable is the handler for POST /control/safesearch/disable
|
||||||
|
// HTTP API.
|
||||||
|
//
|
||||||
|
// Deprecated: Use handleSafeSearchSettings.
|
||||||
func (d *DNSFilter) handleSafeSearchDisable(w http.ResponseWriter, r *http.Request) {
|
func (d *DNSFilter) handleSafeSearchDisable(w http.ResponseWriter, r *http.Request) {
|
||||||
setProtectedBool(&d.confLock, &d.Config.SafeSearchConf.Enabled, false)
|
setProtectedBool(&d.confLock, &d.Config.SafeSearchConf.Enabled, false)
|
||||||
d.Config.ConfigModified()
|
d.Config.ConfigModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleSafeSearchStatus is the handler for GET /control/safesearch/status
|
||||||
|
// HTTP API.
|
||||||
func (d *DNSFilter) handleSafeSearchStatus(w http.ResponseWriter, r *http.Request) {
|
func (d *DNSFilter) handleSafeSearchStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
resp := &struct {
|
var resp SafeSearchConfig
|
||||||
Enabled bool `json:"enabled"`
|
func() {
|
||||||
}{
|
d.confLock.RLock()
|
||||||
Enabled: protectedBool(&d.confLock, &d.Config.SafeSearchConf.Enabled),
|
defer d.confLock.RUnlock()
|
||||||
}
|
|
||||||
|
resp = d.Config.SafeSearchConf
|
||||||
|
}()
|
||||||
|
|
||||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleSafeSearchSettings is the handler for PUT /control/safesearch/settings
|
||||||
|
// HTTP API.
|
||||||
|
func (d *DNSFilter) handleSafeSearchSettings(w http.ResponseWriter, r *http.Request) {
|
||||||
|
req := &SafeSearchConfig{}
|
||||||
|
err := json.NewDecoder(r.Body).Decode(req)
|
||||||
|
if err != nil {
|
||||||
|
aghhttp.Error(r, w, http.StatusBadRequest, "reading req: %s", err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func() {
|
||||||
|
d.confLock.Lock()
|
||||||
|
defer d.confLock.Unlock()
|
||||||
|
|
||||||
|
d.Config.SafeSearchConf = *req
|
||||||
|
}()
|
||||||
|
|
||||||
|
d.Config.ConfigModified()
|
||||||
|
|
||||||
|
aghhttp.OK(w)
|
||||||
|
}
|
||||||
|
|
|
@ -27,7 +27,8 @@ type clientJSON struct {
|
||||||
// the allowlist.
|
// the allowlist.
|
||||||
DisallowedRule *string `json:"disallowed_rule,omitempty"`
|
DisallowedRule *string `json:"disallowed_rule,omitempty"`
|
||||||
|
|
||||||
WHOISInfo *RuntimeClientWHOISInfo `json:"whois_info,omitempty"`
|
WHOISInfo *RuntimeClientWHOISInfo `json:"whois_info,omitempty"`
|
||||||
|
SafeSearchConf *filtering.SafeSearchConfig `json:"safe_search"`
|
||||||
|
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ type clientListJSON struct {
|
||||||
Tags []string `json:"supported_tags"`
|
Tags []string `json:"supported_tags"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// respond with information about configured clients
|
// handleGetClients is the handler for GET /control/clients HTTP API.
|
||||||
func (clients *clientsContainer) handleGetClients(w http.ResponseWriter, r *http.Request) {
|
func (clients *clientsContainer) handleGetClients(w http.ResponseWriter, r *http.Request) {
|
||||||
data := clientListJSON{}
|
data := clientListJSON{}
|
||||||
|
|
||||||
|
@ -88,32 +89,36 @@ func (clients *clientsContainer) handleGetClients(w http.ResponseWriter, r *http
|
||||||
_ = aghhttp.WriteJSONResponse(w, r, data)
|
_ = aghhttp.WriteJSONResponse(w, r, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert JSON object to Client object
|
// jsonToClient converts JSON object to Client object.
|
||||||
func jsonToClient(cj clientJSON) (c *Client) {
|
func jsonToClient(cj clientJSON) (c *Client) {
|
||||||
// TODO(d.kolyshev): Remove after cleaning the deprecated
|
var safeSearchConf filtering.SafeSearchConfig
|
||||||
// [clientJSON.SafeSearchEnabled] field.
|
if cj.SafeSearchConf != nil {
|
||||||
safeSearchConf := filtering.SafeSearchConfig{Enabled: cj.SafeSearchEnabled}
|
safeSearchConf = *cj.SafeSearchConf
|
||||||
|
} else {
|
||||||
|
// TODO(d.kolyshev): Remove after cleaning the deprecated
|
||||||
|
// [clientJSON.SafeSearchEnabled] field.
|
||||||
|
safeSearchConf = filtering.SafeSearchConfig{Enabled: cj.SafeSearchEnabled}
|
||||||
|
|
||||||
// Set default service flags for enabled safesearch.
|
// Set default service flags for enabled safesearch.
|
||||||
if safeSearchConf.Enabled {
|
if safeSearchConf.Enabled {
|
||||||
safeSearchConf.Bing = true
|
safeSearchConf.Bing = true
|
||||||
safeSearchConf.DuckDuckGo = true
|
safeSearchConf.DuckDuckGo = true
|
||||||
safeSearchConf.Google = true
|
safeSearchConf.Google = true
|
||||||
safeSearchConf.Pixabay = true
|
safeSearchConf.Pixabay = true
|
||||||
safeSearchConf.Yandex = true
|
safeSearchConf.Yandex = true
|
||||||
safeSearchConf.YouTube = true
|
safeSearchConf.YouTube = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
Name: cj.Name,
|
Name: cj.Name,
|
||||||
IDs: cj.IDs,
|
IDs: cj.IDs,
|
||||||
Tags: cj.Tags,
|
Tags: cj.Tags,
|
||||||
UseOwnSettings: !cj.UseGlobalSettings,
|
UseOwnSettings: !cj.UseGlobalSettings,
|
||||||
FilteringEnabled: cj.FilteringEnabled,
|
FilteringEnabled: cj.FilteringEnabled,
|
||||||
ParentalEnabled: cj.ParentalEnabled,
|
ParentalEnabled: cj.ParentalEnabled,
|
||||||
safeSearchConf: safeSearchConf,
|
SafeBrowsingEnabled: cj.SafeBrowsingEnabled,
|
||||||
SafeBrowsingEnabled: cj.SafeBrowsingEnabled,
|
safeSearchConf: safeSearchConf,
|
||||||
|
|
||||||
UseOwnBlockedServices: !cj.UseGlobalBlockedServices,
|
UseOwnBlockedServices: !cj.UseGlobalBlockedServices,
|
||||||
BlockedServices: cj.BlockedServices,
|
BlockedServices: cj.BlockedServices,
|
||||||
|
|
||||||
|
@ -121,7 +126,7 @@ func jsonToClient(cj clientJSON) (c *Client) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert Client object to JSON
|
// clientToJSON converts Client object to JSON.
|
||||||
func clientToJSON(c *Client) (cj *clientJSON) {
|
func clientToJSON(c *Client) (cj *clientJSON) {
|
||||||
// TODO(d.kolyshev): Remove after cleaning the deprecated
|
// TODO(d.kolyshev): Remove after cleaning the deprecated
|
||||||
// [clientJSON.SafeSearchEnabled] field.
|
// [clientJSON.SafeSearchEnabled] field.
|
||||||
|
@ -136,6 +141,7 @@ func clientToJSON(c *Client) (cj *clientJSON) {
|
||||||
FilteringEnabled: c.FilteringEnabled,
|
FilteringEnabled: c.FilteringEnabled,
|
||||||
ParentalEnabled: c.ParentalEnabled,
|
ParentalEnabled: c.ParentalEnabled,
|
||||||
SafeSearchEnabled: safeSearchConf.Enabled,
|
SafeSearchEnabled: safeSearchConf.Enabled,
|
||||||
|
SafeSearchConf: safeSearchConf,
|
||||||
SafeBrowsingEnabled: c.SafeBrowsingEnabled,
|
SafeBrowsingEnabled: c.SafeBrowsingEnabled,
|
||||||
|
|
||||||
UseGlobalBlockedServices: !c.UseOwnBlockedServices,
|
UseGlobalBlockedServices: !c.UseOwnBlockedServices,
|
||||||
|
@ -145,7 +151,7 @@ func clientToJSON(c *Client) (cj *clientJSON) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a new client
|
// handleAddClient is the handler for POST /control/clients/add HTTP API.
|
||||||
func (clients *clientsContainer) handleAddClient(w http.ResponseWriter, r *http.Request) {
|
func (clients *clientsContainer) handleAddClient(w http.ResponseWriter, r *http.Request) {
|
||||||
cj := clientJSON{}
|
cj := clientJSON{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&cj)
|
err := json.NewDecoder(r.Body).Decode(&cj)
|
||||||
|
@ -172,7 +178,7 @@ func (clients *clientsContainer) handleAddClient(w http.ResponseWriter, r *http.
|
||||||
onConfigModified()
|
onConfigModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove client
|
// handleDelClient is the handler for POST /control/clients/delete HTTP API.
|
||||||
func (clients *clientsContainer) handleDelClient(w http.ResponseWriter, r *http.Request) {
|
func (clients *clientsContainer) handleDelClient(w http.ResponseWriter, r *http.Request) {
|
||||||
cj := clientJSON{}
|
cj := clientJSON{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&cj)
|
err := json.NewDecoder(r.Body).Decode(&cj)
|
||||||
|
@ -202,7 +208,7 @@ type updateJSON struct {
|
||||||
Data clientJSON `json:"data"`
|
Data clientJSON `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update client's properties
|
// handleUpdateClient is the handler for POST /control/clients/update HTTP API.
|
||||||
func (clients *clientsContainer) handleUpdateClient(w http.ResponseWriter, r *http.Request) {
|
func (clients *clientsContainer) handleUpdateClient(w http.ResponseWriter, r *http.Request) {
|
||||||
dj := updateJSON{}
|
dj := updateJSON{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&dj)
|
err := json.NewDecoder(r.Body).Decode(&dj)
|
||||||
|
@ -229,7 +235,7 @@ func (clients *clientsContainer) handleUpdateClient(w http.ResponseWriter, r *ht
|
||||||
onConfigModified()
|
onConfigModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the list of clients by IP address list
|
// handleFindClient is the handler for GET /control/clients/find HTTP API.
|
||||||
func (clients *clientsContainer) handleFindClient(w http.ResponseWriter, r *http.Request) {
|
func (clients *clientsContainer) handleFindClient(w http.ResponseWriter, r *http.Request) {
|
||||||
q := r.URL.Query()
|
q := r.URL.Query()
|
||||||
data := []map[string]*clientJSON{}
|
data := []map[string]*clientJSON{}
|
||||||
|
|
|
@ -83,6 +83,78 @@ accept and return a JSON object with the following format:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## v0.107.27: API changes
|
||||||
|
|
||||||
|
### Deprecated HTTP APIs
|
||||||
|
|
||||||
|
The following HTTP APIs are deprecated:
|
||||||
|
|
||||||
|
* `POST /control/safesearch/enable` is deprecated. Use the new
|
||||||
|
`PUT /control/safesearch/settings`.
|
||||||
|
|
||||||
|
* `POST /control/safesearch/disable` is deprecated. Use the new
|
||||||
|
`PUT /control/safesearch/settings`.
|
||||||
|
|
||||||
|
### New HTTP API `PUT /control/safesearch/settings`
|
||||||
|
|
||||||
|
* The new `PUT /control/safesearch/settings` HTTP API allows safesearch
|
||||||
|
settings updates. It accepts a JSON object with the following format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"enabled": true,
|
||||||
|
"bing": false,
|
||||||
|
"duckduckgo": true,
|
||||||
|
"google": false,
|
||||||
|
"pixabay": false,
|
||||||
|
"yandex": true,
|
||||||
|
"youtube": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `GET /control/safesearch/status`
|
||||||
|
|
||||||
|
* The `control/safesearch/status` HTTP API has been changed. It now returns a
|
||||||
|
JSON object with the following format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"enabled": true,
|
||||||
|
"bing": false,
|
||||||
|
"duckduckgo": true,
|
||||||
|
"google": false,
|
||||||
|
"pixabay": false,
|
||||||
|
"yandex": true,
|
||||||
|
"youtube": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `/control/clients` HTTP APIs
|
||||||
|
|
||||||
|
The following HTTP APIs have been changed:
|
||||||
|
|
||||||
|
* `GET /control/clients`;
|
||||||
|
* `GET /control/clients/find?ip0=...&ip1=...&ip2=...`;
|
||||||
|
* `POST /control/clients/add`;
|
||||||
|
* `POST /control/clients/update`;
|
||||||
|
|
||||||
|
The `safesearch_enabled` field is deprecated. The new field `safe_search` has
|
||||||
|
been added to JSON objects. It has the following format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"enabled": true,
|
||||||
|
"bing": false,
|
||||||
|
"duckduckgo": true,
|
||||||
|
"google": false,
|
||||||
|
"pixabay": false,
|
||||||
|
"yandex": true,
|
||||||
|
"youtube": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## v0.107.23: API changes
|
## v0.107.23: API changes
|
||||||
|
|
||||||
### Experimental “beta” APIs removed
|
### Experimental “beta” APIs removed
|
||||||
|
|
|
@ -795,6 +795,7 @@
|
||||||
'sensitivity': 13
|
'sensitivity': 13
|
||||||
'/safesearch/enable':
|
'/safesearch/enable':
|
||||||
'post':
|
'post':
|
||||||
|
'deprecated': true
|
||||||
'tags':
|
'tags':
|
||||||
- 'safesearch'
|
- 'safesearch'
|
||||||
'operationId': 'safesearchEnable'
|
'operationId': 'safesearchEnable'
|
||||||
|
@ -804,6 +805,7 @@
|
||||||
'description': 'OK.'
|
'description': 'OK.'
|
||||||
'/safesearch/disable':
|
'/safesearch/disable':
|
||||||
'post':
|
'post':
|
||||||
|
'deprecated': true
|
||||||
'tags':
|
'tags':
|
||||||
- 'safesearch'
|
- 'safesearch'
|
||||||
'operationId': 'safesearchDisable'
|
'operationId': 'safesearchDisable'
|
||||||
|
@ -811,6 +813,20 @@
|
||||||
'responses':
|
'responses':
|
||||||
'200':
|
'200':
|
||||||
'description': 'OK.'
|
'description': 'OK.'
|
||||||
|
'/safesearch/settings':
|
||||||
|
'put':
|
||||||
|
'tags':
|
||||||
|
- 'safesearch'
|
||||||
|
'operationId': 'safesearchSettings'
|
||||||
|
'summary': 'Update safesearch settings'
|
||||||
|
'requestBody':
|
||||||
|
'content':
|
||||||
|
'application/json':
|
||||||
|
'schema':
|
||||||
|
'$ref': '#/components/schemas/SafeSearchConfig'
|
||||||
|
'responses':
|
||||||
|
'200':
|
||||||
|
'description': 'OK.'
|
||||||
'/safesearch/status':
|
'/safesearch/status':
|
||||||
'get':
|
'get':
|
||||||
'tags':
|
'tags':
|
||||||
|
@ -823,14 +839,7 @@
|
||||||
'content':
|
'content':
|
||||||
'application/json':
|
'application/json':
|
||||||
'schema':
|
'schema':
|
||||||
'type': 'object'
|
'$ref': '#/components/schemas/SafeSearchConfig'
|
||||||
'properties':
|
|
||||||
'enabled':
|
|
||||||
'type': 'boolean'
|
|
||||||
'examples':
|
|
||||||
'response':
|
|
||||||
'value':
|
|
||||||
'enabled': false
|
|
||||||
'/clients':
|
'/clients':
|
||||||
'get':
|
'get':
|
||||||
'tags':
|
'tags':
|
||||||
|
@ -2394,6 +2403,24 @@
|
||||||
- 'name'
|
- 'name'
|
||||||
- 'language'
|
- 'language'
|
||||||
- 'theme'
|
- 'theme'
|
||||||
|
'SafeSearchConfig':
|
||||||
|
'type': 'object'
|
||||||
|
'description': 'Safe search settings.'
|
||||||
|
'properties':
|
||||||
|
'enabled':
|
||||||
|
'type': 'boolean'
|
||||||
|
'bing':
|
||||||
|
'type': 'boolean'
|
||||||
|
'duckduckgo':
|
||||||
|
'type': 'boolean'
|
||||||
|
'google':
|
||||||
|
'type': 'boolean'
|
||||||
|
'pixabay':
|
||||||
|
'type': 'boolean'
|
||||||
|
'yandex':
|
||||||
|
'type': 'boolean'
|
||||||
|
'youtube':
|
||||||
|
'type': 'boolean'
|
||||||
'Client':
|
'Client':
|
||||||
'type': 'object'
|
'type': 'object'
|
||||||
'description': 'Client information.'
|
'description': 'Client information.'
|
||||||
|
@ -2416,7 +2443,10 @@
|
||||||
'safebrowsing_enabled':
|
'safebrowsing_enabled':
|
||||||
'type': 'boolean'
|
'type': 'boolean'
|
||||||
'safesearch_enabled':
|
'safesearch_enabled':
|
||||||
|
'deprecated': true
|
||||||
'type': 'boolean'
|
'type': 'boolean'
|
||||||
|
'safe_search':
|
||||||
|
'$ref': '#/components/schemas/SafeSearchConfig'
|
||||||
'use_global_blocked_services':
|
'use_global_blocked_services':
|
||||||
'type': 'boolean'
|
'type': 'boolean'
|
||||||
'blocked_services':
|
'blocked_services':
|
||||||
|
@ -2477,6 +2507,7 @@
|
||||||
'parental_enabled': true
|
'parental_enabled': true
|
||||||
'safebrowsing_enabled': true
|
'safebrowsing_enabled': true
|
||||||
'safesearch_enabled': true
|
'safesearch_enabled': true
|
||||||
|
'safe_search': {}
|
||||||
'use_global_blocked_services': true
|
'use_global_blocked_services': true
|
||||||
'blocked_services': null
|
'blocked_services': null
|
||||||
'upstreams': null
|
'upstreams': null
|
||||||
|
@ -2491,6 +2522,7 @@
|
||||||
'parental_enabled': true
|
'parental_enabled': true
|
||||||
'safebrowsing_enabled': true
|
'safebrowsing_enabled': true
|
||||||
'safesearch_enabled': true
|
'safesearch_enabled': true
|
||||||
|
'safe_search': {}
|
||||||
'use_global_blocked_services': true
|
'use_global_blocked_services': true
|
||||||
'blocked_services': null
|
'blocked_services': null
|
||||||
'upstreams': null
|
'upstreams': null
|
||||||
|
@ -2551,7 +2583,10 @@
|
||||||
'safebrowsing_enabled':
|
'safebrowsing_enabled':
|
||||||
'type': 'boolean'
|
'type': 'boolean'
|
||||||
'safesearch_enabled':
|
'safesearch_enabled':
|
||||||
|
'deprecated': true
|
||||||
'type': 'boolean'
|
'type': 'boolean'
|
||||||
|
'safe_search':
|
||||||
|
'$ref': '#/components/schemas/SafeSearchConfig'
|
||||||
'use_global_blocked_services':
|
'use_global_blocked_services':
|
||||||
'type': 'boolean'
|
'type': 'boolean'
|
||||||
'blocked_services':
|
'blocked_services':
|
||||||
|
|
Loading…
Reference in New Issue