Merge branch 'dev'
This commit is contained in:
		
						commit
						0fc03b394d
					
				| @ -37,7 +37,7 @@ The compiled dist files will be automatically moved to `/js`. | |||||||
| 
 | 
 | ||||||
| Each API endpoint has usually one overlying category, for example all user and authorization endpoints belong to the [UserAPI](/core/Api/UserAPI.class.php). | Each API endpoint has usually one overlying category, for example all user and authorization endpoints belong to the [UserAPI](/core/Api/UserAPI.class.php). | ||||||
| These endpoints can be accessed by requesting URLs starting with `/api/user`, for example: `/api/user/login`. There are also endpoints, which don't have | These endpoints can be accessed by requesting URLs starting with `/api/user`, for example: `/api/user/login`. There are also endpoints, which don't have | ||||||
| a category, e.g. [PatchSQL](/core/Api/PatchSQL.class.php). These functions can be called directly, for example with `/api/patchSQL`. Both methods have one thing in common: | a category, e.g. [VerifyCaptcha](/core/Api/VerifyCaptcha.class.php). These functions can be called directly, for example with `/api/verifyCaptcha`. Both methods have one thing in common: | ||||||
| Each endpoint is represented by a class inheriting the [Request Class](/core/Api/Request.class.php). An example endpoint looks like this: | Each endpoint is represented by a class inheriting the [Request Class](/core/Api/Request.class.php). An example endpoint looks like this: | ||||||
| 
 | 
 | ||||||
| ```php | ```php | ||||||
| @ -112,7 +112,7 @@ If any result is expected from the api call, the `$req->getResult()` method can | |||||||
| This step is not really required, as and changes made to the database must not be presented inside the code. | This step is not really required, as and changes made to the database must not be presented inside the code. | ||||||
| On the other hand, it is recommended to keep track of any modifications for later use or to deploy the application | On the other hand, it is recommended to keep track of any modifications for later use or to deploy the application | ||||||
| to other systems. Therefore, either the [default installation script](/core/Configuration/CreateDatabase.class.php) or | to other systems. Therefore, either the [default installation script](/core/Configuration/CreateDatabase.class.php) or | ||||||
| an additional patch file, which can be executed using the API (`/api/PatchSQL`), can be created. The patch files are usually | an additional patch file, which can be executed using the [CLI](/cli.php), can be created. The patch files are usually | ||||||
| located in [/core/Configuration/Patch](/core/Configuration/Patch) and have the following structure: | located in [/core/Configuration/Patch](/core/Configuration/Patch) and have the following structure: | ||||||
| 
 | 
 | ||||||
| ```php | ```php | ||||||
|  | |||||||
							
								
								
									
										410
									
								
								cli.php
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										410
									
								
								cli.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,410 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | include_once 'core/core.php'; | ||||||
|  | include_once 'core/constants.php'; | ||||||
|  | 
 | ||||||
|  | use Configuration\Configuration; | ||||||
|  | use Configuration\DatabaseScript; | ||||||
|  | use Objects\ConnectionData; | ||||||
|  | use Objects\User; | ||||||
|  | 
 | ||||||
|  | function printLine(string $line = "") { | ||||||
|  |   echo $line . PHP_EOL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function _exit(string $line = "") { | ||||||
|  |   printLine($line); | ||||||
|  |   die(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | if (php_sapi_name() !== "cli") { | ||||||
|  |   _exit("Can only be executed via CLI"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function getDatabaseConfig(): ConnectionData { | ||||||
|  |   $configClass = "\\Configuration\\Database"; | ||||||
|  |   $file = getClassPath($configClass); | ||||||
|  |   if (!file_exists($file) || !is_readable($file)) { | ||||||
|  |     _exit("Database configuration does not exist or is not readable"); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   include_once $file; | ||||||
|  |   return new $configClass(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function getUser(): User { | ||||||
|  |   $config = new Configuration(); | ||||||
|  |   $user = new User($config); | ||||||
|  |   if (!$user->getSQL() || !$user->getSQL()->isConnected()) { | ||||||
|  |     _exit("Could not establish database connection"); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return $user; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function printHelp() { | ||||||
|  |   // TODO: help
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function handleDatabase(array $argv) { | ||||||
|  |   $action = $argv[2] ?? ""; | ||||||
|  | 
 | ||||||
|  |   if ($action === "migrate") { | ||||||
|  |     $class = $argv[3] ?? null; | ||||||
|  |     if (!$class) { | ||||||
|  |       _exit("Usage: cli.php db migrate <class name>"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     $class = str_replace('/', '\\', $class); | ||||||
|  |     $className = "\\Configuration\\$class"; | ||||||
|  |     $classPath = getClassPath($className); | ||||||
|  |     if (!file_exists($classPath) || !is_readable($classPath)) { | ||||||
|  |       _exit("Database script file does not exist or is not readable"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     include_once $classPath; | ||||||
|  |     $obj = new $className(); | ||||||
|  |     if (!($obj instanceof DatabaseScript)) { | ||||||
|  |       _exit("Not a database script"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     $user = getUser(); | ||||||
|  |     $sql = $user->getSQL(); | ||||||
|  |     $queries = $obj->createQueries($sql); | ||||||
|  |     foreach ($queries as $query) { | ||||||
|  |       if (!$query->execute($sql)) { | ||||||
|  |         _exit($sql->getLastError()); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } else if ($action === "export" || $action === "import") { | ||||||
|  | 
 | ||||||
|  |     // database config
 | ||||||
|  |     $config = getDatabaseConfig(); | ||||||
|  |     $dbType = $config->getProperty("type") ?? null; | ||||||
|  |     $user = $config->getLogin(); | ||||||
|  |     $password = $config->getPassword(); | ||||||
|  |     $database = $config->getProperty("database"); | ||||||
|  |     $host = $config->getHost(); | ||||||
|  |     $port = $config->getPort(); | ||||||
|  | 
 | ||||||
|  |     // subprocess config
 | ||||||
|  |     $env = []; | ||||||
|  |     $options = array_slice($argv, 3); | ||||||
|  |     $dataOnly = in_array("--data-only", $options) || in_array("-d", $options); | ||||||
|  |     $descriptorSpec = [STDIN, STDOUT, STDOUT]; | ||||||
|  |     $inputData = null; | ||||||
|  | 
 | ||||||
|  |     // argument config
 | ||||||
|  |     if ($action === "import") { | ||||||
|  |       $file = $argv[3] ?? null; | ||||||
|  |       if (!$file) { | ||||||
|  |         _exit("Usage: cli.php db import <path>"); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (!file_exists($file) || !is_readable($file)) { | ||||||
|  |         _exit("File not found or not readable"); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       $inputData = file_get_contents($file); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if ($dbType === "mysql") { | ||||||
|  |       $command_args = ["-u", $user, '-h', $host, '-P', $port, "--password=$password"]; | ||||||
|  |       if ($action === "export") { | ||||||
|  |         $command_bin = "mysqldump"; | ||||||
|  |         if ($dataOnly) { | ||||||
|  |           $command_args[] = "--skip-triggers"; | ||||||
|  |           $command_args[] = "--compact"; | ||||||
|  |           $command_args[] = "--no-create-info"; | ||||||
|  |         } | ||||||
|  |       } else if ($action === "import") { | ||||||
|  |         $command_bin = "mysql"; | ||||||
|  |         $descriptorSpec[0] = ["pipe", "r"]; | ||||||
|  |       } | ||||||
|  |     } else if ($dbType === "postgres") { | ||||||
|  | 
 | ||||||
|  |       $env["PGPASSWORD"] = $password; | ||||||
|  |       $command_args = ["-U", $user, '-h', $host, '-p', $port]; | ||||||
|  | 
 | ||||||
|  |       if ($action === "export") { | ||||||
|  |         $command_bin = "/usr/bin/pg_dump"; | ||||||
|  |         if ($dataOnly) { | ||||||
|  |           $command_args[] = "--data-only"; | ||||||
|  |         } | ||||||
|  |       } else if ($action === "import") { | ||||||
|  |         $command_bin = "/usr/bin/psql"; | ||||||
|  |         $descriptorSpec[0] = ["pipe", "r"]; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |     } else { | ||||||
|  |       _exit("Unsupported database type"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if ($database) { | ||||||
|  |       $command_args[] = $database; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     $command = array_merge([$command_bin], $command_args); | ||||||
|  |     $process = proc_open($command, $descriptorSpec, $pipes, null, $env); | ||||||
|  | 
 | ||||||
|  |     if (is_resource($process)) { | ||||||
|  |       if ($action === "import" && $inputData && count($pipes) > 0) { | ||||||
|  |         fwrite($pipes[0], $inputData); | ||||||
|  |         fclose($pipes[0]); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       proc_close($process); | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     _exit("Usage: cli.php db <migrate|import|export> [options...]"); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function findPullBranch(array $output): ?string { | ||||||
|  |   foreach ($output as $line) { | ||||||
|  |     $parts = preg_split('/\s+/', $line); | ||||||
|  |     if (count($parts) >= 3 && $parts[2] === '(fetch)') { | ||||||
|  |       $remoteName = $parts[0]; | ||||||
|  |       $url = $parts[1]; | ||||||
|  |       if (endsWith($url, "@github.com:rhergenreder/web-base.git") || | ||||||
|  |           endsWith($url, "@romanh.de:Projekte/web-base.git") || | ||||||
|  |           $url === 'https://github.com/rhergenreder/web-base.git' || | ||||||
|  |           $url === 'https://git.romanh.de/Projekte/web-base.git') { | ||||||
|  |         return "$remoteName/master"; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return null; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function onMaintenance(array $argv) { | ||||||
|  |   $action = $argv[2] ?? "status"; | ||||||
|  |   $maintenanceFile = "MAINTENANCE"; | ||||||
|  |   $isMaintenanceEnabled = file_exists($maintenanceFile); | ||||||
|  | 
 | ||||||
|  |   if ($action === "status") { | ||||||
|  |     _exit("Maintenance: " . ($isMaintenanceEnabled ? "on" : "off")); | ||||||
|  |   } else if ($action === "on") { | ||||||
|  |     $file = fopen($maintenanceFile, 'w') or _exit("Unable to create maintenance file"); | ||||||
|  |     fclose($file); | ||||||
|  |     _exit("Maintenance enabled"); | ||||||
|  |   } else if ($action === "off") { | ||||||
|  |     if (file_exists($maintenanceFile)) { | ||||||
|  |       if (!unlink($maintenanceFile)) { | ||||||
|  |         _exit("Unable to delete maintenance file"); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     _exit("Maintenance disabled"); | ||||||
|  |   } else if ($action === "update") { | ||||||
|  | 
 | ||||||
|  |     printLine("$ git remote -v"); | ||||||
|  |     exec("git remote -v", $gitRemote, $ret); | ||||||
|  |     if ($ret !== 0) { | ||||||
|  |       die(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     $pullBranch = findPullBranch($gitRemote); | ||||||
|  |     if ($pullBranch === null) { | ||||||
|  |       $pullBranch = 'origin/master'; | ||||||
|  |       printLine("Unable to find remote update branch. Make sure, you are still in a git repository, and one of the remote branches " . | ||||||
|  |                       "have the original fetch url"); | ||||||
|  |       printLine("Trying to continue with '$pullBranch'"); | ||||||
|  |     } else { | ||||||
|  |       printLine("Using remote update branch: $pullBranch"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     printLine("$ git fetch " . str_replace("/", " ", $pullBranch)); | ||||||
|  |     exec("git fetch " . str_replace("/", " ", $pullBranch), $gitFetch, $ret); | ||||||
|  |     if ($ret !== 0) { | ||||||
|  |       die(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     printLine("$ git log HEAD..$pullBranch --oneline"); | ||||||
|  |     exec("git log HEAD..$pullBranch --oneline", $gitLog, $ret); | ||||||
|  |     if ($ret !== 0) { | ||||||
|  |       die(); | ||||||
|  |     } else if (count($gitLog) === 0) { | ||||||
|  |       _exit("Already up to date."); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     printLine("Found updates, checking repository state"); | ||||||
|  |     printLine("$ git diff-index --quiet HEAD --"); // check for any uncommitted changes
 | ||||||
|  |     exec("git diff-index --quiet HEAD --", $gitDiff, $ret); | ||||||
|  |     if ($ret !== 0) { | ||||||
|  |       _exit("You have uncommitted changes. Please commit them before updating."); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // enable maintenance mode if it wasn't turned on before
 | ||||||
|  |     if (!$isMaintenanceEnabled) { | ||||||
|  |       printLine("Turning on maintenance mode"); | ||||||
|  |       $file = fopen($maintenanceFile, 'w') or _exit("Unable to create maintenance file"); | ||||||
|  |       fclose($file); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     printLine("Ready to update, pulling and merging"); | ||||||
|  |     printLine("$ git pull " . str_replace("/", " ", $pullBranch) . " --no-ff"); | ||||||
|  |     exec("git pull " . str_replace("/", " ", $pullBranch) . " --no-ff", $gitPull, $ret); | ||||||
|  |     if ($ret !== 0) { | ||||||
|  |       printLine(); | ||||||
|  |       printLine("Update could not be applied, check the git output."); | ||||||
|  |       printLine("Follow the instructions and afterwards turn off the maintenance mode again using:"); | ||||||
|  |       printLine("cli.php maintenance off"); | ||||||
|  |       die(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // disable maintenance mode again
 | ||||||
|  |     if (!$isMaintenanceEnabled) { | ||||||
|  |       printLine("Turning off maintenance mode"); | ||||||
|  |       if (file_exists($maintenanceFile)) { | ||||||
|  |         if (!unlink($maintenanceFile)) { | ||||||
|  |           _exit("Unable to delete maintenance file"); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     _exit("Usage: cli.php maintenance <status|on|off|update>"); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function printTable(array $head, array $body) { | ||||||
|  | 
 | ||||||
|  |   $columns = []; | ||||||
|  |   foreach ($head as $key) { | ||||||
|  |     $columns[$key] = strlen($key); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   foreach ($body as $row) { | ||||||
|  |     foreach ($head as $key) { | ||||||
|  |       $value = $row[$key] ?? ""; | ||||||
|  |       $length = strlen($value); | ||||||
|  |       $columns[$key] = max($columns[$key], $length); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // print table
 | ||||||
|  |   foreach ($head as $key) { | ||||||
|  |     echo str_pad($key, $columns[$key]) . '   '; | ||||||
|  |   } | ||||||
|  |   printLine(); | ||||||
|  | 
 | ||||||
|  |   foreach ($body as $row) { | ||||||
|  |     foreach ($head as $key) { | ||||||
|  |       echo str_pad($row[$key] ?? "", $columns[$key]) . '   '; | ||||||
|  |     } | ||||||
|  |     printLine(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TODO: add missing api functions (should be all internal only i guess)
 | ||||||
|  | function onRoutes(array $argv) { | ||||||
|  | 
 | ||||||
|  |   $user = getUser(); | ||||||
|  |   $action = $argv[2] ?? "list"; | ||||||
|  | 
 | ||||||
|  |   if ($action === "list") { | ||||||
|  |     $req = new Api\Routes\Fetch($user); | ||||||
|  |     $success = $req->execute(); | ||||||
|  |     if (!$success) { | ||||||
|  |       _exit("Error fetching routes: " . $req->getLastError()); | ||||||
|  |     } else { | ||||||
|  |       $routes = $req->getResult()["routes"]; | ||||||
|  |       $head = ["uid", "request", "action", "target", "extra", "active"]; | ||||||
|  | 
 | ||||||
|  |       // strict boolean
 | ||||||
|  |       foreach ($routes as &$route) { | ||||||
|  |         $route["active"] = $route["active"] ? "true" : "false"; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       printTable($head, $routes); | ||||||
|  |     } | ||||||
|  |   } else if ($action === "add") { | ||||||
|  |     if (count($argv) < 6) { | ||||||
|  |       _exit("Usage: cli.php routes add <request> <action> <target> [extra]"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     $params = array( | ||||||
|  |       "request" => $argv[3], | ||||||
|  |       "action" => $argv[4], | ||||||
|  |       "target" => $argv[5], | ||||||
|  |       "extra" => $argv[6] ?? "" | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     $req  = new Api\Routes\Add($user); | ||||||
|  |     $success = $req->execute($params); | ||||||
|  |     if (!$success) { | ||||||
|  |       _exit($req->getLastError()); | ||||||
|  |     } else { | ||||||
|  |       _exit("Route added successfully"); | ||||||
|  |     } | ||||||
|  |   } else if (in_array($action, ["remove","modify","enable","disable"])) { | ||||||
|  |     $uid = $argv[3] ?? null; | ||||||
|  |     if ($uid === null || ($action === "modify" && count($argv) < 7)) { | ||||||
|  |       if ($action === "modify") { | ||||||
|  |         _exit("Usage: cli.php routes $action <uid> <request> <action> <target> [extra]"); | ||||||
|  |       } else { | ||||||
|  |         _exit("Usage: cli.php routes $action <uid>"); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     $params = ["uid" => $uid]; | ||||||
|  |     if ($action === "remove") { | ||||||
|  |       $input = null; | ||||||
|  |       do { | ||||||
|  |         if ($input === "n") { | ||||||
|  |           die(); | ||||||
|  |         } | ||||||
|  |         echo "Remove route #$uid? (y|n): "; | ||||||
|  |       } while(($input = trim(fgets(STDIN))) !== "y"); | ||||||
|  | 
 | ||||||
|  |       $req = new Api\Routes\Remove($user); | ||||||
|  |     } else if ($action === "enable") { | ||||||
|  |       $req = new Api\Routes\Enable($user); | ||||||
|  |     } else if ($action === "disable") { | ||||||
|  |       $req = new Api\Routes\Disable($user); | ||||||
|  |     } else if ($action === "modify") { | ||||||
|  |       $req = new Api\Routes\Update($user); | ||||||
|  |       $params["request"] = $argv[4]; | ||||||
|  |       $params["action"] = $argv[5]; | ||||||
|  |       $params["target"] = $argv[6]; | ||||||
|  |       $params["extra"] = $argv[7] ?? ""; | ||||||
|  |     } else { | ||||||
|  |       _exit("Unsupported action"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     $success = $req->execute($params); | ||||||
|  |     if (!$success) { | ||||||
|  |       _exit($req->getLastError()); | ||||||
|  |     } else { | ||||||
|  |       _exit("Route updated successfully"); | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     _exit("Usage: cli.php routes <list|enable|disable|add|remove|modify> [options...]"); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | $argv = $_SERVER['argv']; | ||||||
|  | if (count($argv) < 2) { | ||||||
|  |   _exit("Usage: cli.php <db|routes|settings|maintenance> [options...]"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | $command = $argv[1]; | ||||||
|  | switch ($command) { | ||||||
|  |   case 'help': | ||||||
|  |     printHelp(); | ||||||
|  |     exit; | ||||||
|  |   case 'db': | ||||||
|  |     handleDatabase($argv); | ||||||
|  |     break; | ||||||
|  |   case 'routes': | ||||||
|  |     onRoutes($argv); | ||||||
|  |     break; | ||||||
|  |   case 'maintenance': | ||||||
|  |     onMaintenance($argv); | ||||||
|  |     break; | ||||||
|  |   default: | ||||||
|  |     printLine("Unknown command '$command'"); | ||||||
|  |     printLine(); | ||||||
|  |     printHelp(); | ||||||
|  |     exit; | ||||||
|  | } | ||||||
| @ -1,65 +0,0 @@ | |||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace Api; |  | ||||||
| 
 |  | ||||||
| use Api\Parameter\StringType; |  | ||||||
| use Configuration\DatabaseScript; |  | ||||||
| use Objects\User; |  | ||||||
| 
 |  | ||||||
| class PatchSQL extends Request { |  | ||||||
| 
 |  | ||||||
|   public function __construct(User $user, bool $externalCall = false) { |  | ||||||
|     parent::__construct($user, $externalCall, array( |  | ||||||
|       "className" => new StringType("className", 64) |  | ||||||
|     )); |  | ||||||
|     $this->loginRequired = true; |  | ||||||
|     $this->csrfTokenRequired = false; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public function execute($values = array()): bool { |  | ||||||
|     if (!parent::execute($values)) { |  | ||||||
|       return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     $className = $this->getParam("className"); |  | ||||||
|     $fullClassName = "\\Configuration\\Patch\\" . $className; |  | ||||||
|     $path = getClassPath($fullClassName, true); |  | ||||||
|     if (!file_exists($path)) { |  | ||||||
|       return $this->createError("File not found"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(!class_exists($fullClassName)) { |  | ||||||
|       return $this->createError("Class not found."); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     try { |  | ||||||
|       $reflection = new \ReflectionClass($fullClassName); |  | ||||||
|       if (!$reflection->isInstantiable()) { |  | ||||||
|         return $this->createError("Class is not instantiable"); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       if (!$reflection->isSubclassOf(DatabaseScript::class)) { |  | ||||||
|         return $this->createError("Not a database script."); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       $sql = $this->user->getSQL(); |  | ||||||
|       $obj = $reflection->newInstance(); |  | ||||||
|       $queries = $obj->createQueries($sql); |  | ||||||
|       if (!is_array($queries)) { |  | ||||||
|         return $this->createError("Database script returned invalid values"); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       foreach($queries as $query) { |  | ||||||
|         if (!$query->execute()) { |  | ||||||
|           return $this->createError("Query error: " . $sql->getLastError()); |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       $this->success = true; |  | ||||||
|     } catch (\ReflectionException $e) { |  | ||||||
|       return $this->createError("Error reflecting class: " . $e->getMessage()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return $this->success; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -111,13 +111,15 @@ class Request { | |||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if ($this->externalCall) { | ||||||
|  | 
 | ||||||
|  |       // check the request method
 | ||||||
|       if (!in_array($_SERVER['REQUEST_METHOD'], $this->allowedMethods)) { |       if (!in_array($_SERVER['REQUEST_METHOD'], $this->allowedMethods)) { | ||||||
|         $this->lastError = 'This method is not allowed'; |         $this->lastError = 'This method is not allowed'; | ||||||
|         header('HTTP 1.1 405 Method Not Allowed'); |         header('HTTP 1.1 405 Method Not Allowed'); | ||||||
|         return false; |         return false; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|     if ($this->externalCall) { |  | ||||||
|       $apiKeyAuthorized = false; |       $apiKeyAuthorized = false; | ||||||
| 
 | 
 | ||||||
|       // Logged in or api key authorized?
 |       // Logged in or api key authorized?
 | ||||||
|  | |||||||
| @ -1,8 +1,13 @@ | |||||||
| <?php | <?php | ||||||
| 
 | 
 | ||||||
| namespace Api { | namespace Api { | ||||||
|  | 
 | ||||||
|  |   use Driver\SQL\Condition\Compare; | ||||||
|  | 
 | ||||||
|   abstract class RoutesAPI extends Request { |   abstract class RoutesAPI extends Request { | ||||||
| 
 | 
 | ||||||
|  |     const ACTIONS = array("redirect_temporary", "redirect_permanently", "static", "dynamic"); | ||||||
|  | 
 | ||||||
|     protected function formatRegex(string $input, bool $append) : string { |     protected function formatRegex(string $input, bool $append) : string { | ||||||
|       $start = startsWith($input, "^"); |       $start = startsWith($input, "^"); | ||||||
|       $end = endsWith($input, "$"); |       $end = endsWith($input, "$"); | ||||||
| @ -16,6 +21,39 @@ namespace Api { | |||||||
| 
 | 
 | ||||||
|       return $input; |       return $input; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     protected function routeExists($uid): bool { | ||||||
|  |       $sql = $this->user->getSQL(); | ||||||
|  |       $res = $sql->select($sql->count()) | ||||||
|  |         ->from("Route") | ||||||
|  |         ->where(new Compare("uid", $uid)) | ||||||
|  |         ->execute(); | ||||||
|  | 
 | ||||||
|  |       $this->success = ($res !== false); | ||||||
|  |       $this->lastError = $sql->getLastError(); | ||||||
|  |       if ($this->success) { | ||||||
|  |         if ($res[0]["count"] === 0) { | ||||||
|  |           return $this->createError("Route not found"); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       return $this->success; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected function toggleRoute($uid, $active): bool { | ||||||
|  |       if (!$this->routeExists($uid)) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       $sql = $this->user->getSQL(); | ||||||
|  |       $this->success = $sql->update("Route") | ||||||
|  |         ->set("active", $active) | ||||||
|  |         ->where(new Compare("uid", $uid)) | ||||||
|  |         ->execute(); | ||||||
|  | 
 | ||||||
|  |       $this->lastError = $sql->getLastError(); | ||||||
|  |       return $this->success; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -25,8 +63,10 @@ namespace Api\Routes { | |||||||
|   use Api\Parameter\StringType; |   use Api\Parameter\StringType; | ||||||
|   use Api\RoutesAPI; |   use Api\RoutesAPI; | ||||||
|   use Driver\SQL\Column\Column; |   use Driver\SQL\Column\Column; | ||||||
|  |   use Driver\SQL\Condition\Compare; | ||||||
|   use Driver\SQL\Condition\CondBool; |   use Driver\SQL\Condition\CondBool; | ||||||
|   use Driver\SQL\Condition\CondRegex; |   use Driver\SQL\Condition\CondRegex; | ||||||
|  |   use Objects\User; | ||||||
| 
 | 
 | ||||||
|   class Fetch extends RoutesAPI { |   class Fetch extends RoutesAPI { | ||||||
| 
 | 
 | ||||||
| @ -162,7 +202,7 @@ namespace Api\Routes { | |||||||
|       return $this->success; |       return $this->success; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function validateRoutes() { |     private function validateRoutes(): bool { | ||||||
| 
 | 
 | ||||||
|       $this->routes = array(); |       $this->routes = array(); | ||||||
|       $keys = array( |       $keys = array( | ||||||
| @ -173,10 +213,6 @@ namespace Api\Routes { | |||||||
|         "active" => Parameter::TYPE_BOOLEAN |         "active" => Parameter::TYPE_BOOLEAN | ||||||
|       ); |       ); | ||||||
| 
 | 
 | ||||||
|       $actions = array( |  | ||||||
|         "redirect_temporary", "redirect_permanently", "static", "dynamic" |  | ||||||
|       ); |  | ||||||
| 
 |  | ||||||
|       foreach($this->getParam("routes") as $index => $route) { |       foreach($this->getParam("routes") as $index => $route) { | ||||||
|         foreach($keys as $key => $expectedType) { |         foreach($keys as $key => $expectedType) { | ||||||
|           if (!array_key_exists($key, $route)) { |           if (!array_key_exists($key, $route)) { | ||||||
| @ -193,7 +229,7 @@ namespace Api\Routes { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         $action = $route["action"]; |         $action = $route["action"]; | ||||||
|         if (!in_array($action, $actions)) { |         if (!in_array($action, self::ACTIONS)) { | ||||||
|           return $this->createError("Invalid action: $action"); |           return $this->createError("Invalid action: $action"); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -213,5 +249,149 @@ namespace Api\Routes { | |||||||
|       return true; |       return true; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   class Add extends RoutesAPI { | ||||||
|  | 
 | ||||||
|  |     public function __construct(User $user, bool $externalCall = false) { | ||||||
|  |       parent::__construct($user, $externalCall, array( | ||||||
|  |         "request" => new StringType("request", 128), | ||||||
|  |         "action" => new StringType("action"), | ||||||
|  |         "target" => new StringType("target", 128), | ||||||
|  |         "extra"  => new StringType("extra", 64, true, ""), | ||||||
|  |       )); | ||||||
|  |       $this->isPublic = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function execute($values = array()): bool { | ||||||
|  |       if (!parent::execute($values)) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       $request = $this->formatRegex($this->getParam("request"), true); | ||||||
|  |       $action = $this->getParam("action"); | ||||||
|  |       $target = $this->getParam("target"); | ||||||
|  |       $extra = $this->getParam("extra"); | ||||||
|  | 
 | ||||||
|  |       if (!in_array($action, self::ACTIONS)) { | ||||||
|  |         return $this->createError("Invalid action: $action"); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       $sql = $this->user->getSQL(); | ||||||
|  |       $this->success = $sql->insert("Route", ["request", "action", "target", "extra"]) | ||||||
|  |         ->addRow($request, $action, $target, $extra) | ||||||
|  |         ->execute(); | ||||||
|  | 
 | ||||||
|  |       $this->lastError = $sql->getLastError(); | ||||||
|  |       return $this->success; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   class Update extends RoutesAPI { | ||||||
|  |     public function __construct(User $user, bool $externalCall = false) { | ||||||
|  |       parent::__construct($user, $externalCall, array( | ||||||
|  |         "uid" => new Parameter("uid", Parameter::TYPE_INT), | ||||||
|  |         "request" => new StringType("request", 128), | ||||||
|  |         "action" => new StringType("action"), | ||||||
|  |         "target" => new StringType("target", 128), | ||||||
|  |         "extra"  => new StringType("extra", 64, true, ""), | ||||||
|  |       )); | ||||||
|  |       $this->isPublic = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function execute($values = array()): bool { | ||||||
|  |       if (!parent::execute($values)) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       $uid = $this->getParam("uid"); | ||||||
|  |       if (!$this->routeExists($uid)) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       $request = $this->formatRegex($this->getParam("request"), true); | ||||||
|  |       $action = $this->getParam("action"); | ||||||
|  |       $target = $this->getParam("target"); | ||||||
|  |       $extra = $this->getParam("extra"); | ||||||
|  |       if (!in_array($action, self::ACTIONS)) { | ||||||
|  |         return $this->createError("Invalid action: $action"); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       $sql = $this->user->getSQL(); | ||||||
|  |       $this->success = $sql->update("Route") | ||||||
|  |         ->set("request", $request) | ||||||
|  |         ->set("action", $action) | ||||||
|  |         ->set("target", $target) | ||||||
|  |         ->set("extra", $extra) | ||||||
|  |         ->where(new Compare("uid", $uid)) | ||||||
|  |         ->execute(); | ||||||
|  | 
 | ||||||
|  |       $this->lastError = $sql->getLastError(); | ||||||
|  |       return $this->success; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   class Remove extends RoutesAPI { | ||||||
|  |     public function __construct(User $user, bool $externalCall = false) { | ||||||
|  |       parent::__construct($user, $externalCall, array( | ||||||
|  |         "uid" => new Parameter("uid", Parameter::TYPE_INT) | ||||||
|  |       )); | ||||||
|  |       $this->isPublic = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function execute($values = array()): bool { | ||||||
|  |       if (!parent::execute($values)) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       $uid = $this->getParam("uid"); | ||||||
|  |       if (!$this->routeExists($uid)) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       $sql = $this->user->getSQL(); | ||||||
|  |       $this->success = $sql->delete("Route") | ||||||
|  |         ->where(new Compare("uid", $uid)) | ||||||
|  |         ->execute(); | ||||||
|  | 
 | ||||||
|  |       $this->lastError = $sql->getLastError(); | ||||||
|  |       return $this->success; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   class Enable extends RoutesAPI { | ||||||
|  |     public function __construct(User $user, bool $externalCall = false) { | ||||||
|  |       parent::__construct($user, $externalCall, array( | ||||||
|  |         "uid" => new Parameter("uid", Parameter::TYPE_INT) | ||||||
|  |       )); | ||||||
|  |       $this->isPublic = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function execute($values = array()): bool { | ||||||
|  |       if (!parent::execute($values)) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       $uid = $this->getParam("uid"); | ||||||
|  |       return $this->toggleRoute($uid, true); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   class Disable extends RoutesAPI { | ||||||
|  |     public function __construct(User $user, bool $externalCall = false) { | ||||||
|  |       parent::__construct($user, $externalCall, array( | ||||||
|  |         "uid" => new Parameter("uid", Parameter::TYPE_INT) | ||||||
|  |       )); | ||||||
|  |       $this->isPublic = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function execute($values = array()): bool { | ||||||
|  |       if (!parent::execute($values)) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       $uid = $this->getParam("uid"); | ||||||
|  |       return $this->toggleRoute($uid, false); | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -126,7 +126,8 @@ class CreateDatabase extends DatabaseScript { | |||||||
|       ->addString("target", 128) |       ->addString("target", 128) | ||||||
|       ->addString("extra", 64, true) |       ->addString("extra", 64, true) | ||||||
|       ->addBool("active", true) |       ->addBool("active", true) | ||||||
|       ->primaryKey("uid"); |       ->primaryKey("uid") | ||||||
|  |       ->unique("request"); | ||||||
| 
 | 
 | ||||||
|     $queries[] = $sql->insert("Route", array("request", "action", "target", "extra")) |     $queries[] = $sql->insert("Route", array("request", "action", "target", "extra")) | ||||||
|       ->addRow("^/admin(/.*)?$", "dynamic", "\\Documents\\Admin", NULL) |       ->addRow("^/admin(/.*)?$", "dynamic", "\\Documents\\Admin", NULL) | ||||||
| @ -192,8 +193,7 @@ class CreateDatabase extends DatabaseScript { | |||||||
|       ->addRow("User/edit", array(USER_GROUP_ADMIN), "Allows users to edit details and group memberships of any user") |       ->addRow("User/edit", array(USER_GROUP_ADMIN), "Allows users to edit details and group memberships of any user") | ||||||
|       ->addRow("User/delete", array(USER_GROUP_ADMIN), "Allows users to delete any other user") |       ->addRow("User/delete", array(USER_GROUP_ADMIN), "Allows users to delete any other user") | ||||||
|       ->addRow("Permission/fetch", array(USER_GROUP_ADMIN), "Allows users to list all API permissions") |       ->addRow("Permission/fetch", array(USER_GROUP_ADMIN), "Allows users to list all API permissions") | ||||||
|       ->addRow("Visitors/stats", array(USER_GROUP_ADMIN, USER_GROUP_SUPPORT), "Allows users to see visitor statistics") |       ->addRow("Visitors/stats", array(USER_GROUP_ADMIN, USER_GROUP_SUPPORT), "Allows users to see visitor statistics"); | ||||||
|       ->addRow("PatchSQL", array(USER_GROUP_ADMIN), "Allows users to import database patches"); |  | ||||||
| 
 | 
 | ||||||
|     self::loadPatches($queries, $sql); |     self::loadPatches($queries, $sql); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -33,7 +33,7 @@ class Settings { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public static function loadDefaults(): Settings { |   public static function loadDefaults(): Settings { | ||||||
|     $hostname = $_SERVER["SERVER_NAME"]; |     $hostname = $_SERVER["SERVER_NAME"] ?? "localhost"; | ||||||
|     $protocol = getProtocol(); |     $protocol = getProtocol(); | ||||||
|     $jwt = generateRandomString(32); |     $jwt = generateRandomString(32); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -280,9 +280,7 @@ namespace Documents\Install { | |||||||
|             $success = false; |             $success = false; | ||||||
|             $msg = "Unable to write file"; |             $msg = "Unable to write file"; | ||||||
|           } |           } | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         if ($sql) { |  | ||||||
|           $sql->close(); |           $sql->close(); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  | |||||||
| @ -53,7 +53,7 @@ class PostgreSQL extends SQL { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     $this->connection = @pg_connect(implode(" ", $connectionString)); |     $this->connection = @pg_connect(implode(" ", $connectionString), PGSQL_CONNECT_FORCE_NEW); | ||||||
|     if (!$this->connection) { |     if (!$this->connection) { | ||||||
|       $this->lastError = "Failed to connect to Database"; |       $this->lastError = "Failed to connect to Database"; | ||||||
|       $this->connection = NULL; |       $this->connection = NULL; | ||||||
|  | |||||||
| @ -1,9 +1,24 @@ | |||||||
| <?php | <?php | ||||||
| 
 | 
 | ||||||
| define("WEBBASE_VERSION", "1.2.3"); | define("WEBBASE_VERSION", "1.2.4"); | ||||||
|  | 
 | ||||||
|  | spl_autoload_extensions(".php"); | ||||||
|  | spl_autoload_register(function($class) { | ||||||
|  |   $full_path = getClassPath($class); | ||||||
|  |   if(file_exists($full_path)) { | ||||||
|  |     include_once $full_path; | ||||||
|  |   } else { | ||||||
|  |     include_once getClassPath($class, false); | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| function getProtocol(): string { | function getProtocol(): string { | ||||||
|   return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https" : "http"; |   $isSecure = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || | ||||||
|  |               (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') || | ||||||
|  |               (!empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on'); | ||||||
|  | 
 | ||||||
|  |   return $isSecure ? 'https' : 'http'; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function generateRandomString($length): string { | function generateRandomString($length): string { | ||||||
|  | |||||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 2.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								img/maintenance.png
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						
									
										
											BIN
										
									
								
								img/maintenance.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 46 KiB | 
							
								
								
									
										25
									
								
								index.php
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										25
									
								
								index.php
									
									
									
									
									
								
							| @ -1,29 +1,26 @@ | |||||||
| <?php | <?php | ||||||
| 
 | 
 | ||||||
|  | include_once 'core/core.php'; | ||||||
|  | include_once 'core/datetime.php'; | ||||||
|  | include_once 'core/constants.php'; | ||||||
|  | 
 | ||||||
|  | if (is_file("MAINTENANCE")) { | ||||||
|  |   http_response_code(503); | ||||||
|  |   $currentDir = dirname(__FILE__); | ||||||
|  |   serveStatic($currentDir, "/static/maintenance.html"); | ||||||
|  |   die(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| use Api\Request; | use Api\Request; | ||||||
| use Configuration\Configuration; | use Configuration\Configuration; | ||||||
| use Documents\Document404; | use Documents\Document404; | ||||||
| use Elements\Document; | use Elements\Document; | ||||||
| 
 | 
 | ||||||
| include_once 'core/core.php'; |  | ||||||
| include_once 'core/datetime.php'; |  | ||||||
| include_once 'core/constants.php'; |  | ||||||
| 
 |  | ||||||
| if (!is_readable(getClassPath(Configuration::class))) { | if (!is_readable(getClassPath(Configuration::class))) { | ||||||
|   header("Content-Type: application/json"); |   header("Content-Type: application/json"); | ||||||
|   die(json_encode(array( "success" => false, "msg" => "Configuration directory is not readable, check permissions before proceeding." ))); |   die(json_encode(array( "success" => false, "msg" => "Configuration directory is not readable, check permissions before proceeding." ))); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| spl_autoload_extensions(".php"); |  | ||||||
| spl_autoload_register(function($class) { |  | ||||||
|   $full_path = getClassPath($class, true); |  | ||||||
|   if(file_exists($full_path)) { |  | ||||||
|     include_once $full_path; |  | ||||||
|   } else { |  | ||||||
|     include_once getClassPath($class, false); |  | ||||||
|   } |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| $config = new Configuration(); | $config = new Configuration(); | ||||||
| $user   = new Objects\User($config); | $user   = new Objects\User($config); | ||||||
| $sql    = $user->getSQL(); | $sql    = $user->getSQL(); | ||||||
|  | |||||||
| @ -138,22 +138,26 @@ $(document).ready(function() { | |||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   // DATABASE PORT
 |   // DATABASE PORT
 | ||||||
|   let prevPort = $("#port").val(); |   let portField = $("#port"); | ||||||
|   let prevDbms = $("#type option:selected").val(); |   let typeField = $("#type"); | ||||||
|  | 
 | ||||||
|  |   let prevPort = parseInt(portField.val()); | ||||||
|  |   let prevDbms = typeField.find("option:selected").val(); | ||||||
|   function updateDefaultPort() { |   function updateDefaultPort() { | ||||||
|     let defaultPorts = { |     let defaultPorts = { | ||||||
|       "mysql": 3306, |       "mysql": 3306, | ||||||
|       "postgres": 5432 |       "postgres": 5432 | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     let curDbms = $("#type option:selected").val(); |     let curDbms = typeField.find("option:selected").val(); | ||||||
|     if(defaultPorts[prevDbms] === prevPort) { |     if(defaultPorts[prevDbms] === prevPort) { | ||||||
|       $("#port").val(defaultPorts[curDbms]); |       prevDbms = curDbms; | ||||||
|  |       portField.val(prevPort = defaultPorts[curDbms]); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   updateDefaultPort(); |   updateDefaultPort(); | ||||||
|   $("#type").change(function() { |   typeField.change(function() { | ||||||
|     updateDefaultPort(); |     updateDefaultPort(); | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|  | |||||||
							
								
								
									
										36
									
								
								static/maintenance.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										36
									
								
								static/maintenance.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <head> | ||||||
|  |   <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
|  |   <meta name="format-detection" content="telephone=yes"> | ||||||
|  |   <meta charset="utf-8"> | ||||||
|  |   <meta http-equiv="expires" content="0"> | ||||||
|  |   <meta name="robots" content="noarchive"> | ||||||
|  |   <title>Maintenance</title> | ||||||
|  |   <link rel="stylesheet" href="/css/bootstrap.min.css" type="text/css"> | ||||||
|  |   <script type="text/javascript" src="/js/bootstrap.bundle.min.js"></script> | ||||||
|  |   <link rel="stylesheet" href="/css/fontawesome.min.css" type="text/css"> | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  |   <div class="container mt-5"> | ||||||
|  |     <div class="row"> | ||||||
|  |       <div class="col-md-6"> | ||||||
|  |         <div class="error-template pt-5 pl-5"> | ||||||
|  |           <h1>Service unavailable</h1> | ||||||
|  |           <h3>Temporarily down for maintenance</h3> | ||||||
|  |           <div> | ||||||
|  |             <p> | ||||||
|  |               Sorry for the inconvenience but we're performing some maintenance at the moment. | ||||||
|  |               we'll be back online shortly! | ||||||
|  |             </p> | ||||||
|  |           </div> | ||||||
|  |           <a href="javascript:document.location.reload()" class="btn btn-info btn-lg mt-3"> | ||||||
|  |             <i class="fa fa-redo mr-2"></i>Retry | ||||||
|  |           </a> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |       <div class="col-md-6 text-center pt-5"> | ||||||
|  |         <img src="/img/maintenance.png" alt="[maintenance]"> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </body> | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user