Allow completely deleting a user with all the sites etc.

This commit is contained in:
Ralph J. Smit
2022-08-16 13:20:23 +02:00
parent ae1b41e068
commit dc87d0d415
8 changed files with 168 additions and 57 deletions

View File

@@ -0,0 +1,50 @@
<?php
namespace App\Actions\User;
use App\Jobs\Servers\DeleteServer;
use App\Jobs\Sites\DeleteSite;
use App\Models\Server;
use App\Models\Site;
use App\Models\User;
class DeleteUserAction
{
public function execute(User $user, bool $removeAllData): void
{
if ( $removeAllData ) {
$this->removeAllData($user);
}
// The next items are already being deleted by the "deleting" event:
// systemLogs, servers detached, sites detached, supportTickets, supportTicketReplies, userProviders
$user->delete();
}
protected function removeAllData(User $user): void
{
$user
->sites()
->withCount('users')
->get()
->filter(fn (Site $site) => $site->users_count === 1)
->each(function (Site $site) {
dispatch(new DeleteSite($site->server->ploi_id, $site->ploi_id));
// Deletes databases, redirects, cronjobs, certificates.
$site->delete();
});
$user
->servers()
->withCount('users')
->get()
->filter(fn (Server $server) => $server->users_count === 1)
->each(function (Server $server) {
dispatch(new DeleteServer($server->ploi_id));
// Deletes databases, redirects, cronjobs, certificates.
$server->delete();
});
}
}

View File

@@ -2,36 +2,35 @@
namespace App\DataTransferObjects;
use App\Models\User;
use App\Models\Package;
use Illuminate\Support\Carbon;
use App\DataTransferObjects\Support\Data;
use Spatie\LaravelData\Attributes\Validation\Max;
use App\Models\Package;
use App\Models\User;
use Illuminate\Support\Carbon;
use Spatie\LaravelData\Attributes\Validation\BooleanType;
use Spatie\LaravelData\Attributes\Validation\Email;
use Spatie\LaravelData\Attributes\Validation\Exists;
use Spatie\LaravelData\Attributes\Validation\Unique;
use Spatie\LaravelData\Attributes\Validation\StringType;
use Spatie\LaravelData\Attributes\Validation\IntegerType;
use Spatie\LaravelData\Attributes\Validation\Max;
use Spatie\LaravelData\Attributes\Validation\StringType;
use Spatie\LaravelData\Attributes\Validation\Unique;
class UserData extends Data
{
public function __construct(
public ?int $id = null,
public ?string $avatar = null,
#[StringType,
Max(255)]
#[StringType, Max( 255 )]
public ?string $name = null,
#[StringType,
Email,
Max(255),
Unique(User::class)]
#[StringType, Email, Max( 255 ), Unique( User::class )]
public ?string $email = null,
#[Exists(Package::class, 'id'),
IntegerType]
#[Exists( Package::class, 'id' ), IntegerType]
public ?int $package_id = null,
#[StringType]
public ?string $blocked = null,
#[StringType]
public ?string $language = 'en',
#[BooleanType]
public ?bool $requires_password_for_ftp = true,
public ?Carbon $created_at = null,
) {
}
) {}
}

View File

@@ -2,9 +2,11 @@
namespace App\Filament\Resources\UserResource\Pages;
use Filament\Pages\Actions;
use App\Actions\User\DeleteUserAction;
use App\Filament\Resources\UserResource;
use Filament\Forms\Components\Toggle;
use Filament\Notifications\Notification;
use Filament\Pages\Actions;
use Filament\Resources\Pages\EditRecord;
class EditUser extends EditRecord
@@ -27,7 +29,25 @@ class EditUser extends EditRecord
})
->visible(fn () => $this->record->hasTwoFactorEnabled())
->requiresConfirmation(),
Actions\DeleteAction::make(),
Actions\Action::make('delete')
->form([
Toggle::make('remove_all_data')
->label(__('Delete all sites, databases, etc.'))
->default(true)
->helperText(__('This will delete all the sites, databases, etc. associated with this user. Sites that belong to multiple users will not be deleted. This action cannot be undone.')),
])
->requiresConfirmation()
->action(function (array $data) {
app(DeleteUserAction::class)->execute($this->getRecord(), $data['remove_all_data']);
Notification::make()
->body(__('User deleted'))
->success()
->send();
$this->redirectRoute('filament.resources.users.index');
})
->color('danger'),
];
}
}

View File

