This repository has been archived on 2026-07-19. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
DataMigration/files/custom/Espo/Modules/DataMigration/Api/DeleteBackup.php
T
chaim ec759b89c1 feat: snapshot management UI — list and delete database snapshots
- ListBackups API now returns snapshots alongside backups
- DeleteBackup API accepts snapshot- prefixed names
- New "Database Snapshots" panel in admin UI with delete buttons
- i18n: English + Hebrew translations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 20:38:33 +00:00

74 lines
1.7 KiB
PHP

<?php
namespace Espo\Modules\DataMigration\Api;
use Espo\Core\Api\Action;
use Espo\Core\Api\Request;
use Espo\Core\Api\Response;
use Espo\Core\Api\ResponseComposer;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Forbidden;
use Espo\Entities\User;
class DeleteBackup implements Action
{
public function __construct(
private User $user
) {}
public function process(Request $request): Response
{
if (!$this->user->isAdmin()) {
throw new Forbidden();
}
$body = $request->getParsedBody();
$backupName = $body->backupName ?? null;
if (!$backupName) {
throw new BadRequest('backupName is required.');
}
// Sanitize to prevent directory traversal
$safeName = basename($backupName);
if (!str_starts_with($safeName, 'datamigration-') && !str_starts_with($safeName, 'snapshot-')) {
throw new Forbidden('Invalid backup name.');
}
$fullPath = 'data/backups/' . $safeName;
if (!is_dir($fullPath)) {
throw new BadRequest("Backup not found: {$safeName}");
}
$this->deleteDirectory($fullPath);
return ResponseComposer::json([
'success' => true,
'deleted' => $safeName,
]);
}
private function deleteDirectory(string $dir): void
{
$items = scandir($dir);
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$path = $dir . '/' . $item;
if (is_dir($path)) {
$this->deleteDirectory($path);
} else {
unlink($path);
}
}
rmdir($dir);
}
}