GeoNetAgent, LDAPWeb, server-audit, server-connection
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

114 lines
3.6 KiB

<?php
namespace App\Services;
use App\Contracts\DatabaseListerInterface;
use Illuminate\Support\Facades\DB;
use InvalidArgumentException;
use Throwable;
class PgsqlDatabaseService implements DatabaseListerInterface
{
private const PROTECTED = ['postgres', 'template0', 'template1'];
private const QUERY = <<<'SQL'
SELECT
d.datname AS db_name,
'—' AS created,
0 AS active_days,
CASE WHEN d.datallowconn THEN 'ONLINE' ELSE 'RESTRICTED' END AS state,
COALESCE(to_char(s.stats_reset, 'YYYY-MM-DD HH24:MI:SS'), '—') AS last_index_activity,
'—' AS last_backup
FROM pg_database d
LEFT JOIN pg_stat_database s ON s.datname = d.datname
WHERE d.datistemplate = false
SQL;
private string $connection = 'pgsql';
public function using(string $connection): self
{
$clone = clone $this;
$clone->connection = $connection;
return $clone;
}
public function listDatabases(string $sort, string $dir, string $search = ''): array
{
try {
$rows = collect(DB::connection($this->connection)->select(self::QUERY))
->map(fn ($row) => (array) $row);
if ($search !== '') {
$needle = mb_strtolower($search);
$rows = $rows->filter(fn (array $row) => str_contains(mb_strtolower($row['db_name'] ?? ''), $needle)
|| str_contains(mb_strtolower($row['state'] ?? ''), $needle));
}
$rows = $rows->sortBy(fn (array $row) => $row[$sort] ?? '', SORT_REGULAR, $dir === 'desc');
return [
'rows' => $rows->values()->all(),
'meta' => [
'total' => $rows->count(),
'server' => config('database.connections.' . $this->connection . '.host'),
'driver' => 'pgsql',
'sql_started' => null,
'uptime_days' => null,
],
'error' => null,
];
} catch (Throwable) {
return [
'rows' => [],
'meta' => [
'total' => 0,
'server' => config('database.connections.' . $this->connection . '.host'),
'driver' => 'pgsql',
'sql_started' => null,
'uptime_days' => null,
],
'error' => 'Unable to load PostgreSQL databases. Check connectivity and credentials.',
];
}
}
public function makeOffline(string $databaseName): void
{
$databaseName = $this->validateName($databaseName);
if ($this->isProtected($databaseName)) {
throw new InvalidArgumentException('System databases cannot be restricted.');
}
$quoted = '"' . str_replace('"', '""', $databaseName) . '"';
DB::connection($this->connection)->statement("ALTER DATABASE {$quoted} CONNECTION LIMIT 0");
DB::connection($this->connection)->statement(
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = ? AND pid <> pg_backend_pid()",
[$databaseName]
);
}
public function isProtected(string $databaseName): bool
{
return in_array(mb_strtolower($databaseName), self::PROTECTED, true);
}
public function supportsOffline(): bool
{
return true;
}
private function validateName(string $databaseName): string
{
$databaseName = trim($databaseName);
if (! preg_match('/^[a-zA-Z0-9_]+$/', $databaseName)) {
throw new InvalidArgumentException('Invalid database name.');
}
return $databaseName;
}
}