Add 100+ automated unit tests from .expect file specifications Add session system test Add rsx:constants:regenerate command test Add rsx:logrotate command test Add rsx:clean command test Add rsx:manifest:stats command test Add model enum system test Add model mass assignment prevention test Add rsx:check command test Add migrate:status command test 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
130 lines
3.7 KiB
PHP
130 lines
3.7 KiB
PHP
<?php
|
|
/**
|
|
* CODING CONVENTION:
|
|
* This file follows the coding convention where variable_names and function_names
|
|
* use snake_case (underscore_wherever_possible).
|
|
*/
|
|
|
|
namespace App\RSpade\Commands\Rsx;
|
|
|
|
use Illuminate\Console\Command;
|
|
use Rsx\Models\File_Attachment_Model;
|
|
use App\RSpade\Core\Locks\RsxLocks;
|
|
|
|
/**
|
|
* File Delete Command
|
|
* ====================
|
|
*
|
|
* PURPOSE:
|
|
* Delete a file attachment and cleanup orphaned storage.
|
|
*
|
|
* BEHAVIOR:
|
|
* 1. Deletes File_Attachment_Model record
|
|
* 2. Auto-cleanup triggers (via model boot() event):
|
|
* - If no other attachments reference the same storage:
|
|
* * Deletes physical file from disk
|
|
* * Deletes File_Storage_Model record
|
|
* - If other attachments exist, storage is preserved
|
|
*
|
|
* SAFETY:
|
|
* - Requires confirmation unless --force flag provided
|
|
* - Uses file write lock to prevent race conditions
|
|
*/
|
|
class RsxFileDeleteCommand extends Command
|
|
{
|
|
/**
|
|
* The name and signature of the console command.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $signature = 'rsx:file:delete
|
|
{key : 64-char hex attachment key}
|
|
{--force : Skip confirmation prompt}';
|
|
|
|
/**
|
|
* The console command description.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $description = 'Delete file attachment and cleanup orphaned storage';
|
|
|
|
/**
|
|
* Execute the console command.
|
|
*/
|
|
public function handle()
|
|
{
|
|
$key = $this->argument('key');
|
|
|
|
// Find attachment
|
|
$attachment = File_Attachment_Model::where('key', $key)->first();
|
|
|
|
if (!$attachment) {
|
|
$this->error("Error: File attachment not found with key: {$key}");
|
|
return 1;
|
|
}
|
|
|
|
// Get storage info before deletion
|
|
$storage_id = $attachment->file_storage_id;
|
|
$filename = $attachment->file_name;
|
|
|
|
// Check if storage will be deleted
|
|
$ref_count = File_Attachment_Model::where('file_storage_id', $storage_id)->count();
|
|
$will_delete_storage = ($ref_count === 1);
|
|
|
|
// Confirmation prompt
|
|
if (!$this->option('force')) {
|
|
$this->info('');
|
|
$this->info("File: {$filename}");
|
|
$this->info("Key: {$key}");
|
|
|
|
if ($will_delete_storage) {
|
|
$this->warn('');
|
|
$this->warn('Warning: This is the last reference to the physical file.');
|
|
$this->warn(' Physical storage will be deleted from disk.');
|
|
} else {
|
|
$this->info('');
|
|
$this->info("Note: Physical storage has {$ref_count} references and will be preserved.");
|
|
}
|
|
|
|
$this->info('');
|
|
|
|
if (!$this->confirm('Delete this file attachment?', false)) {
|
|
$this->info('Deletion cancelled');
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Acquire file write lock
|
|
$lock = RsxLocks::get_lock(
|
|
RsxLocks::SERVER_LOCK,
|
|
RsxLocks::LOCK_FILE_WRITE,
|
|
RsxLocks::WRITE_LOCK,
|
|
30
|
|
);
|
|
|
|
try {
|
|
// Delete attachment (auto-cleanup will handle storage)
|
|
$attachment->delete();
|
|
|
|
$this->info('');
|
|
$this->info('[OK] File attachment deleted');
|
|
$this->info('');
|
|
$this->info(" File: {$filename}");
|
|
$this->info(" Key: {$key}");
|
|
|
|
if ($will_delete_storage) {
|
|
$this->info(' Physical storage was also deleted');
|
|
} else {
|
|
$this->info(" Physical storage preserved ({$ref_count} total references)");
|
|
}
|
|
|
|
$this->info('');
|
|
|
|
return 0;
|
|
|
|
} finally {
|
|
RsxLocks::release_lock($lock);
|
|
}
|
|
}
|
|
}
|