Import alt-mysql plugin

This commit is contained in:
Marek Miklewicz
2026-07-04 15:48:19 +02:00
commit d71fcf9ace
101 changed files with 18635 additions and 0 deletions
+472
View File
@@ -0,0 +1,472 @@
<?php
declare(strict_types=1);
final class Settings
{
/** @var array<string,string> */
private array $values;
private function __construct(array $values)
{
$this->values = $values;
}
public static function load(string $path): self
{
$values = [];
if (is_readable($path)) {
$lines = file($path, FILE_IGNORE_NEW_LINES);
if ($lines !== false) {
foreach ($lines as $line) {
$line = trim($line);
if ($line === '' || $line[0] === '#') {
continue;
}
if (!preg_match('/^([A-Za-z0-9_]+)=(.*)$/', $line, $m)) {
continue;
}
$values[$m[1]] = self::parseShValue((string)$m[2]);
}
}
}
return new self($values);
}
/**
* @return array{host:string,port:int,database:string,user:string,password:string,socket:string}
*/
public static function loadMysqlCredentials(string $path): array
{
if (!is_readable($path)) {
throw new RuntimeException('Brak pliku z danymi MySQL: ' . $path);
}
$lines = file($path, FILE_IGNORE_NEW_LINES);
if ($lines === false) {
throw new RuntimeException('Nie można odczytać pliku: ' . $path);
}
$kv = [];
foreach ($lines as $line) {
$line = trim($line);
if ($line === '' || $line[0] === '#') {
continue;
}
if (strpos($line, '=') !== false) {
[$k, $v] = array_map('trim', explode('=', $line, 2));
if ($k !== '') {
$kv[strtoupper($k)] = self::parseShValue($v);
}
continue;
}
$parts = explode(':', $line);
if (count($parts) >= 5) {
return [
'host' => trim($parts[0]) !== '' ? trim($parts[0]) : 'localhost',
'port' => (int)(trim($parts[1]) !== '' ? trim($parts[1]) : '3306'),
'database' => trim($parts[2]) !== '' ? trim($parts[2]) : 'mysql',
'user' => trim($parts[3]) !== '' ? trim($parts[3]) : 'root',
'password' => trim(implode(':', array_slice($parts, 4))),
'socket' => '',
];
}
}
$host = trim((string)($kv['HOST'] ?? $kv['MYSQL_HOST'] ?? ''));
if ($host === '') {
$host = 'localhost';
}
$portRaw = trim((string)($kv['PORT'] ?? $kv['MYSQL_PORT'] ?? '3306'));
$port = preg_match('/^[0-9]+$/', $portRaw) ? (int)$portRaw : 3306;
if ($port < 1 || $port > 65535) {
$port = 3306;
}
$database = trim((string)($kv['DATABASE'] ?? $kv['MYSQL_DATABASE'] ?? $kv['DB'] ?? 'mysql'));
if ($database === '' || $database === '*') {
$database = 'mysql';
}
$user = trim((string)($kv['USER'] ?? $kv['MYSQL_USER'] ?? 'root'));
$password = trim((string)($kv['PASSWORD'] ?? $kv['PASSWD'] ?? $kv['MYSQL_PASSWORD'] ?? ''));
$socket = trim((string)($kv['SOCKET'] ?? $kv['MYSQL_SOCKET'] ?? ''));
if ($user === '') {
throw new RuntimeException('Brak użytkownika MySQL w pliku: ' . $path);
}
return [
'host' => $host,
'port' => $port,
'database' => $database,
'user' => $user,
'password' => $password,
'socket' => $socket,
];
}
public function mysqlCredentialsFile(): string
{
$path = trim($this->getString('MYSQL_PLUGIN_CREDENTIALS_FILE', '/usr/local/directadmin/conf/alt-mysql.conf'));
return $path !== '' ? $path : '/usr/local/directadmin/conf/alt-mysql.conf';
}
public function mysqlClientBinDir(): string
{
return rtrim(trim($this->getString('MYSQL_PLUGIN_CLIENT_BIN_DIR', '')), '/');
}
public function getString(string $key, string $default = ''): string
{
return isset($this->values[$key]) ? trim($this->values[$key]) : $default;
}
public function getInt(string $key, int $default = 0): int
{
if (!isset($this->values[$key])) {
return $default;
}
$value = trim($this->values[$key]);
if ($value === '' || !preg_match('/^-?[0-9]+$/', $value)) {
return $default;
}
return (int)$value;
}
public function getBool(string $key, bool $default = false): bool
{
if (!isset($this->values[$key])) {
return $default;
}
return self::toBool($this->values[$key], $default);
}
public function defaultDatabasesLimit(): int
{
$limit = $this->getInt('MYSQL_PLUGIN_DEFAULT_DB_LIMIT', 5);
return $limit < 0 ? 0 : $limit;
}
public function alwaysEnabled(): bool
{
return $this->getBool('always_enabled', true);
}
public function maxHostsPerUser(): int
{
$limit = $this->getInt('MYSQL_PLUGIN_MAX_HOSTS_PER_USER', 30);
return $limit < 1 ? 1 : $limit;
}
public function allowRemoteHosts(): bool
{
if (array_key_exists('allow_remote_hosts', $this->values)) {
return self::toBool($this->values['allow_remote_hosts'], false);
}
if (array_key_exists('MYSQL_PLUGIN_ALLOW_REMOTE_HOSTS', $this->values)) {
return self::toBool($this->values['MYSQL_PLUGIN_ALLOW_REMOTE_HOSTS'], false);
}
return false;
}
public function modifyServerConfiguration(): bool
{
if ($this->allowRemoteHosts()) {
return true;
}
if (array_key_exists('modify_server_configuration', $this->values)) {
return self::toBool($this->values['modify_server_configuration'], false);
}
if (array_key_exists('MYSQL_PLUGIN_MODIFY_SERVER_CONFIGURATION', $this->values)) {
return self::toBool($this->values['MYSQL_PLUGIN_MODIFY_SERVER_CONFIGURATION'], false);
}
return false;
}
public function hostSyncScript(): string
{
$default = '/usr/local/directadmin/plugins/alt-mysql/scripts/setup/mysql_host_sync.sh';
$path = $this->getString('MYSQL_PLUGIN_SUDO_SYNC_HOSTS', $default);
return $path !== '' ? $path : $default;
}
public function enableBackup(): bool
{
return $this->getBool('ENABLE_BACKUP', true);
}
public function enableUploadBackup(): bool
{
return $this->getBool('ENABLE_UPLOAD_BACKUP', true);
}
public function enableAdminer(): bool
{
if (!$this->getBool('ENABLE_PHPMYADMIN', $this->getBool('ENABLE_ADMINER', true))) {
return false;
}
return $this->isPhpMyAdminInstalled();
}
public function phpMyAdminInstallDir(): string
{
$path = trim($this->getString('PHPMYADMIN_INSTALL_DIR', '/var/www/html/alt-mysql-phpmyadmin'));
return $path !== '' ? rtrim($path, '/') : '/var/www/html/alt-mysql-phpmyadmin';
}
public function adminerUrlPath(): string
{
$path = trim($this->getString('PHPMYADMIN_URL_PATH', $this->getString('ADMINER_URL_PATH', '/alt-mysql-phpmyadmin')));
if ($path === '') {
return '/alt-mysql-phpmyadmin';
}
if ($path[0] !== '/') {
$path = '/' . $path;
}
$path = rtrim($path, '/');
return $path !== '' ? $path : '/alt-mysql-phpmyadmin';
}
public function adminerPublicBaseUrl(): string
{
$url = trim($this->getString('PHPMYADMIN_PUBLIC_BASE_URL', $this->getString('ADMINER_PUBLIC_BASE_URL', '')));
if ($url === '') {
return '';
}
$url = rtrim($url, '/');
if (!preg_match('#^https?://#i', $url)) {
return '';
}
return $url;
}
public function adminerPublic(): bool
{
return $this->getBool('PHPMYADMIN_PUBLIC', $this->getBool('ADMINER_PUBLIC', false));
}
public function adminerSsoDir(): string
{
$path = trim($this->getString('PHPMYADMIN_SSO_DIR', $this->getString('ADMINER_SSO_DIR', $this->phpMyAdminInstallDir() . '/runtime')));
if ($path === '') {
$path = $this->phpMyAdminInstallDir() . '/runtime';
}
return rtrim($path, '/');
}
public function adminerTicketTtl(): int
{
$ttl = $this->getInt('PHPMYADMIN_TICKET_TTL', $this->getInt('ADMINER_TICKET_TTL', 120));
if ($ttl < 10) {
return 10;
}
if ($ttl > 300) {
return 300;
}
return $ttl;
}
public function adminerSessionTtl(): int
{
$ttl = $this->getInt('PHPMYADMIN_SESSION_TTL', $this->getInt('ADMINER_SESSION_TTL', 900));
if ($ttl < 60) {
return 60;
}
if ($ttl > 3600) {
return 3600;
}
return $ttl;
}
public function adminerRoleTtl(): int
{
$ttl = $this->getInt('PHPMYADMIN_ROLE_TTL', $this->getInt('ADMINER_ROLE_TTL', 1200));
if ($ttl < 120) {
return 120;
}
if ($ttl > 7200) {
return 7200;
}
return $ttl;
}
public function phpMyAdminIntegrationWarning(): string
{
if (!$this->getBool('ENABLE_PHPMYADMIN', $this->getBool('ENABLE_ADMINER', true))) {
return '';
}
$dir = $this->phpMyAdminInstallDir();
if (!is_dir($dir)) {
return 'Prywatna kopia phpMyAdmin dla pluginu nie jest zainstalowana. Uruchom scripts/setup/phpmyadmin_private_install.sh jako root.';
}
$config = $dir . '/config.inc.php';
$signon = $dir . '/da_signon.php';
$signonUrl = $dir . '/da_login.php';
if (!is_readable($config)) {
return 'Prywatny phpMyAdmin pluginu jest zainstalowany, ale nie można odczytać config.inc.php.';
}
if (!is_readable($signon)) {
return 'Prywatny phpMyAdmin pluginu jest zainstalowany, ale brakuje da_signon.php. Uruchom scripts/setup/phpmyadmin_repair.sh jako root.';
}
if (!is_readable($signonUrl)) {
return 'Prywatny phpMyAdmin pluginu jest zainstalowany, ale brakuje da_login.php. Uruchom scripts/setup/phpmyadmin_repair.sh jako root.';
}
$contents = file_get_contents($config);
if (!is_string($contents) || strpos($contents, 'DA_MYSQL_PMA_DEFAULT_CONF') === false) {
return 'Prywatny phpMyAdmin pluginu nie ma konfiguracji alt-mysql. Uruchom scripts/setup/phpmyadmin_repair.sh jako root.';
}
if (strpos($contents, "SignonURL'] = da_mysql_phpmyadmin_signon_url") === false) {
return 'Prywatny phpMyAdmin pluginu nie ma wymaganej konfiguracji SignonURL. Uruchom scripts/setup/phpmyadmin_repair.sh jako root.';
}
$signonUrlContents = file_get_contents($signonUrl);
if (!is_string($signonUrlContents) || strpos($signonUrlContents, 'da_mysql_login_consume_ticket') === false) {
return 'Prywatny phpMyAdmin pluginu nie obsługuje jednorazowych ticketów SSO. Uruchom scripts/setup/phpmyadmin_repair.sh jako root.';
}
return '';
}
public function allowRestoreToOtherDatabase(): bool
{
return $this->getBool('ALLOW_RESTORE_TO_OTHER_DATABASE', true);
}
public function enableTableCount(): bool
{
return $this->getBool('ENABLE_TABLE_COUNT', true);
}
public function countDatabaseSize(): bool
{
return $this->getBool('COUNT_DATABASE_SIZE', true);
}
public function compressBackup(): bool
{
return $this->getBool('COMPRESS_BACKUP', true);
}
public function hitmeBackupLocation(string $daUser = ''): string
{
$path = $this->getString('HITME_BACKUP_LOCATION', '/home/admin/mysql_backup');
if ($path === '') {
$path = '/home/admin/mysql_backup';
}
if ($daUser !== '') {
$path = str_replace(['DA_USER', 'da_user'], $daUser, $path);
}
return rtrim($path, '/');
}
public function userBaseBackupDir(string $daUser = ''): string
{
$path = $this->getString('USER_BASE_BACKUP_DIR', '/home/DA_USER/mysql_backup');
if ($path === '') {
$path = '/home/DA_USER/mysql_backup';
}
if ($daUser !== '') {
$path = str_replace(['DA_USER', 'da_user'], $daUser, $path);
}
return rtrim($path, '/');
}
public function userBackupDateFormat(): string
{
$format = $this->getString('USER_BACKUP_DATE_FORMAT', 'd.m.Y-H.i');
return $format !== '' ? $format : 'd.m.Y-H.i';
}
public function loadOrCreateSecret(string $path): string
{
if (is_readable($path)) {
$value = trim((string)file_get_contents($path));
if ($value !== '') {
return $value;
}
}
$secret = bin2hex(random_bytes(32));
$dir = dirname($path);
if (!is_dir($dir)) {
@mkdir($dir, 0700, true);
}
if (@file_put_contents($path, $secret) !== false) {
@chmod($path, 0600);
return $secret;
}
return hash('sha256', __FILE__ . '|' . php_uname('n') . '|' . $path . '|alt-mysql');
}
public static function toBool(string $value, bool $default = false): bool
{
$v = strtolower(trim($value));
if ($v === '') {
return $default;
}
if (in_array($v, ['1', 'true', 'yes', 'y', 'on', 'checked'], true)) {
return true;
}
if (in_array($v, ['0', 'false', 'no', 'n', 'off', 'unchecked'], true)) {
return false;
}
return $default;
}
private function isPhpMyAdminInstalled(): bool
{
$dir = $this->phpMyAdminInstallDir();
return is_dir($dir) && is_readable($dir . '/index.php') && is_readable($dir . '/config.inc.php');
}
private static function parseShValue(string $value): string
{
$value = trim($value);
if ($value === '') {
return '';
}
if ($value[0] === '\'' && substr($value, -1) === '\'') {
return substr($value, 1, -1);
}
if ($value[0] === '"' && substr($value, -1) === '"') {
return stripcslashes(substr($value, 1, -1));
}
$value = preg_replace('/\s+#.*$/', '', $value);
return trim((string)$value);
}
}