Allow completely deleting a user with all the sites etc.
This commit is contained in:
50
app/Actions/User/DeleteUserAction.php
Normal file
50
app/Actions/User/DeleteUserAction.php
Normal 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();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -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'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'));
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user