This commit is contained in:
Saeed Vaziry
2025-02-20 18:00:13 +01:00
parent 8c7c3d2192
commit a1cf09e35d
20 changed files with 550 additions and 9 deletions

View File

@ -0,0 +1,32 @@
<?php
namespace App\Cli\Commands;
use App\Models\User;
use Illuminate\Console\Command;
use function Laravel\Prompts\error;
abstract class AbstractCommand extends Command
{
private User|null $user = null;
public function user()
{
if ($this->user) {
return $this->user->refresh();
}
/** @var User $user */
$user = User::query()->first();
if (!$user) {
error('The application is not setup');
exit(1);
}
$this->user = $user;
return $user;
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace App\Cli\Commands;
use Illuminate\Console\Command;
class InfoCommand extends Command
{
protected $signature = 'info';
protected $description = 'Show the application information';
public function handle(): void
{
$this->info('Version: '.config('app.version'));
$this->info('Timezone: '.config('app.timezone'));
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Cli\Commands\Projects;
use App\Cli\Commands\AbstractCommand;
use App\Models\Project;
use function Laravel\Prompts\table;
class ProjectsListCommand extends AbstractCommand
{
protected $signature = 'projects:list';
protected $description = 'Show projects list';
public function handle(): void
{
$projects = Project::all();
table(
headers: ['ID', 'Name', 'Selected'],
rows: $projects->map(fn (Project $project) => [
$project->id,
$project->name,
$project->id === $this->user()->current_project_id ? 'Yes' : 'No',
])->toArray(),
);
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace App\Cli\Commands\Projects;
use App\Cli\Commands\AbstractCommand;
use App\Models\Project;
use function Laravel\Prompts\error;
use function Laravel\Prompts\info;
class ProjectsSelectCommand extends AbstractCommand
{
protected $signature = 'projects:select {project}';
protected $description = 'Select a project';
public function handle(): void
{
$project = Project::query()->find($this->argument('project'));
if (! $project) {
error('The project does not exist');
return;
}
$this->user()->update([
'current_project_id' => $project->id,
]);
info(__('The project [:project] has been selected' , [
'project' => $project->name,
]));
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Cli\Commands\ServerProviders;
use App\Cli\Commands\AbstractCommand;
use function Laravel\Prompts\select;
use function Laravel\Prompts\text;
class ServerProvidersCreateCommand extends AbstractCommand
{
protected $signature = 'server-providers:create';
protected $description = 'Create a new server provider';
public function handle(): void
{
$provider = select(
label: 'What is the server provider?',
options: collect(config('core.server_providers'))
->filter(fn($provider) => $provider != \App\Enums\ServerProvider::CUSTOM)
->mapWithKeys(fn($provider) => [$provider => $provider]),
);
$profile = text(
label: 'What should we call this provider profile?',
required: true,
);
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\Cli\Commands\ServerProviders;
use App\Cli\Commands\AbstractCommand;
use App\Models\Project;
use App\Models\Server;
use App\Models\ServerProvider;
use function Laravel\Prompts\table;
class ServerProvidersListCommand extends AbstractCommand
{
protected $signature = 'server-providers:list';
protected $description = 'Show server providers list';
public function handle(): void
{
$providers = $this->user()->serverProviders;
table(
headers: ['ID', 'Provider', 'Name', 'Created At'],
rows: $providers->map(fn (ServerProvider $provider) => [
$provider->id,
$provider->provider,
$provider->profile,
$provider->created_at_by_timezone,
])->toArray(),
);
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Cli\Commands\Servers;
use App\Cli\Commands\AbstractCommand;
use function Laravel\Prompts\select;
use function Laravel\Prompts\text;
class ServersCreateCommand extends AbstractCommand
{
protected $signature = 'servers:create';
protected $description = 'Create a new server';
public function handle(): void
{
$name = text(
label: 'What is the server name?',
required: true,
);
$os = select(
label: 'What is the server OS?',
options: collect(config('core.operating_systems'))
->mapWithKeys(fn($value) => [$value => $value]),
);
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace App\Cli\Commands\Servers;
use App\Cli\Commands\AbstractCommand;
use App\Models\Project;
use App\Models\Server;
use function Laravel\Prompts\table;
class ServersListCommand extends AbstractCommand
{
protected $signature = 'servers:list';
protected $description = 'Show servers list';
public function handle(): void
{
$servers = $this->user()->currentProject->servers;
table(
headers: ['ID', 'Name', 'IP', 'Provider', 'OS', 'Status', 'Created At'],
rows: $servers->map(fn (Server $server) => [
$server->id,
$server->name,
$server->ip,
$server->provider,
$server->os,
$server->status,
$server->created_at_by_timezone,
])->toArray(),
);
}
}

View File

@ -0,0 +1,81 @@
<?php
namespace App\Cli\Commands;
use App\Models\Project;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use function Laravel\Prompts\text;
use function Laravel\Prompts\info;
class SetupCommand extends Command
{
protected $signature = 'setup';
protected $description = 'Setup the application';
public function handle(): void
{
$this->prepareStorage();
$this->migrate();
$this->makeUser();
$this->makeProject();
info('The application has been setup');
}
private function prepareStorage(): void
{
File::ensureDirectoryExists(storage_path());
}
private function migrate(): void
{
$this->call('migrate', ['--force' => true]);
}
private function makeUser(): void
{
$user = User::query()->first();
if ($user) {
return;
}
$name = text(
label: 'What is your name?',
required: true,
);
$email = text(
label: 'What is your email?',
required: true,
);
User::query()->create([
'name' => $name,
'email' => $email,
'password' => bcrypt(str()->random(16)),
]);
}
private function makeProject(): void
{
$project = Project::query()->first();
if ($project) {
return;
}
$project = Project::query()->create([
'name' => 'default',
]);
$user = User::query()->first();
$user->update([
'current_project_id' => $project->id,
]);
}
}

43
app/Cli/Kernel.php Normal file
View File

@ -0,0 +1,43 @@
<?php
namespace App\Cli;
use Illuminate\Console\Application as Artisan;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\Console\Migrations\InstallCommand;
use Illuminate\Database\Console\Migrations\MigrateCommand;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
protected $commands = [
'command.migrate',
'command.migrate.install'
];
/**
* Register the commands for the application.
*/
protected function commands(): void
{
$this->load(__DIR__ . '/Commands');
$this->app->singleton('command.migrate', function ($app) {
return new MigrateCommand($app['migrator'], $app[Dispatcher::class]);
});
$this->app->singleton('command.migrate.install', function ($app) {
return new InstallCommand($app['migration.repository']);
});
}
protected function shouldDiscoverCommands(): false
{
return false;
}
protected function getArtisan(): ?Artisan
{
return $this->artisan = (new Artisan($this->app, $this->events, $this->app->version()))
->resolveCommands($this->commands);
}
}

View File

@ -18,6 +18,9 @@ class AppServiceProvider extends ServiceProvider
*/
public function register(): void
{
if ($this->app->runningInConsole()) {
return;
}
Fortify::ignoreRoutes();
}
@ -36,6 +39,8 @@ public function boot(): void
return new FTP;
});
Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
if (! $this->app->runningInConsole()) {
Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
}
}
}

View File

@ -23,6 +23,9 @@ class RouteServiceProvider extends ServiceProvider
*/
public function boot(): void
{
if ($this->app->runningInConsole()) {
return;
}
$this->configureRateLimiting();
}

View File

@ -38,11 +38,18 @@ class WebServiceProvider extends ServiceProvider
*/
public function register(): void
{
if ($this->app->runningInConsole()) {
return;
}
Filament::registerPanel($this->panel(Panel::make()));
}
public function boot(): void
{
if ($this->app->runningInConsole()) {
return;
}
FilamentView::registerRenderHook(
PanelsRenderHook::SIDEBAR_NAV_START,
fn () => Livewire::mount(SelectProject::class)