From e2c26ec554e4cf38b424fc02c1369a6768ef78ab Mon Sep 17 00:00:00 2001 From: Simon Zolin Date: Mon, 14 Oct 2019 15:55:58 +0300 Subject: [PATCH 1/4] * API changes * filtering_info -> filtering/status * filtering_config -> filtering/config --- AGHTechDoc.md | 24 ++++++++------------ home/control_filtering.go | 6 ++--- openapi/openapi.yaml | 46 ++++++++++++++++++++++++--------------- querylog/qlog_http.go | 44 ++++++++++++++++++------------------- 4 files changed, 61 insertions(+), 59 deletions(-) diff --git a/AGHTechDoc.md b/AGHTechDoc.md index c6e70391..adf483d0 100644 --- a/AGHTechDoc.md +++ b/AGHTechDoc.md @@ -1044,20 +1044,14 @@ We store data for a limited amount of time - the log file is automatically rotat Request: - POST /control/querylog + GET /control/querylog + ?older_than=2006-01-02T15:04:05.999999999Z07:00 + &filter_domain=... + &filter_client=... + &filter_question_type=A | AAAA + &filter_response_status= | filtered - { - older_than: "2006-01-02T15:04:05.999999999Z07:00" // must be "" for the first request - - filter:{ - domain: "..." - client: "..." - question_type: "A" | "AAAA" - response_status: "" | "filtered" - } - } - -If `older_than` value is set, server returns the next chunk of entries that are older than this time stamp. This setting is used for paging. UI sets this value to `""` on the first request and gets the latest log entries. To get the older entries, UI sets this value to the timestamp of the last (the oldest) entry from the previous response from Server. +If `older_than` value is set, server returns the next chunk of entries that are older than this time stamp. This setting is used for paging. UI sets the empty value on the first request and gets the latest log entries. To get the older entries, UI sets this value to the timestamp of the last (the oldest) entry from the previous response from Server. If "filter" settings are set, server returns only entries that match the specified request. @@ -1143,7 +1137,7 @@ As a result of the update procedure, all enabled filter files are written to dis Request: - GET /control/filtering_info + GET /control/filtering/status Response: @@ -1171,7 +1165,7 @@ Response: Request: - POST /control/filtering_config + POST /control/filtering/config { "enabled": true | false diff --git a/home/control_filtering.go b/home/control_filtering.go index 2eddaf13..92a0a01b 100644 --- a/home/control_filtering.go +++ b/home/control_filtering.go @@ -204,7 +204,7 @@ type filteringConfig struct { } // Get filtering configuration -func handleFilteringInfo(w http.ResponseWriter, r *http.Request) { +func handleFilteringStatus(w http.ResponseWriter, r *http.Request) { resp := filteringConfig{} config.RLock() resp.Enabled = config.DNS.FilteringEnabled @@ -261,8 +261,8 @@ func handleFilteringConfig(w http.ResponseWriter, r *http.Request) { // RegisterFilteringHandlers - register handlers func RegisterFilteringHandlers() { - httpRegister(http.MethodGet, "/control/filtering_info", handleFilteringInfo) - httpRegister(http.MethodPost, "/control/filtering_config", handleFilteringConfig) + httpRegister(http.MethodGet, "/control/filtering/status", handleFilteringStatus) + httpRegister(http.MethodPost, "/control/filtering/config", handleFilteringConfig) httpRegister(http.MethodPost, "/control/filtering/add_url", handleFilteringAddURL) httpRegister(http.MethodPost, "/control/filtering/remove_url", handleFilteringRemoveURL) httpRegister(http.MethodPost, "/control/filtering/set_url", handleFilteringSetURL) diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index ffb7fc4a..36df7793 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -176,16 +176,34 @@ paths: # -------------------------------------------------- /querylog: - post: + get: tags: - log operationId: queryLog summary: 'Get DNS server query log' parameters: - - in: "body" - name: "body" - schema: - $ref: '#/definitions/QueryLogRequest' + - name: older_than + in: query + type: string + - name: filter_domain + in: query + type: string + description: "Filter by domain name" + - name: filter_client + in: query + type: string + description: "Filter by client" + - name: filter_question_type + in: query + type: string + description: "Filter by question type" + - name: filter_response_status + in: query + type: string + description: "Filter by response status" + enum: + - + - filtered responses: 200: description: OK @@ -438,19 +456,19 @@ paths: # Filtering status methods # -------------------------------------------------- - /filtering_info: + /filtering/status: get: tags: - filtering - operationId: filteringInfo + operationId: filteringStatus summary: 'Get filtering parameters' responses: 200: description: OK schema: - $ref: "#/definitions/FilterInfo" + $ref: "#/definitions/FilterStatus" - /filtering_config: + /filtering/config: post: tags: - filtering @@ -1063,7 +1081,7 @@ definitions: type: "string" example: "https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt" - FilterInfo: + FilterStatus: type: "object" description: "Filtering settings" properties: @@ -1400,14 +1418,6 @@ definitions: items: $ref: "#/definitions/QueryLogItem" - QueryLogRequest: - type: "object" - description: "Query log request data" - properties: - offset: - type: "integer" - example: 1234 - QueryLogConfig: type: "object" description: "Query log configuration" diff --git a/querylog/qlog_http.go b/querylog/qlog_http.go index 1b06abc9..d48cd053 100644 --- a/querylog/qlog_http.go +++ b/querylog/qlog_http.go @@ -18,16 +18,12 @@ func httpError(r *http.Request, w http.ResponseWriter, code int, format string, http.Error(w, text, code) } -type filterJSON struct { - Domain string `json:"domain"` - Client string `json:"client"` - QuestionType string `json:"question_type"` - ResponseStatus string `json:"response_status"` -} - type request struct { - OlderThan string `json:"older_than"` - Filter filterJSON `json:"filter"` + olderThan string + filterDomain string + filterClient string + filterQuestionType string + filterResponseStatus string } // "value" -> value, return TRUE @@ -41,20 +37,22 @@ func getDoubleQuotesEnclosedValue(s *string) bool { } func (l *queryLog) handleQueryLog(w http.ResponseWriter, r *http.Request) { + var err error req := request{} - err := json.NewDecoder(r.Body).Decode(&req) - if err != nil { - httpError(r, w, http.StatusBadRequest, "json decode: %s", err) - return - } + q := r.URL.Query() + req.olderThan = q.Get("older_than") + req.filterDomain = q.Get("filter_domain") + req.filterClient = q.Get("filter_client") + req.filterQuestionType = q.Get("filter_question_type") + req.filterResponseStatus = q.Get("filter_response_status") params := getDataParams{ - Domain: req.Filter.Domain, - Client: req.Filter.Client, + Domain: req.filterDomain, + Client: req.filterClient, ResponseStatus: responseStatusAll, } - if len(req.OlderThan) != 0 { - params.OlderThan, err = time.Parse(time.RFC3339Nano, req.OlderThan) + if len(req.olderThan) != 0 { + params.OlderThan, err = time.Parse(time.RFC3339Nano, req.olderThan) if err != nil { httpError(r, w, http.StatusBadRequest, "invalid time stamp: %s", err) return @@ -68,8 +66,8 @@ func (l *queryLog) handleQueryLog(w http.ResponseWriter, r *http.Request) { params.StrictMatchClient = true } - if len(req.Filter.QuestionType) != 0 { - qtype, ok := dns.StringToType[req.Filter.QuestionType] + if len(req.filterQuestionType) != 0 { + qtype, ok := dns.StringToType[req.filterQuestionType] if !ok { httpError(r, w, http.StatusBadRequest, "invalid question_type") return @@ -77,8 +75,8 @@ func (l *queryLog) handleQueryLog(w http.ResponseWriter, r *http.Request) { params.QuestionType = qtype } - if len(req.Filter.ResponseStatus) != 0 { - switch req.Filter.ResponseStatus { + if len(req.filterResponseStatus) != 0 { + switch req.filterResponseStatus { case "filtered": params.ResponseStatus = responseStatusFiltered default: @@ -155,7 +153,7 @@ func (l *queryLog) handleQueryLogConfig(w http.ResponseWriter, r *http.Request) // Register web handlers func (l *queryLog) initWeb() { - l.conf.HTTPRegister("POST", "/control/querylog", l.handleQueryLog) + l.conf.HTTPRegister("GET", "/control/querylog", l.handleQueryLog) l.conf.HTTPRegister("GET", "/control/querylog_info", l.handleQueryLogInfo) l.conf.HTTPRegister("POST", "/control/querylog_clear", l.handleQueryLogClear) l.conf.HTTPRegister("POST", "/control/querylog_config", l.handleQueryLogConfig) From 92b6adbdc1a1dcfd5e369934db38236996d1f282 Mon Sep 17 00:00:00 2001 From: Simon Zolin Date: Mon, 14 Oct 2019 15:56:14 +0300 Subject: [PATCH 2/4] * openapi: minor --- openapi/openapi.yaml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 36df7793..3f1474cb 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -2,7 +2,7 @@ swagger: '2.0' info: title: 'AdGuard Home' description: 'AdGuard Home REST API. Admin web interface is built on top of this REST API.' - version: 0.98.1 + version: '0.99' schemes: - http basePath: /control @@ -286,7 +286,6 @@ paths: description: OK schema: $ref: "#/definitions/StatsConfig" - description: OK /stats_config: post: @@ -552,13 +551,6 @@ paths: This should work as intended, a `force` parameter is offered as last-resort attempt to make filter lists fresh. If you ever find yourself using `force` to make something work that otherwise wont, this is a bug and report it accordingly. - - parameters: - - - name: force - in: query - type: boolean - description: 'If any value is set, ignore cache and force re-download of all filters' responses: 200: description: OK with how many filters were actually updated From 7b29c56791ab51ac934e8a4e3438b87b2fc82a84 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Tue, 15 Oct 2019 12:28:49 +0300 Subject: [PATCH 3/4] + client: use search params for querylog request --- client/package-lock.json | 5 +++++ client/package.json | 3 ++- client/src/actions/queryLogs.js | 2 +- client/src/api/Api.js | 19 +++++++++---------- client/src/components/Logs/index.js | 8 ++++---- client/src/helpers/constants.js | 8 ++++---- client/src/helpers/helpers.js | 7 +++++++ 7 files changed, 32 insertions(+), 20 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 8419dae8..697279db 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -12725,6 +12725,11 @@ "requires-port": "^1.0.0" } }, + "url-polyfill": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/url-polyfill/-/url-polyfill-1.1.7.tgz", + "integrity": "sha512-ZrAxYWCREjmMtL8gSbSiKKLZZticgihCvVBtrFbUVpyoETt8GQJeG2okMWA8XryDAaHMjJfhnc+rnhXRbI4DXA==" + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", diff --git a/client/package.json b/client/package.json index 7c2b1d18..93620f74 100644 --- a/client/package.json +++ b/client/package.json @@ -33,7 +33,8 @@ "redux-actions": "^2.4.0", "redux-form": "^7.4.2", "redux-thunk": "^2.3.0", - "svg-url-loader": "^2.3.2" + "svg-url-loader": "^2.3.2", + "url-polyfill": "^1.1.7" }, "devDependencies": { "autoprefixer": "^8.6.3", diff --git a/client/src/actions/queryLogs.js b/client/src/actions/queryLogs.js index 20672cf6..09e93a60 100644 --- a/client/src/actions/queryLogs.js +++ b/client/src/actions/queryLogs.js @@ -15,7 +15,7 @@ export const getLogs = config => async (dispatch) => { dispatch(getLogsRequest()); try { const { filter, lastRowTime: older_than } = config; - const logs = normalizeLogs(await apiClient.getQueryLog({ filter, older_than })); + const logs = normalizeLogs(await apiClient.getQueryLog({ ...filter, older_than })); dispatch(getLogsSuccess({ logs, ...config })); } catch (error) { dispatch(addErrorToast({ error })); diff --git a/client/src/api/Api.js b/client/src/api/Api.js index 9cd0e650..c067d3f3 100644 --- a/client/src/api/Api.js +++ b/client/src/api/Api.js @@ -1,5 +1,7 @@ import axios from 'axios'; +import { getPathWithQueryParams } from '../helpers/helpers'; + class Api { baseUrl = 'control'; @@ -90,16 +92,16 @@ class Api { } // Filtering - FILTERING_INFO = { path: 'filtering_info', method: 'GET' }; + FILTERING_STATUS = { path: 'filtering/status', method: 'GET' }; FILTERING_ADD_FILTER = { path: 'filtering/add_url', method: 'POST' }; FILTERING_REMOVE_FILTER = { path: 'filtering/remove_url', method: 'POST' }; FILTERING_SET_RULES = { path: 'filtering/set_rules', method: 'POST' }; FILTERING_REFRESH = { path: 'filtering/refresh', method: 'POST' }; FILTERING_SET_URL = { path: 'filtering/set_url', method: 'POST' }; - FILTERING_CONFIG = { path: 'filtering_config', method: 'POST' }; + FILTERING_CONFIG = { path: 'filtering/config', method: 'POST' }; getFilteringStatus() { - const { path, method } = this.FILTERING_INFO; + const { path, method } = this.FILTERING_STATUS; return this.makeRequest(path, method); } @@ -482,18 +484,15 @@ class Api { } // Query log - GET_QUERY_LOG = { path: 'querylog', method: 'POST' }; + GET_QUERY_LOG = { path: 'querylog', method: 'GET' }; QUERY_LOG_CONFIG = { path: 'querylog_config', method: 'POST' }; QUERY_LOG_INFO = { path: 'querylog_info', method: 'GET' }; QUERY_LOG_CLEAR = { path: 'querylog_clear', method: 'POST' }; - getQueryLog(data) { + getQueryLog(params) { const { path, method } = this.GET_QUERY_LOG; - const config = { - data, - headers: { 'Content-Type': 'application/json' }, - }; - return this.makeRequest(path, method, config); + const url = getPathWithQueryParams(path, params); + return this.makeRequest(url, method); } getQueryLogInfo() { diff --git a/client/src/components/Logs/index.js b/client/src/components/Logs/index.js index 6ce8eef2..e61b19d1 100644 --- a/client/src/components/Logs/index.js +++ b/client/src/components/Logs/index.js @@ -255,10 +255,10 @@ class Logs extends Component { } = filteredObj; return { - domain: domain || '', - client: client || '', - question_type: isValidQuestionType(type) ? type.toUpperCase() : '', - response_status: response === RESPONSE_FILTER.FILTERED ? response : '', + filter_domain: domain || '', + filter_client: client || '', + filter_question_type: isValidQuestionType(type) ? type.toUpperCase() : '', + filter_response_status: response === RESPONSE_FILTER.FILTERED ? response : '', }; }; diff --git a/client/src/helpers/constants.js b/client/src/helpers/constants.js index a0b6a6fa..99d147b9 100644 --- a/client/src/helpers/constants.js +++ b/client/src/helpers/constants.js @@ -372,8 +372,8 @@ export const DNS_RECORD_TYPES = [ ]; export const DEFAULT_LOGS_FILTER = { - domain: '', - client: '', - question_type: '', - response_status: '', + filter_domain: '', + filter_client: '', + filter_question_type: '', + filter_response_status: '', }; diff --git a/client/src/helpers/helpers.js b/client/src/helpers/helpers.js index ebb96923..fd9e5d3a 100644 --- a/client/src/helpers/helpers.js +++ b/client/src/helpers/helpers.js @@ -1,3 +1,4 @@ +import 'url-polyfill'; import dateParse from 'date-fns/parse'; import dateFormat from 'date-fns/format'; import subHours from 'date-fns/sub_hours'; @@ -321,3 +322,9 @@ export const normalizeWhois = (whois) => { }; export const isValidQuestionType = type => type && DNS_RECORD_TYPES.includes(type.toUpperCase()); + +export const getPathWithQueryParams = (path, params) => { + const searchParams = new URLSearchParams(params); + + return `${path}?${searchParams.toString()}`; +}; From 1583262df180006e70112c74373de3f39bfcb11b Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Tue, 15 Oct 2019 12:31:01 +0300 Subject: [PATCH 4/4] - client: rename helper --- client/src/api/Api.js | 4 ++-- client/src/helpers/helpers.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/api/Api.js b/client/src/api/Api.js index c067d3f3..c5ced2b8 100644 --- a/client/src/api/Api.js +++ b/client/src/api/Api.js @@ -1,6 +1,6 @@ import axios from 'axios'; -import { getPathWithQueryParams } from '../helpers/helpers'; +import { getPathWithQueryString } from '../helpers/helpers'; class Api { baseUrl = 'control'; @@ -491,7 +491,7 @@ class Api { getQueryLog(params) { const { path, method } = this.GET_QUERY_LOG; - const url = getPathWithQueryParams(path, params); + const url = getPathWithQueryString(path, params); return this.makeRequest(url, method); } diff --git a/client/src/helpers/helpers.js b/client/src/helpers/helpers.js index fd9e5d3a..ced2e9ad 100644 --- a/client/src/helpers/helpers.js +++ b/client/src/helpers/helpers.js @@ -323,7 +323,7 @@ export const normalizeWhois = (whois) => { export const isValidQuestionType = type => type && DNS_RECORD_TYPES.includes(type.toUpperCase()); -export const getPathWithQueryParams = (path, params) => { +export const getPathWithQueryString = (path, params) => { const searchParams = new URLSearchParams(params); return `${path}?${searchParams.toString()}`;