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.
 
 
 
 
 
 

153 lines
5.1 KiB

<?php
namespace App\Services;
use App\Contracts\DatabaseListerInterface;
use Illuminate\Support\Facades\DB;
use InvalidArgumentException;
use Throwable;
class SqlServerDatabaseService implements DatabaseListerInterface
{
private const PROTECTED_DATABASES = ['master', 'model', 'msdb', 'tempdb'];
private const QUERY = <<<'SQL'
SELECT
d.name AS db_name,
CONVERT(varchar(19), d.create_date, 120) AS created,
DATEDIFF(day, d.create_date, GETDATE()) AS active_days,
d.state_desc AS state,
CONVERT(varchar(19), act.last_activity, 120) AS last_index_activity,
CONVERT(varchar(19), bak.last_backup, 120) AS last_backup
FROM sys.databases d
OUTER APPLY (
SELECT MAX(v) AS last_activity
FROM (
SELECT last_user_seek AS v FROM sys.dm_db_index_usage_stats WHERE database_id = d.database_id
UNION ALL
SELECT last_user_scan FROM sys.dm_db_index_usage_stats WHERE database_id = d.database_id
UNION ALL
SELECT last_user_lookup FROM sys.dm_db_index_usage_stats WHERE database_id = d.database_id
UNION ALL
SELECT last_user_update FROM sys.dm_db_index_usage_stats WHERE database_id = d.database_id
) x
) act
OUTER APPLY (
SELECT MAX(backup_finish_date) AS last_backup
FROM msdb.dbo.backupset
WHERE database_name = d.name AND type = 'D'
) bak
SQL;
private string $connection = 'sqlsrv';
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(function (array $row) use ($needle) {
return str_contains(mb_strtolower($row['db_name'] ?? ''), $needle)
|| str_contains(mb_strtolower($row['state'] ?? ''), $needle);
});
}
$rows = $rows->sortBy(function (array $row) use ($sort) {
if ($sort === 'active_days') {
return (int) ($row['active_days'] ?? 0);
}
return $row[$sort] ?? '';
}, SORT_REGULAR, $dir === 'desc');
$uptime = DB::connection($this->connection)->selectOne(
'SELECT CONVERT(varchar(19), sqlserver_start_time, 120) AS started, DATEDIFF(day, sqlserver_start_time, GETDATE()) AS uptime_days FROM sys.dm_os_sys_info'
);
$host = config('database.connections.' . $this->connection . '.host');
return [
'rows' => $rows->values()->all(),
'meta' => [
'total' => $rows->count(),
'server' => $host,
'driver' => 'sqlsrv',
'sql_started' => $uptime->started ?? null,
'uptime_days' => $uptime->uptime_days ?? null,
],
'error' => null,
];
} catch (Throwable) {
return [
'rows' => [],
'meta' => [
'total' => 0,
'server' => config('database.connections.' . $this->connection . '.host'),
'driver' => 'sqlsrv',
'sql_started' => null,
'uptime_days' => null,
],
'error' => 'Unable to load databases. Please check SQL Server connectivity.',
];
}
}
public function makeOffline(string $databaseName): void
{
$databaseName = $this->validateName($databaseName);
if ($this->isProtected($databaseName)) {
throw new InvalidArgumentException('System databases cannot be taken offline.');
}
$exists = DB::connection($this->connection)->selectOne(
'SELECT name, state_desc FROM sys.databases WHERE name = ?',
[$databaseName]
);
if (! $exists) {
throw new InvalidArgumentException('Database not found.');
}
if (($exists->state_desc ?? '') === 'OFFLINE') {
throw new InvalidArgumentException('Database is already offline.');
}
$quoted = str_replace(']', ']]', $databaseName);
DB::connection($this->connection)->statement(
"ALTER DATABASE [{$quoted}] SET OFFLINE WITH ROLLBACK IMMEDIATE"
);
}
public function isProtected(string $databaseName): bool
{
return in_array(mb_strtolower($databaseName), self::PROTECTED_DATABASES, 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;
}
}