mirror of
https://github.com/vitodeploy/vito.git
synced 2025-07-01 05:56:16 +00:00
API Feature (#334)
This commit is contained in:
@ -7,9 +7,11 @@
|
||||
use App\Models\Service;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Spatie\RouteAttributes\Attributes\Post;
|
||||
|
||||
class AgentController extends Controller
|
||||
{
|
||||
#[Post('api/servers/{server}/agent/{id}', name: 'api.servers.agent')]
|
||||
public function __invoke(Request $request, Server $server, int $id): JsonResponse
|
||||
{
|
||||
$validated = $this->validate($request, [
|
||||
|
97
app/Http/Controllers/API/CronJobController.php
Normal file
97
app/Http/Controllers/API/CronJobController.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\CronJob\CreateCronJob;
|
||||
use App\Actions\CronJob\DeleteCronJob;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\CronJobResource;
|
||||
use App\Models\CronJob;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
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('api/projects/{project}/servers/{server}/cron-jobs')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'cron-jobs')]
|
||||
class CronJobController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.servers.cron-jobs', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all cron jobs.')]
|
||||
#[ResponseFromApiResource(CronJobResource::class, CronJob::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project, Server $server): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', [CronJob::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
return CronJobResource::collection($server->cronJobs()->simplePaginate(25));
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.servers.cron-jobs.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create', description: 'Create a new cron job.')]
|
||||
#[BodyParam(name: 'command', required: true)]
|
||||
#[BodyParam(name: 'user', required: true, enum: ['root', 'vito'])]
|
||||
#[BodyParam(name: 'frequency', description: 'Frequency of the cron job.', required: true, example: '* * * * *')]
|
||||
#[ResponseFromApiResource(CronJobResource::class, CronJob::class)]
|
||||
public function create(Request $request, Project $project, Server $server): CronJobResource
|
||||
{
|
||||
$this->authorize('create', [CronJob::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
$this->validate($request, CreateCronJob::rules($request->all()));
|
||||
|
||||
$cronJob = app(CreateCronJob::class)->create($server, $request->all());
|
||||
|
||||
return new CronJobResource($cronJob);
|
||||
}
|
||||
|
||||
#[Get('{cronJob}', name: 'api.projects.servers.cron-jobs.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show', description: 'Get a cron job by ID.')]
|
||||
#[ResponseFromApiResource(CronJobResource::class, CronJob::class)]
|
||||
public function show(Project $project, Server $server, CronJob $cronJob): CronJobResource
|
||||
{
|
||||
$this->authorize('view', [$cronJob, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $cronJob);
|
||||
|
||||
return new CronJobResource($cronJob);
|
||||
}
|
||||
|
||||
#[Delete('{cronJob}', name: 'api.projects.servers.cron-jobs.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete cron job.')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, Server $server, CronJob $cronJob)
|
||||
{
|
||||
$this->authorize('delete', [$cronJob, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $cronJob);
|
||||
|
||||
app(DeleteCronJob::class)->delete($server, $cronJob);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, Server $server, ?CronJob $cronJob = null): void
|
||||
{
|
||||
if ($project->id !== $server->project_id) {
|
||||
abort(404, 'Server not found in project');
|
||||
}
|
||||
|
||||
if ($cronJob && $cronJob->server_id !== $server->id) {
|
||||
abort(404, 'Firewall rule not found in server');
|
||||
}
|
||||
}
|
||||
}
|
94
app/Http/Controllers/API/DatabaseController.php
Normal file
94
app/Http/Controllers/API/DatabaseController.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\Database\CreateDatabase;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\DatabaseResource;
|
||||
use App\Models\Database;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
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('api/projects/{project}/servers/{server}/databases')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'databases')]
|
||||
class DatabaseController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.servers.databases', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all databases.')]
|
||||
#[ResponseFromApiResource(DatabaseResource::class, Database::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project, Server $server): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', [Database::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
return DatabaseResource::collection($server->databases()->simplePaginate(25));
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.servers.databases.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create', description: 'Create a new database.')]
|
||||
#[BodyParam(name: 'name', required: true)]
|
||||
#[ResponseFromApiResource(DatabaseResource::class, Database::class)]
|
||||
public function create(Request $request, Project $project, Server $server): DatabaseResource
|
||||
{
|
||||
$this->authorize('create', [Database::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
$this->validate($request, CreateDatabase::rules($server, $request->input()));
|
||||
|
||||
$database = app(CreateDatabase::class)->create($server, $request->all());
|
||||
|
||||
return new DatabaseResource($database);
|
||||
}
|
||||
|
||||
#[Get('{database}', name: 'api.projects.servers.databases.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show', description: 'Get a database by ID.')]
|
||||
#[ResponseFromApiResource(DatabaseResource::class, Database::class)]
|
||||
public function show(Project $project, Server $server, Database $database): DatabaseResource
|
||||
{
|
||||
$this->authorize('view', [$database, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $database);
|
||||
|
||||
return new DatabaseResource($database);
|
||||
}
|
||||
|
||||
#[Delete('{database}', name: 'api.projects.servers.databases.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete database.')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, Server $server, Database $database)
|
||||
{
|
||||
$this->authorize('delete', [$database, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $database);
|
||||
|
||||
$database->delete();
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, Server $server, ?Database $database = null): void
|
||||
{
|
||||
if ($project->id !== $server->project_id) {
|
||||
abort(404, 'Server not found in project');
|
||||
}
|
||||
|
||||
if ($database && $database->server_id !== $server->id) {
|
||||
abort(404, 'Database not found in server');
|
||||
}
|
||||
}
|
||||
}
|
114
app/Http/Controllers/API/DatabaseUserController.php
Normal file
114
app/Http/Controllers/API/DatabaseUserController.php
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\Database\CreateDatabaseUser;
|
||||
use App\Actions\Database\LinkUser;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\DatabaseUserResource;
|
||||
use App\Models\DatabaseUser;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
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('api/projects/{project}/servers/{server}/database-users')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'database-users')]
|
||||
class DatabaseUserController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.servers.database-users', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all database users.')]
|
||||
#[ResponseFromApiResource(DatabaseUserResource::class, DatabaseUser::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project, Server $server): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', [DatabaseUser::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
return DatabaseUserResource::collection($server->databaseUsers()->simplePaginate(25));
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.servers.database-users.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create', description: 'Create a new database user.')]
|
||||
#[BodyParam(name: 'username', required: true)]
|
||||
#[BodyParam(name: 'password', required: true)]
|
||||
#[BodyParam(name: 'host', description: 'Host, if it is a remote user.', example: '%')]
|
||||
#[ResponseFromApiResource(DatabaseUserResource::class, DatabaseUser::class)]
|
||||
public function create(Request $request, Project $project, Server $server): DatabaseUserResource
|
||||
{
|
||||
$this->authorize('create', [DatabaseUser::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
$this->validate($request, CreateDatabaseUser::rules($server, $request->input()));
|
||||
|
||||
$databaseUser = app(CreateDatabaseUser::class)->create($server, $request->all());
|
||||
|
||||
return new DatabaseUserResource($databaseUser);
|
||||
}
|
||||
|
||||
#[Get('{databaseUser}', name: 'api.projects.servers.database-users.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show', description: 'Get a database user by ID.')]
|
||||
#[ResponseFromApiResource(DatabaseUserResource::class, DatabaseUser::class)]
|
||||
public function show(Project $project, Server $server, DatabaseUser $databaseUser): DatabaseUserResource
|
||||
{
|
||||
$this->authorize('view', [$databaseUser, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $databaseUser);
|
||||
|
||||
return new DatabaseUserResource($databaseUser);
|
||||
}
|
||||
|
||||
#[Post('{databaseUser}/link', name: 'api.projects.servers.database-users.link', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'link', description: 'Link to databases')]
|
||||
#[BodyParam(name: 'databases', description: 'Array of database names to link to the user.', required: true)]
|
||||
#[ResponseFromApiResource(DatabaseUserResource::class, DatabaseUser::class)]
|
||||
public function link(Request $request, Project $project, Server $server, DatabaseUser $databaseUser): DatabaseUserResource
|
||||
{
|
||||
$this->authorize('update', [$databaseUser, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $databaseUser);
|
||||
|
||||
$this->validate($request, LinkUser::rules($server, $request->all()));
|
||||
|
||||
$databaseUser = app(LinkUser::class)->link($databaseUser, $request->all());
|
||||
|
||||
return new DatabaseUserResource($databaseUser);
|
||||
}
|
||||
|
||||
#[Delete('{databaseUser}', name: 'api.projects.servers.database-users.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete database user.')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, Server $server, DatabaseUser $databaseUser)
|
||||
{
|
||||
$this->authorize('delete', [$databaseUser, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $databaseUser);
|
||||
|
||||
$databaseUser->delete();
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, Server $server, ?DatabaseUser $databaseUser = null): void
|
||||
{
|
||||
if ($project->id !== $server->project_id) {
|
||||
abort(404, 'Server not found in project');
|
||||
}
|
||||
|
||||
if ($databaseUser && $databaseUser->server_id !== $server->id) {
|
||||
abort(404, 'Database user not found in server');
|
||||
}
|
||||
}
|
||||
}
|
99
app/Http/Controllers/API/FirewallRuleController.php
Normal file
99
app/Http/Controllers/API/FirewallRuleController.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\FirewallRule\CreateRule;
|
||||
use App\Actions\FirewallRule\DeleteRule;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\FirewallRuleResource;
|
||||
use App\Models\FirewallRule;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
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('api/projects/{project}/servers/{server}/firewall-rules')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'firewall-rules')]
|
||||
class FirewallRuleController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.servers.firewall-rules', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all firewall rules.')]
|
||||
#[ResponseFromApiResource(FirewallRuleResource::class, FirewallRule::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project, Server $server): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', [FirewallRule::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
return FirewallRuleResource::collection($server->firewallRules()->simplePaginate(25));
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.servers.firewall-rules.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create', description: 'Create a new firewall rule.')]
|
||||
#[BodyParam(name: 'type', required: true, enum: ['allow', 'deny'])]
|
||||
#[BodyParam(name: 'protocol', required: true, enum: ['tcp', 'udp'])]
|
||||
#[BodyParam(name: 'port', required: true)]
|
||||
#[BodyParam(name: 'source', required: true)]
|
||||
#[BodyParam(name: 'mask', description: 'Mask for source IP.', example: '0')]
|
||||
#[ResponseFromApiResource(FirewallRuleResource::class, FirewallRule::class)]
|
||||
public function create(Request $request, Project $project, Server $server): FirewallRuleResource
|
||||
{
|
||||
$this->authorize('create', [FirewallRule::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
$this->validate($request, CreateRule::rules());
|
||||
|
||||
$firewallRule = app(CreateRule::class)->create($server, $request->all());
|
||||
|
||||
return new FirewallRuleResource($firewallRule);
|
||||
}
|
||||
|
||||
#[Get('{firewallRule}', name: 'api.projects.servers.firewall-rules.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show', description: 'Get a firewall rule by ID.')]
|
||||
#[ResponseFromApiResource(FirewallRuleResource::class, FirewallRule::class)]
|
||||
public function show(Project $project, Server $server, FirewallRule $firewallRule): FirewallRuleResource
|
||||
{
|
||||
$this->authorize('view', [$firewallRule, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $firewallRule);
|
||||
|
||||
return new FirewallRuleResource($firewallRule);
|
||||
}
|
||||
|
||||
#[Delete('{firewallRule}', name: 'api.projects.servers.firewall-rules.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete firewall rule.')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, Server $server, FirewallRule $firewallRule)
|
||||
{
|
||||
$this->authorize('delete', [$firewallRule, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $firewallRule);
|
||||
|
||||
app(DeleteRule::class)->delete($server, $firewallRule);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, Server $server, ?FirewallRule $firewallRule = null): void
|
||||
{
|
||||
if ($project->id !== $server->project_id) {
|
||||
abort(404, 'Server not found in project');
|
||||
}
|
||||
|
||||
if ($firewallRule && $firewallRule->server_id !== $server->id) {
|
||||
abort(404, 'Firewall rule not found in server');
|
||||
}
|
||||
}
|
||||
}
|
@ -11,10 +11,12 @@
|
||||
use App\Notifications\SourceControlDisconnected;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Spatie\RouteAttributes\Attributes\Any;
|
||||
use Throwable;
|
||||
|
||||
class GitHookController extends Controller
|
||||
{
|
||||
#[Any('api/git-hooks', name: 'api.git-hooks')]
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
if (! $request->input('secret')) {
|
||||
|
@ -3,9 +3,17 @@
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Unauthenticated;
|
||||
use Spatie\RouteAttributes\Attributes\Get;
|
||||
|
||||
#[Group(name: 'general')]
|
||||
class HealthController extends Controller
|
||||
{
|
||||
#[Get('api/health', name: 'api.health')]
|
||||
#[Unauthenticated]
|
||||
#[Endpoint(title: 'health-check')]
|
||||
public function __invoke()
|
||||
{
|
||||
return response()->json([
|
||||
|
89
app/Http/Controllers/API/ProjectController.php
Normal file
89
app/Http/Controllers/API/ProjectController.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\Projects\CreateProject;
|
||||
use App\Actions\Projects\DeleteProject;
|
||||
use App\Actions\Projects\UpdateProject;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\ProjectResource;
|
||||
use App\Models\Project;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Illuminate\Http\Response;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
use Spatie\RouteAttributes\Attributes\Delete;
|
||||
use Spatie\RouteAttributes\Attributes\Get;
|
||||
use Spatie\RouteAttributes\Attributes\Middleware;
|
||||
use Spatie\RouteAttributes\Attributes\Post;
|
||||
use Spatie\RouteAttributes\Attributes\Put;
|
||||
|
||||
#[Middleware('auth:sanctum')]
|
||||
#[Group(name: 'projects')]
|
||||
class ProjectController extends Controller
|
||||
{
|
||||
#[Get('api/projects', name: 'api.projects.index', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all projects.')]
|
||||
#[ResponseFromApiResource(ProjectResource::class, Project::class, collection: true, paginate: 25)]
|
||||
public function index(): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', Project::class);
|
||||
|
||||
return ProjectResource::collection(Project::all());
|
||||
}
|
||||
|
||||
#[Post('api/projects', name: 'api.projects.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create', description: 'Create a new project.')]
|
||||
#[BodyParam(name: 'name', description: 'The name of the project.', required: true)]
|
||||
#[ResponseFromApiResource(ProjectResource::class, Project::class)]
|
||||
public function create(Request $request): ProjectResource
|
||||
{
|
||||
$this->authorize('create', Project::class);
|
||||
|
||||
$this->validate($request, CreateProject::rules());
|
||||
|
||||
$project = app(CreateProject::class)->create(auth()->user(), $request->all());
|
||||
|
||||
return new ProjectResource($project);
|
||||
}
|
||||
|
||||
#[Get('api/projects/{project}', name: 'api.projects.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show', description: 'Get a project by ID.')]
|
||||
#[ResponseFromApiResource(ProjectResource::class, Project::class)]
|
||||
public function show(Project $project): ProjectResource
|
||||
{
|
||||
$this->authorize('view', $project);
|
||||
|
||||
return new ProjectResource($project);
|
||||
}
|
||||
|
||||
#[Put('api/projects/{project}', name: 'api.projects.update', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'update', description: 'Update project.')]
|
||||
#[BodyParam(name: 'name', description: 'The name of the project.', required: true)]
|
||||
#[ResponseFromApiResource(ProjectResource::class, Project::class)]
|
||||
public function update(Request $request, Project $project): ProjectResource
|
||||
{
|
||||
$this->authorize('update', $project);
|
||||
|
||||
$this->validate($request, UpdateProject::rules($project));
|
||||
|
||||
$project = app(UpdateProject::class)->update($project, $request->all());
|
||||
|
||||
return new ProjectResource($project);
|
||||
}
|
||||
|
||||
#[Delete('api/projects/{project}', name: 'api.projects.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete project.')]
|
||||
#[\Knuckles\Scribe\Attributes\Response(status: 204)]
|
||||
public function delete(Project $project): Response
|
||||
{
|
||||
$this->authorize('delete', $project);
|
||||
|
||||
app(DeleteProject::class)->delete(auth()->user(), $project);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
}
|
131
app/Http/Controllers/API/ServerController.php
Normal file
131
app/Http/Controllers/API/ServerController.php
Normal file
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\Server\CreateServer;
|
||||
use App\Actions\Server\RebootServer;
|
||||
use App\Actions\Server\Update;
|
||||
use App\Enums\Database;
|
||||
use App\Enums\PHP;
|
||||
use App\Enums\ServerProvider;
|
||||
use App\Enums\ServerType;
|
||||
use App\Enums\Webserver;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\ServerResource;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
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('api/projects/{project}/servers')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'servers')]
|
||||
class ServerController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.servers', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all servers in a project.')]
|
||||
#[ResponseFromApiResource(ServerResource::class, Server::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', [Server::class, $project]);
|
||||
|
||||
return ServerResource::collection($project->servers()->simplePaginate(25));
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.servers.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create', description: 'Create a new server.')]
|
||||
#[BodyParam(name: 'provider', description: 'The server provider type', required: true)]
|
||||
#[BodyParam(name: 'server_provider', description: 'If the provider is not custom, the ID of the server provider profile', enum: [ServerProvider::CUSTOM, ServerProvider::HETZNER, ServerProvider::DIGITALOCEAN, ServerProvider::LINODE, ServerProvider::VULTR])]
|
||||
#[BodyParam(name: 'region', description: 'Provider region if the provider is not custom')]
|
||||
#[BodyParam(name: 'plan', description: 'Provider plan if the provider is not custom')]
|
||||
#[BodyParam(name: 'ip', description: 'SSH IP address if the provider is custom')]
|
||||
#[BodyParam(name: 'port', description: 'SSH Port if the provider is custom')]
|
||||
#[BodyParam(name: 'name', description: 'The name of the server.', required: true)]
|
||||
#[BodyParam(name: 'os', description: 'The os of the server', required: true)]
|
||||
#[BodyParam(name: 'type', description: 'Server type', required: true, enum: [ServerType::REGULAR, ServerType::DATABASE])]
|
||||
#[BodyParam(name: 'webserver', description: 'Web server', required: true, enum: [Webserver::NONE, Webserver::NGINX])]
|
||||
#[BodyParam(name: 'database', description: 'Database', required: true, enum: [Database::NONE, Database::MYSQL57, Database::MYSQL80, Database::MARIADB103, Database::MARIADB104, Database::MARIADB103, Database::POSTGRESQL12, Database::POSTGRESQL13, Database::POSTGRESQL14, Database::POSTGRESQL15, Database::POSTGRESQL16], )]
|
||||
#[BodyParam(name: 'php', description: 'PHP version', required: true, enum: [PHP::V70, PHP::V71, PHP::V72, PHP::V73, PHP::V74, PHP::V80, PHP::V81, PHP::V82, PHP::V83])]
|
||||
#[ResponseFromApiResource(ServerResource::class, Server::class)]
|
||||
public function create(Request $request, Project $project): ServerResource
|
||||
{
|
||||
$this->authorize('create', [Server::class, $project]);
|
||||
|
||||
$this->validate($request, CreateServer::rules($project, $request->input()));
|
||||
|
||||
$server = app(CreateServer::class)->create(auth()->user(), $project, $request->all());
|
||||
|
||||
return new ServerResource($server);
|
||||
}
|
||||
|
||||
#[Get('{server}', name: 'api.projects.servers.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show', description: 'Get a server by ID.')]
|
||||
#[ResponseFromApiResource(ServerResource::class, Server::class)]
|
||||
public function show(Project $project, Server $server): ServerResource
|
||||
{
|
||||
$this->authorize('view', [$server, $project]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
return new ServerResource($server);
|
||||
}
|
||||
|
||||
#[Post('{server}/reboot', name: 'api.projects.servers.reboot', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'reboot', description: 'Reboot a server.')]
|
||||
#[Response(status: 204)]
|
||||
public function reboot(Project $project, Server $server)
|
||||
{
|
||||
$this->authorize('update', [$server, $project]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
app(RebootServer::class)->reboot($server);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
#[Post('{server}/upgrade', name: 'api.projects.servers.upgrade', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'upgrade', description: 'Upgrade server.')]
|
||||
#[Response(status: 204)]
|
||||
public function upgrade(Project $project, Server $server)
|
||||
{
|
||||
$this->authorize('update', [$server, $project]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
app(Update::class)->update($server);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
#[Delete('{server}', name: 'api.projects.servers.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete server.')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, Server $server)
|
||||
{
|
||||
$this->authorize('delete', [$server, $project]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
$server->delete();
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, Server $server): void
|
||||
{
|
||||
if ($project->id !== $server->project_id) {
|
||||
abort(404, 'Server not found in project');
|
||||
}
|
||||
}
|
||||
}
|
116
app/Http/Controllers/API/ServerProviderController.php
Normal file
116
app/Http/Controllers/API/ServerProviderController.php
Normal file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\ServerProvider\CreateServerProvider;
|
||||
use App\Actions\ServerProvider\DeleteServerProvider;
|
||||
use App\Actions\ServerProvider\EditServerProvider;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\ServerProviderResource;
|
||||
use App\Models\Project;
|
||||
use App\Models\ServerProvider;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
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;
|
||||
use Spatie\RouteAttributes\Attributes\Put;
|
||||
|
||||
#[Prefix('api/projects/{project}/server-providers')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'server-providers')]
|
||||
class ServerProviderController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.server-providers', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list')]
|
||||
#[ResponseFromApiResource(ServerProviderResource::class, ServerProvider::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', ServerProvider::class);
|
||||
|
||||
$serverProviders = ServerProvider::getByProjectId($project->id)->simplePaginate(25);
|
||||
|
||||
return ServerProviderResource::collection($serverProviders);
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.server-providers.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create')]
|
||||
#[BodyParam(name: 'provider', description: 'The provider (aws, linode, hetzner, digitalocean, vultr, ...)', required: true)]
|
||||
#[BodyParam(name: 'name', description: 'The name of the server provider.', required: true)]
|
||||
#[BodyParam(name: 'token', description: 'The token if provider requires api token')]
|
||||
#[BodyParam(name: 'key', description: 'The key if provider requires key')]
|
||||
#[BodyParam(name: 'secret', description: 'The secret if provider requires key')]
|
||||
#[ResponseFromApiResource(ServerProviderResource::class, ServerProvider::class)]
|
||||
public function create(Request $request, Project $project): ServerProviderResource
|
||||
{
|
||||
$this->authorize('create', ServerProvider::class);
|
||||
|
||||
$this->validate($request, CreateServerProvider::rules($request->all()));
|
||||
|
||||
$serverProvider = app(CreateServerProvider::class)->create(auth()->user(), $project, $request->all());
|
||||
|
||||
return new ServerProviderResource($serverProvider);
|
||||
}
|
||||
|
||||
#[Get('{serverProvider}', name: 'api.projects.server-providers.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show')]
|
||||
#[ResponseFromApiResource(ServerProviderResource::class, ServerProvider::class)]
|
||||
public function show(Project $project, ServerProvider $serverProvider)
|
||||
{
|
||||
$this->authorize('view', $serverProvider);
|
||||
|
||||
$this->validateRoute($project, $serverProvider);
|
||||
|
||||
return new ServerProviderResource($serverProvider);
|
||||
}
|
||||
|
||||
#[Put('{serverProvider}', name: 'api.projects.server-providers.update', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'update')]
|
||||
#[BodyParam(name: 'name', description: 'The name of the server provider.', required: true)]
|
||||
#[BodyParam(name: 'global', description: 'Accessible in all projects', enum: [true, false])]
|
||||
#[ResponseFromApiResource(ServerProviderResource::class, ServerProvider::class)]
|
||||
public function update(Request $request, Project $project, ServerProvider $serverProvider)
|
||||
{
|
||||
$this->authorize('update', $serverProvider);
|
||||
|
||||
$this->validateRoute($project, $serverProvider);
|
||||
|
||||
$this->validate($request, EditServerProvider::rules());
|
||||
|
||||
$serverProvider = app(EditServerProvider::class)->edit($serverProvider, $project, $request->all());
|
||||
|
||||
return new ServerProviderResource($serverProvider);
|
||||
}
|
||||
|
||||
#[Delete('{serverProvider}', name: 'api.projects.server-providers.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, ServerProvider $serverProvider)
|
||||
{
|
||||
$this->authorize('delete', $serverProvider);
|
||||
|
||||
$this->validateRoute($project, $serverProvider);
|
||||
|
||||
app(DeleteServerProvider::class)->delete($serverProvider);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, ServerProvider $serverProvider): void
|
||||
{
|
||||
if (! $serverProvider->project_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($project->id !== $serverProvider->project_id) {
|
||||
abort(404, 'Server provider not found in project');
|
||||
}
|
||||
}
|
||||
}
|
92
app/Http/Controllers/API/ServerSSHKeyController.php
Normal file
92
app/Http/Controllers/API/ServerSSHKeyController.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\SshKey\CreateSshKey;
|
||||
use App\Actions\SshKey\DeleteKeyFromServer;
|
||||
use App\Actions\SshKey\DeployKeyToServer;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\SshKeyResource;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\SshKey;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
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('api/projects/{project}/servers/{server}/ssh-keys')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'ssh-keys')]
|
||||
class ServerSSHKeyController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.servers.ssh-keys', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all ssh keys.')]
|
||||
#[ResponseFromApiResource(SshKeyResource::class, SshKey::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project, Server $server): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAnyServer', [SshKey::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
return SshKeyResource::collection($server->sshKeys()->simplePaginate(25));
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.servers.ssh-keys.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create', description: 'Deploy ssh key to server.')]
|
||||
#[BodyParam(name: 'key_id', description: 'The ID of the key.')]
|
||||
#[BodyParam(name: 'name', description: 'Key name, required if key_id is not provided.')]
|
||||
#[BodyParam(name: 'public_key', description: 'Public Key, required if key_id is not provided.')]
|
||||
#[ResponseFromApiResource(SshKeyResource::class, SshKey::class)]
|
||||
public function create(Request $request, Project $project, Server $server): SshKeyResource
|
||||
{
|
||||
$this->authorize('create', [SshKey::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
$sshKey = null;
|
||||
if ($request->has('key_id')) {
|
||||
$this->validate($request, DeployKeyToServer::rules($request->user(), $server));
|
||||
|
||||
$sshKey = $request->user()->sshKeys()->findOrFail($request->key_id);
|
||||
}
|
||||
|
||||
if (! $sshKey) {
|
||||
$this->validate($request, CreateSshKey::rules());
|
||||
$sshKey = app(CreateSshKey::class)->create($request->user(), $request->all());
|
||||
}
|
||||
|
||||
app(DeployKeyToServer::class)->deploy($server, ['key_id' => $sshKey->id]);
|
||||
|
||||
return new SshKeyResource($sshKey);
|
||||
}
|
||||
|
||||
#[Delete('{sshKey}', name: 'api.projects.servers.ssh-keys.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete ssh key from server.')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, Server $server, SshKey $sshKey)
|
||||
{
|
||||
$this->authorize('delete', [$sshKey, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
app(DeleteKeyFromServer::class)->delete($server, $sshKey);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, Server $server): void
|
||||
{
|
||||
if ($project->id !== $server->project_id) {
|
||||
abort(404, 'Server not found in project');
|
||||
}
|
||||
}
|
||||
}
|
146
app/Http/Controllers/API/ServiceController.php
Normal file
146
app/Http/Controllers/API/ServiceController.php
Normal file
@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\Service\Manage;
|
||||
use App\Actions\Service\Uninstall;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\ServiceResource;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
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('api/projects/{project}/servers/{server}/services')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'services')]
|
||||
class ServiceController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.servers.services', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all services.')]
|
||||
#[ResponseFromApiResource(ServiceResource::class, Service::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project, Server $server): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', [Service::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
return ServiceResource::collection($server->services()->simplePaginate(25));
|
||||
}
|
||||
|
||||
#[Get('{service}', name: 'api.projects.servers.services.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show', description: 'Get a service by ID.')]
|
||||
#[ResponseFromApiResource(ServiceResource::class, Service::class)]
|
||||
public function show(Project $project, Server $server, Service $service): ServiceResource
|
||||
{
|
||||
$this->authorize('view', [$service, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $service);
|
||||
|
||||
return new ServiceResource($service);
|
||||
}
|
||||
|
||||
#[Post('{service}/start', name: 'api.projects.servers.services.start', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'start', description: 'Start service.')]
|
||||
#[Response(status: 204)]
|
||||
public function start(Project $project, Server $server, Service $service): \Illuminate\Http\Response
|
||||
{
|
||||
$this->authorize('update', [$service, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $service);
|
||||
|
||||
app(Manage::class)->start($service);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
#[Post('{service}/stop', name: 'api.projects.servers.services.stop', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'stop', description: 'Stop service.')]
|
||||
#[Response(status: 204)]
|
||||
public function stop(Project $project, Server $server, Service $service): \Illuminate\Http\Response
|
||||
{
|
||||
$this->authorize('update', [$service, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $service);
|
||||
|
||||
app(Manage::class)->stop($service);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
#[Post('{service}/restart', name: 'api.projects.servers.services.restart', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'restart', description: 'Restart service.')]
|
||||
#[Response(status: 204)]
|
||||
public function restart(Project $project, Server $server, Service $service): \Illuminate\Http\Response
|
||||
{
|
||||
$this->authorize('update', [$service, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $service);
|
||||
|
||||
app(Manage::class)->restart($service);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
#[Post('{service}/enable', name: 'api.projects.servers.services.enable', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'enable', description: 'Enable service.')]
|
||||
#[Response(status: 204)]
|
||||
public function enable(Project $project, Server $server, Service $service): \Illuminate\Http\Response
|
||||
{
|
||||
$this->authorize('update', [$service, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $service);
|
||||
|
||||
app(Manage::class)->enable($service);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
#[Post('{service}/disable', name: 'api.projects.servers.services.disable', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'disable', description: 'Disable service.')]
|
||||
#[Response(status: 204)]
|
||||
public function disable(Project $project, Server $server, Service $service): \Illuminate\Http\Response
|
||||
{
|
||||
$this->authorize('update', [$service, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $service);
|
||||
|
||||
app(Manage::class)->disable($service);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
#[Delete('{service}', name: 'api.projects.servers.services.uninstall', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete service.')]
|
||||
#[Response(status: 204)]
|
||||
public function uninstall(Project $project, Server $server, Service $service): \Illuminate\Http\Response
|
||||
{
|
||||
$this->authorize('delete', [$service, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $service);
|
||||
|
||||
app(Uninstall::class)->uninstall($service);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, Server $server, ?Service $service = null): void
|
||||
{
|
||||
if ($project->id !== $server->project_id) {
|
||||
abort(404, 'Server not found in project');
|
||||
}
|
||||
|
||||
if ($service && $service->server_id !== $server->id) {
|
||||
abort(404, 'Service not found in server');
|
||||
}
|
||||
}
|
||||
}
|
105
app/Http/Controllers/API/SiteController.php
Normal file
105
app/Http/Controllers/API/SiteController.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\Site\CreateSite;
|
||||
use App\Enums\SiteType;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\ServerResource;
|
||||
use App\Http\Resources\SiteResource;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\Site;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
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('api/projects/{project}/servers/{server}/sites')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'sites')]
|
||||
class SiteController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.servers.sites', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list', description: 'Get all sites.')]
|
||||
#[ResponseFromApiResource(SiteResource::class, Site::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project, Server $server): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', [Site::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
return SiteResource::collection($server->sites()->simplePaginate(25));
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.servers.sites.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create', description: 'Create a new site.')]
|
||||
#[BodyParam(name: 'type', required: true, enum: [SiteType::PHP, SiteType::PHP_BLANK, SiteType::PHPMYADMIN, SiteType::LARAVEL, SiteType::WORDPRESS])]
|
||||
#[BodyParam(name: 'domain', required: true)]
|
||||
#[BodyParam(name: 'aliases', type: 'array')]
|
||||
#[BodyParam(name: 'php_version', description: 'One of the installed PHP Versions', required: true, example: '7.4')]
|
||||
#[BodyParam(name: 'web_directory', description: 'Required for PHP and Laravel sites', example: 'public')]
|
||||
#[BodyParam(name: 'source_control', description: 'Source control ID, Required for Sites which support source control')]
|
||||
#[BodyParam(name: 'repository', description: 'Repository, Required for Sites which support source control', example: 'organization/repository')]
|
||||
#[BodyParam(name: 'branch', description: 'Branch, Required for Sites which support source control', example: 'main')]
|
||||
#[BodyParam(name: 'composer', type: 'boolean', description: 'Run composer if site supports composer', example: true)]
|
||||
#[BodyParam(name: 'version', description: 'Version, if the site type requires a version like PHPMyAdmin', example: '5.2.1')]
|
||||
#[ResponseFromApiResource(SiteResource::class, Site::class)]
|
||||
public function create(Request $request, Project $project, Server $server): SiteResource
|
||||
{
|
||||
$this->authorize('create', [Site::class, $server]);
|
||||
|
||||
$this->validateRoute($project, $server);
|
||||
|
||||
$this->validate($request, CreateSite::rules($server, $request->input()));
|
||||
|
||||
$site = app(CreateSite::class)->create($server, $request->all());
|
||||
|
||||
return new SiteResource($site);
|
||||
}
|
||||
|
||||
#[Get('{site}', name: 'api.projects.servers.sites.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show', description: 'Get a site by ID.')]
|
||||
#[ResponseFromApiResource(SiteResource::class, Site::class)]
|
||||
public function show(Project $project, Server $server, Site $site): ServerResource
|
||||
{
|
||||
$this->authorize('view', [$site, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $site);
|
||||
|
||||
return new ServerResource($server);
|
||||
}
|
||||
|
||||
#[Delete('{site}', name: 'api.projects.servers.sites.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete', description: 'Delete site.')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, Server $server, Site $site)
|
||||
{
|
||||
$this->authorize('delete', [$site, $server]);
|
||||
|
||||
$this->validateRoute($project, $server, $site);
|
||||
|
||||
$site->delete();
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, Server $server, ?Site $site = null): void
|
||||
{
|
||||
if ($project->id !== $server->project_id) {
|
||||
abort(404, 'Server not found in project');
|
||||
}
|
||||
|
||||
if ($site && $site->server_id !== $server->id) {
|
||||
abort(404, 'Site not found in server');
|
||||
}
|
||||
}
|
||||
}
|
121
app/Http/Controllers/API/SourceControlController.php
Normal file
121
app/Http/Controllers/API/SourceControlController.php
Normal file
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\SourceControl\ConnectSourceControl;
|
||||
use App\Actions\SourceControl\DeleteSourceControl;
|
||||
use App\Actions\SourceControl\EditSourceControl;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\SourceControlResource;
|
||||
use App\Models\Project;
|
||||
use App\Models\SourceControl;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
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;
|
||||
use Spatie\RouteAttributes\Attributes\Put;
|
||||
|
||||
#[Prefix('api/projects/{project}/source-controls')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'source-controls')]
|
||||
class SourceControlController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.source-controls', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list')]
|
||||
#[ResponseFromApiResource(SourceControlResource::class, SourceControl::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', SourceControl::class);
|
||||
|
||||
$sourceControls = SourceControl::getByProjectId($project->id)->simplePaginate(25);
|
||||
|
||||
return SourceControlResource::collection($sourceControls);
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.source-controls.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create')]
|
||||
#[BodyParam(name: 'provider', description: 'The provider', required: true, enum: [\App\Enums\SourceControl::GITLAB, \App\Enums\SourceControl::GITHUB, \App\Enums\SourceControl::BITBUCKET])]
|
||||
#[BodyParam(name: 'name', description: 'The name of the storage provider.', required: true)]
|
||||
#[BodyParam(name: 'token', description: 'The token if provider requires api token')]
|
||||
#[BodyParam(name: 'url', description: 'The URL if the provider is Gitlab and it is self-hosted')]
|
||||
#[BodyParam(name: 'username', description: 'The username if the provider is Bitbucket')]
|
||||
#[BodyParam(name: 'password', description: 'The password if the provider is Bitbucket')]
|
||||
#[ResponseFromApiResource(SourceControlResource::class, SourceControl::class)]
|
||||
public function create(Request $request, Project $project): SourceControlResource
|
||||
{
|
||||
$this->authorize('create', SourceControl::class);
|
||||
|
||||
$this->validate($request, ConnectSourceControl::rules($request->all()));
|
||||
|
||||
$sourceControl = app(ConnectSourceControl::class)->connect(auth()->user(), $project, $request->all());
|
||||
|
||||
return new SourceControlResource($sourceControl);
|
||||
}
|
||||
|
||||
#[Get('{sourceControl}', name: 'api.projects.source-controls.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show')]
|
||||
#[ResponseFromApiResource(SourceControlResource::class, SourceControl::class)]
|
||||
public function show(Project $project, SourceControl $sourceControl)
|
||||
{
|
||||
$this->authorize('view', $sourceControl);
|
||||
|
||||
$this->validateRoute($project, $sourceControl);
|
||||
|
||||
return new SourceControlResource($sourceControl);
|
||||
}
|
||||
|
||||
#[Put('{sourceControl}', name: 'api.projects.source-controls.update', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'update')]
|
||||
#[BodyParam(name: 'name', description: 'The name of the storage provider.', required: true)]
|
||||
#[BodyParam(name: 'token', description: 'The token if provider requires api token')]
|
||||
#[BodyParam(name: 'url', description: 'The URL if the provider is Gitlab and it is self-hosted')]
|
||||
#[BodyParam(name: 'username', description: 'The username if the provider is Bitbucket')]
|
||||
#[BodyParam(name: 'password', description: 'The password if the provider is Bitbucket')]
|
||||
#[BodyParam(name: 'global', description: 'Accessible in all projects', enum: [true, false])]
|
||||
#[ResponseFromApiResource(SourceControlResource::class, SourceControl::class)]
|
||||
public function update(Request $request, Project $project, SourceControl $sourceControl)
|
||||
{
|
||||
$this->authorize('update', $sourceControl);
|
||||
|
||||
$this->validateRoute($project, $sourceControl);
|
||||
|
||||
$this->validate($request, EditSourceControl::rules($sourceControl, $request->all()));
|
||||
|
||||
$sourceControl = app(EditSourceControl::class)->edit($sourceControl, $project, $request->all());
|
||||
|
||||
return new SourceControlResource($sourceControl);
|
||||
}
|
||||
|
||||
#[Delete('{sourceControl}', name: 'api.projects.source-controls.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, SourceControl $sourceControl)
|
||||
{
|
||||
$this->authorize('delete', $sourceControl);
|
||||
|
||||
$this->validateRoute($project, $sourceControl);
|
||||
|
||||
app(DeleteSourceControl::class)->delete($sourceControl);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, SourceControl $sourceControl): void
|
||||
{
|
||||
if (! $sourceControl->project_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($project->id !== $sourceControl->project_id) {
|
||||
abort(404, 'Source Control not found in project');
|
||||
}
|
||||
}
|
||||
}
|
116
app/Http/Controllers/API/StorageProviderController.php
Normal file
116
app/Http/Controllers/API/StorageProviderController.php
Normal file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Actions\StorageProvider\CreateStorageProvider;
|
||||
use App\Actions\StorageProvider\DeleteStorageProvider;
|
||||
use App\Actions\StorageProvider\EditStorageProvider;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\StorageProviderResource;
|
||||
use App\Models\Project;
|
||||
use App\Models\StorageProvider;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Knuckles\Scribe\Attributes\BodyParam;
|
||||
use Knuckles\Scribe\Attributes\Endpoint;
|
||||
use Knuckles\Scribe\Attributes\Group;
|
||||
use Knuckles\Scribe\Attributes\Response;
|
||||
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
|
||||
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;
|
||||
use Spatie\RouteAttributes\Attributes\Put;
|
||||
|
||||
#[Prefix('api/projects/{project}/storage-providers')]
|
||||
#[Middleware(['auth:sanctum', 'can-see-project'])]
|
||||
#[Group(name: 'storage-providers')]
|
||||
class StorageProviderController extends Controller
|
||||
{
|
||||
#[Get('/', name: 'api.projects.storage-providers', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'list')]
|
||||
#[ResponseFromApiResource(StorageProviderResource::class, StorageProvider::class, collection: true, paginate: 25)]
|
||||
public function index(Project $project): ResourceCollection
|
||||
{
|
||||
$this->authorize('viewAny', StorageProvider::class);
|
||||
|
||||
$storageProviders = StorageProvider::getByProjectId($project->id)->simplePaginate(25);
|
||||
|
||||
return StorageProviderResource::collection($storageProviders);
|
||||
}
|
||||
|
||||
#[Post('/', name: 'api.projects.storage-providers.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create')]
|
||||
#[BodyParam(name: 'provider', description: 'The provider (aws, linode, hetzner, digitalocean, vultr, ...)', required: true)]
|
||||
#[BodyParam(name: 'name', description: 'The name of the storage provider.', required: true)]
|
||||
#[BodyParam(name: 'token', description: 'The token if provider requires api token')]
|
||||
#[BodyParam(name: 'key', description: 'The key if provider requires key')]
|
||||
#[BodyParam(name: 'secret', description: 'The secret if provider requires key')]
|
||||
#[ResponseFromApiResource(StorageProviderResource::class, StorageProvider::class)]
|
||||
public function create(Request $request, Project $project): StorageProviderResource
|
||||
{
|
||||
$this->authorize('create', StorageProvider::class);
|
||||
|
||||
$this->validate($request, CreateStorageProvider::rules($request->all()));
|
||||
|
||||
$storageProvider = app(CreateStorageProvider::class)->create(auth()->user(), $project, $request->all());
|
||||
|
||||
return new StorageProviderResource($storageProvider);
|
||||
}
|
||||
|
||||
#[Get('{storageProvider}', name: 'api.projects.storage-providers.show', middleware: 'ability:read')]
|
||||
#[Endpoint(title: 'show')]
|
||||
#[ResponseFromApiResource(StorageProviderResource::class, StorageProvider::class)]
|
||||
public function show(Project $project, StorageProvider $storageProvider)
|
||||
{
|
||||
$this->authorize('view', $storageProvider);
|
||||
|
||||
$this->validateRoute($project, $storageProvider);
|
||||
|
||||
return new StorageProviderResource($storageProvider);
|
||||
}
|
||||
|
||||
#[Put('{storageProvider}', name: 'api.projects.storage-providers.update', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'update')]
|
||||
#[BodyParam(name: 'name', description: 'The name of the storage provider.', required: true)]
|
||||
#[BodyParam(name: 'global', description: 'Accessible in all projects', enum: [true, false])]
|
||||
#[ResponseFromApiResource(StorageProviderResource::class, StorageProvider::class)]
|
||||
public function update(Request $request, Project $project, StorageProvider $storageProvider)
|
||||
{
|
||||
$this->authorize('update', $storageProvider);
|
||||
|
||||
$this->validateRoute($project, $storageProvider);
|
||||
|
||||
$this->validate($request, EditStorageProvider::rules());
|
||||
|
||||
$storageProvider = app(EditStorageProvider::class)->edit($storageProvider, $project, $request->all());
|
||||
|
||||
return new StorageProviderResource($storageProvider);
|
||||
}
|
||||
|
||||
#[Delete('{storageProvider}', name: 'api.projects.storage-providers.delete', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'delete')]
|
||||
#[Response(status: 204)]
|
||||
public function delete(Project $project, StorageProvider $storageProvider)
|
||||
{
|
||||
$this->authorize('delete', $storageProvider);
|
||||
|
||||
$this->validateRoute($project, $storageProvider);
|
||||
|
||||
app(DeleteStorageProvider::class)->delete($storageProvider);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function validateRoute(Project $project, StorageProvider $storageProvider): void
|
||||
{
|
||||
if (! $storageProvider->project_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($project->id !== $storageProvider->project_id) {
|
||||
abort(404, 'Storage provider not found in project');
|
||||
}
|
||||
}
|
||||
}
|
@ -5,9 +5,13 @@
|
||||
use App\Models\Server;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Spatie\RouteAttributes\Attributes\Middleware;
|
||||
use Spatie\RouteAttributes\Attributes\Post;
|
||||
|
||||
#[Middleware('auth')]
|
||||
class ConsoleController extends Controller
|
||||
{
|
||||
#[Post('/{server}/console', name: 'servers.console.run')]
|
||||
public function run(Server $server, Request $request)
|
||||
{
|
||||
$this->authorize('update', $server);
|
||||
|
Reference in New Issue
Block a user