@@ -2,11 +2,11 @@
namespace App\Http\Controllers;
use Illuminate\Support\Str;
use App\Jobs\Databases\CreateDatabase;
use App\Jobs\Databases\DeleteDatabase;
use App\Http\Requests\SiteDatabaseRequest;
use App\Http\Resources\SiteDatabaseResource;
use App\Jobs\Databases\CreateDatabase;
use App\Jobs\Databases\DeleteDatabase;
use Illuminate\Support\Str;
class SiteDatabaseController extends Controller
{
@@ -16,7 +16,7 @@ class SiteDatabaseController extends Controller
return inertia('Sites/Databases', [
'site' => $site,
'databases' => SiteDatabaseResource::collection($site->databases()->latest()->paginate())
'databases' => SiteDatabaseResource::collection($site->databases()->latest()->paginate()),
]);
}
@@ -25,21 +25,21 @@ class SiteDatabaseController extends Controller
$site = auth()->user()->sites()->findOrFail($id);
$database = $site->databases()->create([
'name' => Str::of($site->domain)->limit(8)->remove(['.', '-'])->lower()->append('_')->append($request->input('name'))
'name' => Str::of($site->domain)->limit(8)->remove(['.', '-'])->lower()->append('_')->append($request->input('name')),
]);
$database->users()->create([
'name' => $request->input('user_name', ),
'name' => $request->input('user_name',),
]);
$database->server_id = $site->server_id;
$database->save();
dispatch(new CreateDatabase($database, $request->input('password')));
dispatch_sync(new CreateDatabase($database, $request->input('password')));
$request->user()->systemLogs()->create([
'title' => 'New database :database created',
'description' => 'A new database has been created'
'description' => 'A new database has been created',
])->model()->associate($database)->save();
return redirect()->route('sites.databases.index', $id)->with('success', __('New database has been created'));

View File

