diff --git a/modules/mining-checker/bootstrap.php b/modules/mining-checker/bootstrap.php
index 8cfed81..a6d4744 100644
--- a/modules/mining-checker/bootstrap.php
+++ b/modules/mining-checker/bootstrap.php
@@ -1,6 +1,9 @@
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',
+ ];
+});
diff --git a/modules/mining-checker/src/Infrastructure/SchemaManager.php b/modules/mining-checker/src/Infrastructure/SchemaManager.php
index 58446a6..f421522 100644
--- a/modules/mining-checker/src/Infrastructure/SchemaManager.php
+++ b/modules/mining-checker/src/Infrastructure/SchemaManager.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Modules\MiningChecker\Infrastructure;
use App\SqlDataImporter;
+use App\UploadedSqlFile;
use Modules\MiningChecker\Support\ApiException;
use PDO;
@@ -370,34 +371,16 @@ final class SchemaManager
public function importSqlFile(array $uploadedFile): array
{
- $errorCode = (int) ($uploadedFile['error'] ?? UPLOAD_ERR_NO_FILE);
- if ($errorCode !== UPLOAD_ERR_OK) {
- throw new ApiException(
- 'SQL-Datei konnte nicht hochgeladen werden.',
- 422,
- ['upload_error' => $errorCode]
- );
+ try {
+ $file = UploadedSqlFile::read($uploadedFile);
+ } catch (\RuntimeException $exception) {
+ throw new ApiException($exception->getMessage(), 422);
}
- $originalName = (string) ($uploadedFile['name'] ?? 'import.sql');
- $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);
+ $statementCount = $this->executeSqlContent((string) $file['sql'], (string) $file['file']);
return [
- 'file' => $originalName,
+ 'file' => (string) $file['file'],
'statement_count' => $statementCount,
'message' => 'SQL-Datei wurde erfolgreich eingespielt.',
];
diff --git a/partials/landingpages/modules/install.php b/partials/landingpages/modules/install.php
index 830c5d3..a7391ce 100644
--- a/partials/landingpages/modules/install.php
+++ b/partials/landingpages/modules/install.php
@@ -31,6 +31,9 @@ foreach ($modules as $m) {
diff --git a/partials/landingpages/modules/sql_import.php b/partials/landingpages/modules/sql_import.php
new file mode 100644
index 0000000..d160bc9
--- /dev/null
+++ b/partials/landingpages/modules/sql_import.php
@@ -0,0 +1,94 @@
+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();
+ }
+}
+?>
+
+
SQL Import
+
Zentraler SQL-Import fuer Module
+
+ Diese Seite ist eine gemeinsame Standard-Loesung. Module koennen sie direkt nutzen oder weiterhin einen eigenen spezialisierten Uploader bereitstellen.
+
+
+
+
+ = e($error) ?>
+
+
+
+ = e($notice) ?>
+
+
+
+
+
+ Aktuell ist kein Modul fuer den generischen SQL-Import vorbereitet.
+
+
+
+
+
+
+
+
= e($module['title']) ?>
+
= e($module['description']) ?>
+
+
+
+
+
+
diff --git a/public/index.php b/public/index.php
index 21983c7..4178e52 100755
--- a/public/index.php
+++ b/public/index.php
@@ -38,7 +38,7 @@ $publicPaths = [
'auth/me',
'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/');
if (defined('APP_AUTH_ENABLED') && APP_AUTH_ENABLED && $requiresGlobalAuth && !in_array($uriPath, $publicPaths, true)) {
$user = auth_user();
@@ -214,6 +214,8 @@ if (str_starts_with($uriPath, 'modules/install')) {
} elseif (str_starts_with($uriPath, 'modules/setup/')) {
$_GET['module'] = trim(substr($uriPath, strlen('modules/setup/')), '/');
$target = $pagesBase . '/modules/setup.php';
+} elseif ($uriPath === 'modules/sql-import') {
+ $target = $pagesBase . '/modules/sql_import.php';
} elseif ($uriPath === 'auth/login') {
$target = $pagesBase . '/auth/login.php';
} elseif ($uriPath === 'auth/callback') {
diff --git a/src/App/ModuleManager.php b/src/App/ModuleManager.php
index 32e5ba7..c3004b2 100644
--- a/src/App/ModuleManager.php
+++ b/src/App/ModuleManager.php
@@ -220,6 +220,11 @@ final class ModuleManager
return ($this->callbacks[$key])(...$args);
}
+ public function hasFunction(string $module, string $name): bool
+ {
+ return isset($this->callbacks[$module . ':' . $name]);
+ }
+
private function scanModules(): void
{
$this->modules = [];
diff --git a/src/App/ModuleSqlImportService.php b/src/App/ModuleSqlImportService.php
new file mode 100644
index 0000000..77e6c5e
--- /dev/null
+++ b/src/App/ModuleSqlImportService.php
@@ -0,0 +1,116 @@
+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 . '.');
+ }
+}
diff --git a/src/App/UploadedSqlFile.php b/src/App/UploadedSqlFile.php
new file mode 100644
index 0000000..212d0b4
--- /dev/null
+++ b/src/App/UploadedSqlFile.php
@@ -0,0 +1,35 @@
+ $originalName,
+ 'sql' => $sql,
+ ];
+ }
+}