feat(cleanup): add command for sanitizing name fields across models
- Introduced `CleanupNames` command to sanitize name fields by removing invalid characters, ensuring only letters, numbers, spaces, dashes, underscores, and dots are retained. - Implemented options for dry run, model-specific cleaning, database backup, and forced execution. - Updated `Init` command to call the new `cleanup:names` command. - Enhanced project and environment validation to enforce name sanitization rules. - Added `HasSafeNameAttribute` trait to relevant models for consistent name handling.
This commit is contained in:
245
app/Console/Commands/CleanupNames.php
Normal file
245
app/Console/Commands/CleanupNames.php
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\Environment;
|
||||||
|
use App\Models\PrivateKey;
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Models\S3Storage;
|
||||||
|
use App\Models\ScheduledTask;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\StandaloneClickhouse;
|
||||||
|
use App\Models\StandaloneDragonfly;
|
||||||
|
use App\Models\StandaloneKeydb;
|
||||||
|
use App\Models\StandaloneMariadb;
|
||||||
|
use App\Models\StandaloneMongodb;
|
||||||
|
use App\Models\StandaloneMysql;
|
||||||
|
use App\Models\StandalonePostgresql;
|
||||||
|
use App\Models\StandaloneRedis;
|
||||||
|
use App\Models\Tag;
|
||||||
|
use App\Models\Team;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class CleanupNames extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'cleanup:names
|
||||||
|
{--dry-run : Preview changes without applying them}
|
||||||
|
{--model= : Clean specific model (e.g., Project, Server)}
|
||||||
|
{--backup : Create database backup before changes}
|
||||||
|
{--force : Skip confirmation prompt}';
|
||||||
|
|
||||||
|
protected $description = 'Sanitize name fields by removing invalid characters (keeping only letters, numbers, spaces, dashes, underscores, dots)';
|
||||||
|
|
||||||
|
protected array $modelsToClean = [
|
||||||
|
'Project' => Project::class,
|
||||||
|
'Environment' => Environment::class,
|
||||||
|
'Application' => Application::class,
|
||||||
|
'Service' => Service::class,
|
||||||
|
'Server' => Server::class,
|
||||||
|
'Team' => Team::class,
|
||||||
|
'StandalonePostgresql' => StandalonePostgresql::class,
|
||||||
|
'StandaloneMysql' => StandaloneMysql::class,
|
||||||
|
'StandaloneRedis' => StandaloneRedis::class,
|
||||||
|
'StandaloneMongodb' => StandaloneMongodb::class,
|
||||||
|
'StandaloneMariadb' => StandaloneMariadb::class,
|
||||||
|
'StandaloneKeydb' => StandaloneKeydb::class,
|
||||||
|
'StandaloneDragonfly' => StandaloneDragonfly::class,
|
||||||
|
'StandaloneClickhouse' => StandaloneClickhouse::class,
|
||||||
|
'S3Storage' => S3Storage::class,
|
||||||
|
'Tag' => Tag::class,
|
||||||
|
'PrivateKey' => PrivateKey::class,
|
||||||
|
'ScheduledTask' => ScheduledTask::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
protected array $changes = [];
|
||||||
|
|
||||||
|
protected int $totalProcessed = 0;
|
||||||
|
|
||||||
|
protected int $totalCleaned = 0;
|
||||||
|
|
||||||
|
public function handle(): int
|
||||||
|
{
|
||||||
|
$this->info('🔍 Scanning for invalid characters in name fields...');
|
||||||
|
|
||||||
|
if ($this->option('backup') && ! $this->option('dry-run')) {
|
||||||
|
$this->createBackup();
|
||||||
|
}
|
||||||
|
|
||||||
|
$modelFilter = $this->option('model');
|
||||||
|
$modelsToProcess = $modelFilter
|
||||||
|
? [$modelFilter => $this->modelsToClean[$modelFilter] ?? null]
|
||||||
|
: $this->modelsToClean;
|
||||||
|
|
||||||
|
if ($modelFilter && ! isset($this->modelsToClean[$modelFilter])) {
|
||||||
|
$this->error("❌ Unknown model: {$modelFilter}");
|
||||||
|
$this->info('Available models: '.implode(', ', array_keys($this->modelsToClean)));
|
||||||
|
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($modelsToProcess as $modelName => $modelClass) {
|
||||||
|
if (! $modelClass) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->processModel($modelName, $modelClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->displaySummary();
|
||||||
|
|
||||||
|
if (! $this->option('dry-run') && $this->totalCleaned > 0) {
|
||||||
|
$this->logChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processModel(string $modelName, string $modelClass): void
|
||||||
|
{
|
||||||
|
$this->info("\n📋 Processing {$modelName}...");
|
||||||
|
|
||||||
|
try {
|
||||||
|
$records = $modelClass::all(['id', 'name']);
|
||||||
|
$cleaned = 0;
|
||||||
|
|
||||||
|
foreach ($records as $record) {
|
||||||
|
$this->totalProcessed++;
|
||||||
|
|
||||||
|
$originalName = $record->name;
|
||||||
|
$sanitizedName = $this->sanitizeName($originalName);
|
||||||
|
|
||||||
|
if ($sanitizedName !== $originalName) {
|
||||||
|
$this->changes[] = [
|
||||||
|
'model' => $modelName,
|
||||||
|
'id' => $record->id,
|
||||||
|
'original' => $originalName,
|
||||||
|
'sanitized' => $sanitizedName,
|
||||||
|
'timestamp' => now(),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (! $this->option('dry-run')) {
|
||||||
|
// Update without triggering events/mutators to avoid conflicts
|
||||||
|
$modelClass::where('id', $record->id)->update(['name' => $sanitizedName]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cleaned++;
|
||||||
|
$this->totalCleaned++;
|
||||||
|
|
||||||
|
$this->warn(" 🧹 {$modelName} #{$record->id}:");
|
||||||
|
$this->line(' From: '.$this->truncate($originalName, 80));
|
||||||
|
$this->line(' To: '.$this->truncate($sanitizedName, 80));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cleaned > 0) {
|
||||||
|
$action = $this->option('dry-run') ? 'would be sanitized' : 'sanitized';
|
||||||
|
$this->info(" ✅ {$cleaned}/{$records->count()} records {$action}");
|
||||||
|
} else {
|
||||||
|
$this->info(' ✨ No invalid characters found');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->error(" ❌ Error processing {$modelName}: ".$e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function sanitizeName(string $name): string
|
||||||
|
{
|
||||||
|
// Remove all characters that don't match the allowed pattern
|
||||||
|
$sanitized = preg_replace('/[^a-zA-Z0-9\s\-_.]+/', '', $name);
|
||||||
|
|
||||||
|
// Clean up excessive whitespace but preserve other allowed characters
|
||||||
|
$sanitized = preg_replace('/\s+/', ' ', $sanitized);
|
||||||
|
$sanitized = trim($sanitized);
|
||||||
|
|
||||||
|
// If result is empty, provide a default name
|
||||||
|
if (empty($sanitized)) {
|
||||||
|
$sanitized = 'sanitized-item';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sanitized;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function displaySummary(): void
|
||||||
|
{
|
||||||
|
$this->info("\n".str_repeat('=', 60));
|
||||||
|
$this->info('📊 CLEANUP SUMMARY');
|
||||||
|
$this->info(str_repeat('=', 60));
|
||||||
|
|
||||||
|
$this->line("Records processed: {$this->totalProcessed}");
|
||||||
|
$this->line("Records with invalid characters: {$this->totalCleaned}");
|
||||||
|
|
||||||
|
if ($this->option('dry-run')) {
|
||||||
|
$this->warn("\n🔍 DRY RUN - No changes were made to the database");
|
||||||
|
$this->info('Run without --dry-run to apply these changes');
|
||||||
|
} else {
|
||||||
|
if ($this->totalCleaned > 0) {
|
||||||
|
$this->info("\n✅ Database successfully sanitized!");
|
||||||
|
$this->info('Changes logged to storage/logs/name-cleanup.log');
|
||||||
|
} else {
|
||||||
|
$this->info("\n✨ No cleanup needed - all names are valid!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function logChanges(): void
|
||||||
|
{
|
||||||
|
$logFile = storage_path('logs/name-cleanup.log');
|
||||||
|
$logData = [
|
||||||
|
'timestamp' => now()->toISOString(),
|
||||||
|
'total_processed' => $this->totalProcessed,
|
||||||
|
'total_cleaned' => $this->totalCleaned,
|
||||||
|
'changes' => $this->changes,
|
||||||
|
];
|
||||||
|
|
||||||
|
file_put_contents($logFile, json_encode($logData, JSON_PRETTY_PRINT)."\n", FILE_APPEND);
|
||||||
|
|
||||||
|
Log::info('Name Sanitization completed', [
|
||||||
|
'total_processed' => $this->totalProcessed,
|
||||||
|
'total_sanitized' => $this->totalCleaned,
|
||||||
|
'changes_count' => count($this->changes),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createBackup(): void
|
||||||
|
{
|
||||||
|
$this->info('💾 Creating database backup...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$backupFile = storage_path('backups/name-cleanup-backup-'.now()->format('Y-m-d-H-i-s').'.sql');
|
||||||
|
|
||||||
|
// Ensure backup directory exists
|
||||||
|
if (! file_exists(dirname($backupFile))) {
|
||||||
|
mkdir(dirname($backupFile), 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbConfig = config('database.connections.'.config('database.default'));
|
||||||
|
$command = sprintf(
|
||||||
|
'pg_dump -h %s -p %s -U %s -d %s > %s',
|
||||||
|
$dbConfig['host'],
|
||||||
|
$dbConfig['port'],
|
||||||
|
$dbConfig['username'],
|
||||||
|
$dbConfig['database'],
|
||||||
|
$backupFile
|
||||||
|
);
|
||||||
|
|
||||||
|
exec($command, $output, $returnCode);
|
||||||
|
|
||||||
|
if ($returnCode === 0) {
|
||||||
|
$this->info("✅ Backup created: {$backupFile}");
|
||||||
|
} else {
|
||||||
|
$this->warn('⚠️ Backup creation may have failed. Proceeding anyway...');
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->warn('⚠️ Could not create backup: '.$e->getMessage());
|
||||||
|
$this->warn('Proceeding without backup...');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function truncate(string $text, int $length): string
|
||||||
|
{
|
||||||
|
return strlen($text) > $length ? substr($text, 0, $length).'...' : $text;
|
||||||
|
}
|
||||||
|
}
|
@@ -53,6 +53,11 @@ class Init extends Command
|
|||||||
|
|
||||||
$this->call('cleanup:redis');
|
$this->call('cleanup:redis');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->call('cleanup:names');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleanup:names command: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
$this->call('cleanup:stucked-resources');
|
$this->call('cleanup:stucked-resources');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api;
|
|||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
|
|
||||||
class ProjectController extends Controller
|
class ProjectController extends Controller
|
||||||
@@ -227,9 +228,14 @@ class ProjectController extends Controller
|
|||||||
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
$validator = customApiValidator($request->all(), [
|
$validator = Validator::make($request->all(), [
|
||||||
'name' => 'string|max:255|required',
|
'name' => ['required', 'string', 'max:255', 'regex:/^[a-zA-Z0-9\s\-_.]+$/'],
|
||||||
'description' => 'string|nullable',
|
'description' => 'string|nullable',
|
||||||
|
], [
|
||||||
|
'name.regex' => 'The project name may only contain letters, numbers, spaces, dashes, underscores, and dots.',
|
||||||
|
'name.required' => 'The project name is required.',
|
||||||
|
'name.min' => 'The project name must be at least 3 characters.',
|
||||||
|
'name.max' => 'The project name may not be greater than 255 characters.',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
|
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
|
||||||
@@ -337,9 +343,13 @@ class ProjectController extends Controller
|
|||||||
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
$validator = customApiValidator($request->all(), [
|
$validator = Validator::make($request->all(), [
|
||||||
'name' => 'string|max:255|nullable',
|
'name' => ['nullable', 'string', 'max:255', 'regex:/^[a-zA-Z0-9\s\-_.]+$/'],
|
||||||
'description' => 'string|nullable',
|
'description' => 'string|nullable',
|
||||||
|
], [
|
||||||
|
'name.regex' => 'The project name may only contain letters, numbers, spaces, dashes, underscores, and dots.',
|
||||||
|
'name.min' => 'The project name must be at least 3 characters.',
|
||||||
|
'name.max' => 'The project name may not be greater than 255 characters.',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
|
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
|
||||||
@@ -579,8 +589,13 @@ class ProjectController extends Controller
|
|||||||
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
$validator = customApiValidator($request->all(), [
|
$validator = Validator::make($request->all(), [
|
||||||
'name' => 'string|max:255|required',
|
'name' => ['required', 'string', 'max:255', 'regex:/^[a-zA-Z0-9\s\-_.]+$/'],
|
||||||
|
], [
|
||||||
|
'name.regex' => 'The environment name may only contain letters, numbers, spaces, dashes, underscores, and dots.',
|
||||||
|
'name.required' => 'The environment name is required.',
|
||||||
|
'name.min' => 'The environment name must be at least 3 characters.',
|
||||||
|
'name.max' => 'The environment name may not be greater than 255 characters.',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
|
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
|
||||||
|
@@ -9,12 +9,18 @@ use Visus\Cuid2\Cuid2;
|
|||||||
|
|
||||||
class AddEmpty extends Component
|
class AddEmpty extends Component
|
||||||
{
|
{
|
||||||
#[Validate(['required', 'string', 'min:3'])]
|
#[Validate(['required', 'string', 'min:3', 'max:255', 'regex:/^[a-zA-Z0-9\s\-_.]+$/'])]
|
||||||
public string $name;
|
public string $name;
|
||||||
|
|
||||||
#[Validate(['nullable', 'string'])]
|
#[Validate(['nullable', 'string'])]
|
||||||
public string $description = '';
|
public string $description = '';
|
||||||
|
|
||||||
|
protected $messages = [
|
||||||
|
'name.regex' => 'The name may only contain letters, numbers, spaces, dashes, underscores, and dots.',
|
||||||
|
'name.min' => 'The name must be at least 3 characters.',
|
||||||
|
'name.max' => 'The name may not be greater than 255 characters.',
|
||||||
|
];
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
@@ -45,7 +45,10 @@ class CloneMe extends Component
|
|||||||
protected $messages = [
|
protected $messages = [
|
||||||
'selectedServer' => 'Please select a server.',
|
'selectedServer' => 'Please select a server.',
|
||||||
'selectedDestination' => 'Please select a server & destination.',
|
'selectedDestination' => 'Please select a server & destination.',
|
||||||
'newName' => 'Please enter a name for the new project or environment.',
|
'newName.required' => 'Please enter a name for the new project or environment.',
|
||||||
|
'newName.regex' => 'The name may only contain letters, numbers, spaces, dashes, underscores, and dots.',
|
||||||
|
'newName.min' => 'The name must be at least 3 characters.',
|
||||||
|
'newName.max' => 'The name may not be greater than 255 characters.',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount($project_uuid)
|
public function mount($project_uuid)
|
||||||
@@ -90,7 +93,7 @@ class CloneMe extends Component
|
|||||||
try {
|
try {
|
||||||
$this->validate([
|
$this->validate([
|
||||||
'selectedDestination' => 'required',
|
'selectedDestination' => 'required',
|
||||||
'newName' => 'required',
|
'newName' => ['required', 'string', 'min:3', 'max:255', 'regex:/^[a-zA-Z0-9\s\-_.]+$/'],
|
||||||
]);
|
]);
|
||||||
if ($type === 'project') {
|
if ($type === 'project') {
|
||||||
$foundProject = Project::where('name', $this->newName)->first();
|
$foundProject = Project::where('name', $this->newName)->first();
|
||||||
|
@@ -10,12 +10,18 @@ class Edit extends Component
|
|||||||
{
|
{
|
||||||
public Project $project;
|
public Project $project;
|
||||||
|
|
||||||
#[Validate(['required', 'string', 'min:3', 'max:255'])]
|
#[Validate(['required', 'string', 'min:3', 'max:255', 'regex:/^[a-zA-Z0-9\s\-_.]+$/'])]
|
||||||
public string $name;
|
public string $name;
|
||||||
|
|
||||||
#[Validate(['nullable', 'string', 'max:255'])]
|
#[Validate(['nullable', 'string', 'max:255'])]
|
||||||
public ?string $description = null;
|
public ?string $description = null;
|
||||||
|
|
||||||
|
protected $messages = [
|
||||||
|
'name.regex' => 'The name may only contain letters, numbers, spaces, dashes, underscores, and dots.',
|
||||||
|
'name.min' => 'The name must be at least 3 characters.',
|
||||||
|
'name.max' => 'The name may not be greater than 255 characters.',
|
||||||
|
];
|
||||||
|
|
||||||
public function mount(string $project_uuid)
|
public function mount(string $project_uuid)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
@@ -17,12 +17,18 @@ class EnvironmentEdit extends Component
|
|||||||
#[Locked]
|
#[Locked]
|
||||||
public $environment;
|
public $environment;
|
||||||
|
|
||||||
#[Validate(['required', 'string', 'min:3', 'max:255'])]
|
#[Validate(['required', 'string', 'min:3', 'max:255', 'regex:/^[a-zA-Z0-9\s\-_.]+$/'])]
|
||||||
public string $name;
|
public string $name;
|
||||||
|
|
||||||
#[Validate(['nullable', 'string', 'max:255'])]
|
#[Validate(['nullable', 'string', 'max:255'])]
|
||||||
public ?string $description = null;
|
public ?string $description = null;
|
||||||
|
|
||||||
|
protected $messages = [
|
||||||
|
'name.regex' => 'The environment name may only contain letters, numbers, spaces, dashes, underscores, and dots.',
|
||||||
|
'name.min' => 'The environment name must be at least 3 characters.',
|
||||||
|
'name.max' => 'The environment name may not be greater than 255 characters.',
|
||||||
|
];
|
||||||
|
|
||||||
public function mount(string $project_uuid, string $environment_uuid)
|
public function mount(string $project_uuid, string $environment_uuid)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
@@ -12,12 +12,18 @@ class Show extends Component
|
|||||||
{
|
{
|
||||||
public Project $project;
|
public Project $project;
|
||||||
|
|
||||||
#[Validate(['required', 'string', 'min:3'])]
|
#[Validate(['required', 'string', 'min:3', 'max:255', 'regex:/^[a-zA-Z0-9\s\-_.]+$/'])]
|
||||||
public string $name;
|
public string $name;
|
||||||
|
|
||||||
#[Validate(['nullable', 'string'])]
|
#[Validate(['nullable', 'string'])]
|
||||||
public ?string $description = null;
|
public ?string $description = null;
|
||||||
|
|
||||||
|
protected $messages = [
|
||||||
|
'name.regex' => 'The environment name may only contain letters, numbers, spaces, dashes, underscores, and dots.',
|
||||||
|
'name.min' => 'The environment name must be at least 3 characters.',
|
||||||
|
'name.max' => 'The environment name may not be greater than 255 characters.',
|
||||||
|
];
|
||||||
|
|
||||||
public function mount(string $project_uuid)
|
public function mount(string $project_uuid)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
@@ -5,6 +5,7 @@ namespace App\Models;
|
|||||||
use App\Enums\ApplicationDeploymentStatus;
|
use App\Enums\ApplicationDeploymentStatus;
|
||||||
use App\Services\ConfigurationGenerator;
|
use App\Services\ConfigurationGenerator;
|
||||||
use App\Traits\HasConfiguration;
|
use App\Traits\HasConfiguration;
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
@@ -109,7 +110,7 @@ use Visus\Cuid2\Cuid2;
|
|||||||
|
|
||||||
class Application extends BaseModel
|
class Application extends BaseModel
|
||||||
{
|
{
|
||||||
use HasConfiguration, HasFactory, SoftDeletes;
|
use HasConfiguration, HasFactory, HasSafeNameAttribute, SoftDeletes;
|
||||||
|
|
||||||
private static $parserVersion = '5';
|
private static $parserVersion = '5';
|
||||||
|
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
|
|
||||||
@@ -19,6 +20,8 @@ use OpenApi\Attributes as OA;
|
|||||||
)]
|
)]
|
||||||
class Environment extends BaseModel
|
class Environment extends BaseModel
|
||||||
{
|
{
|
||||||
|
use HasSafeNameAttribute;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
protected static function booted()
|
protected static function booted()
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
@@ -27,7 +28,7 @@ use phpseclib3\Crypt\PublicKeyLoader;
|
|||||||
)]
|
)]
|
||||||
class PrivateKey extends BaseModel
|
class PrivateKey extends BaseModel
|
||||||
{
|
{
|
||||||
use WithRateLimiting;
|
use HasSafeNameAttribute, WithRateLimiting;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'name',
|
'name',
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
@@ -23,6 +24,8 @@ use Visus\Cuid2\Cuid2;
|
|||||||
)]
|
)]
|
||||||
class Project extends BaseModel
|
class Project extends BaseModel
|
||||||
{
|
{
|
||||||
|
use HasSafeNameAttribute;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
public static function ownedByCurrentTeam()
|
public static function ownedByCurrentTeam()
|
||||||
|
@@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
class S3Storage extends BaseModel
|
class S3Storage extends BaseModel
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory, HasSafeNameAttribute;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
|
@@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
|
|
||||||
class ScheduledTask extends BaseModel
|
class ScheduledTask extends BaseModel
|
||||||
{
|
{
|
||||||
|
use HasSafeNameAttribute;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
public function service()
|
public function service()
|
||||||
|
@@ -13,6 +13,7 @@ use App\Jobs\RegenerateSslCertJob;
|
|||||||
use App\Notifications\Server\Reachable;
|
use App\Notifications\Server\Reachable;
|
||||||
use App\Notifications\Server\Unreachable;
|
use App\Notifications\Server\Unreachable;
|
||||||
use App\Services\ConfigurationRepository;
|
use App\Services\ConfigurationRepository;
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
@@ -164,6 +165,8 @@ class Server extends BaseModel
|
|||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
|
use HasSafeNameAttribute;
|
||||||
|
|
||||||
public function type()
|
public function type()
|
||||||
{
|
{
|
||||||
return 'server';
|
return 'server';
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use App\Enums\ProcessStatus;
|
use App\Enums\ProcessStatus;
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
@@ -40,7 +41,7 @@ use Visus\Cuid2\Cuid2;
|
|||||||
)]
|
)]
|
||||||
class Service extends BaseModel
|
class Service extends BaseModel
|
||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, HasSafeNameAttribute, SoftDeletes;
|
||||||
|
|
||||||
private static $parserVersion = '5';
|
private static $parserVersion = '5';
|
||||||
|
|
||||||
|
@@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
class StandaloneClickhouse extends BaseModel
|
class StandaloneClickhouse extends BaseModel
|
||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, HasSafeNameAttribute, SoftDeletes;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
|
@@ -2,8 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
|
|
||||||
class StandaloneDocker extends BaseModel
|
class StandaloneDocker extends BaseModel
|
||||||
{
|
{
|
||||||
|
use HasSafeNameAttribute;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
protected static function boot()
|
protected static function boot()
|
||||||
|
@@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
class StandaloneDragonfly extends BaseModel
|
class StandaloneDragonfly extends BaseModel
|
||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, HasSafeNameAttribute, SoftDeletes;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
|
@@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
class StandaloneKeydb extends BaseModel
|
class StandaloneKeydb extends BaseModel
|
||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, HasSafeNameAttribute, SoftDeletes;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||||
@@ -9,7 +10,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
|||||||
|
|
||||||
class StandaloneMariadb extends BaseModel
|
class StandaloneMariadb extends BaseModel
|
||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, HasSafeNameAttribute, SoftDeletes;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
|
@@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
class StandaloneMongodb extends BaseModel
|
class StandaloneMongodb extends BaseModel
|
||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, HasSafeNameAttribute, SoftDeletes;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
|
@@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
class StandaloneMysql extends BaseModel
|
class StandaloneMysql extends BaseModel
|
||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, HasSafeNameAttribute, SoftDeletes;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
|
@@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
class StandalonePostgresql extends BaseModel
|
class StandalonePostgresql extends BaseModel
|
||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, HasSafeNameAttribute, SoftDeletes;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
@@ -322,7 +323,7 @@ class StandalonePostgresql extends BaseModel
|
|||||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||||
return [
|
return [
|
||||||
(int) $metric['time'],
|
(int) $metric['time'],
|
||||||
(float) ($metric['percent'] ?? 0.0)
|
(float) ($metric['percent'] ?? 0.0),
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
class StandaloneRedis extends BaseModel
|
class StandaloneRedis extends BaseModel
|
||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, HasSafeNameAttribute, SoftDeletes;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
|
@@ -2,10 +2,13 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
|
|
||||||
class Tag extends BaseModel
|
class Tag extends BaseModel
|
||||||
{
|
{
|
||||||
|
use HasSafeNameAttribute;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
public function name(): Attribute
|
public function name(): Attribute
|
||||||
|
@@ -8,6 +8,7 @@ use App\Notifications\Channels\SendsEmail;
|
|||||||
use App\Notifications\Channels\SendsPushover;
|
use App\Notifications\Channels\SendsPushover;
|
||||||
use App\Notifications\Channels\SendsSlack;
|
use App\Notifications\Channels\SendsSlack;
|
||||||
use App\Traits\HasNotificationSettings;
|
use App\Traits\HasNotificationSettings;
|
||||||
|
use App\Traits\HasSafeNameAttribute;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
@@ -36,7 +37,7 @@ use OpenApi\Attributes as OA;
|
|||||||
|
|
||||||
class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, SendsSlack
|
class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, SendsSlack
|
||||||
{
|
{
|
||||||
use HasNotificationSettings, Notifiable;
|
use HasNotificationSettings, HasSafeNameAttribute, Notifiable;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
|
14
app/Traits/HasSafeNameAttribute.php
Normal file
14
app/Traits/HasSafeNameAttribute.php
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Traits;
|
||||||
|
|
||||||
|
trait HasSafeNameAttribute
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set the name attribute - strip any HTML tags for safety
|
||||||
|
*/
|
||||||
|
public function setNameAttribute($value)
|
||||||
|
{
|
||||||
|
$this->attributes['name'] = strip_tags($value);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user