diff --git a/app/Console/Commands/UpdateCommand.php b/app/Console/Commands/UpdateCommand.php new file mode 100644 index 00000000..8c63df66 --- /dev/null +++ b/app/Console/Commands/UpdateCommand.php @@ -0,0 +1,189 @@ +installed = $this->getInstalledVersion(); + $this->version = $this->getLatestVersion(); + + if (!$this->version) { + $this->info('No Update Available! You are already on the latest version.'); + return; + } + + if (!$this->confirm("Do you wish to update to {$this->version}?")) { + return; + } + + if (!$path = $this->download()) { + return; + } + + if (!$path = $this->unzip($path)) { + return; + } + + if (!$this->copyFiles($path)) { + return; + } + + if (!$this->migrateUpdate()) { + return; + } + + if (!$this->finish()) { + return; + } + + $this->info('Successfully updated to ' . $this->version); + } + + public function getInstalledVersion() + { + return Setting::getSetting('version'); + } + + public function getLatestVersion() + { + $this->info('Your currently installed version is ' . $this->installed); + $this->line(''); + $this->info('Checking for update...'); + + try { + $response = Updater::checkForUpdate($this->installed); + + if ($response->success) { + return $response->version->version; + } + + return false; + } catch (\Exception $e) { + $this->error($e->getMessage()); + + return false; + } + } + + public function download() + { + $this->info('Downloading update...'); + + try { + $path = Updater::download($this->version); + if (!is_string($path)) { + $this->error('Download exception'); + return false; + } + } catch (\Exception $e) { + $this->error($e->getMessage()); + + return false; + } + + return $path; + } + + public function unzip($path) + { + $this->info('Unzipping update package...'); + + try { + $path = Updater::unzip($path); + if (!is_string($path)) { + $this->error('Unzipping exception'); + return false; + } + } catch (\Exception $e) { + $this->error($e->getMessage()); + + return false; + } + + return $path; + } + + public function copyFiles($path) + { + $this->info('Copying update files...'); + + try { + Updater::copyFiles($path); + } catch (\Exception $e) { + $this->error($e->getMessage()); + + return false; + } + + return true; + } + + public function migrateUpdate() + { + $this->info('Running Migrations...'); + + try { + Updater::migrateUpdate(); + } catch (\Exception $e) { + $this->error($e->getMessage()); + + return false; + } + + return true; + } + + public function finish() + { + $this->info('Finishing update...'); + + try { + Updater::finishUpdate($this->installed, $this->version); + } catch (\Exception $e) { + $this->error($e->getMessage()); + + return false; + } + + return true; + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 39bbe5c7..cd270548 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -12,7 +12,8 @@ class Kernel extends ConsoleKernel * @var array */ protected $commands = [ - Commands\ResetApp::class + Commands\ResetApp::class, + Commands\UpdateCommand::class ]; /** diff --git a/app/Http/Controllers/UpdateController.php b/app/Http/Controllers/UpdateController.php index 130c5096..65e98926 100644 --- a/app/Http/Controllers/UpdateController.php +++ b/app/Http/Controllers/UpdateController.php @@ -2,23 +2,81 @@ namespace Crater\Http\Controllers; +use Crater\Setting; use Illuminate\Http\Request; use Crater\Space\Updater; use Crater\Space\SiteApi; +use Illuminate\Support\Facades\Artisan; class UpdateController extends Controller { - public function update(Request $request) + + public function download(Request $request) { - set_time_limit(600); // 10 minutes + $request->validate([ + 'version' => 'required', + ]); - $json = Updater::update($request->installed, $request->version); + $path = Updater::download($request->version); - return response()->json($json); + return response()->json([ + 'success' => true, + 'path' => $path + ]); + } + + public function unzip(Request $request) + { + $request->validate([ + 'path' => 'required', + ]); + + try { + $path = Updater::unzip($request->path); + + return response()->json([ + 'success' => true, + 'path' => $path + ]); + + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'error' => $e->getMessage() + ]); + } + } + + public function copyFiles(Request $request) + { + $request->validate([ + 'path' => 'required', + ]); + + $path = Updater::copyFiles($request->path); + + return response()->json([ + 'success' => true, + 'path' => $path + ]); + } + + public function migrate(Request $request) + { + Updater::migrateUpdate(); + + return response()->json([ + 'success' => true + ]); } public function finishUpdate(Request $request) { + $request->validate([ + 'installed' => 'required', + 'version' => 'required', + ]); + $json = Updater::finishUpdate($request->installed, $request->version); return response()->json($json); @@ -28,7 +86,7 @@ class UpdateController extends Controller { set_time_limit(600); // 10 minutes - $json = Updater::checkForUpdate(); + $json = Updater::checkForUpdate(Setting::getSetting('version')); return response()->json($json); } diff --git a/app/Space/Updater.php b/app/Space/Updater.php index 51cbaae0..5cc592cb 100644 --- a/app/Space/Updater.php +++ b/app/Space/Updater.php @@ -2,28 +2,43 @@ namespace Crater\Space; use File; -use ZipArchive; use Artisan; use GuzzleHttp\Exception\RequestException; -use Crater\Space\SiteApi; use Crater\Events\UpdateFinished; -use Crater\Setting; -use Illuminate\Http\Request; +use ZipArchive; class Updater { use SiteApi; - public static function update($installed, $version) + public static function checkForUpdate($installed_version) + { + $data = null; + if(env('APP_ENV') === 'development') + { + $url = 'https://craterapp.com/downloads/check/latest/'. $installed_version . '?type=update&is_dev=1'; + } else { + $url = 'https://craterapp.com/downloads/check/latest/'. $installed_version . '?type=update'; + } + + $response = static::getRemote($url, ['timeout' => 100, 'track_redirects' => true]); + + if ($response && ($response->getStatusCode() == 200)) { + $data = $response->getBody()->getContents(); + } + + return json_decode($data); + } + + public static function download($new_version) { $data = null; $path = null; - if(env('APP_ENV') === 'development') - { - $url = 'https://craterapp.com/downloads/file/'.$version.'?type=update&is_dev=1'; + if (env('APP_ENV') === 'development') { + $url = 'https://craterapp.com/downloads/file/' . $new_version . '?type=update&is_dev=1'; } else { - $url = 'https://craterapp.com/downloads/file/'.$version.'?type=update'; + $url = 'https://craterapp.com/downloads/file/' . $new_version . '?type=update'; } $response = static::getRemote($url, ['timeout' => 100, 'track_redirects' => true]); @@ -44,66 +59,68 @@ class Updater } // Create temp directory - $path = 'temp-' . md5(mt_rand()); - $path2 = 'temp2-' . md5(mt_rand()); - $temp_path = storage_path('app') . '/' . $path; - $temp_path2 = storage_path('app') . '/' . $path2; + $temp_dir = storage_path('app/temp-' . md5(mt_rand())); - if (!File::isDirectory($temp_path)) { - File::makeDirectory($temp_path); - File::makeDirectory($temp_path2); + if (!File::isDirectory($temp_dir)) { + File::makeDirectory($temp_dir); } - try { + $zip_file_path = $temp_dir . '/upload.zip'; - $file = $temp_path . '/upload.zip'; + // Add content to the Zip file + $uploaded = is_int(file_put_contents($zip_file_path, $data)) ? true : false; - // Add content to the Zip file - $uploaded = is_int(file_put_contents($file, $data)) ? true : false; - - if (!$uploaded) { - return false; - } - - // Unzip the file - $zip = new ZipArchive(); - - if ($zip->open($file)) { - $zip->extractTo($temp_path2); - } - - $zip->close(); - - // Delete zip file - File::delete($file); - - if (!File::copyDirectory($temp_path2.'/Crater', base_path())) { - return false; - } - - // Delete temp directory - File::deleteDirectory($temp_path); - File::deleteDirectory($temp_path2); - - return [ - 'success' => true, - 'error' => false, - 'data' => [] - ]; - } catch (\Exception $e) { - - if (File::isDirectory($temp_path)) { - // Delete temp directory - File::deleteDirectory($temp_path); - File::deleteDirectory($temp_path2); - } - - return [ - 'success' => false, - 'error' => 'Update error', - 'data' => [] - ]; + if (!$uploaded) { + return false; } + + return $zip_file_path; + } + + public static function unzip($zip_file_path) + { + if(!file_exists($zip_file_path)) { + throw new \Exception('Zip file not found'); + } + + $temp_extract_dir = storage_path('app/temp2-' . md5(mt_rand())); + + if (!File::isDirectory($temp_extract_dir)) { + File::makeDirectory($temp_extract_dir); + } + // Unzip the file + $zip = new ZipArchive(); + + if ($zip->open($zip_file_path)) { + $zip->extractTo($temp_extract_dir); + } + + $zip->close(); + + // Delete zip file + File::delete($zip_file_path); + + return $temp_extract_dir; + } + + public static function copyFiles($temp_extract_dir) + { + + if (!File::copyDirectory($temp_extract_dir . '/Crater', base_path())) { + return false; + } + + // Delete temp directory + File::deleteDirectory($temp_extract_dir); + + return true; + } + + public static function migrateUpdate() + { + Artisan::call('migrate --force'); + + return true; } public static function finishUpdate($installed, $version) @@ -117,22 +134,4 @@ class Updater ]; } - public static function checkForUpdate() - { - $data = null; - if(env('APP_ENV') === 'development') - { - $url = 'https://craterapp.com/downloads/check/latest/'. Setting::getSetting('version') . '?type=update&is_dev=1'; - } else { - $url = 'https://craterapp.com/downloads/check/latest/'. Setting::getSetting('version') . '?type=update'; - } - - $response = static::getRemote($url, ['timeout' => 100, 'track_redirects' => true]); - - if ($response && ($response->getStatusCode() == 200)) { - $data = $response->getBody()->getContents(); - } - - return json_decode($data); - } } diff --git a/routes/api.php b/routes/api.php index d75e1e30..688aa517 100644 --- a/routes/api.php +++ b/routes/api.php @@ -122,23 +122,41 @@ Route::group(['middleware' => 'api'], function () { 'middleware' => 'admin' ], function () { - - // Auto update routes + // Self Update //---------------------------------- - Route::post('/update', [ - 'as' => 'auto.update', - 'uses' => 'UpdateController@update' + + Route::get('/check/update', [ + 'as' => 'update.check', + 'uses' => 'UpdateController@checkLatestVersion' + ]); + + Route::post('/update/download', [ + 'as' => 'update.download', + 'uses' => 'UpdateController@download' + ]); + + Route::post('/update/unzip', [ + 'as' => 'update.unzip', + 'uses' => 'UpdateController@unzip' + ]); + + Route::post('/update/copy', [ + 'as' => 'update.copy', + 'uses' => 'UpdateController@copyFiles' + ]); + + Route::post('/update/migrate', [ + 'as' => 'update.migrate', + 'uses' => 'UpdateController@migrate' ]); Route::post('/update/finish', [ - 'as' => 'auto.update.finish', + 'as' => 'update.finish', 'uses' => 'UpdateController@finishUpdate' ]); - Route::get('/check/update', [ - 'as' => 'check.update', - 'uses' => 'UpdateController@checkLatestVersion' - ]); + // Bootstrap + //---------------------------------- Route::get('/bootstrap', [ 'as' => 'bootstrap',