mirror of
https://github.com/crater-invoice/crater.git
synced 2025-10-27 11:41:09 -04:00
Refactor Self Update Endpoints and Add Update Console Command
This commit is contained in:
committed by
Mohit Panjwani
parent
a24d8d3ebc
commit
183953f4c4
189
app/Console/Commands/UpdateCommand.php
Normal file
189
app/Console/Commands/UpdateCommand.php
Normal file
@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
namespace Crater\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Crater\Space\Updater;
|
||||
use Crater\Setting;
|
||||
|
||||
class UpdateCommand extends Command
|
||||
{
|
||||
public $installed;
|
||||
|
||||
public $version;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'crater:update';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Automatically update your crater app';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
set_time_limit(3600); // 1 hour
|
||||
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,8 @@ class Kernel extends ConsoleKernel
|
||||
* @var array
|
||||
*/
|
||||
protected $commands = [
|
||||
Commands\ResetApp::class
|
||||
Commands\ResetApp::class,
|
||||
Commands\UpdateCommand::class
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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',
|
||||
|
||||
Reference in New Issue
Block a user