@@ -2,19 +2,20 @@
namespace App\Jobs\Databases;
use App\Traits\HasPloi;
use App\Models\Database;
use App\Traits\HasPloi;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class CreateDatabase implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, HasPloi;
public $database;
public $password;
/**
@@ -43,9 +44,11 @@ class CreateDatabase implements ShouldQueue
->databases()
->create($this->database->name, $databaseUser->name, $this->password);
ray($ploiDatabase);
$this->database->ploi_id = $ploiDatabase->id;
$this->database->save();
ray($ploiDatabase);
$databaseUser->ploi_id = $ploiDatabase->users[0]->id;
$databaseUser->save();

View File

@@ -2,12 +2,16 @@
namespace App\Models;
use DateTimeInterface;
use App\Casts\SiteAlias;
use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Model;
use App\Jobs\Certificates\DeleteCertificate;
use App\Jobs\Cronjobs\DeleteCronjob;
use App\Jobs\Databases\DeleteDatabase;
use App\Jobs\Redirects\DeleteRedirect;
use DateTimeInterface;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Support\Str;
class Site extends Model
{
@@ -25,7 +29,7 @@ class Site extends Model
'domain',
'dns_id',
'project',
'aliases'
'aliases',
];
public $casts = [
@@ -34,7 +38,7 @@ class Site extends Model
public function setDnsIdAttribute($value)
{
if (!$value) {
if ( ! $value ) {
return;
}
@@ -100,7 +104,7 @@ class Site extends Model
public function getSystemUser($withPassword = true)
{
if (setting('isolate_per_site_per_user') && $this->systemUsers()->first()) {
if ( setting('isolate_per_site_per_user') && $this->systemUsers()->first() ) {
$user = $this->systemUsers()->first();
} else {
$user = $this->users()->first();
@@ -108,7 +112,7 @@ class Site extends Model
return [
'user_name' => $user->user_name,
] + ($withPassword ? ['ftp_password' => $user->ftp_password] : []);
] + ( $withPassword ? ['ftp_password' => $user->ftp_password] : [] );
}
public function addAlias($alias)
@@ -135,14 +139,19 @@ class Site extends Model
static::created(function (self $site) {
$site->systemUsers()->create([
'user_name' => Str::of($site->domain)->remove(['.', '-'])->limit(8, '')->lower()
'user_name' => Str::of($site->domain)->remove(['.', '-'])->limit(8, '')->lower(),
]);
});
static::deleting(function (self $site) {
foreach ($site->databases as $database) {
$database->delete();
}
$site
->databases()
->get()
->each(function (Database $database) {
dispatch(new DeleteDatabase($database->server->ploi_id, $database->ploi_id));
$database->delete();
});
$ids = $site->systemUsers->pluck('id');
// Detach all db users
@@ -151,15 +160,42 @@ class Site extends Model
// Loop through ids an remove old users.
foreach ($ids as $id) {
$record = SiteSystemUser::find($id);
if ($record) {
if ( $record ) {
$record->delete();
}
}
$site->redirects()->delete();
$site->cronjobs()->delete();
$site->certificates()->delete();
// MOETEN HIER OOK JOBS VOOR WORDEN GEDISPATCHET?
$site
->redirects()
->get()
->each(function (Redirect $redirect) {
dispatch(new DeleteRedirect($redirect->server->ploi_id, $redirect->site->ploi_id, $redirect->ploi_id));
$redirect->delete();
});
$site
->cronjobs()
->get()
->each(function (Cronjob $cronJob) {
dispatch(new DeleteCronjob($cronJob->server->ploi_id, $cronJob->ploi_id));
$cronJob->delete();
});
$site
->certificates()
->get()
->each(function (Certificate $certificate) {
dispatch(new DeleteCertificate($certificate->server->ploi_id, $certificate->site->ploi_id, $certificate->ploi_id));
$certificate->delete();
});
$site->logs()->delete();
$site->users()->detach();
});
}

View File

@@ -39,7 +39,7 @@ class User extends Authenticatable implements HasLocalePreference, TwoFactorAuth
'theme',
'keyboard_shortcuts',
'requires_password_for_ftp',
'package_id'
'package_id',
];
protected $hidden = [
@@ -57,12 +57,12 @@ class User extends Authenticatable implements HasLocalePreference, TwoFactorAuth
];
protected $appends = [
'avatar'
'avatar',
];
public function setPasswordAttribute($value)
{
if (!$value) {
if ( ! $value ) {
$this->attributes['password'] = null;
} else {
$this->attributes['password'] = bcrypt($value);
@@ -86,7 +86,7 @@ class User extends Authenticatable implements HasLocalePreference, TwoFactorAuth
public function getGravatar($size = 150)
{
return 'https://www.gravatar.com/avatar/' . md5(strtolower(trim(Arr::get($this->attributes, 'email')))) . '?s=' . (int)$size;
return 'https://www.gravatar.com/avatar/' . md5(strtolower(trim(Arr::get($this->attributes, 'email')))) . '?s=' . (int) $size;
}
public function isAdmin()
@@ -144,11 +144,11 @@ class User extends Authenticatable implements HasLocalePreference, TwoFactorAuth
$user->user_name = strtolower(Str::random(10));
$user->ftp_password = Str::random();
if (!$user->language) {
if ( ! $user->language ) {
$user->language = setting('default_language', 'en');
}
if ($days = setting('trial')) {
if ( $days = setting('trial') ) {
$user->trial_ends_at = now()->addDays($days);
}
});
@@ -156,7 +156,7 @@ class User extends Authenticatable implements HasLocalePreference, TwoFactorAuth
static::created(function (self $user) {
// Usually I don't like using such conditions. However, otherwise when using Mail::fake(),
// this would fake all emails going out leading to possible unexpected results as well.
if (! app()->runningUnitTests()) {
if ( ! app()->runningUnitTests() ) {
Mail::to($user)->send(new WelcomeEmail($user));
}
});
@@ -165,10 +165,13 @@ class User extends Authenticatable implements HasLocalePreference, TwoFactorAuth
$user->systemLogs()->delete();
$user->servers()->detach();
$user->sites()->detach();
foreach ($user->supportTickets as $supportTicket) {
$supportTicket->replies()->delete();
}
$user->supportTickets()->delete();
$user->providers()->delete();
});
}
}

View File

@@ -2,8 +2,8 @@
namespace App\Services\Ploi\Resources;
use stdClass;
use App\Services\Ploi\Exceptions\Http\NotValid;
use stdClass;
class Database extends Resource
{
@@ -24,7 +24,7 @@ class Database extends Resource
{
$this->setEndpoint($this->getServer()->getEndpoint() . '/' . $this->getServer()->getId() . '/databases');
if ($this->getId()) {
if ( $this->getId() ) {
$this->setEndpoint($this->getEndpoint() . '/' . $this->getId());
}
@@ -33,7 +33,7 @@ class Database extends Resource
public function get(int $id = null)
{
if ($id) {
if ( $id ) {
$this->setId($id);
}
@@ -66,15 +66,15 @@ class Database extends Resource
}
// Set the id of the site
$this->setId($response->getJson()->data->id);
$this->setId($response->getData()->id);
// Return the data
return $response->getJson()->data;
return $response->getData();
}
public function delete(int $id): bool
{
if ($id) {
if ( $id ) {
$this->setId($id);
}