Compare commits
	
		
			26 Commits
		
	
	
		
			1.1.7
			...
			c4a9272444
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| c4a9272444 | |||
| 9b4bc15e80 | |||
| cb07365020 | |||
| 46cf0f1f4e | |||
| 5a0d96b6c6 | |||
| c0c8a43c77 | |||
| a91c21ecfd | |||
| 49f466f298 | |||
| f0171a0232 | |||
| c95880d22f | |||
| 2f99a07ef7 | |||
| 97f62b8ee8 | |||
| 198a83b4c7 | |||
| 803e7185d8 | |||
| 30361a9aae | |||
| 115e8367da | |||
| 0d61dd1725 | |||
| d7147b6c81 | |||
| 63ec0ebaa8 | |||
| f925665d23 | |||
| a1aa1c0d14 | |||
| ff97952f67 | |||
| f0ce402830 | |||
| 80147b69eb | |||
| 11d8475c8a | |||
| 8f684513fd | 
							
								
								
									
										114
									
								
								common.js
									
									
									
									
									
								
							
							
						
						| @@ -110,29 +110,35 @@ function check_vpn() { | ||||
|   var url  = 'scan.php?op=check_vpn'; | ||||
|   jQuery.get(url, function(data) { | ||||
|         console.log('check_vpn: '+data); | ||||
|         if (data=="1") { // save_vpn has finished | ||||
|         if (data=="1") { // save_vpn has finished or VPN ON | ||||
| 	  const vpn_div = document.getElementById("vpn"); | ||||
| 	  console.log(vpn_div); | ||||
| 	  console.log('vpn_div: '+vpn_div); | ||||
| 	  if (vpn_div) { | ||||
| 	  	vpn_div.innerHTML = 'VPN start process has finished'; | ||||
| 		//document.getElementById('installAppsBtn').click(); | ||||
| 		setTimeout(function() { | ||||
| 			document.getElementById('installAppsBtn').click(); | ||||
| 		}, 2000); | ||||
| 	  } | ||||
| 	  else { | ||||
| 		$('#pro_off').hide(); | ||||
| 		$('#pro_on').show(); | ||||
| 		$('#vpn_off').hide(); | ||||
| 		$('#vpn_on').show(); | ||||
| 		//document.getElementById('vpnToggle').checked = true; | ||||
| 		//document.querySelector(".switch-label").textContent = "ON"; | ||||
| 	  } | ||||
| 	} | ||||
| 	else if (data=="2") { | ||||
| 		document.getElementById('vpnToggle').checked = true; | ||||
|   		document.querySelector(".switch-label").textContent = "ON"; | ||||
|           $('#vpn_off').hide(); | ||||
|           $('#vpn_on').show(); | ||||
|           $('#pro_off').hide(); | ||||
|           $('#pro_on').show(); | ||||
| 		$('#pro_off').hide(); | ||||
| 		$('#pro_on').show(); | ||||
| 		$('#vpn_on').hide(); | ||||
| 		$('#vpn_off').show(); | ||||
| 		//document.getElementById('vpnToggle').checked = false; | ||||
| 		//document.querySelector(".switch-label").textContent = "OFF"; | ||||
|         } | ||||
|         else { | ||||
| 		document.getElementById('vpnToggle').checked = false; | ||||
|   		document.querySelector(".switch-label").textContent = "OFF"; | ||||
|           $('#vpn_on').hide(); | ||||
|           $('#vpn_off').show(); | ||||
|           $('#pro_on').hide(); | ||||
|           $('#pro_off').show(); | ||||
|         else { // data == 0 | ||||
| 		$('#pro_on').hide(); | ||||
| 		$('#pro_off').show(); | ||||
|         } | ||||
|         setTimeout(check_vpn, 10000); | ||||
|   }); | ||||
| @@ -211,7 +217,8 @@ function load_template(additional, block) { | ||||
|   jQuery("div.deployment").each(function(index) { | ||||
|         $(this).html(''); | ||||
|   }); | ||||
|   jQuery("#"+block).html('Loading '+additional+' template...'); | ||||
|   //jQuery("#"+block).html('Loading '+additional+' template...'); | ||||
|   jQuery("#"+block).html('<div class="loading">Loading...</div>'); | ||||
|   var url  = 'scan.php?op=deployment&additional='+additional; | ||||
|   jQuery.get(url, function(data) { | ||||
| 	console.log('load_template: '+data); | ||||
| @@ -237,7 +244,8 @@ function reinstall(additional, block) { | ||||
|   jQuery("div.deployment").each(function(index) { | ||||
|         $(this).html(''); | ||||
|   }); | ||||
|   jQuery("#"+block).html('Loading '+additional+' template...'); | ||||
|   //jQuery("#"+block).html('Loading '+additional+' template...'); | ||||
|   jQuery("#"+block).html('<div class="loading">Loading...</div>'); | ||||
|   var url  = 'scan.php?op=reinstall&additional='+additional; | ||||
|   jQuery.get(url, function(data) { | ||||
| 	console.log('reinstall '+additional+': '+data); | ||||
| @@ -271,14 +279,14 @@ function uninstall(additional) { | ||||
|   jQuery("div.deployment").each(function(index) { | ||||
|         $(this).html(''); | ||||
|   }); | ||||
| 	data = '<fieldset><form action="#" method="post"><div class="row">YOU ARE GOING TO UNINSTALL '+additional.toUpperCase()+'.<br>ARE YOU SURE? IF YES, PLEASE CLICK ON THE BUTTON BELOW.<br><br></div><div class="row"><div class="mb-3"><button class="btn btn-lg btn-primary btn-block" type="button" onclick="confirm_uninstall(\''+additional+'\')">Uninstall</button></div></div></form></fieldset>'; | ||||
| 	data = '<div class="confirm"><form action="#" method="post"><div class="row">You are going to uninstall '+additional.toUpperCase()+'.<br>Are you sure? If yes, please click on the Uninstall button below.<br><br></div><div class="row buttons"><div class="mb-3"><button class="btn" type="button" onclick="confirm_uninstall(\''+additional+'\')">Uninstall</button></div><div class="mb-3" style="margin-left:200px;float:"><button class="btn" onclick="reinstall(\''+additional+'\',\''+additional+'\')">Cancel</button></div></div></form></div>'; | ||||
| 	jQuery("#"+additional).html(data); | ||||
| 	jQuery("#popupText").html(data); // manage2 | ||||
| } | ||||
|  | ||||
| function confirm_uninstall(additional) { | ||||
|   jQuery("#"+additional).html('Loading...'); | ||||
|   jQuery("#popupText").html('Loading'); // manage2 | ||||
|   jQuery("#"+additional).html('<div class="loading">Loading...</div>'); | ||||
|   jQuery("#popupText").html('<div class="loading">Loading...</div>'); // manage2 | ||||
|   var url  = 'scan.php?op=uninstall&additional='+additional; | ||||
|   jQuery.get(url, function(data) { | ||||
|         console.log('uninstall '+additional+': '+data); | ||||
| @@ -406,53 +414,52 @@ function get_services() { | ||||
|  | ||||
| function get_proxy_html() { | ||||
|         proxy_html = ` | ||||
| 	<fieldset> | ||||
| 	<legend>Enable proxy</legend> | ||||
|         <div class="input-group"> | ||||
| 	<form class="form-install" action="#" method="post" id="save_vpn"> | ||||
|         <div class="app-fields"> | ||||
|         	<div class="app-field"> | ||||
| 		<div class="row"> | ||||
| 		    <div class="mb-3"> | ||||
| 				<label for="vpn_domain">Please add domain url to download the VPN hash from (default: https://portal.safebox.network):</label> | ||||
| 				<input type="text" class="form-control" name="VPN_DOMAIN" id="vpn_domain" value="https://portal.safebox.network"> | ||||
| 				<div class="invalid-feedback"> | ||||
| 				Please enter a valid domain. | ||||
| 				</div> | ||||
| 			<label for="vpn_domain">Please add domain url to download the VPN hash from:</label> | ||||
| 		    <div class="input-container"> | ||||
| 				<input type="text" class="form-control" name="VPN_DOMAIN" id="vpn_domain" value="https://portal.safebox.network" size="40"> | ||||
| 		    </div> | ||||
| 		</div> | ||||
| 		</div> | ||||
|         	<div class="app-field"> | ||||
| 		<div class="row"> | ||||
| 		    <div class="mb-3"> | ||||
| 				<label for="vpn_pass">Please type in the generated VPN passkey (8 digits):</label> | ||||
| 			<label for="vpn_pass">Please type in the generated VPN passkey (8 digits):</label> | ||||
| 		    <div class="input-container"> | ||||
| 				<input type="text" class="form-control" name="VPN_PASS" id="vpn_pass" value="" maxlength="8" size="10"> | ||||
| 		    </div> | ||||
| 		</div> | ||||
| 		</div> | ||||
|         	<div class="app-field"> | ||||
| 		<div class="row"> | ||||
| 		    <div class="mb-3"> | ||||
| 				<label for="letsencrypt_mail">Please add the letsencrypt mail address:</label> | ||||
| 			<label for="letsencrypt_mail">Please add the letsencrypt mail address:</label> | ||||
| 		    <div class="input-container"> | ||||
| 				<input type="email" class="form-control" name="LETSENCRYPT_MAIL" id="letsencrypt_mail" value=""> | ||||
| 				<div class="invalid-feedback"> | ||||
| 				Please enter a valid email. | ||||
| 				</div> | ||||
| 		    </div> | ||||
| 		</div> | ||||
| 		</div> | ||||
|         	<div class="app-field"> | ||||
| 		<div class="row"> | ||||
| 		    <div class="mb-3"> | ||||
| 				<label for="letsencrypt_servername">Please add letsencrypt server name (default is letsencrypt but you can add zerossl too):</label> | ||||
| 			<label for="letsencrypt_servername">Please add letsencrypt server name (default is letsencrypt but you can add zerossl too):</label> | ||||
| 		    <div class="input-container"> | ||||
| 				<input type="text" class="form-control" name="LETSENCRYPT_SERVERNAME" id="letsencrypt_servername" value="letsencrypt"> | ||||
| 		    </div> | ||||
| 		</div> | ||||
| 		<div class="row buttons"> | ||||
| 		    <div class="mb-3"> | ||||
| 			<button class="btn btn-lg btn-primary btn-block" type="button" id="vpn_save_btn"> Save </button> | ||||
| 		    </div> | ||||
| 		</div> | ||||
| 	</form> | ||||
| 	</div> | ||||
| 	</fieldset> | ||||
| 	<div class="row buttons"> | ||||
| 	    <div class="mb-3"> | ||||
| 		<button class="btn btn-lg btn-primary btn-block" type="button" id="vpn_save_btn"> Save </button> | ||||
| 	    </div> | ||||
| 	</div> | ||||
| 	</form> | ||||
| 	<script> | ||||
| 	jQuery('#vpn_save_btn').click(function() { | ||||
| 		console.log('vpn save'); | ||||
| 		save_vpn(); | ||||
| 		jQuery('#vpn').html('VPN start process in progress. Please wait...'); | ||||
| 		jQuery('#vpn').html('<div class="loading">VPN start process in progress. Please wait...</div>'); | ||||
| 	}); | ||||
| 	</script> | ||||
|         `; | ||||
| @@ -480,8 +487,17 @@ function get_containers() { | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function get_version() { | ||||
|   var url  = 'scan.php?op=version'; | ||||
|   jQuery.get(url, function(data) { | ||||
| 	console.log('version: '+data); | ||||
| 	jQuery('#logo').attr('title',data); | ||||
|   }); | ||||
| } | ||||
|  | ||||
| jQuery(document).ready(function(){ | ||||
|  | ||||
| 	get_version(); | ||||
| 	get_repositories(); | ||||
| 	get_deployments(); | ||||
| 	get_system(); | ||||
| @@ -514,19 +530,19 @@ jQuery(document).ready(function(){ | ||||
| 	}); | ||||
|  | ||||
| 	jQuery('#update_btn').click(function() { | ||||
| 		jQuery('#updates').html('Looking for updates... Please wait...'); | ||||
| 		jQuery('#updates').html('<div class="loading">Looking for updates...</div>'); | ||||
| 		get_updates(); | ||||
| 	}); | ||||
|  | ||||
| 	jQuery('#add_repo').submit(function() { | ||||
| 		jQuery('#repositories').html('Loading...'); | ||||
| 		jQuery('#repositories').html('<div class="loading">Loading...</div>'); | ||||
| 		add_repository(); | ||||
| 		return false; | ||||
| 	}); | ||||
|  | ||||
| 	jQuery('#save_vpn').submit(function() { | ||||
| 		save_vpn(); | ||||
| 		jQuery('#vpn').html('Loading...'); | ||||
| 		jQuery('#vpn').html('<div class="loading">Loading...</div>'); | ||||
| 		return false; | ||||
| 	}); | ||||
|  | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								img/grey-box.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.1 KiB | 
							
								
								
									
										14
									
								
								img/logo.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 14 KiB | 
							
								
								
									
										
											BIN
										
									
								
								img/monitor.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 50 KiB | 
							
								
								
									
										
											BIN
										
									
								
								img/off.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 684 B After Width: | Height: | Size: 914 B | 
							
								
								
									
										
											BIN
										
									
								
								img/on.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 701 B After Width: | Height: | Size: 992 B | 
							
								
								
									
										
											BIN
										
									
								
								img/yellow-box.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.5 KiB | 
							
								
								
									
										13
									
								
								index.html
									
									
									
									
									
								
							
							
						
						| @@ -3,21 +3,26 @@ | ||||
| <head> | ||||
| <meta charset="UTF-8"> | ||||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
| <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> | ||||
| <meta http-equiv="Pragma" content="no-cache" /> | ||||
| <meta http-equiv="Expires" content="0" /> | ||||
| <title>Safebox installer</title> | ||||
| <style> | ||||
| body, html { | ||||
| html, body { | ||||
|     margin: 0; | ||||
|     padding: 0; | ||||
|     height: 100%; | ||||
|     overflow: hidden; | ||||
| } | ||||
| iframe { | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     display: block; | ||||
|     width: 100vw; | ||||
|     height: 100vh; | ||||
|     border: none; | ||||
| } | ||||
| </style> | ||||
| </head> | ||||
| <body> | ||||
|     <iframe src="scan.html"></iframe> | ||||
|     <iframe src="scan.html?t=1"></iframe> | ||||
| </body> | ||||
| </html> | ||||
|   | ||||
							
								
								
									
										257
									
								
								install.html
									
									
									
									
									
								
							
							
						
						| @@ -1,175 +1,172 @@ | ||||
| <!doctype html> | ||||
| <html lang="en"> | ||||
| <!DOCTYPE html> | ||||
| <html lang="hu"> | ||||
| <head> | ||||
| <!-- Required meta tags --> | ||||
| <meta charset="utf-8"> | ||||
| <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | ||||
| <title>INSTALLER TOOL</title> | ||||
| <!-- Bootstrap CSS --> | ||||
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.2.1/dist/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous"> | ||||
| <!-- Custom styles for this template --> | ||||
| <link href="installer.css?t=3" rel="stylesheet"> | ||||
| <meta charset="UTF-8" /> | ||||
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||||
| <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> | ||||
| <meta http-equiv="Pragma" content="no-cache" /> | ||||
| <meta http-equiv="Expires" content="0" /> | ||||
| <title>Safebox - INSTALLER TOOL</title> | ||||
| <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Switzer:ital,wght@0,300;0,400;0,500;0,600;1,400&display=swap"/> | ||||
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"/> | ||||
| <link rel="stylesheet" href="style.css?t=1" /> | ||||
| </head> | ||||
| <body id="installer" class="text-center"> | ||||
| <div class="container-fluid"> | ||||
| <div class="col-md-12"> | ||||
| <body id="installer"> | ||||
|   <div class="main" style="max-width:1000px;margin:0px auto;"> | ||||
|     <div id="myAppsContainer"> | ||||
| 	      <div class="logo"> | ||||
| 		<img src="/img/logo.svg" alt="Safebox"/> | ||||
| 		<span>Safebox</span> | ||||
| 	      </div> | ||||
| 		<h3 style="text-align:center">No any previous deployed environment has found</h3> | ||||
| 		<div class="main-header"> | ||||
| 			<h1>Base settings</h1> | ||||
| 		</div> | ||||
|  | ||||
| <form class="form-install" action="install.php" method="post"> | ||||
|  | ||||
| <h1>No any previous deployed environment found</h1> | ||||
|  | ||||
| <fieldset> | ||||
| <legend>Base settings</legend> | ||||
| <div class="row"> | ||||
|     <div class="mb-3"> | ||||
| 		<label for="user_auth">User auth (homeguard) enable?</label> | ||||
| <!-- base settings --> | ||||
| <div class="input-row"> | ||||
| 	<label for="user_auth">User auth (homeguard) enable?</label> | ||||
| 	<div class="input-container"> | ||||
| 		<select class="custom-select d-block w-100" name="USER_AUTH" id="user_auth"> | ||||
| 		<option value="yes">Yes</option> | ||||
| 		<option value="no" selected>No</option> | ||||
| 		</select> | ||||
|     </div> | ||||
| 	</div> | ||||
| </div> | ||||
| <div id="div_user_auth" class="hidden"> | ||||
| 	<div class="row"> | ||||
| 	    <div class="mb-3"> | ||||
| 	<div class="input-row"> | ||||
| 		<label for="auth_username">Username:</label> | ||||
| 		<div class="input-container"> | ||||
| 		<input type="text" class="form-control" name="AUTH_USERNAME" id="auth_username" value="" maxlength="20" size="20"> | ||||
| 	    </div> | ||||
| 	    	</div> | ||||
| 	</div> | ||||
| 	<div class="row"> | ||||
| 	    <div class="mb-3"> | ||||
| 	<div class="input-row"> | ||||
| 		<label for="auth_password">Password:</label> | ||||
| 		<div class="input-container"> | ||||
| 		<input type="text" class="form-control" name="AUTH_PASSWORD" id="auth_password" value="" maxlength="20" size="20"> | ||||
| 	    </div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	<div class="row"> | ||||
| 	    <div class="mb-3"> | ||||
| 	<div class="input-row"> | ||||
| 		<label for="auth_role">Role:</label> | ||||
| 		<div class="input-container"> | ||||
| 		<select class="custom-select d-block w-100" name="AUTH_ROLE" id="auth_role"> | ||||
| 		<option value="admin">Admin</option> | ||||
| 		</select> | ||||
| 	    </div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </div> | ||||
| <div class="row"> | ||||
|     <div class="mb-3"> | ||||
| 		<label for="vpn">Proxy enable?</label> | ||||
| <div class="input-row"> | ||||
| 	<label for="vpn">Proxy enable?</label> | ||||
| 	<div class="input-container"> | ||||
| 		<select class="custom-select d-block w-100" name="VPN_PROXY" id="vpn"> | ||||
| 		<option value="yes">Yes</option> | ||||
| 		<option value="no" selected>No</option> | ||||
| 		</select> | ||||
|     </div> | ||||
| </div> | ||||
| <div id="div_vpn" class="hidden"> | ||||
| 	<div class="row"> | ||||
| 	    <div class="mb-3"> | ||||
| 			<label for="vpn_domain">Please add domain url to download the VPN hash from (default: https://portal.safebox.network):</label> | ||||
| 			<input type="text" class="form-control" name="VPN_DOMAIN" id="vpn_domain" value="https://portal.safebox.network"> | ||||
| 			<div class="invalid-feedback"> | ||||
| 			Please enter a valid domain. | ||||
| 			</div> | ||||
| 	    </div> | ||||
| 	</div> | ||||
| 	<div class="row"> | ||||
| 	    <div class="mb-3"> | ||||
| 			<label for="vpn_pass">Please type in the generated VPN passkey (8 digits):</label> | ||||
| </div> | ||||
| <div id="div_vpn1" class="hidden"> | ||||
| 	<div class="input-row"> | ||||
| 		<label for="vpn_pass">Please type in the generated VPN passkey (8 digits):</label> | ||||
| 		<div class="input-container"> | ||||
| 			<input type="text" class="form-control" name="VPN_PASS" id="vpn_pass" value="" maxlength="8" size="10"> | ||||
| 	    </div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	<div class="row"> | ||||
| 	    <div class="mb-3"> | ||||
| 			<label for="letsencrypt_mail">Please add the letsencrypt mail address:</label> | ||||
| 	<div class="input-row"> | ||||
| 		<label for="letsencrypt_mail">Please add the letsencrypt mail address:</label> | ||||
| 		<div class="input-container"> | ||||
| 			<input type="email" class="form-control" name="LETSENCRYPT_MAIL" id="letsencrypt_mail" value=""> | ||||
| 			<div class="invalid-feedback"> | ||||
| 			Please enter a valid email. | ||||
| 			</div> | ||||
| 	    </div> | ||||
| 	</div> | ||||
| 	<div class="row"> | ||||
| 	    <div class="mb-3"> | ||||
| 			<label for="letsencrypt_servername">Please add letsencrypt server name (default is letsencrypt but you can add zerossl too):</label> | ||||
| 			<input type="text" class="form-control" name="LETSENCRYPT_SERVERNAME" id="letsencrypt_servername" value="letsencrypt"> | ||||
| 	    </div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </div> | ||||
| <div class="row"> | ||||
|     <div class="mb-3"> | ||||
| 		<label for="diagnostic">Diagnostic mode enable?</label> | ||||
| <div class="input-row"> | ||||
| 	<label for="diagnostic">Diagnostic mode enable?</label> | ||||
| 	<div class="input-container"> | ||||
| 		<select class="custom-select d-block w-100" name="DIAGNOSTIC" id="diagnostic"> | ||||
| 		<option value="yes">Yes</option> | ||||
| 		<option value="no" selected>No</option> | ||||
| 		</select> | ||||
|     </div> | ||||
| 	</div> | ||||
| </div> | ||||
| </fieldset> | ||||
|  | ||||
| <fieldset> | ||||
| <legend><a href="javascript:void()" id="advanced">Advanced settings</a></legend> | ||||
| <!-- base settings end --> | ||||
| <br> | ||||
| <br> | ||||
| 		<div class="main-header"> | ||||
| 			<h1 id="advanced" style="cursor:pointer">Advanced settings</h1> | ||||
| 		</div> | ||||
| <!-- advanced settings --> | ||||
| <div id="advanced_div" style="display:none"> | ||||
| <div class="row"> | ||||
|     <div class="mb-3"> | ||||
| 	<div id="div_vpn2" class="hidden"> | ||||
| 		<div class="input-row"> | ||||
| 			<label for="vpn_domain">Please add domain url to download the VPN hash from:</label> | ||||
| 			<div class="input-container"> | ||||
| 				<input type="text" class="form-control" name="VPN_DOMAIN" id="vpn_domain" value="https://portal.safebox.network" placeholder="https://portal.safebox.network" size="40"> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<div class="input-row"> | ||||
| 			<label for="letsencrypt_servername">Please add letsencrypt server name (default is letsencrypt but you can add zerossl too):</label> | ||||
| 			<div class="input-container"> | ||||
| 				<input type="text" class="form-control" name="LETSENCRYPT_SERVERNAME" id="letsencrypt_servername" value="letsencrypt" placeholder="letsencrypt"> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	<div class="input-row"> | ||||
| 		<label for="registry">Please fill in the docker registry name (default:safebox):</label> | ||||
| 		<input type="registry" class="form-control" name="DOCKER_REGISTRY_URL" id="registry" value="safebox" required> | ||||
| 		<div class="invalid-feedback"> | ||||
| 		Please enter a valid registry url. | ||||
| 		<div class="input-container"> | ||||
| 			<input type="registry" class="form-control" name="DOCKER_REGISTRY_URL" id="registry" value="safebox" placeholder="safebox" required> | ||||
| 		</div> | ||||
|     </div> | ||||
| </div> | ||||
| <div class="row"> | ||||
|     <div class="mb-3"> | ||||
| 	</div> | ||||
| 	<div class="input-row"> | ||||
| 		<label for="smarthost">Smarthost proxy?</label> | ||||
| 		<select class="custom-select d-block w-100" name="SMARTHOST_PROXY" id="smarthost"> | ||||
| 		<option value="yes" selected>Yes</option> | ||||
| 		<option value="no">No</option> | ||||
| 		</select> | ||||
|     </div> | ||||
| </div> | ||||
| <div id="div_smarthost"> | ||||
| 	<div class="row"> | ||||
| 	    <div class="mb-3"> | ||||
| 		<label for="domain">Please fill in the domain name</label> | ||||
| 		<input type="domain" class="form-control" name="DOMAIN" id="domain" value="localhost"> | ||||
| 		<div class="invalid-feedback"> | ||||
| 		Please enter a valid domain. | ||||
| 		<div class="input-container"> | ||||
| 			<select class="custom-select d-block w-100" name="SMARTHOST_PROXY" id="smarthost"> | ||||
| 			<option value="yes" selected>Yes</option> | ||||
| 			<option value="no">No</option> | ||||
| 			</select> | ||||
| 		</div> | ||||
| 	    </div> | ||||
| 	</div> | ||||
| 	<div class="row"> | ||||
| 	    <div class="mb-3"> | ||||
| 		<label for="local_backend">Would you like to run local backend?</label> | ||||
| 		<select class="custom-select d-block w-100" name="LOCAL_BACKEND" id="local_backend"> | ||||
| 		<option value="yes">Yes</option> | ||||
| 		<option value="no" selected>No</option> | ||||
| 		</select> | ||||
| 	    </div> | ||||
| 	<div id="div_smarthost"> | ||||
| 		<div class="input-row"> | ||||
| 			<label for="domain">Please fill in the domain name</label> | ||||
| 			<div class="input-container"> | ||||
| 				<input type="domain" class="form-control" name="DOMAIN" id="domain" value="localhost"> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<div class="input-row"> | ||||
| 			<label for="local_backend">Would you like to run local backend?</label> | ||||
| 			<div class="input-container"> | ||||
| 				<select class="custom-select d-block w-100" name="LOCAL_BACKEND" id="local_backend"> | ||||
| 				<option value="yes">Yes</option> | ||||
| 				<option value="no" selected>No</option> | ||||
| 				</select> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </div> | ||||
| <div class="row"> | ||||
|     <div class="mb-3"> | ||||
| 	<div class="input-row"> | ||||
| 		<label for="localproxy">Local proxy?</label> | ||||
| 		<select class="custom-select d-block w-100" name="LOCAL_PROXY" id="localproxy"> | ||||
| 		<option value="yes" selected>Yes</option> | ||||
| 		<option value="no">No</option> | ||||
| 		</select> | ||||
|     </div> | ||||
| </div> | ||||
| <div class="row"> | ||||
|     <div class="mb-3"> | ||||
| 		<div class="input-container"> | ||||
| 			<select class="custom-select d-block w-100" name="LOCAL_PROXY" id="localproxy"> | ||||
| 			<option value="yes" selected>Yes</option> | ||||
| 			<option value="no">No</option> | ||||
| 			</select> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	<div class="input-row"> | ||||
| 		<label for="cron">Cron?</label> | ||||
| 		<select class="custom-select d-block w-100" name="CRON" id="cron"> | ||||
| 		<option value="yes">Yes</option> | ||||
| 		<option value="no">No</option> | ||||
| 		</select> | ||||
|     </div> | ||||
| 		<div class="input-container"> | ||||
| 			<select class="custom-select d-block w-100" name="CRON" id="cron"> | ||||
| 			<option value="yes">Yes</option> | ||||
| 			<option value="no">No</option> | ||||
| 			</select> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </div> | ||||
| </div> | ||||
| </fieldset> | ||||
| <br> | ||||
| <button class="btn btn-lg btn-primary btn-block" type="submit">Start install</button> | ||||
| <!-- advanced settings end --> | ||||
| 	<button class="save-button" type="submit">Start install</button> | ||||
| </form> | ||||
| </div> | ||||
| </div> | ||||
| <br> | ||||
| <br> | ||||
|     </div> | ||||
|   </div> | ||||
|  | ||||
| <!-- Optional JavaScript --> | ||||
| <!-- jQuery first, then Popper.js, then Bootstrap JS --> | ||||
| <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> | ||||
| @@ -204,8 +201,14 @@ jQuery(document).ready(function(){ | ||||
| 		else jQuery('#div_user_auth').hide(); | ||||
| 	}); | ||||
| 	jQuery('select#vpn').click(function() { | ||||
| 		if (jQuery(this).val()=='yes') jQuery('#div_vpn').show(); | ||||
| 		else jQuery('#div_vpn').hide(); | ||||
| 		if (jQuery(this).val()=='yes') { | ||||
| 			jQuery('#div_vpn1').show(); | ||||
| 			jQuery('#div_vpn2').show(); | ||||
| 		} | ||||
| 		else { | ||||
| 			jQuery('#div_vpn1').hide(); | ||||
| 			jQuery('#div_vpn2').hide(); | ||||
| 		} | ||||
| 	}); | ||||
| 	jQuery('select#discovery').click(function() { | ||||
| 		if (jQuery(this).val()=='yes') jQuery('#div_discover').show(); | ||||
|   | ||||
							
								
								
									
										173
									
								
								install.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,173 @@ | ||||
|  | ||||
| function updateProgress(percentage) { | ||||
|     // Clamp percentage between 0 and 99.9 (never reach 100) | ||||
|     percentage = Math.max(0, Math.min(99.9, percentage)); | ||||
|      | ||||
|     progressBar.style.width = percentage + '%'; | ||||
|     progressText.textContent = Math.round(percentage) + '%'; | ||||
|     currentProgress = percentage; | ||||
| } | ||||
|  | ||||
| function setProgress(percentage) { | ||||
|     clearInterval(progressInterval); | ||||
|     updateProgress(percentage); | ||||
|     startProgress(60000); | ||||
| } | ||||
|  | ||||
| function startProgress(duration) { | ||||
|     clearInterval(progressInterval); | ||||
|     const startTime = Date.now() - (currentProgress / 100) * duration; | ||||
|      | ||||
|     progressInterval = setInterval(() => { | ||||
| 	const elapsed = Date.now() - startTime; | ||||
| 	// Use exponential approach to 100% - gets slower as it approaches | ||||
| 	let progress = (1 - Math.exp(-4 * elapsed / duration)) * 100; | ||||
| 	 | ||||
| 	// Ensure it never reaches 100% | ||||
| 	progress = Math.min(progress, 99.9); | ||||
| 	 | ||||
| 	updateProgress(progress); | ||||
| 	 | ||||
| 	// Stop when we've been running for 60+ seconds | ||||
| 	if (elapsed >= duration) { | ||||
| 	    clearInterval(progressInterval); | ||||
| 	} | ||||
|     }, 100); // Update every 100ms | ||||
| } | ||||
|  | ||||
| function resetProgress() { | ||||
|     clearInterval(progressInterval); | ||||
|     updateProgress(0); | ||||
| } | ||||
|  | ||||
| // Example: Simulate loading with custom speeds | ||||
| function simulateLoading(duration = 3000) { | ||||
|     clearInterval(progressInterval); | ||||
|     const startTime = Date.now(); | ||||
|      | ||||
|     progressInterval = setInterval(() => { | ||||
| 	const elapsed = Date.now() - startTime; | ||||
| 	const progress = Math.min((elapsed / duration) * 100, 99.9); | ||||
| 	 | ||||
| 	updateProgress(progress); | ||||
| 	 | ||||
| 	if (elapsed >= duration) { | ||||
| 	    clearInterval(progressInterval); | ||||
| 	} | ||||
|     }, 16); // ~60fps | ||||
| } | ||||
|  | ||||
| function redirectToInstall() { | ||||
| 	setProgress(100); | ||||
| 	window.location.href = 'install.html?t='+Date.now(); | ||||
| } | ||||
|  | ||||
| function redirectToManage() { | ||||
| 	setProgress(100); | ||||
| 	window.location.href = 'manage.html?t='+Date.now(); | ||||
| } | ||||
|  | ||||
| function start_system() { | ||||
|   var url  = 'scan.php?op=system'; | ||||
|   $.get(url, function(data){ | ||||
|     console.log('start_system: '+data); | ||||
|     if (data=='OK') { | ||||
|       $("#info").html('Scanning for previous install. Please wait...'); | ||||
|       check_system(); | ||||
|     } | ||||
|     else { | ||||
|       $("#info").html('Scanning for previous install has aborted...'); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function check_system() { | ||||
|   var url  = 'scan.php?op=check_system'; | ||||
|   $.get(url, function(data){ | ||||
|     console.log('check_system: '+data); | ||||
|     if (data=='NEW') { | ||||
|       $("#info").html('No previous install has found...'); | ||||
|       setTimeout(redirectToInstall, 3000); | ||||
|     } | ||||
|     else if (data=='EXISTS') { | ||||
|       $("#info").html('Previous install has found...'); | ||||
|       setProgress(80); | ||||
|       setTimeout(redirectToManage, 3000); | ||||
|     } | ||||
|     else if (data=='WAIT') { | ||||
|       setTimeout(check_system, 1000); | ||||
|     } | ||||
|     else { | ||||
| 	    // UNEXPECTED ERROR | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function check_redis() { | ||||
|  | ||||
|   var url  = 'scan.php?op=redis'; | ||||
|   $.get(url, function(data){ | ||||
|     console.log('check_redis: '+data); | ||||
|     if (data=='OK') { | ||||
|       $("#info").html('Redis server - OK'); | ||||
|       check_install(); | ||||
|     } | ||||
|     else { | ||||
|       $("#info").html('Redis server is not available...'); | ||||
|       setTimeout(check_redis, 1000); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function check_directory() { | ||||
|  | ||||
|   var url  = 'scan.php?op=directory'; | ||||
|   $.get(url, function(data){ | ||||
|     console.log('check_directory: '+data); | ||||
|     if (data=='OK') { | ||||
|       $("#info").html('Connection is ready - OK'); | ||||
|       check_install(); | ||||
|     } | ||||
|     else { | ||||
|       $("#info").html('Shared directory is not available...'); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function check_interface() { | ||||
|  | ||||
|   var url  = 'scan.php?op=get_interface'; | ||||
|   $.get(url, function(data){ | ||||
|     console.log('check_interface: '+data); | ||||
|     if (data=='redis') { | ||||
|   	check_redis(); | ||||
|     } | ||||
|     else if (data=='directory') { | ||||
|   	check_directory(); | ||||
|     } | ||||
|     else { | ||||
| 	$("#info").html('Invalid interface definition...'); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function check_install() { | ||||
|  | ||||
|     console.log('install: '+install); | ||||
|     if (install==1) { | ||||
| 	  var url  = 'scan.php?op=check_install'; | ||||
| 	  $.get(url, function(data){ | ||||
| 	    console.log('check_install:'+data+' counter: '+counter); | ||||
| 	    if (data=='INSTALLED') { | ||||
| 		redirectToManage(); | ||||
| 	    } | ||||
| 	    else { | ||||
| 	      counter+=1 | ||||
| 	      $("#info").html('Please wait ...'); | ||||
| 	      setTimeout(check_install, 1000); | ||||
| 	    } | ||||
| 	  }); | ||||
|     } | ||||
|     else start_system(); // scan | ||||
| } | ||||
|  | ||||
							
								
								
									
										126
									
								
								install.php
									
									
									
									
									
								
							
							
						
						| @@ -46,7 +46,7 @@ else { | ||||
| 	$header_text="Installing in progress... Please wait..."; | ||||
| 	//$key = "install:".date("YmdHis"); | ||||
| 	$key = "install"; | ||||
| 	if (set_output($key,$json)) echo "OK"; | ||||
| 	if (set_output($key,$json)) echo ""; | ||||
| 	else echo "ERROR"; | ||||
| } | ||||
|  | ||||
| @@ -61,106 +61,54 @@ echo "<pre>".$output."</pre>"; | ||||
| //echo $output; | ||||
| */ | ||||
|  | ||||
| ?> | ||||
| <!doctype html> | ||||
| ?><!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
| <!-- Required meta tags --> | ||||
| <meta charset="utf-8"> | ||||
| <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | ||||
| <title>INSTALLER TOOL</title> | ||||
| <!-- Bootstrap CSS --> | ||||
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.2.1/dist/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous"> | ||||
| <!-- Custom styles for this template --> | ||||
| <link href="installer.css?t=1" rel="stylesheet"> | ||||
|   <meta charset="UTF-8" /> | ||||
|   <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||||
|   <title>Safebox - INSTALLER TOOL</title> | ||||
|   <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Switzer:ital,wght@0,300;0,400;0,500;0,600;1,400&display=swap"/> | ||||
|   <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"/> | ||||
|   <link rel="stylesheet" href="style.css?t=4" /> | ||||
| </head> | ||||
| <body id="install" class="text-center"> | ||||
| <div class="container-fluid"> | ||||
| <div class="col-md-12"> | ||||
| 	<h1><?php echo $header_text?></h1> | ||||
| 	<div id="redis"></div> | ||||
| 	<div id="response"></div> | ||||
| </div> | ||||
| </div> | ||||
|   <div class="main"> | ||||
|     <div id="myAppsContainer"> | ||||
| 	<div class="logo" style="margin:100px 0px 20px 0px;"> | ||||
| 		<img src="/img/logo.svg" alt="Safebox"/> | ||||
| 		<span>Safebox</span> | ||||
| 	</div> | ||||
| 	<div class="progress-box"> | ||||
| 		<div class="progress-title"><?php echo $header_text?></div> | ||||
| 		<div class="progress-description" id="info"></div> | ||||
| 		<div class="progress-container-shadow"> | ||||
| 		</div> | ||||
| 		<div class="progress-container"> | ||||
| 			<div class="progress-bar" id="progressBar"></div> | ||||
| 			<div class="progress-text" id="progressText">0%</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
|     </div> | ||||
|   </div> | ||||
| <!-- Optional JavaScript --> | ||||
| <!-- jQuery first, then Popper.js, then Bootstrap JS --> | ||||
| <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> | ||||
| <script src="https://cdn.jsdelivr.net/npm/popper.js@1.14.6/dist/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script> | ||||
| <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.2.1/dist/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script> | ||||
| <script src="install.js?t=7"></script> | ||||
| <script> | ||||
| $(function() { | ||||
| const progressBar = document.getElementById('progressBar'); | ||||
| const progressText = document.getElementById('progressText'); | ||||
| let currentProgress = 0; | ||||
| let progressInterval; | ||||
| let install = 1; | ||||
|  | ||||
| function redirectToManage() { | ||||
|     window.location.href = 'manage.html'; | ||||
| } | ||||
| // Initialize | ||||
| updateProgress(0); | ||||
| startProgress(90000);// 90 seconds | ||||
|  | ||||
| function check_install() { | ||||
|  | ||||
|   var url  = 'scan.php?op=check_install&key=<?php echo $key;?>'; | ||||
|   $.get(url, function(data){ | ||||
|     console.log('check_install:'+data+' counter: '+counter); | ||||
|     if (data=='INSTALLED') { | ||||
| 	redirectToManage(); | ||||
|     } | ||||
|     else { | ||||
|       counter+=1 | ||||
|       $("#response").html('Please wait... ' + counter); | ||||
|       setTimeout(check_install, 1000); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function check_redis() { | ||||
|  | ||||
|   var url  = 'scan.php?op=redis'; | ||||
|   $.get(url, function(data){ | ||||
|     console.log('check_redis: '+data); | ||||
|     if (data=='OK') { | ||||
|       $("#redis").html('Redis server - OK'); | ||||
|       check_install(); | ||||
|     } | ||||
|     else { | ||||
|       $("#redis").html('Redis server is not available...'); | ||||
|       setTimeout(check_redis, 1000); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function check_directory() { | ||||
|  | ||||
|   var url  = 'scan.php?op=directory'; | ||||
|   $.get(url, function(data){ | ||||
|     console.log('check_directory: '+data); | ||||
|     if (data=='OK') { | ||||
|       $("#redis").html('Connection is ready - OK'); | ||||
|       check_install(); | ||||
|     } | ||||
|     else { | ||||
|       $("#redis").html('Shared directory is not available...'); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function check_interface() { | ||||
|  | ||||
|   var url  = 'scan.php?op=get_interface'; | ||||
|   $.get(url, function(data){ | ||||
|     console.log('check_interface: '+data); | ||||
|     if (data=='redis') { | ||||
|   	check_redis(); | ||||
|     } | ||||
|     else if (data=='directory') { | ||||
|   	check_directory(); | ||||
|     } | ||||
|     else { | ||||
| 	$("#redis").html('Invalid interface definition...'); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
|   check_interface(); | ||||
|   counter=0; | ||||
| }); | ||||
| check_interface(); | ||||
| counter=0; | ||||
| </script> | ||||
| </body> | ||||
| </html> | ||||
|   | ||||
							
								
								
									
										761
									
								
								manage.html
									
									
									
									
									
								
							
							
						
						| @@ -1,685 +1,24 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="hu"> | ||||
| <head> | ||||
|   <meta charset="UTF-8" /> | ||||
|   <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||||
|   <title>Safebox</title> | ||||
|   <link href="https://fonts.googleapis.com/css2?family=Switzer:ital,wght@0,300;0,400;0,500;0,600;1,400&display=swap" rel="stylesheet" /> | ||||
|   <style> | ||||
|     :root { | ||||
|       --highlight-color: #F9DB54; | ||||
|     } | ||||
|  | ||||
|     body { | ||||
|       margin: 0; | ||||
|       font-family: 'Switzer', sans-serif; | ||||
|       font-weight: 500; | ||||
|       font-size: 16px; | ||||
|       background-color: #000; | ||||
|       color: white; | ||||
|       display: flex; | ||||
|       height: 100vh; | ||||
|     } | ||||
|  | ||||
|     h4 { | ||||
| 	font-size: 16px; | ||||
|     } | ||||
|  | ||||
| 	table {border: 1px solid var(--highlight-color); width:100%;} | ||||
| 	table th {padding-left: 20px; text-align: left; color: var(--highlight-color); width: 25%;} | ||||
| 	table td {padding-left: 20px; text-align:left; width: 25%;} | ||||
|  | ||||
|     .sidebar { | ||||
|       width: 250px; | ||||
|       background-color: #101214; | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|       justify-content: space-between; | ||||
|       padding: 20px 0; | ||||
|       margin: 20px; | ||||
|       border-radius: 20px; | ||||
|       transition: width 0.3s; | ||||
|     } | ||||
|  | ||||
|     .logo { | ||||
|       text-align: center; | ||||
|       margin-bottom: 40px; | ||||
|       font-size: 24px; | ||||
|       font-weight: bold; | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       justify-content: center; | ||||
|       gap: 10px; | ||||
|     } | ||||
|  | ||||
|     .logo img { | ||||
|       height: 30px; | ||||
|     } | ||||
|  | ||||
|     .menu { | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|       gap: 5px; | ||||
|       padding: 0 10px; | ||||
|     } | ||||
|  | ||||
|     .vpn-item { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       gap: 10px; | ||||
|       padding: 10px 20px; | ||||
|       cursor: pointer; | ||||
|       color: white; | ||||
|       transition: color 0.3s; | ||||
|     } | ||||
|  | ||||
|     .menu-item { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       gap: 10px; | ||||
|       padding: 10px 20px; | ||||
|       cursor: pointer; | ||||
|       color: white; | ||||
|       transition: color 0.3s; | ||||
|     } | ||||
|  | ||||
|     .menu-item:hover { | ||||
|       color: var(--highlight-color); | ||||
|     } | ||||
|  | ||||
|     .menu-item i { | ||||
|       font-size: 18px; | ||||
|     } | ||||
|  | ||||
|     .menu-item span { | ||||
|       display: inline; | ||||
|     } | ||||
|  | ||||
|     .menu-item img { | ||||
|       width: 24px; | ||||
|     } | ||||
|  | ||||
|     .menu-item.active { | ||||
|       background-color: var(--highlight-color); | ||||
|       color: black; | ||||
|       border-radius: 10px; | ||||
|       font-weight: bold; | ||||
|     } | ||||
|  | ||||
|     .menu-item.active:hover { | ||||
|       color: black; | ||||
|     } | ||||
|  | ||||
| .menu-item span.arrow { | ||||
|   margin-left: auto; | ||||
|   color: gray; | ||||
|   font-size: 20px; | ||||
|   transition: transform 0.3s ease; | ||||
| } | ||||
|  | ||||
| .menu-item.open .arrow { | ||||
|   transform: rotate(180deg); | ||||
| } | ||||
|  | ||||
| .submenu { | ||||
|   display: none; | ||||
|   margin-left: 40px; | ||||
|   flex-direction: column; | ||||
|   margin-bottom: 10px; | ||||
| } | ||||
|  | ||||
| .submenu-item { | ||||
|   padding: 5px 14px; | ||||
|   font-size: 14px; | ||||
|   color: #ffffff; | ||||
|   cursor: pointer; | ||||
|   border-radius: 10px; | ||||
| } | ||||
|  | ||||
| .submenu-item:hover { | ||||
|   color: #000000; | ||||
|   background-color: var(--highlight-color); | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Switch container */ | ||||
| .switch { | ||||
|   position: relative; | ||||
|   display: inline-block; | ||||
|   width: 60px; | ||||
|   height: 26px; | ||||
| } | ||||
|  | ||||
| /* Hide native checkbox */ | ||||
| .switch input { | ||||
|   opacity: 0; | ||||
|   width: 0; | ||||
|   height: 0; | ||||
| } | ||||
|  | ||||
| /* Slider background */ | ||||
| .slider-text { | ||||
|   position: absolute; | ||||
|   top: 0; | ||||
|   left: 0; | ||||
|   right: 0; | ||||
|   bottom: 0; | ||||
|   background-color: #ccc; | ||||
|   border-radius: 26px; | ||||
|   transition: background-color 0.3s; | ||||
|   display: flex !important; | ||||
|   align-items: center; | ||||
|   justify-content: flex-end; /* default: OFF on the right */ | ||||
|   padding: 0 8px; | ||||
|   font-size: 12px; | ||||
|   font-weight: bold; | ||||
|   color: #fff; | ||||
| } | ||||
|  | ||||
| /* Circle */ | ||||
| .slider-text::before { | ||||
|   content: ""; | ||||
|   position: absolute; | ||||
|   height: 22px; | ||||
|   width: 22px; | ||||
|   left: 2px; | ||||
|   top: 2px; | ||||
|   background-color: white; | ||||
|   border-radius: 50%; | ||||
|   transition: transform 0.3s; | ||||
| } | ||||
|  | ||||
| /* Checked: move dot, change background and text alignment */ | ||||
| .switch input:checked + .slider-text { | ||||
|   background-color: #4CAF50; | ||||
|   justify-content: flex-start; | ||||
| } | ||||
|  | ||||
| /* Dot animation */ | ||||
| .switch input:checked + .slider-text::before { | ||||
|   transform: translateX(34px); | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Status label */ | ||||
| .status-label { | ||||
|   font-size: 12px; | ||||
|   color: #888; | ||||
|   min-width: 28px; | ||||
|   text-align: left; | ||||
| } | ||||
|  | ||||
|     .details { | ||||
|       color: white; | ||||
|       text-decoration: none; | ||||
|     } | ||||
|  | ||||
|     .yellow-row { | ||||
| 	max-width:250px; | ||||
| 	display: flex; | ||||
| 	align-items: flex-start; | ||||
| 	box-sizing: border-box; | ||||
|         margin: 0px; | ||||
| 	width: 100%; | ||||
| 	padding: 0 20px; | ||||
|     } | ||||
|  | ||||
|     .yellow-box { | ||||
|       background-color: var(--highlight-color); | ||||
|       color: black; | ||||
|       text-align: left; | ||||
|       margin: 0px; | ||||
|       padding: 10px 18px; | ||||
|       border-radius: 20px 20px 0px 20px; | ||||
|       font-weight: bold; | ||||
|       font-size: 12px; | ||||
|     } | ||||
|  | ||||
|     .yellow-corner { | ||||
| 	padding:0px; | ||||
| 	border: 0px; | ||||
| 	display: flex; | ||||
| 	margin: 0px; | ||||
| 	margin-top: auto; | ||||
| 	align-items: flex-end; | ||||
|     } | ||||
|  | ||||
|     .main { | ||||
|       flex: 1; | ||||
|       padding: 30px; | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|       overflow-y: auto; | ||||
|     } | ||||
|  | ||||
| 	#appsContainer { | ||||
| 	  display: flex; | ||||
| 	  flex-wrap: wrap; | ||||
| 	  gap: 16px; | ||||
| 	  padding: 0px; | ||||
| 	} | ||||
|  | ||||
| 	.app { | ||||
| 	  border: 1px solid #41464f; | ||||
| 	  border-radius: 15px; | ||||
| 	  text-align: left; | ||||
| 	  padding: 20px; | ||||
| 	  width: 130px; | ||||
| 	  min-height: 200px; | ||||
| 	  text-align: left; | ||||
| 	  display: flex; | ||||
| 	  flex-direction: column; | ||||
| 	  align-items: left; | ||||
| 	} | ||||
|  | ||||
| 	.app-img { | ||||
| 	  padding: 0px; | ||||
| 	} | ||||
|  | ||||
| 	.app img { | ||||
| 	  border-radius: 15px; | ||||
| 	  background-color: #101214; | ||||
| 	  width: 70px; | ||||
| 	  height: 70px; | ||||
| 	  object-fit: contain; | ||||
| 	  margin-bottom: 20px; | ||||
| 	  padding: 30px; | ||||
| 	} | ||||
|  | ||||
| 	.app-label { | ||||
| 	  font-size: 10px; | ||||
| 	  color: #cccccc; | ||||
| 	  margin-bottom: 10px; | ||||
| 	  align-items: left; | ||||
| 	  text-align: left; | ||||
| 	} | ||||
|  | ||||
| 	.app-name { | ||||
| 	  font-size: 16px; | ||||
| 	  font-weight: bold; | ||||
| 	  margin-bottom: 30px; | ||||
| 	} | ||||
|  | ||||
| 	.more-btn { | ||||
| 	  background-color: inherit; | ||||
| 	  color: #cccccc; | ||||
| 	  border: 1px solid #cccccc; | ||||
| 	  border-radius: 15px; | ||||
| 	  padding: 18px; | ||||
| 	  font-size: 16px; | ||||
| 	  font-weight: 400; | ||||
| 	  cursor: pointer; | ||||
| 	  width: 100%; | ||||
| 	  font-weight: bold; | ||||
| 	} | ||||
|  | ||||
| 	.more-btn:hover { | ||||
| 	  color: var(--highlight-color); | ||||
| 	  border: 1px solid var(--highlight-color); | ||||
| 	} | ||||
|  | ||||
| 	.app-details { | ||||
| 		padding: 0px 50px; | ||||
| 	} | ||||
|  | ||||
| 	.app-fields { | ||||
| 	  border: 1px solid #41464f; | ||||
| 	  border-radius: 15px; | ||||
| 	  text-align: left; | ||||
| 	  padding: 20px; | ||||
| 	  margin: 30px 0px 10px 0px; | ||||
| 	  display: flex; | ||||
| 	  flex-direction: column; | ||||
| 	  align-items: left; | ||||
| 	} | ||||
|  | ||||
| 	.app-fields .row { | ||||
|             display: block; | ||||
| 	      justify-content: space-between; | ||||
| 	      align-items: center; | ||||
| 	      padding: 5px 20px; | ||||
| 	      border-radius: 10px; | ||||
| 	      gap: 10px; | ||||
| 	} | ||||
|  | ||||
| 	#letsencrypt { | ||||
| 		padding-top: 20px; | ||||
| 	} | ||||
|  | ||||
| .header-block { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   align-items: flex-start; | ||||
|   padding: 20px 0px; | ||||
| } | ||||
|  | ||||
| .logo-and-text { | ||||
|   display: flex; | ||||
|   align-items: flex-start; | ||||
|   gap: 16px; | ||||
| } | ||||
|  | ||||
| .applogo img { | ||||
|   border-radius: 15px; | ||||
|   background-color: #101214; | ||||
|   width: 70px; | ||||
|   height: 70px; | ||||
|   object-fit: contain; | ||||
|   padding: 40px; | ||||
| } | ||||
|  | ||||
| .text-content { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
| } | ||||
|  | ||||
| .title { | ||||
|   font-size: 28px; | ||||
|   font-weight: bold; | ||||
|   margin: 0; | ||||
| } | ||||
|  | ||||
| .subtitle { | ||||
|   font-size: 18px; | ||||
|   margin: 4px 0; | ||||
|   color: #999; | ||||
| } | ||||
|  | ||||
| .description { | ||||
|   font-size: 16px; | ||||
|   margin: 20px 0 0; | ||||
|   color: #999; | ||||
|   line-height: 1.5; | ||||
| } | ||||
|  | ||||
| .notification-btn { | ||||
|       background-color: var(--highlight-color); | ||||
|       color: black; | ||||
|       border: none; | ||||
|       padding: 10px 20px; | ||||
|       border-radius: 10px; | ||||
|       cursor: pointer; | ||||
|       font-weight: bold; | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       gap: 10px; | ||||
| } | ||||
|  | ||||
|     .main-header { | ||||
|       display: flex; | ||||
|       justify-content: space-between; | ||||
|       align-items: center; | ||||
|       margin-bottom: 30px; | ||||
|     } | ||||
|  | ||||
|     .main-header h1 { | ||||
|       margin: 0; | ||||
|       font-size: 28px; | ||||
|     } | ||||
|  | ||||
|     .main-header button { | ||||
|       background-color: var(--highlight-color); | ||||
|       color: black; | ||||
|       border: none; | ||||
|       padding: 10px 20px; | ||||
|       border-radius: 10px; | ||||
|       cursor: pointer; | ||||
|       font-weight: bold; | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       gap: 10px; | ||||
|     } | ||||
|  | ||||
|     .input-row, .input-group { | ||||
|       display: flex; | ||||
|       justify-content: space-between; | ||||
|       align-items: center; | ||||
|       border: 1px solid #41464f; | ||||
|       padding: 15px 20px; | ||||
|       margin-bottom: 15px; | ||||
|       border-radius: 10px; | ||||
|       gap: 10px; | ||||
|     } | ||||
|  | ||||
|     .input-container { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       gap: 5px; | ||||
|       flex: 1; | ||||
|       justify-content: flex-end; | ||||
|       position: relative; | ||||
|     } | ||||
|  | ||||
|     .input-row input, | ||||
|     .input-row select, | ||||
|     .row input,  | ||||
|     .row select { | ||||
|       background: transparent; | ||||
|       border: 1px solid #41464f; | ||||
|       padding: 8px 30px 8px 8px; | ||||
|       border-radius: 10px; | ||||
|       color: white; | ||||
|       transition: color 0.2s; | ||||
|       max-width: 40%; | ||||
|       box-sizing: border-box; | ||||
|     } | ||||
|  | ||||
|     .row input,  | ||||
|     .row select { | ||||
|       padding: 12px 30px 12px 12px; | ||||
|       font-size: 16px; | ||||
|     } | ||||
|  | ||||
|     .input-row input:focus, | ||||
|     .input-row select:focus, | ||||
|     .row input, | ||||
|     .row select { | ||||
|       outline: none; | ||||
|       background-color: black; | ||||
|       color: var(--highlight-color); | ||||
|     } | ||||
|  | ||||
|     .input-row select, | ||||
|     .row select { | ||||
|       appearance: none; | ||||
|       -webkit-appearance: none; | ||||
|       -moz-appearance: none; | ||||
|       background-image: url('data:image/svg+xml;utf8,<svg fill="white" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M7 10l5-5 5 5zM7 14l5 5 5-5z"/></svg>'); | ||||
|       background-repeat: no-repeat; | ||||
|       background-position: right 8px center; | ||||
|       background-size: 16px; | ||||
|       padding-right: 32px; | ||||
|     } | ||||
|  | ||||
| 	.info-container { | ||||
| 		position: relative; | ||||
| 		left: -18px; | ||||
| 		top: -22px; | ||||
| 		display: inline-block; | ||||
| 		font-family: Arial, sans-serif; | ||||
| 		margin: 0px; | ||||
| 		width: 0px; | ||||
| 	} | ||||
|  | ||||
|     .info-icon { | ||||
|       background-color: var(--highlight-color); | ||||
|       color: black; | ||||
|       border-radius: 50%; | ||||
|       padding: 1px 4px; | ||||
|       cursor: pointer; | ||||
|       font-weight: bold; | ||||
|       font-size: 10px; | ||||
|       text-align: center; | ||||
|       line-height: 1; | ||||
|       display: inline-block; | ||||
|     } | ||||
|  | ||||
|     .tooltip { | ||||
|       visibility: hidden; | ||||
|       background-color: #333; | ||||
|       border-radius: 5px; | ||||
|       padding: 4px 8px; | ||||
|       position: absolute; | ||||
|       z-index: 1; | ||||
|       bottom: 100%; /* above the icon */ | ||||
|       left: 50%; | ||||
|       transform: translateX(-78%); | ||||
|       opacity: 0; | ||||
|       transition: opacity 0.3s; | ||||
|       /*white-space: nowrap;*/ | ||||
|       text-align: right; | ||||
|       width: 300px; | ||||
|     } | ||||
|  | ||||
|     .tooltip::after { | ||||
|       content: ""; | ||||
|       position: absolute; | ||||
|       top: 100%; /* bottom of tooltip */ | ||||
|       left: 80%; | ||||
|       margin-left: -5px; | ||||
|       border-width: 5px; | ||||
|       border-style: solid; | ||||
|       border-color: #333 transparent transparent transparent; | ||||
|     } | ||||
|  | ||||
|     .info-container:hover .tooltip { | ||||
|       visibility: visible; | ||||
|       opacity: 1; | ||||
|     } | ||||
|  | ||||
|     .clear-btn { | ||||
|       position: absolute; | ||||
|       right: 8px; | ||||
|       background: transparent; | ||||
|       border: none; | ||||
|       color: white; | ||||
|       cursor: pointer; | ||||
|       font-size: 16px; | ||||
|       padding: 0; | ||||
|       width: 20px; | ||||
|       height: 20px; | ||||
|       line-height: 20px; | ||||
|       text-align: center; | ||||
|       user-select: none; | ||||
|     } | ||||
|  | ||||
|     .save-button { | ||||
|       background-color: var(--highlight-color); | ||||
|       color: black; | ||||
|       border: none; | ||||
|       padding: 15px 30px; | ||||
|       font-size: 16px; | ||||
|       border-radius: 10px; | ||||
|       font-weight: bold; | ||||
|       cursor: pointer; | ||||
|       margin-top: 30px; | ||||
|       align-self: flex-start; | ||||
|     } | ||||
|  | ||||
|     .save-button:hover { | ||||
|       background-color: #f5c300; | ||||
|     } | ||||
|  | ||||
|     .buttons .btn { | ||||
|       background-color: black; | ||||
|       border: 1px solid #999; | ||||
|       color: #999; | ||||
|       padding: 10px 15px; | ||||
|       font-size: 14px; | ||||
|       border-radius: 5px; | ||||
|       font-weight: bold; | ||||
|       cursor: pointer; | ||||
|       margin-top: 10px; | ||||
|       align-self: flex-start; | ||||
|     } | ||||
|  | ||||
|     .buttons .btn:hover { | ||||
|       border: 1px solid #f5c300; | ||||
|       color: #f5c300; | ||||
|     } | ||||
|  | ||||
|         .hidden { | ||||
|             display: none; | ||||
|         } | ||||
|  | ||||
|         .popup { | ||||
| 	    width: 100%; | ||||
|         } | ||||
|  | ||||
|         .popup-content { | ||||
|             width: 100%; | ||||
|             text-align: left; | ||||
|         } | ||||
|  | ||||
|         #popup .row { | ||||
|             display: flex; | ||||
|         } | ||||
|  | ||||
|     @media (max-width: 768px) { | ||||
|       .sidebar { | ||||
|         width: 80px; | ||||
|         padding: 20px 5px; | ||||
|       } | ||||
|  | ||||
|       .logo { | ||||
|         font-size: 0; | ||||
|       } | ||||
|  | ||||
|       .logo span { | ||||
|         display: none; | ||||
|       } | ||||
|  | ||||
|       .menu-item { | ||||
|         justify-content: center; | ||||
|         gap: 0; | ||||
|       } | ||||
|  | ||||
|       .menu-item span { | ||||
|         display: none; | ||||
|       } | ||||
|  | ||||
|       .yellow-box { | ||||
|         font-size: 0; | ||||
|         padding: 10px; | ||||
|       } | ||||
|  | ||||
|       .input-row { | ||||
|         flex-direction: column; | ||||
|         align-items: flex-start; | ||||
|       } | ||||
|  | ||||
|       .input-container { | ||||
|         width: 100%; | ||||
|         justify-content: flex-start; | ||||
|       } | ||||
|  | ||||
|       .input-row input, | ||||
|       .input-row select { | ||||
|         max-width: 100%; | ||||
|       } | ||||
|     } | ||||
|   </style> | ||||
|   <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"/> | ||||
| <meta charset="UTF-8" /> | ||||
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||||
| <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> | ||||
| <meta http-equiv="Pragma" content="no-cache" /> | ||||
| <meta http-equiv="Expires" content="0" /> | ||||
| <title>Safebox</title> | ||||
| <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Switzer:ital,wght@0,300;0,400;0,500;0,600;1,400&display=swap"/> | ||||
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"/> | ||||
| <link rel="stylesheet" href="style.css?t=14" /> | ||||
| </head> | ||||
| <body> | ||||
|   <div class="sidebar"> | ||||
|     <div> | ||||
|       <div class="logo"> | ||||
|         <img src="/img/logo.png" alt="Safebox"/> | ||||
|     <div class="sidebar-top"> | ||||
|       <div id="logo" class="logo"> | ||||
|         <img src="/img/logo.svg" alt="Safebox"/> | ||||
|         <span>Safebox</span> | ||||
|       </div> | ||||
|       <div class="menu"> | ||||
| 	    <div class="menu-item" style="margin-bottom:20px"> | ||||
| 		<img src="/img/globe.png" data-src="/img/globe.png" data-hover="/img/globe.png" alt="VPN" /> | ||||
| 		<div class="menu-label"> | ||||
| 		    <span id="vpnBtn">VPN</span> | ||||
| 		    <label class="switch"> | ||||
| 		      <input type="checkbox" id="vpnToggle"> | ||||
| 		      <span class="slider-text"><span class="switch-label">OFF</span></span> | ||||
| 		    </label> | ||||
| 		</div> | ||||
| 	    </div> | ||||
|  | ||||
| <!-- | ||||
| 	<div class="menu-item"><i class="fas fa-th"></i><span>Applications</span></div> | ||||
|         <div class="menu-item"><i class="fas fa-hdd"></i><span>Disk Management</span></div> | ||||
| @@ -699,29 +38,42 @@ | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="yellow-row"> | ||||
| 	<div id="pro_off"> | ||||
| 	    <div class="yellow-box"> | ||||
| 		<div id="pro_off"> | ||||
| 		      <h4>Safebox Pro</h4> | ||||
| 		<div class="pro-text"> | ||||
| 		  <span>Safebox Pro</span> | ||||
| 		</div> | ||||
| 		      <p>Enjoy benefits and unlock more feature such as remote access, geo-redundant backups etc. <br><br> | ||||
| 		      <a href="" class="details">Read details</a><br><br> | ||||
| 			<img src="/img/upgrade.png" alt="Upgrade now" width="100%"/>	 | ||||
| 			<img src="/img/upgrade.png" alt="Upgrade now" width="80%" id="upgradeBtn" style="cursor:pointer"/>	 | ||||
| 		      </p> | ||||
| 	    </div> | ||||
| 	</div> | ||||
| 	<div id="pro_on" class="hidden"> | ||||
| 	    <div class="grey-box"> | ||||
| 		<div class="vpn-text" id="vpnBtn" > | ||||
| 		  <img src="/img/globe.png" data-src="/img/globe.png" data-hover="/img/globe.png" alt="VPN"/> | ||||
| 		  <span>Remote access</span> | ||||
| 		</div> | ||||
| 		<div id="pro_on" class="hidden"> | ||||
| 		      <h4>Login</h4> | ||||
| 		      <p>Enjoy benefits and unlock more feature such as remote access, geo-redundant backups etc. <br><br> | ||||
| 		      <a href="" class="details">Read details</a><br><br> | ||||
| 		      <p>It allows you to connect your installed apps to a custom domain (your own or one registered through us), so you can securely log in from any browser, anywhere in the world. | ||||
| 		      </p> | ||||
| 		</div> | ||||
| 	    </div> | ||||
| 	    <div class="yellow-corner"> | ||||
| 		<img src="img/yellow_corner.png" alt=""/> | ||||
| 			<div class="vpn-item"> | ||||
| <!-- | ||||
| 			    <label class="switch"> | ||||
| 			      <input type="checkbox" id="vpnToggle" onclick="return false"> | ||||
| 			      <span class="slider-text"><span class="switch-label">OFF</span></span> | ||||
| 			    </label> | ||||
| --> | ||||
| 				<span id="vpn_on" class="hidden"><img src="/img/on.png" alt="on" /></span> | ||||
| 				<span id="vpn_off" class="hidden"><img src="/img/off.png" alt="off" /></span> | ||||
| 			</div> | ||||
| 	    </div> | ||||
| 	</div> | ||||
|     </div> | ||||
|   </div> | ||||
|   <div class="main" > | ||||
|     <div id="myAppsContainer"> | ||||
| 	    Loading applications. Please wait... | ||||
| 	    <div class="loading">Loading applications...</div> | ||||
|     </div> | ||||
|     <div id="popup" class="popup hidden"> | ||||
| 	<div class="popup-content"> | ||||
| @@ -746,6 +98,7 @@ | ||||
|     const settingsBtn = document.getElementById('settingsBtn'); | ||||
|  | ||||
|     const vpnBtn = document.getElementById('vpnBtn'); | ||||
|     const upgradeBtn = document.getElementById('upgradeBtn'); | ||||
|  | ||||
|     const myAppsContainer = document.getElementById('myAppsContainer'); | ||||
|     const popup = document.getElementById('popup'); | ||||
| @@ -829,16 +182,31 @@ | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function renderMonitor() { | ||||
|     	myAppsContainer.classList.remove('hidden'); | ||||
|         popup.classList.add('hidden'); | ||||
|  | ||||
|         //TODO: itt kell a buttont editálni és így kell hozzáadni hozzá mindent | ||||
|         myAppsContainer.innerHTML = ` | ||||
| 	    <div class="main-header"> | ||||
| 	      <h1>Monitor</h1> | ||||
| 	      <button id="updatesBtn"><i class="fas fa-bell"></i> Notification</button> | ||||
| 	    </div> | ||||
| 	    <label>Under development...</label><br> | ||||
|         	<img src="/img/monitor.jpg" alt="Under development..." width="1000" /> | ||||
|         `; | ||||
|     } | ||||
|  | ||||
|     function renderVPN() { | ||||
|     	myAppsContainer.classList.remove('hidden'); | ||||
|         popup.classList.add('hidden'); | ||||
|  | ||||
|         myAppsContainer.innerHTML = ` | ||||
| 	    <div class="main-header"> | ||||
| 	      <h1>VPN</h1> | ||||
| 	      <h1>VPN - enable proxy</h1> | ||||
| 	    </div> | ||||
|             <div id="vpn" class="innerDiv"> | ||||
| 	    Loading... | ||||
| 	    <div class="loading">Loading...</div> | ||||
|             </div> | ||||
|         `; | ||||
| 	get_proxy_html(); | ||||
| @@ -850,7 +218,7 @@ | ||||
| 	      <h1>Services</h1> | ||||
| 	    </div> | ||||
|             <div id="services" class="innerDiv"> | ||||
| 	    Loading... | ||||
| 	    <div class="loading">Loading...</div> | ||||
|             </div> | ||||
|         `; | ||||
| 	get_services(); | ||||
| @@ -862,7 +230,7 @@ | ||||
| 	      <h1>Updates</h1> | ||||
| 	    </div> | ||||
|             <div id="updates" class="innerDiv"> | ||||
| 	    Looking for updates... Please wait... | ||||
| 	    <div class="loading">Looking for updates...</div> | ||||
|             </div> | ||||
|         `; | ||||
| 	get_updates(); | ||||
| @@ -874,7 +242,7 @@ | ||||
| 	      <h1>System services</h1> | ||||
| 	    </div> | ||||
|             <div id="system" class="innerDiv"> | ||||
| 	    Loading... | ||||
| 	    <div class="loading">Loading...</div> | ||||
|             </div> | ||||
|         `; | ||||
| 	get_system(); | ||||
| @@ -946,7 +314,7 @@ | ||||
| 	    <div class="main-header"> | ||||
| 	      <h1>Repositories</h1> | ||||
| 	    </div> | ||||
| 		<div id="repositories" style="text-align:left">Loading...</div> | ||||
| 		<div id="repositories" style="text-align:left">Loading repositories...</div> | ||||
| 		<br> | ||||
| 		<form class="form-install" action="#" method="post" id="add_repo"> | ||||
| 		<div class="input-row"> | ||||
| @@ -989,13 +357,15 @@ | ||||
| 	installAppsBtn.addEventListener('click', () => {renderApps(true); activate(installAppsBtn);}); | ||||
| 	backupBtn.addEventListener('click', () => {renderText('Backup'); activate(backupBtn);}); | ||||
| 	diskBtn.addEventListener('click', () => {renderText('Disk Management'); activate(diskBtn)}); | ||||
| 	monitorBtn.addEventListener('click', () => {renderText('Monitor'); activate(monitorBtn)}); | ||||
| 	monitorBtn.addEventListener('click', () => {renderMonitor(); activate(monitorBtn)}); | ||||
|     	settingsBtn.addEventListener('click', () => {renderSettings(); activate(settingsBtn)}); | ||||
|  | ||||
| 	vpnBtn.addEventListener('click', () => renderVPN()); | ||||
| 	upgradeBtn.addEventListener('click', () => renderVPN()); | ||||
|  | ||||
| //	renderApps(true); | ||||
|  | ||||
| /* | ||||
| 	const vpnToggle = document.getElementById("vpnToggle"); | ||||
|   	const vpnLabel = document.querySelector(".switch-label"); | ||||
|  | ||||
| @@ -1004,9 +374,13 @@ | ||||
| 	} | ||||
|  | ||||
| 	vpnToggle.addEventListener("change", updateStatus); | ||||
| 	vpnToggle.addEventListener('click', function (e) { | ||||
| 		e.preventDefault(); | ||||
| 	}); | ||||
|  | ||||
| 	// Alapállapot beállítása | ||||
| 	updateStatus(); | ||||
| */ | ||||
|  | ||||
| }); | ||||
| </script> | ||||
| @@ -1016,7 +390,6 @@ | ||||
| <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> | ||||
| <script src="https://cdn.jsdelivr.net/npm/popper.js@1.14.6/dist/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script> | ||||
| <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.2.1/dist/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script> | ||||
| <script src="common.js?t=30"></script> | ||||
| <script src="common.js?t=43"></script> | ||||
| </body> | ||||
| </html> | ||||
|  | ||||
|   | ||||
							
								
								
									
										224
									
								
								scan.html
									
									
									
									
									
								
							
							
						
						| @@ -1,198 +1,58 @@ | ||||
| <!doctype html> | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
| <!-- Required meta tags --> | ||||
| <meta charset="utf-8"> | ||||
| <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | ||||
| <title>INSTALLER TOOL</title> | ||||
| <!-- Bootstrap CSS --> | ||||
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.2.1/dist/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous"> | ||||
| <!-- Custom styles for this template --> | ||||
| <link href="installer.css?t=1" rel="stylesheet"> | ||||
| <meta charset="UTF-8" /> | ||||
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||||
| <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> | ||||
| <meta http-equiv="Pragma" content="no-cache" /> | ||||
| <meta http-equiv="Expires" content="0" /> | ||||
| <title>Safebox - INSTALLER TOOL</title> | ||||
| <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Switzer:ital,wght@0,300;0,400;0,500;0,600;1,400&display=swap"/> | ||||
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"/> | ||||
| <link rel="stylesheet" href="style.css?t=4" /> | ||||
| </head> | ||||
| <body id="scan" class="text-center"> | ||||
| <div class="container-fluid"> | ||||
| <div class="col-md-12"> | ||||
| 	<h1>Scanning your device for any previous installed versions</h1> | ||||
| 	<div class="row d-flex justify-content-center mt-100"> | ||||
| 	       <div class="progress blue" data-value="100"> | ||||
| 		 <span class="progress-left"> | ||||
| 		   <span class="progress-bar"></span> | ||||
| 		 </span> | ||||
| 		 <span class="progress-right"> | ||||
| 		   <span class="progress-bar"></span> | ||||
| 		 </span> | ||||
| 		 <div class="progress-value"></div> | ||||
| 	       </div> | ||||
|   <div class="main"> | ||||
|     <div id="myAppsContainer"> | ||||
| 	<div class="logo" style="margin:100px 0px 20px 0px;"> | ||||
| 		<img src="/img/logo.svg" alt="Safebox"/> | ||||
| 		<span>Safebox</span> | ||||
| 	</div> | ||||
| 	<div><a href="install.html" class="stop">STOP AND START INSTALL</a></div> | ||||
| 	<br> | ||||
| 	<br> | ||||
| 	<div id="redis"></div> | ||||
| 	<div id="previous"></div> | ||||
| </div> | ||||
| </div> | ||||
| 	<div class="progress-box"> | ||||
| 		<div class="progress-title">Scanning your device</div> | ||||
| 		<div class="progress-description" id="info">Looking for any previously installed version</div> | ||||
| 		<div class="progress-container-shadow"> | ||||
| 		</div> | ||||
| 		<div class="progress-container"> | ||||
| 			<div class="progress-bar" id="progressBar"></div> | ||||
| 			<div class="progress-text" id="progressText">0%</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| <!-- | ||||
| 	<div class="controls"> | ||||
|         	<button onclick="document.location='install.html'" class="save-button">STOP AND START INSTALL</button> | ||||
| 	</div> | ||||
| --> | ||||
|     </div> | ||||
|   </div> | ||||
| <!-- Optional JavaScript --> | ||||
| <!-- jQuery first, then Popper.js, then Bootstrap JS --> | ||||
| <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> | ||||
| <script src="https://cdn.jsdelivr.net/npm/popper.js@1.14.6/dist/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script> | ||||
| <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.2.1/dist/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script> | ||||
| <script src="install.js?t=7"></script> | ||||
| <script> | ||||
| $(function() { | ||||
| const progressBar = document.getElementById('progressBar'); | ||||
| const progressText = document.getElementById('progressText'); | ||||
| let currentProgress = 0; | ||||
| let progressInterval; | ||||
| let install = 0; // scan | ||||
|  | ||||
| function redirectToInstall() { | ||||
|     window.location.href = 'install.html'; | ||||
| } | ||||
| // Initialize | ||||
| updateProgress(0); | ||||
| startProgress(30000);// 30 seconds | ||||
|  | ||||
| function redirectToManage() { | ||||
|     window.location.href = 'manage.html'; | ||||
| } | ||||
|  | ||||
| function start_system() { | ||||
|   var url  = 'scan.php?op=system'; | ||||
|   $.get(url, function(data){ | ||||
|     console.log('start_system: '+data); | ||||
|     if (data=='OK') { | ||||
|       $("#previous").html('Scanning for previous install. Please wait...'); | ||||
|       check_system(); | ||||
|     } | ||||
|     else { | ||||
|       $("#previous").html('Scanning for previous install has aborted...'); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function check_system() { | ||||
|   var url  = 'scan.php?op=check_system'; | ||||
|   $.get(url, function(data){ | ||||
|     console.log('check_system: '+data); | ||||
|     if (data=='NEW') { | ||||
|       $("#previous").html('No previous install has found...'); | ||||
|       setTimeout(redirectToInstall, 3000); | ||||
|     } | ||||
|     else if (data=='EXISTS') { | ||||
|       $("#previous").html('Previous install has found...'); | ||||
|       setTimeout(redirectToManage, 3000); | ||||
|     } | ||||
|     else if (data=='WAIT') { | ||||
|       setTimeout(check_system, 1000); | ||||
|     } | ||||
|     else { | ||||
| 	    // UNEXPECTED ERROR | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function check_redis() { | ||||
|  | ||||
|   var url  = 'scan.php?op=redis'; | ||||
|   $.get(url, function(data){ | ||||
|     console.log('check_redis: '+data); | ||||
|     if (data=='OK') { | ||||
|       $("#redis").html('Redis server - OK'); | ||||
|       start_system(); | ||||
|     } | ||||
|     else { | ||||
|       $("#redis").html('Redis server is not available...'); | ||||
|       setTimeout(check_redis, 1000); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function check_directory() { | ||||
|  | ||||
|   var url  = 'scan.php?op=directory'; | ||||
|   $.get(url, function(data){ | ||||
|     console.log('check_directory: '+data); | ||||
|     if (data=='OK') { | ||||
|       $("#redis").html('Connection is ready - OK'); | ||||
|       start_system(); | ||||
|     } | ||||
|     else { | ||||
|       $("#redis").html('Shared directory is not available...'); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function check_interface() { | ||||
|  | ||||
|   var url  = 'scan.php?op=get_interface'; | ||||
|   $.get(url, function(data){ | ||||
|     console.log('check_interface: '+data); | ||||
|     if (data=='redis') { | ||||
|   	check_redis(); | ||||
|     } | ||||
|     else if (data=='directory') { | ||||
|   	check_directory(); | ||||
|     } | ||||
|     else { | ||||
| 	$("#redis").html('Invalid interface definition...'); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
|   check_interface(); | ||||
|  | ||||
|   //setTimeout(redirectToManage, 10000); | ||||
|  | ||||
|   $(".progress").each(function() { | ||||
|  | ||||
|     var value = $(this).attr('data-value'); | ||||
|     var left = $(this).find('.progress-left .progress-bar'); | ||||
|     var right = $(this).find('.progress-right .progress-bar'); | ||||
|  | ||||
|     if (value > 0) { | ||||
| /* | ||||
| 	angle1 = getRotationDegrees(right); | ||||
| 	angle2 = getRotationDegrees(left); | ||||
| 		console.log(angle1); | ||||
| 		console.log(angle2); | ||||
| 	idx=1 | ||||
| 	while (idx < 50) { | ||||
| 		angle1 = getRotationDegrees(right); | ||||
| 		angle2 = getRotationDegrees(left); | ||||
| 		console.log(angle1); | ||||
| 		console.log(angle2); | ||||
| 		$('div.progress-value').html(angle2); | ||||
| 		idx++; | ||||
| 	} | ||||
| */ | ||||
| /* | ||||
|       if (value <= 50) { | ||||
|         right.css('transform', 'rotate(' + percentageToDegrees(value) + 'deg)') | ||||
|       } else { | ||||
|         right.css('transform', 'rotate(180deg)') | ||||
|         left.css('transform', 'rotate(' + percentageToDegrees(value - 50) + 'deg)') | ||||
|       } | ||||
| */ | ||||
|     } | ||||
|  | ||||
|   }) | ||||
|  | ||||
|   function percentageToDegrees(percentage) { | ||||
|     return percentage / 100 * 360 | ||||
|   } | ||||
|  | ||||
| 	/** | ||||
| 	 * Returns rotation in degrees when obtaining transform-styles using javascript | ||||
| 	 * http://stackoverflow.com/questions/8270612/get-element-moz-transformrotate-value-in-jquery | ||||
| 	 */ | ||||
| 	function getRotationDegrees(obj) { | ||||
| 	    var matrix = obj.css("-webkit-transform") || | ||||
| 	    obj.css("-moz-transform")    || | ||||
| 	    obj.css("-ms-transform")     || | ||||
| 	    obj.css("-o-transform")      || | ||||
| 	    obj.css("transform"); | ||||
| 	    if(matrix !== 'none') { | ||||
| 		var values = matrix.split('(')[1].split(')')[0].split(','); | ||||
| 		var a = values[0]; | ||||
| 		var b = values[1]; | ||||
| 		var angle = Math.round(Math.atan2(b, a) * (180/Math.PI)); | ||||
| 	    } else { var angle = 0; } | ||||
| 	    return angle; | ||||
| 	} | ||||
|  | ||||
| }); | ||||
| check_interface(); | ||||
| </script> | ||||
| </body> | ||||
| </html> | ||||
|   | ||||
							
								
								
									
										28
									
								
								scan.php
									
									
									
									
									
								
							
							
						
						| @@ -59,12 +59,12 @@ switch ($_GET["op"]) { | ||||
| 		} | ||||
| 		else echo "WAIT"; | ||||
| 	break; | ||||
| 	case "check_install": // called in install.php - check if install process has finished | ||||
| 		$arr = check_response($_GET["key"]); // TODO - replace key with "install", key can be "install*" | ||||
| 	case "check_install": // called in install.js - check if install process has finished | ||||
| 		$arr = check_response("install"); | ||||
| 		if (!empty($arr)) { | ||||
| 			foreach ($arr as $key=>$data) { | ||||
| 				//echo $key."-".$_GET["key"]; | ||||
| 				if ($key==$_GET["key"]) { // if install key moved to web_out | ||||
| 				if ($key=="install") { // if install key moved to web_out | ||||
| 					if ($data["INSTALL_STATUS"]>0) { | ||||
| 						remove_response("$key"); | ||||
| 						echo "INSTALLED"; | ||||
| @@ -149,7 +149,7 @@ switch ($_GET["op"]) { | ||||
| 								$subtitle = $content["subtitle"]; | ||||
| 								if (empty($subtitle) || $subtitle == "null") $subtitle = ""; | ||||
| 								$icon = $content["icon"]; | ||||
| 								if (empty($icon) || $icon == "null") $icon = "img/default_logo.png"; // default icon image | ||||
| 								if (empty($icon) || $icon == "null") $icon = "img/logo.svg"; // default icon image | ||||
| 								if (array_key_exists($service_name,$data["INSTALLED_SERVICES"])) $installed = "true"; | ||||
| 								else $installed = "false"; | ||||
| 								if (!empty($deployments)) $deployments .= ", "; | ||||
| @@ -182,6 +182,7 @@ switch ($_GET["op"]) { | ||||
| 				if ($key=="deployment") { | ||||
| 					if ($data["STATUS"]=="0") { // ask | ||||
| 						$template = json_decode(base64_decode($data["TEMPLATE"])); | ||||
| 						$template->name = strtolower($template->name); | ||||
|  | ||||
| 						echo ' | ||||
| 						<div class="app-details"> | ||||
| @@ -191,7 +192,7 @@ switch ($_GET["op"]) { | ||||
| 								<img src="'.$template->icon.'"> | ||||
| 							    </div> | ||||
| 							    <div class="text-content"> | ||||
| 							      <h1 class="title">'.$template->name.'</h1> | ||||
| 							      <h1 class="title">'.$template->title.'</h1> | ||||
| 							      <h2 class="subtitle">'.$template->subtitle.'</h2> | ||||
| 							      <p class="description">'.$template->description.'</p> | ||||
| 							    </div> | ||||
| @@ -203,7 +204,7 @@ switch ($_GET["op"]) { | ||||
| 							echo '<div id="letsencrypt">'; | ||||
| 							//var_dump($template); | ||||
|                                                         $letsencrypt = check_letsencrypt(); | ||||
| 							if (empty($letsencrypt)) echo "LETSENCRYPT file doesn't exists...<br><br>"; | ||||
| 							if (empty($letsencrypt)) echo ""; //echo "LETSENCRYPT file doesn't exists...<br><br>"; | ||||
| 							elseif ($letsencrypt=="ERROR") echo "LETSENCRYPT file: read JSON error...<br><br>"; | ||||
| 							else { | ||||
| 								$domain = ""; | ||||
| @@ -267,7 +268,7 @@ switch ($_GET["op"]) { | ||||
| 						} | ||||
| 						echo "<div class=\"mb-3\" style=\"margin-left:200px;float:\"> | ||||
| 						<button class=\"btn btn-lg btn-primary btn-block\" type=\"button\" id=\"cancel_{$template->name}_btn\">Cancel</button> | ||||
| 						</div>"; // buttons | ||||
| 						</div>"; | ||||
|                                                 echo " | ||||
|                                                 </div> | ||||
| 						</form> | ||||
| @@ -334,7 +335,7 @@ switch ($_GET["op"]) { | ||||
| 	case "check_letsencrypt": | ||||
| 		$domain = $_GET["domain"]; | ||||
| 		$letsencrypt = check_letsencrypt(); | ||||
| 		if (empty($letsencrypt)) echo "LETSENCRYPT file doesn't exists..."; | ||||
| 		if (empty($letsencrypt)) echo ""; //echo "LETSENCRYPT file doesn't exists..."; | ||||
| 		elseif ($letsencrypt=="ERROR") echo "LETSENCRYPT file: read JSON error..."; | ||||
| 		else { | ||||
| 			show_letsencrypt($letsencrypt, $domain); | ||||
| @@ -347,7 +348,7 @@ switch ($_GET["op"]) { | ||||
| 			$text="A deployment ({$_GET["additional"]}) has already started.<br>Please wait and do not start a new one..."; | ||||
| 		} | ||||
| 		else { | ||||
| 			$text="Install in progress... Please wait..."; | ||||
| 			$text="Install in progress..."; | ||||
| 			$fields = $_GET; | ||||
| 			unset($fields["op"]); | ||||
| 			unset($fields["additional"]); | ||||
| @@ -492,6 +493,15 @@ switch ($_GET["op"]) { | ||||
|                 } | ||||
|                 echo $text; | ||||
|         break; | ||||
| 	case "version": | ||||
| 		$arr = check_response("version"); | ||||
| 		if (!empty($arr)) { | ||||
| 			$data = $arr["version"]; | ||||
| 			echo "Framework version: ".$data["VERSION"]."\n"; | ||||
| 		} | ||||
| 		else echo "Framework version not found\n"; | ||||
| 		echo "Frontend version: 1.1.15"; | ||||
| 	break; | ||||
| 	case "repositories": | ||||
| 		$arr = array("STATUS" => 0); | ||||
| 		$json = json_encode($arr, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT); | ||||
|   | ||||
							
								
								
									
										777
									
								
								style.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,777 @@ | ||||
|     :root { | ||||
|       --highlight-color: #FFB806; | ||||
|     } | ||||
|  | ||||
|     body { | ||||
|       margin: 0; | ||||
|       font-family: 'Switzer', sans-serif; | ||||
|       font-weight: 500; | ||||
|       font-size: 16px; | ||||
|       background-color: #000; | ||||
|       color: white; | ||||
|       display: flex; | ||||
|       height: 100vh; | ||||
|     } | ||||
|  | ||||
|     h4 { | ||||
| 	font-size: 16px; | ||||
|     } | ||||
|  | ||||
| 	table {border: 1px solid var(--highlight-color); width:100%;} | ||||
| 	table th {padding-left: 20px; text-align: left; color: var(--highlight-color); width: 25%;} | ||||
| 	table td {padding-left: 20px; text-align:left; width: 25%;} | ||||
|  | ||||
| .progress-box { | ||||
| 	max-width: 300px; | ||||
| 	margin: auto; | ||||
| 	text-align: center; | ||||
| 	align-items: center; | ||||
| 	border: 2px solid #41464f; | ||||
| 	padding: 30px 30px 0px 30px; | ||||
| 	border-radius: 20px; | ||||
| } | ||||
| .progress-container-shadow { | ||||
|     width: 300px; | ||||
|     height: 40px; | ||||
|     background-color: var(--highlight-color); | ||||
|     border-radius: 5px; | ||||
|     overflow: hidden; | ||||
|     position: relative; | ||||
|     margin: 40px auto 0px auto; | ||||
| } | ||||
| .progress-container-shadow::after { | ||||
|   content: ""; | ||||
|   position: absolute; | ||||
|   inset: 0; | ||||
|   background-color: rgba(0, 0, 0, 0.5); | ||||
|   pointer-events: none; | ||||
| } | ||||
| .progress-container { | ||||
|     width: 300px; | ||||
|     height: 40px; | ||||
|     border-radius: 5px; | ||||
|     overflow: hidden; | ||||
|     position: relative; | ||||
|     margin: 0px; | ||||
|     top:-45px; | ||||
|     left:5px; | ||||
| } | ||||
| .progress-bar { | ||||
|     height: 100%; | ||||
|     background-color: var(--highlight-color); | ||||
|     width: 0%; | ||||
|     border-radius: 5px; | ||||
|     transition: width 0.3s ease; | ||||
|     position: relative; | ||||
| } | ||||
| .progress-text { | ||||
|     font-size: 20px; | ||||
|     position: absolute; | ||||
|     top: 50%; | ||||
|     left: 50%; | ||||
|     transform: translate(-50%, -50%); | ||||
|     font-weight: bold; | ||||
|     color: #000000; | ||||
|     z-index: 10; | ||||
| } | ||||
| .progress-title { | ||||
| 	text-align: left; | ||||
| 	font-size: 18px; | ||||
| 	font-weight: bold; | ||||
| 	margin-bottom: 10px; | ||||
| } | ||||
| .progress-description { | ||||
| 	text-align: left; | ||||
| 	font-size: 16px; | ||||
| 	margin-bottom: 10px; | ||||
| 	color: #999; | ||||
| 	min-height: 40px; | ||||
| } | ||||
| .controls { | ||||
|     text-align: center; | ||||
| } | ||||
|  | ||||
|     .sidebar { | ||||
|       width: 250px; | ||||
|       background-color: #101214; | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|       /*height: 100vh;*/ | ||||
|       justify-content: space-between; | ||||
|       margin: 20px; | ||||
|       border-radius: 20px; | ||||
|       transition: width 0.3s; | ||||
|     } | ||||
|  | ||||
| .sidebar-top { | ||||
|       padding: 20px 0px 0px 0px; | ||||
| 	overflow-y: auto; | ||||
| 	flex: 1 1 auto; | ||||
| 	display: flex; | ||||
| 	flex-direction: column; | ||||
| 	justify-content: center; /* ez teszi középre függőlegesen a menüt */ | ||||
| } | ||||
|  | ||||
|     .logo { | ||||
|       text-align: center; | ||||
|       font-size: 24px; | ||||
|       font-weight: bold; | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       justify-content: center; | ||||
|       gap: 10px; | ||||
|     } | ||||
|  | ||||
|     .logo img { | ||||
|       height: 30px; | ||||
|     } | ||||
|  | ||||
|     .loading { | ||||
| 	text-align:center; | ||||
| 	margin-top:300px; | ||||
|     } | ||||
|  | ||||
|     .confirm { | ||||
| 	border: 2px solid #41464f; | ||||
| 	border-radius: 10px; | ||||
| 	margin-top:250px; | ||||
| 	padding: 20px; | ||||
|     } | ||||
|  | ||||
|     .menu { | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|       justify-content: center; /* középre igazít függőlegesen */ | ||||
|       flex-grow: 1; | ||||
|       gap: 5px; | ||||
|       padding: 0 10px; | ||||
|     } | ||||
|  | ||||
|     .vpn-item { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       gap: 10px; | ||||
|       color: white; | ||||
|       transition: color 0.3s; | ||||
|     } | ||||
|  | ||||
|     .menu-item { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       gap: 10px; | ||||
|       padding: 10px 20px; | ||||
|       cursor: pointer; | ||||
|       color: white; | ||||
|       transition: color 0.3s; | ||||
|     } | ||||
|  | ||||
|     .menu-item:hover { | ||||
|       color: var(--highlight-color); | ||||
|     } | ||||
|  | ||||
|     .menu-item i { | ||||
|       font-size: 18px; | ||||
|     } | ||||
|  | ||||
|     .menu-item span { | ||||
|       display: inline; | ||||
|     } | ||||
|  | ||||
|     .menu-item img { | ||||
|       width: 24px; | ||||
|     } | ||||
|  | ||||
|     .menu-item.active { | ||||
|       background-color: var(--highlight-color); | ||||
|       color: black; | ||||
|       border-radius: 10px; | ||||
|       font-weight: bold; | ||||
|     } | ||||
|  | ||||
|     .menu-item.active:hover { | ||||
|       color: black; | ||||
|     } | ||||
|  | ||||
| .menu-item span.arrow { | ||||
|   margin-left: auto; | ||||
|   color: gray; | ||||
|   font-size: 20px; | ||||
|   transition: transform 0.3s ease; | ||||
| } | ||||
|  | ||||
| .menu-item.open .arrow { | ||||
|   transform: rotate(180deg); | ||||
| } | ||||
|  | ||||
| .submenu { | ||||
|   display: none; | ||||
|   margin-left: 40px; | ||||
|   flex-direction: column; | ||||
|   margin-bottom: 10px; | ||||
| } | ||||
|  | ||||
| .submenu-item { | ||||
|   padding: 5px 14px; | ||||
|   font-size: 14px; | ||||
|   color: #ffffff; | ||||
|   cursor: pointer; | ||||
|   border-radius: 10px; | ||||
| } | ||||
|  | ||||
| .submenu-item:hover { | ||||
|   color: #000000; | ||||
|   background-color: var(--highlight-color); | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Switch container */ | ||||
| .switch { | ||||
|   position: relative; | ||||
|   display: inline-block; | ||||
|   width: 60px; | ||||
|   height: 26px; | ||||
| } | ||||
|  | ||||
| /* Hide native checkbox */ | ||||
| .switch input { | ||||
|   opacity: 0; | ||||
|   width: 0; | ||||
|   height: 0; | ||||
| } | ||||
|  | ||||
| /* Slider background */ | ||||
| .slider-text { | ||||
|   position: absolute; | ||||
|   top: 0; | ||||
|   left: 0; | ||||
|   right: 0; | ||||
|   bottom: 0; | ||||
|   background-color: #ccc; | ||||
|   border-radius: 8px; | ||||
|   transition: background-color 0.3s; | ||||
|   display: flex !important; | ||||
|   align-items: center; | ||||
|   justify-content: flex-end; /* default: OFF on the right */ | ||||
|   padding: 0 8px; | ||||
|   font-size: 12px; | ||||
|   font-weight: bold; | ||||
|   color: #000; | ||||
| } | ||||
|  | ||||
| /* Circle */ | ||||
| .slider-text::before { | ||||
|   content: ""; | ||||
|   position: absolute; | ||||
|   height: 22px; | ||||
|   width: 22px; | ||||
|   left: 2px; | ||||
|   top: 2px; | ||||
|   background-color: white; | ||||
|   border-radius: 50%; | ||||
|   transition: transform 0.3s; | ||||
| } | ||||
|  | ||||
| /* Checked: move dot, change background and text alignment */ | ||||
| .switch input:checked + .slider-text { | ||||
|   background-color: var(--highlight-color); | ||||
|   justify-content: flex-start; | ||||
| } | ||||
|  | ||||
| /* Dot animation */ | ||||
| .switch input:checked + .slider-text::before { | ||||
|   transform: translateX(34px); | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Status label */ | ||||
| .status-label { | ||||
|   font-size: 12px; | ||||
|   color: #888; | ||||
|   min-width: 28px; | ||||
|   text-align: left; | ||||
| } | ||||
|  | ||||
|     .details { | ||||
|       color: white; | ||||
|       text-decoration: none; | ||||
|     } | ||||
|  | ||||
|     .yellow-row { | ||||
| 	flex-shrink: 0; | ||||
| 	max-width:250px; | ||||
| 	display: flex; | ||||
| 	align-items: flex-start; | ||||
| 	box-sizing: border-box; | ||||
|         margin: 0px; | ||||
| 	width: 100%; | ||||
| 	padding: 0px 20px 20px 20px; | ||||
|     } | ||||
|  | ||||
|     .yellow-box { | ||||
|       background-image: url('img/yellow-box.png'); | ||||
|       background-repeat: no-repeat; | ||||
|       /*background-color: var(--highlight-color); | ||||
|       border-radius: 20px 20px 0px 20px;*/ | ||||
|       color: black; | ||||
|       text-align: left; | ||||
|       margin: 0px; | ||||
|       padding: 20px 18px; | ||||
|       font-weight: bold; | ||||
|       font-size: 12px; | ||||
|       width: 156px; | ||||
|       height: 176px; | ||||
|     } | ||||
|  | ||||
|     .yellow-corner { | ||||
| 	padding:0px; | ||||
| 	border: 0px; | ||||
| 	display: flex; | ||||
| 	margin: 0px; | ||||
| 	margin-top: auto; | ||||
| 	align-items: flex-end; | ||||
|     } | ||||
|  | ||||
| .pro-text { | ||||
| 	font-size: 16px; | ||||
| 	color: black; | ||||
| } | ||||
|  | ||||
|     .grey-box { | ||||
|       background-image: url('img/grey-box.png'); | ||||
|       background-repeat: no-repeat; | ||||
|  /*     border: 2px solid #41464f;  */ | ||||
|       border-radius: 20px 20px 0px 20px; | ||||
|       color: #999; | ||||
|       text-align: left; | ||||
|       margin: 0px; | ||||
|       padding: 20px 18px; | ||||
|       font-weight: bold; | ||||
|       font-size: 12px; | ||||
|       width: 158px; | ||||
|       height: 178px; | ||||
|     } | ||||
|  | ||||
| .vpn-text { | ||||
| 	display: flex; | ||||
| 	align-items: center;  /* Ez igazítja középre függőlegesen a tartalmat */ | ||||
| 	gap: 10px;            /* Távolság kép és szöveg között (opcionális) */ | ||||
| 	font-size: 16px; | ||||
| 	color: var(--highlight-color); | ||||
| 	cursor: pointer; | ||||
| } | ||||
|  | ||||
|     .main { | ||||
|       flex: 1; | ||||
|       padding: 30px; | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|       overflow-y: auto; | ||||
|     } | ||||
|  | ||||
| 	#appsContainer { | ||||
| 	  display: flex; | ||||
| 	  flex-wrap: wrap; | ||||
| 	  gap: 16px; | ||||
| 	  padding: 0px; | ||||
| 	} | ||||
|  | ||||
| 	.app { | ||||
| 	  border: 1px solid #41464f; | ||||
| 	  border-radius: 15px; | ||||
| 	  text-align: left; | ||||
| 	  padding: 20px; | ||||
| 	  width: 130px; | ||||
| 	  min-height: 200px; | ||||
| 	  text-align: left; | ||||
| 	  display: flex; | ||||
| 	  flex-direction: column; | ||||
| 	  align-items: left; | ||||
| 	} | ||||
|  | ||||
| 	.app-img { | ||||
| 	  padding: 0px; | ||||
| 	} | ||||
|  | ||||
| 	.app img { | ||||
| 	  border-radius: 15px; | ||||
| 	  background-color: #101214; | ||||
| 	  width: 70px; | ||||
| 	  height: 70px; | ||||
| 	  object-fit: contain; | ||||
| 	  margin-bottom: 20px; | ||||
| 	  padding: 30px; | ||||
| 	} | ||||
|  | ||||
| 	.app-label { | ||||
| 	  font-size: 10px; | ||||
| 	  color: #cccccc; | ||||
| 	  margin-bottom: 10px; | ||||
| 	  align-items: left; | ||||
| 	  text-align: left; | ||||
| 	} | ||||
|  | ||||
| 	.app-name { | ||||
| 	  font-size: 16px; | ||||
| 	  font-weight: bold; | ||||
| 	  margin-bottom: 30px; | ||||
| 	} | ||||
|  | ||||
| 	.more-btn { | ||||
| 	  background-color: inherit; | ||||
| 	  color: #cccccc; | ||||
| 	  border: 1px solid #cccccc; | ||||
| 	  border-radius: 15px; | ||||
| 	  padding: 18px; | ||||
| 	  font-size: 16px; | ||||
| 	  font-weight: 400; | ||||
| 	  cursor: pointer; | ||||
| 	  width: 100%; | ||||
| 	  font-weight: bold; | ||||
| 	} | ||||
|  | ||||
| 	.more-btn:hover { | ||||
| 	  color: var(--highlight-color); | ||||
| 	  border: 1px solid var(--highlight-color); | ||||
| 	} | ||||
|  | ||||
| 	.app-details { | ||||
| 		padding: 0px 50px; | ||||
| 	} | ||||
|  | ||||
| 	.app-fields { | ||||
| 	  border: 1px solid #41464f; | ||||
| 	  border-radius: 15px; | ||||
| 	  text-align: left; | ||||
| 	  padding: 20px; | ||||
| 	  margin: 30px 0px 10px 0px; | ||||
| 	  display: flex; | ||||
| 	  flex-direction: column; | ||||
| 	  align-items: left; | ||||
| 	} | ||||
|  | ||||
| 	.app-fields .row { | ||||
|             display: flex; | ||||
| 	      justify-content: space-between; | ||||
| 	      align-items: center; | ||||
| 	      padding: 5px 20px; | ||||
| 	      border-radius: 10px; | ||||
| 	      gap: 10px; | ||||
| 	} | ||||
|  | ||||
| 	#letsencrypt { | ||||
| 		padding-top: 20px; | ||||
| 	} | ||||
|  | ||||
| .header-block { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   align-items: flex-start; | ||||
|   padding: 20px 0px; | ||||
| } | ||||
|  | ||||
| .logo-and-text { | ||||
|   display: flex; | ||||
|   align-items: flex-start; | ||||
|   gap: 16px; | ||||
| } | ||||
|  | ||||
| .applogo img { | ||||
|   border-radius: 15px; | ||||
|   background-color: #101214; | ||||
|   width: 70px; | ||||
|   height: 70px; | ||||
|   object-fit: contain; | ||||
|   padding: 40px; | ||||
| } | ||||
|  | ||||
| .text-content { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
| } | ||||
|  | ||||
| .title { | ||||
|   font-size: 28px; | ||||
|   font-weight: bold; | ||||
|   margin: 0; | ||||
| } | ||||
|  | ||||
| .subtitle { | ||||
|   font-size: 18px; | ||||
|   margin: 4px 0; | ||||
|   color: #999; | ||||
| } | ||||
|  | ||||
| .description { | ||||
|   font-size: 16px; | ||||
|   margin: 20px 0 0; | ||||
|   color: #999; | ||||
|   line-height: 1.5; | ||||
| } | ||||
|  | ||||
| .notification-btn { | ||||
|       background-color: var(--highlight-color); | ||||
|       color: black; | ||||
|       border: none; | ||||
|       padding: 10px 20px; | ||||
|       border-radius: 10px; | ||||
|       cursor: pointer; | ||||
|       font-weight: bold; | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       gap: 10px; | ||||
| } | ||||
|  | ||||
|     .main-header { | ||||
|       display: flex; | ||||
|       justify-content: space-between; | ||||
|       align-items: center; | ||||
|       margin-bottom: 30px; | ||||
|     } | ||||
|  | ||||
|     .main-header h1 { | ||||
|       margin: 0; | ||||
|       font-size: 28px; | ||||
|     } | ||||
|  | ||||
|     .main-header button { | ||||
|       background-color: var(--highlight-color); | ||||
|       color: black; | ||||
|       border: none; | ||||
|       padding: 10px 20px; | ||||
|       border-radius: 10px; | ||||
|       cursor: pointer; | ||||
|       font-weight: bold; | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       gap: 10px; | ||||
|     } | ||||
|  | ||||
|     .input-row, .input-group { | ||||
|       display: flex; | ||||
|       justify-content: space-between; | ||||
|       align-items: center; | ||||
|       border: 1px solid #41464f; | ||||
|       padding: 15px 20px; | ||||
|       margin-bottom: 15px; | ||||
|       border-radius: 10px; | ||||
|       gap: 10px; | ||||
|     } | ||||
|  | ||||
|     .input-container { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       gap: 5px; | ||||
|       flex: 1; | ||||
|       justify-content: flex-end; | ||||
|       position: relative; | ||||
|     } | ||||
|  | ||||
|     .input-row input, | ||||
|     .input-row select, | ||||
|     .row input,  | ||||
|     .row select { | ||||
|       background: transparent; | ||||
|       border: 1px solid #41464f; | ||||
|       padding: 8px 30px 8px 8px; | ||||
|       border-radius: 10px; | ||||
|       color: white; | ||||
|       transition: color 0.2s; | ||||
|       max-width: 40%; | ||||
|       box-sizing: border-box; | ||||
|     } | ||||
|  | ||||
|     .row input,  | ||||
|     .row select { | ||||
|       padding: 12px 30px 12px 12px; | ||||
|       font-size: 16px; | ||||
|     } | ||||
|  | ||||
|     .input-row input:focus, | ||||
|     .input-row select:focus, | ||||
|     .row input, | ||||
|     .row select { | ||||
|       outline: none; | ||||
|       background-color: black; | ||||
|       color: var(--highlight-color); | ||||
|     } | ||||
|  | ||||
|     .input-row select, | ||||
|     .row select { | ||||
|       appearance: none; | ||||
|       -webkit-appearance: none; | ||||
|       -moz-appearance: none; | ||||
|       background-image: url('data:image/svg+xml;utf8,<svg fill="white" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M7 10l5-5 5 5zM7 14l5 5 5-5z"/></svg>'); | ||||
|       background-repeat: no-repeat; | ||||
|       background-position: right 8px center; | ||||
|       background-size: 16px; | ||||
|       padding-right: 32px; | ||||
|     } | ||||
|  | ||||
| 	.info-container { | ||||
| 		position: relative; | ||||
| 		left: -18px; | ||||
| 		top: -22px; | ||||
| 		display: inline-block; | ||||
| 		font-family: Arial, sans-serif; | ||||
| 		margin: 0px; | ||||
| 		width: 0px; | ||||
| 	} | ||||
|  | ||||
|     .info-icon { | ||||
|       background-color: var(--highlight-color); | ||||
|       color: black; | ||||
|       border-radius: 50%; | ||||
|       padding: 1px 4px; | ||||
|       cursor: pointer; | ||||
|       font-weight: bold; | ||||
|       font-size: 10px; | ||||
|       text-align: center; | ||||
|       line-height: 1; | ||||
|       display: inline-block; | ||||
|     } | ||||
|  | ||||
|     .tooltip { | ||||
|       visibility: hidden; | ||||
|       background-color: #333; | ||||
|       border-radius: 5px; | ||||
|       padding: 4px 8px; | ||||
|       position: absolute; | ||||
|       z-index: 1; | ||||
|       bottom: 100%; /* above the icon */ | ||||
|       left: 50%; | ||||
|       transform: translateX(-78%); | ||||
|       opacity: 0; | ||||
|       transition: opacity 0.3s; | ||||
|       /*white-space: nowrap;*/ | ||||
|       text-align: right; | ||||
|       width: 300px; | ||||
|     } | ||||
|  | ||||
|     .tooltip::after { | ||||
|       content: ""; | ||||
|       position: absolute; | ||||
|       top: 100%; /* bottom of tooltip */ | ||||
|       left: 80%; | ||||
|       margin-left: -5px; | ||||
|       border-width: 5px; | ||||
|       border-style: solid; | ||||
|       border-color: #333 transparent transparent transparent; | ||||
|     } | ||||
|  | ||||
|     .info-container:hover .tooltip { | ||||
|       visibility: visible; | ||||
|       opacity: 1; | ||||
|     } | ||||
|  | ||||
|     .clear-btn { | ||||
|       position: absolute; | ||||
|       right: 8px; | ||||
|       background: transparent; | ||||
|       border: none; | ||||
|       color: white; | ||||
|       cursor: pointer; | ||||
|       font-size: 16px; | ||||
|       padding: 0; | ||||
|       width: 20px; | ||||
|       height: 20px; | ||||
|       line-height: 20px; | ||||
|       text-align: center; | ||||
|       user-select: none; | ||||
|     } | ||||
|  | ||||
|     .save-button { | ||||
|       background-color: var(--highlight-color); | ||||
|       color: black; | ||||
|       border: none; | ||||
|       padding: 15px 30px; | ||||
|       font-size: 16px; | ||||
|       border-radius: 10px; | ||||
|       font-weight: bold; | ||||
|       cursor: pointer; | ||||
|       margin-top: 30px; | ||||
|       align-self: flex-start; | ||||
|     } | ||||
|  | ||||
|     .save-button:hover { | ||||
|       background-color: #f5c300; | ||||
|     } | ||||
|  | ||||
|     .buttons .btn { | ||||
|       background-color: black; | ||||
|       border: 1px solid #999; | ||||
|       color: #999; | ||||
|       padding: 10px 15px; | ||||
|       font-size: 14px; | ||||
|       border-radius: 5px; | ||||
|       font-weight: bold; | ||||
|       cursor: pointer; | ||||
|       margin-top: 10px; | ||||
|       align-self: flex-start; | ||||
|     } | ||||
|  | ||||
|     .buttons .btn:hover { | ||||
|       border: 1px solid #f5c300; | ||||
|       color: #f5c300; | ||||
|     } | ||||
|  | ||||
|         .hidden { | ||||
|             display: none; | ||||
|         } | ||||
|  | ||||
|         .popup { | ||||
| 	    width: 100%; | ||||
|         } | ||||
|  | ||||
|         .popup-content { | ||||
|             width: 100%; | ||||
|             text-align: left; | ||||
|         } | ||||
|  | ||||
|         #popup .row { | ||||
|             display: flex; | ||||
|         } | ||||
|  | ||||
|     @media (max-width: 768px) { | ||||
|       .sidebar { | ||||
|         width: 80px; | ||||
|         padding: 20px 5px; | ||||
|       } | ||||
|  | ||||
|       .logo { | ||||
|         font-size: 0; | ||||
|       } | ||||
|  | ||||
|       .logo span { | ||||
|         display: none; | ||||
|       } | ||||
|  | ||||
|       .menu-item { | ||||
|         justify-content: center; | ||||
|         gap: 0; | ||||
|       } | ||||
|  | ||||
|       .menu-item span { | ||||
|         display: none; | ||||
|       } | ||||
|  | ||||
|       .yellow-box { | ||||
|         font-size: 0; | ||||
|         padding: 10px; | ||||
|       } | ||||
|  | ||||
|       .input-row { | ||||
|         flex-direction: column; | ||||
|         align-items: flex-start; | ||||
|       } | ||||
|  | ||||
|       .input-container { | ||||
|         width: 100%; | ||||
|         justify-content: flex-start; | ||||
|       } | ||||
|  | ||||
|       .input-row input, | ||||
|       .input-row select { | ||||
|         max-width: 100%; | ||||
|       } | ||||
|     } | ||||
|  | ||||