|
@@ -976,9 +976,51 @@ function copyToClipboard(text, label = 'Text') {
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// ======================== PPP Profiles Shared Helper ========================
|
|
|
|
|
+async function fetchPPPProfilesList() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await fetch(`${API_BASE}/vpn/ppp/profiles`);
|
|
|
|
|
+ if (res.ok) {
|
|
|
|
|
+ state.pppProfiles = await res.json();
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ console.error('Failed to load PPP profiles:', err);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function populateProfileSelect(selectId, selectedVal, defaultVal = 'default') {
|
|
|
|
|
+ const sel = document.getElementById(selectId);
|
|
|
|
|
+ if (!sel) return;
|
|
|
|
|
+ const profiles = (state.pppProfiles && state.pppProfiles.length > 0) ? state.pppProfiles : [
|
|
|
|
|
+ { name: 'default', use_encryption: 'no' },
|
|
|
|
|
+ { name: 'default-encryption', use_encryption: 'required' }
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ sel.innerHTML = '';
|
|
|
|
|
+ profiles.forEach(p => {
|
|
|
|
|
+ const opt = document.createElement('option');
|
|
|
|
|
+ opt.value = p.name;
|
|
|
|
|
+ const encBadge = p.use_encryption === 'required' ? ' (MPPE-128)' : (p.use_encryption === 'yes' ? ' (MPPE)' : '');
|
|
|
|
|
+ opt.innerText = `${p.name}${encBadge}`;
|
|
|
|
|
+ sel.appendChild(opt);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (selectedVal) {
|
|
|
|
|
+ sel.value = selectedVal;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ sel.value = defaultVal;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!sel.value && sel.options.length > 0) {
|
|
|
|
|
+ sel.selectedIndex = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// ======================== PPPoE Multi-Instance ========================
|
|
// ======================== PPPoE Multi-Instance ========================
|
|
|
async function loadPPPoE(populateInputs = true) {
|
|
async function loadPPPoE(populateInputs = true) {
|
|
|
try {
|
|
try {
|
|
|
|
|
+ if (!state.pppProfiles || state.pppProfiles.length === 0) {
|
|
|
|
|
+ await fetchPPPProfilesList();
|
|
|
|
|
+ }
|
|
|
const res = await fetch(`${API_BASE}/vpn/pppoe/clients`);
|
|
const res = await fetch(`${API_BASE}/vpn/pppoe/clients`);
|
|
|
const list = await res.json();
|
|
const list = await res.json();
|
|
|
state.pppoeList = Array.isArray(list) ? list : [];
|
|
state.pppoeList = Array.isArray(list) ? list : [];
|
|
@@ -1000,11 +1042,12 @@ async function loadPPPoE(populateInputs = true) {
|
|
|
tr.className = 'hover:bg-slate-800/40 transition text-xs';
|
|
tr.className = 'hover:bg-slate-800/40 transition text-xs';
|
|
|
tr.innerHTML = `
|
|
tr.innerHTML = `
|
|
|
<td class="py-2.5 px-3 text-center">${renderMikroTikFlag(inst.running, inst.disabled)}</td>
|
|
<td class="py-2.5 px-3 text-center">${renderMikroTikFlag(inst.running, inst.disabled)}</td>
|
|
|
- <td class="py-2.5 px-3 font-bold text-white">${inst.name}</td>
|
|
|
|
|
- <td class="py-2.5 px-3 text-cyan-400 font-mono">${inst.interface}</td>
|
|
|
|
|
- <td class="py-2.5 px-3 text-slate-300 font-mono">${inst.username || '-'}</td>
|
|
|
|
|
- <td class="py-2.5 px-3 font-mono ${inst.assigned_ip ? 'text-emerald-400 font-bold' : 'text-slate-500'}">${inst.assigned_ip || '-'}</td>
|
|
|
|
|
- <td class="py-2.5 px-3 text-slate-400 font-mono">${inst.ppp_device || '-'}</td>
|
|
|
|
|
|
|
+ <td class="py-2.5 px-3 font-bold text-white">${escapeHtml(inst.name)}</td>
|
|
|
|
|
+ <td class="py-2.5 px-3 text-cyan-400 font-mono">${escapeHtml(inst.interface)}</td>
|
|
|
|
|
+ <td class="py-2.5 px-3 text-slate-300 font-mono">${escapeHtml(inst.username || '-')}</td>
|
|
|
|
|
+ <td class="py-2.5 px-3 font-mono text-cyan-300"><span class="px-1.5 py-0.5 rounded text-[10px] bg-slate-800 border border-slate-700">${escapeHtml(inst.profile || 'default')}</span></td>
|
|
|
|
|
+ <td class="py-2.5 px-3 font-mono ${inst.assigned_ip ? 'text-emerald-400 font-bold' : 'text-slate-500'}">${escapeHtml(inst.assigned_ip || '-')}</td>
|
|
|
|
|
+ <td class="py-2.5 px-3 text-slate-400 font-mono">${escapeHtml(inst.ppp_device || '-')}</td>
|
|
|
<td class="py-2.5 px-3 text-center">
|
|
<td class="py-2.5 px-3 text-center">
|
|
|
<span class="px-2 py-0.5 rounded text-[10px] font-sans ${inst.default_route ? 'bg-blue-500/20 text-blue-300 border border-blue-500/30' : 'bg-slate-800 text-slate-500'}">
|
|
<span class="px-2 py-0.5 rounded text-[10px] font-sans ${inst.default_route ? 'bg-blue-500/20 text-blue-300 border border-blue-500/30' : 'bg-slate-800 text-slate-500'}">
|
|
|
${inst.default_route ? 'Yes' : 'No'}
|
|
${inst.default_route ? 'Yes' : 'No'}
|
|
@@ -1044,6 +1087,9 @@ function openAddPPPoEModal() {
|
|
|
document.getElementById('pppoe-modal-reconnect').checked = true;
|
|
document.getElementById('pppoe-modal-reconnect').checked = true;
|
|
|
document.getElementById('pppoe-modal-disabled').checked = false;
|
|
document.getElementById('pppoe-modal-disabled').checked = false;
|
|
|
|
|
|
|
|
|
|
+ // Populate profiles
|
|
|
|
|
+ populateProfileSelect('pppoe-modal-profile', 'default', 'default');
|
|
|
|
|
+
|
|
|
// Populate interfaces
|
|
// Populate interfaces
|
|
|
const sel = document.getElementById('pppoe-modal-iface');
|
|
const sel = document.getElementById('pppoe-modal-iface');
|
|
|
sel.innerHTML = '';
|
|
sel.innerHTML = '';
|
|
@@ -1074,6 +1120,9 @@ function openEditPPPoEModal(name) {
|
|
|
document.getElementById('pppoe-modal-reconnect').checked = inst.auto_reconnect ?? true;
|
|
document.getElementById('pppoe-modal-reconnect').checked = inst.auto_reconnect ?? true;
|
|
|
document.getElementById('pppoe-modal-disabled').checked = !!inst.disabled;
|
|
document.getElementById('pppoe-modal-disabled').checked = !!inst.disabled;
|
|
|
|
|
|
|
|
|
|
+ // Populate profiles
|
|
|
|
|
+ populateProfileSelect('pppoe-modal-profile', inst.profile || 'default', 'default');
|
|
|
|
|
+
|
|
|
const sel = document.getElementById('pppoe-modal-iface');
|
|
const sel = document.getElementById('pppoe-modal-iface');
|
|
|
sel.innerHTML = '';
|
|
sel.innerHTML = '';
|
|
|
(state.interfaces || []).forEach(i => {
|
|
(state.interfaces || []).forEach(i => {
|
|
@@ -1098,6 +1147,7 @@ async function submitSavePPPoE(e) {
|
|
|
const iface = document.getElementById('pppoe-modal-iface').value;
|
|
const iface = document.getElementById('pppoe-modal-iface').value;
|
|
|
const user = document.getElementById('pppoe-modal-user').value.trim();
|
|
const user = document.getElementById('pppoe-modal-user').value.trim();
|
|
|
const pass = document.getElementById('pppoe-modal-pass').value;
|
|
const pass = document.getElementById('pppoe-modal-pass').value;
|
|
|
|
|
+ const profile = document.getElementById('pppoe-modal-profile')?.value || 'default';
|
|
|
const service = document.getElementById('pppoe-modal-service').value.trim();
|
|
const service = document.getElementById('pppoe-modal-service').value.trim();
|
|
|
const mtu = parseInt(document.getElementById('pppoe-modal-mtu').value, 10) || 1492;
|
|
const mtu = parseInt(document.getElementById('pppoe-modal-mtu').value, 10) || 1492;
|
|
|
const defRoute = document.getElementById('pppoe-modal-defroute').checked;
|
|
const defRoute = document.getElementById('pppoe-modal-defroute').checked;
|
|
@@ -1113,6 +1163,7 @@ async function submitSavePPPoE(e) {
|
|
|
interface: iface,
|
|
interface: iface,
|
|
|
username: user,
|
|
username: user,
|
|
|
password: pass,
|
|
password: pass,
|
|
|
|
|
+ profile: profile,
|
|
|
service_name: service,
|
|
service_name: service,
|
|
|
mtu,
|
|
mtu,
|
|
|
default_route: defRoute,
|
|
default_route: defRoute,
|
|
@@ -2219,6 +2270,9 @@ async function deleteIPSecConn(name) {
|
|
|
// ======================== L2TP Multi-Instance ========================
|
|
// ======================== L2TP Multi-Instance ========================
|
|
|
async function loadL2TP(populateInputs = true) {
|
|
async function loadL2TP(populateInputs = true) {
|
|
|
try {
|
|
try {
|
|
|
|
|
+ if (!state.pppProfiles || state.pppProfiles.length === 0) {
|
|
|
|
|
+ await fetchPPPProfilesList();
|
|
|
|
|
+ }
|
|
|
const res = await fetch(`${API_BASE}/vpn/l2tp/clients`);
|
|
const res = await fetch(`${API_BASE}/vpn/l2tp/clients`);
|
|
|
const list = await res.json();
|
|
const list = await res.json();
|
|
|
state.l2tpList = Array.isArray(list) ? list : [];
|
|
state.l2tpList = Array.isArray(list) ? list : [];
|
|
@@ -2240,11 +2294,12 @@ async function loadL2TP(populateInputs = true) {
|
|
|
tr.className = 'hover:bg-slate-800/40 transition text-xs';
|
|
tr.className = 'hover:bg-slate-800/40 transition text-xs';
|
|
|
tr.innerHTML = `
|
|
tr.innerHTML = `
|
|
|
<td class="py-2.5 px-3 text-center">${renderMikroTikFlag(inst.running, inst.disabled)}</td>
|
|
<td class="py-2.5 px-3 text-center">${renderMikroTikFlag(inst.running, inst.disabled)}</td>
|
|
|
- <td class="py-2.5 px-3 font-bold text-white">${inst.name}</td>
|
|
|
|
|
- <td class="py-2.5 px-3 text-cyan-400 font-mono">${inst.connect_to}</td>
|
|
|
|
|
- <td class="py-2.5 px-3 text-slate-300 font-mono">${inst.username || '-'}</td>
|
|
|
|
|
- <td class="py-2.5 px-3 font-mono ${inst.assigned_ip ? 'text-emerald-400 font-bold' : 'text-slate-500'}">${inst.assigned_ip || '-'}</td>
|
|
|
|
|
- <td class="py-2.5 px-3 text-slate-400 font-mono">${inst.ppp_device || '-'}</td>
|
|
|
|
|
|
|
+ <td class="py-2.5 px-3 font-bold text-white">${escapeHtml(inst.name)}</td>
|
|
|
|
|
+ <td class="py-2.5 px-3 text-cyan-400 font-mono">${escapeHtml(inst.connect_to)}</td>
|
|
|
|
|
+ <td class="py-2.5 px-3 text-slate-300 font-mono">${escapeHtml(inst.username || '-')}</td>
|
|
|
|
|
+ <td class="py-2.5 px-3 font-mono text-cyan-300"><span class="px-1.5 py-0.5 rounded text-[10px] bg-slate-800 border border-slate-700">${escapeHtml(inst.profile || 'default-encryption')}</span></td>
|
|
|
|
|
+ <td class="py-2.5 px-3 font-mono ${inst.assigned_ip ? 'text-emerald-400 font-bold' : 'text-slate-500'}">${escapeHtml(inst.assigned_ip || '-')}</td>
|
|
|
|
|
+ <td class="py-2.5 px-3 text-slate-400 font-mono">${escapeHtml(inst.ppp_device || '-')}</td>
|
|
|
<td class="py-2.5 px-3 text-center">
|
|
<td class="py-2.5 px-3 text-center">
|
|
|
<span class="px-2 py-0.5 rounded text-[10px] font-sans ${inst.default_route ? 'bg-cyan-500/20 text-cyan-300 border border-cyan-500/30' : 'bg-slate-800 text-slate-500'}">
|
|
<span class="px-2 py-0.5 rounded text-[10px] font-sans ${inst.default_route ? 'bg-cyan-500/20 text-cyan-300 border border-cyan-500/30' : 'bg-slate-800 text-slate-500'}">
|
|
|
${inst.default_route ? 'Yes' : 'No'}
|
|
${inst.default_route ? 'Yes' : 'No'}
|
|
@@ -2252,15 +2307,15 @@ async function loadL2TP(populateInputs = true) {
|
|
|
</td>
|
|
</td>
|
|
|
<td class="py-2.5 px-3 text-right space-x-2 font-sans">
|
|
<td class="py-2.5 px-3 text-right space-x-2 font-sans">
|
|
|
${inst.running ? `
|
|
${inst.running ? `
|
|
|
- <button onclick="disconnectL2TPInstance('${inst.name}')" class="text-xs text-rose-400 hover:text-rose-300">Disconnect</button>
|
|
|
|
|
|
|
+ <button onclick="disconnectL2TPInstance('${escapeHtml(inst.name)}')" class="text-xs text-rose-400 hover:text-rose-300">Disconnect</button>
|
|
|
` : `
|
|
` : `
|
|
|
- <button onclick="connectL2TPInstance('${inst.name}')" class="text-xs bg-cyan-600/30 hover:bg-cyan-600/50 text-cyan-300 px-2 py-0.5 rounded border border-cyan-500/30">Connect</button>
|
|
|
|
|
|
|
+ <button onclick="connectL2TPInstance('${escapeHtml(inst.name)}')" class="text-xs bg-cyan-600/30 hover:bg-cyan-600/50 text-cyan-300 px-2 py-0.5 rounded border border-cyan-500/30">Connect</button>
|
|
|
`}
|
|
`}
|
|
|
- <button onclick="toggleL2TPState('${inst.name}', ${inst.disabled ? 'true' : 'false'})" class="text-xs text-slate-400 hover:text-white">
|
|
|
|
|
|
|
+ <button onclick="toggleL2TPState('${escapeHtml(inst.name)}', ${inst.disabled ? 'true' : 'false'})" class="text-xs text-slate-400 hover:text-white">
|
|
|
${inst.disabled ? 'Enable' : 'Disable'}
|
|
${inst.disabled ? 'Enable' : 'Disable'}
|
|
|
</button>
|
|
</button>
|
|
|
- <button onclick="openEditL2TPModal('${inst.name}')" class="text-xs text-slate-400 hover:text-white">Edit</button>
|
|
|
|
|
- <button onclick="deleteL2TPInstance('${inst.name}')" class="text-xs text-slate-500 hover:text-rose-400">Delete</button>
|
|
|
|
|
|
|
+ <button onclick="openEditL2TPModal('${escapeHtml(inst.name)}')" class="text-xs text-slate-400 hover:text-white">Edit</button>
|
|
|
|
|
+ <button onclick="deleteL2TPInstance('${escapeHtml(inst.name)}')" class="text-xs text-slate-500 hover:text-rose-400">Delete</button>
|
|
|
</td>
|
|
</td>
|
|
|
`;
|
|
`;
|
|
|
tbody.appendChild(tr);
|
|
tbody.appendChild(tr);
|
|
@@ -2282,6 +2337,8 @@ function openAddL2TPModal() {
|
|
|
document.getElementById('l2tp-modal-defroute').checked = true;
|
|
document.getElementById('l2tp-modal-defroute').checked = true;
|
|
|
document.getElementById('l2tp-modal-disabled').checked = false;
|
|
document.getElementById('l2tp-modal-disabled').checked = false;
|
|
|
|
|
|
|
|
|
|
+ populateProfileSelect('l2tp-modal-profile', 'default-encryption', 'default-encryption');
|
|
|
|
|
+
|
|
|
document.getElementById('l2tp-modal').classList.remove('hidden');
|
|
document.getElementById('l2tp-modal').classList.remove('hidden');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2299,6 +2356,8 @@ function openEditL2TPModal(name) {
|
|
|
document.getElementById('l2tp-modal-defroute').checked = inst.default_route ?? true;
|
|
document.getElementById('l2tp-modal-defroute').checked = inst.default_route ?? true;
|
|
|
document.getElementById('l2tp-modal-disabled').checked = !!inst.disabled;
|
|
document.getElementById('l2tp-modal-disabled').checked = !!inst.disabled;
|
|
|
|
|
|
|
|
|
|
+ populateProfileSelect('l2tp-modal-profile', inst.profile || 'default-encryption', 'default-encryption');
|
|
|
|
|
+
|
|
|
document.getElementById('l2tp-modal').classList.remove('hidden');
|
|
document.getElementById('l2tp-modal').classList.remove('hidden');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2312,6 +2371,7 @@ async function submitSaveL2TP(e) {
|
|
|
const server = document.getElementById('l2tp-modal-server').value.trim();
|
|
const server = document.getElementById('l2tp-modal-server').value.trim();
|
|
|
const user = document.getElementById('l2tp-modal-user').value.trim();
|
|
const user = document.getElementById('l2tp-modal-user').value.trim();
|
|
|
const pass = document.getElementById('l2tp-modal-pass').value;
|
|
const pass = document.getElementById('l2tp-modal-pass').value;
|
|
|
|
|
+ const profile = document.getElementById('l2tp-modal-profile')?.value || 'default-encryption';
|
|
|
const ipsec = document.getElementById('l2tp-modal-ipsec').value;
|
|
const ipsec = document.getElementById('l2tp-modal-ipsec').value;
|
|
|
const defRoute = document.getElementById('l2tp-modal-defroute').checked;
|
|
const defRoute = document.getElementById('l2tp-modal-defroute').checked;
|
|
|
const disabled = document.getElementById('l2tp-modal-disabled').checked;
|
|
const disabled = document.getElementById('l2tp-modal-disabled').checked;
|
|
@@ -2325,6 +2385,7 @@ async function submitSaveL2TP(e) {
|
|
|
connect_to: server,
|
|
connect_to: server,
|
|
|
username: user,
|
|
username: user,
|
|
|
password: pass,
|
|
password: pass,
|
|
|
|
|
+ profile: profile,
|
|
|
ipsec_secret: ipsec,
|
|
ipsec_secret: ipsec,
|
|
|
default_route: defRoute,
|
|
default_route: defRoute,
|
|
|
disabled
|
|
disabled
|
|
@@ -2398,6 +2459,9 @@ async function deleteL2TPInstance(name) {
|
|
|
// ======================== PPTP Multi-Instance ========================
|
|
// ======================== PPTP Multi-Instance ========================
|
|
|
async function loadPPTP(populateInputs = true) {
|
|
async function loadPPTP(populateInputs = true) {
|
|
|
try {
|
|
try {
|
|
|
|
|
+ if (!state.pppProfiles || state.pppProfiles.length === 0) {
|
|
|
|
|
+ await fetchPPPProfilesList();
|
|
|
|
|
+ }
|
|
|
const res = await fetch(`${API_BASE}/vpn/pptp/clients`);
|
|
const res = await fetch(`${API_BASE}/vpn/pptp/clients`);
|
|
|
const list = await res.json();
|
|
const list = await res.json();
|
|
|
state.pptpList = Array.isArray(list) ? list : [];
|
|
state.pptpList = Array.isArray(list) ? list : [];
|
|
@@ -2419,11 +2483,12 @@ async function loadPPTP(populateInputs = true) {
|
|
|
tr.className = 'hover:bg-slate-800/40 transition text-xs';
|
|
tr.className = 'hover:bg-slate-800/40 transition text-xs';
|
|
|
tr.innerHTML = `
|
|
tr.innerHTML = `
|
|
|
<td class="py-2.5 px-3 text-center">${renderMikroTikFlag(inst.running, inst.disabled)}</td>
|
|
<td class="py-2.5 px-3 text-center">${renderMikroTikFlag(inst.running, inst.disabled)}</td>
|
|
|
- <td class="py-2.5 px-3 font-bold text-white">${inst.name}</td>
|
|
|
|
|
- <td class="py-2.5 px-3 text-purple-400 font-mono">${inst.connect_to}</td>
|
|
|
|
|
- <td class="py-2.5 px-3 text-slate-300 font-mono">${inst.username || '-'}</td>
|
|
|
|
|
- <td class="py-2.5 px-3 font-mono ${inst.assigned_ip ? 'text-emerald-400 font-bold' : 'text-slate-500'}">${inst.assigned_ip || '-'}</td>
|
|
|
|
|
- <td class="py-2.5 px-3 text-slate-400 font-mono">${inst.ppp_device || '-'}</td>
|
|
|
|
|
|
|
+ <td class="py-2.5 px-3 font-bold text-white">${escapeHtml(inst.name)}</td>
|
|
|
|
|
+ <td class="py-2.5 px-3 text-purple-400 font-mono">${escapeHtml(inst.connect_to)}</td>
|
|
|
|
|
+ <td class="py-2.5 px-3 text-slate-300 font-mono">${escapeHtml(inst.username || '-')}</td>
|
|
|
|
|
+ <td class="py-2.5 px-3 font-mono text-purple-300"><span class="px-1.5 py-0.5 rounded text-[10px] bg-slate-800 border border-slate-700">${escapeHtml(inst.profile || 'default-encryption')}</span></td>
|
|
|
|
|
+ <td class="py-2.5 px-3 font-mono ${inst.assigned_ip ? 'text-emerald-400 font-bold' : 'text-slate-500'}">${escapeHtml(inst.assigned_ip || '-')}</td>
|
|
|
|
|
+ <td class="py-2.5 px-3 text-slate-400 font-mono">${escapeHtml(inst.ppp_device || '-')}</td>
|
|
|
<td class="py-2.5 px-3 text-center">
|
|
<td class="py-2.5 px-3 text-center">
|
|
|
<span class="px-2 py-0.5 rounded text-[10px] font-sans ${inst.require_mppe ? 'bg-purple-500/20 text-purple-300 border border-purple-500/30' : 'bg-slate-800 text-slate-500'}">
|
|
<span class="px-2 py-0.5 rounded text-[10px] font-sans ${inst.require_mppe ? 'bg-purple-500/20 text-purple-300 border border-purple-500/30' : 'bg-slate-800 text-slate-500'}">
|
|
|
${inst.require_mppe ? 'MPPE-128' : 'None'}
|
|
${inst.require_mppe ? 'MPPE-128' : 'None'}
|
|
@@ -2431,15 +2496,15 @@ async function loadPPTP(populateInputs = true) {
|
|
|
</td>
|
|
</td>
|
|
|
<td class="py-2.5 px-3 text-right space-x-2 font-sans">
|
|
<td class="py-2.5 px-3 text-right space-x-2 font-sans">
|
|
|
${inst.running ? `
|
|
${inst.running ? `
|
|
|
- <button onclick="disconnectPPTPInstance('${inst.name}')" class="text-xs text-rose-400 hover:text-rose-300">Disconnect</button>
|
|
|
|
|
|
|
+ <button onclick="disconnectPPTPInstance('${escapeHtml(inst.name)}')" class="text-xs text-rose-400 hover:text-rose-300">Disconnect</button>
|
|
|
` : `
|
|
` : `
|
|
|
- <button onclick="connectPPTPInstance('${inst.name}')" class="text-xs bg-purple-600/30 hover:bg-purple-600/50 text-purple-300 px-2 py-0.5 rounded border border-purple-500/30">Connect</button>
|
|
|
|
|
|
|
+ <button onclick="connectPPTPInstance('${escapeHtml(inst.name)}')" class="text-xs bg-purple-600/30 hover:bg-purple-600/50 text-purple-300 px-2 py-0.5 rounded border border-purple-500/30">Connect</button>
|
|
|
`}
|
|
`}
|
|
|
- <button onclick="togglePPTPState('${inst.name}', ${inst.disabled ? 'true' : 'false'})" class="text-xs text-slate-400 hover:text-white">
|
|
|
|
|
|
|
+ <button onclick="togglePPTPState('${escapeHtml(inst.name)}', ${inst.disabled ? 'true' : 'false'})" class="text-xs text-slate-400 hover:text-white">
|
|
|
${inst.disabled ? 'Enable' : 'Disable'}
|
|
${inst.disabled ? 'Enable' : 'Disable'}
|
|
|
</button>
|
|
</button>
|
|
|
- <button onclick="openEditPPTPModal('${inst.name}')" class="text-xs text-slate-400 hover:text-white">Edit</button>
|
|
|
|
|
- <button onclick="deletePPTPInstance('${inst.name}')" class="text-xs text-slate-500 hover:text-rose-400">Delete</button>
|
|
|
|
|
|
|
+ <button onclick="openEditPPTPModal('${escapeHtml(inst.name)}')" class="text-xs text-slate-400 hover:text-white">Edit</button>
|
|
|
|
|
+ <button onclick="deletePPTPInstance('${escapeHtml(inst.name)}')" class="text-xs text-slate-500 hover:text-rose-400">Delete</button>
|
|
|
</td>
|
|
</td>
|
|
|
`;
|
|
`;
|
|
|
tbody.appendChild(tr);
|
|
tbody.appendChild(tr);
|
|
@@ -2461,6 +2526,8 @@ function openAddPPTPModal() {
|
|
|
document.getElementById('pptp-modal-defroute').checked = true;
|
|
document.getElementById('pptp-modal-defroute').checked = true;
|
|
|
document.getElementById('pptp-modal-disabled').checked = false;
|
|
document.getElementById('pptp-modal-disabled').checked = false;
|
|
|
|
|
|
|
|
|
|
+ populateProfileSelect('pptp-modal-profile', 'default-encryption', 'default-encryption');
|
|
|
|
|
+
|
|
|
document.getElementById('pptp-modal').classList.remove('hidden');
|
|
document.getElementById('pptp-modal').classList.remove('hidden');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2478,6 +2545,8 @@ function openEditPPTPModal(name) {
|
|
|
document.getElementById('pptp-modal-defroute').checked = inst.default_route ?? true;
|
|
document.getElementById('pptp-modal-defroute').checked = inst.default_route ?? true;
|
|
|
document.getElementById('pptp-modal-disabled').checked = !!inst.disabled;
|
|
document.getElementById('pptp-modal-disabled').checked = !!inst.disabled;
|
|
|
|
|
|
|
|
|
|
+ populateProfileSelect('pptp-modal-profile', inst.profile || 'default-encryption', 'default-encryption');
|
|
|
|
|
+
|
|
|
document.getElementById('pptp-modal').classList.remove('hidden');
|
|
document.getElementById('pptp-modal').classList.remove('hidden');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2491,6 +2560,7 @@ async function submitSavePPTP(e) {
|
|
|
const server = document.getElementById('pptp-modal-server').value.trim();
|
|
const server = document.getElementById('pptp-modal-server').value.trim();
|
|
|
const user = document.getElementById('pptp-modal-user').value.trim();
|
|
const user = document.getElementById('pptp-modal-user').value.trim();
|
|
|
const pass = document.getElementById('pptp-modal-pass').value;
|
|
const pass = document.getElementById('pptp-modal-pass').value;
|
|
|
|
|
+ const profile = document.getElementById('pptp-modal-profile')?.value || 'default-encryption';
|
|
|
const mppe = document.getElementById('pptp-modal-mppe').checked;
|
|
const mppe = document.getElementById('pptp-modal-mppe').checked;
|
|
|
const defRoute = document.getElementById('pptp-modal-defroute').checked;
|
|
const defRoute = document.getElementById('pptp-modal-defroute').checked;
|
|
|
const disabled = document.getElementById('pptp-modal-disabled').checked;
|
|
const disabled = document.getElementById('pptp-modal-disabled').checked;
|
|
@@ -2504,6 +2574,7 @@ async function submitSavePPTP(e) {
|
|
|
connect_to: server,
|
|
connect_to: server,
|
|
|
username: user,
|
|
username: user,
|
|
|
password: pass,
|
|
password: pass,
|
|
|
|
|
+ profile: profile,
|
|
|
require_mppe: mppe,
|
|
require_mppe: mppe,
|
|
|
default_route: defRoute,
|
|
default_route: defRoute,
|
|
|
auto_reconnect: true,
|
|
auto_reconnect: true,
|