This commit is contained in:
Saeed Vaziry
2025-05-15 14:23:26 +03:00
parent a81e9b18b7
commit b8ba83949b
47 changed files with 1536 additions and 980 deletions

View File

@ -5,6 +5,7 @@
use App\Models\Project;
use App\Models\User;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
class AddUser
@ -14,6 +15,8 @@ class AddUser
*/
public function add(Project $project, array $input): void
{
Validator::make($input, self::rules($project))->validate();
/** @var User $user */
$user = User::query()->findOrFail($input['user']);

View File

@ -13,6 +13,8 @@ class CreateProject
*/
public function create(User $user, array $input): Project
{
Validator::make($input, self::rules())->validate();
if (isset($input['name'])) {
$input['name'] = strtolower((string) $input['name']);
}

View File

@ -4,21 +4,29 @@
use App\Models\Project;
use App\Models\User;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class DeleteProject
{
public function delete(User $user, Project $project): void
/**
* @param array<string, mixed> $input
*/
public function delete(User $user, Project $project, array $input): void
{
Validator::make($input, [
'name' => 'required',
])->validate();
if ($user->projects()->count() === 1) {
throw ValidationException::withMessages([
'project' => __('Cannot delete the last project.'),
'name' => __('Cannot delete the last project.'),
]);
}
if ($user->current_project_id == $project->id) {
throw ValidationException::withMessages([
'project' => __('Cannot delete your current project.'),
'name' => __('Cannot delete your current project.'),
]);
}

View File

@ -1,8 +1,7 @@
<?php
namespace App\Http\Controllers\Settings;
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Http\RedirectResponse;
@ -27,7 +26,7 @@ class ProfileController extends Controller
#[Get('/', name: 'profile')]
public function edit(Request $request): Response
{
return Inertia::render('settings/profile/index', [
return Inertia::render('profile/index', [
'mustVerifyEmail' => $request->user() instanceof MustVerifyEmail,
'status' => $request->session()->get('status'),
]);

View File

@ -0,0 +1,114 @@
<?php
namespace App\Http\Controllers;
use App\Actions\Projects\AddUser;
use App\Actions\Projects\CreateProject;
use App\Actions\Projects\DeleteProject;
use App\Http\Resources\ProjectResource;
use App\Models\Project;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\URL;
use Inertia\Inertia;
use Inertia\Response;
use Spatie\RouteAttributes\Attributes\Delete;
use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Middleware;
use Spatie\RouteAttributes\Attributes\Post;
use Spatie\RouteAttributes\Attributes\Prefix;
#[Prefix('settings/projects')]
#[Middleware(['auth'])]
class ProjectController extends Controller
{
#[Get('/', name: 'projects')]
public function index(): Response
{
$this->authorize('viewAny', Project::class);
return Inertia::render('projects/index', [
'projects' => ProjectResource::collection(
Project::query()->simplePaginate(config('web.pagination_size'))
),
]);
}
#[Post('/', name: 'projects.store')]
public function store(Request $request): RedirectResponse
{
$this->authorize('create', Project::class);
$project = app(CreateProject::class)->create(user(), $request->all());
user()->update([
'current_project_id' => $project->id,
]);
return redirect()->route('projects')
->with('success', 'Project created successfully.');
}
#[Post('switch/{project}', name: 'projects.switch')]
public function switch(Project $project): RedirectResponse
{
$this->authorize('view', $project);
user()->update([
'current_project_id' => $project->id,
]);
$previousUrl = URL::previous();
$previousRequest = Request::create($previousUrl);
$previousRoute = app('router')->getRoutes()->match($previousRequest);
if (count($previousRoute->parameters()) > 0) {
return redirect()->route('servers');
}
return redirect()->route($previousRoute->getName());
}
#[Post('/{project}/users', name: 'projects.users')]
public function storeUser(Request $request, Project $project): RedirectResponse
{
$this->authorize('update', $project);
app(AddUser::class)->add($project, $request->all());
return redirect()->route('projects')
->with('success', 'User added to project successfully.');
}
#[Delete('{project}/users', name: 'projects.users')]
public function destroyUser(Request $request, Project $project): RedirectResponse
{
$this->authorize('update', $project);
$this->validate($request, [
'user' => [
'required',
'exists:users,id',
],
]);
$user = User::query()->find($request->input('user'));
$project->users()->detach($user);
return redirect()->route('projects')
->with('success', 'User removed from project successfully.');
}
#[Delete('{project}', name: 'projects.destroy')]
public function destroy(Request $request, Project $project): RedirectResponse
{
$this->authorize('delete', $project);
app(DeleteProject::class)->delete(user(), $project, $request->all());
return redirect()->route('projects')
->with('success', 'Project deleted successfully.');
}
}

View File

@ -11,8 +11,8 @@
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Inertia\Inertia;
use Inertia\Response;
use Inertia\ResponseFactory;
use Spatie\RouteAttributes\Attributes\Delete;
use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Middleware;
@ -24,7 +24,7 @@
class ServerController extends Controller
{
#[Get('/', name: 'servers')]
public function index(): Response|ResponseFactory
public function index(): Response
{
$project = user()->currentProject;
@ -32,7 +32,7 @@ public function index(): Response|ResponseFactory
$servers = $project->servers()->simplePaginate(config('web.pagination_size'));
return inertia('servers/index', [
return Inertia::render('servers/index', [
'servers' => ServerResource::collection($servers),
'public_key' => __('servers.create.public_key_text', ['public_key' => get_public_key_content()]),
'server_providers' => ServerProviderResource::collection(ServerProvider::getByProjectId($project->id)->get()),
@ -52,11 +52,11 @@ public function store(Request $request): RedirectResponse
}
#[Get('/{server}', name: 'servers.show')]
public function show(Server $server): Response|ResponseFactory
public function show(Server $server): Response
{
$this->authorize('view', $server);
return inertia('servers/show', [
return Inertia::render('servers/show', [
'server' => ServerResource::make($server),
'logs' => ServerLogResource::collection($server->logs()->latest()->paginate(config('web.pagination_size'))),
]);

View File

@ -1,9 +1,8 @@
<?php
namespace App\Http\Controllers\Settings;
namespace App\Http\Controllers;
use App\Actions\ServerProvider\CreateServerProvider;
use App\Http\Controllers\Controller;
use App\Http\Resources\ServerProviderResource;
use App\Models\ServerProvider;
use Illuminate\Http\JsonResponse;

View File

@ -1,27 +0,0 @@
<?php
namespace App\Http\Controllers\Settings;
use App\Http\Controllers\Controller;
use App\Models\Project;
use Illuminate\Http\RedirectResponse;
use Spatie\RouteAttributes\Attributes\Middleware;
use Spatie\RouteAttributes\Attributes\Post;
use Spatie\RouteAttributes\Attributes\Prefix;
#[Prefix('settings/projects')]
#[Middleware(['auth'])]
class ProjectController extends Controller
{
#[Post('switch/{project}', name: 'projects.switch')]
public function switch(Project $project): RedirectResponse
{
$this->authorize('view', $project);
user()->update([
'current_project_id' => $project->id,
]);
return redirect()->route('servers');
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace App\Http\Controllers;
use App\Http\Resources\UserResource;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;
use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Middleware;
use Spatie\RouteAttributes\Attributes\Prefix;
#[Prefix('users')]
#[Middleware(['auth'])]
class UserController extends Controller
{
#[Get('/', name: 'users')]
public function index(Request $request): ResourceCollection
{
$this->authorize('viewAny', User::class);
$this->validate($request, [
'query' => [
'nullable',
'string',
],
]);
$users = User::query()->where('name', 'like', "%{$request->input('query')}%")
->orWhere('email', 'like', "%{$request->input('query')}%")
->take(10)
->get();
return UserResource::collection($users);
}
}

View File

@ -17,8 +17,11 @@ public function toArray(Request $request): array
return [
'id' => $this->id,
'name' => $this->name,
'users' => UserResource::collection($this->users),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'created_at_by_timezone' => $this->created_at_by_timezone,
'updated_at_by_timezone' => $this->updated_at_by_timezone,
];
}
}

View File

@ -4,6 +4,7 @@
use App\Traits\HasTimezoneTimestamps;
use Carbon\Carbon;
use Database\Factories\ProjectFactory;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -25,7 +26,7 @@
*/
class Project extends Model
{
/** @use HasFactory<\Database\Factories\ProjectFactory> */
/** @use HasFactory<ProjectFactory> */
use HasFactory;
use HasTimezoneTimestamps;