Add import/export and update system
Implement CSV/JSON import and export for domains, notification groups and tags (with masking for sensitive channel data), including size/format validation, in-memory CSV building, and logging. Add tag transfer and bulk transfer actions (admin-only). Introduce a new update system: Add UpdateController and UpdateService, migration 025_add_update_system_v1.1.3.sql, and installer changes to include the new migration and version handling; provide endpoints to check, apply, rollback and configure updates. Update helpers and UI bits: add getUpdateBadgeInfo in LayoutHelper, update notification icons/redirects, and add getMaxUploadSize in ViewHelper. Misc: add NotificationGroup::findByName, tweak .gitignore backups path, and update related views and routes.
This commit is contained in:
@@ -19,6 +19,7 @@ class ErrorHandler
|
||||
private Logger $logger;
|
||||
private ?ErrorLog $errorLogModel = null;
|
||||
private bool $isDevelopment;
|
||||
private bool $handling = false; // Recursion guard
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@@ -29,9 +30,8 @@ class ErrorHandler
|
||||
// Initialize ErrorLog model if database is available
|
||||
try {
|
||||
$this->errorLogModel = new ErrorLog();
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
// Database not available, will only use file logging
|
||||
// Don't use error_log as it might fail too
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,22 @@ class ErrorHandler
|
||||
*/
|
||||
public function handleException(\Throwable $exception): void
|
||||
{
|
||||
// Prevent infinite recursion if error handling itself triggers an error
|
||||
if ($this->handling) {
|
||||
// Fallback: just log to file and stop
|
||||
try {
|
||||
$this->logger->critical('Recursive error detected', [
|
||||
'message' => $exception->getMessage(),
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine()
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
// Last resort
|
||||
}
|
||||
return;
|
||||
}
|
||||
$this->handling = true;
|
||||
|
||||
$errorData = $this->captureError($exception);
|
||||
|
||||
// Log to file
|
||||
@@ -62,8 +78,8 @@ class ErrorHandler
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ignore certain non-critical errors during error handling itself
|
||||
if (error_reporting() === 0) {
|
||||
// Prevent recursive handling (e.g. if logToDatabase triggers a warning)
|
||||
if ($this->handling) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -114,7 +130,7 @@ class ErrorHandler
|
||||
'error_message' => $exception->getMessage(),
|
||||
'error_file' => $exception->getFile(),
|
||||
'error_line' => $exception->getLine(),
|
||||
'stack_trace' => json_encode($exception->getTrace()),
|
||||
'stack_trace' => json_encode($exception->getTrace(), JSON_PARTIAL_OUTPUT_ON_ERROR) ?: '[]',
|
||||
'request_method' => $_SERVER['REQUEST_METHOD'] ?? 'CLI',
|
||||
'request_uri' => $_SERVER['REQUEST_URI'] ?? 'N/A',
|
||||
'request_data' => json_encode($requestData),
|
||||
@@ -228,9 +244,19 @@ class ErrorHandler
|
||||
|
||||
try {
|
||||
return $this->errorLogModel->logError($errorData);
|
||||
} catch (\Exception $e) {
|
||||
// Database logging failed, continue with file logging only
|
||||
error_log("Failed to log error to database: " . $e->getMessage());
|
||||
} catch (\Throwable $e) {
|
||||
// Database logging failed — log to file so it's visible in the app's /logs folder
|
||||
try {
|
||||
$this->logger->error('Failed to log error to database', [
|
||||
'db_error' => $e->getMessage(),
|
||||
'db_error_file' => $e->getFile(),
|
||||
'db_error_line' => $e->getLine(),
|
||||
'original_error_id' => $errorData['error_id'] ?? 'unknown'
|
||||
]);
|
||||
} catch (\Throwable $e2) {
|
||||
// Last resort — use PHP's native error_log
|
||||
error_log("ErrorHandler: DB log failed: " . $e->getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -569,30 +569,36 @@ class NotificationService
|
||||
|
||||
/**
|
||||
* Create system upgrade notification for admins (in-app)
|
||||
* @param bool $composerManualRequired If true, appends a note to run composer install manually (e.g. when exec is disabled on cPanel)
|
||||
*/
|
||||
public function notifySystemUpgrade(int $userId, string $fromVersion, string $toVersion, int $migrationsCount): void
|
||||
public function notifySystemUpgrade(int $userId, string $fromVersion, string $toVersion, int $migrationsCount, bool $composerManualRequired = false): void
|
||||
{
|
||||
$message = "Domain Monitor upgraded from v{$fromVersion} to v{$toVersion} ({$migrationsCount} migration" . ($migrationsCount > 1 ? 's' : '') . " applied)";
|
||||
if ($composerManualRequired) {
|
||||
$message .= ". Composer could not be run here (e.g. exec disabled). If dependencies changed, run \"composer install --no-dev\" manually via SSH or Terminal.";
|
||||
}
|
||||
$notificationModel = new \App\Models\Notification();
|
||||
$notificationModel->createNotification(
|
||||
$userId,
|
||||
'system_upgrade',
|
||||
'System Upgraded Successfully',
|
||||
"Domain Monitor upgraded from v{$fromVersion} to v{$toVersion} ({$migrationsCount} migration" . ($migrationsCount > 1 ? 's' : '') . " applied)",
|
||||
$message,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all admins about system upgrade (in-app)
|
||||
* @param bool $composerManualRequired If true, in-app message will include a note to run composer install manually
|
||||
*/
|
||||
public function notifyAdminsUpgrade(string $fromVersion, string $toVersion, int $migrationsCount): void
|
||||
public function notifyAdminsUpgrade(string $fromVersion, string $toVersion, int $migrationsCount, bool $composerManualRequired = false): void
|
||||
{
|
||||
try {
|
||||
$userModel = new \App\Models\User();
|
||||
$admins = $userModel->getAllAdmins();
|
||||
|
||||
foreach ($admins as $admin) {
|
||||
$this->notifySystemUpgrade($admin['id'], $fromVersion, $toVersion, $migrationsCount);
|
||||
$this->notifySystemUpgrade($admin['id'], $fromVersion, $toVersion, $migrationsCount, $composerManualRequired);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$logger = new \App\Services\Logger();
|
||||
@@ -602,6 +608,50 @@ class NotificationService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create "update available" in-app notification for one user
|
||||
*/
|
||||
public function notifyUpdateAvailable(int $userId, string $currentVersion, string $latestVersion, string $type = 'release', ?int $commitsBehind = null): void
|
||||
{
|
||||
$notificationModel = new \App\Models\Notification();
|
||||
$title = 'Update Available';
|
||||
if ($type === 'release') {
|
||||
$message = "A new version of Domain Monitor is available: v{$latestVersion} (you have v{$currentVersion}). Go to Settings → Updates to apply.";
|
||||
} else {
|
||||
$msg = $commitsBehind
|
||||
? "{$commitsBehind} new commit(s) are available on the main branch. Go to Settings → Updates to apply the hotfix."
|
||||
: "New commits are available. Go to Settings → Updates to apply the hotfix.";
|
||||
$message = $msg;
|
||||
}
|
||||
$notificationModel->createNotification(
|
||||
$userId,
|
||||
'update_available',
|
||||
$title,
|
||||
$message,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all admins that an update is available (in-app)
|
||||
* Used by cron when it detects a new version or hotfix.
|
||||
*/
|
||||
public function notifyAdminsUpdateAvailable(string $currentVersion, string $latestVersionOrLabel, string $type = 'release', ?int $commitsBehind = null): void
|
||||
{
|
||||
try {
|
||||
$userModel = new \App\Models\User();
|
||||
$admins = $userModel->getAllAdmins();
|
||||
foreach ($admins as $admin) {
|
||||
$this->notifyUpdateAvailable($admin['id'], $currentVersion, $latestVersionOrLabel, $type, $commitsBehind);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$logger = new \App\Services\Logger();
|
||||
$logger->error("Failed to notify admins about available update", [
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete old read notifications (cleanup)
|
||||
*/
|
||||
|
||||
1240
app/Services/UpdateService.php
Normal file
1240
app/Services/UpdateService.php
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user