Skip to content

Commit

Permalink
adding Projects feature (#85)
Browse files Browse the repository at this point in the history
  • Loading branch information
saeedvaziry authored Jan 2, 2024
1 parent fd2244d commit 10a6bb5
Show file tree
Hide file tree
Showing 32 changed files with 846 additions and 83 deletions.
36 changes: 36 additions & 0 deletions app/Actions/Projects/CreateProject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace App\Actions\Projects;

use App\Models\Project;
use App\Models\User;
use Illuminate\Support\Facades\Validator;

class CreateProject
{
public function create(User $user, array $input): Project
{
$this->validate($user, $input);

$project = new Project([
'user_id' => $user->id,
'name' => $input['name'],
]);

$project->save();

return $project;
}

private function validate(User $user, array $input): void
{
Validator::make($input, [
'name' => [
'required',
'string',
'max:255',
'unique:projects,name,NULL,id,user_id,'.$user->id,
],
])->validate();
}
}
31 changes: 31 additions & 0 deletions app/Actions/Projects/DeleteProject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace App\Actions\Projects;

use App\Models\Project;
use App\Models\User;
use Illuminate\Validation\ValidationException;

class DeleteProject
{
public function delete(User $user, int $projectId): void
{
/** @var Project $project */
$project = $user->projects()->findOrFail($projectId);

if ($user->projects()->count() === 1) {
throw ValidationException::withMessages([
'project' => __('Cannot delete the last project.'),
]);
}

if ($user->current_project_id == $project->id) {
/** @var Project $randomProject */
$randomProject = $user->projects()->where('id', '!=', $project->id)->first();
$user->current_project_id = $randomProject->id;
$user->save();
}

$project->delete();
}
}
33 changes: 33 additions & 0 deletions app/Actions/Projects/UpdateProject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace App\Actions\Projects;

use App\Models\Project;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

class UpdateProject
{
public function update(Project $project, array $input): Project
{
$this->validate($project, $input);

$project->name = $input['name'];

$project->save();

return $project;
}

private function validate(Project $project, array $input): void
{
Validator::make($input, [
'name' => [
'required',
'string',
'max:255',
Rule::unique('projects')->ignore($project->id),
],
])->validate();
}
}
1 change: 1 addition & 0 deletions app/Actions/Server/CreateServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public function create(User $creator, array $input): Server
$this->validateInputs($input);

$server = new Server([
'project_id' => $creator->currentProject->id,
'user_id' => $creator->id,
'name' => $input['name'],
'ssh_user' => config('core.server_providers_default_user')[$input['provider']][$input['os']],
Expand Down
29 changes: 29 additions & 0 deletions app/Http/Controllers/ProjectController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace App\Http\Controllers;

use App\Models\Project;
use App\Models\User;
use Illuminate\Contracts\View\View;

class ProjectController extends Controller
{
public function index(): View
{
return view('projects.index');
}

public function switch($projectId)
{
/** @var User $user */
$user = auth()->user();

/** @var Project $project */
$project = $user->projects()->findOrFail($projectId);

$user->current_project_id = $project->id;
$user->save();

return redirect()->route('servers');
}
}
37 changes: 37 additions & 0 deletions app/Http/Livewire/Projects/CreateProject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace App\Http\Livewire\Projects;

use App\Traits\HasToast;
use App\Traits\RefreshComponentOnBroadcast;
use Illuminate\Contracts\View\View;
use Livewire\Component;

class CreateProject extends Component
{
use HasToast;
use RefreshComponentOnBroadcast;

public bool $open = false;

public array $inputs = [];

public function create(): void
{
app(\App\Actions\Projects\CreateProject::class)
->create(auth()->user(), $this->inputs);

$this->emitTo(ProjectsList::class, '$refresh');

$this->dispatchBrowserEvent('created', true);
}

public function render(): View
{
if (request()->query('create')) {
$this->open = true;
}

return view('livewire.projects.create-project');
}
}
37 changes: 37 additions & 0 deletions app/Http/Livewire/Projects/EditProject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace App\Http\Livewire\Projects;

use App\Actions\Projects\UpdateProject;
use App\Models\Project;
use App\Traits\RefreshComponentOnBroadcast;
use Illuminate\Contracts\View\View;
use Livewire\Component;

class EditProject extends Component
{
use RefreshComponentOnBroadcast;

public Project $project;

public array $inputs = [];

public function save(): void
{
app(UpdateProject::class)->update($this->project, $this->inputs);

$this->redirect(route('projects'));
}

public function mount(): void
{
$this->inputs = [
'name' => $this->project->name,
];
}

public function render(): View
{
return view('livewire.projects.edit-project');
}
}
42 changes: 42 additions & 0 deletions app/Http/Livewire/Projects/ProjectsList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace App\Http\Livewire\Projects;

use App\Actions\Projects\DeleteProject;
use App\Traits\HasToast;
use App\Traits\RefreshComponentOnBroadcast;
use Illuminate\Contracts\View\View;
use Illuminate\Validation\ValidationException;
use Livewire\Component;

class ProjectsList extends Component
{
use HasToast;
use RefreshComponentOnBroadcast;

protected $listeners = [
'$refresh',
];

public int $deleteId;

public function delete(): void
{
try {
app(DeleteProject::class)->delete(auth()->user(), $this->deleteId);

$this->redirect(route('projects'));

return;
} catch (ValidationException $e) {
$this->toast()->error($e->getMessage());
}
}

public function render(): View
{
return view('livewire.projects.projects-list', [
'projects' => auth()->user()->projects()->orderByDesc('id')->get(),
]);
}
}
8 changes: 6 additions & 2 deletions app/Http/Livewire/Servers/ServersList.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace App\Http\Livewire\Servers;

use App\Models\Server;
use App\Models\User;
use App\Traits\RefreshComponentOnBroadcast;
use Illuminate\Contracts\View\View;
use Livewire\Component;
Expand All @@ -13,8 +13,12 @@ class ServersList extends Component

public function render(): View
{
/** @var User $user */
$user = auth()->user();
$servers = $user->currentProject->servers()->orderByDesc('created_at')->get();

return view('livewire.servers.servers-list', [
'servers' => Server::all(),
'servers' => $servers,
]);
}
}
56 changes: 56 additions & 0 deletions app/Models/Project.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace App\Models;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

/**
* @property int $id
* @property int $user_id
* @property string $name
* @property Carbon $created_at
* @property Carbon $updated_at
* @property User $user
* @property Collection<Server> $servers
* @property Collection<NotificationChannel> $notificationChannels
*/
class Project extends Model
{
use HasFactory;

protected $fillable = [
'user_id',
'name',
];

public static function boot(): void
{
parent::boot();

static::deleting(function (Project $project) {
$project->servers()->each(function (Server $server) {
$server->delete();
});
});
}

public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}

public function servers(): HasMany
{
return $this->hasMany(Server::class);
}

public function notificationChannels(): HasMany
{
return $this->hasMany(NotificationChannel::class);
}
}
9 changes: 9 additions & 0 deletions app/Models/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Illuminate\Support\Str;

/**
* @property int $project_id
* @property int $user_id
* @property string $name
* @property string $ssh_user
Expand All @@ -38,6 +39,7 @@
* @property int $security_updates
* @property int $progress
* @property string $progress_step
* @property Project $project
* @property User $creator
* @property ServerProvider $serverProvider
* @property ServerLog[] $logs
Expand All @@ -59,6 +61,7 @@ class Server extends AbstractModel
use HasFactory;

protected $fillable = [
'project_id',
'user_id',
'name',
'ssh_user',
Expand All @@ -82,6 +85,7 @@ class Server extends AbstractModel
];

protected $casts = [
'project_id' => 'integer',
'user_id' => 'integer',
'type_data' => 'json',
'port' => 'integer',
Expand Down Expand Up @@ -125,6 +129,11 @@ public static function boot(): void
});
}

public function project(): BelongsTo
{
return $this->belongsTo(Project::class, 'project_id');
}

public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
Expand Down
Loading

0 comments on commit 10a6bb5

Please sign in to comment.