mediaCacheService = $mediaCacheService; $this->artisan = $artisan; $this->dotenvEditor = $dotenvEditor; $this->hash = $hash; $this->db = $db; $this->settingRepository = $settingRepository; } public function handle(): void { $this->comment('Attempting to install or upgrade Koel.'); $this->comment('Remember, you can always install/upgrade manually following the guide here:'); $this->info('📙 '.config('koel.misc.docs_url').PHP_EOL); $this->maybeGenerateAppKey(); $this->maybeGenerateJwtSecret(); $this->maybeSetUpDatabase(); $this->migrateDatabase(); $this->maybeSeedDatabase(); $this->maybeSetMediaPath(); $this->compileFrontEndAssets(); $this->comment(PHP_EOL.'🎆 Success! Koel can now be run from localhost with `php artisan serve`.'); if ($this->settingRepository->getMediaPath()) { $this->comment('You can also scan for media with `php artisan koel:sync`.'); } $this->comment('Again, for more configuration guidance, refer to'); $this->info('📙 '.config('koel.misc.docs_url')); $this->comment('or open the .env file in the root installation folder.'); $this->comment('Thanks for using Koel. You rock!'); } /** * Prompt user for valid database credentials and set up the database. */ private function setUpDatabase(): void { $config = [ 'DB_CONNECTION' => '', 'DB_HOST' => '', 'DB_PORT' => '', 'DB_DATABASE' => '', 'DB_USERNAME' => '', 'DB_PASSWORD' => '', ]; $config['DB_CONNECTION'] = $this->choice( 'Your DB driver of choice', [ 'mysql' => 'MySQL/MariaDB', 'pgsql' => 'PostgreSQL', 'sqlsrv' => 'SQL Server', 'sqlite-e2e' => 'SQLite', ], 'mysql' ); if ($config['DB_CONNECTION'] === 'sqlite-e2e') { $config['DB_DATABASE'] = $this->ask('Absolute path to the DB file'); } else { $config['DB_HOST'] = $this->anticipate('DB host', ['127.0.0.1', 'localhost']); $config['DB_PORT'] = (string) $this->ask('DB port (leave empty for default)', false); $config['DB_DATABASE'] = $this->anticipate('DB name', ['koel']); $config['DB_USERNAME'] = $this->anticipate('DB user', ['koel']); $config['DB_PASSWORD'] = (string) $this->ask('DB password', false); } foreach ($config as $key => $value) { $this->dotenvEditor->setKey($key, $value); } $this->dotenvEditor->save(); // Set the config so that the next DB attempt uses refreshed credentials config([ 'database.default' => $config['DB_CONNECTION'], "database.connections.{$config['DB_CONNECTION']}.host" => $config['DB_HOST'], "database.connections.{$config['DB_CONNECTION']}.port" => $config['DB_PORT'], "database.connections.{$config['DB_CONNECTION']}.database" => $config['DB_DATABASE'], "database.connections.{$config['DB_CONNECTION']}.username" => $config['DB_USERNAME'], "database.connections.{$config['DB_CONNECTION']}.password" => $config['DB_PASSWORD'], ]); } private function setUpAdminAccount(): void { $this->info("Let's create the admin account."); $name = $this->ask('Your name'); $email = $this->ask('Your email address'); $passwordConfirmed = false; $password = null; while (!$passwordConfirmed) { $password = $this->secret('Your desired password'); $confirmation = $this->secret('Again, just to make sure'); if ($confirmation !== $password) { $this->error('That doesn\'t match. Let\'s try again.'); } else { $passwordConfirmed = true; } } User::create([ 'name' => $name, 'email' => $email, 'password' => $this->hash->make($password), 'is_admin' => true, ]); } private function maybeSetMediaPath(): void { if ($this->settingRepository->getMediaPath()) { return; } $this->info('The absolute path to your media directory. If this is skipped (left blank) now, you can set it later via the web interface.'); while (true) { $path = $this->ask('Media path', false); if ($path === false) { return; } if (is_dir($path) && is_readable($path)) { Setting::set('media_path', $path); return; } $this->error('The path does not exist or not readable. Try again.'); } } private function maybeGenerateAppKey(): void { if (!config('app.key')) { $this->info('Generating app key'); $this->artisan->call('key:generate'); } else { $this->comment('App key exists -- skipping'); } } private function maybeGenerateJwtSecret(): void { if (!config('jwt.secret')) { $this->info('Generating JWT secret'); $this->artisan->call('koel:generate-jwt-secret'); } else { $this->comment('JWT secret exists -- skipping'); } } private function maybeSeedDatabase(): void { if (!User::count()) { $this->setUpAdminAccount(); $this->info('Seeding initial data'); $this->artisan->call('db:seed', ['--force' => true]); } else { $this->comment('Data seeded -- skipping'); } } private function maybeSetUpDatabase(): void { $dbSetUp = false; while (!$dbSetUp) { try { // Make sure the config cache is cleared before another attempt. $this->artisan->call('config:clear'); $this->db->reconnect()->getPdo(); $dbSetUp = true; } catch (Exception $e) { $this->error($e->getMessage()); $this->warn(PHP_EOL.'Koel cannot connect to the database. Let\'s set it up.'); $this->setUpDatabase(); } } } private function migrateDatabase(): void { $this->info('Migrating database'); $this->artisan->call('migrate', ['--force' => true]); // Clear the media cache, just in case we did any media-related migration $this->mediaCacheService->clear(); } private function compileFrontEndAssets(): void { $this->info('Compiling front-end stuff'); system('yarn install'); } }