Files
ploi-core/app/Console/Commands/Core/Install.php
Dennis 2a3d9cabd0 wip
2022-04-09 07:54:47 +02:00

415 lines
13 KiB
PHP

<?php
namespace App\Console\Commands\Core;
use Exception;
use App\Models\User;
use RuntimeException;
use App\Models\Package;
use App\Services\Ploi\Ploi;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Console\Command;
use App\Services\VersionChecker;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Artisan;
class Install extends Command
{
protected $company;
protected $signature = 'core:install {--force}';
protected $description = 'Installation command for Ploi Core';
protected $versionChecker;
protected $installationFile = 'app/installation';
public function handle()
{
$this->init();
$this->intro();
$this->isInstalled();
$this->checkApplicationKey();
$this->checkDatabaseConnection();
$this->runDatabaseMigrations();
$this->checkCredentials();
$this->askAboutAdministrationAccount();
$this->askAboutDefaultPackages();
$this->checkApplicationUrl();
$this->createInstallationFile();
$this->linkStorage();
$this->info('Success! Installation has finished.');
$this->line(' ');
$this->writeSeparationLine();
$this->info('Make sure to also setup emailing, the cronjob and the queue worker.');
$this->line(' ');
$this->info('Setting up emailing: https://docs.ploi-core.io/getting-started/setting-up-email');
$this->info('Setting up cronjob & queue worker: https://docs.ploi-core.io/getting-started/installation');
$this->writeSeparationLine();
$this->line(' ');
$this->info('Visit your platform at ' . env('APP_URL'));
}
protected function init()
{
$this->versionChecker = (new VersionChecker)->getVersions();
}
protected function askAboutAdministrationAccount()
{
if (!User::query()->where('role', User::ADMIN)->count()) {
$this->info('Let\'s start by setting up your administration account.');
$name = $this->ask('What is your name', $this->company['user_name']);
$email = $this->ask('What is your e-mail address', $this->company['email']);
$password = $this->secret('What password do you desire');
$check = User::where('email', $email)->count();
if ($check) {
$this->line('');
$this->comment('This user is already present in your system, please refresh your database or use different credentials.');
$this->comment('Aborting installation..');
exit();
}
User::forceCreate([
'name' => $name,
'email' => $email,
'password' => $password,
'role' => User::ADMIN
]);
} else {
$this->line('Already found a administrator user in your system. Use that user to login.');
}
}
protected function askAboutDefaultPackages()
{
$basicPackages = $this->confirm(
'Do you want to create the basic packages which you can edit later?',
true
);
if (!$basicPackages) {
return false;
}
Package::create([
'name' => 'Basic',
'maximum_sites' => 5,
'site_permissions' => [
'create' => true,
'update' => true,
'delete' => true
],
'server_permissions' => [
'create' => false,
'update' => false,
'delete' => false
]
]);
Package::create([
'name' => 'Professional',
'maximum_sites' => 30,
'site_permissions' => [
'create' => true,
'update' => true,
'delete' => true
],
'server_permissions' => [
'create' => false,
'update' => false,
'delete' => false
]
]);
Package::create([
'name' => 'Unlimited',
'maximum_sites' => 0,
'site_permissions' => [
'create' => true,
'update' => true,
'delete' => true
],
'server_permissions' => [
'create' => false,
'update' => false,
'delete' => false
]
]);
}
protected function getCompany($ploiCoreKey, $token)
{
$response = Http::withHeaders([
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'X-Ploi-Core-Key' => $ploiCoreKey
])
->withToken($token)
->get((new Ploi)->url . 'ping');
if (!$response->ok() || !$response->json()) {
return [
'error' => Arr::get($response->json(), 'message', 'An unknown error has occurred.')
];
}
return $response->json();
}
protected function getInstallationPayload()
{
return [
'installed_at' => now()
];
}
protected function intro()
{
$this->writeSeparationLine();
$this->line('Ploi Core Installation');
$this->line('Ploi Core version: ' . $this->versionChecker->currentVersion);
$this->line('Ploi Core remote: ' . $this->versionChecker->remoteVersion);
$this->line('Laravel version: ' . app()->version());
$this->line('PHP version: ' . trim(phpversion()));
$this->line(' ');
$this->line('Website: https://ploi-core.io');
$this->line('E-mail: core@ploi.io');
$this->line('Terms of service: https://ploi-core.io/terms');
$this->writeSeparationLine();
$this->line('');
}
protected function isInstalled()
{
if (file_exists(storage_path($this->installationFile)) && !$this->option('force')) {
$this->line('');
$this->comment('Ploi Core has already been installed before.');
$this->comment('If you still want to start installation, remove this file to continue: ./storage/' . $this->installationFile);
$this->comment('Aborting installation..');
exit();
}
return false;
}
protected function checkApplicationKey(): void
{
if (!config('app.key')) {
$this->call('key:generate');
$this->info('Application key has been set');
}
}
protected function checkApplicationUrl()
{
// Ask about URL
$url = $this->ask('What URL is this platform using?', env('APP_URL'));
$this->writeToEnvironmentFile('APP_URL', $url);
}
protected function createInstallationFile()
{
file_put_contents(storage_path($this->installationFile), json_encode($this->getInstallationPayload(), JSON_PRETTY_PRINT));
}
protected function linkStorage()
{
Artisan::call('storage:link');
}
protected function createDatabaseCredentials(): bool
{
$storeCredentials = $this->confirm(
'Unable to connect to your database. Would you like to enter your credentials now?',
true
);
if (!$storeCredentials) {
return false;
}
$connection = $this->choice('Type', ['mysql', 'pgsql'], 0);
$variables = [
'DB_CONNECTION' => $connection,
'DB_HOST' => $this->anticipate(
'Host',
['127.0.0.1', 'localhost'],
config("database.connections.{$connection}.host", '127.0.0.1')
),
'DB_PORT' => $this->ask(
'Port',
config("database.connections.{$connection}.port", '3306')
),
'DB_DATABASE' => $this->ask(
'Database',
config("database.connections.{$connection}.database")
),
'DB_USERNAME' => $this->ask(
'Username',
config("database.connections.{$connection}.username")
),
'DB_PASSWORD' => $this->secret(
'Password',
config("database.connections.{$connection}.password")
),
];
$this->persistVariables($variables);
return true;
}
protected function checkCredentials()
{
do {
$ploiApiToken = $this->ask('Enter the Ploi API token', env('PLOI_TOKEN'));
} while (empty($ploiApiToken));
do {
$ploiCoreKey = $this->ask('Enter the Ploi Core key', env('PLOI_CORE_TOKEN'));
} while (empty($ploiCoreKey));
$this->company = $this->getCompany($ploiCoreKey, $ploiApiToken);
if (!$this->company) {
$this->error('Could not authenticate with ploi.io, please retry by running this command again.');
exit();
}
if (isset($this->company['error'])) {
$this->error($this->company['error']);
exit();
}
if ($this->company['user']['subscription'] !== 'unlimited') {
$this->error('Your subscription does not cover the usage of Ploi Core. Please upgrade your subscription to Unlimited.');
exit();
}
$this->writeToEnvironmentFile('PLOI_TOKEN', $ploiApiToken);
$this->writeToEnvironmentFile('PLOI_CORE_TOKEN', $ploiCoreKey);
$name = $this->ask('What is the name of your company? (Press enter to keep the name here)', $this->company['name']);
$this->writeToEnvironmentFile('APP_NAME', $name);
setting(['name' => $name]);
}
protected function runDatabaseMigrations()
{
$this->info('Running database migrations..');
$this->call('migrate', ['--force' => true]);
$this->info('Database migrations successful');
}
protected function checkDatabaseConnection(): void
{
try {
DB::connection()->getPdo();
$this->info('Database connection successful.');
} catch (Exception $e) {
try {
if (!$this->createDatabaseCredentials()) {
$this->error('A database connection could not be established. Please update your configuration and try again.');
$this->printDatabaseConfig();
exit();
}
} catch (RuntimeException $e) {
$this->error('Failed to persist environment configuration.');
exit();
}
$this->checkDatabaseConnection();
}
}
protected function printDatabaseConfig(): void
{
$connection = config('database.default');
$this->line('');
$this->info('Database Configuration:');
$this->line("- Connection: {$connection}");
$this->line('- Host: ' . config("database.connections.{$connection}.host"));
$this->line('- Port: ' . config("database.connections.{$connection}.port"));
$this->line('- Database: ' . config("database.connections.{$connection}.database"));
$this->line('- Username: ' . config("database.connections.{$connection}.username"));
$this->line('- Password: ' . config("database.connections.{$connection}.password"));
}
protected function persistVariables(array $connectionData): void
{
$connection = $connectionData['DB_CONNECTION'];
$configMap = [
'DB_CONNECTION' => "database.default",
'DB_HOST' => "database.connections.{$connection}.host",
'DB_PORT' => "database.connections.{$connection}.port",
'DB_DATABASE' => "database.connections.{$connection}.database",
'DB_USERNAME' => "database.connections.{$connection}.username",
'DB_PASSWORD' => "database.connections.{$connection}.password",
];
foreach ($connectionData as $envKey => $value) {
$this->writeToEnvironmentFile($envKey, $value);
$this->writeToConfig($configMap[$envKey], $value);
}
DB::purge($this->laravel['config']['database.default']);
}
protected function writeToEnvironmentFile(string $key, ?string $value): void
{
file_put_contents($this->laravel->environmentFilePath(), preg_replace(
$this->keyReplacementPattern($key),
"{$key}=\"{$value}\"",
file_get_contents($this->laravel->environmentFilePath())
));
if (!$this->checkEnvValuePresent($key, $value)) {
throw new RuntimeException("Failed to persist environment variable value. {$key}={$value}");
}
}
protected function checkEnvValuePresent(string $key, ?string $value): bool
{
$envContents = file_get_contents($this->laravel->environmentFilePath());
$needle = "{$key}=\"{$value}\"";
return Str::contains($envContents, $needle);
}
protected function keyReplacementPattern(string $key): string
{
return "/^{$key}.*/m";
}
protected function writeToConfig(string $key, ?string $value): void
{
$this->laravel['config'][$key] = $value;
}
protected function writeSeparationLine()
{
$this->info('*---------------------------------------------------------------------------*');
}
}