Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f685ce9928 | |||
| 699aa8a8e1 |
@@ -1,5 +1,11 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-03-30 - 11.18.0 - feat(vpn-ui)
|
||||||
|
add format selection for VPN client config exports
|
||||||
|
|
||||||
|
- Show an export modal that lets operators choose between WireGuard (.conf) and SmartVPN (.json) client configs.
|
||||||
|
- Update VPN client row actions to read the selected item from actionData for toggle, export, rotate keys, and delete handlers.
|
||||||
|
|
||||||
## 2026-03-30 - 11.17.0 - feat(vpn)
|
## 2026-03-30 - 11.17.0 - feat(vpn)
|
||||||
expand VPN operations view with client management and config export actions
|
expand VPN operations view with client management and config export actions
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@serve.zone/dcrouter",
|
"name": "@serve.zone/dcrouter",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "11.17.0",
|
"version": "11.18.0",
|
||||||
"description": "A multifaceted routing service handling mail and SMS delivery functions.",
|
"description": "A multifaceted routing service handling mail and SMS delivery functions.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/dcrouter',
|
name: '@serve.zone/dcrouter',
|
||||||
version: '11.17.0',
|
version: '11.18.0',
|
||||||
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/dcrouter',
|
name: '@serve.zone/dcrouter',
|
||||||
version: '11.17.0',
|
version: '11.18.0',
|
||||||
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -308,7 +308,8 @@ export class OpsViewVpn extends DeesElement {
|
|||||||
name: 'Toggle',
|
name: 'Toggle',
|
||||||
iconName: 'lucide:power',
|
iconName: 'lucide:power',
|
||||||
type: ['contextmenu', 'inRow'],
|
type: ['contextmenu', 'inRow'],
|
||||||
actionFunc: async (client: interfaces.data.IVpnClient) => {
|
actionFunc: async (actionData: any) => {
|
||||||
|
const client = actionData.item as interfaces.data.IVpnClient;
|
||||||
await appstate.vpnStatePart.dispatchAction(appstate.toggleVpnClientAction, {
|
await appstate.vpnStatePart.dispatchAction(appstate.toggleVpnClientAction, {
|
||||||
clientId: client.clientId,
|
clientId: client.clientId,
|
||||||
enabled: !client.enabled,
|
enabled: !client.enabled,
|
||||||
@@ -319,39 +320,73 @@ export class OpsViewVpn extends DeesElement {
|
|||||||
name: 'Export Config',
|
name: 'Export Config',
|
||||||
iconName: 'lucide:download',
|
iconName: 'lucide:download',
|
||||||
type: ['contextmenu', 'inRow'],
|
type: ['contextmenu', 'inRow'],
|
||||||
actionFunc: async (client: interfaces.data.IVpnClient) => {
|
actionFunc: async (actionData: any) => {
|
||||||
const { DeesToast } = await import('@design.estate/dees-catalog');
|
const client = actionData.item as interfaces.data.IVpnClient;
|
||||||
try {
|
const { DeesModal, DeesToast } = await import('@design.estate/dees-catalog');
|
||||||
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
||||||
interfaces.requests.IReq_ExportVpnClientConfig
|
const exportConfig = async (format: 'wireguard' | 'smartvpn') => {
|
||||||
>('/typedrequest', 'exportVpnClientConfig');
|
try {
|
||||||
const response = await request.fire({
|
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||||
identity: appstate.loginStatePart.getState()!.identity!,
|
interfaces.requests.IReq_ExportVpnClientConfig
|
||||||
clientId: client.clientId,
|
>('/typedrequest', 'exportVpnClientConfig');
|
||||||
format: 'wireguard',
|
const response = await request.fire({
|
||||||
});
|
identity: appstate.loginStatePart.getState()!.identity!,
|
||||||
if (response.success && response.config) {
|
clientId: client.clientId,
|
||||||
const blob = new Blob([response.config], { type: 'text/plain' });
|
format,
|
||||||
const url = URL.createObjectURL(blob);
|
});
|
||||||
const a = document.createElement('a');
|
if (response.success && response.config) {
|
||||||
a.href = url;
|
const ext = format === 'wireguard' ? 'conf' : 'json';
|
||||||
a.download = `${client.clientId}.conf`;
|
const blob = new Blob([response.config], { type: 'text/plain' });
|
||||||
a.click();
|
const url = URL.createObjectURL(blob);
|
||||||
URL.revokeObjectURL(url);
|
const a = document.createElement('a');
|
||||||
DeesToast.createAndShow({ message: 'Config downloaded', type: 'success', duration: 3000 });
|
a.href = url;
|
||||||
} else {
|
a.download = `${client.clientId}.${ext}`;
|
||||||
DeesToast.createAndShow({ message: response.message || 'Export failed', type: 'error', duration: 5000 });
|
a.click();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
DeesToast.createAndShow({ message: `${format} config downloaded`, type: 'success', duration: 3000 });
|
||||||
|
} else {
|
||||||
|
DeesToast.createAndShow({ message: response.message || 'Export failed', type: 'error', duration: 5000 });
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
DeesToast.createAndShow({ message: err.message || 'Export failed', type: 'error', duration: 5000 });
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
};
|
||||||
DeesToast.createAndShow({ message: err.message || 'Export failed', type: 'error', duration: 5000 });
|
|
||||||
}
|
DeesModal.createAndShow({
|
||||||
|
heading: `Export Config: ${client.clientId}`,
|
||||||
|
content: html`<p>Choose a config format to download.</p>`,
|
||||||
|
menuOptions: [
|
||||||
|
{
|
||||||
|
name: 'WireGuard (.conf)',
|
||||||
|
iconName: 'lucide:shield',
|
||||||
|
action: async (modalArg: any) => {
|
||||||
|
await modalArg.destroy();
|
||||||
|
await exportConfig('wireguard');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'SmartVPN (.json)',
|
||||||
|
iconName: 'lucide:braces',
|
||||||
|
action: async (modalArg: any) => {
|
||||||
|
await modalArg.destroy();
|
||||||
|
await exportConfig('smartvpn');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Cancel',
|
||||||
|
iconName: 'lucide:x',
|
||||||
|
action: async (modalArg: any) => await modalArg.destroy(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Rotate Keys',
|
name: 'Rotate Keys',
|
||||||
iconName: 'lucide:rotate-cw',
|
iconName: 'lucide:rotate-cw',
|
||||||
type: ['contextmenu'],
|
type: ['contextmenu'],
|
||||||
actionFunc: async (client: interfaces.data.IVpnClient) => {
|
actionFunc: async (actionData: any) => {
|
||||||
|
const client = actionData.item as interfaces.data.IVpnClient;
|
||||||
const { DeesModal, DeesToast } = await import('@design.estate/dees-catalog');
|
const { DeesModal, DeesToast } = await import('@design.estate/dees-catalog');
|
||||||
DeesModal.createAndShow({
|
DeesModal.createAndShow({
|
||||||
heading: 'Rotate Client Keys',
|
heading: 'Rotate Client Keys',
|
||||||
@@ -390,7 +425,8 @@ export class OpsViewVpn extends DeesElement {
|
|||||||
name: 'Delete',
|
name: 'Delete',
|
||||||
iconName: 'lucide:trash2',
|
iconName: 'lucide:trash2',
|
||||||
type: ['contextmenu'],
|
type: ['contextmenu'],
|
||||||
actionFunc: async (client: interfaces.data.IVpnClient) => {
|
actionFunc: async (actionData: any) => {
|
||||||
|
const client = actionData.item as interfaces.data.IVpnClient;
|
||||||
const { DeesModal } = await import('@design.estate/dees-catalog');
|
const { DeesModal } = await import('@design.estate/dees-catalog');
|
||||||
DeesModal.createAndShow({
|
DeesModal.createAndShow({
|
||||||
heading: 'Delete VPN Client',
|
heading: 'Delete VPN Client',
|
||||||
|
|||||||
Reference in New Issue
Block a user