@@ -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