@@ -1511,7 +1511,7 @@ async function renderDashboard() {
15111511 < div style ="padding:8px 12px 0;display:flex;gap:16px;justify-content:center;flex-wrap:wrap ">
15121512
15131513 </ div >
1514- < div style ="padding:4px 0 8px;text-align:center;font-size:0.7rem;color:var(--text-muted);letter-spacing:0.05em "> v1.1.0 </ div >
1514+ < div style ="padding:4px 0 8px;text-align:center;font-size:0.7rem;color:var(--text-muted);letter-spacing:0.05em "> v1.1.1 </ div >
15151515 </ div >
15161516 < div class ="sidebar-resizer " id ="sidebar-resizer "> </ div >
15171517 </ aside >
@@ -1672,6 +1672,13 @@ function initSidebarTooltip() {
16721672 } ) ;
16731673}
16741674
1675+ function renderNestLogoSmall ( logo ) {
1676+ if ( ! logo ) return html `< i data-lucide ="box " style ="width:16px;height:16px;color:var(--text-secondary) "> </ i > ` ;
1677+ if ( logo . startsWith ( 'si:' ) ) return html `< img src ="${ siUrl ( logo . slice ( 3 ) ) } " alt ="" style ="width:16px;height:16px " /> ` ;
1678+ if ( logo . startsWith ( 'lucide:' ) ) return html `< i data-lucide ="${ logo . slice ( 7 ) } " style ="width:16px;height:16px "> </ i > ` ;
1679+ return html `< img src ="${ logo } " alt ="" style ="width:16px;height:16px;border-radius:3px " /> ` ;
1680+ }
1681+
16751682function buildServerSubList ( ) {
16761683 if ( state . sidebarServersLoading ) return html `< div class ="nav-sub-empty "> < span class ="spinner "> </ span > Loading...</ div > ` ;
16771684 if ( state . servers . length === 0 ) return html `< div class ="nav-sub-empty "> No servers</ div > ` ;
@@ -1680,10 +1687,14 @@ function buildServerSubList() {
16801687 const isSuspended = s . status === 'suspended' ;
16811688 const dotClass = isSuspended ? 'dot-suspended' : ( isInstalling ? 'dot-installing' : 'dot-active' ) ;
16821689 const isActive = state . currentPage === 'server' && state . serverId === s . id ;
1690+ const eggName = s . eggDetails ?. name || '' ;
16831691 return html `
16841692 < a class ="nav-sub-item ${ isActive ? 'active' : '' } " data-server-nav ="${ s . id } " href ="/server/ ${ s . id } ">
1685- < span class ="nav-sub-dot ${ dotClass } "> </ span >
1686- ${ escapeHtml ( s . name ) }
1693+ < span class ="nav-sub-nest-icon "> ${ renderNestLogoSmall ( s . nestLogo ) } </ span >
1694+ < span class ="nav-sub-server-info ">
1695+ < span class ="nav-sub-server-name "> ${ escapeHtml ( s . name ) } < span class ="nav-sub-dot ${ dotClass } "> </ span > </ span >
1696+ ${ eggName ? html `< span class ="nav-sub-egg-name "> ${ escapeHtml ( eggName ) } </ span > ` : '' }
1697+ </ span >
16871698 </ a >
16881699 ` ;
16891700 } ) . join ( '' ) ;
@@ -2917,6 +2928,122 @@ function isValidServerName(name) {
29172928 return name && name . length >= 1 && name . length <= 255 && / ^ [ a - z A - Z 0 - 9 _ . - ] + $ / . test ( name ) ;
29182929}
29192930
2931+ function showVerificationPage ( name ) {
2932+ const overlay = document . createElement ( 'div' ) ;
2933+ overlay . id = 'verification-overlay' ;
2934+ overlay . className = 'verification-overlay' ;
2935+ overlay . innerHTML = html `
2936+ < div class ="verification-card ">
2937+ < div class ="verification-spinner "> </ div >
2938+ < h2 class ="verification-title "> Creating "${ escapeHtml ( name ) } "</ h2 >
2939+ < p class ="verification-subtitle "> Performing security checks...</ p >
2940+ < div class ="verification-steps " id ="verification-steps ">
2941+ < div class ="verification-step active " id ="vstep-vpn ">
2942+ < div class ="verification-step-icon "> < span class ="vstep-spinner "> </ span > </ div >
2943+ < span > Checking connection security</ span >
2944+ </ div >
2945+ < div class ="verification-step " id ="vstep-ua ">
2946+ < div class ="verification-step-icon "> < span class ="vstep-pending "> </ span > </ div >
2947+ < span > Verifying browser</ span >
2948+ </ div >
2949+ < div class ="verification-step " id ="vstep-email ">
2950+ < div class ="verification-step-icon "> < span class ="vstep-pending "> </ span > </ div >
2951+ < span > Checking email verification</ span >
2952+ </ div >
2953+ < div class ="verification-step " id ="vstep-country ">
2954+ < div class ="verification-step-icon "> < span class ="vstep-pending "> </ span > </ div >
2955+ < span > Verifying region</ span >
2956+ </ div >
2957+ < div class ="verification-step " id ="vstep-create ">
2958+ < div class ="verification-step-icon "> < span class ="vstep-pending "> </ span > </ div >
2959+ < span > Creating server</ span >
2960+ </ div >
2961+ </ div >
2962+ </ div >
2963+ ` ;
2964+ document . body . appendChild ( overlay ) ;
2965+ requestAnimationFrame ( ( ) => overlay . classList . add ( 'open' ) ) ;
2966+
2967+ let stepIndex = 0 ;
2968+ const steps = [ 'vstep-vpn' , 'vstep-ua' , 'vstep-email' , 'vstep-country' , 'vstep-create' ] ;
2969+ const interval = setInterval ( ( ) => {
2970+ if ( stepIndex < steps . length ) {
2971+ const el = document . getElementById ( steps [ stepIndex ] ) ;
2972+ if ( el ) {
2973+ el . classList . remove ( 'active' ) ;
2974+ el . classList . add ( 'done' ) ;
2975+ el . querySelector ( '.vstep-spinner, .vstep-pending' ) . outerHTML = '<svg class="vstep-check" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--accent-green)" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>' ;
2976+ }
2977+ stepIndex ++ ;
2978+ if ( stepIndex < steps . length ) {
2979+ const next = document . getElementById ( steps [ stepIndex ] ) ;
2980+ if ( next ) {
2981+ next . classList . add ( 'active' ) ;
2982+ next . querySelector ( '.vstep-pending' ) . outerHTML = '<span class="vstep-spinner"></span>' ;
2983+ }
2984+ }
2985+ } else {
2986+ clearInterval ( interval ) ;
2987+ }
2988+ } , 600 ) ;
2989+
2990+ overlay . _interval = interval ;
2991+ }
2992+
2993+ function showVerificationSuccess ( name ) {
2994+ const overlay = document . getElementById ( 'verification-overlay' ) ;
2995+ if ( ! overlay ) return ;
2996+ clearInterval ( overlay . _interval ) ;
2997+
2998+ const steps = overlay . querySelectorAll ( '.verification-step' ) ;
2999+ steps . forEach ( s => {
3000+ s . classList . remove ( 'active' ) ;
3001+ s . classList . add ( 'done' ) ;
3002+ const icon = s . querySelector ( '.vstep-spinner, .vstep-pending' ) ;
3003+ if ( icon ) icon . outerHTML = '<svg class="vstep-check" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--accent-green)" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>' ;
3004+ } ) ;
3005+
3006+ overlay . querySelector ( '.verification-card' ) . innerHTML = html `
3007+ < svg class ="verification-success-icon " width ="56 " height ="56 " viewBox ="0 0 24 24 " fill ="none " stroke ="var(--accent-green) " stroke-width ="2 " stroke-linecap ="round " stroke-linejoin ="round "> < path d ="M22 11.08V12a10 10 0 1 1-5.93-9.14 "/> < polyline points ="22 4 12 14.01 9 11.01 "/> </ svg >
3008+ < h2 class ="verification-title "> Server Created!</ h2 >
3009+ < p class ="verification-subtitle "> "${ escapeHtml ( name ) } " is now being set up.</ p >
3010+ ` ;
3011+ }
3012+
3013+ function showVerificationError ( message ) {
3014+ const overlay = document . getElementById ( 'verification-overlay' ) ;
3015+ if ( ! overlay ) return ;
3016+ clearInterval ( overlay . _interval ) ;
3017+
3018+ const steps = overlay . querySelectorAll ( '.verification-step.active' ) ;
3019+ steps . forEach ( s => {
3020+ s . classList . remove ( 'active' ) ;
3021+ s . classList . add ( 'failed' ) ;
3022+ const icon = s . querySelector ( '.vstep-spinner' ) ;
3023+ if ( icon ) icon . outerHTML = '<svg class="vstep-cross" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--accent-red)" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>' ;
3024+ } ) ;
3025+
3026+ const card = overlay . querySelector ( '.verification-card' ) ;
3027+ card . innerHTML = html `
3028+ < svg class ="verification-error-icon " width ="56 " height ="56 " viewBox ="0 0 24 24 " fill ="none " stroke ="var(--accent-red) " stroke-width ="2 " stroke-linecap ="round " stroke-linejoin ="round "> < circle cx ="12 " cy ="12 " r ="10 "/> < line x1 ="15 " y1 ="9 " x2 ="9 " y2 ="15 "/> < line x1 ="9 " y1 ="9 " x2 ="15 " y2 ="15 "/> </ svg >
3029+ < h2 class ="verification-title " style ="color:var(--accent-red) "> Verification Failed</ h2 >
3030+ < p class ="verification-subtitle "> ${ escapeHtml ( message ) } </ p >
3031+ < button class ="btn btn-primary " id ="verification-retry-btn " style ="margin-top:16px "> Go Back</ button >
3032+ ` ;
3033+
3034+ document . getElementById ( 'verification-retry-btn' ) . addEventListener ( 'click' , ( ) => {
3035+ overlay . remove ( ) ;
3036+ } ) ;
3037+ }
3038+
3039+ function removeVerificationOverlay ( ) {
3040+ const overlay = document . getElementById ( 'verification-overlay' ) ;
3041+ if ( overlay ) {
3042+ clearInterval ( overlay . _interval ) ;
3043+ overlay . remove ( ) ;
3044+ }
3045+ }
3046+
29203047async function handleWizardCreate ( ) {
29213048 const btn = $ ( '#wizard-create-btn' ) ;
29223049 btn . disabled = true ;
@@ -2935,16 +3062,21 @@ async function handleWizardCreate() {
29353062 const environment = { } ;
29363063 const dockerImage = createState . selectedDockerImage || '' ;
29373064
3065+ showVerificationPage ( name ) ;
3066+
29383067 try {
29393068 const capToken = document . querySelector ( '[name="cap-token"]' ) ?. value || '' ;
29403069 await api ( '/servers/create' , {
29413070 method : 'POST' ,
29423071 body : JSON . stringify ( { name, nestId : nest . pteroNestId , eggId : egg . eggId , environment, capToken, dockerImage } ) ,
29433072 } ) ;
2944- showToast ( `Server "${ name } " created successfully!` , 'success' ) ;
2945- navigateTo ( 'servers' ) ;
3073+ showVerificationSuccess ( name ) ;
3074+ setTimeout ( ( ) => {
3075+ removeVerificationOverlay ( ) ;
3076+ navigateTo ( 'servers' ) ;
3077+ } , 2000 ) ;
29463078 } catch ( err ) {
2947- showToast ( err . message , 'error' ) ;
3079+ showVerificationError ( err . message ) ;
29483080 btn . disabled = false ;
29493081 btn . innerHTML = '<i data-lucide="plus" style="width:16px;height:16px"></i> Create Server' ;
29503082 initIcons ( ) ;
0 commit comments