Installer.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <?php
  2. namespace App\Console\Commands;
  3. use Illuminate\Console\Command;
  4. use Illuminate\Support\Facades\Redis;
  5. class Installer extends Command
  6. {
  7. /**
  8. * The name and signature of the console command.
  9. *
  10. * @var string
  11. */
  12. protected $signature = 'install';
  13. /**
  14. * The console command description.
  15. *
  16. * @var string
  17. */
  18. protected $description = 'CLI Installer';
  19. /**
  20. * Create a new command instance.
  21. *
  22. * @return void
  23. */
  24. public function __construct()
  25. {
  26. parent::__construct();
  27. }
  28. /**
  29. * Execute the console command.
  30. *
  31. * @return mixed
  32. */
  33. public function handle()
  34. {
  35. $this->welcome();
  36. }
  37. protected function welcome()
  38. {
  39. $this->info(' ____ _ ______ __ ');
  40. $this->info(' / __ \(_) _____ / / __/__ ____/ / ');
  41. $this->info(' / /_/ / / |/_/ _ \/ / /_/ _ \/ __ / ');
  42. $this->info(' / ____/ /> </ __/ / __/ __/ /_/ / ');
  43. $this->info(' /_/ /_/_/|_|\___/_/_/ \___/\__,_/ ');
  44. $this->info(' ');
  45. $this->info(' Welcome to the Pixelfed Installer!');
  46. $this->info(' ');
  47. $this->info(' ');
  48. $this->info('Pixelfed version: ' . config('pixelfed.version'));
  49. $this->line(' ');
  50. $this->info('Scanning system...');
  51. $this->preflightCheck();
  52. }
  53. protected function preflightCheck()
  54. {
  55. $this->line(' ');
  56. $this->info('Checking for installed dependencies...');
  57. $redis = Redis::connection();
  58. if($redis->ping()) {
  59. $this->info('- Found redis!');
  60. } else {
  61. $this->error('- Redis not found, aborting installation');
  62. exit;
  63. }
  64. $this->checkPhpDependencies();
  65. $this->checkPermissions();
  66. $this->envCheck();
  67. }
  68. protected function checkPhpDependencies()
  69. {
  70. $extensions = [
  71. 'bcmath',
  72. 'ctype',
  73. 'curl',
  74. 'json',
  75. 'mbstring',
  76. 'openssl'
  77. ];
  78. $ffmpeg = exec('which ffmpeg');
  79. if(empty($ffmpeg)) {
  80. $this->error('FFmpeg not found, please install it.');
  81. $this->error('Cancelling installation.');
  82. exit;
  83. } else {
  84. $this->info('- Found FFmpeg!');
  85. }
  86. $this->line('');
  87. $this->info('Checking for required php extensions...');
  88. foreach($extensions as $ext) {
  89. if(extension_loaded($ext) == false) {
  90. $this->error("- {$ext} extension not found, aborting installation");
  91. exit;
  92. }
  93. }
  94. $this->info("- Required PHP extensions found!");
  95. }
  96. protected function checkPermissions()
  97. {
  98. $this->line('');
  99. $this->info('Checking for proper filesystem permissions...');
  100. $paths = [
  101. base_path('bootstrap'),
  102. base_path('storage')
  103. ];
  104. foreach($paths as $path) {
  105. if(is_writeable($path) == false) {
  106. $this->error("- Invalid permission found! Aborting installation.");
  107. $this->error(" Please make the following path writeable by the web server:");
  108. $this->error(" $path");
  109. exit;
  110. } else {
  111. $this->info("- Found valid permissions for {$path}");
  112. }
  113. }
  114. }
  115. protected function envCheck()
  116. {
  117. if(!file_exists(base_path('.env')) || filesize(base_path('.env')) == 0) {
  118. $this->line('');
  119. $this->info('No .env configuration file found. We will create one now!');
  120. $this->createEnv();
  121. } else {
  122. $confirm = $this->confirm('Found .env file, do you want to overwrite it?');
  123. if(!$confirm) {
  124. $this->info('Cancelling installation.');
  125. exit;
  126. }
  127. $confirm = $this->confirm('Are you really sure you want to overwrite it?');
  128. if(!$confirm) {
  129. $this->info('Cancelling installation.');
  130. exit;
  131. }
  132. $this->error('Warning ... if you did not backup your .env before its overwritten it will be permanently deleted.');
  133. $confirm = $this->confirm('The application may be installed already, are you really sure you want to overwrite it?');
  134. if(!$confirm) {
  135. $this->info('Cancelling installation.');
  136. exit;
  137. }
  138. }
  139. $this->postInstall();
  140. }
  141. protected function createEnv()
  142. {
  143. $this->line('');
  144. // copy env
  145. if(!file_exists(app()->environmentFilePath())) {
  146. exec('cp .env.example .env');
  147. $this->call('key:generate');
  148. }
  149. $name = $this->ask('Site name [ex: Pixelfed]');
  150. $this->updateEnvFile('APP_NAME', $name ?? 'pixelfed');
  151. $domain = $this->ask('Site Domain [ex: pixelfed.com]');
  152. $this->updateEnvFile('APP_DOMAIN', $domain ?? 'example.org');
  153. $this->updateEnvFile('ADMIN_DOMAIN', $domain ?? 'example.org');
  154. $this->updateEnvFile('SESSION_DOMAIN', $domain ?? 'example.org');
  155. $this->updateEnvFile('APP_URL', 'https://' . $domain ?? 'https://example.org');
  156. $database = $this->choice('Select database driver', ['mysql', 'pgsql'], 0);
  157. $this->updateEnvFile('DB_CONNECTION', $database ?? 'mysql');
  158. switch ($database) {
  159. case 'mysql':
  160. $database_host = $this->ask('Select database host', '127.0.0.1');
  161. $this->updateEnvFile('DB_HOST', $database_host ?? 'mysql');
  162. $database_port = $this->ask('Select database port', 3306);
  163. $this->updateEnvFile('DB_PORT', $database_port ?? 3306);
  164. $database_db = $this->ask('Select database', 'pixelfed');
  165. $this->updateEnvFile('DB_DATABASE', $database_db ?? 'pixelfed');
  166. $database_username = $this->ask('Select database username', 'pixelfed');
  167. $this->updateEnvFile('DB_USERNAME', $database_username ?? 'pixelfed');
  168. $db_pass = str_random(64);
  169. $database_password = $this->secret('Select database password', $db_pass);
  170. $this->updateEnvFile('DB_PASSWORD', $database_password);
  171. break;
  172. }
  173. $cache = $this->choice('Select cache driver', ["redis", "apc", "array", "database", "file", "memcached"], 0);
  174. $this->updateEnvFile('CACHE_DRIVER', $cache ?? 'redis');
  175. $session = $this->choice('Select session driver', ["redis", "file", "cookie", "database", "apc", "memcached", "array"], 0);
  176. $this->updateEnvFile('SESSION_DRIVER', $session ?? 'redis');
  177. $redis_host = $this->ask('Set redis host', 'localhost');
  178. $this->updateEnvFile('REDIS_HOST', $redis_host);
  179. $redis_password = $this->ask('Set redis password', 'null');
  180. $this->updateEnvFile('REDIS_PASSWORD', $redis_password);
  181. $redis_port = $this->ask('Set redis port', 6379);
  182. $this->updateEnvFile('REDIS_PORT', $redis_port);
  183. $open_registration = $this->choice('Allow new registrations?', ['true', 'false'], 1);
  184. $this->updateEnvFile('OPEN_REGISTRATION', $open_registration);
  185. $enforce_email_verification = $this->choice('Enforce email verification?', ['true', 'false'], 0);
  186. $this->updateEnvFile('ENFORCE_EMAIL_VERIFICATION', $enforce_email_verification);
  187. }
  188. protected function updateEnvFile($key, $value)
  189. {
  190. $envPath = app()->environmentFilePath();
  191. $payload = file_get_contents($envPath);
  192. if ($existing = $this->existingEnv($key, $payload)) {
  193. $payload = str_replace("{$key}={$existing}", "{$key}=\"{$value}\"", $payload);
  194. $this->storeEnv($payload);
  195. } else {
  196. $payload = $payload . "\n{$key}=\"{$value}\"\n";
  197. $this->storeEnv($payload);
  198. }
  199. }
  200. protected function existingEnv($needle, $haystack)
  201. {
  202. preg_match("/^{$needle}=[^\r\n]*/m", $haystack, $matches);
  203. if ($matches && count($matches)) {
  204. return substr($matches[0], strlen($needle) + 1);
  205. }
  206. return false;
  207. }
  208. protected function storeEnv($payload)
  209. {
  210. $file = fopen(app()->environmentFilePath(), 'w');
  211. fwrite($file, $payload);
  212. fclose($file);
  213. }
  214. protected function postInstall()
  215. {
  216. $this->callSilent('config:cache');
  217. //$this->callSilent('route:cache');
  218. $this->info('Pixelfed has been successfully installed!');
  219. }
  220. }