Skip to content

Commit 18d4995

Browse files
committed
More useful buttons
1 parent cbad3ed commit 18d4995

1 file changed

Lines changed: 118 additions & 19 deletions

File tree

public/admin/index.html

Lines changed: 118 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,29 @@ <h3 class="fw-bold">DOM Cloud Panel</h3>
7272
<div class="vr mx-2 text-white opacity-25"></div>
7373

7474
<div class="navbar-text text-light small d-none d-md-block">
75-
<span v-if="sysInfo.version" class="me-3"><i class="bi bi-tag-fill me-1"></i>v{{ sysInfo.version }}</span>
76-
<span v-if="sysInfo.ip" class="me-3"><i class="bi bi-hdd-network-fill me-1"></i>{{ sysInfo.ip }}</span>
77-
<span v-if="sysInfo.status" class="badge" :class="sysInfo.status === 'OK' ? 'bg-success' : 'bg-danger'">{{ sysInfo.status }}</span>
75+
<span v-if="sysInfo.version" class="me-3" title="Server Version">
76+
<i class="bi bi-tag-fill me-1"></i> v{{ sysInfo.version }}
77+
</span>
78+
79+
<span v-if="sysInfo.ip" class="me-3" title="Client IP">
80+
<i class="bi bi-geo-alt-fill me-1"></i> {{ sysInfo.ip }}
81+
</span>
82+
83+
<span v-if="sysInfo.arch" class="me-3" title="System Architecture">
84+
<i class="bi bi-motherboard-fill me-1"></i> {{ sysInfo.arch }}
85+
</span>
86+
87+
<span v-if="sysInfo.cpu" class="me-3" title="CPU Cores">
88+
<i class="bi bi-cpu-fill me-1"></i> {{ sysInfo.cpu }} Core
89+
</span>
90+
91+
<span v-if="sysInfo.mem" class="me-3" title="Total Memory">
92+
<i class="bi bi-memory me-1"></i> {{ sysInfo.mem }} GB
93+
</span>
94+
95+
<span v-if="sysInfo.status" class="badge" :class="sysInfo.status === 'OK' ? 'bg-success' : 'bg-danger'">
96+
{{ sysInfo.status }}
97+
</span>
7898
</div>
7999

80100
<div class="ms-auto d-flex align-items-center">
@@ -156,20 +176,30 @@ <h4 class="mb-1 d-flex align-items-center">
156176
</div>
157177
</div>
158178
</div>
179+
<div class="d-flex gap-2 mb-3">
180+
<button @click="openVscode" class="btn btn-sm btn-outline-secondary" title="Copy Password & Open VSCode">
181+
<i class="bi bi-code-slash me-1"></i> VSCode
182+
</button>
183+
184+
<button @click="openWebSsh" class="btn btn-sm btn-outline-secondary" title="Open Web Terminal">
185+
<i class="bi bi-terminal me-1"></i> WebSSH
186+
</button>
187+
188+
<button @click="showConfig = !showConfig" class="btn btn-sm btn-outline-secondary ms-auto">
189+
<i class="bi" :class="showConfig ? 'bi-caret-up-fill' : 'bi-caret-down-fill'"></i> Show Config
190+
</button>
191+
</div>
159192

160-
<details>
161-
<summary class="btn btn-sm btn-outline-secondary mb-2">Show Full Configuration</summary>
162-
<div class="table-responsive border rounded bg-white" style="max-height: 300px; overflow:auto;">
163-
<table class="table table-sm table-striped mb-0 small">
164-
<tbody>
165-
<tr v-for="(val, key) in domainInfo" :key="key">
166-
<td class="fw-bold text-muted w-25">{{ key }}</td>
167-
<td class="font-monospace text-break">{{ val }}</td>
168-
</tr>
169-
</tbody>
170-
</table>
171-
</div>
172-
</details>
193+
<div v-if="showConfig" class="table-responsive border rounded bg-white" style="max-height: 300px; overflow:auto;">
194+
<table class="table table-sm table-striped mb-0 small">
195+
<tbody>
196+
<tr v-for="(val, key) in domainInfo" :key="key">
197+
<td class="fw-bold text-muted w-25">{{ key }}</td>
198+
<td class="font-monospace text-break">{{ val }}</td>
199+
</tr>
200+
</tbody>
201+
</table>
202+
</div>
173203
</div>
174204
</div>
175205
</div>
@@ -300,10 +330,18 @@ <h6 class="card-title text-truncate mb-1" :title="api.path">{{ api.shortPath }}<
300330
url: localStorage.getItem('dc_url') || window.location.origin
301331
});
302332

