Files
api/services/nginx/app/routes/superuserReplicationRoute.php
T

218 lines
8.3 KiB
PHP

<?php
namespace routes;
use classes\authentication;
use classes\replication_manager;
use Throwable;
use traits\route_t;
class superuserReplicationRoute
{
use route_t;
public function run(): void
{
$this->get('/superuser/replication', function () {
global $response;
$this->requireClassicSuperuserPermission('superuser_replication_view');
$refresh = $this->toBool($this->getParameter('refresh'), false);
$response->success((new replication_manager())->summary($refresh));
}, [
'superuser_replication_view' => 'View database, Redis, and MinIO replication topology and status',
]);
$this->post('/superuser/replication/databases', function () {
global $response;
$this->requireClassicSuperuserPermission('superuser_replication_manage');
$host = (new replication_manager())->addHost('database', $this->getParametersAsArray(), $this->actorUserId());
$response->success($host, 201);
}, [
'superuser_replication_manage' => 'Add and manage database replication host credentials',
]);
$this->post('/superuser/replication/redis', function () {
global $response;
$this->requireClassicSuperuserPermission('superuser_replication_manage');
$host = (new replication_manager())->addHost('redis', $this->getParametersAsArray(), $this->actorUserId());
$response->success($host, 201);
}, [
'superuser_replication_manage' => 'Add and manage Redis replication host credentials',
]);
$this->post('/superuser/replication/minio', function () {
global $response;
$this->requireClassicSuperuserPermission('superuser_replication_manage');
$host = (new replication_manager())->addHost('minio', $this->getParametersAsArray(), $this->actorUserId());
$response->success($host, 201);
}, [
'superuser_replication_manage' => 'Add and manage MinIO replication host credentials',
]);
$this->post('/superuser/replication/compose-template', function () {
global $response;
$this->requireClassicSuperuserPermission('superuser_replication_manage');
$response->success(replication_manager::composeTemplate($this->getParametersAsArray()));
}, [
'superuser_replication_manage' => 'Generate Docker Compose templates for replication-ready database, Redis, and MinIO hosts',
]);
$this->post('/superuser/replication/test-credentials', function () {
global $response;
$this->requireClassicSuperuserPermission('superuser_replication_manage');
$parameters = $this->getParametersAsArray();
$response->success((new replication_manager())->testCredentials(
(string)($parameters['kind'] ?? ''),
$parameters
));
}, [
'superuser_replication_manage' => 'Test database, Redis, and MinIO replication host credentials before saving them',
]);
$this->post('/superuser/replication/{kind}/{id}/test', function () {
global $response;
$this->requireClassicSuperuserPermission('superuser_replication_manage');
$response->success((new replication_manager())->testHost(
(string)$this->fromRoute('kind'),
$this->routeId(),
$this->actorUserId()
));
}, [
'superuser_replication_manage' => 'Validate database, Redis, and MinIO replication host connectivity and privileges',
]);
$this->post('/superuser/replication/{kind}/{id}/provision', function () {
global $response;
$this->requireClassicSuperuserPermission('superuser_replication_manage');
try {
$result = (new replication_manager())->provisionHost(
(string)$this->fromRoute('kind'),
$this->routeId(),
$this->actorUserId(),
true
);
if (($result['ok'] ?? false) !== true) {
$response->error($result, 409);
}
$response->success($result);
} catch (Throwable $throwable) {
$response->error(['message' => $throwable->getMessage()], 409);
}
}, [
'superuser_replication_manage' => 'Provision a database, Redis, or MinIO host as a replica of the current primary',
]);
$this->post('/superuser/replication/{kind}/{id}/promote', function () {
global $response;
$this->requireClassicSuperuserPermission('superuser_replication_promote');
try {
$response->success((new replication_manager())->promoteHost(
(string)$this->fromRoute('kind'),
$this->routeId(),
$this->actorUserId()
));
} catch (Throwable $throwable) {
$response->error(['message' => $throwable->getMessage()], 409);
}
}, [
'superuser_replication_promote' => 'Promote a healthy caught-up database, Redis, or MinIO replica to primary',
]);
$this->patch('/superuser/replication/{kind}/{id}', function () {
global $response;
$this->requireClassicSuperuserPermission('superuser_replication_manage');
try {
$response->success((new replication_manager())->renameHost(
(string)$this->fromRoute('kind'),
$this->routeId(),
$this->getParametersAsArray(),
$this->actorUserId()
));
} catch (Throwable $throwable) {
$response->error(['message' => $throwable->getMessage()], 400);
}
}, [
'superuser_replication_manage' => 'Rename database, Redis, and MinIO replication hosts',
]);
$this->delete('/superuser/replication/{kind}/{id}', function () {
global $response;
$this->requireClassicSuperuserPermission('superuser_replication_remove');
try {
$response->success((new replication_manager())->removeHost(
(string)$this->fromRoute('kind'),
$this->routeId(),
$this->actorUserId()
));
} catch (Throwable $throwable) {
$response->error(['message' => $throwable->getMessage()], 409);
}
}, [
'superuser_replication_remove' => 'Remove inactive prior hosts and unhealthy database, Redis, or MinIO replicas',
]);
}
/**
* Replication controls alter infrastructure state and must only be used by
* a classic superuser session. Subuser bearer tokens can carry a delegated
* customer context via X-Customer-Number, so do not allow them to fall back
* to plain string user permission checks for these routes.
*/
private function requireClassicSuperuserPermission(string $permission): bool
{
global $response;
if ((new authentication())->get_subuser() !== false) {
$response->error('Subuser sessions cannot manage replication.', 403);
}
return $this->requirePermission($permission);
}
private function routeId(): int
{
$id = (int)$this->fromRoute('id');
$this->requireParameterIntPositive($id, 'id');
return $id;
}
private function actorUserId(): ?int
{
try {
$user = (new authentication())->get_user();
return $user !== false && isset($user->id) ? (int)$user->id : null;
} catch (Throwable) {
return null;
}
}
private function toBool(mixed $value, bool $default): bool
{
if (is_bool($value)) {
return $value;
}
if ($value === null) {
return $default;
}
$normalized = strtolower(trim((string)$value));
if (in_array($normalized, ['1', 'true', 'yes', 'on'], true)) {
return true;
}
if (in_array($normalized, ['0', 'false', 'no', 'off'], true)) {
return false;
}
return $default;
}
}