mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2024-11-22 21:15:35 +03:00
Merge: * /control/version.json: add "recheck_now" parameter
Close #815 * commit 'd2258cb66de32092f145f2803a7be3d7869970f2': * openapi.yaml: update /version.json + client: add button for check updates * /control/version.json: add "recheck_now" parameter
This commit is contained in:
commit
b4b11406cf
15 changed files with 133 additions and 49 deletions
|
@ -257,7 +257,11 @@ Server can only auto-update if the current version is equal or higher than `self
|
|||
|
||||
Request:
|
||||
|
||||
GET /control/version.json
|
||||
POST /control/version.json
|
||||
|
||||
{
|
||||
"recheck_now": true | false // if false, server will check for a new version data only once in several hours
|
||||
}
|
||||
|
||||
Response:
|
||||
|
||||
|
|
|
@ -312,5 +312,7 @@
|
|||
"access_disallowed_desc": "A list of CIDR or IP addresses. If configured, AdGuard Home will drop requests from these IP addresses.",
|
||||
"access_blocked_title": "Blocked domains",
|
||||
"access_blocked_desc": "Don't confuse this with filters. AdGuard Home will drop DNS queries with these domains in query's question.",
|
||||
"access_settings_saved": "Access settings successfully saved"
|
||||
"access_settings_saved": "Access settings successfully saved",
|
||||
"updates_checked": "Updates successfully checked",
|
||||
"check_updates_now": "Check updates now"
|
||||
}
|
|
@ -145,11 +145,14 @@ export const getVersionRequest = createAction('GET_VERSION_REQUEST');
|
|||
export const getVersionFailure = createAction('GET_VERSION_FAILURE');
|
||||
export const getVersionSuccess = createAction('GET_VERSION_SUCCESS');
|
||||
|
||||
export const getVersion = () => async (dispatch) => {
|
||||
export const getVersion = (recheck = false) => async (dispatch) => {
|
||||
dispatch(getVersionRequest());
|
||||
try {
|
||||
const newVersion = await apiClient.getGlobalVersion();
|
||||
const newVersion = await apiClient.getGlobalVersion({ recheck_now: recheck });
|
||||
dispatch(getVersionSuccess(newVersion));
|
||||
if (recheck) {
|
||||
dispatch(addSuccessToast('updates_checked'));
|
||||
}
|
||||
} catch (error) {
|
||||
dispatch(addErrorToast({ error }));
|
||||
dispatch(getVersionFailure());
|
||||
|
|
|
@ -36,7 +36,7 @@ export default class Api {
|
|||
GLOBAL_QUERY_LOG_DISABLE = { path: 'querylog_disable', method: 'POST' };
|
||||
GLOBAL_SET_UPSTREAM_DNS = { path: 'set_upstreams_config', method: 'POST' };
|
||||
GLOBAL_TEST_UPSTREAM_DNS = { path: 'test_upstream_dns', method: 'POST' };
|
||||
GLOBAL_VERSION = { path: 'version.json', method: 'GET' };
|
||||
GLOBAL_VERSION = { path: 'version.json', method: 'POST' };
|
||||
GLOBAL_ENABLE_PROTECTION = { path: 'enable_protection', method: 'POST' };
|
||||
GLOBAL_DISABLE_PROTECTION = { path: 'disable_protection', method: 'POST' };
|
||||
GLOBAL_UPDATE = { path: 'update', method: 'POST' };
|
||||
|
@ -125,9 +125,13 @@ export default class Api {
|
|||
return this.makeRequest(path, method, config);
|
||||
}
|
||||
|
||||
getGlobalVersion() {
|
||||
getGlobalVersion(data) {
|
||||
const { path, method } = this.GLOBAL_VERSION;
|
||||
return this.makeRequest(path, method);
|
||||
const config = {
|
||||
data,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
};
|
||||
return this.makeRequest(path, method, config);
|
||||
}
|
||||
|
||||
enableGlobalProtection() {
|
||||
|
|
|
@ -65,8 +65,7 @@ class App extends Component {
|
|||
|
||||
render() {
|
||||
const { dashboard, encryption } = this.props;
|
||||
const updateAvailable =
|
||||
!dashboard.processingVersions && dashboard.isCoreRunning && dashboard.isUpdateAvailable;
|
||||
const updateAvailable = dashboard.isCoreRunning && dashboard.isUpdateAvailable;
|
||||
|
||||
return (
|
||||
<HashRouter hashType="noslash">
|
||||
|
|
|
@ -50,8 +50,26 @@ class Dashboard extends Component {
|
|||
dashboard.processingClients ||
|
||||
dashboard.processingTopStats;
|
||||
|
||||
const refreshFullButton = <button type="button" className="btn btn-outline-primary btn-sm" onClick={() => this.getAllStats()}><Trans>refresh_statics</Trans></button>;
|
||||
const refreshButton = <button type="button" className="btn btn-outline-primary btn-sm card-refresh" onClick={() => this.getAllStats()} />;
|
||||
const refreshFullButton = (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline-primary btn-sm"
|
||||
onClick={() => this.getAllStats()}
|
||||
>
|
||||
<Trans>refresh_statics</Trans>
|
||||
</button>
|
||||
);
|
||||
const refreshButton = (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-icon btn-outline-primary btn-sm"
|
||||
onClick={() => this.getAllStats()}
|
||||
>
|
||||
<svg className="icons">
|
||||
<use xlinkHref="#refresh" />
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
|
|
|
@ -75,7 +75,11 @@
|
|||
}
|
||||
|
||||
.nav-version__value {
|
||||
max-width: 110px;
|
||||
font-weight: 600;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.nav-version__link {
|
||||
|
@ -85,6 +89,11 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav-version__text {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.header-brand-img {
|
||||
height: 32px;
|
||||
}
|
||||
|
|
|
@ -4,12 +4,26 @@ import { Trans, withNamespaces } from 'react-i18next';
|
|||
|
||||
import { getDnsAddress } from '../../helpers/helpers';
|
||||
|
||||
function Version(props) {
|
||||
const { dnsVersion, dnsAddresses, dnsPort } = props;
|
||||
const Version = (props) => {
|
||||
const {
|
||||
dnsVersion, dnsAddresses, dnsPort, processingVersion, t,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div className="nav-version">
|
||||
<div className="nav-version__text">
|
||||
<Trans>version</Trans>: <span className="nav-version__value">{dnsVersion}</span>
|
||||
<Trans>version</Trans>: <span className="nav-version__value" title={dnsVersion}>{dnsVersion}</span>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-icon btn-icon-sm btn-outline-primary btn-sm ml-2"
|
||||
onClick={() => props.getVersion(true)}
|
||||
disabled={processingVersion}
|
||||
title={t('check_updates_now')}
|
||||
>
|
||||
<svg className="icons">
|
||||
<use xlinkHref="#refresh" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div className="nav-version__link">
|
||||
<div className="popover__trigger popover__trigger--address">
|
||||
|
@ -17,20 +31,23 @@ function Version(props) {
|
|||
</div>
|
||||
<div className="popover__body popover__body--address">
|
||||
<div className="popover__list">
|
||||
{dnsAddresses
|
||||
.map(ip => <li key={ip}>{getDnsAddress(ip, dnsPort)}</li>)
|
||||
}
|
||||
{dnsAddresses.map(ip => (
|
||||
<li key={ip}>{getDnsAddress(ip, dnsPort)}</li>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Version.propTypes = {
|
||||
dnsVersion: PropTypes.string.isRequired,
|
||||
dnsAddresses: PropTypes.array.isRequired,
|
||||
dnsPort: PropTypes.number.isRequired,
|
||||
getVersion: PropTypes.func.isRequired,
|
||||
processingVersion: PropTypes.bool.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default withNamespaces()(Version);
|
||||
|
|
|
@ -23,7 +23,7 @@ class Header extends Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { dashboard } = this.props;
|
||||
const { dashboard, getVersion, location } = this.props;
|
||||
const { isMenuOpen } = this.state;
|
||||
const badgeClass = classnames({
|
||||
'badge dns-status': true,
|
||||
|
@ -51,7 +51,7 @@ class Header extends Component {
|
|||
</div>
|
||||
</div>
|
||||
<Menu
|
||||
location={this.props.location}
|
||||
location={location}
|
||||
isMenuOpen={isMenuOpen}
|
||||
toggleMenuOpen={this.toggleMenuOpen}
|
||||
closeMenu={this.closeMenu}
|
||||
|
@ -59,7 +59,8 @@ class Header extends Component {
|
|||
{!dashboard.processing &&
|
||||
<div className="col col-sm-6 col-lg-3">
|
||||
<Version
|
||||
{ ...this.props.dashboard }
|
||||
{ ...dashboard }
|
||||
getVersion={getVersion}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
@ -71,8 +72,9 @@ class Header extends Component {
|
|||
}
|
||||
|
||||
Header.propTypes = {
|
||||
dashboard: PropTypes.object,
|
||||
location: PropTypes.object,
|
||||
dashboard: PropTypes.object.isRequired,
|
||||
location: PropTypes.object.isRequired,
|
||||
getVersion: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default withNamespaces()(Header);
|
||||
|
|
|
@ -88,3 +88,10 @@
|
|||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.btn-icon-sm {
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
min-width: 23px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
|
|
@ -33,21 +33,6 @@
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.card-refresh {
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
background-size: 14px;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjI0IiBzdHJva2U9IiM0NjdmY2YiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtMjMgNHY2aC02Ii8+PHBhdGggZD0ibTEgMjB2LTZoNiIvPjxwYXRoIGQ9Im0zLjUxIDlhOSA5IDAgMCAxIDE0Ljg1LTMuMzZsNC42NCA0LjM2bS0yMiA0IDQuNjQgNC4zNmE5IDkgMCAwIDAgMTQuODUtMy4zNiIvPjwvc3ZnPg==");
|
||||
}
|
||||
|
||||
.card-refresh:hover,
|
||||
.card-refresh:not(:disabled):not(.disabled):active,
|
||||
.card-refresh:focus:active {
|
||||
background-image: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjI0IiBzdHJva2U9IiNmZmYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtMjMgNHY2aC02Ii8+PHBhdGggZD0ibTEgMjB2LTZoNiIvPjxwYXRoIGQ9Im0zLjUxIDlhOSA5IDAgMCAxIDE0Ljg1LTMuMzZsNC42NCA0LjM2bS0yMiA0IDQuNjQgNC4zNmE5IDkgMCAwIDAgMTQuODUtMy4zNiIvPjwvc3ZnPg==");
|
||||
}
|
||||
|
||||
.card-title-stats {
|
||||
font-size: 13px;
|
||||
color: #9aa0ac;
|
||||
|
|
|
@ -55,6 +55,10 @@ const Icons = () => (
|
|||
<symbol id="settings" viewBox="0 0 24 24" stroke="currentColor" fill="none" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2">
|
||||
<circle cx="12" cy="12" r="3"/><path d="m19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1 -2.83 0l-.06-.06a1.65 1.65 0 0 0 -1.82-.33 1.65 1.65 0 0 0 -1 1.51v.17a2 2 0 0 1 -2 2 2 2 0 0 1 -2-2v-.09a1.65 1.65 0 0 0 -1.08-1.51 1.65 1.65 0 0 0 -1.82.33l-.06.06a2 2 0 0 1 -2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0 -1.51-1h-.17a2 2 0 0 1 -2-2 2 2 0 0 1 2-2h.09a1.65 1.65 0 0 0 1.51-1.08 1.65 1.65 0 0 0 -.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33h.08a1.65 1.65 0 0 0 1-1.51v-.17a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0 -.33 1.82v.08a1.65 1.65 0 0 0 1.51 1h.17a2 2 0 0 1 2 2 2 2 0 0 1 -2 2h-.09a1.65 1.65 0 0 0 -1.51 1z"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="refresh" viewBox="0 0 24 24" stroke="currentColor" fill="none" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2">
|
||||
<path d="M23 4v6h-6M1 20v-6h6"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
|
||||
</symbol>
|
||||
</svg>
|
||||
);
|
||||
|
||||
|
|
|
@ -137,6 +137,7 @@ const dashboard = handleActions({
|
|||
newVersion,
|
||||
canAutoUpdate,
|
||||
isUpdateAvailable: true,
|
||||
processingVersion: false,
|
||||
};
|
||||
return newState;
|
||||
}
|
||||
|
|
|
@ -51,6 +51,10 @@ func getVersionResp(data []byte) []byte {
|
|||
return d
|
||||
}
|
||||
|
||||
type getVersionJSONRequest struct {
|
||||
RecheckNow bool `json:"recheck_now"`
|
||||
}
|
||||
|
||||
// Get the latest available version from the Internet
|
||||
func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
|
||||
log.Tracef("%s %v", r.Method, r.URL)
|
||||
|
@ -60,19 +64,29 @@ func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
controlLock.Lock()
|
||||
cached := now.Sub(versionCheckLastTime) <= versionCheckPeriod && len(versionCheckJSON) != 0
|
||||
data := versionCheckJSON
|
||||
controlLock.Unlock()
|
||||
|
||||
if cached {
|
||||
// return cached copy
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(getVersionResp(data))
|
||||
req := getVersionJSONRequest{}
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
httpError(w, http.StatusBadRequest, "JSON parse: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
if !req.RecheckNow {
|
||||
controlLock.Lock()
|
||||
cached := now.Sub(versionCheckLastTime) <= versionCheckPeriod && len(versionCheckJSON) != 0
|
||||
data := versionCheckJSON
|
||||
controlLock.Unlock()
|
||||
|
||||
if cached {
|
||||
log.Tracef("Returning cached data")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(getVersionResp(data))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
log.Tracef("Downloading data from %s", versionCheckURL)
|
||||
resp, err := client.Get(versionCheckURL)
|
||||
if err != nil {
|
||||
httpError(w, http.StatusBadGateway, "Couldn't get version check json from %s: %T %s\n", versionCheckURL, err, err)
|
||||
|
|
|
@ -135,11 +135,19 @@ paths:
|
|||
"192.168.1.104:53535": "Couldn't communicate with DNS server"
|
||||
|
||||
/version.json:
|
||||
get:
|
||||
post:
|
||||
tags:
|
||||
- global
|
||||
operationId: getVersionJson
|
||||
summary: 'Gets information about the latest available version of AdGuard'
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- in: "body"
|
||||
name: "body"
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/GetVersionRequest"
|
||||
produces:
|
||||
- 'application/json'
|
||||
responses:
|
||||
|
@ -994,6 +1002,13 @@ definitions:
|
|||
example:
|
||||
- '||example.org^'
|
||||
- '||example.com^'
|
||||
GetVersionRequest:
|
||||
type: "object"
|
||||
description: "/version.json request data"
|
||||
properties:
|
||||
recheck_now:
|
||||
description: "If false, server will check for a new version data only once in several hours"
|
||||
type: "boolean"
|
||||
VersionInfo:
|
||||
type: "object"
|
||||
description: "Information about the latest available version of AdGuard Home"
|
||||
|
|
Loading…
Reference in a new issue