@@ -1596,6 +1596,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p class="modal-desc">Expose a local port as an hs:// tunnel. The generated URL can be shared with others.</p>
|
<p class="modal-desc">Expose a local port as an hs:// tunnel. The generated URL can be shared with others.</p>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="startServerLabel">Label <span style="color:var(--text4);font-weight:400;">(optional)</span></label>
|
||||||
|
<input type="text" id="startServerLabel" class="input" placeholder="e.g. My Web App" autocomplete="off">
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="form-group" style="max-width:120px;">
|
<div class="form-group" style="max-width:120px;">
|
||||||
<label class="form-label" for="startServerPort">Port</label>
|
<label class="form-label" for="startServerPort">Port</label>
|
||||||
|
|||||||
+44
-10
@@ -1333,10 +1333,11 @@ function initOvLists() {
|
|||||||
searchId: 'ovServersSearch', subtitleId: 'ovServersSubtitle',
|
searchId: 'ovServersSearch', subtitleId: 'ovServersSubtitle',
|
||||||
cols: 3, emptyMsg: 'No server tunnels',
|
cols: 3, emptyMsg: 'No server tunnels',
|
||||||
rowFn: s => {
|
rowFn: s => {
|
||||||
const key = s.hsUrl || '';
|
const key = s.url || s.hsUrl || '';
|
||||||
const short = key.length > 22 ? key.slice(0, 9) + '…' + key.slice(-7) : key;
|
const short = key.length > 22 ? key.slice(0, 9) + '…' + key.slice(-7) : key;
|
||||||
|
const display = s.label ? escapeHtml(s.label) : `<span class="mono" style="font-size:11px;color:var(--text3);">${escapeHtml(String(s.port || '—'))}</span>`;
|
||||||
return `<tr>
|
return `<tr>
|
||||||
<td class="mono" style="font-size:12px;">${escapeHtml(String(s.port || '—'))}</td>
|
<td style="font-size:12px;">${display}</td>
|
||||||
<td>${stateTag(s.state)}</td>
|
<td>${stateTag(s.state)}</td>
|
||||||
<td class="mono" style="font-size:11px;color:var(--text3);"
|
<td class="mono" style="font-size:11px;color:var(--text3);"
|
||||||
title="${escapeHtml(key)}">${escapeHtml(short)}</td>
|
title="${escapeHtml(key)}">${escapeHtml(short)}</td>
|
||||||
@@ -1378,7 +1379,25 @@ function initOvLists() {
|
|||||||
|
|
||||||
// Quick action buttons → open existing modals directly
|
// Quick action buttons → open existing modals directly
|
||||||
$('qaAddVhost') ?.addEventListener('click', () => openModal('modal-addVhost'));
|
$('qaAddVhost') ?.addEventListener('click', () => openModal('modal-addVhost'));
|
||||||
$('qaAddServer') ?.addEventListener('click', () => openModal('modal-startServer'));
|
$('qaAddServer')?.addEventListener('click', () => {
|
||||||
|
const editIdEl = $('serverEditId');
|
||||||
|
if (editIdEl) editIdEl.value = '';
|
||||||
|
const labelEl = $('startServerLabel');
|
||||||
|
if (labelEl) labelEl.value = '';
|
||||||
|
const portEl = $('startServerPort');
|
||||||
|
if (portEl) portEl.value = '3000';
|
||||||
|
const hostEl = $('startServerHost');
|
||||||
|
if (hostEl) hostEl.value = '127.0.0.1';
|
||||||
|
const secureEl = $('startServerSecure');
|
||||||
|
if (secureEl) secureEl.checked = true;
|
||||||
|
const tcpEl = $('startServerProtocolTcp');
|
||||||
|
if (tcpEl) tcpEl.checked = true;
|
||||||
|
const titleEl = $('modal-startServer-title');
|
||||||
|
if (titleEl) titleEl.textContent = 'Start Server Tunnel';
|
||||||
|
const submitEl = $('startServerSubmit');
|
||||||
|
if (submitEl) submitEl.textContent = 'Start Server';
|
||||||
|
openModal('modal-startServer');
|
||||||
|
});
|
||||||
$('qaAddSvc') ?.addEventListener('click', () => openModal('modal-addServiceTunnel'));
|
$('qaAddSvc') ?.addEventListener('click', () => openModal('modal-addServiceTunnel'));
|
||||||
$('qaAddSsh') ?.addEventListener('click', () => openAddSshModal(null));
|
$('qaAddSsh') ?.addEventListener('click', () => openAddSshModal(null));
|
||||||
$('qaAddRdp') ?.addEventListener('click', () => openAddRdpModal(null));
|
$('qaAddRdp') ?.addEventListener('click', () => openAddRdpModal(null));
|
||||||
@@ -1589,9 +1608,13 @@ function updateSwarmsTable(state) {
|
|||||||
const id = s.id || s.serverId || '';
|
const id = s.id || s.serverId || '';
|
||||||
const url = s.url || s.hsUrl || '';
|
const url = s.url || s.hsUrl || '';
|
||||||
const safeId = id.replace(/"/g, '"');
|
const safeId = id.replace(/"/g, '"');
|
||||||
|
const label = s.label || '';
|
||||||
return `
|
return `
|
||||||
<tr>
|
<tr>
|
||||||
<td class="mono" style="font-size:11px;color:var(--text3);">${escapeHtml(truncate(id, 20))}</td>
|
<td>
|
||||||
|
${label ? `<div style="font-weight:600;color:var(--text);margin-bottom:2px;">${escapeHtml(label)}</div>` : ''}
|
||||||
|
<div class="mono" style="font-size:11px;color:var(--text3);">${escapeHtml(truncate(id, 20))}</div>
|
||||||
|
</td>
|
||||||
<td style="font-weight:600;color:var(--text);">${s.port ?? '—'}</td>
|
<td style="font-weight:600;color:var(--text);">${s.port ?? '—'}</td>
|
||||||
<td>
|
<td>
|
||||||
<div style="display:flex;align-items:center;gap:6px;">
|
<div style="display:flex;align-items:center;gap:6px;">
|
||||||
@@ -1630,9 +1653,11 @@ function updateSwarmsTable(state) {
|
|||||||
const server = (state.servers || []).find(s => (s.id || s.serverId) === serverId);
|
const server = (state.servers || []).find(s => (s.id || s.serverId) === serverId);
|
||||||
if (!server) return;
|
if (!server) return;
|
||||||
$('serverEditId').value = serverId;
|
$('serverEditId').value = serverId;
|
||||||
|
const labelEl = $('startServerLabel');
|
||||||
const portEl = $('startServerPort');
|
const portEl = $('startServerPort');
|
||||||
const hostEl = $('startServerHost');
|
const hostEl = $('startServerHost');
|
||||||
const secureEl = $('startServerSecure');
|
const secureEl = $('startServerSecure');
|
||||||
|
if (labelEl) labelEl.value = server.label || '';
|
||||||
if (portEl) portEl.value = server.port ?? 3000;
|
if (portEl) portEl.value = server.port ?? 3000;
|
||||||
if (hostEl) hostEl.value = server.host ?? '127.0.0.1';
|
if (hostEl) hostEl.value = server.host ?? '127.0.0.1';
|
||||||
if (secureEl) secureEl.checked = server.secure !== false;
|
if (secureEl) secureEl.checked = server.secure !== false;
|
||||||
@@ -2047,15 +2072,22 @@ function setupEvents() {
|
|||||||
|
|
||||||
// ── Start Server ────────────────────────────────────────────────────────
|
// ── Start Server ────────────────────────────────────────────────────────
|
||||||
$('startServerBtn')?.addEventListener('click', () => {
|
$('startServerBtn')?.addEventListener('click', () => {
|
||||||
// Reset to "new server" mode
|
|
||||||
const editIdEl = $('serverEditId');
|
const editIdEl = $('serverEditId');
|
||||||
if (editIdEl) editIdEl.value = '';
|
if (editIdEl) editIdEl.value = '';
|
||||||
|
const labelEl = $('startServerLabel');
|
||||||
|
if (labelEl) labelEl.value = '';
|
||||||
|
const portEl = $('startServerPort');
|
||||||
|
if (portEl) portEl.value = '3000';
|
||||||
|
const hostEl = $('startServerHost');
|
||||||
|
if (hostEl) hostEl.value = '127.0.0.1';
|
||||||
|
const secureEl = $('startServerSecure');
|
||||||
|
if (secureEl) secureEl.checked = true;
|
||||||
|
const tcpEl = $('startServerProtocolTcp');
|
||||||
|
if (tcpEl) tcpEl.checked = true;
|
||||||
const titleEl = $('modal-startServer-title');
|
const titleEl = $('modal-startServer-title');
|
||||||
if (titleEl) titleEl.textContent = 'Start Server Tunnel';
|
if (titleEl) titleEl.textContent = 'Start Server Tunnel';
|
||||||
const submitEl = $('startServerSubmit');
|
const submitEl = $('startServerSubmit');
|
||||||
if (submitEl) submitEl.textContent = 'Start Server';
|
if (submitEl) submitEl.textContent = 'Start Server';
|
||||||
const tcpEl = $('startServerProtocolTcp');
|
|
||||||
if (tcpEl) tcpEl.checked = true;
|
|
||||||
openModal('modal-startServer');
|
openModal('modal-startServer');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2063,18 +2095,21 @@ function setupEvents() {
|
|||||||
const portEl = $('startServerPort');
|
const portEl = $('startServerPort');
|
||||||
const hostEl = $('startServerHost');
|
const hostEl = $('startServerHost');
|
||||||
const secureEl = $('startServerSecure');
|
const secureEl = $('startServerSecure');
|
||||||
|
const labelEl = $('startServerLabel');
|
||||||
|
const editIdEl = $('serverEditId');
|
||||||
const port = parseInt(portEl?.value, 10) || 3000;
|
const port = parseInt(portEl?.value, 10) || 3000;
|
||||||
const host = (hostEl?.value || '127.0.0.1').trim();
|
const host = (hostEl?.value || '127.0.0.1').trim();
|
||||||
const secure = secureEl?.checked !== false;
|
const secure = secureEl?.checked !== false;
|
||||||
const udp = document.querySelector('input[name="startServerProtocol"]:checked')?.value === 'udp';
|
const udp = document.querySelector('input[name="startServerProtocol"]:checked')?.value === 'udp';
|
||||||
const editId = ($('serverEditId')?.value || '').trim();
|
const label = (labelEl?.value || '').trim();
|
||||||
|
const editId = (editIdEl?.value || '').trim();
|
||||||
if (!port || port < 1 || port > 65535) { showModalError('modal-startServer', 'startServerError', 'Port must be 1–65535'); return; }
|
if (!port || port < 1 || port > 65535) { showModalError('modal-startServer', 'startServerError', 'Port must be 1–65535'); return; }
|
||||||
const btn = $('startServerSubmit');
|
const btn = $('startServerSubmit');
|
||||||
if (btn) { btn.disabled = true; btn.textContent = editId ? 'Saving…' : 'Starting…'; }
|
if (btn) { btn.disabled = true; btn.textContent = editId ? 'Saving…' : 'Starting…'; }
|
||||||
|
|
||||||
const doStart = () => {
|
const doStart = () => {
|
||||||
chrome.runtime.sendMessage(
|
chrome.runtime.sendMessage(
|
||||||
{ target: 'holesail-native', action: 'send', payload: { type: 'startServer', payload: { port, host, secure, udp } } },
|
{ target: 'holesail-native', action: 'send', payload: { type: 'startServer', payload: { port, host, secure, udp, label } } },
|
||||||
(response) => {
|
(response) => {
|
||||||
if (btn) { btn.disabled = false; btn.textContent = editId ? 'Save Changes' : 'Start Server'; }
|
if (btn) { btn.disabled = false; btn.textContent = editId ? 'Save Changes' : 'Start Server'; }
|
||||||
if (response?.ok) {
|
if (response?.ok) {
|
||||||
@@ -2090,7 +2125,6 @@ function setupEvents() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (editId) {
|
if (editId) {
|
||||||
// Stop old server first, then start new one with updated settings
|
|
||||||
chrome.runtime.sendMessage(
|
chrome.runtime.sendMessage(
|
||||||
{ target: 'holesail-native', action: 'send', payload: { type: 'stopServer', payload: { serverId: editId } } },
|
{ target: 'holesail-native', action: 'send', payload: { type: 'stopServer', payload: { serverId: editId } } },
|
||||||
() => doStart()
|
() => doStart()
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ function saveStateSync() {
|
|||||||
const file = getStatePath();
|
const file = getStatePath();
|
||||||
const serversList = [];
|
const serversList = [];
|
||||||
for (const [id, s] of servers) {
|
for (const [id, s] of servers) {
|
||||||
serversList.push({ id, port: s.port, host: s.host, secure: s.secure, udp: s.udp || false });
|
serversList.push({ id, port: s.port, host: s.host, secure: s.secure, udp: s.udp || false, label: s.label || '' });
|
||||||
}
|
}
|
||||||
const virtualHostsList = [];
|
const virtualHostsList = [];
|
||||||
for (const [hostname, v] of virtualHosts) {
|
for (const [hostname, v] of virtualHosts) {
|
||||||
@@ -309,8 +309,9 @@ async function startServer(payload) {
|
|||||||
const host = payload.host || '127.0.0.1';
|
const host = payload.host || '127.0.0.1';
|
||||||
const secure = payload.secure !== false;
|
const secure = payload.secure !== false;
|
||||||
const udp = payload.udp === true;
|
const udp = payload.udp === true;
|
||||||
|
const label = (payload.label || '').trim();
|
||||||
const serverId = useServerId(payload.serverId);
|
const serverId = useServerId(payload.serverId);
|
||||||
debugLog('startServer: serverId=', serverId, 'port=', port, 'host=', host, 'secure=', secure, 'udp=', udp);
|
debugLog('startServer: serverId=', serverId, 'port=', port, 'host=', host, 'secure=', secure, 'udp=', udp, 'label=', label);
|
||||||
if (servers.has(serverId)) {
|
if (servers.has(serverId)) {
|
||||||
debugLog('startServer: serverId already in use');
|
debugLog('startServer: serverId already in use');
|
||||||
return { ok: false, error: 'Server id already in use' };
|
return { ok: false, error: 'Server id already in use' };
|
||||||
@@ -325,6 +326,7 @@ async function startServer(payload) {
|
|||||||
host: hs.info.host,
|
host: hs.info.host,
|
||||||
secure,
|
secure,
|
||||||
udp,
|
udp,
|
||||||
|
label,
|
||||||
url,
|
url,
|
||||||
createdAt: Date.now()
|
createdAt: Date.now()
|
||||||
});
|
});
|
||||||
@@ -337,7 +339,8 @@ async function startServer(payload) {
|
|||||||
port: hs.info.port,
|
port: hs.info.port,
|
||||||
host: hs.info.host,
|
host: hs.info.host,
|
||||||
secure,
|
secure,
|
||||||
udp
|
udp,
|
||||||
|
label
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugLog('startServer: error', e.message);
|
debugLog('startServer: error', e.message);
|
||||||
@@ -372,6 +375,7 @@ function getServers() {
|
|||||||
url: s.url,
|
url: s.url,
|
||||||
secure: s.secure,
|
secure: s.secure,
|
||||||
udp: s.udp || false,
|
udp: s.udp || false,
|
||||||
|
label: s.label || '',
|
||||||
createdAt: s.createdAt
|
createdAt: s.createdAt
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -107,7 +107,8 @@ async function restorePersistedTunnels(restored) {
|
|||||||
port: s.port,
|
port: s.port,
|
||||||
host: s.host || '127.0.0.1',
|
host: s.host || '127.0.0.1',
|
||||||
secure: s.secure !== false,
|
secure: s.secure !== false,
|
||||||
udp: s.udp === true
|
udp: s.udp === true,
|
||||||
|
label: s.label || ''
|
||||||
});
|
});
|
||||||
if (result.ok) serverOk++;
|
if (result.ok) serverOk++;
|
||||||
else serverFail++;
|
else serverFail++;
|
||||||
|
|||||||
Reference in New Issue
Block a user