Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0fa65f31c3 | |||
| 93d6c7d341 | |||
| b2ccd54079 | |||
| 4e9b09616d | |||
| ddb420835e | |||
| 505fd044c0 | |||
| 7711204fef | |||
| d7b6fbb241 | |||
| a670b27a1c | |||
| c2f57b086f | |||
| 083f16d7b4 | |||
| 2994b6e686 | |||
| ba15c169d7 |
38
changelog.md
38
changelog.md
@@ -1,5 +1,43 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-04-05 - 13.0.6 - fix(certificates)
|
||||
resolve base-domain certificate lookups and route profile list inputs
|
||||
|
||||
- Look up ACME certificate metadata by base domain first, with fallback to the exact domain, so subdomain certificate status and deletion work reliably.
|
||||
- Trigger certificate reprovisioning through SmartProxy routes and clear cached status before refresh, including force-renew cache invalidation handling.
|
||||
- Replace comma-separated target profile form fields with list inputs and route suggestions for domains, targets, and route references.
|
||||
|
||||
## 2026-04-05 - 13.0.5 - fix(ts_web)
|
||||
replace custom section heading component with dees-heading across ops views
|
||||
|
||||
- updates all operations dashboard views to use <dees-heading level="2"> for section titles
|
||||
- removes the unused shared ops-sectionheading component export and source file
|
||||
- bumps UI and data layer dependencies to compatible patch/minor releases
|
||||
|
||||
## 2026-04-05 - 13.0.4 - fix(deps)
|
||||
bump @push.rocks/smartdata and @push.rocks/smartdb to the latest patch releases
|
||||
|
||||
- Updates @push.rocks/smartdata from ^7.1.4 to ^7.1.5
|
||||
- Updates @push.rocks/smartdb from ^2.5.2 to ^2.5.4
|
||||
|
||||
## 2026-04-05 - 13.0.3 - fix(deps)
|
||||
bump @push.rocks/smartdb to ^2.5.2
|
||||
|
||||
- Updates @push.rocks/smartdb from ^2.5.1 to ^2.5.2 in package.json.
|
||||
|
||||
## 2026-04-05 - 13.0.2 - fix(deps)
|
||||
bump smartdata, smartdb, and catalog dependencies
|
||||
|
||||
- updates @push.rocks/smartdata from ^7.1.3 to ^7.1.4
|
||||
- updates @push.rocks/smartdb from ^2.4.1 to ^2.5.1
|
||||
- updates @serve.zone/catalog from ^2.11.1 to ^2.11.2
|
||||
|
||||
## 2026-04-05 - 13.0.1 - fix(deps)
|
||||
bump @design.estate/dees-catalog and @push.rocks/smartdb dependencies
|
||||
|
||||
- updates @design.estate/dees-catalog from ^3.55.6 to ^3.59.1
|
||||
- updates @push.rocks/smartdb from ^2.3.1 to ^2.4.1
|
||||
|
||||
## 2026-04-05 - 13.0.0 - BREAKING CHANGE(vpn)
|
||||
replace tag-based VPN access control with source and target profiles
|
||||
|
||||
|
||||
12
package.json
12
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@serve.zone/dcrouter",
|
||||
"private": false,
|
||||
"version": "13.0.0",
|
||||
"version": "13.0.6",
|
||||
"description": "A multifaceted routing service handling mail and SMS delivery functions.",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
@@ -35,14 +35,14 @@
|
||||
"@api.global/typedserver": "^8.4.6",
|
||||
"@api.global/typedsocket": "^4.1.2",
|
||||
"@apiclient.xyz/cloudflare": "^7.1.0",
|
||||
"@design.estate/dees-catalog": "^3.55.6",
|
||||
"@design.estate/dees-catalog": "^3.61.1",
|
||||
"@design.estate/dees-element": "^2.2.4",
|
||||
"@push.rocks/lik": "^6.4.0",
|
||||
"@push.rocks/projectinfo": "^5.1.0",
|
||||
"@push.rocks/qenv": "^6.1.3",
|
||||
"@push.rocks/smartacme": "^9.4.0",
|
||||
"@push.rocks/smartdata": "^7.1.3",
|
||||
"@push.rocks/smartdb": "^2.3.1",
|
||||
"@push.rocks/smartacme": "^9.5.0",
|
||||
"@push.rocks/smartdata": "^7.1.6",
|
||||
"@push.rocks/smartdb": "^2.5.9",
|
||||
"@push.rocks/smartdns": "^7.9.0",
|
||||
"@push.rocks/smartfs": "^1.5.0",
|
||||
"@push.rocks/smartguard": "^3.1.0",
|
||||
@@ -61,7 +61,7 @@
|
||||
"@push.rocks/smartunique": "^3.0.9",
|
||||
"@push.rocks/smartvpn": "1.19.1",
|
||||
"@push.rocks/taskbuffer": "^8.0.2",
|
||||
"@serve.zone/catalog": "^2.11.1",
|
||||
"@serve.zone/catalog": "^2.11.2",
|
||||
"@serve.zone/interfaces": "^5.3.0",
|
||||
"@serve.zone/remoteingress": "^4.15.3",
|
||||
"@tsclass/tsclass": "^9.5.0",
|
||||
|
||||
102
pnpm-lock.yaml
generated
102
pnpm-lock.yaml
generated
@@ -24,8 +24,8 @@ importers:
|
||||
specifier: ^7.1.0
|
||||
version: 7.1.0
|
||||
'@design.estate/dees-catalog':
|
||||
specifier: ^3.55.6
|
||||
version: 3.55.6(@tiptap/pm@2.27.2)
|
||||
specifier: ^3.61.1
|
||||
version: 3.61.1(@tiptap/pm@2.27.2)
|
||||
'@design.estate/dees-element':
|
||||
specifier: ^2.2.4
|
||||
version: 2.2.4
|
||||
@@ -39,14 +39,14 @@ importers:
|
||||
specifier: ^6.1.3
|
||||
version: 6.1.3
|
||||
'@push.rocks/smartacme':
|
||||
specifier: ^9.4.0
|
||||
version: 9.4.0(socks@2.8.7)
|
||||
specifier: ^9.5.0
|
||||
version: 9.5.0(socks@2.8.7)
|
||||
'@push.rocks/smartdata':
|
||||
specifier: ^7.1.3
|
||||
version: 7.1.3(socks@2.8.7)
|
||||
specifier: ^7.1.6
|
||||
version: 7.1.6(socks@2.8.7)
|
||||
'@push.rocks/smartdb':
|
||||
specifier: ^2.3.1
|
||||
version: 2.3.1(@tiptap/pm@2.27.2)
|
||||
specifier: ^2.5.9
|
||||
version: 2.5.9(@tiptap/pm@2.27.2)(socks@2.8.7)
|
||||
'@push.rocks/smartdns':
|
||||
specifier: ^7.9.0
|
||||
version: 7.9.0
|
||||
@@ -102,8 +102,8 @@ importers:
|
||||
specifier: ^8.0.2
|
||||
version: 8.0.2
|
||||
'@serve.zone/catalog':
|
||||
specifier: ^2.11.1
|
||||
version: 2.11.1(@tiptap/pm@2.27.2)
|
||||
specifier: ^2.11.2
|
||||
version: 2.11.2(@tiptap/pm@2.27.2)
|
||||
'@serve.zone/interfaces':
|
||||
specifier: ^5.3.0
|
||||
version: 5.3.0
|
||||
@@ -350,8 +350,8 @@ packages:
|
||||
'@configvault.io/interfaces@1.0.17':
|
||||
resolution: {integrity: sha512-bEcCUR2VBDJsTin8HQh8Uw/mlYl2v8A3jMIaQ+MTB9Hrqd6CZL2dL7iJdWyFl/3EIX+LDxWFR+Oq7liIq7w+1Q==}
|
||||
|
||||
'@design.estate/dees-catalog@3.55.6':
|
||||
resolution: {integrity: sha512-aBuofV18v2X9U+WXQcwx/uOMofDECYRoqGovpB2cD2gpf5eCvaD2Y6HZetocKeW/VeOFlzwgykeSxAY8KvfiKQ==}
|
||||
'@design.estate/dees-catalog@3.61.1':
|
||||
resolution: {integrity: sha512-RA85O87pRM3QPlncBNB27wJTl+UVGaGtx8l5DaeOhru78agu4+y+ByAdUgS9Ahdpr/ZZVYSAADkZETsf/l08UQ==}
|
||||
|
||||
'@design.estate/dees-comms@1.0.30':
|
||||
resolution: {integrity: sha512-KchMlklJfKAjQiJiR0xmofXtQ27VgZtBIxcMwPE9d+h3jJRv+lPZxzBQVOM0eyM0uS44S5vJMZ11IeV4uDXSHg==}
|
||||
@@ -1108,8 +1108,8 @@ packages:
|
||||
'@push.rocks/qenv@6.1.3':
|
||||
resolution: {integrity: sha512-+z2hsAU/7CIgpYLFqvda8cn9rUBMHqLdQLjsFfRn5jPoD7dJ5rFlpkbhfM4Ws8mHMniwWaxGKo+q/YBhtzRBLg==}
|
||||
|
||||
'@push.rocks/smartacme@9.4.0':
|
||||
resolution: {integrity: sha512-mSqsI859mHI9fCZxLfayzPf/WvukDFzVHOh02vXq3ujxbb5M+ArMnXe0MmC2egR9GeXmQTm3DTENaETX5ffMtw==}
|
||||
'@push.rocks/smartacme@9.5.0':
|
||||
resolution: {integrity: sha512-soOjER2c4umKaOSsB6uq/k08aA9rfd7Dicm6DNX3XB16LjCjldVHpizeOGqRBkFga+VroDQ/rEYecHT5tFiWvg==}
|
||||
|
||||
'@push.rocks/smartarchive@4.2.4':
|
||||
resolution: {integrity: sha512-uiqVAXPxmr8G5rv3uZvZFMOCt8l7cZC3nzvsy4YQqKf/VkPhKIEX+b7LkAeNlxPSYUiBQUkNRoawg9+5BaMcHg==}
|
||||
@@ -1138,11 +1138,14 @@ packages:
|
||||
'@push.rocks/smartcrypto@2.0.4':
|
||||
resolution: {integrity: sha512-1+/5bsjyataf5uUkUNnnVXGRAt+gHVk1KDzozjTqgqJxHvQk1d9fVDohL6CxUhUucTPtu5VR5xNBiV8YCDuGyw==}
|
||||
|
||||
'@push.rocks/smartdata@7.1.3':
|
||||
resolution: {integrity: sha512-7vQJ9pdRk450yn2m9tmGPdSRlQVmxFPZjHD4sGYsfqCQPg+GLFusu+H16zpf+jKzAq4F2ZBMPaYymJHXvXiVcw==}
|
||||
'@push.rocks/smartdata@7.1.5':
|
||||
resolution: {integrity: sha512-7x7VedEg6RocWndqUPuTbY2Bh85Q/x0LOVHL4o/NVXyh3IGNtiVQ8ple4WR0qYqlHRAojX4eDSBPMiYzIasqAg==}
|
||||
|
||||
'@push.rocks/smartdb@2.3.1':
|
||||
resolution: {integrity: sha512-l5/qqv+vXgXJezwcXs1BxsHbGPSSMw8y9lI832yM+WcO0PH4grTYSGNyZjx6hql8pJxSjMiixdnxK9XvIQukzA==}
|
||||
'@push.rocks/smartdata@7.1.6':
|
||||
resolution: {integrity: sha512-GraeLUc/EKjMGwVwD4VWQxkpnF/Tcy0k9n0642kSiM7bRPZWh4IXLMo1bJGPesl1ofsHPR1kXIUsB9LVfUpQsw==}
|
||||
|
||||
'@push.rocks/smartdb@2.5.9':
|
||||
resolution: {integrity: sha512-8GVR0zPi6khm7xFSirhOkD6V1QrikEPW0JCRriGMictNkGb3DhMg8KswSDOYTUlEuvx4ldrJCRZ7CmQu3uiUuQ==}
|
||||
|
||||
'@push.rocks/smartdelay@3.0.5':
|
||||
resolution: {integrity: sha512-mUuI7kj2f7ztjpic96FvRIlf2RsKBa5arw81AHNsndbxO6asRcxuWL8dTVxouEIK8YsBUlj0AsrCkHhMbLQdHw==}
|
||||
@@ -1583,8 +1586,8 @@ packages:
|
||||
'@selderee/plugin-htmlparser2@0.11.0':
|
||||
resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
|
||||
|
||||
'@serve.zone/catalog@2.11.1':
|
||||
resolution: {integrity: sha512-KeeShLELKANWEAY3Z4h+Kx1bmatWLa0nvtw8wa7iHLxV6Vm3rRJNdRyh91ozuWPgFYMC48V8/4REA8zJsdDcbg==}
|
||||
'@serve.zone/catalog@2.11.2':
|
||||
resolution: {integrity: sha512-dxrIEAx79h+V0h7IEiHnc4g0Ks3yMcldCZFw8QXarFLNbqjiNzbR5aryumiCjEBHjdjgFJItO59LqWUU+3ML2g==}
|
||||
|
||||
'@serve.zone/interfaces@5.3.0':
|
||||
resolution: {integrity: sha512-venO7wtDR9ixzD9NhdERBGjNKbFA5LL0yHw4eqGh0UpmvtXVc3SFG0uuHDilOKMZqZ8bttV88qVsFy1aSTJrtA==}
|
||||
@@ -4355,7 +4358,7 @@ snapshots:
|
||||
'@api.global/typedrequest-interfaces': 3.0.19
|
||||
'@api.global/typedsocket': 4.1.2(@push.rocks/smartserve@2.0.3)
|
||||
'@cloudflare/workers-types': 4.20260317.1
|
||||
'@design.estate/dees-catalog': 3.55.6(@tiptap/pm@2.27.2)
|
||||
'@design.estate/dees-catalog': 3.61.1(@tiptap/pm@2.27.2)
|
||||
'@design.estate/dees-comms': 1.0.30
|
||||
'@push.rocks/lik': 6.4.0
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
@@ -4884,7 +4887,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@api.global/typedrequest-interfaces': 3.0.19
|
||||
|
||||
'@design.estate/dees-catalog@3.55.6(@tiptap/pm@2.27.2)':
|
||||
'@design.estate/dees-catalog@3.61.1(@tiptap/pm@2.27.2)':
|
||||
dependencies:
|
||||
'@design.estate/dees-domtools': 2.5.4
|
||||
'@design.estate/dees-element': 2.2.4
|
||||
@@ -5974,12 +5977,12 @@ snapshots:
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
|
||||
'@push.rocks/smartacme@9.4.0(socks@2.8.7)':
|
||||
'@push.rocks/smartacme@9.5.0(socks@2.8.7)':
|
||||
dependencies:
|
||||
'@apiclient.xyz/cloudflare': 7.1.0
|
||||
'@peculiar/x509': 2.0.0
|
||||
'@push.rocks/lik': 6.4.0
|
||||
'@push.rocks/smartdata': 7.1.3(socks@2.8.7)
|
||||
'@push.rocks/smartdata': 7.1.6(socks@2.8.7)
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartdns': 7.9.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
@@ -6114,7 +6117,7 @@ snapshots:
|
||||
'@types/node-forge': 1.3.14
|
||||
node-forge: 1.4.0
|
||||
|
||||
'@push.rocks/smartdata@7.1.3(socks@2.8.7)':
|
||||
'@push.rocks/smartdata@7.1.5(socks@2.8.7)':
|
||||
dependencies:
|
||||
'@push.rocks/lik': 6.4.0
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
@@ -6144,17 +6147,58 @@ snapshots:
|
||||
- supports-color
|
||||
- vue
|
||||
|
||||
'@push.rocks/smartdb@2.3.1(@tiptap/pm@2.27.2)':
|
||||
'@push.rocks/smartdata@7.1.6(socks@2.8.7)':
|
||||
dependencies:
|
||||
'@push.rocks/lik': 6.4.0
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartmongo': 5.1.1(socks@2.8.7)
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
'@push.rocks/smartstring': 4.1.0
|
||||
'@push.rocks/smarttime': 4.2.3
|
||||
'@push.rocks/smartunique': 3.0.9
|
||||
'@push.rocks/taskbuffer': 8.0.2
|
||||
'@tsclass/tsclass': 9.5.0
|
||||
mongodb: 7.1.1(socks@2.8.7)
|
||||
transitivePeerDependencies:
|
||||
- '@aws-sdk/credential-providers'
|
||||
- '@mongodb-js/zstd'
|
||||
- '@nuxt/kit'
|
||||
- bare-abort-controller
|
||||
- bare-buffer
|
||||
- gcp-metadata
|
||||
- kerberos
|
||||
- mongodb-client-encryption
|
||||
- react
|
||||
- react-native-b4a
|
||||
- snappy
|
||||
- socks
|
||||
- supports-color
|
||||
- vue
|
||||
|
||||
'@push.rocks/smartdb@2.5.9(@tiptap/pm@2.27.2)(socks@2.8.7)':
|
||||
dependencies:
|
||||
'@api.global/typedserver': 8.4.6(@tiptap/pm@2.27.2)
|
||||
'@design.estate/dees-element': 2.2.4
|
||||
'@push.rocks/smartdata': 7.1.5(socks@2.8.7)
|
||||
'@push.rocks/smartrust': 1.3.2
|
||||
bson: 7.2.0
|
||||
transitivePeerDependencies:
|
||||
- '@aws-sdk/credential-providers'
|
||||
- '@mongodb-js/zstd'
|
||||
- '@nuxt/kit'
|
||||
- '@tiptap/pm'
|
||||
- bare-abort-controller
|
||||
- bare-buffer
|
||||
- bufferutil
|
||||
- gcp-metadata
|
||||
- kerberos
|
||||
- mongodb-client-encryption
|
||||
- react
|
||||
- react-native-b4a
|
||||
- snappy
|
||||
- socks
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
- vue
|
||||
@@ -6387,7 +6431,7 @@ snapshots:
|
||||
'@push.rocks/smartmongo@5.1.1(socks@2.8.7)':
|
||||
dependencies:
|
||||
'@push.rocks/mongodump': 1.1.0(socks@2.8.7)
|
||||
'@push.rocks/smartdata': 7.1.3(socks@2.8.7)
|
||||
'@push.rocks/smartdata': 7.1.6(socks@2.8.7)
|
||||
'@push.rocks/smartfs': 1.5.0
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
@@ -6922,9 +6966,9 @@ snapshots:
|
||||
domhandler: 5.0.3
|
||||
selderee: 0.11.0
|
||||
|
||||
'@serve.zone/catalog@2.11.1(@tiptap/pm@2.27.2)':
|
||||
'@serve.zone/catalog@2.11.2(@tiptap/pm@2.27.2)':
|
||||
dependencies:
|
||||
'@design.estate/dees-catalog': 3.55.6(@tiptap/pm@2.27.2)
|
||||
'@design.estate/dees-catalog': 3.61.1(@tiptap/pm@2.27.2)
|
||||
'@design.estate/dees-domtools': 2.5.4
|
||||
'@design.estate/dees-element': 2.2.4
|
||||
'@design.estate/dees-wcctools': 3.8.0
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@serve.zone/dcrouter',
|
||||
version: '13.0.0',
|
||||
version: '13.0.6',
|
||||
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
||||
}
|
||||
|
||||
@@ -1076,7 +1076,10 @@ export class DcRouter {
|
||||
if (!expiryDate) {
|
||||
try {
|
||||
const cleanDomain = entry.domain.replace(/^\*\.?/, '');
|
||||
const certDoc = await AcmeCertDoc.findByDomain(cleanDomain);
|
||||
const domParts = cleanDomain.split('.');
|
||||
const baseDomain = domParts.length > 2 ? domParts.slice(-2).join('.') : cleanDomain;
|
||||
const certDoc = await AcmeCertDoc.findByDomain(baseDomain)
|
||||
|| (baseDomain !== cleanDomain ? await AcmeCertDoc.findByDomain(cleanDomain) : null);
|
||||
if (certDoc?.validUntil) {
|
||||
expiryDate = new Date(certDoc.validUntil).toISOString();
|
||||
}
|
||||
|
||||
@@ -191,7 +191,11 @@ export class CertificateHandler {
|
||||
// Check persisted cert data from smartdata document classes
|
||||
if (status === 'unknown') {
|
||||
const cleanDomain = domain.replace(/^\*\.?/, '');
|
||||
const acmeDoc = await AcmeCertDoc.findByDomain(cleanDomain);
|
||||
// SmartAcme stores certs under the base domain (e.g. example.com for api.example.com)
|
||||
const parts = cleanDomain.split('.');
|
||||
const baseDomain = parts.length > 2 ? parts.slice(-2).join('.') : cleanDomain;
|
||||
const acmeDoc = await AcmeCertDoc.findByDomain(baseDomain)
|
||||
|| (baseDomain !== cleanDomain ? await AcmeCertDoc.findByDomain(cleanDomain) : null);
|
||||
const proxyDoc = !acmeDoc ? await ProxyCertDoc.findByDomain(domain) : null;
|
||||
|
||||
if (acmeDoc?.validUntil) {
|
||||
@@ -331,31 +335,32 @@ export class CertificateHandler {
|
||||
await dcRouter.certProvisionScheduler.clearBackoff(domain);
|
||||
}
|
||||
|
||||
// Clear status map entry so it gets refreshed
|
||||
// Find routes matching this domain — needed to provision through SmartProxy
|
||||
const routeNames = dcRouter.findRouteNamesForDomain(domain);
|
||||
if (routeNames.length === 0) {
|
||||
return { success: false, message: `No routes found for domain '${domain}'` };
|
||||
}
|
||||
|
||||
// If forceRenew, invalidate SmartAcme's cache so the next provision gets a fresh cert
|
||||
if (forceRenew && dcRouter.smartAcme) {
|
||||
try {
|
||||
await dcRouter.smartAcme.getCertificateForDomain(domain, { forceRenew: true });
|
||||
} catch {
|
||||
// Cache invalidation failed — proceed with provisioning anyway
|
||||
}
|
||||
}
|
||||
|
||||
// Clear status map entry so it gets refreshed by the certificate-issued event
|
||||
dcRouter.certificateStatusMap.delete(domain);
|
||||
|
||||
// Try to provision via SmartAcme directly
|
||||
if (dcRouter.smartAcme) {
|
||||
try {
|
||||
await dcRouter.smartAcme.getCertificateForDomain(domain, { forceRenew: forceRenew ?? false });
|
||||
return { success: true, message: forceRenew ? `Certificate force-renewed for domain '${domain}'` : `Certificate reprovisioning triggered for domain '${domain}'` };
|
||||
} catch (err: unknown) {
|
||||
return { success: false, message: (err as Error).message || `Failed to reprovision certificate for ${domain}` };
|
||||
}
|
||||
// Provision through SmartProxy — this triggers the full pipeline:
|
||||
// certProvisionFunction → bridge.loadCertificate → certificate-issued event → status map updated
|
||||
try {
|
||||
await smartProxy.provisionCertificate(routeNames[0]);
|
||||
return { success: true, message: forceRenew ? `Certificate force-renewed for domain '${domain}'` : `Certificate reprovisioning triggered for domain '${domain}'` };
|
||||
} catch (err: unknown) {
|
||||
return { success: false, message: (err as Error).message || `Failed to reprovision certificate for ${domain}` };
|
||||
}
|
||||
|
||||
// Fallback: try provisioning via the first matching route
|
||||
const routeNames = dcRouter.findRouteNamesForDomain(domain);
|
||||
if (routeNames.length > 0) {
|
||||
try {
|
||||
await smartProxy.provisionCertificate(routeNames[0]);
|
||||
return { success: true, message: `Certificate reprovisioning triggered for domain '${domain}' via route '${routeNames[0]}'` };
|
||||
} catch (err: unknown) {
|
||||
return { success: false, message: (err as Error).message || `Failed to reprovision certificate for ${domain}` };
|
||||
}
|
||||
}
|
||||
|
||||
return { success: false, message: `No routes found for domain '${domain}'` };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,9 +369,12 @@ export class CertificateHandler {
|
||||
private async deleteCertificate(domain: string): Promise<{ success: boolean; message?: string }> {
|
||||
const dcRouter = this.opsServerRef.dcRouterRef;
|
||||
const cleanDomain = domain.replace(/^\*\.?/, '');
|
||||
const parts = cleanDomain.split('.');
|
||||
const baseDomain = parts.length > 2 ? parts.slice(-2).join('.') : cleanDomain;
|
||||
|
||||
// Delete from smartdata document classes
|
||||
const acmeDoc = await AcmeCertDoc.findByDomain(cleanDomain);
|
||||
// Delete from smartdata document classes (try base domain first, then exact)
|
||||
const acmeDoc = await AcmeCertDoc.findByDomain(baseDomain)
|
||||
|| (baseDomain !== cleanDomain ? await AcmeCertDoc.findByDomain(cleanDomain) : null);
|
||||
if (acmeDoc) {
|
||||
await acmeDoc.delete();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@serve.zone/dcrouter',
|
||||
version: '13.0.0',
|
||||
version: '13.0.6',
|
||||
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ export class OpsViewApiTokens extends DeesElement {
|
||||
const { apiTokens } = this.routeState;
|
||||
|
||||
return html`
|
||||
<ops-sectionheading>API Tokens</ops-sectionheading>
|
||||
<dees-heading level="2">API Tokens</dees-heading>
|
||||
|
||||
<div class="apiTokensContainer">
|
||||
<dees-table
|
||||
|
||||
@@ -159,7 +159,7 @@ export class OpsViewCertificates extends DeesElement {
|
||||
const { summary } = this.certState;
|
||||
|
||||
return html`
|
||||
<ops-sectionheading>Certificates</ops-sectionheading>
|
||||
<dees-heading level="2">Certificates</dees-heading>
|
||||
|
||||
<div class="certificatesContainer">
|
||||
${this.renderStatsTiles(summary)}
|
||||
|
||||
@@ -57,7 +57,7 @@ export class OpsViewConfig extends DeesElement {
|
||||
|
||||
public render() {
|
||||
return html`
|
||||
<ops-sectionheading>Configuration</ops-sectionheading>
|
||||
<dees-heading level="2">Configuration</dees-heading>
|
||||
|
||||
${this.configState.isLoading
|
||||
? html`
|
||||
|
||||
@@ -60,7 +60,7 @@ export class OpsViewEmails extends DeesElement {
|
||||
|
||||
public render() {
|
||||
return html`
|
||||
<ops-sectionheading>Email Operations</ops-sectionheading>
|
||||
<dees-heading level="2">Email Operations</dees-heading>
|
||||
<div class="viewContainer">
|
||||
${this.currentView === 'detail' && this.selectedEmail
|
||||
? html`
|
||||
|
||||
@@ -39,7 +39,7 @@ export class OpsViewLogs extends DeesElement {
|
||||
|
||||
public render() {
|
||||
return html`
|
||||
<ops-sectionheading>Logs</ops-sectionheading>
|
||||
<dees-heading level="2">Logs</dees-heading>
|
||||
|
||||
<dees-chart-log
|
||||
.label=${'Application Logs'}
|
||||
|
||||
@@ -285,7 +285,7 @@ export class OpsViewNetwork extends DeesElement {
|
||||
|
||||
public render() {
|
||||
return html`
|
||||
<ops-sectionheading>Network Activity</ops-sectionheading>
|
||||
<dees-heading level="2">Network Activity</dees-heading>
|
||||
|
||||
<div class="networkContainer">
|
||||
<!-- Stats Grid -->
|
||||
|
||||
@@ -64,7 +64,7 @@ export class OpsViewNetworkTargets extends DeesElement {
|
||||
];
|
||||
|
||||
return html`
|
||||
<ops-sectionheading>Network Targets</ops-sectionheading>
|
||||
<dees-heading level="2">Network Targets</dees-heading>
|
||||
<div class="targetsContainer">
|
||||
<dees-statsgrid .tiles=${statsTiles}></dees-statsgrid>
|
||||
<dees-table
|
||||
|
||||
@@ -94,7 +94,7 @@ export class OpsViewOverview extends DeesElement {
|
||||
|
||||
public render() {
|
||||
return html`
|
||||
<ops-sectionheading>Overview</ops-sectionheading>
|
||||
<dees-heading level="2">Overview</dees-heading>
|
||||
|
||||
${this.statsState.isLoading ? html`
|
||||
<div class="loadingMessage">
|
||||
|
||||
@@ -174,7 +174,7 @@ export class OpsViewRemoteIngress extends DeesElement {
|
||||
];
|
||||
|
||||
return html`
|
||||
<ops-sectionheading>Remote Ingress</ops-sectionheading>
|
||||
<dees-heading level="2">Remote Ingress</dees-heading>
|
||||
|
||||
${this.riState.newEdgeId ? html`
|
||||
<div class="secretDialog">
|
||||
|
||||
@@ -200,7 +200,7 @@ export class OpsViewRoutes extends DeesElement {
|
||||
});
|
||||
|
||||
return html`
|
||||
<ops-sectionheading>Route Management</ops-sectionheading>
|
||||
<dees-heading level="2">Route Management</dees-heading>
|
||||
|
||||
<div class="routesContainer">
|
||||
<dees-statsgrid
|
||||
|
||||
@@ -192,7 +192,7 @@ export class OpsViewSecurity extends DeesElement {
|
||||
|
||||
public render() {
|
||||
return html`
|
||||
<ops-sectionheading>Security</ops-sectionheading>
|
||||
<dees-heading level="2">Security</dees-heading>
|
||||
|
||||
<div class="tabs">
|
||||
<button
|
||||
|
||||
@@ -64,7 +64,7 @@ export class OpsViewSourceProfiles extends DeesElement {
|
||||
];
|
||||
|
||||
return html`
|
||||
<ops-sectionheading>Source Profiles</ops-sectionheading>
|
||||
<dees-heading level="2">Source Profiles</dees-heading>
|
||||
<div class="profilesContainer">
|
||||
<dees-statsgrid .tiles=${statsTiles}></dees-statsgrid>
|
||||
<dees-table
|
||||
|
||||
@@ -77,7 +77,7 @@ export class OpsViewTargetProfiles extends DeesElement {
|
||||
];
|
||||
|
||||
return html`
|
||||
<ops-sectionheading>Target Profiles</ops-sectionheading>
|
||||
<dees-heading level="2">Target Profiles</dees-heading>
|
||||
<div class="profilesContainer">
|
||||
<dees-statsgrid .tiles=${statsTiles}></dees-statsgrid>
|
||||
<dees-table
|
||||
@@ -148,17 +148,35 @@ export class OpsViewTargetProfiles extends DeesElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private getRouteCandidates() {
|
||||
const routeState = appstate.routeManagementStatePart.getState();
|
||||
const routes = routeState?.mergedRoutes || [];
|
||||
return routes
|
||||
.filter((mr) => mr.route.name)
|
||||
.map((mr) => ({ viewKey: mr.route.name! }));
|
||||
}
|
||||
|
||||
private async ensureRoutesLoaded() {
|
||||
const routeState = appstate.routeManagementStatePart.getState();
|
||||
if (!routeState?.mergedRoutes?.length) {
|
||||
await appstate.routeManagementStatePart.dispatchAction(appstate.fetchMergedRoutesAction, null);
|
||||
}
|
||||
}
|
||||
|
||||
private async showCreateProfileDialog() {
|
||||
const { DeesModal } = await import('@design.estate/dees-catalog');
|
||||
await this.ensureRoutesLoaded();
|
||||
const routeCandidates = this.getRouteCandidates();
|
||||
|
||||
DeesModal.createAndShow({
|
||||
heading: 'Create Target Profile',
|
||||
content: html`
|
||||
<dees-form>
|
||||
<dees-input-text .key=${'name'} .label=${'Name'} .required=${true}></dees-input-text>
|
||||
<dees-input-text .key=${'description'} .label=${'Description'}></dees-input-text>
|
||||
<dees-input-text .key=${'domains'} .label=${'Domains (comma-separated, e.g. *.example.com)'} ></dees-input-text>
|
||||
<dees-input-text .key=${'targets'} .label=${'Targets (comma-separated host:port, e.g. 10.0.0.1:443)'}></dees-input-text>
|
||||
<dees-input-text .key=${'routeRefs'} .label=${'Route Refs (comma-separated route names/IDs)'}></dees-input-text>
|
||||
<dees-input-list .key=${'domains'} .label=${'Domains'} .placeholder=${'e.g. *.example.com'} .allowFreeform=${true}></dees-input-list>
|
||||
<dees-input-list .key=${'targets'} .label=${'Targets (host:port)'} .placeholder=${'e.g. 10.0.0.1:443'} .allowFreeform=${true}></dees-input-list>
|
||||
<dees-input-list .key=${'routeRefs'} .label=${'Route Refs'} .placeholder=${'Type to search routes...'} .candidates=${routeCandidates} .allowFreeform=${true}></dees-input-list>
|
||||
</dees-form>
|
||||
`,
|
||||
menuOptions: [
|
||||
@@ -172,30 +190,26 @@ export class OpsViewTargetProfiles extends DeesElement {
|
||||
const data = await form.collectFormData();
|
||||
if (!data.name) return;
|
||||
|
||||
const domains = data.domains
|
||||
? String(data.domains).split(',').map((s: string) => s.trim()).filter(Boolean)
|
||||
: undefined;
|
||||
const targets = data.targets
|
||||
? String(data.targets).split(',').map((s: string) => {
|
||||
const trimmed = s.trim();
|
||||
const lastColon = trimmed.lastIndexOf(':');
|
||||
if (lastColon === -1) return null;
|
||||
return {
|
||||
host: trimmed.substring(0, lastColon),
|
||||
port: parseInt(trimmed.substring(lastColon + 1), 10),
|
||||
};
|
||||
}).filter((t): t is { host: string; port: number } => t !== null && !isNaN(t.port))
|
||||
: undefined;
|
||||
const routeRefs = data.routeRefs
|
||||
? String(data.routeRefs).split(',').map((s: string) => s.trim()).filter(Boolean)
|
||||
: undefined;
|
||||
const domains: string[] = Array.isArray(data.domains) ? data.domains : [];
|
||||
const targetStrings: string[] = Array.isArray(data.targets) ? data.targets : [];
|
||||
const targets = targetStrings
|
||||
.map((s: string) => {
|
||||
const lastColon = s.lastIndexOf(':');
|
||||
if (lastColon === -1) return null;
|
||||
return {
|
||||
host: s.substring(0, lastColon),
|
||||
port: parseInt(s.substring(lastColon + 1), 10),
|
||||
};
|
||||
})
|
||||
.filter((t): t is { host: string; port: number } => t !== null && !isNaN(t.port));
|
||||
const routeRefs: string[] = Array.isArray(data.routeRefs) ? data.routeRefs : [];
|
||||
|
||||
await appstate.targetProfilesStatePart.dispatchAction(appstate.createTargetProfileAction, {
|
||||
name: String(data.name),
|
||||
description: data.description ? String(data.description) : undefined,
|
||||
domains,
|
||||
targets,
|
||||
routeRefs,
|
||||
domains: domains.length > 0 ? domains : undefined,
|
||||
targets: targets.length > 0 ? targets : undefined,
|
||||
routeRefs: routeRefs.length > 0 ? routeRefs : undefined,
|
||||
});
|
||||
modalArg.destroy();
|
||||
},
|
||||
@@ -205,20 +219,23 @@ export class OpsViewTargetProfiles extends DeesElement {
|
||||
}
|
||||
|
||||
private async showEditProfileDialog(profile: interfaces.data.ITargetProfile) {
|
||||
const currentDomains = profile.domains?.join(', ') ?? '';
|
||||
const currentTargets = profile.targets?.map(t => `${t.host}:${t.port}`).join(', ') ?? '';
|
||||
const currentRouteRefs = profile.routeRefs?.join(', ') ?? '';
|
||||
const currentDomains = profile.domains || [];
|
||||
const currentTargets = profile.targets?.map(t => `${t.host}:${t.port}`) || [];
|
||||
const currentRouteRefs = profile.routeRefs || [];
|
||||
|
||||
const { DeesModal } = await import('@design.estate/dees-catalog');
|
||||
await this.ensureRoutesLoaded();
|
||||
const routeCandidates = this.getRouteCandidates();
|
||||
|
||||
DeesModal.createAndShow({
|
||||
heading: `Edit Profile: ${profile.name}`,
|
||||
content: html`
|
||||
<dees-form>
|
||||
<dees-input-text .key=${'name'} .label=${'Name'} .value=${profile.name}></dees-input-text>
|
||||
<dees-input-text .key=${'description'} .label=${'Description'} .value=${profile.description || ''}></dees-input-text>
|
||||
<dees-input-text .key=${'domains'} .label=${'Domains (comma-separated, e.g. *.example.com)'} .value=${currentDomains}></dees-input-text>
|
||||
<dees-input-text .key=${'targets'} .label=${'Targets (comma-separated host:port, e.g. 10.0.0.1:443)'} .value=${currentTargets}></dees-input-text>
|
||||
<dees-input-text .key=${'routeRefs'} .label=${'Route Refs (comma-separated route names/IDs)'} .value=${currentRouteRefs}></dees-input-text>
|
||||
<dees-input-list .key=${'domains'} .label=${'Domains'} .placeholder=${'e.g. *.example.com'} .allowFreeform=${true} .value=${currentDomains}></dees-input-list>
|
||||
<dees-input-list .key=${'targets'} .label=${'Targets (host:port)'} .placeholder=${'e.g. 10.0.0.1:443'} .allowFreeform=${true} .value=${currentTargets}></dees-input-list>
|
||||
<dees-input-list .key=${'routeRefs'} .label=${'Route Refs'} .placeholder=${'Type to search routes...'} .candidates=${routeCandidates} .allowFreeform=${true} .value=${currentRouteRefs}></dees-input-list>
|
||||
</dees-form>
|
||||
`,
|
||||
menuOptions: [
|
||||
@@ -231,24 +248,19 @@ export class OpsViewTargetProfiles extends DeesElement {
|
||||
if (!form) return;
|
||||
const data = await form.collectFormData();
|
||||
|
||||
const domains = data.domains
|
||||
? String(data.domains).split(',').map((s: string) => s.trim()).filter(Boolean)
|
||||
: [];
|
||||
const targets = data.targets
|
||||
? String(data.targets).split(',').map((s: string) => {
|
||||
const trimmed = s.trim();
|
||||
if (!trimmed) return null;
|
||||
const lastColon = trimmed.lastIndexOf(':');
|
||||
if (lastColon === -1) return null;
|
||||
return {
|
||||
host: trimmed.substring(0, lastColon),
|
||||
port: parseInt(trimmed.substring(lastColon + 1), 10),
|
||||
};
|
||||
}).filter((t): t is { host: string; port: number } => t !== null && !isNaN(t.port))
|
||||
: [];
|
||||
const routeRefs = data.routeRefs
|
||||
? String(data.routeRefs).split(',').map((s: string) => s.trim()).filter(Boolean)
|
||||
: [];
|
||||
const domains: string[] = Array.isArray(data.domains) ? data.domains : [];
|
||||
const targetStrings: string[] = Array.isArray(data.targets) ? data.targets : [];
|
||||
const targets = targetStrings
|
||||
.map((s: string) => {
|
||||
const lastColon = s.lastIndexOf(':');
|
||||
if (lastColon === -1) return null;
|
||||
return {
|
||||
host: s.substring(0, lastColon),
|
||||
port: parseInt(s.substring(lastColon + 1), 10),
|
||||
};
|
||||
})
|
||||
.filter((t): t is { host: string; port: number } => t !== null && !isNaN(t.port));
|
||||
const routeRefs: string[] = Array.isArray(data.routeRefs) ? data.routeRefs : [];
|
||||
|
||||
await appstate.targetProfilesStatePart.dispatchAction(appstate.updateTargetProfileAction, {
|
||||
id: profile.id,
|
||||
|
||||
@@ -221,7 +221,7 @@ export class OpsViewVpn extends DeesElement {
|
||||
];
|
||||
|
||||
return html`
|
||||
<ops-sectionheading>VPN</ops-sectionheading>
|
||||
<dees-heading level="2">VPN</dees-heading>
|
||||
<div class="vpnContainer">
|
||||
|
||||
${this.vpnState.newClientConfig ? html`
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
export * from './css.js';
|
||||
export * from './ops-sectionheading.js';
|
||||
export * from './css.js';
|
||||
@@ -1,38 +0,0 @@
|
||||
import {
|
||||
DeesElement,
|
||||
css,
|
||||
cssManager,
|
||||
customElement,
|
||||
html,
|
||||
type TemplateResult
|
||||
} from '@design.estate/dees-element';
|
||||
|
||||
@customElement('ops-sectionheading')
|
||||
export class OpsSectionHeading extends DeesElement {
|
||||
public static styles = [
|
||||
cssManager.defaultStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.heading {
|
||||
font-family: 'Cal Sans', 'Inter', sans-serif;
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
color: ${cssManager.bdTheme('#111', '#fff')};
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
public render(): TemplateResult {
|
||||
return html`
|
||||
<h1 class="heading">
|
||||
<slot></slot>
|
||||
</h1>
|
||||
`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user