sb
This commit is contained in:
@@ -6,12 +6,18 @@ namespace App;
|
||||
class Config
|
||||
{
|
||||
public string $assetVersion;
|
||||
public bool $dbAutoInit;
|
||||
public ?string $dbInitScript;
|
||||
public ?string $dbInitCmd;
|
||||
|
||||
public function __construct(
|
||||
public array $db,
|
||||
public bool $dbEnabled = true
|
||||
) {
|
||||
$this->assetVersion = defined('ASSET_VERSION') ? ASSET_VERSION : '';
|
||||
$this->dbAutoInit = defined('APP_DB_AUTO_INIT') ? (bool)APP_DB_AUTO_INIT : false;
|
||||
$this->dbInitScript = defined('APP_DB_INIT_SCRIPT') ? (string)APP_DB_INIT_SCRIPT : null;
|
||||
$this->dbInitCmd = defined('APP_DB_INIT_CMD') ? (string)APP_DB_INIT_CMD : null;
|
||||
}
|
||||
|
||||
public function primaryUrl(): string
|
||||
|
||||
@@ -43,6 +43,8 @@ final class Database
|
||||
}
|
||||
}
|
||||
|
||||
self::ensureSchema($pdo, $config);
|
||||
|
||||
return $pdo;
|
||||
} catch (\PDOException $e) {
|
||||
http_response_code(500);
|
||||
@@ -61,6 +63,126 @@ final class Database
|
||||
}
|
||||
}
|
||||
|
||||
private static function ensureSchema(\PDO $pdo, Config $config): void
|
||||
{
|
||||
$driver = (string)$pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
|
||||
if ($driver !== 'pgsql') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self::tableExists($pdo, 'hosts')) {
|
||||
if ($config->dbAutoInit) {
|
||||
self::initKeaSchema($pdo, $config);
|
||||
}
|
||||
}
|
||||
|
||||
// After init, ensure our metadata table exists (non-invasive)
|
||||
if (self::tableExists($pdo, 'hosts')) {
|
||||
self::ensureNexusTables($pdo);
|
||||
}
|
||||
}
|
||||
|
||||
private static function tableExists(\PDO $pdo, string $table): bool
|
||||
{
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT 1
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = current_schema()
|
||||
AND table_name = :table
|
||||
LIMIT 1"
|
||||
);
|
||||
$stmt->execute(['table' => $table]);
|
||||
return (bool)$stmt->fetchColumn();
|
||||
}
|
||||
|
||||
private static function initKeaSchema(\PDO $pdo, Config $config): void
|
||||
{
|
||||
if ($config->dbInitCmd) {
|
||||
self::runInitCommand($config->dbInitCmd);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($config->dbInitScript) {
|
||||
self::execSqlFile($pdo, $config->dbInitScript);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new \RuntimeException(
|
||||
'DB auto-init enabled but no APP_DB_INIT_CMD or APP_DB_INIT_SCRIPT configured.'
|
||||
);
|
||||
}
|
||||
|
||||
private static function runInitCommand(string $cmd): void
|
||||
{
|
||||
$descriptor = [
|
||||
1 => ['pipe', 'w'],
|
||||
2 => ['pipe', 'w'],
|
||||
];
|
||||
$process = proc_open($cmd, $descriptor, $pipes);
|
||||
if (!is_resource($process)) {
|
||||
throw new \RuntimeException('Failed to start DB init command.');
|
||||
}
|
||||
|
||||
$stdout = stream_get_contents($pipes[1]);
|
||||
$stderr = stream_get_contents($pipes[2]);
|
||||
foreach ($pipes as $pipe) {
|
||||
fclose($pipe);
|
||||
}
|
||||
$code = proc_close($process);
|
||||
|
||||
if ($code !== 0) {
|
||||
throw new \RuntimeException('DB init command failed: ' . trim($stderr ?: $stdout));
|
||||
}
|
||||
}
|
||||
|
||||
private static function execSqlFile(\PDO $pdo, string $path): void
|
||||
{
|
||||
if (!is_readable($path)) {
|
||||
throw new \RuntimeException('DB init script not readable: ' . $path);
|
||||
}
|
||||
|
||||
$sql = file_get_contents($path);
|
||||
if ($sql === false) {
|
||||
throw new \RuntimeException('Failed to read DB init script: ' . $path);
|
||||
}
|
||||
|
||||
// Strip psql meta-commands (lines starting with backslash)
|
||||
$sql = preg_replace('/^\\\\.+$/m', '', $sql);
|
||||
|
||||
$pdo->exec($sql);
|
||||
}
|
||||
|
||||
private static function ensureNexusTables(\PDO $pdo): void
|
||||
{
|
||||
$pdo->exec(
|
||||
"CREATE TABLE IF NOT EXISTS nexus_host_meta (
|
||||
host_id BIGINT PRIMARY KEY,
|
||||
location TEXT,
|
||||
device_type TEXT,
|
||||
owner TEXT,
|
||||
tags JSONB,
|
||||
notes TEXT,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)"
|
||||
);
|
||||
|
||||
$pdo->exec(
|
||||
"DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.table_constraints
|
||||
WHERE constraint_name = 'fk_nexus_host_meta_host'
|
||||
) THEN
|
||||
ALTER TABLE nexus_host_meta
|
||||
ADD CONSTRAINT fk_nexus_host_meta_host
|
||||
FOREIGN KEY (host_id)
|
||||
REFERENCES hosts(host_id)
|
||||
ON DELETE CASCADE;
|
||||
END IF;
|
||||
END $$;"
|
||||
);
|
||||
}
|
||||
|
||||
private static function buildMysqlDsn(array $db): string
|
||||
{
|
||||
if (empty($db['dbname'])) {
|
||||
|
||||
Reference in New Issue
Block a user