fix(dns,routes): keep DoH socket-handler routes runtime-only and prune stale persisted entries
This commit is contained in:
@@ -1,5 +1,14 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-04-14 - 13.17.6 - fix(dns,routes)
|
||||||
|
keep DoH socket-handler routes runtime-only and prune stale persisted entries
|
||||||
|
|
||||||
|
- stops persisting generated DNS-over-HTTPS routes that depend on live socket handlers
|
||||||
|
- removes stale persisted runtime-only DoH routes from RouteDoc during startup
|
||||||
|
- applies runtime DNS routes alongside DB-backed routes through RouteConfigManager
|
||||||
|
- updates DnsManager warning to clarify that dnsNsDomains is still required for nameserver and DoH bootstrap
|
||||||
|
- adds tests covering runtime DoH route application, stale route pruning, and updated DNS warning behavior
|
||||||
|
|
||||||
## 2026-04-13 - 13.17.5 - fix(vpn,target-profiles)
|
## 2026-04-13 - 13.17.5 - fix(vpn,target-profiles)
|
||||||
normalize target profile route references and stabilize VPN host-IP client routing behavior
|
normalize target profile route references and stabilize VPN host-IP client routing behavior
|
||||||
|
|
||||||
|
|||||||
@@ -51,10 +51,10 @@
|
|||||||
"@push.rocks/smartmetrics": "^3.0.3",
|
"@push.rocks/smartmetrics": "^3.0.3",
|
||||||
"@push.rocks/smartmigration": "1.2.0",
|
"@push.rocks/smartmigration": "1.2.0",
|
||||||
"@push.rocks/smartmta": "^5.3.1",
|
"@push.rocks/smartmta": "^5.3.1",
|
||||||
"@push.rocks/smartnetwork": "^4.5.2",
|
"@push.rocks/smartnetwork": "^4.6.0",
|
||||||
"@push.rocks/smartpath": "^6.0.0",
|
"@push.rocks/smartpath": "^6.0.0",
|
||||||
"@push.rocks/smartpromise": "^4.2.3",
|
"@push.rocks/smartpromise": "^4.2.3",
|
||||||
"@push.rocks/smartproxy": "^27.6.0",
|
"@push.rocks/smartproxy": "^27.7.0",
|
||||||
"@push.rocks/smartradius": "^1.1.1",
|
"@push.rocks/smartradius": "^1.1.1",
|
||||||
"@push.rocks/smartrequest": "^5.0.1",
|
"@push.rocks/smartrequest": "^5.0.1",
|
||||||
"@push.rocks/smartrx": "^3.0.10",
|
"@push.rocks/smartrx": "^3.0.10",
|
||||||
@@ -62,12 +62,12 @@
|
|||||||
"@push.rocks/smartunique": "^3.0.9",
|
"@push.rocks/smartunique": "^3.0.9",
|
||||||
"@push.rocks/smartvpn": "1.19.2",
|
"@push.rocks/smartvpn": "1.19.2",
|
||||||
"@push.rocks/taskbuffer": "^8.0.2",
|
"@push.rocks/taskbuffer": "^8.0.2",
|
||||||
"@serve.zone/catalog": "^2.12.3",
|
"@serve.zone/catalog": "^2.12.4",
|
||||||
"@serve.zone/interfaces": "^5.3.0",
|
"@serve.zone/interfaces": "^5.3.0",
|
||||||
"@serve.zone/remoteingress": "^4.15.3",
|
"@serve.zone/remoteingress": "^4.15.3",
|
||||||
"@tsclass/tsclass": "^9.5.0",
|
"@tsclass/tsclass": "^9.5.0",
|
||||||
"@types/qrcode": "^1.5.6",
|
"@types/qrcode": "^1.5.6",
|
||||||
"lru-cache": "^11.3.3",
|
"lru-cache": "^11.3.5",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"uuid": "^13.0.0"
|
"uuid": "^13.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
67
pnpm-lock.yaml
generated
67
pnpm-lock.yaml
generated
@@ -72,8 +72,8 @@ importers:
|
|||||||
specifier: ^5.3.1
|
specifier: ^5.3.1
|
||||||
version: 5.3.1
|
version: 5.3.1
|
||||||
'@push.rocks/smartnetwork':
|
'@push.rocks/smartnetwork':
|
||||||
specifier: ^4.5.2
|
specifier: ^4.6.0
|
||||||
version: 4.5.2
|
version: 4.6.0
|
||||||
'@push.rocks/smartpath':
|
'@push.rocks/smartpath':
|
||||||
specifier: ^6.0.0
|
specifier: ^6.0.0
|
||||||
version: 6.0.0
|
version: 6.0.0
|
||||||
@@ -81,8 +81,8 @@ importers:
|
|||||||
specifier: ^4.2.3
|
specifier: ^4.2.3
|
||||||
version: 4.2.3
|
version: 4.2.3
|
||||||
'@push.rocks/smartproxy':
|
'@push.rocks/smartproxy':
|
||||||
specifier: ^27.6.0
|
specifier: ^27.7.0
|
||||||
version: 27.6.0
|
version: 27.7.0
|
||||||
'@push.rocks/smartradius':
|
'@push.rocks/smartradius':
|
||||||
specifier: ^1.1.1
|
specifier: ^1.1.1
|
||||||
version: 1.1.1
|
version: 1.1.1
|
||||||
@@ -105,8 +105,8 @@ importers:
|
|||||||
specifier: ^8.0.2
|
specifier: ^8.0.2
|
||||||
version: 8.0.2
|
version: 8.0.2
|
||||||
'@serve.zone/catalog':
|
'@serve.zone/catalog':
|
||||||
specifier: ^2.12.3
|
specifier: ^2.12.4
|
||||||
version: 2.12.3(@tiptap/pm@2.27.2)
|
version: 2.12.4(@tiptap/pm@2.27.2)
|
||||||
'@serve.zone/interfaces':
|
'@serve.zone/interfaces':
|
||||||
specifier: ^5.3.0
|
specifier: ^5.3.0
|
||||||
version: 5.3.0
|
version: 5.3.0
|
||||||
@@ -120,8 +120,8 @@ importers:
|
|||||||
specifier: ^1.5.6
|
specifier: ^1.5.6
|
||||||
version: 1.5.6
|
version: 1.5.6
|
||||||
lru-cache:
|
lru-cache:
|
||||||
specifier: ^11.3.3
|
specifier: ^11.3.5
|
||||||
version: 11.3.3
|
version: 11.3.5
|
||||||
qrcode:
|
qrcode:
|
||||||
specifier: ^1.5.4
|
specifier: ^1.5.4
|
||||||
version: 1.5.4
|
version: 1.5.4
|
||||||
@@ -365,9 +365,6 @@ packages:
|
|||||||
'@design.estate/dees-element@2.2.4':
|
'@design.estate/dees-element@2.2.4':
|
||||||
resolution: {integrity: sha512-O9cA6flBMMd+pBwMQrZXwAWel9yVxgokolb+Em6gvkXxPJ0P/B5UDn4Vc2d4ts3ta55PTBm+l2dPeDVGx/bl7Q==}
|
resolution: {integrity: sha512-O9cA6flBMMd+pBwMQrZXwAWel9yVxgokolb+Em6gvkXxPJ0P/B5UDn4Vc2d4ts3ta55PTBm+l2dPeDVGx/bl7Q==}
|
||||||
|
|
||||||
'@design.estate/dees-wcctools@3.8.0':
|
|
||||||
resolution: {integrity: sha512-CC14iVKUrguzD9jIrdPBd9fZ4egVJEZMxl5y8iy0l7WLumeoYvGsoXj5INVkRPLRVLqziIdi4Je1hXqHt2NU+g==}
|
|
||||||
|
|
||||||
'@design.estate/dees-wcctools@3.9.0':
|
'@design.estate/dees-wcctools@3.9.0':
|
||||||
resolution: {integrity: sha512-0vZBaGBEGIbl8hx+8BezIIea3U5T7iSHHF9VqlJZGf+nOFIW4zBAxcCljH8YzZ1Yayp6BEjxp/pQXjHN2YB3Jg==}
|
resolution: {integrity: sha512-0vZBaGBEGIbl8hx+8BezIIea3U5T7iSHHF9VqlJZGf+nOFIW4zBAxcCljH8YzZ1Yayp6BEjxp/pQXjHN2YB3Jg==}
|
||||||
|
|
||||||
@@ -1260,8 +1257,8 @@ packages:
|
|||||||
'@push.rocks/smartmustache@3.0.2':
|
'@push.rocks/smartmustache@3.0.2':
|
||||||
resolution: {integrity: sha512-G3LyRXoJhyM+iQhkvP/MR/2WYMvC9U7zc2J44JxUM5tPdkQ+o3++FbfRtnZj6rz5X/A7q03//vsxPitVQwoi2Q==}
|
resolution: {integrity: sha512-G3LyRXoJhyM+iQhkvP/MR/2WYMvC9U7zc2J44JxUM5tPdkQ+o3++FbfRtnZj6rz5X/A7q03//vsxPitVQwoi2Q==}
|
||||||
|
|
||||||
'@push.rocks/smartnetwork@4.5.2':
|
'@push.rocks/smartnetwork@4.6.0':
|
||||||
resolution: {integrity: sha512-lbMMyc2f/WWd5+qzZyF1ynXndjCtasxPWmj/d8GUuis9rDrW7sLIT1PlAPC2F6Qsy4H/K32JrYU+01d/6sWObg==}
|
resolution: {integrity: sha512-ubaO/Qp8r30A+qwk33M/0+nQi+o8gNHEI9zq3jv1MwqiLxhiV1hnbr4CL9AUcvs4EhwUBiw0EswKjCJROwDqvQ==}
|
||||||
|
|
||||||
'@push.rocks/smartnftables@1.1.0':
|
'@push.rocks/smartnftables@1.1.0':
|
||||||
resolution: {integrity: sha512-7JNzerlW20HEl2wKMBIHltwneCQRpXiD2lJkXZZc02ctnfjgFejXVDIeWomhPx6PZ0Z6zmqdF6rrFDtDHyqqfA==}
|
resolution: {integrity: sha512-7JNzerlW20HEl2wKMBIHltwneCQRpXiD2lJkXZZc02ctnfjgFejXVDIeWomhPx6PZ0Z6zmqdF6rrFDtDHyqqfA==}
|
||||||
@@ -1287,8 +1284,8 @@ packages:
|
|||||||
'@push.rocks/smartpromise@4.2.3':
|
'@push.rocks/smartpromise@4.2.3':
|
||||||
resolution: {integrity: sha512-Ycg/TJR+tMt+S3wSFurOpEoW6nXv12QBtKXgBcjMZ4RsdO28geN46U09osPn9N9WuwQy1PkmTV5J/V4F9U8qEw==}
|
resolution: {integrity: sha512-Ycg/TJR+tMt+S3wSFurOpEoW6nXv12QBtKXgBcjMZ4RsdO28geN46U09osPn9N9WuwQy1PkmTV5J/V4F9U8qEw==}
|
||||||
|
|
||||||
'@push.rocks/smartproxy@27.6.0':
|
'@push.rocks/smartproxy@27.7.0':
|
||||||
resolution: {integrity: sha512-1mPzabUKhlC0EdeI7Hjee/aiptTsOLftbq8oWBTlIg9JhCQwkIs5UNGTJV/VvlEflJKnay8TbzLzlr95gUr/1w==}
|
resolution: {integrity: sha512-0u8HF5ocQ2xmfCN1FWyulGTddZ4ZkWaip1j0alT8Bc/LdIYerjKtNJCU4N2wMk/Zz0Wl5UQOmBm4qIWmgRiEcg==}
|
||||||
|
|
||||||
'@push.rocks/smartpuppeteer@2.0.5':
|
'@push.rocks/smartpuppeteer@2.0.5':
|
||||||
resolution: {integrity: sha512-yK/qSeWVHIGWRp3c8S5tfdGP6WCKllZC4DR8d8CQlEjszOSBmHtlTdyyqOMBZ/BA4kd+eU5f3A1r4K2tGYty1g==}
|
resolution: {integrity: sha512-yK/qSeWVHIGWRp3c8S5tfdGP6WCKllZC4DR8d8CQlEjszOSBmHtlTdyyqOMBZ/BA4kd+eU5f3A1r4K2tGYty1g==}
|
||||||
@@ -1591,8 +1588,8 @@ packages:
|
|||||||
'@selderee/plugin-htmlparser2@0.11.0':
|
'@selderee/plugin-htmlparser2@0.11.0':
|
||||||
resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
|
resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
|
||||||
|
|
||||||
'@serve.zone/catalog@2.12.3':
|
'@serve.zone/catalog@2.12.4':
|
||||||
resolution: {integrity: sha512-/QLFjFcy/ig6cdr4517smSc/VCutW/qF/8lCM3v7tpQ5yLApjqiL314Dyvk9zzSwHpw69IeuM9EmPOeTuCY0iQ==}
|
resolution: {integrity: sha512-GRfJZ0yQxChUy7Gp4mxhuN5y4GXZMOEk0W7rJiyZbezA938q+pFTplb9ahSaEHjiUht1MmTu/5WtoJFwgAP8SQ==}
|
||||||
|
|
||||||
'@serve.zone/interfaces@5.3.0':
|
'@serve.zone/interfaces@5.3.0':
|
||||||
resolution: {integrity: sha512-venO7wtDR9ixzD9NhdERBGjNKbFA5LL0yHw4eqGh0UpmvtXVc3SFG0uuHDilOKMZqZ8bttV88qVsFy1aSTJrtA==}
|
resolution: {integrity: sha512-venO7wtDR9ixzD9NhdERBGjNKbFA5LL0yHw4eqGh0UpmvtXVc3SFG0uuHDilOKMZqZ8bttV88qVsFy1aSTJrtA==}
|
||||||
@@ -3085,8 +3082,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==}
|
resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==}
|
||||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
|
|
||||||
lru-cache@11.3.3:
|
lru-cache@11.3.5:
|
||||||
resolution: {integrity: sha512-JvNw9Y81y33E+BEYPr0U7omo+U9AySnsMsEiXgwT6yqd31VQWTLNQqmT4ou5eqPFUrTfIDFta2wKhB1hyohtAQ==}
|
resolution: {integrity: sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==}
|
||||||
engines: {node: 20 || >=22}
|
engines: {node: 20 || >=22}
|
||||||
|
|
||||||
lru-cache@7.18.3:
|
lru-cache@7.18.3:
|
||||||
@@ -4937,18 +4934,6 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
'@design.estate/dees-wcctools@3.8.0':
|
|
||||||
dependencies:
|
|
||||||
'@design.estate/dees-domtools': 2.5.4
|
|
||||||
'@design.estate/dees-element': 2.2.4
|
|
||||||
'@push.rocks/smartdelay': 3.0.5
|
|
||||||
lit: 3.3.2
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- '@nuxt/kit'
|
|
||||||
- react
|
|
||||||
- supports-color
|
|
||||||
- vue
|
|
||||||
|
|
||||||
'@design.estate/dees-wcctools@3.9.0':
|
'@design.estate/dees-wcctools@3.9.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@design.estate/dees-domtools': 2.5.4
|
'@design.estate/dees-domtools': 2.5.4
|
||||||
@@ -5169,7 +5154,7 @@ snapshots:
|
|||||||
'@push.rocks/smartjson': 6.0.0
|
'@push.rocks/smartjson': 6.0.0
|
||||||
'@push.rocks/smartlog': 3.2.2
|
'@push.rocks/smartlog': 3.2.2
|
||||||
'@push.rocks/smartmongo': 5.1.1(socks@2.8.7)
|
'@push.rocks/smartmongo': 5.1.1(socks@2.8.7)
|
||||||
'@push.rocks/smartnetwork': 4.5.2
|
'@push.rocks/smartnetwork': 4.6.0
|
||||||
'@push.rocks/smartpath': 6.0.0
|
'@push.rocks/smartpath': 6.0.0
|
||||||
'@push.rocks/smartpromise': 4.2.3
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
'@push.rocks/smartrequest': 5.0.1
|
'@push.rocks/smartrequest': 5.0.1
|
||||||
@@ -5972,7 +5957,7 @@ snapshots:
|
|||||||
'@push.rocks/smartdelay': 3.0.5
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
'@push.rocks/smartdns': 7.9.0
|
'@push.rocks/smartdns': 7.9.0
|
||||||
'@push.rocks/smartlog': 3.2.2
|
'@push.rocks/smartlog': 3.2.2
|
||||||
'@push.rocks/smartnetwork': 4.5.2
|
'@push.rocks/smartnetwork': 4.6.0
|
||||||
'@push.rocks/smartstring': 4.1.0
|
'@push.rocks/smartstring': 4.1.0
|
||||||
'@push.rocks/smarttime': 4.2.3
|
'@push.rocks/smarttime': 4.2.3
|
||||||
'@push.rocks/smartunique': 3.0.9
|
'@push.rocks/smartunique': 3.0.9
|
||||||
@@ -6429,7 +6414,7 @@ snapshots:
|
|||||||
'@push.rocks/smartpath': 6.0.0
|
'@push.rocks/smartpath': 6.0.0
|
||||||
'@push.rocks/smartrust': 1.3.2
|
'@push.rocks/smartrust': 1.3.2
|
||||||
'@tsclass/tsclass': 9.5.0
|
'@tsclass/tsclass': 9.5.0
|
||||||
lru-cache: 11.3.3
|
lru-cache: 11.3.5
|
||||||
mailparser: 3.9.6
|
mailparser: 3.9.6
|
||||||
uuid: 13.0.0
|
uuid: 13.0.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -6439,7 +6424,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
handlebars: 4.7.9
|
handlebars: 4.7.9
|
||||||
|
|
||||||
'@push.rocks/smartnetwork@4.5.2':
|
'@push.rocks/smartnetwork@4.6.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@push.rocks/smartdns': 7.9.0
|
'@push.rocks/smartdns': 7.9.0
|
||||||
'@push.rocks/smartrust': 1.3.2
|
'@push.rocks/smartrust': 1.3.2
|
||||||
@@ -6500,7 +6485,7 @@ snapshots:
|
|||||||
'@push.rocks/smartdelay': 3.0.5
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
'@push.rocks/smartfs': 1.5.0
|
'@push.rocks/smartfs': 1.5.0
|
||||||
'@push.rocks/smartjimp': 1.2.0
|
'@push.rocks/smartjimp': 1.2.0
|
||||||
'@push.rocks/smartnetwork': 4.5.2
|
'@push.rocks/smartnetwork': 4.6.0
|
||||||
'@push.rocks/smartpath': 6.0.0
|
'@push.rocks/smartpath': 6.0.0
|
||||||
'@push.rocks/smartpromise': 4.2.3
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
'@push.rocks/smartpuppeteer': 2.0.5(typescript@6.0.2)
|
'@push.rocks/smartpuppeteer': 2.0.5(typescript@6.0.2)
|
||||||
@@ -6521,7 +6506,7 @@ snapshots:
|
|||||||
|
|
||||||
'@push.rocks/smartpromise@4.2.3': {}
|
'@push.rocks/smartpromise@4.2.3': {}
|
||||||
|
|
||||||
'@push.rocks/smartproxy@27.6.0':
|
'@push.rocks/smartproxy@27.7.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@push.rocks/smartcrypto': 2.0.4
|
'@push.rocks/smartcrypto': 2.0.4
|
||||||
'@push.rocks/smartlog': 3.2.2
|
'@push.rocks/smartlog': 3.2.2
|
||||||
@@ -6923,12 +6908,12 @@ snapshots:
|
|||||||
domhandler: 5.0.3
|
domhandler: 5.0.3
|
||||||
selderee: 0.11.0
|
selderee: 0.11.0
|
||||||
|
|
||||||
'@serve.zone/catalog@2.12.3(@tiptap/pm@2.27.2)':
|
'@serve.zone/catalog@2.12.4(@tiptap/pm@2.27.2)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@design.estate/dees-catalog': 3.78.2(@tiptap/pm@2.27.2)
|
'@design.estate/dees-catalog': 3.78.2(@tiptap/pm@2.27.2)
|
||||||
'@design.estate/dees-domtools': 2.5.4
|
'@design.estate/dees-domtools': 2.5.4
|
||||||
'@design.estate/dees-element': 2.2.4
|
'@design.estate/dees-element': 2.2.4
|
||||||
'@design.estate/dees-wcctools': 3.8.0
|
'@design.estate/dees-wcctools': 3.9.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@nuxt/kit'
|
- '@nuxt/kit'
|
||||||
- '@tiptap/pm'
|
- '@tiptap/pm'
|
||||||
@@ -8659,7 +8644,7 @@ snapshots:
|
|||||||
|
|
||||||
lowercase-keys@3.0.0: {}
|
lowercase-keys@3.0.0: {}
|
||||||
|
|
||||||
lru-cache@11.3.3: {}
|
lru-cache@11.3.5: {}
|
||||||
|
|
||||||
lru-cache@7.18.3: {}
|
lru-cache@7.18.3: {}
|
||||||
|
|
||||||
@@ -9304,7 +9289,7 @@ snapshots:
|
|||||||
|
|
||||||
path-scurry@2.0.2:
|
path-scurry@2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
lru-cache: 11.3.3
|
lru-cache: 11.3.5
|
||||||
minipass: 7.1.3
|
minipass: 7.1.3
|
||||||
|
|
||||||
path-to-regexp@8.4.2: {}
|
path-to-regexp@8.4.2: {}
|
||||||
|
|||||||
230
test/test.dns-runtime-routes.node.ts
Normal file
230
test/test.dns-runtime-routes.node.ts
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||||
|
import { DcRouter } from '../ts/classes.dcrouter.js';
|
||||||
|
import { RouteConfigManager } from '../ts/config/index.js';
|
||||||
|
import { DcRouterDb, DomainDoc, RouteDoc } from '../ts/db/index.js';
|
||||||
|
import { DnsManager } from '../ts/dns/manager.dns.js';
|
||||||
|
import { logger } from '../ts/logger.js';
|
||||||
|
import * as plugins from '../ts/plugins.js';
|
||||||
|
|
||||||
|
const createTestDb = async () => {
|
||||||
|
const storagePath = plugins.path.join(
|
||||||
|
plugins.os.tmpdir(),
|
||||||
|
`dcrouter-dns-runtime-routes-${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
DcRouterDb.resetInstance();
|
||||||
|
const db = DcRouterDb.getInstance({
|
||||||
|
storagePath,
|
||||||
|
dbName: `dcrouter-test-${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
||||||
|
});
|
||||||
|
await db.start();
|
||||||
|
await db.getDb().mongoDb.createCollection('__test_init');
|
||||||
|
|
||||||
|
return {
|
||||||
|
async cleanup() {
|
||||||
|
await db.stop();
|
||||||
|
DcRouterDb.resetInstance();
|
||||||
|
await plugins.fs.promises.rm(storagePath, { recursive: true, force: true });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const testDbPromise = createTestDb();
|
||||||
|
|
||||||
|
const clearTestState = async () => {
|
||||||
|
for (const route of await RouteDoc.findAll()) {
|
||||||
|
await route.delete();
|
||||||
|
}
|
||||||
|
for (const domain of await DomainDoc.findAll()) {
|
||||||
|
await domain.delete();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tap.test('RouteConfigManager applies runtime DoH routes without persisting them', async () => {
|
||||||
|
await testDbPromise;
|
||||||
|
await clearTestState();
|
||||||
|
|
||||||
|
const dcRouter = new DcRouter({
|
||||||
|
dnsNsDomains: ['ns1.example.com', 'ns2.example.com'],
|
||||||
|
dnsScopes: ['example.com'],
|
||||||
|
smartProxyConfig: { routes: [] },
|
||||||
|
dbConfig: { enabled: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
const appliedRoutes: any[][] = [];
|
||||||
|
const smartProxy = {
|
||||||
|
updateRoutes: async (routes: any[]) => {
|
||||||
|
appliedRoutes.push(routes);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const routeManager = new RouteConfigManager(
|
||||||
|
() => smartProxy as any,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
() => (dcRouter as any).generateDnsRoutes(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await routeManager.initialize([], [], []);
|
||||||
|
await routeManager.applyRoutes();
|
||||||
|
|
||||||
|
const persistedRoutes = await RouteDoc.findAll();
|
||||||
|
expect(persistedRoutes.length).toEqual(0);
|
||||||
|
expect(appliedRoutes.length).toEqual(2);
|
||||||
|
|
||||||
|
for (const routeSet of appliedRoutes) {
|
||||||
|
const dnsQueryRoute = routeSet.find((route) => route.name === 'dns-over-https-dns-query');
|
||||||
|
const resolveRoute = routeSet.find((route) => route.name === 'dns-over-https-resolve');
|
||||||
|
|
||||||
|
expect(dnsQueryRoute).toBeDefined();
|
||||||
|
expect(resolveRoute).toBeDefined();
|
||||||
|
expect(typeof dnsQueryRoute.action.socketHandler).toEqual('function');
|
||||||
|
expect(typeof resolveRoute.action.socketHandler).toEqual('function');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('RouteConfigManager removes stale persisted DoH socket-handler routes on startup', async () => {
|
||||||
|
await testDbPromise;
|
||||||
|
await clearTestState();
|
||||||
|
|
||||||
|
const staleDnsQueryRoute = new RouteDoc();
|
||||||
|
staleDnsQueryRoute.id = 'stale-doh-query';
|
||||||
|
staleDnsQueryRoute.route = {
|
||||||
|
name: 'dns-over-https-dns-query',
|
||||||
|
match: {
|
||||||
|
ports: [443],
|
||||||
|
domains: ['ns1.example.com'],
|
||||||
|
path: '/dns-query',
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
type: 'socket-handler' as any,
|
||||||
|
} as any,
|
||||||
|
};
|
||||||
|
staleDnsQueryRoute.enabled = true;
|
||||||
|
staleDnsQueryRoute.createdAt = Date.now();
|
||||||
|
staleDnsQueryRoute.updatedAt = Date.now();
|
||||||
|
staleDnsQueryRoute.createdBy = 'test';
|
||||||
|
staleDnsQueryRoute.origin = 'dns';
|
||||||
|
await staleDnsQueryRoute.save();
|
||||||
|
|
||||||
|
const staleResolveRoute = new RouteDoc();
|
||||||
|
staleResolveRoute.id = 'stale-doh-resolve';
|
||||||
|
staleResolveRoute.route = {
|
||||||
|
name: 'dns-over-https-resolve',
|
||||||
|
match: {
|
||||||
|
ports: [443],
|
||||||
|
domains: ['ns1.example.com'],
|
||||||
|
path: '/resolve',
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
type: 'socket-handler' as any,
|
||||||
|
} as any,
|
||||||
|
};
|
||||||
|
staleResolveRoute.enabled = true;
|
||||||
|
staleResolveRoute.createdAt = Date.now();
|
||||||
|
staleResolveRoute.updatedAt = Date.now();
|
||||||
|
staleResolveRoute.createdBy = 'test';
|
||||||
|
staleResolveRoute.origin = 'dns';
|
||||||
|
await staleResolveRoute.save();
|
||||||
|
|
||||||
|
const validRoute = new RouteDoc();
|
||||||
|
validRoute.id = 'valid-forward-route';
|
||||||
|
validRoute.route = {
|
||||||
|
name: 'valid-forward-route',
|
||||||
|
match: {
|
||||||
|
ports: [443],
|
||||||
|
domains: ['app.example.com'],
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
type: 'forward',
|
||||||
|
targets: [{ host: '127.0.0.1', port: 8443 }],
|
||||||
|
tls: { mode: 'terminate' as const },
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
validRoute.enabled = true;
|
||||||
|
validRoute.createdAt = Date.now();
|
||||||
|
validRoute.updatedAt = Date.now();
|
||||||
|
validRoute.createdBy = 'test';
|
||||||
|
validRoute.origin = 'api';
|
||||||
|
await validRoute.save();
|
||||||
|
|
||||||
|
const appliedRoutes: any[][] = [];
|
||||||
|
const smartProxy = {
|
||||||
|
updateRoutes: async (routes: any[]) => {
|
||||||
|
appliedRoutes.push(routes);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const routeManager = new RouteConfigManager(() => smartProxy as any);
|
||||||
|
await routeManager.initialize([], [], []);
|
||||||
|
|
||||||
|
expect((await RouteDoc.findByName('dns-over-https-dns-query'))).toEqual(null);
|
||||||
|
expect((await RouteDoc.findByName('dns-over-https-resolve'))).toEqual(null);
|
||||||
|
|
||||||
|
const remainingRoutes = await RouteDoc.findAll();
|
||||||
|
expect(remainingRoutes.length).toEqual(1);
|
||||||
|
expect(remainingRoutes[0].route.name).toEqual('valid-forward-route');
|
||||||
|
|
||||||
|
expect(appliedRoutes.length).toEqual(1);
|
||||||
|
expect(appliedRoutes[0].length).toEqual(1);
|
||||||
|
expect(appliedRoutes[0][0].name).toEqual('valid-forward-route');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('DnsManager warning keeps dnsNsDomains in scope', async () => {
|
||||||
|
await testDbPromise;
|
||||||
|
await clearTestState();
|
||||||
|
const originalLog = logger.log.bind(logger);
|
||||||
|
const warningMessages: string[] = [];
|
||||||
|
|
||||||
|
(logger as any).log = (level: 'error' | 'warn' | 'info' | 'success' | 'debug', message: string, context?: Record<string, any>) => {
|
||||||
|
if (level === 'warn') {
|
||||||
|
warningMessages.push(message);
|
||||||
|
}
|
||||||
|
return originalLog(level, message, context || {});
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const existingDomain = new DomainDoc();
|
||||||
|
existingDomain.id = 'existing-domain';
|
||||||
|
existingDomain.name = 'example.com';
|
||||||
|
existingDomain.source = 'dcrouter';
|
||||||
|
existingDomain.authoritative = true;
|
||||||
|
existingDomain.createdAt = Date.now();
|
||||||
|
existingDomain.updatedAt = Date.now();
|
||||||
|
existingDomain.createdBy = 'test';
|
||||||
|
await existingDomain.save();
|
||||||
|
|
||||||
|
const dnsManager = new DnsManager({
|
||||||
|
dnsNsDomains: ['ns1.example.com'],
|
||||||
|
dnsScopes: ['example.com'],
|
||||||
|
dnsRecords: [{ name: 'www.example.com', type: 'A', value: '127.0.0.1' }],
|
||||||
|
smartProxyConfig: { routes: [] },
|
||||||
|
});
|
||||||
|
|
||||||
|
await dnsManager.start();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
warningMessages.some((message) =>
|
||||||
|
message.includes('ignoring legacy dnsScopes/dnsRecords constructor config')
|
||||||
|
&& message.includes('dnsNsDomains is still required for nameserver and DoH bootstrap'),
|
||||||
|
),
|
||||||
|
).toEqual(true);
|
||||||
|
expect(
|
||||||
|
warningMessages.some((message) =>
|
||||||
|
message.includes('ignoring legacy dnsScopes/dnsRecords/dnsNsDomains constructor config'),
|
||||||
|
),
|
||||||
|
).toEqual(false);
|
||||||
|
} finally {
|
||||||
|
(logger as any).log = originalLog;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('cleanup test db', async () => {
|
||||||
|
await clearTestState();
|
||||||
|
const testDb = await testDbPromise;
|
||||||
|
await testDb.cleanup();
|
||||||
|
});
|
||||||
|
|
||||||
|
export default tap.start();
|
||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/dcrouter',
|
name: '@serve.zone/dcrouter',
|
||||||
version: '13.17.5',
|
version: '13.17.6',
|
||||||
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -315,7 +315,8 @@ export class DcRouter {
|
|||||||
// Seed routes assembled during setupSmartProxy, passed to RouteConfigManager for DB seeding
|
// Seed routes assembled during setupSmartProxy, passed to RouteConfigManager for DB seeding
|
||||||
private seedConfigRoutes: plugins.smartproxy.IRouteConfig[] = [];
|
private seedConfigRoutes: plugins.smartproxy.IRouteConfig[] = [];
|
||||||
private seedEmailRoutes: plugins.smartproxy.IRouteConfig[] = [];
|
private seedEmailRoutes: plugins.smartproxy.IRouteConfig[] = [];
|
||||||
private seedDnsRoutes: plugins.smartproxy.IRouteConfig[] = [];
|
// Runtime-only DoH routes. These carry live socket handlers and must never be persisted.
|
||||||
|
private runtimeDnsRoutes: plugins.smartproxy.IRouteConfig[] = [];
|
||||||
|
|
||||||
// Environment access
|
// Environment access
|
||||||
private qenv = new plugins.qenv.Qenv('./', '.nogit/');
|
private qenv = new plugins.qenv.Qenv('./', '.nogit/');
|
||||||
@@ -580,13 +581,13 @@ export class DcRouter {
|
|||||||
this.tunnelManager.syncAllowedEdges();
|
this.tunnelManager.syncAllowedEdges();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
() => this.runtimeDnsRoutes,
|
||||||
);
|
);
|
||||||
this.apiTokenManager = new ApiTokenManager();
|
this.apiTokenManager = new ApiTokenManager();
|
||||||
await this.apiTokenManager.initialize();
|
await this.apiTokenManager.initialize();
|
||||||
await this.routeConfigManager.initialize(
|
await this.routeConfigManager.initialize(
|
||||||
this.seedConfigRoutes as import('../ts_interfaces/data/remoteingress.js').IDcRouterRouteConfig[],
|
this.seedConfigRoutes as import('../ts_interfaces/data/remoteingress.js').IDcRouterRouteConfig[],
|
||||||
this.seedEmailRoutes as import('../ts_interfaces/data/remoteingress.js').IDcRouterRouteConfig[],
|
this.seedEmailRoutes as import('../ts_interfaces/data/remoteingress.js').IDcRouterRouteConfig[],
|
||||||
this.seedDnsRoutes as import('../ts_interfaces/data/remoteingress.js').IDcRouterRouteConfig[],
|
|
||||||
);
|
);
|
||||||
await this.targetProfileManager.normalizeAllRouteRefs();
|
await this.targetProfileManager.normalizeAllRouteRefs();
|
||||||
|
|
||||||
@@ -892,7 +893,7 @@ export class DcRouter {
|
|||||||
this.smartProxy = undefined;
|
this.smartProxy = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assemble seed routes from constructor config — these will be seeded into DB
|
// Assemble serializable seed routes from constructor config — these will be seeded into DB
|
||||||
// by RouteConfigManager.initialize() when the ConfigManagers service starts.
|
// by RouteConfigManager.initialize() when the ConfigManagers service starts.
|
||||||
this.seedConfigRoutes = (this.options.smartProxyConfig?.routes || []) as plugins.smartproxy.IRouteConfig[];
|
this.seedConfigRoutes = (this.options.smartProxyConfig?.routes || []) as plugins.smartproxy.IRouteConfig[];
|
||||||
logger.log('info', `Found ${this.seedConfigRoutes.length} routes in config`);
|
logger.log('info', `Found ${this.seedConfigRoutes.length} routes in config`);
|
||||||
@@ -903,17 +904,17 @@ export class DcRouter {
|
|||||||
logger.log('debug', 'Email routes generated', { routes: JSON.stringify(this.seedEmailRoutes) });
|
logger.log('debug', 'Email routes generated', { routes: JSON.stringify(this.seedEmailRoutes) });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.seedDnsRoutes = [];
|
this.runtimeDnsRoutes = [];
|
||||||
if (this.options.dnsNsDomains && this.options.dnsNsDomains.length > 0) {
|
if (this.options.dnsNsDomains && this.options.dnsNsDomains.length > 0) {
|
||||||
this.seedDnsRoutes = this.generateDnsRoutes();
|
this.runtimeDnsRoutes = this.generateDnsRoutes();
|
||||||
logger.log('debug', `DNS routes for nameservers ${this.options.dnsNsDomains.join(', ')}`, { routes: JSON.stringify(this.seedDnsRoutes) });
|
logger.log('debug', `DNS routes for nameservers ${this.options.dnsNsDomains.join(', ')}`, { routes: JSON.stringify(this.runtimeDnsRoutes) });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combined routes for SmartProxy bootstrap (before DB routes are loaded)
|
// Combined routes for SmartProxy bootstrap (before DB routes are loaded)
|
||||||
let routes: plugins.smartproxy.IRouteConfig[] = [
|
let routes: plugins.smartproxy.IRouteConfig[] = [
|
||||||
...this.seedConfigRoutes,
|
...this.seedConfigRoutes,
|
||||||
...this.seedEmailRoutes,
|
...this.seedEmailRoutes,
|
||||||
...this.seedDnsRoutes,
|
...this.runtimeDnsRoutes,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Build the ACME options for SmartProxy from the DB-backed AcmeConfigManager.
|
// Build the ACME options for SmartProxy from the DB-backed AcmeConfigManager.
|
||||||
@@ -1463,7 +1464,6 @@ export class DcRouter {
|
|||||||
await this.routeConfigManager.initialize(
|
await this.routeConfigManager.initialize(
|
||||||
this.seedConfigRoutes as import('../ts_interfaces/data/remoteingress.js').IDcRouterRouteConfig[],
|
this.seedConfigRoutes as import('../ts_interfaces/data/remoteingress.js').IDcRouterRouteConfig[],
|
||||||
this.seedEmailRoutes as import('../ts_interfaces/data/remoteingress.js').IDcRouterRouteConfig[],
|
this.seedEmailRoutes as import('../ts_interfaces/data/remoteingress.js').IDcRouterRouteConfig[],
|
||||||
this.seedDnsRoutes as import('../ts_interfaces/data/remoteingress.js').IDcRouterRouteConfig[],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2185,7 +2185,7 @@ export class DcRouter {
|
|||||||
// Pass current bootstrap routes so the manager can derive edge ports initially.
|
// Pass current bootstrap routes so the manager can derive edge ports initially.
|
||||||
// Once RouteConfigManager applies the full DB set, the onRoutesApplied callback
|
// Once RouteConfigManager applies the full DB set, the onRoutesApplied callback
|
||||||
// will push the complete merged routes here.
|
// will push the complete merged routes here.
|
||||||
const bootstrapRoutes = [...this.seedConfigRoutes, ...this.seedEmailRoutes, ...this.seedDnsRoutes];
|
const bootstrapRoutes = [...this.seedConfigRoutes, ...this.seedEmailRoutes, ...this.runtimeDnsRoutes];
|
||||||
this.remoteIngressManager.setRoutes(bootstrapRoutes as any[]);
|
this.remoteIngressManager.setRoutes(bootstrapRoutes as any[]);
|
||||||
|
|
||||||
// If ConfigManagers finished before us, re-apply routes
|
// If ConfigManagers finished before us, re-apply routes
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export class RouteConfigManager {
|
|||||||
private getVpnClientIpsForRoute?: (route: IDcRouterRouteConfig, routeId?: string) => TIpAllowEntry[],
|
private getVpnClientIpsForRoute?: (route: IDcRouterRouteConfig, routeId?: string) => TIpAllowEntry[],
|
||||||
private referenceResolver?: ReferenceResolver,
|
private referenceResolver?: ReferenceResolver,
|
||||||
private onRoutesApplied?: (routes: plugins.smartproxy.IRouteConfig[]) => void,
|
private onRoutesApplied?: (routes: plugins.smartproxy.IRouteConfig[]) => void,
|
||||||
|
private getRuntimeRoutes?: () => plugins.smartproxy.IRouteConfig[],
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/** Expose routes map for reference resolution lookups. */
|
/** Expose routes map for reference resolution lookups. */
|
||||||
@@ -63,7 +64,8 @@ export class RouteConfigManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load persisted routes, seed config/email/dns routes, compute warnings, apply to SmartProxy.
|
* Load persisted routes, seed serializable config/email/dns routes,
|
||||||
|
* compute warnings, and apply the combined DB-backed + runtime route set to SmartProxy.
|
||||||
*/
|
*/
|
||||||
public async initialize(
|
public async initialize(
|
||||||
configRoutes: IDcRouterRouteConfig[] = [],
|
configRoutes: IDcRouterRouteConfig[] = [],
|
||||||
@@ -284,9 +286,12 @@ export class RouteConfigManager {
|
|||||||
|
|
||||||
private async loadRoutes(): Promise<void> {
|
private async loadRoutes(): Promise<void> {
|
||||||
const docs = await RouteDoc.findAll();
|
const docs = await RouteDoc.findAll();
|
||||||
|
let prunedRuntimeRoutes = 0;
|
||||||
|
|
||||||
for (const doc of docs) {
|
for (const doc of docs) {
|
||||||
if (doc.id) {
|
if (!doc.id) continue;
|
||||||
this.routes.set(doc.id, {
|
|
||||||
|
const storedRoute: IRoute = {
|
||||||
id: doc.id,
|
id: doc.id,
|
||||||
route: doc.route,
|
route: doc.route,
|
||||||
enabled: doc.enabled,
|
enabled: doc.enabled,
|
||||||
@@ -295,12 +300,26 @@ export class RouteConfigManager {
|
|||||||
createdBy: doc.createdBy,
|
createdBy: doc.createdBy,
|
||||||
origin: doc.origin || 'api',
|
origin: doc.origin || 'api',
|
||||||
metadata: doc.metadata,
|
metadata: doc.metadata,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (this.isPersistedRuntimeRoute(storedRoute)) {
|
||||||
|
await doc.delete();
|
||||||
|
prunedRuntimeRoutes++;
|
||||||
|
logger.log(
|
||||||
|
'warn',
|
||||||
|
`Removed persisted runtime-only route '${storedRoute.route.name || storedRoute.id}' (${storedRoute.id}) from RouteDoc`,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.routes.set(doc.id, storedRoute);
|
||||||
}
|
}
|
||||||
if (this.routes.size > 0) {
|
if (this.routes.size > 0) {
|
||||||
logger.log('info', `Loaded ${this.routes.size} route(s) from database`);
|
logger.log('info', `Loaded ${this.routes.size} route(s) from database`);
|
||||||
}
|
}
|
||||||
|
if (prunedRuntimeRoutes > 0) {
|
||||||
|
logger.log('info', `Pruned ${prunedRuntimeRoutes} persisted runtime-only route(s) from RouteDoc`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async persistRoute(stored: IRoute): Promise<void> {
|
private async persistRoute(stored: IRoute): Promise<void> {
|
||||||
@@ -389,34 +408,16 @@ export class RouteConfigManager {
|
|||||||
|
|
||||||
const enabledRoutes: plugins.smartproxy.IRouteConfig[] = [];
|
const enabledRoutes: plugins.smartproxy.IRouteConfig[] = [];
|
||||||
|
|
||||||
const http3Config = this.getHttp3Config?.();
|
|
||||||
const vpnCallback = this.getVpnClientIpsForRoute;
|
|
||||||
|
|
||||||
// Helper: inject VPN security into a vpnOnly route
|
|
||||||
const injectVpn = (route: plugins.smartproxy.IRouteConfig, routeId?: string): plugins.smartproxy.IRouteConfig => {
|
|
||||||
if (!vpnCallback) return route;
|
|
||||||
const dcRoute = route as IDcRouterRouteConfig;
|
|
||||||
if (!dcRoute.vpnOnly) return route;
|
|
||||||
const vpnEntries = vpnCallback(dcRoute, routeId);
|
|
||||||
const existingEntries = route.security?.ipAllowList || [];
|
|
||||||
return {
|
|
||||||
...route,
|
|
||||||
security: {
|
|
||||||
...route.security,
|
|
||||||
ipAllowList: [...existingEntries, ...vpnEntries],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add all enabled routes with HTTP/3 and VPN augmentation
|
// Add all enabled routes with HTTP/3 and VPN augmentation
|
||||||
for (const route of this.routes.values()) {
|
for (const route of this.routes.values()) {
|
||||||
if (route.enabled) {
|
if (route.enabled) {
|
||||||
let r = route.route;
|
enabledRoutes.push(this.prepareRouteForApply(route.route, route.id));
|
||||||
if (http3Config?.enabled !== false) {
|
|
||||||
r = augmentRouteWithHttp3(r, { enabled: true, ...http3Config });
|
|
||||||
}
|
}
|
||||||
enabledRoutes.push(injectVpn(r, route.id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const runtimeRoutes = this.getRuntimeRoutes?.() || [];
|
||||||
|
for (const route of runtimeRoutes) {
|
||||||
|
enabledRoutes.push(this.prepareRouteForApply(route));
|
||||||
}
|
}
|
||||||
|
|
||||||
await smartProxy.updateRoutes(enabledRoutes);
|
await smartProxy.updateRoutes(enabledRoutes);
|
||||||
@@ -429,4 +430,47 @@ export class RouteConfigManager {
|
|||||||
logger.log('info', `Applied ${enabledRoutes.length} routes to SmartProxy (${this.routes.size} total)`);
|
logger.log('info', `Applied ${enabledRoutes.length} routes to SmartProxy (${this.routes.size} total)`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private prepareRouteForApply(
|
||||||
|
route: plugins.smartproxy.IRouteConfig,
|
||||||
|
routeId?: string,
|
||||||
|
): plugins.smartproxy.IRouteConfig {
|
||||||
|
let preparedRoute = route;
|
||||||
|
const http3Config = this.getHttp3Config?.();
|
||||||
|
|
||||||
|
if (http3Config?.enabled !== false) {
|
||||||
|
preparedRoute = augmentRouteWithHttp3(preparedRoute, { enabled: true, ...http3Config });
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.injectVpnSecurity(preparedRoute, routeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private injectVpnSecurity(
|
||||||
|
route: plugins.smartproxy.IRouteConfig,
|
||||||
|
routeId?: string,
|
||||||
|
): plugins.smartproxy.IRouteConfig {
|
||||||
|
const vpnCallback = this.getVpnClientIpsForRoute;
|
||||||
|
if (!vpnCallback) return route;
|
||||||
|
|
||||||
|
const dcRoute = route as IDcRouterRouteConfig;
|
||||||
|
if (!dcRoute.vpnOnly) return route;
|
||||||
|
|
||||||
|
const vpnEntries = vpnCallback(dcRoute, routeId);
|
||||||
|
const existingEntries = route.security?.ipAllowList || [];
|
||||||
|
return {
|
||||||
|
...route,
|
||||||
|
security: {
|
||||||
|
...route.security,
|
||||||
|
ipAllowList: [...existingEntries, ...vpnEntries],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private isPersistedRuntimeRoute(storedRoute: IRoute): boolean {
|
||||||
|
const routeName = storedRoute.route.name || '';
|
||||||
|
const actionType = storedRoute.route.action?.type;
|
||||||
|
|
||||||
|
return (routeName.startsWith('dns-over-https-') && actionType === 'socket-handler')
|
||||||
|
|| (storedRoute.origin === 'dns' && actionType === 'socket-handler');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ export class DnsManager {
|
|||||||
if (hasLegacyConfig) {
|
if (hasLegacyConfig) {
|
||||||
logger.log(
|
logger.log(
|
||||||
'warn',
|
'warn',
|
||||||
'DnsManager: DB has DomainDoc entries — ignoring legacy dnsScopes/dnsRecords/dnsNsDomains constructor config. ' +
|
'DnsManager: DB has DomainDoc entries — ignoring legacy dnsScopes/dnsRecords constructor config. ' +
|
||||||
'Manage DNS via the Domains UI instead.',
|
'dnsNsDomains is still required for nameserver and DoH bootstrap unless that moves into DB-backed config.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/dcrouter',
|
name: '@serve.zone/dcrouter',
|
||||||
version: '13.17.5',
|
version: '13.17.6',
|
||||||
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user