Merge pull request #972 from denisdulici/master

Added update command
This commit is contained in:
Denis Duliçi 2019-12-03 15:49:01 +03:00 committed by GitHub
commit fe2ed7befc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 427 additions and 232 deletions

View File

@ -0,0 +1,49 @@
<?php
namespace App\Console\Commands;
use App\Events\Install\UpdateFinished;
use Illuminate\Console\Command;
class FinishUpdate extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'update:finish {alias} {company_id} {new} {old}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Finish the update process through CLI';
/**
* Create a new command instance.
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
set_time_limit(900); // 15 minutes
$this->info('Finishing update...');
session(['company_id' => $this->argument('company_id')]);
$this->call('cache:clear');
event(new UpdateFinished($this->argument('alias'), $this->argument('new'), $this->argument('old')));
}
}

View File

@ -0,0 +1,158 @@
<?php
namespace App\Console\Commands;
use App\Utilities\Updater;
use App\Utilities\Versions;
use Illuminate\Console\Command;
class Update extends Command
{
const CMD_SUCCESS = 0;
const CMD_ERROR = 1;
public $alias;
public $new;
public $old;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'update {alias} {company_id} {new=latest}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Allows to update Akaunting and modules directly through CLI';
/**
* Create a new command instance.
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
set_time_limit(900); // 15 minutes
$this->alias = $this->argument('alias');
$this->new = $this->getNewVersion();
$this->old = $this->getOldVersion();
session(['company_id' => $this->argument('company_id')]);
if (!$path = $this->download()) {
return self::CMD_ERROR;
}
if (!$this->unzip($path)) {
return self::CMD_ERROR;
}
if (!$this->copyFiles($path)) {
return self::CMD_ERROR;
}
if (!$this->finish()) {
return self::CMD_ERROR;
}
return self::CMD_SUCCESS;
}
public function getNewVersion()
{
$new = $this->argument('new');
if ($new == 'latest') {
$modules = ($this->alias == 'core') ? [] : [$this->alias];
$new = Versions::latest($modules)[$this->alias];
}
return $new;
}
public function getOldVersion()
{
return ($this->alias == 'core')
? version('short')
: module($this->alias)->get('version');
}
public function download()
{
$this->info('Downloading update...');
try {
$path = Updater::download($this->alias, $this->new, $this->old);
} catch (\Exception $e) {
$this->error($e->getMessage());
return false;
}
return $path;
}
public function unzip($path)
{
$this->info('Unzipping update...');
try {
Updater::unzip($path, $this->alias, $this->new, $this->old);
} catch (\Exception $e) {
$this->error($e->getMessage());
return false;
}
return true;
}
public function copyFiles($path)
{
$this->info('Copying update files...');
try {
Updater::copyFiles($path, $this->alias, $this->new, $this->old);
} catch (\Exception $e) {
$this->error($e->getMessage());
return false;
}
return true;
}
public function finish()
{
$this->info('Finishing update...');
try {
Updater::finish($this->alias, $this->new, $this->old);
} catch (\Exception $e) {
$this->error($e->getMessage());
return false;
}
return true;
}
}

View File

@ -6,7 +6,6 @@ use App\Abstracts\Http\Controller;
use App\Utilities\Updater;
use App\Utilities\Versions;
use Illuminate\Http\Request;
use Module;
class Updates extends Controller
{
@ -111,31 +110,31 @@ class Updates extends Controller
// Download
$steps[] = [
'text' => trans('modules.installation.download', ['module' => $name]),
'url' => url('install/updates/download')
'url' => route('updates.download'),
];
// Unzip
$steps[] = [
'text' => trans('modules.installation.unzip', ['module' => $name]),
'url' => url('install/updates/unzip')
'url' => route('updates.unzip'),
];
// File Copy
// Copy files
$steps[] = [
'text' => trans('modules.installation.file_copy', ['module' => $name]),
'url' => url('install/updates/file-copy')
'url' => route('updates.copy'),
];
// Finish installation
// Finish/Apply
$steps[] = [
'text' => trans('modules.installation.finish', ['module' => $name]),
'url' => url('install/updates/finish')
'url' => route('updates.finish'),
];
// Redirect
$steps[] = [
'text' => trans('modules.installation.redirect', ['module' => $name]),
'url' => url('install/updates/redirect')
'url' => route('updates.redirect'),
];
return response()->json([
@ -155,9 +154,27 @@ class Updates extends Controller
*/
public function download(Request $request)
{
set_time_limit(600); // 10 minutes
set_time_limit(900); // 15 minutes
$json = Updater::download($request['alias'], $request['version'], $request['installed']);
try {
$path = Updater::download($request['alias'], $request['version'], $request['installed']);
$json = [
'success' => true,
'error' => false,
'message' => null,
'data' => [
'path' => $path,
],
];
} catch (\Exception $e) {
$json = [
'success' => false,
'error' => true,
'message' => $e->getMessage(),
'data' => [],
];
}
return response()->json($json);
}
@ -171,9 +188,27 @@ class Updates extends Controller
*/
public function unzip(Request $request)
{
set_time_limit(600); // 10 minutes
set_time_limit(900); // 15 minutes
$json = Updater::unzip($request['path'], $request['alias'], $request['version'], $request['installed']);
try {
$path = Updater::unzip($request['path'], $request['alias'], $request['version'], $request['installed']);
$json = [
'success' => true,
'error' => false,
'message' => null,
'data' => [
'path' => $path,
],
];
} catch (\Exception $e) {
$json = [
'success' => false,
'error' => true,
'message' => $e->getMessage(),
'data' => [],
];
}
return response()->json($json);
}
@ -185,11 +220,29 @@ class Updates extends Controller
*
* @return Response
*/
public function fileCopy(Request $request)
public function copyFiles(Request $request)
{
set_time_limit(600); // 10 minutes
set_time_limit(900); // 15 minutes
$json = Updater::fileCopy($request['path'], $request['alias'], $request['version'], $request['installed']);
try {
$path = Updater::copyFiles($request['path'], $request['alias'], $request['version'], $request['installed']);
$json = [
'success' => true,
'error' => false,
'message' => null,
'data' => [
'path' => $path,
],
];
} catch (\Exception $e) {
$json = [
'success' => false,
'error' => true,
'message' => $e->getMessage(),
'data' => [],
];
}
return response()->json($json);
}
@ -203,7 +256,25 @@ class Updates extends Controller
*/
public function finish(Request $request)
{
$json = Updater::finish($request['alias'], $request['version'], $request['installed']);
set_time_limit(900); // 15 minutes
try {
Updater::finish($request['alias'], $request['version'], $request['installed']);
$json = [
'success' => true,
'error' => false,
'message' => null,
'data' => [],
];
} catch (\Exception $e) {
$json = [
'success' => false,
'error' => true,
'message' => $e->getMessage(),
'data' => [],
];
}
return response()->json($json);
}
@ -215,13 +286,15 @@ class Updates extends Controller
*
* @return Response
*/
public function redirect(Request $request)
public function redirect()
{
return response()->json([
$json = [
'success' => true,
'errors' => false,
'redirect' => route('updates.index'),
'data' => [],
]);
];
return response()->json($json);
}
}

