157 lines
5.2 KiB
PHP
157 lines
5.2 KiB
PHP
<?php
|
|
|
|
namespace classes;
|
|
|
|
use RuntimeException;
|
|
use Throwable;
|
|
|
|
class replication_bootstrap_config
|
|
{
|
|
public static function snapshotPath(): string
|
|
{
|
|
return self::storageDir() . DIRECTORY_SEPARATOR . 'replication-bootstrap.json';
|
|
}
|
|
|
|
public static function loadSnapshot(?string $path = null): array
|
|
{
|
|
$path = $path ?: self::snapshotPath();
|
|
if (!is_file($path) || !is_readable($path)) {
|
|
return [];
|
|
}
|
|
|
|
$decoded = json_decode((string)file_get_contents($path), true);
|
|
return is_array($decoded) ? $decoded : [];
|
|
}
|
|
|
|
public static function writeSnapshot(array $snapshot, ?string $path = null): void
|
|
{
|
|
$path = $path ?: self::snapshotPath();
|
|
$dir = dirname($path);
|
|
if (!is_dir($dir) && !mkdir($dir, 0770, true) && !is_dir($dir)) {
|
|
throw new RuntimeException('Could not create replication bootstrap snapshot directory.');
|
|
}
|
|
|
|
$snapshot = array_replace([
|
|
'version' => 1,
|
|
'generated_at' => date('c'),
|
|
], $snapshot);
|
|
|
|
$json = json_encode($snapshot, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
|
if ($json === false) {
|
|
throw new RuntimeException('Could not encode replication bootstrap snapshot.');
|
|
}
|
|
|
|
$tempPath = tempnam($dir, 'replication-bootstrap-');
|
|
if ($tempPath === false) {
|
|
throw new RuntimeException('Could not create replication bootstrap snapshot temp file.');
|
|
}
|
|
|
|
try {
|
|
if (file_put_contents($tempPath, $json . PHP_EOL, LOCK_EX) === false) {
|
|
throw new RuntimeException('Could not write replication bootstrap snapshot.');
|
|
}
|
|
|
|
if (!rename($tempPath, $path)) {
|
|
throw new RuntimeException('Could not atomically replace replication bootstrap snapshot.');
|
|
}
|
|
} finally {
|
|
if (is_file($tempPath)) {
|
|
@unlink($tempPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static function applyToGlobals(array $snapshot): void
|
|
{
|
|
try {
|
|
$active = is_array($snapshot['active'] ?? null) ? $snapshot['active'] : [];
|
|
$database = self::activeDatabaseConfigFromSnapshot($active['database'] ?? null);
|
|
$redis = self::activeRedisConfigFromSnapshot($active['redis'] ?? null);
|
|
$minio = self::activeMinioConfigFromSnapshot($active['minio'] ?? null);
|
|
|
|
if ($database !== null) {
|
|
$GLOBALS['CONFIG_DB'] = array_merge($GLOBALS['CONFIG_DB'] ?? [], $database);
|
|
}
|
|
|
|
if ($redis !== null) {
|
|
$GLOBALS['REDIS_CONFIG'] = array_merge($GLOBALS['REDIS_CONFIG'] ?? [], $redis);
|
|
}
|
|
|
|
if ($minio !== null) {
|
|
$GLOBALS['MINIO'] = array_merge($GLOBALS['MINIO'] ?? [], $minio);
|
|
}
|
|
} catch (Throwable $throwable) {
|
|
error_log('[replication-bootstrap] Falling back to environment configuration: ' . $throwable->getMessage());
|
|
}
|
|
}
|
|
|
|
public static function activeDatabaseConfigFromSnapshot(mixed $config): ?array
|
|
{
|
|
if (!is_array($config)) {
|
|
return null;
|
|
}
|
|
|
|
$host = trim((string)($config['host'] ?? ''));
|
|
$database = trim((string)($config['database'] ?? ''));
|
|
$user = trim((string)($config['user'] ?? ''));
|
|
if ($host === '' || $database === '' || $user === '') {
|
|
return null;
|
|
}
|
|
|
|
return [
|
|
'host' => $host,
|
|
'user' => $user,
|
|
'password' => replication_secret_box::decrypt($config['password_secret'] ?? ''),
|
|
'database' => $database,
|
|
'port' => (int)($config['port'] ?? 3306) ?: 3306,
|
|
'ssl_mode' => (string)($config['ssl_mode'] ?? 'DISABLED'),
|
|
];
|
|
}
|
|
|
|
public static function activeRedisConfigFromSnapshot(mixed $config): ?array
|
|
{
|
|
if (!is_array($config)) {
|
|
return null;
|
|
}
|
|
|
|
$host = trim((string)($config['host'] ?? ''));
|
|
if ($host === '') {
|
|
return null;
|
|
}
|
|
|
|
return [
|
|
'host' => $host,
|
|
'user' => (string)($config['user'] ?? ''),
|
|
'password' => replication_secret_box::decrypt($config['password_secret'] ?? ''),
|
|
'database' => (int)($config['database'] ?? 0),
|
|
'port' => (int)($config['port'] ?? 6379) ?: 6379,
|
|
];
|
|
}
|
|
|
|
public static function activeMinioConfigFromSnapshot(mixed $config): ?array
|
|
{
|
|
if (!is_array($config)) {
|
|
return null;
|
|
}
|
|
|
|
$endpoint = trim((string)($config['endpoint'] ?? ''));
|
|
$accessKey = trim((string)($config['access_key'] ?? $config['user'] ?? ''));
|
|
if ($endpoint === '' || $accessKey === '') {
|
|
return null;
|
|
}
|
|
|
|
return [
|
|
'endpoint' => $endpoint,
|
|
'access_key' => $accessKey,
|
|
'secret_key' => replication_secret_box::decrypt($config['secret_key_secret'] ?? $config['password_secret'] ?? ''),
|
|
'buckets' => is_array($config['buckets'] ?? null) ? array_values($config['buckets']) : ($config['buckets'] ?? null),
|
|
];
|
|
}
|
|
|
|
private static function storageDir(): string
|
|
{
|
|
$root = defined('WD') ? WD : dirname(__DIR__);
|
|
return $root . DIRECTORY_SEPARATOR . 'storage';
|
|
}
|
|
}
|