mirror of
https://github.com/ditatompel/xmr-remote-nodes.git
synced 2025-01-08 05:52:10 +07:00
Compare commits
19 commits
da61d132b5
...
b4b13728b0
Author | SHA1 | Date | |
---|---|---|---|
|
b4b13728b0 | ||
|
d78eea3da0 | ||
|
1c67b09229 | ||
|
89b90775ee | ||
|
5119ee75b1 | ||
|
71c8737cff | ||
b60a67c8cb | |||
9bd609e4dd | |||
cdb0816bc3 | |||
fc172a0bd0 | |||
7553ad8b45 | |||
f6b048b017 | |||
0e3dc04af8 | |||
61cc98e378 | |||
c3f837e122 | |||
|
a7bf9470fb | ||
|
3b9212618b | ||
|
6365b8a3c8 | ||
518d4b4335 |
21 changed files with 375 additions and 353 deletions
|
@ -8,6 +8,7 @@ SERVER_ENDPOINT="http://127.0.0.1:18901"
|
||||||
API_KEY=
|
API_KEY=
|
||||||
ACCEPT_TOR=false
|
ACCEPT_TOR=false
|
||||||
TOR_SOCKS="127.0.0.1:9050"
|
TOR_SOCKS="127.0.0.1:9050"
|
||||||
|
IPV6_CAPABLE=false
|
||||||
|
|
||||||
# Server Config
|
# Server Config
|
||||||
# #############
|
# #############
|
||||||
|
|
|
@ -38,14 +38,14 @@ To build the executable binaries, you need:
|
||||||
|
|
||||||
- MySQL/MariaDB
|
- MySQL/MariaDB
|
||||||
- [GeoIP Database][geoip_doc] (optional). Place it to `./assets/geoip`,
|
- [GeoIP Database][geoip_doc] (optional). Place it to `./assets/geoip`,
|
||||||
see [./internal/geo/ip.go](./internal/geo/ip.go).
|
see [./internal/ip/geo/geoip.go](./internal/ip/geo/geoip.go).
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### For initial server setup:
|
### For initial server setup:
|
||||||
|
|
||||||
1. Download [GeoIP Database][geoip_doc] and place it to `./assets/geoip`.
|
1. Download [GeoIP Database][geoip_doc] and place it to `./assets/geoip`.
|
||||||
(see [./internal/geo/ip.go](./internal/geo/ip.go)).
|
(see [./internal/ip/geo/geoip.go](./internal/ip/geo/geoip.go)).
|
||||||
2. Pepare your MySQL/MariaDB.
|
2. Pepare your MySQL/MariaDB.
|
||||||
3. Copy `.env.example` to `.env` and edit it to match with server environment.
|
3. Copy `.env.example` to `.env` and edit it to match with server environment.
|
||||||
4. Build the binary with `make server` (or `make build` to build both
|
4. Build the binary with `make server` (or `make build` to build both
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/config"
|
"github.com/ditatompel/xmr-remote-nodes/internal/config"
|
||||||
|
"github.com/ditatompel/xmr-remote-nodes/internal/ip"
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/monero"
|
"github.com/ditatompel/xmr-remote-nodes/internal/monero"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -36,20 +37,22 @@ func (err errProber) Error() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type proberClient struct {
|
type proberClient struct {
|
||||||
endpoint string // server endpoint
|
endpoint string // server endpoint
|
||||||
apiKey string // prober api key
|
apiKey string // prober api key
|
||||||
acceptTor bool // accept tor
|
acceptTor bool // accept tor
|
||||||
torSOCKS string // IP:Port of tor socks
|
torSOCKS string // IP:Port of tor socks
|
||||||
message string // message to include when reporting back to server
|
acceptIPv6 bool // accept ipv6
|
||||||
|
message string // message to include when reporting back to server
|
||||||
}
|
}
|
||||||
|
|
||||||
func newProber() *proberClient {
|
func newProber() *proberClient {
|
||||||
cfg := config.AppCfg()
|
cfg := config.AppCfg()
|
||||||
return &proberClient{
|
return &proberClient{
|
||||||
endpoint: cfg.ServerEndpoint,
|
endpoint: cfg.ServerEndpoint,
|
||||||
apiKey: cfg.APIKey,
|
apiKey: cfg.APIKey,
|
||||||
acceptTor: cfg.AcceptTor,
|
acceptTor: cfg.AcceptTor,
|
||||||
torSOCKS: cfg.TorSOCKS,
|
torSOCKS: cfg.TorSOCKS,
|
||||||
|
acceptIPv6: cfg.IPv6Capable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +88,10 @@ func (p *proberClient) SetAcceptTor(acceptTor bool) {
|
||||||
p.acceptTor = acceptTor
|
p.acceptTor = acceptTor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *proberClient) SetAcceptIPv6(acceptIPv6 bool) {
|
||||||
|
p.acceptIPv6 = acceptIPv6
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch a new job from the server, fetches node info, and sends it to the server
|
// Fetch a new job from the server, fetches node info, and sends it to the server
|
||||||
func (p *proberClient) Run() error {
|
func (p *proberClient) Run() error {
|
||||||
if err := p.validateConfig(); err != nil {
|
if err := p.validateConfig(); err != nil {
|
||||||
|
@ -121,20 +128,26 @@ func (p *proberClient) validateConfig() error {
|
||||||
|
|
||||||
// Get monero node info to fetch from the server
|
// Get monero node info to fetch from the server
|
||||||
func (p *proberClient) fetchJob() (monero.Node, error) {
|
func (p *proberClient) fetchJob() (monero.Node, error) {
|
||||||
queryParams := ""
|
acceptTor := 0
|
||||||
if p.acceptTor {
|
if p.acceptTor {
|
||||||
queryParams = "?accept_tor=1"
|
acceptTor = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptIPv6 := 0
|
||||||
|
if p.acceptIPv6 {
|
||||||
|
acceptIPv6 = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
var node monero.Node
|
var node monero.Node
|
||||||
|
|
||||||
uri := fmt.Sprintf("%s/api/v1/job%s", p.endpoint, queryParams)
|
uri := fmt.Sprintf("%s/api/v1/job?accept_tor=%d&accept_ipv6=%d", p.endpoint, acceptTor, acceptIPv6)
|
||||||
slog.Info(fmt.Sprintf("[PROBE] Getting node from %s", uri))
|
slog.Info(fmt.Sprintf("[PROBE] Getting node from %s", uri))
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, uri, nil)
|
req, err := http.NewRequest(http.MethodGet, uri, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return node, err
|
return node, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Add(monero.ProberAPIKey, p.apiKey)
|
req.Header.Add(monero.ProberAPIKey, p.apiKey)
|
||||||
req.Header.Set("User-Agent", RPCUserAgent)
|
req.Header.Set("User-Agent", RPCUserAgent)
|
||||||
|
|
||||||
|
@ -322,6 +335,13 @@ func (p *proberClient) fetchFee(client http.Client, endpoint string) (uint, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *proberClient) reportResult(node monero.Node, tookTime float64) error {
|
func (p *proberClient) reportResult(node monero.Node, tookTime float64) error {
|
||||||
|
if !node.IsTor {
|
||||||
|
if hostIps, err := net.LookupIP(node.Hostname); err == nil {
|
||||||
|
node.IPv6Only = ip.IsIPv6Only(hostIps)
|
||||||
|
node.IPAddresses = ip.SliceToString(hostIps)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
jsonData, err := json.Marshal(monero.ProbeReport{
|
jsonData, err := json.Marshal(monero.ProbeReport{
|
||||||
TookTime: tookTime,
|
TookTime: tookTime,
|
||||||
Message: p.message,
|
Message: p.message,
|
||||||
|
|
381
frontend/package-lock.json
generated
381
frontend/package-lock.json
generated
|
@ -1,35 +1,35 @@
|
||||||
{
|
{
|
||||||
"name": "xmr-nodes-frontend",
|
"name": "xmr-nodes-frontend",
|
||||||
"version": "v0.0.6",
|
"version": "v0.1.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "xmr-nodes-frontend",
|
"name": "xmr-nodes-frontend",
|
||||||
"version": "v0.0.6",
|
"version": "v0.1.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@floating-ui/dom": "^1.6.10",
|
"@floating-ui/dom": "^1.6.10",
|
||||||
"@skeletonlabs/skeleton": "^2.10.2",
|
"@skeletonlabs/skeleton": "^2.10.2",
|
||||||
"@skeletonlabs/tw-plugin": "^0.4.0",
|
"@skeletonlabs/tw-plugin": "^0.4.0",
|
||||||
"@sveltejs/adapter-static": "^3.0.4",
|
"@sveltejs/adapter-static": "^3.0.4",
|
||||||
"@sveltejs/kit": "^2.5.25",
|
"@sveltejs/kit": "^2.5.26",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.1.2",
|
"@sveltejs/vite-plugin-svelte": "^3.1.2",
|
||||||
"@tailwindcss/forms": "^0.5.8",
|
"@tailwindcss/forms": "^0.5.8",
|
||||||
"@types/eslint": "^8.56.0",
|
"@types/eslint": "^9.6.1",
|
||||||
"@vincjo/datatables": "^1.14.10",
|
"@vincjo/datatables": "^1.14.10",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"date-fns": "^3.3.1",
|
"date-fns": "^3.3.1",
|
||||||
"eslint": "^9.7.0",
|
"eslint": "^9.10.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-svelte": "^2.43.0",
|
"eslint-plugin-svelte": "^2.43.0",
|
||||||
"postcss": "^8.4.43",
|
"postcss": "^8.4.45",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"prettier-plugin-svelte": "^3.2.6",
|
"prettier-plugin-svelte": "^3.2.6",
|
||||||
"svelte": "^4.2.19",
|
"svelte": "^4.2.19",
|
||||||
"svelte-check": "^3.8.6",
|
"svelte-check": "^4.0.1",
|
||||||
"tailwindcss": "^3.4.10",
|
"tailwindcss": "^3.4.10",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.5.4",
|
||||||
"vite": "^5.4.2"
|
"vite": "^5.4.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@alloc/quick-lru": {
|
"node_modules/@alloc/quick-lru": {
|
||||||
|
@ -462,9 +462,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/config-array": {
|
"node_modules/@eslint/config-array": {
|
||||||
"version": "0.17.0",
|
"version": "0.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz",
|
||||||
"integrity": "sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==",
|
"integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint/object-schema": "^2.1.4",
|
"@eslint/object-schema": "^2.1.4",
|
||||||
|
@ -499,9 +499,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "9.7.0",
|
"version": "9.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.10.0.tgz",
|
||||||
"integrity": "sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==",
|
"integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
@ -516,6 +516,18 @@
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@eslint/plugin-kit": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.1.0.tgz",
|
||||||
|
"integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"levn": "^0.4.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@floating-ui/core": {
|
"node_modules/@floating-ui/core": {
|
||||||
"version": "1.6.2",
|
"version": "1.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.2.tgz",
|
||||||
|
@ -949,9 +961,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sveltejs/kit": {
|
"node_modules/@sveltejs/kit": {
|
||||||
"version": "2.5.25",
|
"version": "2.5.26",
|
||||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.25.tgz",
|
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.26.tgz",
|
||||||
"integrity": "sha512-5hBSEN8XEjDZ5+2bHkFh8Z0QyOk0C187cyb12aANe1c8aeKbfu5ZD5XaC2vEH4h0alJFDXPdUkXQBmeeXeMr1A==",
|
"integrity": "sha512-8l1JTIM2L+bS8ebq1E+nGjv/YSKSnD9Q19bYIUkc41vaEG2JjVUx6ikvPIJv2hkQAuqJLzoPrXlKk4KcyWOv3Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -1038,9 +1050,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/eslint": {
|
"node_modules/@types/eslint": {
|
||||||
"version": "8.56.10",
|
"version": "9.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
|
||||||
"integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==",
|
"integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "*",
|
"@types/estree": "*",
|
||||||
|
@ -1059,12 +1071,6 @@
|
||||||
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
|
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/pug": {
|
|
||||||
"version": "2.0.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz",
|
|
||||||
"integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/@vincjo/datatables": {
|
"node_modules/@vincjo/datatables": {
|
||||||
"version": "1.14.10",
|
"version": "1.14.10",
|
||||||
"resolved": "https://registry.npmjs.org/@vincjo/datatables/-/datatables-1.14.10.tgz",
|
"resolved": "https://registry.npmjs.org/@vincjo/datatables/-/datatables-1.14.10.tgz",
|
||||||
|
@ -1293,15 +1299,6 @@
|
||||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/buffer-crc32": {
|
|
||||||
"version": "0.2.13",
|
|
||||||
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
|
||||||
"integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/callsites": {
|
"node_modules/callsites": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||||
|
@ -1537,15 +1534,6 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/detect-indent": {
|
|
||||||
"version": "6.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
|
|
||||||
"integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/devalue": {
|
"node_modules/devalue": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.0.0.tgz",
|
||||||
|
@ -1582,12 +1570,6 @@
|
||||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/es6-promise": {
|
|
||||||
"version": "3.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
|
|
||||||
"integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.21.5",
|
"version": "0.21.5",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
|
||||||
|
@ -1648,16 +1630,17 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "9.7.0",
|
"version": "9.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.10.0.tgz",
|
||||||
"integrity": "sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==",
|
"integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.11.0",
|
"@eslint-community/regexpp": "^4.11.0",
|
||||||
"@eslint/config-array": "^0.17.0",
|
"@eslint/config-array": "^0.18.0",
|
||||||
"@eslint/eslintrc": "^3.1.0",
|
"@eslint/eslintrc": "^3.1.0",
|
||||||
"@eslint/js": "9.7.0",
|
"@eslint/js": "9.10.0",
|
||||||
|
"@eslint/plugin-kit": "^0.1.0",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
"@humanwhocodes/retry": "^0.3.0",
|
"@humanwhocodes/retry": "^0.3.0",
|
||||||
"@nodelib/fs.walk": "^1.2.8",
|
"@nodelib/fs.walk": "^1.2.8",
|
||||||
|
@ -1680,7 +1663,6 @@
|
||||||
"is-glob": "^4.0.0",
|
"is-glob": "^4.0.0",
|
||||||
"is-path-inside": "^3.0.3",
|
"is-path-inside": "^3.0.3",
|
||||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||||
"levn": "^0.4.1",
|
|
||||||
"lodash.merge": "^4.6.2",
|
"lodash.merge": "^4.6.2",
|
||||||
"minimatch": "^3.1.2",
|
"minimatch": "^3.1.2",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
|
@ -1696,6 +1678,14 @@
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://eslint.org/donate"
|
"url": "https://eslint.org/donate"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"jiti": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"jiti": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-compat-utils": {
|
"node_modules/eslint-compat-utils": {
|
||||||
|
@ -2004,12 +1994,6 @@
|
||||||
"url": "https://github.com/sponsors/rawify"
|
"url": "https://github.com/sponsors/rawify"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fs.realpath": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
|
@ -2033,27 +2017,6 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/glob": {
|
|
||||||
"version": "7.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
|
||||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
|
||||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"fs.realpath": "^1.0.0",
|
|
||||||
"inflight": "^1.0.4",
|
|
||||||
"inherits": "2",
|
|
||||||
"minimatch": "^3.1.1",
|
|
||||||
"once": "^1.3.0",
|
|
||||||
"path-is-absolute": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/glob-parent": {
|
"node_modules/glob-parent": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||||
|
@ -2090,12 +2053,6 @@
|
||||||
"integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
|
"integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/graceful-fs": {
|
|
||||||
"version": "4.2.11",
|
|
||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
|
||||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/has-flag": {
|
"node_modules/has-flag": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
|
@ -2161,23 +2118,6 @@
|
||||||
"node": ">=0.8.19"
|
"node": ">=0.8.19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/inflight": {
|
|
||||||
"version": "1.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
|
||||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
|
||||||
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"once": "^1.3.0",
|
|
||||||
"wrappy": "1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/inherits": {
|
|
||||||
"version": "2.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
|
||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/is-binary-path": {
|
"node_modules/is-binary-path": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
|
@ -2447,15 +2387,6 @@
|
||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/min-indent": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mini-svg-data-uri": {
|
"node_modules/mini-svg-data-uri": {
|
||||||
"version": "1.4.4",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
|
||||||
|
@ -2477,15 +2408,6 @@
|
||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/minimist": {
|
|
||||||
"version": "1.2.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
|
||||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
|
||||||
"dev": true,
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/minipass": {
|
"node_modules/minipass": {
|
||||||
"version": "7.1.2",
|
"version": "7.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||||
|
@ -2495,18 +2417,6 @@
|
||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mkdirp": {
|
|
||||||
"version": "0.5.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
|
||||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"minimist": "^1.2.6"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"mkdirp": "bin/cmd.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mri": {
|
"node_modules/mri": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
||||||
|
@ -2608,15 +2518,6 @@
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/once": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
|
||||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"wrappy": "1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/optionator": {
|
"node_modules/optionator": {
|
||||||
"version": "0.9.4",
|
"version": "0.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||||
|
@ -2685,15 +2586,6 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/path-is-absolute": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/path-key": {
|
"node_modules/path-key": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
|
@ -2773,9 +2665,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.43",
|
"version": "8.4.45",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.43.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz",
|
||||||
"integrity": "sha512-gJAQVYbh5R3gYm33FijzCZj7CHyQ3hWMgJMprLUlIYqCwTeZhBQ19wp0e9mA25BUbEvY5+EXuuaAjqQsrBxQBQ==",
|
"integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -3065,19 +2957,6 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rimraf": {
|
|
||||||
"version": "2.7.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
|
||||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
|
||||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"glob": "^7.1.3"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"rimraf": "bin.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.21.0",
|
"version": "4.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz",
|
||||||
|
@ -3148,18 +3027,6 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sander": {
|
|
||||||
"version": "0.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz",
|
|
||||||
"integrity": "sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"es6-promise": "^3.1.2",
|
|
||||||
"graceful-fs": "^4.1.3",
|
|
||||||
"mkdirp": "^0.5.1",
|
|
||||||
"rimraf": "^2.5.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.6.2",
|
"version": "7.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
|
||||||
|
@ -3225,21 +3092,6 @@
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sorcery": {
|
|
||||||
"version": "0.11.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.0.tgz",
|
|
||||||
"integrity": "sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.14",
|
|
||||||
"buffer-crc32": "^0.2.5",
|
|
||||||
"minimist": "^1.2.0",
|
|
||||||
"sander": "^0.5.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"sorcery": "bin/sorcery"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
||||||
|
@ -3339,18 +3191,6 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/strip-indent": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"min-indent": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/strip-json-comments": {
|
"node_modules/strip-json-comments": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
|
||||||
|
@ -3481,23 +3321,54 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svelte-check": {
|
"node_modules/svelte-check": {
|
||||||
"version": "3.8.6",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.8.6.tgz",
|
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.0.1.tgz",
|
||||||
"integrity": "sha512-ij0u4Lw/sOTREP13BdWZjiXD/BlHE6/e2e34XzmVmsp5IN4kVa3PWP65NM32JAgwjZlwBg/+JtiNV1MM8khu0Q==",
|
"integrity": "sha512-AuWnCZdREoOzMhoptHPUUPYUxLNdXSkoZnPnlv19SZJJimRzLmjjZLKsOiRB4AnhgX+56/WSEdvkWXI/q2BSsA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/trace-mapping": "^0.3.17",
|
"@jridgewell/trace-mapping": "^0.3.25",
|
||||||
"chokidar": "^3.4.1",
|
"chokidar": "^3.4.1",
|
||||||
|
"fdir": "^6.2.0",
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.0.0",
|
||||||
"sade": "^1.7.4",
|
"sade": "^1.7.4"
|
||||||
"svelte-preprocess": "^5.1.3",
|
|
||||||
"typescript": "^5.0.3"
|
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"svelte-check": "bin/svelte-check"
|
"svelte-check": "bin/svelte-check"
|
||||||
},
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18.0.0"
|
||||||
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0"
|
"svelte": "^4.0.0 || ^5.0.0-next.0",
|
||||||
|
"typescript": ">=5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svelte-check/node_modules/fdir": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-QOnuT+BOtivR77wYvCWHfGt9s4Pz1VIMbD463vegT5MLqNXy8rYFT/lPVEqf/bhYeT6qmqrNHhsX+rWwe3rOCQ==",
|
||||||
|
"dev": true,
|
||||||
|
"peerDependencies": {
|
||||||
|
"picomatch": "^3 || ^4"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"picomatch": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svelte-check/node_modules/picomatch": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svelte-eslint-parser": {
|
"node_modules/svelte-eslint-parser": {
|
||||||
|
@ -3584,68 +3455,6 @@
|
||||||
"svelte": "^3.19.0 || ^4.0.0"
|
"svelte": "^3.19.0 || ^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svelte-preprocess": {
|
|
||||||
"version": "5.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.4.tgz",
|
|
||||||
"integrity": "sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA==",
|
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/pug": "^2.0.6",
|
|
||||||
"detect-indent": "^6.1.0",
|
|
||||||
"magic-string": "^0.30.5",
|
|
||||||
"sorcery": "^0.11.0",
|
|
||||||
"strip-indent": "^3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 16.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@babel/core": "^7.10.2",
|
|
||||||
"coffeescript": "^2.5.1",
|
|
||||||
"less": "^3.11.3 || ^4.0.0",
|
|
||||||
"postcss": "^7 || ^8",
|
|
||||||
"postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0",
|
|
||||||
"pug": "^3.0.0",
|
|
||||||
"sass": "^1.26.8",
|
|
||||||
"stylus": "^0.55.0",
|
|
||||||
"sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0",
|
|
||||||
"svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0",
|
|
||||||
"typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@babel/core": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"coffeescript": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"less": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"postcss": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"postcss-load-config": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"pug": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"sass": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"stylus": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"sugarss": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"typescript": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "3.4.10",
|
"version": "3.4.10",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz",
|
||||||
|
@ -3877,13 +3686,13 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "5.4.2",
|
"version": "5.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.4.tgz",
|
||||||
"integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==",
|
"integrity": "sha512-RHFCkULitycHVTtelJ6jQLd+KSAAzOgEYorV32R2q++M6COBjKJR6BxqClwp5sf0XaBDjVMuJ9wnNfyAJwjMkA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.21.3",
|
"esbuild": "^0.21.3",
|
||||||
"postcss": "^8.4.41",
|
"postcss": "^8.4.43",
|
||||||
"rollup": "^4.20.0"
|
"rollup": "^4.20.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -4067,12 +3876,6 @@
|
||||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/wrappy": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/yaml": {
|
"node_modules/yaml": {
|
||||||
"version": "1.10.2",
|
"version": "1.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||||
|
|
|
@ -16,24 +16,24 @@
|
||||||
"@skeletonlabs/skeleton": "^2.10.2",
|
"@skeletonlabs/skeleton": "^2.10.2",
|
||||||
"@skeletonlabs/tw-plugin": "^0.4.0",
|
"@skeletonlabs/tw-plugin": "^0.4.0",
|
||||||
"@sveltejs/adapter-static": "^3.0.4",
|
"@sveltejs/adapter-static": "^3.0.4",
|
||||||
"@sveltejs/kit": "^2.5.25",
|
"@sveltejs/kit": "^2.5.26",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.1.2",
|
"@sveltejs/vite-plugin-svelte": "^3.1.2",
|
||||||
"@tailwindcss/forms": "^0.5.8",
|
"@tailwindcss/forms": "^0.5.8",
|
||||||
"@types/eslint": "^8.56.0",
|
"@types/eslint": "^9.6.1",
|
||||||
"@vincjo/datatables": "^1.14.10",
|
"@vincjo/datatables": "^1.14.10",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"date-fns": "^3.3.1",
|
"date-fns": "^3.3.1",
|
||||||
"eslint": "^9.7.0",
|
"eslint": "^9.10.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-svelte": "^2.43.0",
|
"eslint-plugin-svelte": "^2.43.0",
|
||||||
"postcss": "^8.4.43",
|
"postcss": "^8.4.45",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"prettier-plugin-svelte": "^3.2.6",
|
"prettier-plugin-svelte": "^3.2.6",
|
||||||
"svelte": "^4.2.19",
|
"svelte": "^4.2.19",
|
||||||
"svelte-check": "^3.8.6",
|
"svelte-check": "^4.0.1",
|
||||||
"tailwindcss": "^3.4.10",
|
"tailwindcss": "^3.4.10",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.5.4",
|
||||||
"vite": "^5.4.2"
|
"vite": "^5.4.4"
|
||||||
},
|
},
|
||||||
"type": "module"
|
"type": "module"
|
||||||
}
|
}
|
||||||
|
|
1
frontend/src/app.d.ts
vendored
1
frontend/src/app.d.ts
vendored
|
@ -21,6 +21,7 @@ declare global {
|
||||||
is_tor: boolean;
|
is_tor: boolean;
|
||||||
is_available: boolean;
|
is_available: boolean;
|
||||||
nettype: string;
|
nettype: string;
|
||||||
|
ip_addresses: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ApiResponse {
|
interface ApiResponse {
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
<script>
|
<script>
|
||||||
import { getModalStore } from '@skeletonlabs/skeleton';
|
import { getModalStore } from '@skeletonlabs/skeleton';
|
||||||
|
import { formatHostname } from '$lib/utils/strings';
|
||||||
|
|
||||||
const modalStore = getModalStore();
|
const modalStore = getModalStore();
|
||||||
/** @type {string} */
|
|
||||||
export let ip;
|
|
||||||
/** @type {boolean} */
|
|
||||||
export let is_tor;
|
|
||||||
/** @type {string} */
|
|
||||||
export let hostname;
|
|
||||||
/** @type {number} */
|
|
||||||
export let port;
|
|
||||||
|
|
||||||
// if (is_tor) {
|
/**
|
||||||
// hostname = hostname.substring(0, 8) + '[...].onion';
|
* @type {{
|
||||||
// }
|
* is_tor: boolean,
|
||||||
|
* hostname: string,
|
||||||
|
* port: number,
|
||||||
|
* ipv6_only: boolean
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
export let is_tor;
|
||||||
|
export let hostname;
|
||||||
|
export let port;
|
||||||
|
export let ipv6_only;
|
||||||
|
/** @type {string} */
|
||||||
|
export let ip_addresses;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} onionAddr
|
* @param {string} onionAddr
|
||||||
|
@ -33,15 +37,20 @@
|
||||||
|
|
||||||
{#if is_tor}
|
{#if is_tor}
|
||||||
<button
|
<button
|
||||||
class="max-w-32 truncate text-orange-800 dark:text-orange-300"
|
class="max-w-40 truncate text-orange-800 dark:text-orange-300"
|
||||||
on:click={() => modalAlert(hostname, port)}
|
on:click={() => modalAlert(hostname, port)}
|
||||||
>
|
>
|
||||||
👁 {hostname}
|
👁 {hostname}
|
||||||
</button><br />.onion:<span class="text-indigo-800 dark:text-indigo-400">{port}</span>
|
</button><br />.onion:<span class="text-indigo-800 dark:text-indigo-400">{port}</span>
|
||||||
<span class="text-gray-700 dark:text-gray-400">(TOR)</span>
|
<span class="text-gray-700 dark:text-gray-400">(TOR)</span>
|
||||||
{:else}
|
{:else}
|
||||||
{hostname}:<span class="text-indigo-800 dark:text-indigo-400">{port}</span>
|
{formatHostname(hostname)}:<span class="text-indigo-800 dark:text-indigo-400">{port}</span><br />
|
||||||
{#if ip !== ''}
|
<div class="max-w-40 text-ellipsis overflow-x-auto md:overflow-hidden hover:overflow-visible">
|
||||||
<br /><span class="text-gray-700 dark:text-gray-400">{ip}</span>
|
<span class="whitespace-break-spaces text-gray-700 dark:text-gray-400"
|
||||||
{/if}
|
>{ip_addresses.replace(/,/g, ' ')}</span
|
||||||
|
>
|
||||||
|
{#if ipv6_only}
|
||||||
|
<span class="text-rose-800 dark:text-rose-400">(IPv6 only)</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -1,3 +1,26 @@
|
||||||
|
/**
|
||||||
|
* Modifies the input string based on whether it is an IPv6 address.
|
||||||
|
* If the input is an IPv6 address, it wraps it in square brackets `[ ]`.
|
||||||
|
* Otherwise, it returns the input string as-is (for domain names or
|
||||||
|
* IPv4 addresses). AND I'M SORRY USING REGEX FOR THIS!
|
||||||
|
*
|
||||||
|
* @param {string} hostname
|
||||||
|
* @returns {string} - The modified string, IPv6 addresses wrapped in `[ ]`.
|
||||||
|
*/
|
||||||
|
export const formatHostname = (hostname) => {
|
||||||
|
// const ipv6Pattern = /^(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$/; // full
|
||||||
|
// pattern for both full and compressed IPv6 addresses.
|
||||||
|
// source: https://regex101.com/library/cP9mH9?filterFlavors=dotnet&filterFlavors=javascript&orderBy=RELEVANCE&search=ip
|
||||||
|
// This may be incorrect, but let's assume it's correct. xD
|
||||||
|
const ipv6Pattern =
|
||||||
|
/^(([0-9A-Fa-f]{1,4}:){7})([0-9A-Fa-f]{1,4})$|(([0-9A-Fa-f]{1,4}:){1,6}:)(([0-9A-Fa-f]{1,4}:){0,4})([0-9A-Fa-f]{1,4})$/;
|
||||||
|
if (ipv6Pattern.test(hostname)) {
|
||||||
|
return `[${hostname}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hostname;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} bytes
|
* @param {number} bytes
|
||||||
* @param {number} decimals
|
* @param {number} decimals
|
||||||
|
|
|
@ -4,7 +4,7 @@ export async function load() {
|
||||||
meta: {
|
meta: {
|
||||||
title: 'Monero Remote Node',
|
title: 'Monero Remote Node',
|
||||||
description:
|
description:
|
||||||
'A website that helps you monitor your favourite Monero remote nodes, a device on the internet running the Monero software with copy of the Monero blockchain.',
|
'A website that helps you monitor your favourite Monero remote nodes, but YOU BETTER RUN AND USE YOUR OWN NODE.',
|
||||||
keywords:
|
keywords:
|
||||||
'monero,monero,xmr,monero node,xmrnode,cryptocurrency,monero remote node,monero testnet,monero stagenet'
|
'monero,monero,xmr,monero node,xmrnode,cryptocurrency,monero remote node,monero testnet,monero stagenet'
|
||||||
},
|
},
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
|
|
||||||
<section id="form-add-monero-node">
|
<section id="form-add-monero-node">
|
||||||
<div class="section-container text-center">
|
<div class="section-container text-center">
|
||||||
<p>Enter your Monero node information below (IPv4 host only):</p>
|
<p>Enter your Monero node information below (IPv6 host check is experimental):</p>
|
||||||
|
|
||||||
<form
|
<form
|
||||||
class="mx-auto w-full max-w-3xl py-2"
|
class="mx-auto w-full max-w-3xl py-2"
|
||||||
|
|
|
@ -4,7 +4,7 @@ export async function load() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
meta: {
|
meta: {
|
||||||
title: 'Public Monero Remote Nodes List',
|
title: 'Public Monero Remote Nodes List',
|
||||||
description: 'List of public Monero remote nodes that you can use with your favourite Monero wallet. You can filter by country, protocol, or CORS capable nodes.',
|
description: "Although it's possible to use these existing public Monero nodes, you're MUST RUN AND USE YOUR OWN NODE!",
|
||||||
keywords: 'monero remote nodes,public monero nodes,monero public nodes,monero wallet,tor monero node,monero cors rpc'
|
keywords: 'monero remote nodes,public monero nodes,monero public nodes,monero wallet,tor monero node,monero cors rpc'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -79,7 +79,7 @@
|
||||||
<section id="introduction ">
|
<section id="introduction ">
|
||||||
<div class="section-container text-center !max-w-4xl">
|
<div class="section-container text-center !max-w-4xl">
|
||||||
<p>Remote node can be used by people who, for their own reasons (usually because of hardware requirements, disk space, or technical abilities), cannot/don't want to run their own node and prefer to relay on one publicly available on the Monero network.</p>
|
<p>Remote node can be used by people who, for their own reasons (usually because of hardware requirements, disk space, or technical abilities), cannot/don't want to run their own node and prefer to relay on one publicly available on the Monero network.</p>
|
||||||
<p>Using an open node will allow to make a transaction instantaneously, without the need to download the blockchain and sync to the Monero network first, but at the cost of the control over your privacy. the <strong>Monero community suggests to always run your own node</strong> to obtain the maximum possible privacy and to help decentralize the network.</p>
|
<p>Using an open node will allow to make a transaction instantaneously, without the need to download the blockchain and sync to the Monero network first, but at the cost of the control over your privacy. the <strong>Monero community suggests to <span class="font-extrabold text-2xl underline decoration-double decoration-2 decoration-pink-500">always run and use your own node</span></strong> to obtain the maximum possible privacy and to help decentralize the network.</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -211,12 +211,12 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td
|
<td
|
||||||
><HostPortCell
|
><HostPortCell
|
||||||
ip={row.ip}
|
ip_addresses={row.ip_addresses}
|
||||||
is_tor={row.is_tor}
|
is_tor={row.is_tor}
|
||||||
hostname={row.hostname}
|
hostname={row.hostname}
|
||||||
port={row.port}
|
port={row.port}
|
||||||
|
ipv6_only={row.ipv6_only}
|
||||||
/>
|
/>
|
||||||
<a class="anchor" href="/remote-nodes/logs/?node_id={row.id}">[Logs]</a>
|
|
||||||
</td>
|
</td>
|
||||||
<td><NetTypeCell nettype={row.nettype} height={row.height} /></td>
|
<td><NetTypeCell nettype={row.nettype} height={row.height} /></td>
|
||||||
<td><ProtocolCell protocol={row.protocol} cors={row.cors} /></td>
|
<td><ProtocolCell protocol={row.protocol} cors={row.cors} /></td>
|
||||||
|
@ -241,7 +241,13 @@
|
||||||
majority_fee={majorityFee[row.nettype]}
|
majority_fee={majorityFee[row.nettype]}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td><UptimeCell uptime={row.uptime} /></td>
|
<td
|
||||||
|
><UptimeCell uptime={row.uptime} /><br />
|
||||||
|
<a
|
||||||
|
class="anchor !text-purple-800 dark:!text-purple-400"
|
||||||
|
href="/remote-nodes/logs/?node_id={row.id}">[Logs]</a
|
||||||
|
>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{format(row.last_checked * 1000, 'PP HH:mm')}<br />
|
{format(row.last_checked * 1000, 'PP HH:mm')}<br />
|
||||||
{formatDistance(row.last_checked * 1000, new Date(), { addSuffix: true })}
|
{formatDistance(row.last_checked * 1000, new Date(), { addSuffix: true })}
|
||||||
|
@ -278,7 +284,12 @@
|
||||||
rel="noopener">still can return high fee only if you about to create a transactions</a
|
rel="noopener">still can return high fee only if you about to create a transactions</a
|
||||||
>.
|
>.
|
||||||
</li>
|
</li>
|
||||||
<li><strong>The best and safest way is running your own node</strong>!</li>
|
<li>
|
||||||
|
<strong
|
||||||
|
class="font-extrabold text-2xl underline decoration-double decoration-2 decoration-pink-500"
|
||||||
|
>The best and safest way is running your own node!</strong
|
||||||
|
>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Nodes with 0% uptime within 1 month with more than 300 check attempt will be removed. You
|
Nodes with 0% uptime within 1 month with more than 300 check attempt will be removed. You
|
||||||
can always add your node again latter.
|
can always add your node again latter.
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import { format, formatDistance } from 'date-fns';
|
import { format, formatDistance } from 'date-fns';
|
||||||
import { loadData, loadNodeInfo } from './api-handler';
|
import { loadData, loadNodeInfo } from './api-handler';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { formatHashes, formatBytes } from '$lib/utils/strings';
|
import { formatHostname, formatHashes, formatBytes } from '$lib/utils/strings';
|
||||||
import {
|
import {
|
||||||
DtSrRowsPerPage,
|
DtSrRowsPerPage,
|
||||||
DtSrThSort,
|
DtSrThSort,
|
||||||
|
@ -71,11 +71,11 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="font-bold">Hostname:Port</td>
|
<td class="font-bold">Hostname:Port</td>
|
||||||
<td>{nodeInfo?.hostname}:{nodeInfo?.port}</td>
|
<td>{formatHostname(nodeInfo?.hostname)}:{nodeInfo?.port}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="font-bold">Public IP</td>
|
<td class="font-bold">Public IP</td>
|
||||||
<td>{nodeInfo?.ip}</td>
|
<td>{nodeInfo?.ip_addresses.replace(/,/g, ', ')}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="font-bold">Net Type</td>
|
<td class="font-bold">Net Type</td>
|
||||||
|
|
|
@ -24,6 +24,7 @@ type App struct {
|
||||||
APIKey string
|
APIKey string
|
||||||
AcceptTor bool
|
AcceptTor bool
|
||||||
TorSOCKS string
|
TorSOCKS string
|
||||||
|
IPv6Capable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -65,4 +66,5 @@ func LoadApp() {
|
||||||
app.APIKey = os.Getenv("API_KEY")
|
app.APIKey = os.Getenv("API_KEY")
|
||||||
app.AcceptTor, _ = strconv.ParseBool(os.Getenv("ACCEPT_TOR"))
|
app.AcceptTor, _ = strconv.ParseBool(os.Getenv("ACCEPT_TOR"))
|
||||||
app.TorSOCKS = os.Getenv("TOR_SOCKS")
|
app.TorSOCKS = os.Getenv("TOR_SOCKS")
|
||||||
|
app.IPv6Capable, _ = strconv.ParseBool(os.Getenv("IPV6_CAPABLE"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
|
|
||||||
type migrateFn func(*DB) error
|
type migrateFn func(*DB) error
|
||||||
|
|
||||||
var dbMigrate = [...]migrateFn{v1, v2}
|
var dbMigrate = [...]migrateFn{v1, v2, v3}
|
||||||
|
|
||||||
func MigrateDb(db *DB) error {
|
func MigrateDb(db *DB) error {
|
||||||
version := getSchemaVersion(db)
|
version := getSchemaVersion(db)
|
||||||
|
@ -256,3 +256,19 @@ func v2(db *DB) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func v3(db *DB) error {
|
||||||
|
slog.Debug("[DB] Migrating database schema version 3")
|
||||||
|
|
||||||
|
// table: tbl_node
|
||||||
|
slog.Debug("[DB] Adding additional columns to tbl_node")
|
||||||
|
_, err := db.Exec(`
|
||||||
|
ALTER TABLE tbl_node
|
||||||
|
ADD COLUMN ipv6_only TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER cors_capable,
|
||||||
|
ADD COLUMN ip_addresses TEXT NOT NULL DEFAULT '' AFTER cors_capable;`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -171,9 +171,10 @@ func Countries(c *fiber.Ctx) error {
|
||||||
// This handler should protected by `CheckProber` middleware.
|
// This handler should protected by `CheckProber` middleware.
|
||||||
func GiveJob(c *fiber.Ctx) error {
|
func GiveJob(c *fiber.Ctx) error {
|
||||||
acceptTor := c.QueryInt("accept_tor", 0)
|
acceptTor := c.QueryInt("accept_tor", 0)
|
||||||
|
acceptIPv6 := c.QueryInt("accept_ipv6", 0)
|
||||||
|
|
||||||
moneroRepo := monero.New()
|
moneroRepo := monero.New()
|
||||||
node, err := moneroRepo.GiveJob(acceptTor)
|
node, err := moneroRepo.GiveJob(acceptTor, acceptIPv6)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"status": "error",
|
"status": "error",
|
||||||
|
|
28
internal/ip/ip.go
Normal file
28
internal/ip/ip.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// Package ip provides IP address related functions
|
||||||
|
package ip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsIPv6Only returns true if all given IPs are IPv6
|
||||||
|
func IsIPv6Only(ips []net.IP) bool {
|
||||||
|
for _, ip := range ips {
|
||||||
|
if ip.To4() != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceToString converts []net.IP to a string separated by comma.
|
||||||
|
// If the separator is empty, it defaults to ",".
|
||||||
|
func SliceToString(ips []net.IP) string {
|
||||||
|
r := make([]string, len(ips))
|
||||||
|
for i, j := range ips {
|
||||||
|
r[i] = j.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(r, ",")
|
||||||
|
}
|
86
internal/ip/ip_test.go
Normal file
86
internal/ip/ip_test.go
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package ip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Single test: go test ./internal/ip -bench TestIsIPv6Only -benchmem -run=^$ -v
|
||||||
|
func TestIsIPv6Only(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
ips []net.IP
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "IPv4",
|
||||||
|
ips: []net.IP{
|
||||||
|
net.ParseIP("1.1.1.1"),
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IPv6",
|
||||||
|
ips: []net.IP{
|
||||||
|
net.ParseIP("2606:4700::6810:85e5"),
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IPv6 and IPv4",
|
||||||
|
ips: []net.IP{
|
||||||
|
net.ParseIP("1.1.1.1"),
|
||||||
|
net.ParseIP("2606:4700::6810:84e5"),
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := IsIPv6Only(tt.ips); got != tt.want {
|
||||||
|
t.Errorf("IsIPv6Only() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single test: go test ./internal/ip -bench TestSliceToString -benchmem -run=^$ -v
|
||||||
|
func TestSliceToString(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
ips []net.IP
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "IPv4",
|
||||||
|
ips: []net.IP{
|
||||||
|
net.ParseIP("1.1.1.1"),
|
||||||
|
},
|
||||||
|
want: "1.1.1.1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IPv6",
|
||||||
|
ips: []net.IP{
|
||||||
|
net.ParseIP("2606:4700::6810:85e5"),
|
||||||
|
},
|
||||||
|
want: "2606:4700::6810:85e5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IPv6 and IPv4",
|
||||||
|
ips: []net.IP{
|
||||||
|
net.ParseIP("1.1.1.1"),
|
||||||
|
net.ParseIP("2606:4700::6810:85e5"),
|
||||||
|
},
|
||||||
|
want: "1.1.1.1,2606:4700::6810:85e5",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := SliceToString(tt.ips); got != tt.want {
|
||||||
|
t.Errorf("SliceToString() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/database"
|
"github.com/ditatompel/xmr-remote-nodes/internal/database"
|
||||||
|
"github.com/ditatompel/xmr-remote-nodes/internal/ip"
|
||||||
"github.com/jmoiron/sqlx/types"
|
"github.com/jmoiron/sqlx/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -54,6 +54,8 @@ type Node struct {
|
||||||
FailedCount uint `json:"failed_count,omitempty" db:"failed_count"`
|
FailedCount uint `json:"failed_count,omitempty" db:"failed_count"`
|
||||||
LastCheckStatus types.JSONText `json:"last_check_statuses" db:"last_check_status"`
|
LastCheckStatus types.JSONText `json:"last_check_statuses" db:"last_check_status"`
|
||||||
CORSCapable bool `json:"cors" db:"cors_capable"`
|
CORSCapable bool `json:"cors" db:"cors_capable"`
|
||||||
|
IPv6Only bool `json:"ipv6_only" db:"ipv6_only"`
|
||||||
|
IPAddresses string `json:"ip_addresses" db:"ip_addresses"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get node from database by id
|
// Get node from database by id
|
||||||
|
@ -195,7 +197,10 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
|
||||||
if strings.HasSuffix(hostname, ".onion") {
|
if strings.HasSuffix(hostname, ".onion") {
|
||||||
is_tor = true
|
is_tor = true
|
||||||
}
|
}
|
||||||
ip := ""
|
|
||||||
|
ipAddr := ""
|
||||||
|
ips := ""
|
||||||
|
ipv6_only := false
|
||||||
|
|
||||||
if !is_tor {
|
if !is_tor {
|
||||||
hostIps, err := net.LookupIP(hostname)
|
hostIps, err := net.LookupIP(hostname)
|
||||||
|
@ -203,10 +208,9 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
hostIp := hostIps[0].To4()
|
ipv6_only = ip.IsIPv6Only(hostIps)
|
||||||
if hostIp == nil {
|
|
||||||
return errors.New("Host IP is not IPv4")
|
hostIp := hostIps[0]
|
||||||
}
|
|
||||||
if hostIp.IsPrivate() {
|
if hostIp.IsPrivate() {
|
||||||
return errors.New("IP address is private")
|
return errors.New("IP address is private")
|
||||||
}
|
}
|
||||||
|
@ -214,7 +218,8 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
|
||||||
return errors.New("IP address is loopback address")
|
return errors.New("IP address is loopback address")
|
||||||
}
|
}
|
||||||
|
|
||||||
ip = hostIp.String()
|
ipAddr = hostIp.String()
|
||||||
|
ips = ip.SliceToString(hostIps)
|
||||||
}
|
}
|
||||||
|
|
||||||
row, err := r.db.Query(`
|
row, err := r.db.Query(`
|
||||||
|
@ -248,7 +253,9 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
|
||||||
lon,
|
lon,
|
||||||
date_entered,
|
date_entered,
|
||||||
last_checked,
|
last_checked,
|
||||||
last_check_status
|
last_check_status,
|
||||||
|
ip_addresses,
|
||||||
|
ipv6_only
|
||||||
) VALUES (
|
) VALUES (
|
||||||
?,
|
?,
|
||||||
?,
|
?,
|
||||||
|
@ -260,6 +267,8 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
|
||||||
?,
|
?,
|
||||||
?,
|
?,
|
||||||
?,
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
?
|
?
|
||||||
)`,
|
)`,
|
||||||
protocol,
|
protocol,
|
||||||
|
@ -267,12 +276,14 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
|
||||||
port,
|
port,
|
||||||
is_tor,
|
is_tor,
|
||||||
"",
|
"",
|
||||||
ip,
|
ipAddr,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
time.Now().Unix(),
|
time.Now().Unix(),
|
||||||
0,
|
0,
|
||||||
string(statusDb))
|
string(statusDb),
|
||||||
|
ips,
|
||||||
|
ipv6_only)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/geo"
|
"github.com/ditatompel/xmr-remote-nodes/internal/ip/geo"
|
||||||
)
|
)
|
||||||
|
|
||||||
type QueryLogs struct {
|
type QueryLogs struct {
|
||||||
|
@ -108,7 +108,7 @@ func (r *moneroRepo) Logs(q QueryLogs) (FetchLogs, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GiveJob returns node that should be probed for the next time
|
// GiveJob returns node that should be probed for the next time
|
||||||
func (r *moneroRepo) GiveJob(acceptTor int) (Node, error) {
|
func (r *moneroRepo) GiveJob(acceptTor, acceptIPv6 int) (Node, error) {
|
||||||
args := []interface{}{}
|
args := []interface{}{}
|
||||||
wq := []string{}
|
wq := []string{}
|
||||||
where := ""
|
where := ""
|
||||||
|
@ -117,6 +117,10 @@ func (r *moneroRepo) GiveJob(acceptTor int) (Node, error) {
|
||||||
wq = append(wq, "is_tor = ?")
|
wq = append(wq, "is_tor = ?")
|
||||||
args = append(args, 0)
|
args = append(args, 0)
|
||||||
}
|
}
|
||||||
|
if acceptIPv6 != 1 {
|
||||||
|
wq = append(wq, "ipv6_only = ?")
|
||||||
|
args = append(args, 0)
|
||||||
|
}
|
||||||
|
|
||||||
if len(wq) > 0 {
|
if len(wq) > 0 {
|
||||||
where = "WHERE " + strings.Join(wq, " AND ")
|
where = "WHERE " + strings.Join(wq, " AND ")
|
||||||
|
@ -304,7 +308,9 @@ func (r *moneroRepo) ProcessJob(report ProbeReport, proberId int64) error {
|
||||||
city = ?,
|
city = ?,
|
||||||
last_checked = ?,
|
last_checked = ?,
|
||||||
last_check_status = ?,
|
last_check_status = ?,
|
||||||
cors_capable = ?
|
cors_capable = ?,
|
||||||
|
ip_addresses = ?,
|
||||||
|
ipv6_only = ?
|
||||||
WHERE
|
WHERE
|
||||||
id = ?`
|
id = ?`
|
||||||
_, err := r.db.Exec(update,
|
_, err := r.db.Exec(update,
|
||||||
|
@ -326,6 +332,8 @@ func (r *moneroRepo) ProcessJob(report ProbeReport, proberId int64) error {
|
||||||
now.Unix(),
|
now.Unix(),
|
||||||
statuses,
|
statuses,
|
||||||
report.Node.CORSCapable,
|
report.Node.CORSCapable,
|
||||||
|
report.Node.IPAddresses,
|
||||||
|
report.Node.IPv6Only,
|
||||||
report.Node.ID)
|
report.Node.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Warn(err.Error())
|
slog.Warn(err.Error())
|
||||||
|
@ -337,10 +345,12 @@ func (r *moneroRepo) ProcessJob(report ProbeReport, proberId int64) error {
|
||||||
is_available = ?,
|
is_available = ?,
|
||||||
uptime = ?,
|
uptime = ?,
|
||||||
last_checked = ?,
|
last_checked = ?,
|
||||||
last_check_status = ?
|
last_check_status = ?,
|
||||||
|
ip_addresses = ?,
|
||||||
|
ipv6_only = ?
|
||||||
WHERE
|
WHERE
|
||||||
id = ?`
|
id = ?`
|
||||||
if _, err := r.db.Exec(u, 0, report.Node.Uptime, now.Unix(), statuses, report.Node.ID); err != nil {
|
if _, err := r.db.Exec(u, 0, report.Node.Uptime, now.Unix(), statuses, report.Node.IPAddresses, report.Node.IPv6Only, report.Node.ID); err != nil {
|
||||||
slog.Warn(err.Error())
|
slog.Warn(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue