patch sql -> cli
This commit is contained in:
parent
186083a315
commit
ebdece7144
@ -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
|
||||||
|
157
cli.php
157
cli.php
@ -44,76 +44,117 @@ function printHelp() {
|
|||||||
function handleDatabase($argv) {
|
function handleDatabase($argv) {
|
||||||
$action = $argv[2] ?? "";
|
$action = $argv[2] ?? "";
|
||||||
|
|
||||||
switch ($action) {
|
if ($action === "migrate") {
|
||||||
case 'migrate':
|
$class = $argv[3] ?? null;
|
||||||
$class = $argv[3] ?? null;
|
if (!$class) {
|
||||||
if (!$class) {
|
die("Usage: cli.php db migrate <class name>\n");
|
||||||
die("Usage: cli.php db migrate <class name>\n");
|
}
|
||||||
|
|
||||||
|
$class = str_replace('/', '\\', $class);
|
||||||
|
$className = "\\Configuration\\$class";
|
||||||
|
$classPath = getClassPath($className);
|
||||||
|
if (!file_exists($classPath) || !is_readable($classPath)) {
|
||||||
|
die("Database script file does not exist or is not readable\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
include_once $classPath;
|
||||||
|
$obj = new $className();
|
||||||
|
if (!($obj instanceof DatabaseScript)) {
|
||||||
|
die("Not a database script\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = connectDatabase();
|
||||||
|
$queries = $obj->createQueries($db);
|
||||||
|
foreach ($queries as $query) {
|
||||||
|
if (!$query->execute($db)) {
|
||||||
|
die($db->getLastError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->close();
|
||||||
|
} 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) {
|
||||||
|
die("Usage: cli.php db import <path>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
$class = str_replace('/', '\\', $class);
|
if (!file_exists($file) || !is_readable($file)) {
|
||||||
$className = "\\Configuration\\$class";
|
die("File not found or not readable\n");
|
||||||
$classPath = getClassPath($className);
|
|
||||||
if (!file_exists($classPath) || !is_readable($classPath)) {
|
|
||||||
die("Database script file does not exist or is not readable\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
include_once $classPath;
|
$inputData = file_get_contents($file);
|
||||||
$obj = new $className();
|
}
|
||||||
if (!($obj instanceof DatabaseScript)) {
|
|
||||||
die("Not a database script\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
$db = connectDatabase();
|
if ($dbType === "mysql") {
|
||||||
$queries = $obj->createQueries($db);
|
$command_args = ["-u", $user, '-h', $host, '-P', $port, "--password=$password"];
|
||||||
foreach ($queries as $query) {
|
if ($action === "export") {
|
||||||
if (!$query->execute($db)) {
|
$command_bin = "mysqldump";
|
||||||
die($db->getLastError());
|
if ($dataOnly) {
|
||||||
|
$command_args[] = "--skip-triggers";
|
||||||
|
$command_args[] = "--compact";
|
||||||
|
$command_args[] = "--no-create-info";
|
||||||
}
|
}
|
||||||
}
|
} else if ($action === "import") {
|
||||||
|
$command_bin = "mysql";
|
||||||
$db->close();
|
$descriptorSpec[0] = ["pipe", "r"];
|
||||||
break;
|
|
||||||
case 'export':
|
|
||||||
$config = getDatabaseConfig();
|
|
||||||
$dbType = $config->getProperty("type") ?? null;
|
|
||||||
$user = $config->getLogin();
|
|
||||||
$password = $config->getPassword();
|
|
||||||
$database = $config->getProperty("database");
|
|
||||||
$host = $config->getHost();
|
|
||||||
$port = $config->getPort();
|
|
||||||
|
|
||||||
$env = [];
|
|
||||||
$output = $argv[3] ?? null;
|
|
||||||
$descriptorSpec = [STDIN, STDOUT, STDOUT];
|
|
||||||
|
|
||||||
if ($dbType === "mysql") {
|
|
||||||
|
|
||||||
$command = ["mysqldump", "-u", $user, '-h', $host, '-P', $port, "--password=$password"];
|
|
||||||
if ($database) {
|
|
||||||
$command[] = $database;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if ($dbType === "postgres") {
|
|
||||||
$command = ["pg_dump", "-U", $user, '-h', $host, '-p', $port];
|
|
||||||
if ($database) {
|
|
||||||
$command[] = $database;
|
|
||||||
}
|
|
||||||
|
|
||||||
$env["PGPASSWORD"] = $password;
|
|
||||||
} else {
|
} else {
|
||||||
die("Unsupported database type\n");
|
die("Unsupported action\n");
|
||||||
|
}
|
||||||
|
} 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 {
|
||||||
|
die("Unsupported action\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($output) {
|
} else {
|
||||||
$descriptorSpec[1] = ["file", $output, "w"];
|
die("Unsupported database type\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$process = proc_open($command, $descriptorSpec, $pipes, null, $env);
|
|
||||||
proc_close($process);
|
proc_close($process);
|
||||||
break;
|
}
|
||||||
default:
|
|
||||||
die("Usage: cli.php db <import|export|migrate>\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -193,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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user