Pull request 1907: 951-blocked-services-schedule-api
Updates #951. Squashed commit of the following: commit 6b840fd516f5a87fde0420e3aceb9c239b22c974 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Tue Aug 29 19:53:03 2023 +0300 client: imp docs more commit 7fc8f0363fbe4c4266cb0f67428fe4d18c351d2d Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Tue Aug 29 19:40:00 2023 +0300 client: imp docs commit 00bc14d5760614f2797714cdc2c4c19b1a94b86e Author: Ildar Kamalov <ik@adguard.com> Date: Mon Aug 28 18:43:49 2023 +0300 try to fix lock file commit d749df74b576091e0b58928d86ea8b3b49f919da Merge: c69f9230be1f6229e5Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Mon Aug 28 18:14:02 2023 +0300 Merge branch 'master' into 951-blocked-services-schedule-api commit c69f9230b12f7c983db06b74324b3df77d74b32b Author: Ildar Kamalov <ik@adguard.com> Date: Mon Aug 28 17:16:20 2023 +0300 revert eslintrc commit b37916c2dff0ddea5293d87570bb58e3443d2d21 Author: Ildar Kamalov <ik@adguard.com> Date: Mon Aug 28 12:02:39 2023 +0300 fix translations commit f5bb67d81506c687d0abd580049a3eee0af808e0 Author: Ildar Kamalov <ik@adguard.com> Date: Mon Aug 28 11:43:57 2023 +0300 fix helpers commit 13ec6a8b3a0acfb62762ae7e46c6e98eb7c82212 Author: Ildar Kamalov <ik@adguard.com> Date: Mon Aug 28 11:24:57 2023 +0300 remove todo commit 23724ec2fd683ed17b9f1cee841ad9aaf4c9d04f Author: Ildar Kamalov <ik@adguard.com> Date: Mon Aug 28 09:56:56 2023 +0300 add clients schedule form commit 84d29e558a329068e64e7a95ee183946aa4515b5 Author: Ildar Kamalov <ik@adguard.com> Date: Fri Aug 25 17:44:40 2023 +0300 fix schedule form commit 83e4017688082e9eb670091d5a24d98157050502 Author: Ildar Kamalov <ik@adguard.com> Date: Fri Aug 18 12:58:16 2023 +0300 remove unused commit ef2b68e138da382e3cf42586ae604e12d9493504 Author: Ildar Kamalov <ik@adguard.com> Date: Fri Aug 18 12:57:37 2023 +0300 client: fix translation string commit 32ea80c968f52f18adbc811b2f06874644cdfe20 Author: Ildar Kamalov <ik@adguard.com> Date: Fri Aug 18 12:26:26 2023 +0300 wip schedule commit 9b770873859186c9424c8d108812e32ddff33bad Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Fri Jul 21 14:29:50 2023 +0300 all: imp naming commit ea4e9514ea3b264bcce7f2a301db817de4e87059 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Jul 19 18:09:27 2023 +0300 all: imp code commit 98a705bdaa5c1e79394c73e5d75af2416fe9f297 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Tue Jul 18 18:23:26 2023 +0300 all: imp naming commit 4f84b55c7bfc9f7b680feac0ec45f5ea9189299a Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Fri Jul 14 15:01:17 2023 +0300 all: add global schedule api commit 87cf1646869ee9138964b47a27b7493674c8854a Merge: cabb80ac12adc8624cAuthor: Stanislav Chzhen <s.chzhen@adguard.com> Date: Fri Jul 14 12:09:29 2023 +0300 Merge branch 'master' into 951-blocked-services-schedule-api commit cabb80ac16de437a8118bb0166479574379c97a3 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Thu Jul 13 13:37:23 2023 +0300 openapi: fix typo commit 2279b03acbcfc3d76216f8aaf30ae1c7894127bc Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Thu Jul 13 12:26:19 2023 +0300 all: imp docs ... and 3 more commits
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
package aghhttp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -61,23 +60,3 @@ func WriteTextPlainDeprecated(w http.ResponseWriter, r *http.Request) (isPlainTe
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// WriteJSONResponse sets the content-type header in w.Header() to
|
||||
// "application/json", writes a header with a "200 OK" status, encodes resp to
|
||||
// w, calls [Error] on any returned error, and returns it as well.
|
||||
func WriteJSONResponse(w http.ResponseWriter, r *http.Request, resp any) (err error) {
|
||||
return WriteJSONResponseCode(w, r, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// WriteJSONResponseCode is like [WriteJSONResponse] but adds the ability to
|
||||
// redefine the status code.
|
||||
func WriteJSONResponseCode(w http.ResponseWriter, r *http.Request, code int, resp any) (err error) {
|
||||
w.Header().Set(httphdr.ContentType, HdrValApplicationJSON)
|
||||
w.WriteHeader(code)
|
||||
err = json.NewEncoder(w).Encode(resp)
|
||||
if err != nil {
|
||||
Error(r, w, http.StatusInternalServerError, "encoding resp: %s", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package websvc
|
||||
package aghhttp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/golibs/httphdr"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
@@ -87,30 +86,29 @@ func (t *JSONTime) UnmarshalJSON(b []byte) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeJSONOKResponse writes headers with the code 200 OK, encodes v into w,
|
||||
// and logs any errors it encounters. r is used to get additional information
|
||||
// from the request.
|
||||
func writeJSONOKResponse(w http.ResponseWriter, r *http.Request, v any) {
|
||||
writeJSONResponse(w, r, v, http.StatusOK)
|
||||
}
|
||||
|
||||
// writeJSONResponse writes headers with code, encodes v into w, and logs any
|
||||
// errors it encounters. r is used to get additional information from the
|
||||
// WriteJSONResponse writes headers with the code, encodes resp into w, and logs
|
||||
// any errors it encounters. r is used to get additional information from the
|
||||
// request.
|
||||
func writeJSONResponse(w http.ResponseWriter, r *http.Request, v any, code int) {
|
||||
// TODO(a.garipov): Put some of these to a middleware.
|
||||
func WriteJSONResponse(w http.ResponseWriter, r *http.Request, code int, resp any) {
|
||||
h := w.Header()
|
||||
h.Set(httphdr.ContentType, aghhttp.HdrValApplicationJSON)
|
||||
h.Set(httphdr.Server, aghhttp.UserAgent())
|
||||
h.Set(httphdr.ContentType, HdrValApplicationJSON)
|
||||
h.Set(httphdr.Server, UserAgent())
|
||||
|
||||
w.WriteHeader(code)
|
||||
|
||||
err := json.NewEncoder(w).Encode(v)
|
||||
err := json.NewEncoder(w).Encode(resp)
|
||||
if err != nil {
|
||||
log.Error("websvc: writing resp to %s %s: %s", r.Method, r.URL.Path, err)
|
||||
log.Error("aghhttp: writing json resp to %s %s: %s", r.Method, r.URL.Path, err)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteJSONResponseOK writes headers with the code 200 OK, encodes v into w,
|
||||
// and logs any errors it encounters. r is used to get additional information
|
||||
// from the request.
|
||||
func WriteJSONResponseOK(w http.ResponseWriter, r *http.Request, v any) {
|
||||
WriteJSONResponse(w, r, http.StatusOK, v)
|
||||
}
|
||||
|
||||
// ErrorCode is the error code as used by the HTTP API. See the ErrorCode
|
||||
// definition in the OpenAPI specification.
|
||||
type ErrorCode string
|
||||
@@ -131,14 +129,14 @@ type HTTPAPIErrorResp struct {
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
// writeJSONErrorResponse encodes err as a JSON error into w, and logs any
|
||||
// WriteJSONResponseError encodes err as a JSON error into w, and logs any
|
||||
// errors it encounters. r is used to get additional information from the
|
||||
// request.
|
||||
func writeJSONErrorResponse(w http.ResponseWriter, r *http.Request, err error) {
|
||||
log.Error("websvc: %s %s: %s", r.Method, r.URL.Path, err)
|
||||
func WriteJSONResponseError(w http.ResponseWriter, r *http.Request, err error) {
|
||||
log.Error("aghhttp: writing json error to %s %s: %s", r.Method, r.URL.Path, err)
|
||||
|
||||
writeJSONResponse(w, r, &HTTPAPIErrorResp{
|
||||
WriteJSONResponse(w, r, http.StatusUnprocessableEntity, &HTTPAPIErrorResp{
|
||||
Code: ErrorCodeTMP000,
|
||||
Msg: err.Error(),
|
||||
}, http.StatusUnprocessableEntity)
|
||||
})
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
package websvc_test
|
||||
package aghhttp_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/next/websvc"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testJSONTime is the JSON time for tests.
|
||||
var testJSONTime = websvc.JSONTime(time.Unix(1_234_567_890, 123_456_000).UTC())
|
||||
var testJSONTime = aghhttp.JSONTime(time.Unix(1_234_567_890, 123_456_000).UTC())
|
||||
|
||||
// testJSONTimeStr is the string with the JSON encoding of testJSONTime.
|
||||
const testJSONTimeStr = "1234567890123.456"
|
||||
@@ -21,17 +21,17 @@ func TestJSONTime_MarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
wantErrMsg string
|
||||
in websvc.JSONTime
|
||||
in aghhttp.JSONTime
|
||||
want []byte
|
||||
}{{
|
||||
name: "unix_zero",
|
||||
wantErrMsg: "",
|
||||
in: websvc.JSONTime(time.Unix(0, 0)),
|
||||
in: aghhttp.JSONTime(time.Unix(0, 0)),
|
||||
want: []byte("0"),
|
||||
}, {
|
||||
name: "empty",
|
||||
wantErrMsg: "",
|
||||
in: websvc.JSONTime{},
|
||||
in: aghhttp.JSONTime{},
|
||||
want: []byte("-6795364578871.345"),
|
||||
}, {
|
||||
name: "time",
|
||||
@@ -51,7 +51,7 @@ func TestJSONTime_MarshalJSON(t *testing.T) {
|
||||
|
||||
t.Run("json", func(t *testing.T) {
|
||||
in := &struct {
|
||||
A websvc.JSONTime
|
||||
A aghhttp.JSONTime
|
||||
}{
|
||||
A: testJSONTime,
|
||||
}
|
||||
@@ -67,7 +67,7 @@ func TestJSONTime_UnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
wantErrMsg string
|
||||
want websvc.JSONTime
|
||||
want aghhttp.JSONTime
|
||||
data []byte
|
||||
}{{
|
||||
name: "time",
|
||||
@@ -78,13 +78,13 @@ func TestJSONTime_UnmarshalJSON(t *testing.T) {
|
||||
name: "bad",
|
||||
wantErrMsg: `parsing json time: strconv.ParseFloat: parsing "{}": ` +
|
||||
`invalid syntax`,
|
||||
want: websvc.JSONTime{},
|
||||
want: aghhttp.JSONTime{},
|
||||
data: []byte(`{}`),
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var got websvc.JSONTime
|
||||
var got aghhttp.JSONTime
|
||||
err := got.UnmarshalJSON(tc.data)
|
||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
||||
|
||||
@@ -93,7 +93,7 @@ func TestJSONTime_UnmarshalJSON(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("nil", func(t *testing.T) {
|
||||
err := (*websvc.JSONTime)(nil).UnmarshalJSON([]byte("0"))
|
||||
err := (*aghhttp.JSONTime)(nil).UnmarshalJSON([]byte("0"))
|
||||
require.Error(t, err)
|
||||
|
||||
msg := err.Error()
|
||||
@@ -103,7 +103,7 @@ func TestJSONTime_UnmarshalJSON(t *testing.T) {
|
||||
t.Run("json", func(t *testing.T) {
|
||||
want := testJSONTime
|
||||
var got struct {
|
||||
A websvc.JSONTime
|
||||
A aghhttp.JSONTime
|
||||
}
|
||||
|
||||
err := json.Unmarshal([]byte(`{"A":`+testJSONTimeStr+`}`), &got)
|
||||
@@ -146,7 +146,7 @@ func (s *server) handleDHCPStatus(w http.ResponseWriter, r *http.Request) {
|
||||
status.Leases = leasesToDynamic(s.Leases(LeasesDynamic))
|
||||
status.StaticLeases = leasesToStatic(s.Leases(LeasesStatic))
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, status)
|
||||
aghhttp.WriteJSONResponseOK(w, r, status)
|
||||
}
|
||||
|
||||
func (s *server) enableDHCP(ifaceName string) (code int, err error) {
|
||||
@@ -395,7 +395,7 @@ func (s *server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// newNetInterfaceJSON creates a JSON object from a [net.Interface] iface.
|
||||
@@ -547,7 +547,7 @@ func (s *server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Reque
|
||||
|
||||
setOtherDHCPResult(ifaceName, result)
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, result)
|
||||
aghhttp.WriteJSONResponseOK(w, r, result)
|
||||
}
|
||||
|
||||
// setOtherDHCPResult sets the results of the check for another DHCP server in
|
||||
|
||||
@@ -24,7 +24,7 @@ type jsonError struct {
|
||||
// TODO(a.garipov): Either take the logger from the server after we've
|
||||
// refactored logging or make this not a method of *Server.
|
||||
func (s *server) notImplemented(w http.ResponseWriter, r *http.Request) {
|
||||
_ = aghhttp.WriteJSONResponseCode(w, r, http.StatusNotImplemented, &jsonError{
|
||||
aghhttp.WriteJSONResponse(w, r, http.StatusNotImplemented, &jsonError{
|
||||
Message: aghos.Unsupported("dhcp").Error(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ func (s *Server) accessListJSON() (j accessListJSON) {
|
||||
}
|
||||
|
||||
func (s *Server) handleAccessList(w http.ResponseWriter, r *http.Request) {
|
||||
_ = aghhttp.WriteJSONResponse(w, r, s.accessListJSON())
|
||||
aghhttp.WriteJSONResponseOK(w, r, s.accessListJSON())
|
||||
}
|
||||
|
||||
// validateAccessSet checks the internal accessListJSON lists. To search for
|
||||
|
||||
@@ -169,7 +169,7 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) {
|
||||
// handleGetConfig handles requests to the GET /control/dns_info endpoint.
|
||||
func (s *Server) handleGetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
resp := s.getDNSConfig()
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
func (req *jsonDNSConfig) checkBlockingMode() (err error) {
|
||||
@@ -758,7 +758,7 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, result)
|
||||
aghhttp.WriteJSONResponseOK(w, r, result)
|
||||
}
|
||||
|
||||
// handleCacheClear is the handler for the POST /control/cache_clear HTTP API.
|
||||
|
||||
@@ -50,10 +50,10 @@ func initBlockedServices() {
|
||||
// BlockedServices is the configuration of blocked services.
|
||||
type BlockedServices struct {
|
||||
// Schedule is blocked services schedule for every day of the week.
|
||||
Schedule *schedule.Weekly `yaml:"schedule"`
|
||||
Schedule *schedule.Weekly `json:"schedule" yaml:"schedule"`
|
||||
|
||||
// IDs is the names of blocked services.
|
||||
IDs []string `yaml:"ids"`
|
||||
IDs []string `json:"ids" yaml:"ids"`
|
||||
}
|
||||
|
||||
// Clone returns a deep copy of blocked services.
|
||||
@@ -114,25 +114,33 @@ func (d *DNSFilter) ApplyBlockedServicesList(setts *Settings, list []string) {
|
||||
}
|
||||
|
||||
func (d *DNSFilter) handleBlockedServicesIDs(w http.ResponseWriter, r *http.Request) {
|
||||
_ = aghhttp.WriteJSONResponse(w, r, serviceIDs)
|
||||
aghhttp.WriteJSONResponseOK(w, r, serviceIDs)
|
||||
}
|
||||
|
||||
func (d *DNSFilter) handleBlockedServicesAll(w http.ResponseWriter, r *http.Request) {
|
||||
_ = aghhttp.WriteJSONResponse(w, r, struct {
|
||||
aghhttp.WriteJSONResponseOK(w, r, struct {
|
||||
BlockedServices []blockedService `json:"blocked_services"`
|
||||
}{
|
||||
BlockedServices: blockedServices,
|
||||
})
|
||||
}
|
||||
|
||||
// handleBlockedServicesList is the handler for the GET
|
||||
// /control/blocked_services/list HTTP API.
|
||||
//
|
||||
// Deprecated: Use handleBlockedServicesGet.
|
||||
func (d *DNSFilter) handleBlockedServicesList(w http.ResponseWriter, r *http.Request) {
|
||||
d.confLock.RLock()
|
||||
list := d.Config.BlockedServices.IDs
|
||||
d.confLock.RUnlock()
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, list)
|
||||
aghhttp.WriteJSONResponseOK(w, r, list)
|
||||
}
|
||||
|
||||
// handleBlockedServicesSet is the handler for the POST
|
||||
// /control/blocked_services/set HTTP API.
|
||||
//
|
||||
// Deprecated: Use handleBlockedServicesUpdate.
|
||||
func (d *DNSFilter) handleBlockedServicesSet(w http.ResponseWriter, r *http.Request) {
|
||||
list := []string{}
|
||||
err := json.NewDecoder(r.Body).Decode(&list)
|
||||
@@ -150,3 +158,51 @@ func (d *DNSFilter) handleBlockedServicesSet(w http.ResponseWriter, r *http.Requ
|
||||
|
||||
d.Config.ConfigModified()
|
||||
}
|
||||
|
||||
// handleBlockedServicesGet is the handler for the GET
|
||||
// /control/blocked_services/get HTTP API.
|
||||
func (d *DNSFilter) handleBlockedServicesGet(w http.ResponseWriter, r *http.Request) {
|
||||
var bsvc *BlockedServices
|
||||
func() {
|
||||
d.confLock.RLock()
|
||||
defer d.confLock.RUnlock()
|
||||
|
||||
bsvc = d.Config.BlockedServices.Clone()
|
||||
}()
|
||||
|
||||
aghhttp.WriteJSONResponseOK(w, r, bsvc)
|
||||
}
|
||||
|
||||
// handleBlockedServicesUpdate is the handler for the PUT
|
||||
// /control/blocked_services/update HTTP API.
|
||||
func (d *DNSFilter) handleBlockedServicesUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
bsvc := &BlockedServices{}
|
||||
err := json.NewDecoder(r.Body).Decode(bsvc)
|
||||
if err != nil {
|
||||
aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
err = bsvc.Validate()
|
||||
if err != nil {
|
||||
aghhttp.Error(r, w, http.StatusUnprocessableEntity, "validating: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if bsvc.Schedule == nil {
|
||||
bsvc.Schedule = schedule.EmptyWeekly()
|
||||
}
|
||||
|
||||
func() {
|
||||
d.confLock.Lock()
|
||||
defer d.confLock.Unlock()
|
||||
|
||||
d.Config.BlockedServices = bsvc
|
||||
}()
|
||||
|
||||
log.Debug("updated blocked services schedule: %d", len(bsvc.IDs))
|
||||
|
||||
d.Config.ConfigModified()
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ func (d *DNSFilter) handleFilteringRefresh(w http.ResponseWriter, r *http.Reques
|
||||
return
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
type filterJSON struct {
|
||||
@@ -354,7 +354,7 @@ func (d *DNSFilter) handleFilteringStatus(w http.ResponseWriter, r *http.Request
|
||||
resp.UserRules = d.UserRules
|
||||
d.filtersMu.RUnlock()
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// Set filtering configuration
|
||||
@@ -456,7 +456,7 @@ func (d *DNSFilter) handleCheckHost(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// setProtectedBool sets the value of a boolean pointer under a lock. l must
|
||||
@@ -504,7 +504,7 @@ func (d *DNSFilter) handleSafeBrowsingStatus(w http.ResponseWriter, r *http.Requ
|
||||
Enabled: protectedBool(&d.confLock, &d.Config.SafeBrowsingEnabled),
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// handleParentalEnable is the handler for the POST /control/parental/enable
|
||||
@@ -530,7 +530,7 @@ func (d *DNSFilter) handleParentalStatus(w http.ResponseWriter, r *http.Request)
|
||||
Enabled: protectedBool(&d.confLock, &d.Config.ParentalEnabled),
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// RegisterFilteringHandlers - register handlers
|
||||
@@ -560,9 +560,14 @@ func (d *DNSFilter) RegisterFilteringHandlers() {
|
||||
|
||||
registerHTTP(http.MethodGet, "/control/blocked_services/services", d.handleBlockedServicesIDs)
|
||||
registerHTTP(http.MethodGet, "/control/blocked_services/all", d.handleBlockedServicesAll)
|
||||
|
||||
// Deprecated handlers.
|
||||
registerHTTP(http.MethodGet, "/control/blocked_services/list", d.handleBlockedServicesList)
|
||||
registerHTTP(http.MethodPost, "/control/blocked_services/set", d.handleBlockedServicesSet)
|
||||
|
||||
registerHTTP(http.MethodGet, "/control/blocked_services/get", d.handleBlockedServicesGet)
|
||||
registerHTTP(http.MethodPut, "/control/blocked_services/update", d.handleBlockedServicesUpdate)
|
||||
|
||||
registerHTTP(http.MethodGet, "/control/filtering/status", d.handleFilteringStatus)
|
||||
registerHTTP(http.MethodPost, "/control/filtering/config", d.handleFilteringConfig)
|
||||
registerHTTP(http.MethodPost, "/control/filtering/add_url", d.handleFilteringAddURL)
|
||||
|
||||
@@ -28,7 +28,7 @@ func (d *DNSFilter) handleRewriteList(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
d.confLock.Unlock()
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, arr)
|
||||
aghhttp.WriteJSONResponseOK(w, r, arr)
|
||||
}
|
||||
|
||||
func (d *DNSFilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -36,7 +36,7 @@ func (d *DNSFilter) handleSafeSearchStatus(w http.ResponseWriter, r *http.Reques
|
||||
resp = d.Config.SafeSearchConf
|
||||
}()
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// handleSafeSearchSettings is the handler for PUT /control/safesearch/settings
|
||||
|
||||
@@ -34,8 +34,12 @@ type clientJSON struct {
|
||||
WHOIS *whois.Info `json:"whois_info,omitempty"`
|
||||
SafeSearchConf *filtering.SafeSearchConfig `json:"safe_search"`
|
||||
|
||||
// Schedule is blocked services schedule for every day of the week.
|
||||
Schedule *schedule.Weekly `json:"blocked_services_schedule"`
|
||||
|
||||
Name string `json:"name"`
|
||||
|
||||
// BlockedServices is the names of blocked services.
|
||||
BlockedServices []string `json:"blocked_services"`
|
||||
IDs []string `json:"ids"`
|
||||
Tags []string `json:"tags"`
|
||||
@@ -53,6 +57,34 @@ type clientJSON struct {
|
||||
IgnoreStatistics aghalg.NullBool `json:"ignore_statistics"`
|
||||
}
|
||||
|
||||
// copySettings returns a copy of specific settings from JSON or a previous
|
||||
// client.
|
||||
func (j *clientJSON) copySettings(
|
||||
prev *Client,
|
||||
) (weekly *schedule.Weekly, ignoreQueryLog, ignoreStatistics bool) {
|
||||
if j.Schedule != nil {
|
||||
weekly = j.Schedule.Clone()
|
||||
} else if prev != nil && prev.BlockedServices != nil {
|
||||
weekly = prev.BlockedServices.Schedule.Clone()
|
||||
} else {
|
||||
weekly = schedule.EmptyWeekly()
|
||||
}
|
||||
|
||||
if j.IgnoreQueryLog != aghalg.NBNull {
|
||||
ignoreQueryLog = j.IgnoreQueryLog == aghalg.NBTrue
|
||||
} else if prev != nil {
|
||||
ignoreQueryLog = prev.IgnoreQueryLog
|
||||
}
|
||||
|
||||
if j.IgnoreStatistics != aghalg.NBNull {
|
||||
ignoreStatistics = j.IgnoreStatistics == aghalg.NBTrue
|
||||
} else if prev != nil {
|
||||
ignoreStatistics = prev.IgnoreStatistics
|
||||
}
|
||||
|
||||
return weekly, ignoreQueryLog, ignoreStatistics
|
||||
}
|
||||
|
||||
type runtimeClientJSON struct {
|
||||
WHOIS *whois.Info `json:"whois_info"`
|
||||
|
||||
@@ -93,7 +125,7 @@ func (clients *clientsContainer) handleGetClients(w http.ResponseWriter, r *http
|
||||
|
||||
data.Tags = clientTags
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, data)
|
||||
aghhttp.WriteJSONResponseOK(w, r, data)
|
||||
}
|
||||
|
||||
// jsonToClient converts JSON object to Client object.
|
||||
@@ -119,9 +151,15 @@ func (clients *clientsContainer) jsonToClient(cj clientJSON, prev *Client) (c *C
|
||||
}
|
||||
}
|
||||
|
||||
weekly := schedule.EmptyWeekly()
|
||||
if prev != nil {
|
||||
weekly = prev.BlockedServices.Schedule.Clone()
|
||||
weekly, ignoreQueryLog, ignoreStatistics := cj.copySettings(prev)
|
||||
|
||||
bs := &filtering.BlockedServices{
|
||||
Schedule: weekly,
|
||||
IDs: cj.BlockedServices,
|
||||
}
|
||||
err = bs.Validate()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("validating blocked services: %w", err)
|
||||
}
|
||||
|
||||
c = &Client{
|
||||
@@ -129,10 +167,7 @@ func (clients *clientsContainer) jsonToClient(cj clientJSON, prev *Client) (c *C
|
||||
|
||||
Name: cj.Name,
|
||||
|
||||
BlockedServices: &filtering.BlockedServices{
|
||||
Schedule: weekly,
|
||||
IDs: cj.BlockedServices,
|
||||
},
|
||||
BlockedServices: bs,
|
||||
|
||||
IDs: cj.IDs,
|
||||
Tags: cj.Tags,
|
||||
@@ -143,18 +178,8 @@ func (clients *clientsContainer) jsonToClient(cj clientJSON, prev *Client) (c *C
|
||||
ParentalEnabled: cj.ParentalEnabled,
|
||||
SafeBrowsingEnabled: cj.SafeBrowsingEnabled,
|
||||
UseOwnBlockedServices: !cj.UseGlobalBlockedServices,
|
||||
}
|
||||
|
||||
if cj.IgnoreQueryLog != aghalg.NBNull {
|
||||
c.IgnoreQueryLog = cj.IgnoreQueryLog == aghalg.NBTrue
|
||||
} else if prev != nil {
|
||||
c.IgnoreQueryLog = prev.IgnoreQueryLog
|
||||
}
|
||||
|
||||
if cj.IgnoreStatistics != aghalg.NBNull {
|
||||
c.IgnoreStatistics = cj.IgnoreStatistics == aghalg.NBTrue
|
||||
} else if prev != nil {
|
||||
c.IgnoreStatistics = prev.IgnoreStatistics
|
||||
IgnoreQueryLog: ignoreQueryLog,
|
||||
IgnoreStatistics: ignoreStatistics,
|
||||
}
|
||||
|
||||
if safeSearchConf.Enabled {
|
||||
@@ -191,6 +216,7 @@ func clientToJSON(c *Client) (cj *clientJSON) {
|
||||
|
||||
UseGlobalBlockedServices: !c.UseOwnBlockedServices,
|
||||
|
||||
Schedule: c.BlockedServices.Schedule,
|
||||
BlockedServices: c.BlockedServices.IDs,
|
||||
|
||||
Upstreams: c.Upstreams,
|
||||
@@ -338,7 +364,7 @@ func (clients *clientsContainer) handleFindClient(w http.ResponseWriter, r *http
|
||||
})
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, data)
|
||||
aghhttp.WriteJSONResponseOK(w, r, data)
|
||||
}
|
||||
|
||||
// findRuntime looks up the IP in runtime and temporary storages, like
|
||||
|
||||
@@ -170,7 +170,7 @@ func handleStatus(w http.ResponseWriter, r *http.Request) {
|
||||
resp.IsDHCPAvailable = Context.dhcpServer != nil
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// ------------------------
|
||||
|
||||
@@ -59,7 +59,7 @@ func (web *webAPI) handleInstallGetAddresses(w http.ResponseWriter, r *http.Requ
|
||||
data.Interfaces[iface.Name] = iface
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, data)
|
||||
aghhttp.WriteJSONResponseOK(w, r, data)
|
||||
}
|
||||
|
||||
type checkConfReqEnt struct {
|
||||
@@ -190,7 +190,7 @@ func (web *webAPI) handleInstallCheckConfig(w http.ResponseWriter, r *http.Reque
|
||||
resp.StaticIP = handleStaticIP(req.DNS.IP, req.SetStaticIP)
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// handleStaticIP - handles static IP request
|
||||
|
||||
@@ -33,7 +33,7 @@ func (web *webAPI) handleVersionJSON(w http.ResponseWriter, r *http.Request) {
|
||||
resp := &versionResponse{}
|
||||
if web.conf.disableUpdate {
|
||||
resp.Disabled = true
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -68,7 +68,7 @@ func (web *webAPI) handleVersionJSON(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// requestVersionInfo sets the VersionInfo field of resp if it can reach the
|
||||
|
||||
@@ -58,7 +58,7 @@ type languageJSON struct {
|
||||
func handleI18nCurrentLanguage(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("home: language is %s", config.Language)
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, &languageJSON{
|
||||
aghhttp.WriteJSONResponseOK(w, r, &languageJSON{
|
||||
Language: config.Language,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ func handleGetProfile(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}()
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// handlePutProfile is the handler for PUT /control/profile/update endpoint.
|
||||
|
||||
@@ -770,7 +770,7 @@ func marshalTLS(w http.ResponseWriter, r *http.Request, data tlsConfig) {
|
||||
data.PrivateKey = ""
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, data)
|
||||
aghhttp.WriteJSONResponseOK(w, r, data)
|
||||
}
|
||||
|
||||
// registerWebHandlers registers HTTP handlers for TLS configuration.
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc"
|
||||
)
|
||||
|
||||
@@ -17,13 +18,13 @@ import (
|
||||
type ReqPatchSettingsDNS struct {
|
||||
// TODO(a.garipov): Add more as we go.
|
||||
|
||||
Addresses []netip.AddrPort `json:"addresses"`
|
||||
BootstrapServers []string `json:"bootstrap_servers"`
|
||||
UpstreamServers []string `json:"upstream_servers"`
|
||||
DNS64Prefixes []netip.Prefix `json:"dns64_prefixes"`
|
||||
UpstreamTimeout JSONDuration `json:"upstream_timeout"`
|
||||
BootstrapPreferIPv6 bool `json:"bootstrap_prefer_ipv6"`
|
||||
UseDNS64 bool `json:"use_dns64"`
|
||||
Addresses []netip.AddrPort `json:"addresses"`
|
||||
BootstrapServers []string `json:"bootstrap_servers"`
|
||||
UpstreamServers []string `json:"upstream_servers"`
|
||||
DNS64Prefixes []netip.Prefix `json:"dns64_prefixes"`
|
||||
UpstreamTimeout aghhttp.JSONDuration `json:"upstream_timeout"`
|
||||
BootstrapPreferIPv6 bool `json:"bootstrap_prefer_ipv6"`
|
||||
UseDNS64 bool `json:"use_dns64"`
|
||||
}
|
||||
|
||||
// HTTPAPIDNSSettings are the DNS settings as used by the HTTP API. See the
|
||||
@@ -31,13 +32,13 @@ type ReqPatchSettingsDNS struct {
|
||||
type HTTPAPIDNSSettings struct {
|
||||
// TODO(a.garipov): Add more as we go.
|
||||
|
||||
Addresses []netip.AddrPort `json:"addresses"`
|
||||
BootstrapServers []string `json:"bootstrap_servers"`
|
||||
UpstreamServers []string `json:"upstream_servers"`
|
||||
DNS64Prefixes []netip.Prefix `json:"dns64_prefixes"`
|
||||
UpstreamTimeout JSONDuration `json:"upstream_timeout"`
|
||||
BootstrapPreferIPv6 bool `json:"bootstrap_prefer_ipv6"`
|
||||
UseDNS64 bool `json:"use_dns64"`
|
||||
Addresses []netip.AddrPort `json:"addresses"`
|
||||
BootstrapServers []string `json:"bootstrap_servers"`
|
||||
UpstreamServers []string `json:"upstream_servers"`
|
||||
DNS64Prefixes []netip.Prefix `json:"dns64_prefixes"`
|
||||
UpstreamTimeout aghhttp.JSONDuration `json:"upstream_timeout"`
|
||||
BootstrapPreferIPv6 bool `json:"bootstrap_prefer_ipv6"`
|
||||
UseDNS64 bool `json:"use_dns64"`
|
||||
}
|
||||
|
||||
// handlePatchSettingsDNS is the handler for the PATCH /api/v1/settings/dns HTTP
|
||||
@@ -53,7 +54,7 @@ func (svc *Service) handlePatchSettingsDNS(w http.ResponseWriter, r *http.Reques
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
writeJSONErrorResponse(w, r, fmt.Errorf("decoding: %w", err))
|
||||
aghhttp.WriteJSONResponseError(w, r, fmt.Errorf("decoding: %w", err))
|
||||
|
||||
return
|
||||
}
|
||||
@@ -71,7 +72,7 @@ func (svc *Service) handlePatchSettingsDNS(w http.ResponseWriter, r *http.Reques
|
||||
ctx := r.Context()
|
||||
err = svc.confMgr.UpdateDNS(ctx, newConf)
|
||||
if err != nil {
|
||||
writeJSONErrorResponse(w, r, fmt.Errorf("updating: %w", err))
|
||||
aghhttp.WriteJSONResponseError(w, r, fmt.Errorf("updating: %w", err))
|
||||
|
||||
return
|
||||
}
|
||||
@@ -79,17 +80,17 @@ func (svc *Service) handlePatchSettingsDNS(w http.ResponseWriter, r *http.Reques
|
||||
newSvc := svc.confMgr.DNS()
|
||||
err = newSvc.Start()
|
||||
if err != nil {
|
||||
writeJSONErrorResponse(w, r, fmt.Errorf("starting new service: %w", err))
|
||||
aghhttp.WriteJSONResponseError(w, r, fmt.Errorf("starting new service: %w", err))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
writeJSONOKResponse(w, r, &HTTPAPIDNSSettings{
|
||||
aghhttp.WriteJSONResponseOK(w, r, &HTTPAPIDNSSettings{
|
||||
Addresses: newConf.Addresses,
|
||||
BootstrapServers: newConf.BootstrapServers,
|
||||
UpstreamServers: newConf.UpstreamServers,
|
||||
DNS64Prefixes: newConf.DNS64Prefixes,
|
||||
UpstreamTimeout: JSONDuration(newConf.UpstreamTimeout),
|
||||
UpstreamTimeout: aghhttp.JSONDuration(newConf.UpstreamTimeout),
|
||||
BootstrapPreferIPv6: newConf.BootstrapPreferIPv6,
|
||||
UseDNS64: newConf.UseDNS64,
|
||||
})
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc"
|
||||
@@ -24,7 +25,7 @@ func TestService_HandlePatchSettingsDNS(t *testing.T) {
|
||||
BootstrapServers: []string{"1.0.0.1"},
|
||||
UpstreamServers: []string{"1.1.1.1"},
|
||||
DNS64Prefixes: []netip.Prefix{netip.MustParsePrefix("1234::/64")},
|
||||
UpstreamTimeout: websvc.JSONDuration(2 * time.Second),
|
||||
UpstreamTimeout: aghhttp.JSONDuration(2 * time.Second),
|
||||
BootstrapPreferIPv6: true,
|
||||
UseDNS64: true,
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
@@ -21,9 +22,9 @@ type ReqPatchSettingsHTTP struct {
|
||||
//
|
||||
// TODO(a.garipov): Add wait time.
|
||||
|
||||
Addresses []netip.AddrPort `json:"addresses"`
|
||||
SecureAddresses []netip.AddrPort `json:"secure_addresses"`
|
||||
Timeout JSONDuration `json:"timeout"`
|
||||
Addresses []netip.AddrPort `json:"addresses"`
|
||||
SecureAddresses []netip.AddrPort `json:"secure_addresses"`
|
||||
Timeout aghhttp.JSONDuration `json:"timeout"`
|
||||
}
|
||||
|
||||
// HTTPAPIHTTPSettings are the HTTP settings as used by the HTTP API. See the
|
||||
@@ -31,10 +32,10 @@ type ReqPatchSettingsHTTP struct {
|
||||
type HTTPAPIHTTPSettings struct {
|
||||
// TODO(a.garipov): Add more as we go.
|
||||
|
||||
Addresses []netip.AddrPort `json:"addresses"`
|
||||
SecureAddresses []netip.AddrPort `json:"secure_addresses"`
|
||||
Timeout JSONDuration `json:"timeout"`
|
||||
ForceHTTPS bool `json:"force_https"`
|
||||
Addresses []netip.AddrPort `json:"addresses"`
|
||||
SecureAddresses []netip.AddrPort `json:"secure_addresses"`
|
||||
Timeout aghhttp.JSONDuration `json:"timeout"`
|
||||
ForceHTTPS bool `json:"force_https"`
|
||||
}
|
||||
|
||||
// handlePatchSettingsHTTP is the handler for the PATCH /api/v1/settings/http
|
||||
@@ -46,7 +47,7 @@ func (svc *Service) handlePatchSettingsHTTP(w http.ResponseWriter, r *http.Reque
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
writeJSONErrorResponse(w, r, fmt.Errorf("decoding: %w", err))
|
||||
aghhttp.WriteJSONResponseError(w, r, fmt.Errorf("decoding: %w", err))
|
||||
|
||||
return
|
||||
}
|
||||
@@ -65,10 +66,10 @@ func (svc *Service) handlePatchSettingsHTTP(w http.ResponseWriter, r *http.Reque
|
||||
ForceHTTPS: svc.forceHTTPS,
|
||||
}
|
||||
|
||||
writeJSONOKResponse(w, r, &HTTPAPIHTTPSettings{
|
||||
aghhttp.WriteJSONResponseOK(w, r, &HTTPAPIHTTPSettings{
|
||||
Addresses: newConf.Addresses,
|
||||
SecureAddresses: newConf.SecureAddresses,
|
||||
Timeout: JSONDuration(newConf.Timeout),
|
||||
Timeout: aghhttp.JSONDuration(newConf.Timeout),
|
||||
ForceHTTPS: newConf.ForceHTTPS,
|
||||
})
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/next/websvc"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -20,7 +21,7 @@ func TestService_HandlePatchSettingsHTTP(t *testing.T) {
|
||||
wantWeb := &websvc.HTTPAPIHTTPSettings{
|
||||
Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.1.1:80")},
|
||||
SecureAddresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.1.1:443")},
|
||||
Timeout: websvc.JSONDuration(10 * time.Second),
|
||||
Timeout: aghhttp.JSONDuration(10 * time.Second),
|
||||
ForceHTTPS: false,
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ package websvc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
)
|
||||
|
||||
// All Settings Handlers
|
||||
@@ -25,20 +27,20 @@ func (svc *Service) handleGetSettingsAll(w http.ResponseWriter, r *http.Request)
|
||||
httpConf := webSvc.Config()
|
||||
|
||||
// TODO(a.garipov): Add all currently supported parameters.
|
||||
writeJSONOKResponse(w, r, &RespGetV1SettingsAll{
|
||||
aghhttp.WriteJSONResponseOK(w, r, &RespGetV1SettingsAll{
|
||||
DNS: &HTTPAPIDNSSettings{
|
||||
Addresses: dnsConf.Addresses,
|
||||
BootstrapServers: dnsConf.BootstrapServers,
|
||||
UpstreamServers: dnsConf.UpstreamServers,
|
||||
DNS64Prefixes: dnsConf.DNS64Prefixes,
|
||||
UpstreamTimeout: JSONDuration(dnsConf.UpstreamTimeout),
|
||||
UpstreamTimeout: aghhttp.JSONDuration(dnsConf.UpstreamTimeout),
|
||||
BootstrapPreferIPv6: dnsConf.BootstrapPreferIPv6,
|
||||
UseDNS64: dnsConf.UseDNS64,
|
||||
},
|
||||
HTTP: &HTTPAPIHTTPSettings{
|
||||
Addresses: httpConf.Addresses,
|
||||
SecureAddresses: httpConf.SecureAddresses,
|
||||
Timeout: JSONDuration(httpConf.Timeout),
|
||||
Timeout: aghhttp.JSONDuration(httpConf.Timeout),
|
||||
ForceHTTPS: httpConf.ForceHTTPS,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/next/websvc"
|
||||
@@ -23,14 +24,14 @@ func TestService_HandleGetSettingsAll(t *testing.T) {
|
||||
Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:53")},
|
||||
BootstrapServers: []string{"94.140.14.140", "94.140.14.141"},
|
||||
UpstreamServers: []string{"94.140.14.14", "1.1.1.1"},
|
||||
UpstreamTimeout: websvc.JSONDuration(1 * time.Second),
|
||||
UpstreamTimeout: aghhttp.JSONDuration(1 * time.Second),
|
||||
BootstrapPreferIPv6: true,
|
||||
}
|
||||
|
||||
wantWeb := &websvc.HTTPAPIHTTPSettings{
|
||||
Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:80")},
|
||||
SecureAddresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:443")},
|
||||
Timeout: websvc.JSONDuration(5 * time.Second),
|
||||
Timeout: aghhttp.JSONDuration(5 * time.Second),
|
||||
ForceHTTPS: true,
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"net/http"
|
||||
"runtime"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/version"
|
||||
)
|
||||
|
||||
@@ -12,24 +13,24 @@ import (
|
||||
// RespGetV1SystemInfo describes the response of the GET /api/v1/system/info
|
||||
// HTTP API.
|
||||
type RespGetV1SystemInfo struct {
|
||||
Arch string `json:"arch"`
|
||||
Channel string `json:"channel"`
|
||||
OS string `json:"os"`
|
||||
NewVersion string `json:"new_version,omitempty"`
|
||||
Start JSONTime `json:"start"`
|
||||
Version string `json:"version"`
|
||||
Arch string `json:"arch"`
|
||||
Channel string `json:"channel"`
|
||||
OS string `json:"os"`
|
||||
NewVersion string `json:"new_version,omitempty"`
|
||||
Start aghhttp.JSONTime `json:"start"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// handleGetV1SystemInfo is the handler for the GET /api/v1/system/info HTTP
|
||||
// API.
|
||||
func (svc *Service) handleGetV1SystemInfo(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSONOKResponse(w, r, &RespGetV1SystemInfo{
|
||||
aghhttp.WriteJSONResponseOK(w, r, &RespGetV1SystemInfo{
|
||||
Arch: runtime.GOARCH,
|
||||
Channel: version.Channel(),
|
||||
OS: runtime.GOOS,
|
||||
// TODO(a.garipov): Fill this when we have an updater.
|
||||
NewVersion: "",
|
||||
Start: JSONTime(svc.start),
|
||||
Start: aghhttp.JSONTime(svc.start),
|
||||
Version: version.Version(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ func (l *queryLog) handleQueryLog(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
resp := entriesToJSON(entries, oldest, l.anonymizer.Load())
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// handleQueryLogClear is the handler for the POST /control/querylog/clear HTTP
|
||||
@@ -118,7 +118,7 @@ func (l *queryLog) handleQueryLogInfo(w http.ResponseWriter, r *http.Request) {
|
||||
ivl = timeutil.Day * 90
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, configJSON{
|
||||
aghhttp.WriteJSONResponseOK(w, r, configJSON{
|
||||
Enabled: aghalg.BoolToNullBool(l.conf.Enabled),
|
||||
Interval: ivl.Hours() / 24,
|
||||
AnonymizeClientIP: aghalg.BoolToNullBool(l.conf.AnonymizeClientIP),
|
||||
@@ -143,7 +143,7 @@ func (l *queryLog) handleGetQueryLogConfig(w http.ResponseWriter, r *http.Reques
|
||||
|
||||
slices.Sort(resp.Ignored)
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// AnonymizeIP masks ip to anonymize the client if the ip is a valid one.
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
package schedule
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -50,6 +52,10 @@ func FullWeekly() (w *Weekly) {
|
||||
|
||||
// Clone returns a deep copy of a weekly.
|
||||
func (w *Weekly) Clone() (c *Weekly) {
|
||||
if w == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NOTE: Do not use time.LoadLocation, because the results will be
|
||||
// different on time zone database update.
|
||||
return &Weekly{
|
||||
@@ -75,12 +81,62 @@ func (w *Weekly) Contains(t time.Time) (ok bool) {
|
||||
return dr.contains(offset)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ json.Unmarshaler = (*Weekly)(nil)
|
||||
|
||||
// UnmarshalJSON implements the [json.Unmarshaler] interface for *Weekly.
|
||||
func (w *Weekly) UnmarshalJSON(data []byte) (err error) {
|
||||
conf := &weeklyConfigJSON{}
|
||||
err = json.Unmarshal(data, conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
weekly := Weekly{}
|
||||
|
||||
weekly.location, err = time.LoadLocation(conf.TimeZone)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
days := []*dayConfigJSON{
|
||||
time.Sunday: conf.Sunday,
|
||||
time.Monday: conf.Monday,
|
||||
time.Tuesday: conf.Tuesday,
|
||||
time.Wednesday: conf.Wednesday,
|
||||
time.Thursday: conf.Thursday,
|
||||
time.Friday: conf.Friday,
|
||||
time.Saturday: conf.Saturday,
|
||||
}
|
||||
for i, d := range days {
|
||||
var r dayRange
|
||||
|
||||
if d != nil {
|
||||
r = dayRange{
|
||||
start: time.Duration(d.Start),
|
||||
end: time.Duration(d.End),
|
||||
}
|
||||
}
|
||||
|
||||
err = w.validate(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("weekday %s: %w", time.Weekday(i), err)
|
||||
}
|
||||
|
||||
weekly.days[i] = r
|
||||
}
|
||||
|
||||
*w = weekly
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ yaml.Unmarshaler = (*Weekly)(nil)
|
||||
|
||||
// UnmarshalYAML implements the [yaml.Unmarshaler] interface for *Weekly.
|
||||
func (w *Weekly) UnmarshalYAML(value *yaml.Node) (err error) {
|
||||
conf := &weeklyConfig{}
|
||||
conf := &weeklyConfigYAML{}
|
||||
|
||||
err = value.Decode(conf)
|
||||
if err != nil {
|
||||
@@ -96,7 +152,7 @@ func (w *Weekly) UnmarshalYAML(value *yaml.Node) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
days := []dayConfig{
|
||||
days := []dayConfigYAML{
|
||||
time.Sunday: conf.Sunday,
|
||||
time.Monday: conf.Monday,
|
||||
time.Tuesday: conf.Tuesday,
|
||||
@@ -124,24 +180,24 @@ func (w *Weekly) UnmarshalYAML(value *yaml.Node) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// weeklyConfig is the YAML configuration structure of Weekly.
|
||||
type weeklyConfig struct {
|
||||
// weeklyConfigYAML is the YAML configuration structure of Weekly.
|
||||
type weeklyConfigYAML struct {
|
||||
// TimeZone is the local time zone.
|
||||
TimeZone string `yaml:"time_zone"`
|
||||
|
||||
// Days of the week.
|
||||
|
||||
Sunday dayConfig `yaml:"sun,omitempty"`
|
||||
Monday dayConfig `yaml:"mon,omitempty"`
|
||||
Tuesday dayConfig `yaml:"tue,omitempty"`
|
||||
Wednesday dayConfig `yaml:"wed,omitempty"`
|
||||
Thursday dayConfig `yaml:"thu,omitempty"`
|
||||
Friday dayConfig `yaml:"fri,omitempty"`
|
||||
Saturday dayConfig `yaml:"sat,omitempty"`
|
||||
Sunday dayConfigYAML `yaml:"sun,omitempty"`
|
||||
Monday dayConfigYAML `yaml:"mon,omitempty"`
|
||||
Tuesday dayConfigYAML `yaml:"tue,omitempty"`
|
||||
Wednesday dayConfigYAML `yaml:"wed,omitempty"`
|
||||
Thursday dayConfigYAML `yaml:"thu,omitempty"`
|
||||
Friday dayConfigYAML `yaml:"fri,omitempty"`
|
||||
Saturday dayConfigYAML `yaml:"sat,omitempty"`
|
||||
}
|
||||
|
||||
// dayConfig is the YAML configuration structure of dayRange.
|
||||
type dayConfig struct {
|
||||
// dayConfigYAML is the YAML configuration structure of dayRange.
|
||||
type dayConfigYAML struct {
|
||||
Start timeutil.Duration `yaml:"start"`
|
||||
End timeutil.Duration `yaml:"end"`
|
||||
}
|
||||
@@ -172,38 +228,57 @@ func (w *Weekly) validate(r dayRange) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ json.Marshaler = (*Weekly)(nil)
|
||||
|
||||
// MarshalJSON implements the [json.Marshaler] interface for *Weekly.
|
||||
func (w *Weekly) MarshalJSON() (data []byte, err error) {
|
||||
c := &weeklyConfigJSON{
|
||||
TimeZone: w.location.String(),
|
||||
Sunday: w.days[time.Sunday].toDayConfigJSON(),
|
||||
Monday: w.days[time.Monday].toDayConfigJSON(),
|
||||
Tuesday: w.days[time.Tuesday].toDayConfigJSON(),
|
||||
Wednesday: w.days[time.Wednesday].toDayConfigJSON(),
|
||||
Thursday: w.days[time.Thursday].toDayConfigJSON(),
|
||||
Friday: w.days[time.Friday].toDayConfigJSON(),
|
||||
Saturday: w.days[time.Saturday].toDayConfigJSON(),
|
||||
}
|
||||
|
||||
return json.Marshal(c)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ yaml.Marshaler = (*Weekly)(nil)
|
||||
|
||||
// MarshalYAML implements the [yaml.Marshaler] interface for *Weekly.
|
||||
func (w *Weekly) MarshalYAML() (v any, err error) {
|
||||
return weeklyConfig{
|
||||
return weeklyConfigYAML{
|
||||
TimeZone: w.location.String(),
|
||||
Sunday: dayConfig{
|
||||
Sunday: dayConfigYAML{
|
||||
Start: timeutil.Duration{Duration: w.days[time.Sunday].start},
|
||||
End: timeutil.Duration{Duration: w.days[time.Sunday].end},
|
||||
},
|
||||
Monday: dayConfig{
|
||||
Monday: dayConfigYAML{
|
||||
Start: timeutil.Duration{Duration: w.days[time.Monday].start},
|
||||
End: timeutil.Duration{Duration: w.days[time.Monday].end},
|
||||
},
|
||||
Tuesday: dayConfig{
|
||||
Tuesday: dayConfigYAML{
|
||||
Start: timeutil.Duration{Duration: w.days[time.Tuesday].start},
|
||||
End: timeutil.Duration{Duration: w.days[time.Tuesday].end},
|
||||
},
|
||||
Wednesday: dayConfig{
|
||||
Wednesday: dayConfigYAML{
|
||||
Start: timeutil.Duration{Duration: w.days[time.Wednesday].start},
|
||||
End: timeutil.Duration{Duration: w.days[time.Wednesday].end},
|
||||
},
|
||||
Thursday: dayConfig{
|
||||
Thursday: dayConfigYAML{
|
||||
Start: timeutil.Duration{Duration: w.days[time.Thursday].start},
|
||||
End: timeutil.Duration{Duration: w.days[time.Thursday].end},
|
||||
},
|
||||
Friday: dayConfig{
|
||||
Friday: dayConfigYAML{
|
||||
Start: timeutil.Duration{Duration: w.days[time.Friday].start},
|
||||
End: timeutil.Duration{Duration: w.days[time.Friday].end},
|
||||
},
|
||||
Saturday: dayConfig{
|
||||
Saturday: dayConfigYAML{
|
||||
Start: timeutil.Duration{Duration: w.days[time.Saturday].start},
|
||||
End: timeutil.Duration{Duration: w.days[time.Saturday].end},
|
||||
},
|
||||
@@ -248,3 +323,38 @@ func (r dayRange) validate() (err error) {
|
||||
func (r *dayRange) contains(offset time.Duration) (ok bool) {
|
||||
return r.start <= offset && offset < r.end
|
||||
}
|
||||
|
||||
// toDayConfigJSON returns nil if the day range is empty, otherwise returns
|
||||
// initialized JSON configuration of the day range.
|
||||
func (r dayRange) toDayConfigJSON() (j *dayConfigJSON) {
|
||||
if (r == dayRange{}) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &dayConfigJSON{
|
||||
Start: aghhttp.JSONDuration(r.start),
|
||||
End: aghhttp.JSONDuration(r.end),
|
||||
}
|
||||
}
|
||||
|
||||
// weeklyConfigJSON is the JSON configuration structure of Weekly.
|
||||
type weeklyConfigJSON struct {
|
||||
// TimeZone is the local time zone.
|
||||
TimeZone string `json:"time_zone"`
|
||||
|
||||
// Days of the week.
|
||||
|
||||
Sunday *dayConfigJSON `json:"sun,omitempty"`
|
||||
Monday *dayConfigJSON `json:"mon,omitempty"`
|
||||
Tuesday *dayConfigJSON `json:"tue,omitempty"`
|
||||
Wednesday *dayConfigJSON `json:"wed,omitempty"`
|
||||
Thursday *dayConfigJSON `json:"thu,omitempty"`
|
||||
Friday *dayConfigJSON `json:"fri,omitempty"`
|
||||
Saturday *dayConfigJSON `json:"sat,omitempty"`
|
||||
}
|
||||
|
||||
// dayConfigJSON is the JSON configuration structure of dayRange.
|
||||
type dayConfigJSON struct {
|
||||
Start aghhttp.JSONDuration `json:"start"`
|
||||
End aghhttp.JSONDuration `json:"end"`
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package schedule
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -122,7 +123,7 @@ func TestWeekly_Contains(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
const brusselsSunday = `
|
||||
const brusselsSundayYAML = `
|
||||
sun:
|
||||
start: 12h
|
||||
end: 14h
|
||||
@@ -179,7 +180,7 @@ yaml: "bad"
|
||||
}, {
|
||||
name: "brussels_sunday",
|
||||
wantErrMsg: "",
|
||||
data: []byte(brusselsSunday),
|
||||
data: []byte(brusselsSundayYAML),
|
||||
want: brusselsWeekly,
|
||||
}, {
|
||||
name: "start_equal_end",
|
||||
@@ -240,7 +241,7 @@ func TestWeekly_MarshalYAML(t *testing.T) {
|
||||
want: &Weekly{},
|
||||
}, {
|
||||
name: "brussels_sunday",
|
||||
data: []byte(brusselsSunday),
|
||||
data: []byte(brusselsSundayYAML),
|
||||
want: brusselsWeekly,
|
||||
}}
|
||||
|
||||
@@ -369,3 +370,142 @@ func TestDayRange_Validate(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const brusselsSundayJSON = `{
|
||||
"sun": {
|
||||
"end": 50400000,
|
||||
"start": 43200000
|
||||
},
|
||||
"time_zone": "Europe/Brussels"
|
||||
}`
|
||||
|
||||
func TestWeekly_UnmarshalJSON(t *testing.T) {
|
||||
const (
|
||||
sameTime = `{
|
||||
"sun": {
|
||||
"end": 32400000,
|
||||
"start": 32400000
|
||||
}
|
||||
}`
|
||||
negativeStart = `{
|
||||
"sun": {
|
||||
"end": 3600000,
|
||||
"start": -3600000
|
||||
}
|
||||
}`
|
||||
badTZ = `{
|
||||
"time_zone": "bad_timezone"
|
||||
}`
|
||||
badJSON = `{
|
||||
"bad": "json",
|
||||
}`
|
||||
)
|
||||
|
||||
brusseltsTZ, err := time.LoadLocation("Europe/Brussels")
|
||||
require.NoError(t, err)
|
||||
|
||||
brusselsWeekly := &Weekly{
|
||||
days: [7]dayRange{{
|
||||
start: time.Hour * 12,
|
||||
end: time.Hour * 14,
|
||||
}},
|
||||
location: brusseltsTZ,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
wantErrMsg string
|
||||
data []byte
|
||||
want *Weekly
|
||||
}{{
|
||||
name: "empty",
|
||||
wantErrMsg: "unexpected end of JSON input",
|
||||
data: []byte(""),
|
||||
want: &Weekly{},
|
||||
}, {
|
||||
name: "null",
|
||||
wantErrMsg: "",
|
||||
data: []byte("null"),
|
||||
want: &Weekly{location: time.UTC},
|
||||
}, {
|
||||
name: "brussels_sunday",
|
||||
wantErrMsg: "",
|
||||
data: []byte(brusselsSundayJSON),
|
||||
want: brusselsWeekly,
|
||||
}, {
|
||||
name: "start_equal_end",
|
||||
wantErrMsg: "weekday Sunday: bad day range: start 9h0m0s is greater or equal to end 9h0m0s",
|
||||
data: []byte(sameTime),
|
||||
want: &Weekly{},
|
||||
}, {
|
||||
name: "start_negative",
|
||||
wantErrMsg: "weekday Sunday: bad day range: start -1h0m0s is negative",
|
||||
data: []byte(negativeStart),
|
||||
want: &Weekly{},
|
||||
}, {
|
||||
name: "bad_time_zone",
|
||||
wantErrMsg: "unknown time zone bad_timezone",
|
||||
data: []byte(badTZ),
|
||||
want: &Weekly{},
|
||||
}, {
|
||||
name: "bad_json",
|
||||
wantErrMsg: "invalid character '}' looking for beginning of object key string",
|
||||
data: []byte(badJSON),
|
||||
want: &Weekly{},
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
w := &Weekly{}
|
||||
err = json.Unmarshal(tc.data, w)
|
||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
||||
|
||||
assert.Equal(t, tc.want, w)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWeekly_MarshalJSON(t *testing.T) {
|
||||
brusselsTZ, err := time.LoadLocation("Europe/Brussels")
|
||||
require.NoError(t, err)
|
||||
|
||||
brusselsWeekly := &Weekly{
|
||||
days: [7]dayRange{time.Sunday: {
|
||||
start: time.Hour * 12,
|
||||
end: time.Hour * 14,
|
||||
}},
|
||||
location: brusselsTZ,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
want *Weekly
|
||||
}{{
|
||||
name: "empty",
|
||||
data: []byte(""),
|
||||
want: &Weekly{},
|
||||
}, {
|
||||
name: "null",
|
||||
data: []byte("null"),
|
||||
want: &Weekly{},
|
||||
}, {
|
||||
name: "brussels_sunday",
|
||||
data: []byte(brusselsSundayJSON),
|
||||
want: brusselsWeekly,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var data []byte
|
||||
data, err = json.Marshal(brusselsWeekly)
|
||||
require.NoError(t, err)
|
||||
|
||||
w := &Weekly{}
|
||||
err = json.Unmarshal(data, w)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, brusselsWeekly, w)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func (s *StatsCtx) handleStats(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// configResp is the response to the GET /control/stats_info.
|
||||
@@ -122,7 +122,7 @@ func (s *StatsCtx) handleStatsInfo(w http.ResponseWriter, r *http.Request) {
|
||||
resp.IntervalDays = 0
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// handleGetStatsConfig is the handler for the GET /control/stats/config HTTP
|
||||
@@ -142,7 +142,7 @@ func (s *StatsCtx) handleGetStatsConfig(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
slices.Sort(resp.Ignored)
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// handleStatsConfig is the handler for the POST /control/stats_config HTTP API.
|
||||
|
||||
Reference in New Issue
Block a user