load(); // Check if the created .env file is writable to continue install process if (is_really_writable(ROOTPATH . '.env')) { try { $dotenv->required(['app.baseURL', 'app.adminGateway', 'app.authGateway']); } catch (ValidationException $e) { // form to input instance configuration return $this->instanceConfig(); } try { $dotenv->required([ 'database.default.hostname', 'database.default.database', 'database.default.username', 'database.default.password', 'database.default.DBPrefix', ]); } catch (ValidationException $validationException) { return $this->databaseConfig(); } try { $dotenv->required('cache.handler'); } catch (ValidationException) { return $this->cacheConfig(); } } else { try { $dotenv->required([ 'app.baseURL', 'app.adminGateway', 'app.authGateway', 'database.default.hostname', 'database.default.database', 'database.default.username', 'database.default.password', 'database.default.DBPrefix', 'cache.handler', ]); } catch (ValidationException) { return view('install/manual_config'); } } try { $db = db_connect(); // Check if superadmin has been created, meaning migrations and seeds have passed if ( $db->tableExists('users') && (new UserModel())->countAll() > 0 ) { // if so, show a 404 page throw PageNotFoundException::forPageNotFound(); } } catch (DatabaseException) { // Could not connect to the database // show database config view to fix value session() ->setFlashdata('error', lang('Install.messages.databaseConnectError'),); return view('install/database_config'); } // migrate if no user has been created $this->migrate(); // Check if all seeds have succeeded $this->seed(); return $this->createSuperAdmin(); } public function instanceConfig(): string { return view('install/instance_config'); } public function attemptInstanceConfig(): RedirectResponse { $rules = [ 'hostname' => 'required|validate_url', 'media_base_url' => 'permit_empty|validate_url', 'admin_gateway' => 'required', 'auth_gateway' => 'required|differs[admin_gateway]', ]; if (! $this->validate($rules)) { return redirect() ->to((host_url() === null ? config('App') ->baseURL : host_url()) . config('App')->installGateway,) ->withInput() ->with('errors', $this->validator->getErrors()); } $baseUrl = $this->request->getPost('hostname'); $mediaBaseUrl = $this->request->getPost('media_base_url'); self::writeEnv([ 'app.baseURL' => $baseUrl, 'app.mediaBaseURL' => $mediaBaseUrl === null ? $baseUrl : $mediaBaseUrl, 'app.adminGateway' => $this->request->getPost('admin_gateway'), 'app.authGateway' => $this->request->getPost('auth_gateway'), ]); helper('text'); // redirect to full install url with new baseUrl input return redirect()->to(reduce_double_slashes($baseUrl . '/' . config('App')->installGateway,),); } public function databaseConfig(): string { return view('install/database_config'); } public function attemptDatabaseConfig(): RedirectResponse { $rules = [ 'db_hostname' => 'required', 'db_name' => 'required', 'db_username' => 'required', 'db_password' => 'required', ]; if (! $this->validate($rules)) { return redirect() ->back() ->withInput() ->with('errors', $this->validator->getErrors()); } self::writeEnv([ 'database.default.hostname' => $this->request->getPost('db_hostname',), 'database.default.database' => $this->request->getPost('db_name'), 'database.default.username' => $this->request->getPost('db_username',), 'database.default.password' => $this->request->getPost('db_password',), 'database.default.DBPrefix' => $this->request->getPost('db_prefix'), ]); return redirect()->back(); } public function cacheConfig(): string { return view('install/cache_config'); } public function attemptCacheConfig(): RedirectResponse { $rules = [ 'cache_handler' => 'required', ]; if (! $this->validate($rules)) { return redirect() ->back() ->withInput() ->with('errors', $this->validator->getErrors()); } self::writeEnv([ 'cache.handler' => $this->request->getPost('cache_handler'), ]); return redirect()->back(); } /** * Runs all database migrations required for instance. */ public function migrate(): void { $migrations = Services::migrations(); $migrations->setNamespace('Myth\Auth') ->latest(); $migrations->setNamespace('ActivityPub') ->latest(); $migrations->setNamespace('Analytics') ->latest(); $migrations->setNamespace(APP_NAMESPACE) ->latest(); } /** * Runs all database seeds required for instance. */ public function seed(): void { $seeder = Database::seeder(); // Seed database $seeder->call('AppSeeder'); } /** * Returns the form to create a the first superadmin user for the instance. */ public function createSuperAdmin(): string { return view('install/create_superadmin'); } /** * Creates the first superadmin user or redirects back to form if any error. * * After creation, user is redirected to login page to input its credentials. */ public function attemptCreateSuperAdmin(): RedirectResponse { $userModel = new UserModel(); // Validate here first, since some things, // like the password, can only be validated properly here. $rules = array_merge( $userModel->getValidationRules([ 'only' => ['username'], ]), [ 'email' => 'required|valid_email|is_unique[users.email]', 'password' => 'required|strong_password', ], ); if (! $this->validate($rules)) { return redirect() ->back() ->withInput() ->with('errors', $this->validator->getErrors()); } // Save the user $user = new User($this->request->getPost()); // Activate user $user->activate(); $db = Database::connect(); $db->transStart(); if (! ($userId = $userModel->insert($user, true))) { $db->transRollback(); return redirect() ->back() ->withInput() ->with('errors', $userModel->errors()); } // add newly created user to superadmin group $authorization = Services::authorization(); $authorization->addUserToGroup($userId, 'superadmin'); $db->transComplete(); // Success! // set redirect_url session as admin area to go to after login session() ->set('redirect_url', route_to('admin')); return redirect() ->route('login') ->with('message', lang('Install.messages.createSuperAdminSuccess')); } /** * writes config values in .env file overwrites any existing key and appends new ones * * @param array $configData key/value config pairs */ public static function writeEnv(array $configData): void { $envData = file(ROOTPATH . '.env'); // reads an array of lines foreach ($configData as $key => $value) { $replaced = false; $keyVal = $key . '="' . $value . '"' . PHP_EOL; $envData = array_map( function ($line) use ($key, $keyVal, &$replaced) { if (str_starts_with($line, (string) $key)) { $replaced = true; return $keyVal; } return $line; }, $envData ); if (! $replaced) { $envData[] = $keyVal; } } file_put_contents(ROOTPATH . '.env', implode('', $envData)); } }