importer general
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Modules\MiningChecker\Infrastructure\ConnectionFactory;
|
||||||
|
use Modules\MiningChecker\Infrastructure\ModuleConfig;
|
||||||
|
|
||||||
spl_autoload_register(static function (string $class): void {
|
spl_autoload_register(static function (string $class): void {
|
||||||
$prefix = 'Modules\\MiningChecker\\';
|
$prefix = 'Modules\\MiningChecker\\';
|
||||||
if (!str_starts_with($class, $prefix)) {
|
if (!str_starts_with($class, $prefix)) {
|
||||||
@@ -14,3 +17,14 @@ spl_autoload_register(static function (string $class): void {
|
|||||||
require_once $file;
|
require_once $file;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$mm = isset($modules) && $modules instanceof App\ModuleManager ? $modules : modules();
|
||||||
|
$mm->registerFunction('mining-checker', 'sql_import_target', static function (): array {
|
||||||
|
$moduleBasePath = __DIR__;
|
||||||
|
$config = ModuleConfig::load($moduleBasePath);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'pdo' => ConnectionFactory::make($config),
|
||||||
|
'label' => 'Mining-Checker Projekt-Datenbank',
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
namespace Modules\MiningChecker\Infrastructure;
|
namespace Modules\MiningChecker\Infrastructure;
|
||||||
|
|
||||||
use App\SqlDataImporter;
|
use App\SqlDataImporter;
|
||||||
|
use App\UploadedSqlFile;
|
||||||
use Modules\MiningChecker\Support\ApiException;
|
use Modules\MiningChecker\Support\ApiException;
|
||||||
use PDO;
|
use PDO;
|
||||||
|
|
||||||
@@ -370,34 +371,16 @@ final class SchemaManager
|
|||||||
|
|
||||||
public function importSqlFile(array $uploadedFile): array
|
public function importSqlFile(array $uploadedFile): array
|
||||||
{
|
{
|
||||||
$errorCode = (int) ($uploadedFile['error'] ?? UPLOAD_ERR_NO_FILE);
|
try {
|
||||||
if ($errorCode !== UPLOAD_ERR_OK) {
|
$file = UploadedSqlFile::read($uploadedFile);
|
||||||
throw new ApiException(
|
} catch (\RuntimeException $exception) {
|
||||||
'SQL-Datei konnte nicht hochgeladen werden.',
|
throw new ApiException($exception->getMessage(), 422);
|
||||||
422,
|
|
||||||
['upload_error' => $errorCode]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$originalName = (string) ($uploadedFile['name'] ?? 'import.sql');
|
$statementCount = $this->executeSqlContent((string) $file['sql'], (string) $file['file']);
|
||||||
$tmpPath = (string) ($uploadedFile['tmp_name'] ?? '');
|
|
||||||
if ($tmpPath === '' || !is_uploaded_file($tmpPath)) {
|
|
||||||
throw new ApiException('Ungueltige Upload-Datei fuer SQL-Import.', 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!preg_match('/\.sql$/i', $originalName)) {
|
|
||||||
throw new ApiException('Bitte eine SQL-Datei mit Endung .sql hochladen.', 422, ['file' => $originalName]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$sql = @file_get_contents($tmpPath);
|
|
||||||
if (!is_string($sql) || trim($sql) === '') {
|
|
||||||
throw new ApiException('Die hochgeladene SQL-Datei ist leer oder konnte nicht gelesen werden.', 422, ['file' => $originalName]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$statementCount = $this->executeSqlContent($sql, $originalName);
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'file' => $originalName,
|
'file' => (string) $file['file'],
|
||||||
'statement_count' => $statementCount,
|
'statement_count' => $statementCount,
|
||||||
'message' => 'SQL-Datei wurde erfolgreich eingespielt.',
|
'message' => 'SQL-Datei wurde erfolgreich eingespielt.',
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ foreach ($modules as $m) {
|
|||||||
<div class="pill">Module</div>
|
<div class="pill">Module</div>
|
||||||
<h1 style="margin-top:.75rem;">Module installieren/aktivieren</h1>
|
<h1 style="margin-top:.75rem;">Module installieren/aktivieren</h1>
|
||||||
<p class="muted">Erkannte Module basieren auf Ordnern in <code>modules/</code>.</p>
|
<p class="muted">Erkannte Module basieren auf Ordnern in <code>modules/</code>.</p>
|
||||||
|
<p style="margin-top:.75rem;">
|
||||||
|
<a class="nav-link" href="/modules/sql-import">Zentralen SQL-Import oeffnen</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
<?php if ($error): ?>
|
<?php if ($error): ?>
|
||||||
<div class="bg-red-900 border-l-4 border-red-500 text-red-100 p-4 mb-6" role="alert">
|
<div class="bg-red-900 border-l-4 border-red-500 text-red-100 p-4 mb-6" role="alert">
|
||||||
|
|||||||
94
partials/landingpages/modules/sql_import.php
Normal file
94
partials/landingpages/modules/sql_import.php
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
require_admin();
|
||||||
|
|
||||||
|
$service = new \App\ModuleSqlImportService(modules());
|
||||||
|
$availableModules = $service->importableModules();
|
||||||
|
$selectedModule = (string) ($_POST['module'] ?? ($_GET['module'] ?? ''));
|
||||||
|
$error = null;
|
||||||
|
$notice = null;
|
||||||
|
$result = null;
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
try {
|
||||||
|
if (!isset($_FILES['sql_file']) || !is_array($_FILES['sql_file'])) {
|
||||||
|
throw new RuntimeException('Bitte eine SQL-Datei auswaehlen.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $service->importUploadedFile($selectedModule, $_FILES['sql_file']);
|
||||||
|
$notice = sprintf(
|
||||||
|
'%s %d Statements aus %s wurden nach %s importiert.',
|
||||||
|
(string) ($result['message'] ?? 'Import erfolgreich.'),
|
||||||
|
(int) ($result['statement_count'] ?? 0),
|
||||||
|
(string) ($result['file'] ?? 'der Datei'),
|
||||||
|
(string) ($result['target_label'] ?? 'der Ziel-Datenbank')
|
||||||
|
);
|
||||||
|
} catch (Throwable $exception) {
|
||||||
|
$error = $exception->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div class="card">
|
||||||
|
<div class="pill">SQL Import</div>
|
||||||
|
<h1 style="margin-top:.75rem;">Zentraler SQL-Import fuer Module</h1>
|
||||||
|
<p class="muted">
|
||||||
|
Diese Seite ist eine gemeinsame Standard-Loesung. Module koennen sie direkt nutzen oder weiterhin einen eigenen spezialisierten Uploader bereitstellen.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="bg-red-900 border-l-4 border-red-500 text-red-100 p-4 mb-6" role="alert">
|
||||||
|
<?= e($error) ?>
|
||||||
|
</div>
|
||||||
|
<?php elseif ($notice): ?>
|
||||||
|
<div class="card" style="margin-top:1rem; border-color:var(--accent-2);">
|
||||||
|
<?= e($notice) ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($availableModules === []): ?>
|
||||||
|
<div class="card" style="margin-top:1rem; background:var(--panel-2);">
|
||||||
|
Aktuell ist kein Modul fuer den generischen SQL-Import vorbereitet.
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<form method="post" enctype="multipart/form-data" class="card" style="margin-top:1rem; background:var(--panel-2);">
|
||||||
|
<div style="display:grid; gap:1rem;">
|
||||||
|
<label style="display:grid; gap:.35rem;">
|
||||||
|
<span>Modul</span>
|
||||||
|
<select name="module" required>
|
||||||
|
<option value="">Bitte waehlen</option>
|
||||||
|
<?php foreach ($availableModules as $module): ?>
|
||||||
|
<option value="<?= e($module['name']) ?>" <?= $selectedModule === $module['name'] ? 'selected' : '' ?>>
|
||||||
|
<?= e($module['title']) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label style="display:grid; gap:.35rem;">
|
||||||
|
<span>SQL-Datei</span>
|
||||||
|
<input type="file" name="sql_file" accept=".sql,text/sql,application/sql" required>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="muted" style="font-size:.95rem;">
|
||||||
|
Der generische Import nutzt die Standard-Datenbankverbindung des Moduls oder einen optionalen Modul-Callback fuer Spezialfaelle.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display:flex; gap:10px; flex-wrap:wrap;">
|
||||||
|
<button class="cta-button" type="submit">SQL importieren</button>
|
||||||
|
<a class="nav-link" href="/modules/install">Zur Modulverwaltung</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div style="margin-top:1.25rem;" class="grid">
|
||||||
|
<?php foreach ($availableModules as $module): ?>
|
||||||
|
<div class="card" style="background:var(--panel-2);">
|
||||||
|
<strong><?= e($module['title']) ?></strong>
|
||||||
|
<div class="muted" style="font-size:.85rem;"><?= e($module['description']) ?></div>
|
||||||
|
<div style="margin-top:.75rem;">
|
||||||
|
<a class="nav-link" href="/modules/sql-import?module=<?= e($module['name']) ?>">Fuer dieses Modul importieren</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
@@ -38,7 +38,7 @@ $publicPaths = [
|
|||||||
'auth/me',
|
'auth/me',
|
||||||
'module/pi_control/terminal_info',
|
'module/pi_control/terminal_info',
|
||||||
];
|
];
|
||||||
$requiresGlobalAuth = in_array($uriPath, ['settings', 'users', 'modules', 'modules/install', 'debug', 'exports/database.sql'], true)
|
$requiresGlobalAuth = in_array($uriPath, ['settings', 'users', 'modules', 'modules/install', 'modules/sql-import', 'debug', 'exports/database.sql'], true)
|
||||||
|| str_starts_with($uriPath, 'modules/setup/');
|
|| str_starts_with($uriPath, 'modules/setup/');
|
||||||
if (defined('APP_AUTH_ENABLED') && APP_AUTH_ENABLED && $requiresGlobalAuth && !in_array($uriPath, $publicPaths, true)) {
|
if (defined('APP_AUTH_ENABLED') && APP_AUTH_ENABLED && $requiresGlobalAuth && !in_array($uriPath, $publicPaths, true)) {
|
||||||
$user = auth_user();
|
$user = auth_user();
|
||||||
@@ -214,6 +214,8 @@ if (str_starts_with($uriPath, 'modules/install')) {
|
|||||||
} elseif (str_starts_with($uriPath, 'modules/setup/')) {
|
} elseif (str_starts_with($uriPath, 'modules/setup/')) {
|
||||||
$_GET['module'] = trim(substr($uriPath, strlen('modules/setup/')), '/');
|
$_GET['module'] = trim(substr($uriPath, strlen('modules/setup/')), '/');
|
||||||
$target = $pagesBase . '/modules/setup.php';
|
$target = $pagesBase . '/modules/setup.php';
|
||||||
|
} elseif ($uriPath === 'modules/sql-import') {
|
||||||
|
$target = $pagesBase . '/modules/sql_import.php';
|
||||||
} elseif ($uriPath === 'auth/login') {
|
} elseif ($uriPath === 'auth/login') {
|
||||||
$target = $pagesBase . '/auth/login.php';
|
$target = $pagesBase . '/auth/login.php';
|
||||||
} elseif ($uriPath === 'auth/callback') {
|
} elseif ($uriPath === 'auth/callback') {
|
||||||
|
|||||||
@@ -220,6 +220,11 @@ final class ModuleManager
|
|||||||
return ($this->callbacks[$key])(...$args);
|
return ($this->callbacks[$key])(...$args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function hasFunction(string $module, string $name): bool
|
||||||
|
{
|
||||||
|
return isset($this->callbacks[$module . ':' . $name]);
|
||||||
|
}
|
||||||
|
|
||||||
private function scanModules(): void
|
private function scanModules(): void
|
||||||
{
|
{
|
||||||
$this->modules = [];
|
$this->modules = [];
|
||||||
|
|||||||
116
src/App/ModuleSqlImportService.php
Normal file
116
src/App/ModuleSqlImportService.php
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
final class ModuleSqlImportService
|
||||||
|
{
|
||||||
|
public function __construct(private ModuleManager $modules)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function importableModules(): array
|
||||||
|
{
|
||||||
|
$items = [];
|
||||||
|
foreach ($this->modules->all() as $name => $module) {
|
||||||
|
if ($this->supportsGenericImport($name, $module)) {
|
||||||
|
$items[] = [
|
||||||
|
'name' => $name,
|
||||||
|
'title' => (string) ($module['title'] ?? $name),
|
||||||
|
'description' => (string) ($module['description'] ?? ''),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
usort($items, static fn (array $left, array $right): int => strcmp($left['title'], $right['title']));
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function importUploadedFile(string $moduleName, array $uploadedFile): array
|
||||||
|
{
|
||||||
|
$module = $this->modules->get($moduleName);
|
||||||
|
if ($module === null) {
|
||||||
|
throw new \RuntimeException('Unbekanntes Modul.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = UploadedSqlFile::read($uploadedFile);
|
||||||
|
$target = $this->resolveImportTarget($moduleName, $module);
|
||||||
|
$statementCount = (new SqlDataImporter($target['pdo']))->importString((string) $file['sql']);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'module' => $moduleName,
|
||||||
|
'module_title' => (string) ($module['title'] ?? $moduleName),
|
||||||
|
'target_label' => $target['label'],
|
||||||
|
'file' => (string) $file['file'],
|
||||||
|
'statement_count' => $statementCount,
|
||||||
|
'message' => 'SQL-Datei wurde erfolgreich importiert.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function supportsGenericImport(string $moduleName, array $module): bool
|
||||||
|
{
|
||||||
|
if ($this->modules->hasFunction($moduleName, 'sql_import_target')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->modules->hasFunction($moduleName, 'pdo')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$settings = $this->modules->settings($moduleName);
|
||||||
|
if (is_array($settings['db'] ?? null) && !empty($settings['db'])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_array($module['db_defaults'] ?? null) && !empty($module['db_defaults']);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveImportTarget(string $moduleName, array $module): array
|
||||||
|
{
|
||||||
|
if ($this->modules->hasFunction($moduleName, 'sql_import_target')) {
|
||||||
|
return $this->normalizeTarget(
|
||||||
|
$moduleName,
|
||||||
|
$this->modules->call($moduleName, 'sql_import_target')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->modules->hasFunction($moduleName, 'pdo')) {
|
||||||
|
return $this->normalizeTarget(
|
||||||
|
$moduleName,
|
||||||
|
$this->modules->call($moduleName, 'pdo')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fallback = is_array($module['db_defaults'] ?? null) ? $module['db_defaults'] : [];
|
||||||
|
$pdo = $this->modules->modulePdo($moduleName, $fallback);
|
||||||
|
if (!$pdo instanceof PDO) {
|
||||||
|
throw new \RuntimeException('Fuer dieses Modul ist keine SQL-Datenbank verfuegbar.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'pdo' => $pdo,
|
||||||
|
'label' => (string) ($module['title'] ?? $moduleName) . ' Datenbank',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function normalizeTarget(string $moduleName, mixed $target): array
|
||||||
|
{
|
||||||
|
if ($target instanceof PDO) {
|
||||||
|
return [
|
||||||
|
'pdo' => $target,
|
||||||
|
'label' => $moduleName . ' Datenbank',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($target) && ($target['pdo'] ?? null) instanceof PDO) {
|
||||||
|
return [
|
||||||
|
'pdo' => $target['pdo'],
|
||||||
|
'label' => (string) ($target['label'] ?? ($moduleName . ' Datenbank')),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \RuntimeException('Ungueltiges SQL-Import-Ziel fuer Modul ' . $moduleName . '.');
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/App/UploadedSqlFile.php
Normal file
35
src/App/UploadedSqlFile.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
final class UploadedSqlFile
|
||||||
|
{
|
||||||
|
public static function read(array $uploadedFile): array
|
||||||
|
{
|
||||||
|
$errorCode = (int) ($uploadedFile['error'] ?? UPLOAD_ERR_NO_FILE);
|
||||||
|
if ($errorCode !== UPLOAD_ERR_OK) {
|
||||||
|
throw new \RuntimeException('SQL-Datei konnte nicht hochgeladen werden. Upload-Fehler: ' . $errorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
$originalName = (string) ($uploadedFile['name'] ?? 'import.sql');
|
||||||
|
$tmpPath = (string) ($uploadedFile['tmp_name'] ?? '');
|
||||||
|
if ($tmpPath === '' || !is_uploaded_file($tmpPath)) {
|
||||||
|
throw new \RuntimeException('Ungueltige Upload-Datei fuer SQL-Import.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('/\.sql$/i', $originalName)) {
|
||||||
|
throw new \RuntimeException('Bitte eine SQL-Datei mit Endung .sql hochladen.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = @file_get_contents($tmpPath);
|
||||||
|
if (!is_string($sql) || trim($sql) === '') {
|
||||||
|
throw new \RuntimeException('Die hochgeladene SQL-Datei ist leer oder konnte nicht gelesen werden.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'file' => $originalName,
|
||||||
|
'sql' => $sql,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user