303-
const sysInfo = reactive({ ip: '', version: '', status: '' });
333+
const sysInfo = reactive({
334+
ip: '',
335+
version: '',
336+
status: '',
337+
arch: '',
338+
cpu: 0,
339+
mem: 0
340+
});
304341
const domains = ref([]);
305342
const domainFilter = ref('');
306343
const selectedDomain = ref('');
344+
const showConfig = ref(false);
307345

308346
// Domain Details State
309347
const domainInfo = ref(null);
@@ -344,6 +382,10 @@ <h6 class="card-title text-truncate mb-1" :title="api.path">{{ api.shortPath }}<
344382
return new Date(dateStr) < new Date(new Date().setMonth(new Date().getMonth() + 1));
345383
};
346384

385+
const getHostname = () => {
386+
try { return new URL(auth.url).hostname; } catch(e) { return window.location.hostname; }
387+
};
388+
347389
// AUTH & INIT
348390
const login = async () => {
349391
isLoading.value = true;
@@ -378,7 +420,15 @@ <h6 class="card-title text-truncate mb-1" :title="api.path">{{ api.shortPath }}<
378420
const fetchSystemInfo = () => {
379421
const h = { 'Authorization': `Bearer ${auth.token}` };
380422
const base = getBaseUrl();
381-
axios.get(`${base}/status/ip`, {headers:h}).then(r => sysInfo.ip = r.data.ip).catch(()=>{});
423+
axios.get(`${base}/status/ip`, {headers:h}).then(r => {
424+
sysInfo.ip = r.data.ip;
425+
426+
if(r.data.granted) {
427+
sysInfo.arch = r.data.arch;
428+
sysInfo.cpu = r.data.cpu;
429+
sysInfo.mem = r.data.mem ? parseFloat(r.data.mem).toFixed(1) : 0;
430+
}
431+
}).catch(()=>{});
382432
axios.get(`${base}/status/about`, {headers:h}).then(r => sysInfo.version = r.data.version).catch(()=>{});
383433
axios.get(`${base}/status/check`, {headers:h}).then(r => sysInfo.status = 'OK').catch(()=> sysInfo.status = 'ERR');
384434
};
@@ -479,6 +529,55 @@ <h6 class="card-title text-truncate mb-1" :title="api.path">{{ api.shortPath }}<
479529
}
480530
};
481531

532+
const openVscode = () => {
533+
if (!domainInfo.value) return;
534+
535+
const user = domainInfo.value.Username;
536+
const pass = domainInfo.value.Password;
537+
const host = getHostname();
538+
// Prefer explicit HTML directory, fallback to standard pattern
539+
const path = domainInfo.value['HTML directory'] || `/home/${user}/public_html`;
540+
541+
// Copy password to clipboard
542+
if (pass) {
543+
navigator.clipboard.writeText(pass).catch(err => console.error('Copy failed', err));
544+
}
545+
546+
// Open VSCode URI
547+
window.open(`vscode://vscode-remote/ssh-remote+${user}@${host}${path}`);
548+
};
549+
550+
const openWebSsh = () => {
551+
if (!domainInfo.value) return;
552+
553+
const hostname = getHostname();
554+
const targetUrl = `https://${hostname}/ssh/host/localhost`;
555+
const user = domainInfo.value.Username;
556+
const pass = domainInfo.value.Password;
557+
558+
// Create dynamic form
559+
const form = document.createElement('form');
560+
form.method = 'POST';
561+
form.action = targetUrl;
562+
form.target = '_blank'; // Open in new tab
563+
564+
const addField = (name, val) => {
565+
const input = document.createElement('input');
566+
input.type = 'hidden';
567+
input.name = name;
568+
input.value = val;
569+
form.appendChild(input);
570+
};
571+
572+
addField('username', user);
573+
addField('userpassword', pass);
574+
addField('fontFamily', "'NerdFontsSymbols Nerd Font', courier-new, courier, monospace");
575+
576+
document.body.appendChild(form);
577+
form.submit();
578+
document.body.removeChild(form);
579+
};
580+
482581
const formattedResponse = computed(() => {
483582
const r = apiResponse.value;
484583
if (!r) return '';
@@ -502,7 +601,7 @@ <h6 class="card-title text-truncate mb-1" :title="api.path">{{ api.shortPath }}<
502601
domainInfo, domainInfoLoading, activeUser, isSslExpiring,
503602
isAuthenticated, isLoading, loginError, login, logout, fetchDomains, selectDomain,
504603
groupedApis, currentEndpoint, openApi, formData, executeRequest, apiLoading, apiResponse,
505-
getMethodClass, formattedResponse,
604+
getMethodClass, formattedResponse, showConfig, openVscode, openWebSsh
506605
};
507606
}
508607
}).mount('#app');

0 commit comments

Comments
 (0)