envPath = base_path('.env'); } /** * Returns the .env contents * * @return false|string */ private function getEnvContents() { return file_get_contents($this->envPath); } /** * Updates .env file - inspired by Akaunting * * @return bool */ public function updateEnv(array $data) { if (empty($data) || ! is_array($data) || ! is_file($this->envPath)) { return false; } $env = $this->getEnvContents(); $env = explode($this->delimiter, $env); foreach ($data as $data_key => $data_value) { $updated = false; foreach ($env as $env_key => $env_value) { $entry = explode('=', $env_value, 2); // Check if new or old key if ($entry[0] == $data_key) { $env[$env_key] = sprintf('%s=%s', $data_key, $this->encode($data_value)); $updated = true; } } // Lets create if not available if (! $updated) { $env[] = $data_key.'='.$this->encode($data_value); } } $env = implode($this->delimiter, $env); file_put_contents(base_path('.env'), $env); return true; } /** * Encodes value for .env * * @return mixed|string */ private function encode($str) { // Convert to string if not already $str = (string) $str; // If the value is already properly quoted, return as is if (strlen($str) >= 2 && $str[0] === '"' && $str[strlen($str) - 1] === '"') { return $str; } // Check if the value contains characters that need quoting // Using a character class regex to properly match special characters $specialChars = '\^\'£$%&*()}{@#~?><,|=\-_+¬!'; $needsQuoting = ( strpos($str, ' ') !== false || preg_match('/['.preg_quote($specialChars, '/').']/', $str) ); if ($needsQuoting) { // Escape any existing double quotes in the string $str = str_replace('"', '\\"', $str); $str = '"'.$str.'"'; } return $str; } /** * Save the database content to the .env file. * * @return array */ public function saveDatabaseVariables(DatabaseEnvironmentRequest $request) { $appUrl = $request->get('app_url'); if ($appUrl !== config('app.url')) { config(['app.url' => $appUrl]); } [$sanctumDomain, $sessionDomain] = $this->getDomains( $request->getHttpHost() ); $dbEnv = [ 'APP_URL' => $appUrl, 'APP_LOCALE' => $request->get('app_locale'), 'DB_CONNECTION' => $request->get('database_connection'), 'SESSION_DOMAIN' => $sessionDomain, ]; if ($sanctumDomain !== null) { $dbEnv['SANCTUM_STATEFUL_DOMAINS'] = $sanctumDomain; } if ($dbEnv['DB_CONNECTION'] != 'sqlite') { if ($request->has('database_username') && $request->has('database_password')) { $dbEnv['DB_HOST'] = $request->get('database_hostname'); $dbEnv['DB_PORT'] = $request->get('database_port'); $dbEnv['DB_DATABASE'] = $request->get('database_name'); $dbEnv['DB_USERNAME'] = $request->get('database_username'); $dbEnv['DB_PASSWORD'] = $request->get('database_password'); } } else { // Laravel 11 requires SQLite at least v3.35.0 // https://laravel.com/docs/11.x/database#introduction if (extension_loaded('sqlite3') && class_exists('\SQLite3') && method_exists('\SQLite3', 'version')) { $version = \SQLite3::version(); if (! empty($version['versionString']) && version_compare($version['versionString'], '3.35.0', '<')) { return [ 'error_message' => sprintf('The minimum SQLite version is %s. Your current SQLite version is %s which is not supported. Please upgrade SQLite and retry.', '3.35.0', $version['versionString']), ]; } } else { return [ 'error_message' => sprintf('SQLite3 is not present. Please install SQLite >=%s and retry.', '3.35.0'), ]; } $dbEnv['DB_DATABASE'] = $request->get('database_name'); if (! empty($dbEnv['DB_DATABASE'])) { $sqlite_path = $dbEnv['DB_DATABASE']; } else { $sqlite_path = database_path('database.sqlite'); } // Create empty SQLite database if it doesn't exist. if (! file_exists($sqlite_path)) { copy(database_path('stubs/sqlite.empty.db'), $sqlite_path); $dbEnv['DB_DATABASE'] = $sqlite_path; } } try { $this->checkDatabaseConnection($request); if ($request->get('database_overwrite')) { Artisan::call('db:wipe --force'); } if (\Schema::hasTable('users')) { return [ 'error' => 'database_should_be_empty', ]; } } catch (Exception $e) { return [ 'error_message' => $e->getMessage(), ]; } try { $this->updateEnv($dbEnv); } catch (Exception $e) { return [ 'error' => 'database_variables_save_error', ]; } return [ 'success' => 'database_variables_save_successfully', ]; } /** * Returns PDO object if all ok. * * @return \Closure|\PDO */ private function checkDatabaseConnection(DatabaseEnvironmentRequest $request) { $connection = $request->get('database_connection'); $settings = config("database.connections.$connection"); $connectionArray = array_merge($settings, [ 'driver' => $connection, 'database' => $request->get('database_name'), ]); if ($connection !== 'sqlite' && $request->has('database_username') && $request->has('database_password')) { $connectionArray = array_merge($connectionArray, [ 'username' => $request->get('database_username'), 'password' => $request->get('database_password'), 'host' => $request->get('database_hostname'), 'port' => $request->get('database_port'), ]); } config([ 'database' => [ 'migrations' => 'migrations', 'default' => $connection, 'connections' => [$connection => $connectionArray], ], ]); return DB::connection()->getPdo(); } /** * Save the pdf generation content to the .env file. * * @return array */ public function savePDFVariables(PDFConfigurationRequest $request) { $pdfEnv = $this->getPDFConfiguration($request); try { $this->updateEnv($pdfEnv); } catch (Exception $e) { return [ 'error' => 'pdf_variables_save_error', ]; } return [ 'success' => 'pdf_variables_save_successfully', ]; } /** * Returns the pdf configuration * * @param PDFConfigurationRequest $request * @return array */ private function getPDFConfiguration($request) { $pdfEnv = []; $driver = $request->get('pdf_driver'); switch ($driver) { case 'dompdf': $pdfEnv = [ 'PDF_DRIVER' => $request->get('pdf_driver'), ]; break; case 'gotenberg': $pdfEnv = [ 'PDF_DRIVER' => $request->get('pdf_driver'), 'GOTENBERG_HOST' => $request->get('gotenberg_host'), 'GOTENBERG_MARGINS' => $request->get('gotenberg_margins'), 'GOTENBERG_PAPERSIZE' => $request->get('gotenberg_papersize'), ]; break; } return $pdfEnv; } /** * Save sanctum stateful domain to the .env file. * * @return array */ public function saveDomainVariables(DomainEnvironmentRequest $request) { try { [$sanctumDomain, $sessionDomain] = $this->getDomains( $request->get('app_domain') ); $domainEnv = [ 'SESSION_DOMAIN' => $sessionDomain, ]; if ($sanctumDomain !== null) { $domainEnv['SANCTUM_STATEFUL_DOMAINS'] = $sanctumDomain; } $this->updateEnv($domainEnv); } catch (Exception $e) { return [ 'error' => 'domain_verification_failed', ]; } return [ 'success' => 'domain_variable_save_successfully', ]; } private function getDomains(string $requestDomain): array { $appUrl = config('app.url'); $port = parse_url($appUrl, PHP_URL_PORT); $currentDomain = parse_url($appUrl, PHP_URL_HOST).( $port ? ':'.$port : '' ); $requestHost = parse_url($requestDomain, PHP_URL_HOST) ?: $requestDomain; $isSame = $currentDomain === $requestDomain; return [ $isSame && env('SANCTUM_STATEFUL_DOMAINS', false) === false ? null : $requestDomain, $isSame && env('SESSION_DOMAIN', false) === null ? null : $requestHost, ]; } }