View File

@ -268,25 +268,6 @@ class Item extends Controller
return redirect('apps/' . $alias)->send();
}
/**
* Final actions post update.
*
* @param $alias
* @return Response
*/
public function post($alias)
{
Artisan::call('module:install', ['alias' => $alias, 'company_id' => session('company_id')]);
$module = module($alias);
$message = trans('modules.installed', ['module' => $module->getName()]);
flash($message)->success();
return redirect('apps/' . $alias);
}
public function reviews($alias, Request $request)
{
$page = $request['page'];

View File

@ -0,0 +1,41 @@
<?php
namespace App\Listeners\Update;
use App\Events\Install\UpdateFinished as Event;
use App\Models\Module\Module;
use App\Models\Module\ModuleHistory;
class CreateModuleUpdatedHistory
{
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
$model = Module::where('alias', $event->alias)->first();
if (empty($model)) {
return;
}
// Get module instance
$module = module($event->alias);
if (empty($module)) {
return;
}
// Add history
ModuleHistory::create([
'company_id' => $model->company_id,
'module_id' => $model->id,
'category' => $module->get('category', 'payment-method'),
'version' => $event->version,
'description' => trans('modules.history.updated', ['module' => $module->getAlias()]),
]);
}
}

View File

@ -73,6 +73,9 @@ class Event extends Provider
'App\Events\Menu\PortalCreated' => [
'App\Listeners\Menu\AddPortalItems',
],
'App\Events\Install\UpdateFinished' => [
'App\Listeners\Update\CreateModuleUpdatedHistory',
],
];
/**

View File

@ -3,6 +3,7 @@
namespace App\Traits;
use App\Traits\SiteApi;
use App\Utilities\Console;
use App\Utilities\Info;
use App\Models\Module\Module as Model;
use App\Models\Module\Module;
@ -14,7 +15,6 @@ use Illuminate\Support\Str;
use GuzzleHttp\Exception\RequestException;
use ZipArchive;
trait Modules
{
use SiteApi;
@ -408,17 +408,19 @@ trait Modules
File::copyDirectory($temp_path, $module_path);
File::deleteDirectory($temp_path);
Artisan::call('cache:clear');
$data = [
'path' => $path,
'name' => Str::studly($module->alias),
'alias' => $module->alias
];
$company_id = session('company_id');
Console::run("php artisan module:install {$module->alias} {$company_id}");
return [
'success' => true,
'redirect' => url("apps/post/" . $module->alias),
'redirect' => url('apps/' . $module->alias),
'error' => false,
'message' => null,
'data' => $data,
@ -446,7 +448,7 @@ trait Modules
'success' => true,
'error' => false,
'message' => null,
'data' => $data
'data' => $data,
];
}
@ -468,7 +470,7 @@ trait Modules
'success' => true,
'error' => false,
'message' => null,
'data' => $data
'data' => $data,
];
}
@ -490,7 +492,7 @@ trait Modules
'success' => true,
'error' => false,
'message' => null,
'data' => $data
'data' => $data,
];
}

21
app/Utilities/Console.php Normal file
View File

@ -0,0 +1,21 @@
<?php
namespace App\Utilities;
use Symfony\Component\Process\Process;
class Console
{
public static function run($command)
{
$process = new Process($command, base_path());
$process->run();
if ($process->isSuccessful()) {
return true;
}
return false;
}
}

View File

@ -2,8 +2,9 @@
namespace App\Utilities;
use App\Models\Module\Module as Model;
use App\Models\Module\ModuleHistory as ModelHistory;
use App\Events\Install\UpdateCopied;
use App\Events\Install\UpdateDownloaded;
use App\Events\Install\UpdateUnzipped;
use App\Traits\SiteApi;
use Cache;
use Date;
@ -23,7 +24,7 @@ class Updater
return true;
}
public static function download($alias, $version, $installed)
public static function download($alias, $new, $old)
{
$file = null;
$path = null;
@ -32,32 +33,24 @@ class Updater
$info = Info::all();
if ($alias == 'core') {
$url = 'core/download/' . $version . '/' . $info['php'] . '/' . $info['mysql'];
$url = 'core/download/' . $new . '/' . $info['php'] . '/' . $info['mysql'];
} else {
$url = 'apps/' . $alias . '/download/' . $version . '/' . $info['akaunting'] . '/' . $info['token'];
$url = 'apps/' . $alias . '/download/' . $new . '/' . $info['akaunting'] . '/' . $info['token'];
}
$response = static::getRemote($url, 'GET', ['timeout' => 50, 'track_redirects' => true]);
// Exception
if ($response instanceof RequestException) {
return [
'success' => false,
'error' => true,
'message' => trans('modules.errors.download', ['module' => $alias]),
'data' => [
'path' => $path
]
];
if (!$response || ($response instanceof RequestException) || ($response->getStatusCode() != 200)) {
throw new \Exception(trans('modules.errors.download', ['module' => $alias]));
}
if ($response && ($response->getStatusCode() == 200)) {
$file = $response->getBody()->getContents();
$path = 'temp-' . md5(mt_rand());
$temp_path = storage_path('app/temp') . '/' . $path;
$file_path = $temp_path . '/upload.zip';
$file_path = $temp_path . '/update.zip';
// Create tmp directory
if (!File::isDirectory($temp_path)) {
@ -68,65 +61,25 @@ class Updater
$uploaded = is_int(file_put_contents($file_path, $file)) ? true : false;
if (!$uploaded) {
return [
'success' => false,
'error' => true,
'message' => trans('modules.errors.zip', ['module' => $alias]),
'data' => [
'path' => $path
]
];
throw new \Exception(trans('modules.errors.zip', ['module' => $alias]));
}
try {
event(new \App\Events\Install\UpdateDownloaded($alias, $version, $installed));
event(new UpdateDownloaded($alias, $new, $old));
return [
'success' => true,
'error' => false,
'message' => null,
'data' => [
'path' => $path
]
];
} catch (\Exception $e) {
return [
'success' => false,
'error' => true,
'message' => trans('modules.errors.download', ['module' => $alias]),
'data' => []
];
}
return $path;
}
return [
'success' => false,
'error' => true,
'message' => trans('modules.errors.download', ['module' => $alias]),
'data' => [
'path' => $path
]
];
}
public static function unzip($path, $alias, $version, $installed)
public static function unzip($path, $alias, $new, $old)
{
$temp_path = storage_path('app/temp') . '/' . $path;
$file = $temp_path . '/upload.zip';
$file = $temp_path . '/update.zip';
// Unzip the file
$zip = new ZipArchive();
if (($zip->open($file) !== true) || !$zip->extractTo($temp_path)) {
return [
'success' => false,
'error' => true,
'message' => trans('modules.errors.unzip', ['module' => $alias]),
'data' => [
'path' => $path
]
];
throw new \Exception(trans('modules.errors.unzip', ['module' => $alias]));
}
$zip->close();
@ -134,42 +87,19 @@ class Updater
// Delete zip file
File::delete($file);
try {
event(new \App\Events\Install\UpdateUnzipped($alias, $version, $installed));
event(new UpdateUnzipped($alias, $new, $old));
return [
'success' => true,
'error' => false,
'message' => null,
'data' => [
'path' => $path
]
];
} catch (\Exception $e) {
return [
'success' => false,
'error' => true,
'message' => trans('modules.errors.unzip', ['module' => $alias]),
'data' => []
];
}
return $path;
}
public static function fileCopy($path, $alias, $version, $installed)
public static function copyFiles($path, $alias, $new, $old)
{
$temp_path = storage_path('app/temp') . '/' . $path;
if ($alias == 'core') {
// Move all files/folders from temp path
if (!File::copyDirectory($temp_path, base_path())) {
return [
'success' => false,
'error' => true,
'message' => trans('modules.errors.file_copy', ['module' => $alias]),
'data' => [
'path' => $path
]
];
throw new \Exception(trans('modules.errors.file_copy', ['module' => $alias]));
}
} else {
// Get module instance
@ -184,87 +114,31 @@ class Updater
// Move all files/folders from temp path
if (!File::copyDirectory($temp_path, $module_path)) {
return [
'success' => false,
'error' => true,
'message' => trans('modules.errors.file_copy', ['module' => $alias]),
'data' => [
'path' => $path
]
];
}
$model = Model::where('alias', $alias)->first();
if (!empty($model)) {
// Add history
ModelHistory::create([
'company_id' => session('company_id'),
'module_id' => $model->id,
'category' => $module->get('category', 'payment-method'),
'version' => $version,
'description' => trans('modules.history.updated', ['module' => $module->get('alias')]),
]);
throw new \Exception(trans('modules.errors.file_copy', ['module' => $alias]));
}
}
// Delete temp directory
File::deleteDirectory($temp_path);
Artisan::call('cache:clear');
event(new UpdateCopied($alias, $new, $old));
try {
event(new \App\Events\Install\UpdateCopied($alias, $version, $installed));
return [
'success' => true,
'error' => false,
'message' => null,
'data' => [
'path' => $path
]
];
} catch (\Exception $e) {
return [
'success' => false,
'error' => true,
'message' => trans('modules.errors.file_copy', ['module' => $alias]),
'data' => []
];
}
return $path;
}
public static function finish($alias, $version, $installed)
public static function finish($alias, $new, $old)
{
// Check if the file mirror was successful
if (($alias == 'core') && (version('short') != $version)) {
return [
'success' => false,
'error' => true,
'message' => trans('modules.errors.file_copy', ['module' => $alias]),
'data' => []
];
if (($alias == 'core') && (version('short') != $new)) {
throw new \Exception(trans('modules.errors.finish', ['module' => $alias]));
}
// Clear cache after update
Artisan::call('cache:clear');
$company_id = session('company_id');
try {
event(new \App\Events\Install\UpdateFinished($alias, $installed, $version));
$command = "php artisan update:finish {$alias} {$company_id} {$new} {$old}";
return [
'success' => true,
'error' => false,
'message' => null,
'data' => []
];
} catch (\Exception $e) {
return [
'success' => false,
'error' => true,
'message' => trans('modules.errors.finish', ['module' => $alias]),
'data' => []
];
if (!Console::run($command)) {
throw new \Exception(trans('modules.errors.finish', ['module' => $alias]));
}
}

View File

@ -65,7 +65,7 @@ class Versions
$info = Info::all();
// No data in cache, grab them from remote
$data = array();
$data = [];
// Check core first
$url = 'core/version/' . $info['akaunting'] . '/' . $info['php'] . '/' . $info['mysql'] . '/' . $info['companies'];

View File

@ -39,26 +39,22 @@ class InstallCommand extends Command
// Set company id
session(['company_id' => $company_id]);
$request = [
'company_id' => $company_id,
'alias' => strtolower($alias),
'enabled' => '1',
];
$model = Module::create($request);
$module = module($alias);
$model = Module::create([
'company_id' => $company_id,
'alias' => $alias,
'enabled' => '1',
]);
// Add history
$data = [
ModuleHistory::create([
'company_id' => $company_id,
'module_id' => $model->id,
'category' => $module->get('category', 'payment-method'),
'version' => $module->get('version'),
'description' => trans('modules.installed', ['module' => $module->get('alias')]),
];
ModuleHistory::create($data);
'description' => trans('modules.installed', ['module' => $alias]),
]);
// Clear cache
$this->call('cache:clear');

View File

@ -46,7 +46,7 @@ return [
'unzip' => 'Extracting :module files',
'file_copy' => 'Copying :module files',
'finish' => 'Finalizing :module installation',
'redirect' => ':module installed, redirecting to Updates page',
'redirect' => ':module installed, redirecting to updates page',
'install' => 'Installing :module',
],

View File

@ -177,8 +177,6 @@ Route::group(['as' => 'apps.', 'prefix' => 'apps'], function () {
Route::post('unzip', 'Modules\Item@unzip')->name('unzip');
Route::post('install', 'Modules\Item@install')->name('install');
Route::get('post/{alias}', 'Modules\Item@post');
Route::post('{alias}/reviews', 'Modules\Item@reviews')->name('app.reviews');
Route::get('{alias}/uninstall', 'Modules\Item@uninstall')->name('app.uninstall');
Route::get('{alias}/enable', 'Modules\Item@enable')->name('app.enable');
@ -191,11 +189,10 @@ Route::group(['prefix' => 'install'], function () {
Route::get('updates/changelog', 'Install\Updates@changelog')->name('updates.changelog');
Route::get('updates/check', 'Install\Updates@check')->name('updates.check');
Route::get('updates/update/{alias}/{version}', 'Install\Updates@update')->name('updates.update');
Route::get('updates/post/{alias}/{old}/{new}', 'Install\Updates@post')->name('updates.post');
Route::post('updates/steps', 'Install\Updates@steps')->name('updates.steps');
Route::post('updates/download', 'Install\Updates@download')->middleware('api.key')->name('updates.download');
Route::post('updates/unzip', 'Install\Updates@unzip')->middleware('api.key')->name('updates.unzip');
Route::post('updates/file-copy', 'Install\Updates@fileCopy')->middleware('api.key')->name('updates.copy');
Route::post('updates/copy-files', 'Install\Updates@copyFiles')->middleware('api.key')->name('updates.copy');
Route::post('updates/migrate', 'Install\Updates@migrate')->name('updates.migrate');
Route::post('updates/finish', 'Install\Updates@finish')->name('updates.finish');
Route::post('updates/redirect', 'Install\Updates@redirect')->name('updates.redirect');