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/261-getting-started/918-setting-up-email'); $this->info('Setting up cronjob & queue worker: https://docs.ploi-core.io/261-getting-started/638-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($token) { $response = Http::withHeaders([ 'Accept' => 'application/json', 'Content-Type' => 'application/json' ]) ->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)); $this->company = $this->getCompany($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); $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('*---------------------------------------------------------------------------*'); } }