feat(certificates): add force renew option for domain certificate reprovisioning
This commit is contained in:
@@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-04-03 - 12.8.0 - feat(certificates)
|
||||||
|
add force renew option for domain certificate reprovisioning
|
||||||
|
|
||||||
|
- pass an optional forceRenew flag through certificate reprovision requests from the UI to the ops handler
|
||||||
|
- use smartacme forceRenew support and return renewal-specific success messages
|
||||||
|
- update the SmartAcme dependency to version ^9.4.0
|
||||||
|
|
||||||
## 2026-04-03 - 12.7.0 - feat(opsserver)
|
## 2026-04-03 - 12.7.0 - feat(opsserver)
|
||||||
add RADIUS and VPN metrics to combined ops stats and overview dashboards, and stream live log buffer entries in follow mode
|
add RADIUS and VPN metrics to combined ops stats and overview dashboards, and stream live log buffer entries in follow mode
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
"@push.rocks/lik": "^6.4.0",
|
"@push.rocks/lik": "^6.4.0",
|
||||||
"@push.rocks/projectinfo": "^5.1.0",
|
"@push.rocks/projectinfo": "^5.1.0",
|
||||||
"@push.rocks/qenv": "^6.1.3",
|
"@push.rocks/qenv": "^6.1.3",
|
||||||
"@push.rocks/smartacme": "^9.3.1",
|
"@push.rocks/smartacme": "^9.4.0",
|
||||||
"@push.rocks/smartdata": "^7.1.3",
|
"@push.rocks/smartdata": "^7.1.3",
|
||||||
"@push.rocks/smartdb": "^2.1.1",
|
"@push.rocks/smartdb": "^2.1.1",
|
||||||
"@push.rocks/smartdns": "^7.9.0",
|
"@push.rocks/smartdns": "^7.9.0",
|
||||||
|
|||||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@@ -39,8 +39,8 @@ importers:
|
|||||||
specifier: ^6.1.3
|
specifier: ^6.1.3
|
||||||
version: 6.1.3
|
version: 6.1.3
|
||||||
'@push.rocks/smartacme':
|
'@push.rocks/smartacme':
|
||||||
specifier: ^9.3.1
|
specifier: ^9.4.0
|
||||||
version: 9.3.1(socks@2.8.7)
|
version: 9.4.0(socks@2.8.7)
|
||||||
'@push.rocks/smartdata':
|
'@push.rocks/smartdata':
|
||||||
specifier: ^7.1.3
|
specifier: ^7.1.3
|
||||||
version: 7.1.3(socks@2.8.7)
|
version: 7.1.3(socks@2.8.7)
|
||||||
@@ -1108,8 +1108,8 @@ packages:
|
|||||||
'@push.rocks/qenv@6.1.3':
|
'@push.rocks/qenv@6.1.3':
|
||||||
resolution: {integrity: sha512-+z2hsAU/7CIgpYLFqvda8cn9rUBMHqLdQLjsFfRn5jPoD7dJ5rFlpkbhfM4Ws8mHMniwWaxGKo+q/YBhtzRBLg==}
|
resolution: {integrity: sha512-+z2hsAU/7CIgpYLFqvda8cn9rUBMHqLdQLjsFfRn5jPoD7dJ5rFlpkbhfM4Ws8mHMniwWaxGKo+q/YBhtzRBLg==}
|
||||||
|
|
||||||
'@push.rocks/smartacme@9.3.1':
|
'@push.rocks/smartacme@9.4.0':
|
||||||
resolution: {integrity: sha512-Cl1DVQ+rfpaYkk6VVm/KYVeUYzWfXzSfTXybHfCZ5SuiACuTVHZ6jK8TouELaV1RgrdYnIp0MrbiY2Kqi8ayAw==}
|
resolution: {integrity: sha512-mSqsI859mHI9fCZxLfayzPf/WvukDFzVHOh02vXq3ujxbb5M+ArMnXe0MmC2egR9GeXmQTm3DTENaETX5ffMtw==}
|
||||||
|
|
||||||
'@push.rocks/smartarchive@4.2.4':
|
'@push.rocks/smartarchive@4.2.4':
|
||||||
resolution: {integrity: sha512-uiqVAXPxmr8G5rv3uZvZFMOCt8l7cZC3nzvsy4YQqKf/VkPhKIEX+b7LkAeNlxPSYUiBQUkNRoawg9+5BaMcHg==}
|
resolution: {integrity: sha512-uiqVAXPxmr8G5rv3uZvZFMOCt8l7cZC3nzvsy4YQqKf/VkPhKIEX+b7LkAeNlxPSYUiBQUkNRoawg9+5BaMcHg==}
|
||||||
@@ -5960,7 +5960,7 @@ snapshots:
|
|||||||
'@push.rocks/smartlog': 3.2.1
|
'@push.rocks/smartlog': 3.2.1
|
||||||
'@push.rocks/smartpath': 6.0.0
|
'@push.rocks/smartpath': 6.0.0
|
||||||
|
|
||||||
'@push.rocks/smartacme@9.3.1(socks@2.8.7)':
|
'@push.rocks/smartacme@9.4.0(socks@2.8.7)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@apiclient.xyz/cloudflare': 7.1.0
|
'@apiclient.xyz/cloudflare': 7.1.0
|
||||||
'@peculiar/x509': 2.0.0
|
'@peculiar/x509': 2.0.0
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/dcrouter',
|
name: '@serve.zone/dcrouter',
|
||||||
version: '12.7.0',
|
version: '12.8.0',
|
||||||
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export class CertificateHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ReprovisionCertificateDomain>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ReprovisionCertificateDomain>(
|
||||||
'reprovisionCertificateDomain',
|
'reprovisionCertificateDomain',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
return this.reprovisionCertificateDomain(dataArg.domain);
|
return this.reprovisionCertificateDomain(dataArg.domain, dataArg.forceRenew);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -318,7 +318,7 @@ export class CertificateHandler {
|
|||||||
/**
|
/**
|
||||||
* Domain-based reprovisioning — clears backoff first, then triggers provision
|
* Domain-based reprovisioning — clears backoff first, then triggers provision
|
||||||
*/
|
*/
|
||||||
private async reprovisionCertificateDomain(domain: string): Promise<{ success: boolean; message?: string }> {
|
private async reprovisionCertificateDomain(domain: string, forceRenew?: boolean): Promise<{ success: boolean; message?: string }> {
|
||||||
const dcRouter = this.opsServerRef.dcRouterRef;
|
const dcRouter = this.opsServerRef.dcRouterRef;
|
||||||
const smartProxy = dcRouter.smartProxy;
|
const smartProxy = dcRouter.smartProxy;
|
||||||
|
|
||||||
@@ -337,8 +337,8 @@ export class CertificateHandler {
|
|||||||
// Try to provision via SmartAcme directly
|
// Try to provision via SmartAcme directly
|
||||||
if (dcRouter.smartAcme) {
|
if (dcRouter.smartAcme) {
|
||||||
try {
|
try {
|
||||||
await dcRouter.smartAcme.getCertificateForDomain(domain);
|
await dcRouter.smartAcme.getCertificateForDomain(domain, { forceRenew: forceRenew ?? false });
|
||||||
return { success: true, message: `Certificate reprovisioning triggered for domain '${domain}'` };
|
return { success: true, message: forceRenew ? `Certificate force-renewed for domain '${domain}'` : `Certificate reprovisioning triggered for domain '${domain}'` };
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
return { success: false, message: (err as Error).message || `Failed to reprovision certificate for ${domain}` };
|
return { success: false, message: (err as Error).message || `Failed to reprovision certificate for ${domain}` };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ export interface IReq_ReprovisionCertificateDomain extends plugins.typedrequestI
|
|||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity: authInterfaces.IIdentity;
|
||||||
domain: string;
|
domain: string;
|
||||||
|
forceRenew?: boolean;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/dcrouter',
|
name: '@serve.zone/dcrouter',
|
||||||
version: '12.7.0',
|
version: '12.8.0',
|
||||||
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -605,8 +605,8 @@ export const fetchCertificateOverviewAction = certificateStatePart.createAction(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const reprovisionCertificateAction = certificateStatePart.createAction<string>(
|
export const reprovisionCertificateAction = certificateStatePart.createAction<{ domain: string; forceRenew?: boolean }>(
|
||||||
async (statePartArg, domain, actionContext): Promise<ICertificateState> => {
|
async (statePartArg, dataArg, actionContext): Promise<ICertificateState> => {
|
||||||
const context = getActionContext();
|
const context = getActionContext();
|
||||||
const currentState = statePartArg.getState()!;
|
const currentState = statePartArg.getState()!;
|
||||||
|
|
||||||
@@ -617,7 +617,8 @@ export const reprovisionCertificateAction = certificateStatePart.createAction<st
|
|||||||
|
|
||||||
await request.fire({
|
await request.fire({
|
||||||
identity: context.identity!,
|
identity: context.identity!,
|
||||||
domain,
|
domain: dataArg.domain,
|
||||||
|
forceRenew: dataArg.forceRenew,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Re-fetch overview after reprovisioning
|
// Re-fetch overview after reprovisioning
|
||||||
|
|||||||
@@ -312,14 +312,16 @@ export class OpsViewCertificates extends DeesElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const doReprovision = async () => {
|
const doReprovision = async (forceRenew = false) => {
|
||||||
await appstate.certificateStatePart.dispatchAction(
|
await appstate.certificateStatePart.dispatchAction(
|
||||||
appstate.reprovisionCertificateAction,
|
appstate.reprovisionCertificateAction,
|
||||||
cert.domain,
|
{ domain: cert.domain, forceRenew },
|
||||||
);
|
);
|
||||||
const { DeesToast } = await import('@design.estate/dees-catalog');
|
const { DeesToast } = await import('@design.estate/dees-catalog');
|
||||||
DeesToast.show({
|
DeesToast.show({
|
||||||
message: `Reprovisioning triggered for ${cert.domain}`,
|
message: forceRenew
|
||||||
|
? `Force renewal triggered for ${cert.domain}`
|
||||||
|
: `Reprovisioning triggered for ${cert.domain}`,
|
||||||
type: 'success',
|
type: 'success',
|
||||||
duration: 3000,
|
duration: 3000,
|
||||||
});
|
});
|
||||||
@@ -336,7 +338,7 @@ export class OpsViewCertificates extends DeesElement {
|
|||||||
name: 'Force Renew',
|
name: 'Force Renew',
|
||||||
action: async (modalArg: any) => {
|
action: async (modalArg: any) => {
|
||||||
await modalArg.destroy();
|
await modalArg.destroy();
|
||||||
await doReprovision();
|
await doReprovision(true);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user