Compare commits

..

1 Commits

Author SHA1 Message Date
Andrey Meshkov
d49d284d4a all: github action for potential dups and improved issue forms 2023-06-12 13:53:26 +03:00
18 changed files with 310 additions and 297 deletions

View File

@@ -1,109 +1,142 @@
'body': 'body':
- 'attributes': - 'attributes':
'description': > 'description': >
Please make sure that the issue is not a duplicate or a question. Please make sure that the issue is not a duplicate or a question.
If it's a duplicate, please react to the original issue with a If it's a duplicate, please react to the original issue with a
thumbs up. If it's a question, please post it to the GitHub thumbs up. If it's a question, please post it to the GitHub
Discussions page. Discussions page.
'label': 'Prerequisites' 'label': 'Prerequisites'
'options': 'options':
- 'label': > - 'label': >
I have checked the I have checked the
[Wiki](https://github.com/AdguardTeam/AdGuardHome/wiki) and [Wiki](https://github.com/AdguardTeam/AdGuardHome/wiki) and
[Discussions](https://github.com/AdguardTeam/AdGuardHome/discussions) [Discussions](https://github.com/AdguardTeam/AdGuardHome/discussions)
and found no answer and found no answer
'required': true 'required': true
- 'label': > - 'label': >
I have searched other issues and found no duplicates I have searched other issues and found no duplicates
'required': true 'required': true
- 'label': > - 'label': >
I want to report a bug and not ask a question I want to report a bug and not ask a question
'required': true 'required': true
'id': 'prerequisites' 'id': 'prerequisites'
'type': 'checkboxes' 'type': 'checkboxes'
- 'attributes': - 'attributes':
'description': 'On which operating system type does the issue occur?' 'description': 'On which operating system type does the issue occur?'
'label': 'Operating system type' 'label': 'Operating system type'
'options': 'options':
- 'FreeBSD' - 'FreeBSD'
- 'Linux, OpenWrt' - 'Linux, OpenWrt'
- 'Linux, Other (please mention the version in the description)' - 'Linux, Other (please mention the version in the description)'
- 'macOS (aka Darwin)' - 'macOS (aka Darwin)'
- 'OpenBSD' - 'OpenBSD'
- 'Windows' - 'Windows'
- 'Other (please mention in the description)' - 'Other (please mention in the description)'
'id': 'os' 'id': 'os'
'type': 'dropdown' 'type': 'dropdown'
'validations': 'validations':
'required': true 'required': true
- 'attributes': - 'attributes':
'description': 'On which CPU architecture does the issue occur?' 'description': 'On which CPU architecture does the issue occur?'
'label': 'CPU architecture' 'label': 'CPU architecture'
'options': 'options':
- 'AMD64' - 'AMD64'
- 'x86' - 'x86'
- '64-bit ARM' - '64-bit ARM'
- 'ARMv5' - 'ARMv5'
- 'ARMv6' - 'ARMv6'
- 'ARMv7' - 'ARMv7'
- '64-bit MIPS' - '64-bit MIPS'
- '64-bit MIPS LE' - '64-bit MIPS LE'
- '32-bit MIPS' - '32-bit MIPS'
- '32-bit MIPS LE' - '32-bit MIPS LE'
- '64-bit PowerPC LE' - '64-bit PowerPC LE'
- 'Other (please mention in the description)' - 'Other (please mention in the description)'
'id': 'arch' 'id': 'arch'
'type': 'dropdown' 'type': 'dropdown'
'validations': 'validations':
'required': true 'required': true
- 'attributes': - 'attributes':
'description': 'How did you install AdGuard Home?' 'description': 'How did you install AdGuard Home?'
'label': 'Installation' 'label': 'Installation'
'options': 'options':
- 'GitHub releases or script from README' - 'GitHub releases or script from README'
- 'Docker' - 'Docker'
- 'Snapcraft' - 'Snapcraft'
- 'Custom port' - 'Custom port'
- 'Other (please mention in the description)' - 'Other (please mention in the description)'
'id': 'install' 'id': 'install'
'type': 'dropdown' 'type': 'dropdown'
'validations': 'validations':
'required': true 'required': true
- 'attributes': - 'attributes':
'description': 'How did you setup AdGuard Home?' 'description': 'How did you setup AdGuard Home?'
'label': 'Setup' 'label': 'Setup'
'options': 'options':
- 'On one machine' - 'On one machine'
- 'On a router, DHCP is handled by the router' - 'On a router, DHCP is handled by the router'
- 'On a router, DHCP is handled by AdGuard Home' - 'On a router, DHCP is handled by AdGuard Home'
- 'Other (please mention in the description)' - 'Other (please mention in the description)'
'id': 'setup' 'id': 'setup'
'type': 'dropdown' 'type': 'dropdown'
'validations': 'validations':
'required': true 'required': true
- 'attributes': - 'attributes':
'description': 'What version of AdGuard Home are you using?' 'description': 'What version of AdGuard Home are you using?'
'label': 'AdGuard Home version' 'label': 'AdGuard Home version'
'id': 'version' 'id': 'version'
'type': 'input' 'type': 'input'
'validations': 'validations':
'required': true 'required': true
- 'attributes': - 'attributes':
'description': 'Please describe the bug' 'description': 'Please provide a set of steps to reproduce the issue'
'label': 'Description' 'label': 'Issue Details'
'value': | 'placeholder':
#### What did you do? 'value': |
Steps to reproduce:
#### Expected result 1.
2.
#### Actual result 3.
'id': 'what-happened'
#### Screenshots (if applicable)
#### Additional information
'id': 'description'
'type': 'textarea' 'type': 'textarea'
'validations': 'validations':
'required': true 'required': true
'description': 'File a bug report' - 'attributes':
'name': 'Bug' 'label': 'Expected Behavior'
'description': 'Describe the expected behavior'
'placeholder': |
A clear and concise description of what you expected to happen.
'id': 'expected-behavior'
'type': 'textarea'
'validations':
'required': false
- 'attributes':
'label': 'Actual Behavior'
'description': 'Describe the actual behavior'
'placeholder': 'A clear description of what happened instead.'
'id': 'actual-behavior'
'type': 'textarea'
'validations':
'required': true
- 'attributes':
'label': 'Screenshots'
'description': |
If applicable add screenshots explaining your problem.
You can drag and drop images or paste them from clipboard.
Use `<details> </details>` tag to hide screenshots under the spoiler.
'placeholder': 'If applicable add screenshots explaining your problem.'
'value': |
<details><summary>Screenshot 1:</summary>
<!-- paste screenshot here -->
</details>
'id': 'screenshots'
'type': 'textarea'
'validations':
'required': false
'description': >
Open a bug report. Please do not open bug reports for questions. If you
want to ask a question, ask in in the Discussions section.
'name': 'Bug report'
'labels': [ 'bug' ]

View File

@@ -1,41 +1,54 @@
'body': 'body':
- 'attributes': - 'attributes':
'description': > 'description': >
Please make sure that the issue is not a duplicate or a question. Please make sure that the issue is not a duplicate or a question.
If it's a duplicate, please react to the original issue with a If it's a duplicate, please react to the original issue with a
thumbs up. If it's a question, please post it to the GitHub thumbs up. If it's a question, please post it to the GitHub
Discussions page. Discussions page.
'label': 'Prerequisites' 'label': 'Prerequisites'
'options': 'options':
- 'label': > - 'label': >
I have checked the I have checked the
[Wiki](https://github.com/AdguardTeam/AdGuardHome/wiki) and [Wiki](https://github.com/AdguardTeam/AdGuardHome/wiki) and
[Discussions](https://github.com/AdguardTeam/AdGuardHome/discussions) [Discussions](https://github.com/AdguardTeam/AdGuardHome/discussions)
and found no answer and found no answer
'required': true 'required': true
- 'label': > - 'label': >
I have searched other issues and found no duplicates I have searched other issues and found no duplicates
'required': true 'required': true
- 'label': > - 'label': >
I want to request a feature or enhancement and not ask a I want to request a feature or enhancement and not ask a
question question
'required': true 'required': true
'id': 'prerequisites' 'id': 'prerequisites'
'type': 'checkboxes' 'type': 'checkboxes'
- 'attributes': - 'attributes':
'description': 'Please describe the request' 'description': 'What happened?'
'label': 'Description' 'placeholder': |
'value': | Is your feature request related to a problem? Please add a clear and
#### What problem are you trying to solve? concise description of what the problem is.
'label': 'Issue Details'
#### Proposed solution 'id': 'what-happened'
#### Alternatives considered
#### Additional information
'id': 'description'
'type': 'textarea' 'type': 'textarea'
'validations': 'validations':
'required': true 'required': true
- 'attributes':
'description': 'What do you propose to change in AdGuard Home?'
'placeholder': |
Describe the solution you'd like in a clear and concise manner.
'label': 'Proposed solution'
'id': 'proposed-solution'
'type': 'textarea'
'validations':
'required': true
- 'attributes':
'description': 'Are there any other ways to solve the problem?'
'placeholder': |
A clear and concise description of any alternative solutions or features
you've considered.
'label': 'Alternative solution'
'id': 'alternative-solution'
'type': 'textarea'
'description': 'Suggest a feature or an enhancement for AdGuard Home' 'description': 'Suggest a feature or an enhancement for AdGuard Home'
'name': 'Feature request or enhancement' 'name': 'Feature request or enhancement'
'labels': [ 'feature request' ]

View File

@@ -0,0 +1,34 @@
'name': 'potential-duplicates'
'on':
'issues':
'types': [ 'opened', 'edited' ]
'jobs':
'run':
'runs-on': 'ubuntu-latest'
'steps':
- 'uses': 'wow-actions/potential-duplicates@v1'
'with':
'GITHUB_TOKEN': ${{ secrets.GITHUB_TOKEN }}
# Issue title filter work with https://www.npmjs.com/package/anymatch.
# Any matched issue will stop detection immediately.
# You can specify multi filters in each line.
# 'filter': ''
# Exclude keywords in title before detecting.
# 'exclude': ''
# Label to set, when potential duplicates are detected.
'label': 'potential-duplicate'
# Get issues with state to compare. Supported state: 'all', 'closed',
# 'open'.
'state': 'all'
# If similarity is higher than this threshold([0,1]), issue will be
# marked as duplicate.
'threshold': 0.6
# Reactions to be add to comment when potential duplicates are
# detected. Available reactions: "-1", "+1", "confused", "laugh",
# "heart", "hooray", "rocket", "eyes".
# 'reactions': 'eyes, confused'
# Comment to post when potential duplicates are detected.
'comment': >
Potential duplicates: {{#issues}}
- [#{{ number }}] {{ title }} ({{ accuracy }}%)
{{/issues}}

View File

@@ -28,21 +28,16 @@ NOTE: Add new changes BELOW THIS COMMENT.
- The ability to edit rewrite rules via `PUT /control/rewrite/update` HTTP API - The ability to edit rewrite rules via `PUT /control/rewrite/update` HTTP API
([#1577]). ([#1577]).
[#1577]: https://github.com/AdguardTeam/AdGuardHome/issues/1577
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->
## [v0.107.32] - 2023-06-13
### Fixed ### Fixed
- DNSCrypt upstream not resetting the client and resolver information on - DNSCrypt upstream not resetting the client and resolver information on
dialing errors ([#5872]). dialing errors ([#5872]).
[#1577]: https://github.com/AdguardTeam/AdGuardHome/issues/1577
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->
@@ -2014,12 +2009,11 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
<!-- <!--
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.33...HEAD
[v0.107.33]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.32...v0.107.33
-->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.32...HEAD [Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.32...HEAD
[v0.107.32]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.31...v0.107.32 [v0.107.32]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.31...v0.107.32
-->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.31...HEAD
[v0.107.31]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.30...v0.107.31 [v0.107.31]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.30...v0.107.31
[v0.107.30]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.29...v0.107.30 [v0.107.30]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.29...v0.107.30
[v0.107.29]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.28...v0.107.29 [v0.107.29]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.28...v0.107.29

View File

@@ -0,0 +1,17 @@
//go:build windows
package aghnet
import (
"net"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
)
// listenPacketReusable announces on the local network address additionally
// configuring the socket to have a reusable binding.
func listenPacketReusable(_, _, _ string) (c net.PacketConn, err error) {
// TODO(e.burkov): Check if we are able to control sockets on Windows
// in the same way as on Unix.
return nil, aghos.Unsupported("listening packet reusable")
}

View File

@@ -0,0 +1,15 @@
//go:build windows
package dhcpd
import (
"net"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"golang.org/x/net/ipv4"
)
// Create a socket for receiving broadcast packets
func newBroadcastPacketConn(_ net.IP, _ int, _ string) (*ipv4.PacketConn, error) {
return nil, aghos.Unsupported("newBroadcastPacketConn")
}

View File

@@ -1,5 +1,5 @@
// Package cmd is the AdGuard Home entry point. It assembles the configuration // Package cmd is the AdGuard Home entry point. It contains the on-disk
// file manager, sets up signal processing logic, and so on. // configuration file utilities, signal processing logic, and so on.
// //
// TODO(a.garipov): Move to the upper-level internal/. // TODO(a.garipov): Move to the upper-level internal/.
package cmd package cmd
@@ -7,6 +7,7 @@ package cmd
import ( import (
"context" "context"
"io/fs" "io/fs"
"math/rand"
"os" "os"
"time" "time"
@@ -15,11 +16,12 @@ import (
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
) )
// Main is the entry point of AdGuard Home. // Main is the entry point of application.
func Main(frontend fs.FS) { func Main(clientBuildFS fs.FS) {
// Initial Configuration // Initial Configuration
start := time.Now() start := time.Now()
rand.Seed(start.UnixNano())
// TODO(a.garipov): Set up logging. // TODO(a.garipov): Set up logging.
@@ -27,34 +29,38 @@ func Main(frontend fs.FS) {
// Web Service // Web Service
// TODO(a.garipov): Use in the Web service.
_ = clientBuildFS
// TODO(a.garipov): Set up configuration file name. // TODO(a.garipov): Set up configuration file name.
const confFile = "AdGuardHome.1.yaml" const confFile = "AdGuardHome.1.yaml"
confMgr, err := configmgr.New(confFile, frontend, start) confMgr, err := configmgr.New(confFile, start)
check(err) fatalOnError(err)
web := confMgr.Web() web := confMgr.Web()
err = web.Start() err = web.Start()
check(err) fatalOnError(err)
dns := confMgr.DNS() dns := confMgr.DNS()
err = dns.Start() err = dns.Start()
check(err) fatalOnError(err)
sigHdlr := newSignalHandler( sigHdlr := newSignalHandler(
confFile, confFile,
frontend,
start, start,
web, web,
dns, dns,
) )
sigHdlr.handle() go sigHdlr.handle()
select {}
} }
// defaultTimeout is the timeout used for some operations where another timeout // defaultTimeout is the timeout used for some operations where another timeout
// hasn't been defined yet. // hasn't been defined yet.
const defaultTimeout = 5 * time.Second const defaultTimeout = 15 * time.Second
// ctxWithDefaultTimeout is a helper function that returns a context with // ctxWithDefaultTimeout is a helper function that returns a context with
// timeout set to defaultTimeout. // timeout set to defaultTimeout.
@@ -62,9 +68,10 @@ func ctxWithDefaultTimeout() (ctx context.Context, cancel context.CancelFunc) {
return context.WithTimeout(context.Background(), defaultTimeout) return context.WithTimeout(context.Background(), defaultTimeout)
} }
// check is a simple error-checking helper. It must only be used within Main. // fatalOnError is a helper that exits the program with an error code if err is
func check(err error) { // not nil. It must only be used within Main.
func fatalOnError(err error) {
if err != nil { if err != nil {
panic(err) log.Fatal(err)
} }
} }

View File

@@ -1,7 +1,6 @@
package cmd package cmd
import ( import (
"io/fs"
"os" "os"
"time" "time"
@@ -19,10 +18,6 @@ type signalHandler struct {
// confFile is the path to the configuration file. // confFile is the path to the configuration file.
confFile string confFile string
// frontend is the filesystem with the frontend and other statically
// compiled files.
frontend fs.FS
// start is the time at which AdGuard Home has been started. // start is the time at which AdGuard Home has been started.
start time.Time start time.Time
@@ -63,16 +58,16 @@ func (h *signalHandler) reconfigure() {
// reconfigured without the full shutdown, and the error handling is // reconfigured without the full shutdown, and the error handling is
// currently not the best. // currently not the best.
confMgr, err := configmgr.New(h.confFile, h.frontend, h.start) confMgr, err := configmgr.New(h.confFile, h.start)
check(err) fatalOnError(err)
web := confMgr.Web() web := confMgr.Web()
err = web.Start() err = web.Start()
check(err) fatalOnError(err)
dns := confMgr.DNS() dns := confMgr.DNS()
err = dns.Start() err = dns.Start()
check(err) fatalOnError(err)
h.services = []agh.Service{ h.services = []agh.Service{
dns, dns,
@@ -108,16 +103,10 @@ func (h *signalHandler) shutdown() (status int) {
} }
// newSignalHandler returns a new signalHandler that shuts down svcs. // newSignalHandler returns a new signalHandler that shuts down svcs.
func newSignalHandler( func newSignalHandler(confFile string, start time.Time, svcs ...agh.Service) (h *signalHandler) {
confFile string,
frontend fs.FS,
start time.Time,
svcs ...agh.Service,
) (h *signalHandler) {
h = &signalHandler{ h = &signalHandler{
signal: make(chan os.Signal, 1), signal: make(chan os.Signal, 1),
confFile: confFile, confFile: confFile,
frontend: frontend,
start: start, start: start,
services: svcs, services: svcs,
} }

View File

@@ -5,7 +5,6 @@ package configmgr
import ( import (
"context" "context"
"fmt" "fmt"
"io/fs"
"os" "os"
"sync" "sync"
"time" "time"
@@ -43,11 +42,7 @@ type Manager struct {
// New creates a new *Manager that persists changes to the file pointed to by // New creates a new *Manager that persists changes to the file pointed to by
// fileName. It reads the configuration file and populates the service fields. // fileName. It reads the configuration file and populates the service fields.
// start is the startup time of AdGuard Home. // start is the startup time of AdGuard Home.
func New( func New(fileName string, start time.Time) (m *Manager, err error) {
fileName string,
frontend fs.FS,
start time.Time,
) (m *Manager, err error) {
defer func() { err = errors.Annotate(err, "reading config") }() defer func() { err = errors.Annotate(err, "reading config") }()
conf := &config{} conf := &config{}
@@ -84,7 +79,7 @@ func New(
ctx, cancel := context.WithTimeout(context.Background(), assemblyTimeout) ctx, cancel := context.WithTimeout(context.Background(), assemblyTimeout)
defer cancel() defer cancel()
err = m.assemble(ctx, conf, frontend, start) err = m.assemble(ctx, conf, start)
if err != nil { if err != nil {
// Don't wrap the error, because it's informative enough as is. // Don't wrap the error, because it's informative enough as is.
return nil, err return nil, err
@@ -95,12 +90,7 @@ func New(
// assemble creates all services and puts them into the corresponding fields. // assemble creates all services and puts them into the corresponding fields.
// The fields of conf must not be modified after calling assemble. // The fields of conf must not be modified after calling assemble.
func (m *Manager) assemble( func (m *Manager) assemble(ctx context.Context, conf *config, start time.Time) (err error) {
ctx context.Context,
conf *config,
frontend fs.FS,
start time.Time,
) (err error) {
dnsConf := &dnssvc.Config{ dnsConf := &dnssvc.Config{
Addresses: conf.DNS.Addresses, Addresses: conf.DNS.Addresses,
BootstrapServers: conf.DNS.BootstrapDNS, BootstrapServers: conf.DNS.BootstrapDNS,
@@ -114,7 +104,6 @@ func (m *Manager) assemble(
webSvcConf := &websvc.Config{ webSvcConf := &websvc.Config{
ConfigManager: m, ConfigManager: m,
Frontend: frontend,
// TODO(a.garipov): Fill from config file. // TODO(a.garipov): Fill from config file.
TLS: nil, TLS: nil,
Start: start, Start: start,
@@ -210,10 +199,7 @@ func (m *Manager) updateWeb(ctx context.Context, c *websvc.Config) (err error) {
} }
} }
m.web, err = websvc.New(c) m.web = websvc.New(c)
if err != nil {
return fmt.Errorf("creating web svc: %w", err)
}
return nil return nil
} }

View File

@@ -53,7 +53,6 @@ func (svc *Service) handlePatchSettingsHTTP(w http.ResponseWriter, r *http.Reque
newConf := &Config{ newConf := &Config{
ConfigManager: svc.confMgr, ConfigManager: svc.confMgr,
Frontend: svc.frontend,
TLS: svc.tls, TLS: svc.tls,
Addresses: req.Addresses, Addresses: req.Addresses,
SecureAddresses: req.SecureAddresses, SecureAddresses: req.SecureAddresses,

View File

@@ -24,20 +24,21 @@ func TestService_HandlePatchSettingsHTTP(t *testing.T) {
ForceHTTPS: false, ForceHTTPS: false,
} }
svc, err := websvc.New(&websvc.Config{
TLS: &tls.Config{
Certificates: []tls.Certificate{{}},
},
Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:80")},
SecureAddresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:443")},
Timeout: 5 * time.Second,
ForceHTTPS: true,
})
require.NoError(t, err)
confMgr := newConfigManager() confMgr := newConfigManager()
confMgr.onWeb = func() (s agh.ServiceWithConfig[*websvc.Config]) { return svc } confMgr.onWeb = func() (s agh.ServiceWithConfig[*websvc.Config]) {
confMgr.onUpdateWeb = func(ctx context.Context, c *websvc.Config) (err error) { return nil } return websvc.New(&websvc.Config{
TLS: &tls.Config{
Certificates: []tls.Certificate{{}},
},
Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:80")},
SecureAddresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:443")},
Timeout: 5 * time.Second,
ForceHTTPS: true,
})
}
confMgr.onUpdateWeb = func(ctx context.Context, c *websvc.Config) (err error) {
return nil
}
_, addr := newTestServer(t, confMgr) _, addr := newTestServer(t, confMgr)
u := &url.URL{ u := &url.URL{
@@ -55,7 +56,7 @@ func TestService_HandlePatchSettingsHTTP(t *testing.T) {
respBody := httpPatch(t, u, req, http.StatusOK) respBody := httpPatch(t, u, req, http.StatusOK)
resp := &websvc.HTTPAPIHTTPSettings{} resp := &websvc.HTTPAPIHTTPSettings{}
err = json.Unmarshal(respBody, resp) err := json.Unmarshal(respBody, resp)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, wantWeb, resp) assert.Equal(t, wantWeb, resp)

View File

@@ -2,11 +2,9 @@ package websvc
import ( import (
"net/http" "net/http"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/golibs/httphdr" "github.com/AdguardTeam/golibs/httphdr"
"github.com/AdguardTeam/golibs/log"
) )
// Middlewares // Middlewares
@@ -21,18 +19,3 @@ func jsonMw(h http.Handler) (wrapped http.HandlerFunc) {
return http.HandlerFunc(f) return http.HandlerFunc(f)
} }
// logMw logs the queries with level debug.
func logMw(h http.Handler) (wrapped http.HandlerFunc) {
f := func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
m, u := r.Method, r.RequestURI
log.Debug("websvc: %s %s started", m, u)
defer func() { log.Debug("websvc: %s %s finished in %s", m, u, time.Since(start)) }()
h.ServeHTTP(w, r)
}
return http.HandlerFunc(f)
}

View File

@@ -2,9 +2,6 @@ package websvc
// Path constants // Path constants
const ( const (
PathRoot = "/"
PathFrontend = "/*filepath"
PathHealthCheck = "/health-check" PathHealthCheck = "/health-check"
PathV1SettingsAll = "/api/v1/settings/all" PathV1SettingsAll = "/api/v1/settings/all"

View File

@@ -46,19 +46,16 @@ func TestService_HandleGetSettingsAll(t *testing.T) {
return c return c
} }
svc, err := websvc.New(&websvc.Config{
TLS: &tls.Config{
Certificates: []tls.Certificate{{}},
},
Addresses: wantWeb.Addresses,
SecureAddresses: wantWeb.SecureAddresses,
Timeout: time.Duration(wantWeb.Timeout),
ForceHTTPS: true,
})
require.NoError(t, err)
confMgr.onWeb = func() (s agh.ServiceWithConfig[*websvc.Config]) { confMgr.onWeb = func() (s agh.ServiceWithConfig[*websvc.Config]) {
return svc return websvc.New(&websvc.Config{
TLS: &tls.Config{
Certificates: []tls.Certificate{{}},
},
Addresses: wantWeb.Addresses,
SecureAddresses: wantWeb.SecureAddresses,
Timeout: time.Duration(wantWeb.Timeout),
ForceHTTPS: true,
})
} }
_, addr := newTestServer(t, confMgr) _, addr := newTestServer(t, confMgr)
@@ -70,7 +67,7 @@ func TestService_HandleGetSettingsAll(t *testing.T) {
body := httpGet(t, u, http.StatusOK) body := httpGet(t, u, http.StatusOK)
resp := &websvc.RespGetV1SettingsAll{} resp := &websvc.RespGetV1SettingsAll{}
err = json.Unmarshal(body, resp) err := json.Unmarshal(body, resp)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, wantDNS, resp.DNS) assert.Equal(t, wantDNS, resp.DNS)

View File

@@ -11,7 +11,6 @@ import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io" "io"
"io/fs"
"net" "net"
"net/http" "net/http"
"net/netip" "net/netip"
@@ -40,10 +39,6 @@ type Config struct {
// dynamically reconfigure them. // dynamically reconfigure them.
ConfigManager ConfigManager ConfigManager ConfigManager
// Frontend is the filesystem with the frontend and other statically
// compiled files.
Frontend fs.FS
// TLS is the optional TLS configuration. If TLS is not nil, // TLS is the optional TLS configuration. If TLS is not nil,
// SecureAddresses must not be empty. // SecureAddresses must not be empty.
TLS *tls.Config TLS *tls.Config
@@ -72,7 +67,6 @@ type Config struct {
// [agh.Service] that does nothing. // [agh.Service] that does nothing.
type Service struct { type Service struct {
confMgr ConfigManager confMgr ConfigManager
frontend fs.FS
tls *tls.Config tls *tls.Config
start time.Time start time.Time
servers []*http.Server servers []*http.Server
@@ -83,22 +77,13 @@ type Service struct {
// New returns a new properly initialized *Service. If c is nil, svc is a nil // New returns a new properly initialized *Service. If c is nil, svc is a nil
// *Service that does nothing. The fields of c must not be modified after // *Service that does nothing. The fields of c must not be modified after
// calling New. // calling New.
// func New(c *Config) (svc *Service) {
// TODO(a.garipov): Get rid of this special handling of nil or explain it
// better.
func New(c *Config) (svc *Service, err error) {
if c == nil { if c == nil {
return nil, nil return nil
}
frontend, err := fs.Sub(c.Frontend, "build/static")
if err != nil {
return nil, fmt.Errorf("frontend fs: %w", err)
} }
svc = &Service{ svc = &Service{
confMgr: c.ConfigManager, confMgr: c.ConfigManager,
frontend: frontend,
tls: c.TLS, tls: c.TLS,
start: c.Start, start: c.Start,
timeout: c.Timeout, timeout: c.Timeout,
@@ -136,7 +121,7 @@ func New(c *Config) (svc *Service, err error) {
}) })
} }
return svc, nil return svc
} }
// newMux returns a new HTTP request multiplexor for the AdGuard Home web // newMux returns a new HTTP request multiplexor for the AdGuard Home web
@@ -147,54 +132,41 @@ func newMux(svc *Service) (mux *httptreemux.ContextMux) {
routes := []struct { routes := []struct {
handler http.HandlerFunc handler http.HandlerFunc
method string method string
pattern string path string
isJSON bool isJSON bool
}{{ }{{
handler: svc.handleGetHealthCheck, handler: svc.handleGetHealthCheck,
method: http.MethodGet, method: http.MethodGet,
pattern: PathHealthCheck, path: PathHealthCheck,
isJSON: false,
}, {
handler: http.FileServer(http.FS(svc.frontend)).ServeHTTP,
method: http.MethodGet,
pattern: PathFrontend,
isJSON: false,
}, {
handler: http.FileServer(http.FS(svc.frontend)).ServeHTTP,
method: http.MethodGet,
pattern: PathRoot,
isJSON: false, isJSON: false,
}, { }, {
handler: svc.handleGetSettingsAll, handler: svc.handleGetSettingsAll,
method: http.MethodGet, method: http.MethodGet,
pattern: PathV1SettingsAll, path: PathV1SettingsAll,
isJSON: true, isJSON: true,
}, { }, {
handler: svc.handlePatchSettingsDNS, handler: svc.handlePatchSettingsDNS,
method: http.MethodPatch, method: http.MethodPatch,
pattern: PathV1SettingsDNS, path: PathV1SettingsDNS,
isJSON: true, isJSON: true,
}, { }, {
handler: svc.handlePatchSettingsHTTP, handler: svc.handlePatchSettingsHTTP,
method: http.MethodPatch, method: http.MethodPatch,
pattern: PathV1SettingsHTTP, path: PathV1SettingsHTTP,
isJSON: true, isJSON: true,
}, { }, {
handler: svc.handleGetV1SystemInfo, handler: svc.handleGetV1SystemInfo,
method: http.MethodGet, method: http.MethodGet,
pattern: PathV1SystemInfo, path: PathV1SystemInfo,
isJSON: true, isJSON: true,
}} }}
for _, r := range routes { for _, r := range routes {
var hdlr http.Handler
if r.isJSON { if r.isJSON {
hdlr = jsonMw(r.handler) mux.Handle(r.method, r.path, jsonMw(r.handler))
} else { } else {
hdlr = r.handler mux.Handle(r.method, r.path, r.handler)
} }
mux.Handle(r.method, r.pattern, logMw(hdlr))
} }
return mux return mux

View File

@@ -5,14 +5,12 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"io" "io"
"io/fs"
"net/http" "net/http"
"net/netip" "net/netip"
"net/url" "net/url"
"testing" "testing"
"time" "time"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh" "github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc" "github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc"
"github.com/AdguardTeam/AdGuardHome/internal/next/websvc" "github.com/AdguardTeam/AdGuardHome/internal/next/websvc"
@@ -89,10 +87,7 @@ func newTestServer(
t.Helper() t.Helper()
c := &websvc.Config{ c := &websvc.Config{
ConfigManager: confMgr, ConfigManager: confMgr,
Frontend: &aghtest.FS{
OnOpen: func(_ string) (_ fs.File, _ error) { return nil, fs.ErrNotExist },
},
TLS: nil, TLS: nil,
Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:0")}, Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:0")},
SecureAddresses: nil, SecureAddresses: nil,
@@ -101,10 +96,9 @@ func newTestServer(
ForceHTTPS: false, ForceHTTPS: false,
} }
svc, err := websvc.New(c) svc = websvc.New(c)
require.NoError(t, err)
err = svc.Start() err := svc.Start()
require.NoError(t, err) require.NoError(t, err)
t.Cleanup(func() { t.Cleanup(func() {
ctx, cancel := context.WithTimeout(context.Background(), testTimeout) ctx, cancel := context.WithTimeout(context.Background(), testTimeout)

View File

@@ -13,8 +13,8 @@ import (
// outside of the same or underlying directory. // outside of the same or underlying directory.
//go:embed build //go:embed build
var frontend embed.FS var clientBuildFS embed.FS
func main() { func main() {
cmd.Main(frontend) cmd.Main(clientBuildFS)
} }

View File

@@ -3,7 +3,7 @@
# This comment is used to simplify checking local copies of the script. Bump # This comment is used to simplify checking local copies of the script. Bump
# this number every time a significant change is made to this script. # this number every time a significant change is made to this script.
# #
# AdGuard-Project-Version: 4 # AdGuard-Project-Version: 3
verbose="${VERBOSE:-0}" verbose="${VERBOSE:-0}"
readonly verbose readonly verbose
@@ -80,12 +80,6 @@ esac
# #
# * Package golang.org/x/net/context has been moved into stdlib. # * Package golang.org/x/net/context has been moved into stdlib.
# #
# Currently, the only standard exception are files generated from protobuf
# schemas, which use package reflect. If your project needs more exceptions,
# add and document them.
#
# TODO(a.garipov): Add deprecated packages golang.org/x/exp/maps and
# golang.org/x/exp/slices once all projects switch to Go 1.21.
blocklist_imports() { blocklist_imports() {
git grep\ git grep\
-e '[[:space:]]"errors"$'\ -e '[[:space:]]"errors"$'\
@@ -97,7 +91,6 @@ blocklist_imports() {
-e '[[:space:]]"golang.org/x/net/context"$'\ -e '[[:space:]]"golang.org/x/net/context"$'\
-n\ -n\
-- '*.go'\ -- '*.go'\
':!*.pb.go'\
| sed -e 's/^\([^[:space:]]\+\)\(.*\)$/\1 blocked import:\2/'\ | sed -e 's/^\([^[:space:]]\+\)\(.*\)$/\1 blocked import:\2/'\
|| exit 0 || exit 0
} }
@@ -108,7 +101,6 @@ method_const() {
git grep -F\ git grep -F\
-e '"DELETE"'\ -e '"DELETE"'\
-e '"GET"'\ -e '"GET"'\
-e '"PATCH"'\
-e '"POST"'\ -e '"POST"'\
-e '"PUT"'\ -e '"PUT"'\
-n\ -n\
@@ -135,7 +127,7 @@ underscores() {
-e '_others.go'\ -e '_others.go'\
-e '_test.go'\ -e '_test.go'\
-e '_unix.go'\ -e '_unix.go'\
-e '_windows.go'\ -e '_windows.go' \
-v\ -v\
| sed -e 's/./\t\0/' | sed -e 's/./\t\0/'
)" )"
@@ -174,9 +166,8 @@ run_linter ineffassign ./...
run_linter unparam ./... run_linter unparam ./...
git ls-files -- 'Makefile' '*.conf' '*.go' '*.mod' '*.sh' '*.yaml' '*.yml'\ git ls-files -- 'Makefile' '*.go' '*.mod' '*.sh' '*.yaml' '*.yml'\
| xargs misspell --error\ | xargs misspell --error
| sed -e 's/^/misspell: /'
run_linter looppointer ./... run_linter looppointer ./...
@@ -192,13 +183,4 @@ run_linter -e shadow --strict ./...
# TODO(a.garipov): Enable --blank? # TODO(a.garipov): Enable --blank?
run_linter errcheck --asserts ./... run_linter errcheck --asserts ./...
staticcheck_matrix=' run_linter staticcheck ./...
darwin: GOOS=darwin
freebsd: GOOS=freebsd
linux: GOOS=linux
openbsd: GOOS=openbsd
windows: GOOS=windows
'
readonly staticcheck_matrix
echo "$staticcheck_matrix" | run_linter staticcheck --matrix ./...