Installing and managing modules from admin panel (#847)
This commit is contained in:
1
modules/.gitignore
vendored
1
modules/.gitignore
vendored
@@ -8,3 +8,4 @@
|
||||
!/Installer
|
||||
!/Updater
|
||||
!/Vacentral
|
||||
!/ModulesManager
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'importer' => [
|
||||
'batch_size' => 20,
|
||||
],
|
||||
];
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Console\Commands;
|
||||
|
||||
use App\Contracts\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Importer\Services\ImporterService;
|
||||
|
||||
class ImportFromClassicCommand extends Command
|
||||
{
|
||||
protected $signature = 'phpvms:importer {db_host} {db_name} {db_user} {db_pass?} {table_prefix=phpvms_}';
|
||||
protected $description = 'Import from an older version of phpVMS';
|
||||
|
||||
/**
|
||||
* Run dev related commands
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$creds = [
|
||||
'host' => $this->argument('db_host'),
|
||||
'name' => $this->argument('db_name'),
|
||||
'user' => $this->argument('db_user'),
|
||||
'pass' => $this->argument('db_pass'),
|
||||
'table_prefix' => $this->argument('table_prefix'),
|
||||
];
|
||||
|
||||
$importerSvc = new ImporterService();
|
||||
|
||||
$importerSvc->saveCredentials($creds);
|
||||
$manifest = $importerSvc->generateImportManifest();
|
||||
|
||||
foreach ($manifest as $record) {
|
||||
try {
|
||||
$importerSvc->run($record['importer'], $record['start']);
|
||||
} catch (\Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Http\Controllers;
|
||||
|
||||
use App\Contracts\Controller;
|
||||
use App\Services\Installer\DatabaseService;
|
||||
use App\Services\Installer\InstallerService;
|
||||
use App\Support\Utils;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Importer\Services\ImporterService;
|
||||
|
||||
class ImporterController extends Controller
|
||||
{
|
||||
private $dbSvc;
|
||||
private $importerSvc;
|
||||
|
||||
public function __construct(DatabaseService $dbSvc, ImporterService $importerSvc)
|
||||
{
|
||||
$this->dbSvc = $dbSvc;
|
||||
$this->importerSvc = $importerSvc;
|
||||
|
||||
Utils::disableDebugToolbar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the main page for the importer; show form for the admin email
|
||||
* and the credentials for the other database
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
return view('importer::step1-configure');
|
||||
}
|
||||
|
||||
protected function testDb(Request $request)
|
||||
{
|
||||
$this->dbSvc->checkDbConnection(
|
||||
$request->post('db_conn'),
|
||||
$request->post('db_host'),
|
||||
$request->post('db_port'),
|
||||
$request->post('db_name'),
|
||||
$request->post('db_user'),
|
||||
$request->post('db_pass')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the database connection
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function dbtest(Request $request)
|
||||
{
|
||||
$status = 'success'; // success|warn|danger
|
||||
$message = 'Database connection looks good!';
|
||||
|
||||
try {
|
||||
$this->testDb($request);
|
||||
} catch (\Exception $e) {
|
||||
$status = 'danger';
|
||||
$message = 'Failed! '.$e->getMessage();
|
||||
}
|
||||
|
||||
return view('importer::dbtest', [
|
||||
'status' => $status,
|
||||
'message' => $message,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The post from the above
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function config(Request $request)
|
||||
{
|
||||
try {
|
||||
$this->testDb($request);
|
||||
|
||||
// Save the credentials to use later
|
||||
$this->importerSvc->saveCredentialsFromRequest($request);
|
||||
|
||||
// Generate the import manifest
|
||||
$manifest = $this->importerSvc->generateImportManifest();
|
||||
} catch (\Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
|
||||
// Send it to run, step1
|
||||
return view('importer::error', [
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
|
||||
// Send it to run, step1
|
||||
return view('importer::step2-processing', [
|
||||
'manifest' => $manifest,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the importer. Pass in query string with a few different parameters:
|
||||
*
|
||||
* stage=STAGE NAME
|
||||
* start=record_start
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function run(Request $request)
|
||||
{
|
||||
$importer = $request->input('importer');
|
||||
$start = $request->input('start');
|
||||
|
||||
Log::info('Starting stage '.$importer.' from offset '.$start);
|
||||
|
||||
$this->importerSvc->run($importer, $start);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'completed',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete the import
|
||||
*/
|
||||
public function complete()
|
||||
{
|
||||
$installerSvc = app(InstallerService::class);
|
||||
$installerSvc->disableInstallerModules();
|
||||
|
||||
return redirect('/');
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Providers;
|
||||
|
||||
use App\Contracts\Modules\ServiceProvider;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Modules\Importer\Console\Commands\ImportFromClassicCommand;
|
||||
|
||||
class ImporterServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Boot the application events.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
$this->registerCommands();
|
||||
$this->registerRoutes();
|
||||
$this->registerTranslations();
|
||||
$this->registerConfig();
|
||||
$this->registerViews();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register console commands
|
||||
*/
|
||||
protected function registerCommands()
|
||||
{
|
||||
$this->commands([
|
||||
ImportFromClassicCommand::class,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the routes
|
||||
*/
|
||||
protected function registerRoutes()
|
||||
{
|
||||
Route::group([
|
||||
'as' => 'importer.',
|
||||
'prefix' => 'importer',
|
||||
'middleware' => ['web'],
|
||||
'namespace' => 'Modules\Importer\Http\Controllers',
|
||||
], function () {
|
||||
Route::get('/', 'ImporterController@index')->name('index');
|
||||
Route::post('/config', 'ImporterController@config')->name('config');
|
||||
Route::post('/dbtest', 'ImporterController@dbtest')->name('dbtest');
|
||||
|
||||
// Run the actual importer process. Additional middleware
|
||||
Route::post('/run', 'ImporterController@run')->middleware('api')->name('run');
|
||||
Route::post('/complete', 'ImporterController@complete')->name('complete');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register config.
|
||||
*/
|
||||
protected function registerConfig()
|
||||
{
|
||||
$this->mergeConfigFrom(__DIR__.'/../Config/config.php', 'importer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register views.
|
||||
*/
|
||||
public function registerViews()
|
||||
{
|
||||
$viewPath = resource_path('views/modules/importer');
|
||||
$sourcePath = __DIR__.'/../Resources/views';
|
||||
|
||||
$this->publishes([$sourcePath => $viewPath], 'views');
|
||||
|
||||
$paths = array_map(
|
||||
function ($path) {
|
||||
return $path.'/modules/importer';
|
||||
},
|
||||
\Config::get('view.paths')
|
||||
);
|
||||
|
||||
$paths[] = $sourcePath;
|
||||
$this->loadViewsFrom($paths, 'importer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register translations.
|
||||
*/
|
||||
public function registerTranslations()
|
||||
{
|
||||
$langPath = resource_path('lang/modules/importer');
|
||||
|
||||
if (is_dir($langPath)) {
|
||||
$this->loadTranslationsFrom($langPath, 'importer');
|
||||
} else {
|
||||
$this->loadTranslationsFrom(__DIR__.'/../Resources/lang', 'importer');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>@yield('title') - installer</title>
|
||||
|
||||
<link rel="shortcut icon" type="image/png" href="{{ public_asset('/assets/img/favicon.png') }}"/>
|
||||
|
||||
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no'
|
||||
name='viewport'/>
|
||||
<meta name="base-url" content="{!! url('') !!}">
|
||||
<meta name="api-key" content="{!! Auth::check() ? Auth::user()->api_key: '' !!}">
|
||||
<meta name="csrf-token" content="{!! csrf_token() !!}">
|
||||
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700,200" rel="stylesheet"/>
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css" rel="stylesheet"/>
|
||||
|
||||
<link href="{{ public_asset('/assets/frontend/css/bootstrap.min.css') }}" rel="stylesheet"/>
|
||||
<link href="{{ public_asset('/assets/frontend/css/now-ui-kit.css') }}" rel="stylesheet"/>
|
||||
<link href="{{ public_asset('/assets/installer/css/vendor.css') }}" rel="stylesheet"/>
|
||||
<link href="{{ public_asset('/assets/frontend/css/styles.css') }}" rel="stylesheet"/>
|
||||
|
||||
<link rel="stylesheet"
|
||||
href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
|
||||
|
||||
<style>
|
||||
.table tr:first-child td {
|
||||
border-top: 0px;
|
||||
}
|
||||
@yield('css')
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="login-page" style="background: #067ec1;">
|
||||
<div class="page-header clear-filter">
|
||||
<div class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-8 ml-auto mr-auto content-center">
|
||||
<div class="p-10" style="padding: 10px 0;">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<img src="{{ public_asset('/assets/img/logo_blue_bg.svg') }}" width="135px" style="" alt=""/>
|
||||
</div>
|
||||
<div class="col-8 text-right">
|
||||
<h4 class="text-white mb-0 mr-0 ml-0" style="margin-top: 5px;">@yield('title')</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card card-login card-plain" style="background: #FFF">
|
||||
<div class="card-body">
|
||||
@include('importer::flash.message')
|
||||
@yield('content')
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{--<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>--}}
|
||||
|
||||
<script src="{{ public_mix('/assets/global/js/vendor.js') }}"></script>
|
||||
<script src="{{ public_mix('/assets/installer/js/vendor.js') }}"></script>
|
||||
<script src="{{ public_mix('/assets/installer/js/app.js') }}"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
|
||||
|
||||
<script>
|
||||
hljs.configure({languages: ['sh']});
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
$(".select2").select2();
|
||||
|
||||
$('pre code').each(function (i, block) {
|
||||
hljs.fixMarkup(block);
|
||||
hljs.highlightBlock(block);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@yield('scripts')
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,20 +0,0 @@
|
||||
@extends('importer::app')
|
||||
@section('title', 'Import Completed!')
|
||||
|
||||
@section('content')
|
||||
<div style="align-content: center;">
|
||||
{{ Form::open(['route' => 'importer.complete', 'method' => 'GET']) }}
|
||||
|
||||
<h4>Installer Completed!</h4>
|
||||
|
||||
<p>Edit the <span class="code">config.php</span> to fill in some additional settings. </p>
|
||||
<p>Click the button to proceed to the login screen!</p>
|
||||
|
||||
<p style="text-align: right">
|
||||
{{ Form::submit('Import Complete! Continue to Log-In >>',
|
||||
['class' => 'btn btn-success'])
|
||||
}}
|
||||
</p>
|
||||
{{ Form::close() }}
|
||||
</div>
|
||||
@endsection
|
||||
@@ -1,8 +0,0 @@
|
||||
<div class="alert alert-{{ $status }}" role="alert">
|
||||
<div class="container">
|
||||
<div class="alert-icon">
|
||||
<i class="now-ui-icons ui-1_bell-53"></i>
|
||||
</div>
|
||||
{{ $message }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,9 +0,0 @@
|
||||
@extends('importer::app')
|
||||
@section('title', 'Import Error!')
|
||||
|
||||
@section('content')
|
||||
<div style="align-content: center;">
|
||||
<h4>Error!</h4>
|
||||
<p class="text-danger">{{ $error }}</p>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -1,6 +0,0 @@
|
||||
@if($errors->has($field))
|
||||
<p class="text-danger" style="margin-top: 10px;">{{ $errors->first($field) }}</p>
|
||||
{{--<div class="alert alert-danger" role="alert" style="margin-top: 10px;">
|
||||
{{ $errors->first($field) }}
|
||||
</div>--}}
|
||||
@endif
|
||||
@@ -1,11 +0,0 @@
|
||||
@foreach (session('flash_notification', collect())->toArray() as $message)
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<div class="container">
|
||||
<div class="alert-icon">
|
||||
<i class="now-ui-icons ui-2_like"></i>
|
||||
</div>
|
||||
{{ $message['message'] }}
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
{{ session()->forget('flash_notification') }}
|
||||
@@ -1,129 +0,0 @@
|
||||
@extends('importer::app')
|
||||
@section('title', 'Import Configuration')
|
||||
|
||||
@section('content')
|
||||
<div style="align-content: center;">
|
||||
{{ Form::open(['route' => 'importer.config', 'method' => 'POST']) }}
|
||||
<table class="table">
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<h4>IMPORTANT NOTES</h4>
|
||||
<ul>
|
||||
<li>The first user's password (admin) will be "admin". Please change it after logging in</li>
|
||||
<li>User passwords will be reset and they will need to use "Forgot Password" to reset it</li>
|
||||
<li>If you have more than 1000 PIREPs or flights, it's best to use the command-line importer!
|
||||
<a href="{{ docs_link('importing_legacy') }}" target="_blank">Click here</a> to
|
||||
see the documentation of how to use it.
|
||||
</li>
|
||||
<li><strong>THIS WILL WIPE OUT YOUR EXISTING DATA</strong> - this is required to make sure that things like
|
||||
pilot IDs match up
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<h4>Database Config</h4>
|
||||
<p>Enter the database information for your legacy phpVMS installation</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tbody id="mysql_settings">
|
||||
<tr>
|
||||
<td>Database Host</td>
|
||||
<td style="text-align:center;">
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'db_host', '127.0.0.1', ['class' => 'form-control']) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Database Port</td>
|
||||
<td style="text-align:center;">
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'db_port', '3306', ['class' => 'form-control']) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Database Name</td>
|
||||
<td style="text-align:center;">
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'db_name', 'phpvms', ['class' => 'form-control']) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Database User</td>
|
||||
<td style="text-align:center;">
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'db_user', null, ['class' => 'form-control']) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Database Password</td>
|
||||
<td style="text-align:center;">
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'db_pass', null, ['class' => 'form-control']) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2" style="text-align: right;">
|
||||
{{ Form::submit('Test Database Credentials', ['class' => 'btn btn-info', 'id' => 'dbtest_button']) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tr>
|
||||
<td>Database Prefix</td>
|
||||
<td style="text-align:center;">
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'db_prefix', 'phpvms_', ['class' => 'form-control']) }}
|
||||
<p>Prefix of the tables, if you're using one</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<div id="dbtest"></div>
|
||||
<p style="text-align: right">
|
||||
{{ Form::submit('Start Importer >>', ['class' => 'btn btn-success']) }}
|
||||
</p>
|
||||
{{ Form::close() }}
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
$(document).ready(() => {
|
||||
$("#dbtest_button").click((e) => {
|
||||
e.preventDefault();
|
||||
const opts = {
|
||||
method: 'POST',
|
||||
url: '/importer/dbtest',
|
||||
data: {
|
||||
_token: "{{ csrf_token() }}",
|
||||
db_conn: 'mysql',
|
||||
db_host: $("input[name=db_host]").val(),
|
||||
db_port: $("input[name=db_port]").val(),
|
||||
db_name: $("input[name=db_name]").val(),
|
||||
db_user: $("input[name=db_user]").val(),
|
||||
db_pass: $("input[name=db_pass]").val(),
|
||||
},
|
||||
};
|
||||
|
||||
phpvms.request(opts).then(response => {
|
||||
$("#dbtest").html(response.data);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -1,156 +0,0 @@
|
||||
@extends('importer::app')
|
||||
@section('title', 'Import Configuration')
|
||||
|
||||
@section('content')
|
||||
<div style="align-content: center;">
|
||||
{{ Form::open(['route' => 'importer.complete', 'method' => 'POST']) }}
|
||||
<table class="table" width="25%">
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><h4>Running Importer</h4></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="progress">
|
||||
<div id="progress" class="progress-bar" style="width: 0%"></div>
|
||||
</div>
|
||||
<div>
|
||||
<p id="message" style="margin-top: 7px;"></p>
|
||||
<p id="error" class="text-danger" style="margin-top: 7px;"></p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<p style="text-align: right">
|
||||
{{ Form::submit('Complete Import', [
|
||||
'id' => 'completebutton',
|
||||
'class' => 'btn btn-success'
|
||||
]) }}
|
||||
</p>
|
||||
{{ Form::close() }}
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
const manifest = {!!json_encode($manifest) !!};
|
||||
|
||||
/**
|
||||
* Run each step of the importer
|
||||
*/
|
||||
async function startImporter() {
|
||||
let current = 1;
|
||||
const total_steps = manifest.length;
|
||||
|
||||
/**
|
||||
* Update the progress bar
|
||||
*/
|
||||
const setProgress = (current, message) => {
|
||||
const percent = Math.round(current / total_steps * 100);
|
||||
$("#progress").css("width", `${percent}%`);
|
||||
$("#message").text(message);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sleep for a given interval
|
||||
*/
|
||||
const sleep = (timeout) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, timeout);
|
||||
});
|
||||
};
|
||||
|
||||
const setError = (error) => {
|
||||
let message = '';
|
||||
if (error.response) {
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
console.log(error.response.data);
|
||||
console.log(error.response.status);
|
||||
console.log(error.response.headers);
|
||||
|
||||
message = error.response.data.message;
|
||||
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||
// http.ClientRequest in node.js
|
||||
console.log(error.request);
|
||||
message = error.request;
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.log('Error', error.message);
|
||||
message = error.message;
|
||||
}
|
||||
|
||||
$("#error").text(`Error processing, check the logs: ${message}`);
|
||||
console.log(error.config);
|
||||
};
|
||||
|
||||
/**
|
||||
* Call the endpoint as a POST
|
||||
*/
|
||||
const runStep = async function (stage) {
|
||||
setProgress(current, stage.message);
|
||||
|
||||
try {
|
||||
return await phpvms.request({
|
||||
method: 'post',
|
||||
url: '/importer/run',
|
||||
data: {
|
||||
importer: stage.importer,
|
||||
start: stage.start,
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
|
||||
if (e.response.status === 504) {
|
||||
const err = $("#error");
|
||||
|
||||
console.log('got timeout, retrying');
|
||||
err.text(`Timed out, attempting to retry`);
|
||||
|
||||
// await sleep(5000);
|
||||
const val = await runStep(stage);
|
||||
|
||||
err.text('');
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
setError(e);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
let errors = false;
|
||||
const complete_button = $("#completebutton");
|
||||
complete_button.hide();
|
||||
|
||||
for (let stage of manifest) {
|
||||
console.log(`Running ${stage.importer} step ${stage.start}`);
|
||||
try {
|
||||
await runStep(stage);
|
||||
} catch (e) {
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
|
||||
current++;
|
||||
}
|
||||
|
||||
if (!errors) {
|
||||
$("#message").text('Done!');
|
||||
complete_button.show();
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(() => {
|
||||
startImporter();
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -1,150 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Services;
|
||||
|
||||
use App\Services\Installer\LoggerTrait;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Importer\Utils\IdMapper;
|
||||
use Modules\Importer\Utils\ImporterDB;
|
||||
|
||||
abstract class BaseImporter
|
||||
{
|
||||
use LoggerTrait;
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
/**
|
||||
* Holds the connection to the legacy database
|
||||
*
|
||||
* @var \Modules\Importer\Utils\ImporterDB
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* The mapper class used for old IDs to new IDs
|
||||
*
|
||||
* @var IdMapper
|
||||
*/
|
||||
protected $idMapper;
|
||||
|
||||
/**
|
||||
* The legacy table this importer targets
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table;
|
||||
|
||||
/**
|
||||
* The column used for the ID, used for the ORDER BY
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $idField = 'id';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$importerService = app(ImporterService::class);
|
||||
$this->db = new ImporterDB($importerService->getCredentials());
|
||||
$this->idMapper = app(IdMapper::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* The start method. Takes the offset to start from
|
||||
*
|
||||
* @param int $start
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function run($start = 0);
|
||||
|
||||
/**
|
||||
* Return a manifest of the import tasks to run. Returns an array of objects,
|
||||
* which contain a start and end row
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getManifest(): array
|
||||
{
|
||||
$manifest = [];
|
||||
|
||||
// Ensure that the table exists; if it doesn't skip it from the manifest
|
||||
if (!$this->db->tableExists($this->table)) {
|
||||
Log::info('Table '.$this->table.' doesn\'t exist');
|
||||
return [];
|
||||
}
|
||||
|
||||
$start = 0;
|
||||
$total_rows = $this->db->getTotalRows($this->table);
|
||||
Log::info('Found '.$total_rows.' rows for '.$this->table);
|
||||
|
||||
do {
|
||||
$end = $start + $this->db->batchSize;
|
||||
if ($end > $total_rows) {
|
||||
$end = $total_rows;
|
||||
}
|
||||
|
||||
$idx = $start + 1;
|
||||
|
||||
$manifest[] = [
|
||||
'importer' => static::class,
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'message' => 'Importing '.$this->table.' ('.$idx.' - '.$end.' of '.$total_rows.')',
|
||||
];
|
||||
|
||||
$start += $this->db->batchSize;
|
||||
} while ($start < $total_rows);
|
||||
|
||||
return $manifest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine what columns exist, can be used for feature testing between v2/v5
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getColumns(): array
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $date
|
||||
*
|
||||
* @return Carbon
|
||||
*/
|
||||
protected function parseDate($date)
|
||||
{
|
||||
$carbon = Carbon::parse($date);
|
||||
|
||||
return $carbon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a decimal duration and convert it to minutes
|
||||
*
|
||||
* @param $duration
|
||||
*
|
||||
* @return float|int
|
||||
*/
|
||||
protected function convertDuration($duration)
|
||||
{
|
||||
if (strpos($duration, '.') !== false) {
|
||||
$delim = '.';
|
||||
} elseif (strpos($duration, ':')) {
|
||||
$delim = ':';
|
||||
} else {
|
||||
// no delimiter, assume it's just a straight hour
|
||||
return (int) $duration * 60;
|
||||
}
|
||||
|
||||
$hm = explode($delim, $duration);
|
||||
$hours = (int) $hm[0] * 60;
|
||||
$mins = (int) $hm[1];
|
||||
|
||||
return $hours + $mins;
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Services;
|
||||
|
||||
use App\Contracts\Service;
|
||||
use App\Repositories\KvpRepository;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Importer\Services\Importers\AircraftImporter;
|
||||
use Modules\Importer\Services\Importers\AirlineImporter;
|
||||
use Modules\Importer\Services\Importers\AirportImporter;
|
||||
use Modules\Importer\Services\Importers\ClearDatabase;
|
||||
use Modules\Importer\Services\Importers\ExpenseImporter;
|
||||
use Modules\Importer\Services\Importers\FinalizeImporter;
|
||||
use Modules\Importer\Services\Importers\FlightImporter;
|
||||
use Modules\Importer\Services\Importers\GroupImporter;
|
||||
use Modules\Importer\Services\Importers\LedgerImporter;
|
||||
use Modules\Importer\Services\Importers\PirepImporter;
|
||||
use Modules\Importer\Services\Importers\RankImport;
|
||||
use Modules\Importer\Services\Importers\SettingsImporter;
|
||||
use Modules\Importer\Services\Importers\UserImport;
|
||||
|
||||
class ImporterService extends Service
|
||||
{
|
||||
private $CREDENTIALS_KEY = 'legacy.importer.db';
|
||||
|
||||
/**
|
||||
* @var KvpRepository
|
||||
*/
|
||||
private $kvpRepo;
|
||||
|
||||
/**
|
||||
* The list of importers, in proper order
|
||||
*/
|
||||
private $importList = [
|
||||
ClearDatabase::class,
|
||||
RankImport::class,
|
||||
GroupImporter::class,
|
||||
AirlineImporter::class,
|
||||
AircraftImporter::class,
|
||||
AirportImporter::class,
|
||||
FlightImporter::class,
|
||||
UserImport::class,
|
||||
PirepImporter::class,
|
||||
ExpenseImporter::class,
|
||||
LedgerImporter::class,
|
||||
SettingsImporter::class,
|
||||
FinalizeImporter::class,
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->kvpRepo = app(KvpRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the credentials from a request
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
*/
|
||||
public function saveCredentialsFromRequest(Request $request)
|
||||
{
|
||||
$creds = [
|
||||
'host' => $request->post('db_host'),
|
||||
'port' => $request->post('db_port'),
|
||||
'name' => $request->post('db_name'),
|
||||
'user' => $request->post('db_user'),
|
||||
'pass' => $request->post('db_pass'),
|
||||
'table_prefix' => $request->post('db_prefix'),
|
||||
];
|
||||
|
||||
$this->saveCredentials($creds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the given credentials
|
||||
*
|
||||
* @param array $creds
|
||||
*/
|
||||
public function saveCredentials(array $creds)
|
||||
{
|
||||
$creds = array_merge([
|
||||
'admin_email' => '',
|
||||
'host' => '',
|
||||
'port' => '',
|
||||
'name' => '',
|
||||
'user' => '',
|
||||
'pass' => 3306,
|
||||
'table_prefix' => 'phpvms_',
|
||||
], $creds);
|
||||
|
||||
$this->kvpRepo->save($this->CREDENTIALS_KEY, $creds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the saved credentials
|
||||
*/
|
||||
public function getCredentials()
|
||||
{
|
||||
return $this->kvpRepo->get($this->CREDENTIALS_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a manifest of the import. Creates an array with the importer name,
|
||||
* which then has a subarray of all of the different steps/stages it needs to run
|
||||
*/
|
||||
public function generateImportManifest()
|
||||
{
|
||||
$manifest = [];
|
||||
|
||||
foreach ($this->importList as $importerKlass) {
|
||||
/** @var \Modules\Importer\Services\BaseImporter $importer */
|
||||
$importer = new $importerKlass();
|
||||
$manifest = array_merge($manifest, $importer->getManifest());
|
||||
}
|
||||
|
||||
return $manifest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a given stage
|
||||
*
|
||||
* @param $importer
|
||||
* @param int $start
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return int|void
|
||||
*/
|
||||
public function run($importer, $start = 0)
|
||||
{
|
||||
if (!in_array($importer, $this->importList)) {
|
||||
throw new Exception('Unknown importer "'.$importer.'"');
|
||||
}
|
||||
|
||||
/** @var $importerInst \Modules\Importer\Services\BaseImporter */
|
||||
$importerInst = new $importer();
|
||||
|
||||
try {
|
||||
$importerInst->run($start);
|
||||
} catch (Exception $e) {
|
||||
Log::error('Error running importer: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Services\Importers;
|
||||
|
||||
use App\Models\Aircraft;
|
||||
use App\Models\Airline;
|
||||
use App\Models\Subfleet;
|
||||
use Modules\Importer\Services\BaseImporter;
|
||||
|
||||
class AircraftImporter extends BaseImporter
|
||||
{
|
||||
protected $table = 'aircraft';
|
||||
|
||||
public function run($start = 0)
|
||||
{
|
||||
$this->comment('--- AIRCRAFT IMPORT ---');
|
||||
|
||||
$fields = [
|
||||
'id',
|
||||
'icao',
|
||||
'name',
|
||||
'fullname',
|
||||
'registration',
|
||||
'enabled',
|
||||
];
|
||||
|
||||
// See if there is an airline column
|
||||
$columns = $this->db->getColumns($this->table);
|
||||
if (in_array('airline', $columns)) {
|
||||
$fields[] = 'airline';
|
||||
}
|
||||
|
||||
if (in_array('location', $columns)) {
|
||||
$fields[] = 'location';
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
$rows = $this->db->readRows($this->table, $this->idField, $start, $fields);
|
||||
foreach ($rows as $row) {
|
||||
$subfleet_name = $row->icao;
|
||||
|
||||
$airline_id = null;
|
||||
if (!empty($row->airline)) {
|
||||
$subfleet_name = $row->airline.' - '.$row->icao;
|
||||
$airline_id = $this->idMapper->getMapping('airlines', $row->airline);
|
||||
}
|
||||
|
||||
$subfleet = $this->getSubfleet($subfleet_name, $row->icao, $airline_id);
|
||||
|
||||
$where = [
|
||||
'registration' => $row->registration,
|
||||
];
|
||||
|
||||
$cols = [
|
||||
'icao' => $row->icao,
|
||||
'name' => $row->fullname,
|
||||
'subfleet_id' => $subfleet->id,
|
||||
'active' => $row->enabled,
|
||||
];
|
||||
|
||||
if (!empty($row->location)) {
|
||||
$cols['airport_id'] = $row->location;
|
||||
}
|
||||
|
||||
$aircraft = Aircraft::firstOrCreate($where, $cols);
|
||||
|
||||
$this->idMapper->addMapping('aircraft', $row->id, $aircraft->id);
|
||||
|
||||
if ($aircraft->wasRecentlyCreated) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Imported '.$count.' aircraft');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the subfleet
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $icao ICAO of the subfleet
|
||||
* @param int $airline_id
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getSubfleet($name, $icao, $airline_id = null)
|
||||
{
|
||||
if (empty($airline_id)) {
|
||||
$airline = Airline::first();
|
||||
$airline_id = $airline->id;
|
||||
}
|
||||
|
||||
return Subfleet::firstOrCreate([
|
||||
'airline_id' => $airline_id,
|
||||
'name' => $name,
|
||||
], ['type' => $icao]);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Services\Importers;
|
||||
|
||||
use App\Models\Airline;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Importer\Services\BaseImporter;
|
||||
|
||||
class AirlineImporter extends BaseImporter
|
||||
{
|
||||
public $table = 'airlines';
|
||||
|
||||
/**
|
||||
* @param int $start
|
||||
*/
|
||||
public function run($start = 0)
|
||||
{
|
||||
$this->comment('--- AIRLINE IMPORT ---');
|
||||
|
||||
$count = 0;
|
||||
$rows = $this->db->readRows($this->table, $this->idField, $start);
|
||||
foreach ($rows as $row) {
|
||||
$attrs = [
|
||||
'iata' => $row->code,
|
||||
'name' => $row->name,
|
||||
'active' => $row->enabled,
|
||||
];
|
||||
|
||||
$w = ['icao' => $row->code];
|
||||
|
||||
//$airline = Airline::firstOrCreate($w, $attrs);
|
||||
$airline = Airline::create(array_merge($w, $attrs));
|
||||
|
||||
$this->idMapper->addMapping('airlines', $row->id, $airline->id);
|
||||
$this->idMapper->addMapping('airlines', $row->code, $airline->id);
|
||||
|
||||
Log::debug('Mapping '.$row->id.'/'.$row->code.' to ID '.$airline->id);
|
||||
|
||||
if ($airline->wasRecentlyCreated) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Imported '.$count.' airlines');
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Services\Importers;
|
||||
|
||||
use App\Models\Airport;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Importer\Services\BaseImporter;
|
||||
|
||||
class AirportImporter extends BaseImporter
|
||||
{
|
||||
protected $table = 'airports';
|
||||
|
||||
public function run($start = 0)
|
||||
{
|
||||
$this->comment('--- AIRPORT IMPORT ---');
|
||||
|
||||
$fields = [
|
||||
'icao',
|
||||
'name',
|
||||
'country',
|
||||
'lat',
|
||||
'lng',
|
||||
'hub',
|
||||
];
|
||||
|
||||
$count = 0;
|
||||
$rows = $this->db->readRows($this->table, $this->idField, $start, $fields);
|
||||
foreach ($rows as $row) {
|
||||
$attrs = [
|
||||
'id' => trim($row->icao),
|
||||
'icao' => trim($row->icao),
|
||||
'name' => $row->name,
|
||||
'country' => $row->country,
|
||||
'lat' => $row->lat,
|
||||
'lon' => $row->lng,
|
||||
'hub' => $row->hub,
|
||||
];
|
||||
|
||||
$w = ['id' => $attrs['id']];
|
||||
//$airport = Airport::updateOrCreate($w, $attrs);
|
||||
|
||||
try {
|
||||
$airport = Airport::create(array_merge($w, $attrs));
|
||||
} catch (QueryException $e) {
|
||||
$sqlState = $e->errorInfo[0];
|
||||
$errorCode = $e->errorInfo[1];
|
||||
if ($sqlState === '23000' && $errorCode === 1062) {
|
||||
Log::info('Found duplicate for '.$row->icao.', ignoring');
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($airport->wasRecentlyCreated) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Imported '.$count.' airports');
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Services\Importers;
|
||||
|
||||
use App\Models\Acars;
|
||||
use App\Models\Aircraft;
|
||||
use App\Models\Airline;
|
||||
use App\Models\Airport;
|
||||
use App\Models\Bid;
|
||||
use App\Models\Expense;
|
||||
use App\Models\File;
|
||||
use App\Models\Flight;
|
||||
use App\Models\FlightField;
|
||||
use App\Models\FlightFieldValue;
|
||||
use App\Models\Journal;
|
||||
use App\Models\JournalTransaction;
|
||||
use App\Models\Ledger;
|
||||
use App\Models\News;
|
||||
use App\Models\Pirep;
|
||||
use App\Models\Role;
|
||||
use App\Models\Subfleet;
|
||||
use App\Models\User;
|
||||
use App\Models\UserAward;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Importer\Services\BaseImporter;
|
||||
|
||||
class ClearDatabase extends BaseImporter
|
||||
{
|
||||
/**
|
||||
* Returns a default manifest just so this step gets run
|
||||
*/
|
||||
public function getManifest(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'importer' => static::class,
|
||||
'start' => 0,
|
||||
'end' => 1,
|
||||
'message' => 'Clearing database',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function run($start = 0)
|
||||
{
|
||||
$this->cleanupDb();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup the local database of any users and other data that might conflict
|
||||
* before running the importer
|
||||
*/
|
||||
protected function cleanupDb()
|
||||
{
|
||||
$this->info('Running database cleanup/empty before starting');
|
||||
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS=0');
|
||||
|
||||
Bid::truncate();
|
||||
File::truncate();
|
||||
News::truncate();
|
||||
|
||||
Expense::truncate();
|
||||
JournalTransaction::truncate();
|
||||
Journal::truncate();
|
||||
Ledger::truncate();
|
||||
|
||||
// Clear flights
|
||||
DB::table('flight_fare')->truncate();
|
||||
DB::table('flight_subfleet')->truncate();
|
||||
FlightField::truncate();
|
||||
FlightFieldValue::truncate();
|
||||
Flight::truncate();
|
||||
Subfleet::truncate();
|
||||
Aircraft::truncate();
|
||||
|
||||
Airline::truncate();
|
||||
Airport::truncate();
|
||||
Acars::truncate();
|
||||
Pirep::truncate();
|
||||
|
||||
UserAward::truncate();
|
||||
User::truncate();
|
||||
|
||||
// Clear permissions
|
||||
DB::table('permission_role')->truncate();
|
||||
DB::table('permission_user')->truncate();
|
||||
DB::table('role_user')->truncate();
|
||||
|
||||
// Role::truncate();
|
||||
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS=1');
|
||||
|
||||
$this->idMapper->clear();
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Services\Importers;
|
||||
|
||||
use App\Models\Enums\ExpenseType;
|
||||
use App\Models\Expense;
|
||||
use Modules\Importer\Services\BaseImporter;
|
||||
|
||||
class ExpenseImporter extends BaseImporter
|
||||
{
|
||||
protected $table = 'expenses';
|
||||
|
||||
private $expense_types = [
|
||||
'M' => ExpenseType::MONTHLY,
|
||||
'F' => ExpenseType::FLIGHT,
|
||||
'P' => ExpenseType::MONTHLY, // percent, monthly
|
||||
'G' => ExpenseType::FLIGHT, // percent, per-flight
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run($start = 0)
|
||||
{
|
||||
$this->comment('--- EXPENSES IMPORT ---');
|
||||
|
||||
$count = 0;
|
||||
$rows = $this->db->readRows($this->table, $this->idField, $start);
|
||||
foreach ($rows as $row) {
|
||||
$attrs = [
|
||||
'airline_id' => null,
|
||||
'name' => $row->name,
|
||||
'amount' => $row->amount,
|
||||
'type' => $this->expense_types[$row->type],
|
||||
'active' => 1,
|
||||
'ref_model' => Expense::class,
|
||||
];
|
||||
|
||||
$expense = Expense::updateOrCreate(['name' => $row->name], $attrs);
|
||||
$this->idMapper->addMapping('expenses', $row->id, $expense->id);
|
||||
$this->idMapper->addMapping('expenses', $row->name, $expense->id);
|
||||
|
||||
$count++;
|
||||
}
|
||||
|
||||
$this->info('Imported '.$count.' expenses');
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Services\Importers;
|
||||
|
||||
use App\Models\Airline;
|
||||
use App\Models\Expense;
|
||||
use App\Services\FinanceService;
|
||||
use App\Support\Money;
|
||||
use Modules\Importer\Services\BaseImporter;
|
||||
|
||||
class ExpenseLogImporter extends BaseImporter
|
||||
{
|
||||
protected $table = 'expenselog';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws \Prettus\Validator\Exceptions\ValidatorException
|
||||
*/
|
||||
public function run($start = 0)
|
||||
{
|
||||
$this->comment('--- EXPENSE LOG IMPORT ---');
|
||||
|
||||
/** @var FinanceService $financeSvc */
|
||||
$financeSvc = app(FinanceService::class);
|
||||
$airline = Airline::first();
|
||||
|
||||
$count = 0;
|
||||
$rows = $this->db->readRows($this->table, $this->idField, $start);
|
||||
foreach ($rows as $row) {
|
||||
$expense_id = $this->idMapper->getMapping('expenses', $row->name);
|
||||
$expense = Expense::find($expense_id);
|
||||
|
||||
$debit = Money::createFromAmount($expense->amount);
|
||||
|
||||
$financeSvc->debitFromJournal(
|
||||
$airline->journal,
|
||||
$debit,
|
||||
$airline,
|
||||
'Expense: '.$expense->name,
|
||||
$expense->transaction_group ?? 'Expenses',
|
||||
'expense'
|
||||
);
|
||||
|
||||
$count++;
|
||||
}
|
||||
|
||||
$this->info('Imported '.$count.' expense logs');
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Services\Importers;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Services\AircraftService;
|
||||
use App\Services\UserService;
|
||||
use Modules\Importer\Services\BaseImporter;
|
||||
|
||||
class FinalizeImporter extends BaseImporter
|
||||
{
|
||||
/**
|
||||
* Returns a default manifest just so this step gets run
|
||||
*/
|
||||
public function getManifest(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'importer' => static::class,
|
||||
'start' => 0,
|
||||
'end' => 1,
|
||||
'message' => 'Finalizing import',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The start method. Takes the offset to start from
|
||||
*
|
||||
* @param int $start
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function run($start = 0)
|
||||
{
|
||||
$this->findLastPireps();
|
||||
$this->recalculateUserStats();
|
||||
$this->clearValueStore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through and set the last PIREP ID for the users
|
||||
*/
|
||||
protected function findLastPireps()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculate all of the user stats
|
||||
*/
|
||||
protected function recalculateUserStats()
|
||||
{
|
||||
$this->comment('--- RECALCULATING USER STATS ---');
|
||||
|
||||
/** @var UserService $userSvc */
|
||||
$userSvc = app(UserService::class);
|
||||
|
||||
User::all()->each(function ($user) use ($userSvc) {
|
||||
$userSvc->recalculateStats($user);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the aircraft stats with the newest/latest PIREPs
|
||||
*/
|
||||
protected function recalculateAircraftStats()
|
||||
{
|
||||
$this->comment('--- RECALCULATING AIRCRAFT STATS ---');
|
||||
|
||||
/** @var AircraftService $aircraftSvc */
|
||||
$aircraftSvc = app(AircraftService::class);
|
||||
$aircraftSvc->recalculateStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the value store of any old value mappings
|
||||
*/
|
||||
protected function clearValueStore()
|
||||
{
|
||||
$this->idMapper->clear();
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Services\Importers;
|
||||
|
||||
use App\Models\Flight;
|
||||
use Modules\Importer\Services\BaseImporter;
|
||||
|
||||
class FlightImporter extends BaseImporter
|
||||
{
|
||||
protected $table = 'schedules';
|
||||
|
||||
public function run($start = 0)
|
||||
{
|
||||
$this->comment('--- FLIGHT SCHEDULE IMPORT ---');
|
||||
|
||||
$fields = [
|
||||
'id',
|
||||
'code',
|
||||
'flightnum',
|
||||
'depicao',
|
||||
'arricao',
|
||||
'route',
|
||||
'distance',
|
||||
'flightlevel',
|
||||
'deptime',
|
||||
'arrtime',
|
||||
'flighttime',
|
||||
'notes',
|
||||
'enabled',
|
||||
];
|
||||
|
||||
$count = 0;
|
||||
$rows = $this->db->readRows($this->table, $this->idField, $start, $fields);
|
||||
foreach ($rows as $row) {
|
||||
$airline_id = $this->idMapper->getMapping('airlines', $row->code);
|
||||
|
||||
$flight_num = trim($row->flightnum);
|
||||
|
||||
$attrs = [
|
||||
'dpt_airport_id' => $row->depicao,
|
||||
'arr_airport_id' => $row->arricao,
|
||||
'route' => $row->route ?: '',
|
||||
'distance' => round($row->distance ?: 0, 2),
|
||||
'level' => $row->flightlevel ?: 0,
|
||||
'dpt_time' => $row->deptime ?: '',
|
||||
'arr_time' => $row->arrtime ?: '',
|
||||
'flight_time' => $this->convertDuration($row->flighttime) ?: '',
|
||||
'notes' => $row->notes ?: '',
|
||||
'active' => $row->enabled ?: true,
|
||||
];
|
||||
|
||||
try {
|
||||
$w = ['airline_id' => $airline_id, 'flight_number' => $flight_num];
|
||||
// $flight = Flight::updateOrCreate($w, $attrs);
|
||||
$flight = Flight::create(array_merge($w, $attrs));
|
||||
} catch (\Exception $e) {
|
||||
$this->error($e);
|
||||
}
|
||||
|
||||
$this->idMapper->addMapping('flights', $row->id, $flight->id);
|
||||
|
||||
// TODO: deserialize route_details into ACARS table
|
||||
|
||||
if ($flight->wasRecentlyCreated) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Imported '.$count.' flights');
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Services\Importers;
|
||||
|
||||
use App\Models\Permission;
|
||||
use App\Models\Role;
|
||||
use App\Services\RoleService;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Importer\Services\BaseImporter;
|
||||
|
||||
/**
|
||||
* Imports the groups into the permissions feature(s)
|
||||
*/
|
||||
class GroupImporter extends BaseImporter
|
||||
{
|
||||
protected $table = 'groups';
|
||||
protected $idField = 'groupid';
|
||||
|
||||
/**
|
||||
* Permissions in the legacy system, mapping them to the current system
|
||||
*/
|
||||
protected $legacy_permission_set = [
|
||||
'EDIT_NEWS' => 0x1,
|
||||
'EDIT_PAGES' => 0x2,
|
||||
'EDIT_DOWNLOADS' => 0x4,
|
||||
'EMAIL_PILOTS' => 0x8,
|
||||
'EDIT_AIRLINES' => 0x10, //
|
||||
'EDIT_FLEET' => 0x20, //
|
||||
'EDIT_SCHEDULES' => 0x80, //
|
||||
'IMPORT_SCHEDULES' => 0x100, //
|
||||
'MODERATE_REGISTRATIONS' => 0x200,
|
||||
'EDIT_PILOTS' => 0x400, //
|
||||
'EDIT_GROUPS' => 0x800,
|
||||
'EDIT_RANKS' => 0x1000, //
|
||||
'EDIT_AWARDS' => 0x2000, //
|
||||
'MODERATE_PIREPS' => 0x4000, //
|
||||
'VIEW_FINANCES' => 0x8000, //
|
||||
'EDIT_EXPENSES' => 0x10000, //
|
||||
'EDIT_SETTINGS' => 0x20000, //
|
||||
'EDIT_PIREPS_FIELDS' => 0x40000, //
|
||||
'EDIT_PROFILE_FIELDS' => 0x80000, //
|
||||
'EDIT_VACENTRAL' => 0x100000,
|
||||
'ACCESS_ADMIN' => 0x2000000,
|
||||
'FULL_ADMIN' => 35651519, //
|
||||
];
|
||||
|
||||
/**
|
||||
* Map a legacy value over to one of the current permission values
|
||||
*/
|
||||
protected $legacy_to_permission = [
|
||||
'FULL_ADMIN' => 'admin',
|
||||
'EDIT_AIRLINES' => 'airlines',
|
||||
'EDIT_AWARDS' => 'awards',
|
||||
'EDIT_FLEET' => 'fleet',
|
||||
'EDIT_EXPENCES' => 'finances',
|
||||
'VIEW_FINANCES' => 'finances',
|
||||
'EDIT_SCHEDULES' => 'flights',
|
||||
'EDIT_PILOTS' => 'users',
|
||||
'EDIT_PROFILE_FIELDS' => 'users',
|
||||
'EDIT_SETTINGS' => 'settings',
|
||||
'MODERATE_PIREPS' => 'pireps',
|
||||
'EDIT_PIREPS_FIELDS' => 'pireps',
|
||||
'EDIT_RANKS' => 'ranks',
|
||||
'MODERATE_REGISTRATIONS' => 'users',
|
||||
];
|
||||
|
||||
public function run($start = 0)
|
||||
{
|
||||
$this->comment('--- ROLES/GROUPS IMPORT ---');
|
||||
|
||||
/** @var \App\Services\RoleService $roleSvc */
|
||||
$roleSvc = app(RoleService::class);
|
||||
$permMappings = $this->getPermissions();
|
||||
|
||||
$count = 0;
|
||||
$permCount = 0;
|
||||
$rows = $this->db->readRows($this->table, $this->idField, $start);
|
||||
foreach ($rows as $row) {
|
||||
// Legacy "administrator" role is now "admin", just map that 1:1
|
||||
if (strtolower($row->name) === 'administrators') {
|
||||
$role = Role::where('name', 'admin')->first();
|
||||
$this->idMapper->addMapping('group', $row->groupid, $role->id);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Map the "core" roles, which are active/inactive pilots to a new ID of
|
||||
// -1; so then we can ignore/not add these groups, and then ignore them
|
||||
// for any of the users that are being imported. these groups are unused
|
||||
if ($row->core === 1 || $row->core === '1') {
|
||||
$this->idMapper->addMapping('group', $row->groupid, -1);
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = str_slug($row->name);
|
||||
$role = Role::firstOrCreate(
|
||||
['name' => $name],
|
||||
['display_name' => $row->name]
|
||||
);
|
||||
|
||||
$this->idMapper->addMapping('group', $row->groupid, $role->id);
|
||||
|
||||
// See if the permission set mask contains one of the mappings above
|
||||
// Add all of the ones which apply, and then set them on the new role
|
||||
$permissions = [];
|
||||
foreach ($this->legacy_permission_set as $legacy_name => $mask) {
|
||||
$val = $row->permissions & $mask;
|
||||
if ($val === $mask) {
|
||||
// Map this legacy permission to what it is under the new system
|
||||
if (!array_key_exists($legacy_name, $this->legacy_to_permission)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the ID of the permission
|
||||
try {
|
||||
$permName = $this->legacy_to_permission[$legacy_name];
|
||||
if ($permName === 'admin') {
|
||||
foreach ($permMappings as $name => $value) {
|
||||
if (!in_array($value, $permissions)) {
|
||||
$permissions[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$permMapId = $permMappings[$permName];
|
||||
if (!in_array($permMapId, $permissions)) {
|
||||
$permissions[] = $permMapId;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($permissions) > 0) {
|
||||
$roleSvc->setPermissionsForRole($role, $permissions);
|
||||
$permCount += count($permissions);
|
||||
}
|
||||
|
||||
if ($role->wasRecentlyCreated) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Imported '.$count.' roles, synced '.$permCount.' permissions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the permissions from locally and return a kvp with the
|
||||
* key being the permission short-name and the value being the ID
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getPermissions(): array
|
||||
{
|
||||
$mappings = [];
|
||||
$permissions = Permission::all();
|
||||
/** @var \App\Models\Permission $p */
|
||||
foreach ($permissions as $p) {
|
||||
$mappings[$p->name] = $p->id;
|
||||
}
|
||||
|
||||
return $mappings;
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Services\Importers;
|
||||
|
||||
use App\Models\Pirep;
|
||||
use App\Services\FinanceService;
|
||||
use App\Support\Money;
|
||||
use Modules\Importer\Services\BaseImporter;
|
||||
|
||||
class LedgerImporter extends BaseImporter
|
||||
{
|
||||
protected $table = 'ledger';
|
||||
|
||||
/**
|
||||
* Legacy ID to the current ledger ref_model mapping
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $legacy_paysource = [
|
||||
1 => Pirep::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws \Prettus\Validator\Exceptions\ValidatorException
|
||||
*/
|
||||
public function run($start = 0)
|
||||
{
|
||||
if (!$this->db->tableExists('ledger')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->comment('--- LEDGER IMPORT ---');
|
||||
|
||||
/** @var FinanceService $financeSvc */
|
||||
$financeSvc = app(FinanceService::class);
|
||||
|
||||
$count = 0;
|
||||
$rows = $this->db->readRows($this->table, $this->idField, $start);
|
||||
foreach ($rows as $row) {
|
||||
$pirep = Pirep::find($this->idMapper->getMapping('pireps', $row->pirepid));
|
||||
if (!$pirep) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$pilot_pay = Money::createFromAmount($row->amount * 100);
|
||||
$memo = 'Pilot payment';
|
||||
|
||||
$financeSvc->debitFromJournal(
|
||||
$pirep->airline->journal,
|
||||
$pilot_pay,
|
||||
$pirep,
|
||||
$memo,
|
||||
'Pilot Pay',
|
||||
'pilot_pay',
|
||||
$row->submitdate
|
||||
);
|
||||
|
||||
$financeSvc->creditToJournal(
|
||||
$pirep->user->journal,
|
||||
$pilot_pay,
|
||||
$pirep,
|
||||
$memo,
|
||||
'Pilot Pay',
|
||||
'pilot_pay',
|
||||
$row->submitdate
|
||||
);
|
||||
|
||||
$count++;
|
||||
}
|
||||
|
||||
$this->info('Imported '.$count.' ledger entries');
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Services\Importers;
|
||||
|
||||
use App\Models\Enums\FlightType;
|
||||
use App\Models\Enums\PirepSource;
|
||||
use App\Models\Enums\PirepState;
|
||||
use App\Models\Pirep;
|
||||
use Modules\Importer\Services\BaseImporter;
|
||||
|
||||
class PirepImporter extends BaseImporter
|
||||
{
|
||||
protected $table = 'pireps';
|
||||
protected $idField = 'pirepid';
|
||||
|
||||
public function run($start = 0)
|
||||
{
|
||||
$this->comment('--- PIREP IMPORT ---');
|
||||
|
||||
$fields = [
|
||||
'pirepid',
|
||||
'pilotid',
|
||||
'code',
|
||||
'aircraft',
|
||||
'flightnum',
|
||||
'depicao',
|
||||
'arricao',
|
||||
'fuelused',
|
||||
'route',
|
||||
'source',
|
||||
'accepted',
|
||||
'submitdate',
|
||||
'distance',
|
||||
'flighttime_stamp',
|
||||
'flighttype',
|
||||
];
|
||||
|
||||
// See if there's a flightlevel column, export that if there is
|
||||
$columns = $this->db->getColumns($this->table);
|
||||
if (in_array('flightlevel', $columns)) {
|
||||
$fields[] = 'flightlevel';
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
$rows = $this->db->readRows($this->table, $this->idField, $start, $fields);
|
||||
foreach ($rows as $row) {
|
||||
$pirep_id = $row->pirepid;
|
||||
$user_id = $this->idMapper->getMapping('users', $row->pilotid);
|
||||
$airline_id = $this->idMapper->getMapping('airlines', $row->code);
|
||||
$aircraft_id = $this->idMapper->getMapping('aircraft', $row->aircraft);
|
||||
|
||||
$attrs = [
|
||||
'user_id' => $user_id,
|
||||
'airline_id' => $airline_id,
|
||||
'aircraft_id' => $aircraft_id,
|
||||
'flight_number' => $row->flightnum ?: '',
|
||||
'dpt_airport_id' => $row->depicao,
|
||||
'arr_airport_id' => $row->arricao,
|
||||
'block_fuel' => $row->fuelused,
|
||||
'route' => $row->route ?: '',
|
||||
'source_name' => $row->source,
|
||||
'state' => $this->mapState($row->accepted),
|
||||
'created_at' => $this->parseDate($row->submitdate),
|
||||
'updated_at' => $this->parseDate($row->submitdate),
|
||||
];
|
||||
|
||||
// Set the distance
|
||||
$distance = round($row->distance ?: 0, 2);
|
||||
$attrs['distance'] = $distance;
|
||||
$attrs['planned_distance'] = $distance;
|
||||
|
||||
// Set the flight time properly
|
||||
$duration = $this->convertDuration($row->flighttime_stamp);
|
||||
$attrs['flight_time'] = $duration;
|
||||
$attrs['planned_flight_time'] = $duration;
|
||||
|
||||
// Set how it was filed
|
||||
if (strtoupper($row->source) === 'MANUAL') {
|
||||
$attrs['source'] = PirepSource::MANUAL;
|
||||
} else {
|
||||
$attrs['source'] = PirepSource::ACARS;
|
||||
}
|
||||
|
||||
// Set the flight type
|
||||
$row->flighttype = strtoupper($row->flighttype);
|
||||
if ($row->flighttype === 'P') {
|
||||
$attrs['flight_type'] = FlightType::SCHED_PAX;
|
||||
} elseif ($row->flighttype === 'C') {
|
||||
$attrs['flight_type'] = FlightType::SCHED_CARGO;
|
||||
} else {
|
||||
$attrs['flight_type'] = FlightType::CHARTER_PAX_ONLY;
|
||||
}
|
||||
|
||||
// Set the flight level of the PIREP is set
|
||||
if (property_exists($row, 'flightlevel')) {
|
||||
$attrs['level'] = $row->flightlevel;
|
||||
} else {
|
||||
$attrs['level'] = 0;
|
||||
}
|
||||
|
||||
$w = ['id' => $pirep_id];
|
||||
|
||||
$pirep = Pirep::updateOrCreate($w, $attrs);
|
||||
//$pirep = Pirep::create(array_merge($w, $attrs));
|
||||
|
||||
//Log::debug('pirep oldid='.$pirep_id.', olduserid='.$row->pilotid
|
||||
// .'; new id='.$pirep->id.', user id='.$user_id);
|
||||
|
||||
$source = strtoupper($row->source);
|
||||
if ($source === 'SMARTCARS') {
|
||||
// TODO: Parse smartcars log into the acars table
|
||||
} elseif ($source === 'KACARS') {
|
||||
// TODO: Parse kACARS log into acars table
|
||||
} elseif ($source === 'XACARS') {
|
||||
// TODO: Parse XACARS log into acars table
|
||||
}
|
||||
|
||||
// TODO: Add extra fields in as PIREP fields
|
||||
$this->idMapper->addMapping('pireps', $row->pirepid, $pirep->id);
|
||||
|
||||
if ($pirep->wasRecentlyCreated) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Imported '.$count.' pireps');
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the old status to the current
|
||||
* https://github.com/nabeelio/phpvms_v2/blob/master/core/app.config.php#L450
|
||||
*
|
||||
* @param int $old_state
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function mapState($old_state)
|
||||
{
|
||||
$map = [
|
||||
0 => PirepState::PENDING,
|
||||
1 => PirepState::ACCEPTED,
|
||||
2 => PirepState::REJECTED,
|
||||
3 => PirepState::IN_PROGRESS,
|
||||
];
|
||||
|
||||
$old_state = (int) $old_state;
|
||||
if (!in_array($old_state, $map)) {
|
||||
return PirepState::PENDING;
|
||||
}
|
||||
|
||||
return $map[$old_state];
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Services\Importers;
|
||||
|
||||
use App\Models\Rank;
|
||||
use Modules\Importer\Services\BaseImporter;
|
||||
|
||||
class RankImport extends BaseImporter
|
||||
{
|
||||
protected $table = 'ranks';
|
||||
protected $idField = 'rankid';
|
||||
|
||||
public function run($start = 0)
|
||||
{
|
||||
$this->comment('--- RANK IMPORT ---');
|
||||
|
||||
$count = 0;
|
||||
$rows = $this->db->readRows($this->table, $this->idField, $start);
|
||||
foreach ($rows as $row) {
|
||||
$rank = Rank::updateOrCreate(['name' => $row->rank], [
|
||||
'image_url' => $row->rankimage,
|
||||
'hours' => $row->minhours,
|
||||
'acars_base_payrate' => $row->payrate,
|
||||
'manual_base_payrate' => $row->payrate,
|
||||
]);
|
||||
|
||||
$this->idMapper->addMapping('ranks', $row->rankid, $rank->id);
|
||||
$this->idMapper->addMapping('ranks', $row->rank, $rank->id);
|
||||
|
||||
if ($rank->wasRecentlyCreated) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Imported '.$count.' ranks');
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Services\Importers;
|
||||
|
||||
use App\Repositories\SettingRepository;
|
||||
use Modules\Importer\Services\BaseImporter;
|
||||
|
||||
class SettingsImporter extends BaseImporter
|
||||
{
|
||||
protected $table = 'settings';
|
||||
|
||||
public function run($start = 0)
|
||||
{
|
||||
$this->comment('--- SETTINGS IMPORT ---');
|
||||
|
||||
/** @var SettingRepository $settingsRepo */
|
||||
$settingsRepo = app(SettingRepository::class);
|
||||
|
||||
$count = 0;
|
||||
$rows = $this->db->readRows($this->table, $this->idField, $start);
|
||||
foreach ($rows as $row) {
|
||||
switch ($row->name) {
|
||||
case 'ADMIN_EMAIL':
|
||||
$settingsRepo->store('general.admin_email', $row->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Imported '.$count.' settings');
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Services\Importers;
|
||||
|
||||
use App\Models\Enums\UserState;
|
||||
use App\Models\Role;
|
||||
use App\Models\User;
|
||||
use App\Services\UserService;
|
||||
use App\Support\Units\Time;
|
||||
use App\Support\Utils;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Str;
|
||||
use Modules\Importer\Services\BaseImporter;
|
||||
|
||||
class UserImport extends BaseImporter
|
||||
{
|
||||
protected $table = 'pilots';
|
||||
protected $idField = 'pilotid';
|
||||
|
||||
/**
|
||||
* @var UserService
|
||||
*/
|
||||
private $userSvc;
|
||||
|
||||
public function run($start = 0)
|
||||
{
|
||||
$this->comment('--- USER IMPORT ---');
|
||||
|
||||
$this->userSvc = app(UserService::class);
|
||||
|
||||
$count = 0;
|
||||
$first_row = true;
|
||||
$rows = $this->db->readRows($this->table, $this->idField, $start);
|
||||
foreach ($rows as $row) {
|
||||
$pilot_id = $row->pilotid; // This isn't their actual ID
|
||||
$name = $row->firstname.' '.$row->lastname;
|
||||
|
||||
// Figure out which airline, etc, they belong to
|
||||
$airline_id = $this->idMapper->getMapping('airlines', $row->code);
|
||||
// Log::info('User airline from '.$row->code.' to ID '.$airline_id);
|
||||
|
||||
$rank_id = $this->idMapper->getMapping('ranks', $row->rank);
|
||||
$state = $this->getUserState($row->retired);
|
||||
|
||||
if ($first_row) {
|
||||
$new_password = 'admin';
|
||||
$first_row = false;
|
||||
} else {
|
||||
$new_password = Str::random(60);
|
||||
}
|
||||
|
||||
// Look for a user with that pilot ID already. If someone has it
|
||||
if ($this->userSvc->isPilotIdAlreadyUsed($pilot_id)) {
|
||||
Log::info('User with pilot id '.$pilot_id.' exists');
|
||||
|
||||
// Is this the same user? If not, get a new pilot ID
|
||||
$user_exist = User::where('pilot_id', $pilot_id)->first();
|
||||
if ($user_exist->email !== $row->email) {
|
||||
$pilot_id = $this->userSvc->getNextAvailablePilotId();
|
||||
}
|
||||
}
|
||||
|
||||
$attrs = [
|
||||
'pilot_id' => $pilot_id,
|
||||
'name' => $name,
|
||||
'password' => Hash::make($new_password),
|
||||
'api_key' => Utils::generateApiKey(),
|
||||
'airline_id' => $airline_id,
|
||||
'rank_id' => $rank_id,
|
||||
'home_airport_id' => $row->hub,
|
||||
'curr_airport_id' => $row->hub,
|
||||
'country' => $row->location,
|
||||
'flights' => (int) $row->totalflights,
|
||||
'flight_time' => Time::hoursToMinutes($row->totalhours),
|
||||
'state' => $state,
|
||||
'created_at' => $this->parseDate($row->joindate),
|
||||
];
|
||||
|
||||
$user = User::updateOrCreate(['email' => $row->email], $attrs);
|
||||
$this->idMapper->addMapping('users', $row->pilotid, $user->id);
|
||||
|
||||
$this->updateUserRoles($user, $row->pilotid);
|
||||
|
||||
if ($user->wasRecentlyCreated) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Imported '.$count.' users');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a user's roles and add them to the proper ones
|
||||
*
|
||||
* @param User $user
|
||||
* @param string $old_pilot_id
|
||||
*/
|
||||
protected function updateUserRoles(User $user, $old_pilot_id)
|
||||
{
|
||||
// Be default add them to the user role, and then determine if they
|
||||
// belong to any other groups, and add them to that
|
||||
$newRoles = [];
|
||||
|
||||
// Figure out what other groups they belong to... read from the old table, and map
|
||||
// them to the new group(s)
|
||||
$old_user_groups = $this->db->findBy('groupmembers', ['pilotid' => $old_pilot_id]);
|
||||
foreach ($old_user_groups as $oldGroup) {
|
||||
$newRoleId = $this->idMapper->getMapping('group', $oldGroup->groupid);
|
||||
|
||||
// This role should be ignored
|
||||
if ($newRoleId === -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$newRoles[] = $newRoleId;
|
||||
}
|
||||
|
||||
// Assign the groups to the new user
|
||||
$user->attachRoles($newRoles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's new state from their original state
|
||||
*
|
||||
* @param $state
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getUserState($state)
|
||||
{
|
||||
// Return active for now, let the stats/cron determine the status later
|
||||
return UserState::ACTIVE;
|
||||
/*$state = (int) $state;
|
||||
|
||||
// Declare array of classic states
|
||||
$phpvms_classic_states = [
|
||||
'ACTIVE' => 0,
|
||||
'INACTIVE' => 1,
|
||||
'BANNED' => 2,
|
||||
'ON_LEAVE' => 3,
|
||||
];
|
||||
|
||||
// Decide which state they will be in accordance with v7
|
||||
if ($state === $phpvms_classic_states['ACTIVE']) {
|
||||
return UserState::ACTIVE;
|
||||
}
|
||||
|
||||
if ($state === $phpvms_classic_states['INACTIVE']) {
|
||||
// TODO: Make an inactive state?
|
||||
return UserState::REJECTED;
|
||||
}
|
||||
|
||||
if ($state === $phpvms_classic_states['BANNED']) {
|
||||
return UserState::SUSPENDED;
|
||||
}
|
||||
|
||||
if ($state === $phpvms_classic_states['ON_LEAVE']) {
|
||||
return UserState::ON_LEAVE;
|
||||
}
|
||||
|
||||
$this->error('Unknown status: '.$state);
|
||||
|
||||
return UserState::ACTIVE;*/
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Utils;
|
||||
|
||||
use App\Contracts\Service;
|
||||
use Spatie\Valuestore\Valuestore;
|
||||
|
||||
class IdMapper extends Service
|
||||
{
|
||||
private $valueStore;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->valueStore = Valuestore::make(storage_path('app/legacy_migration.json'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new mapping between an old ID and the new one
|
||||
*
|
||||
* @param string $entity Name of the entity (e,g table)
|
||||
* @param string $old_id
|
||||
* @param string $new_id
|
||||
*/
|
||||
public function addMapping($entity, $old_id, $new_id)
|
||||
{
|
||||
$key_name = $entity.'_'.$old_id;
|
||||
if (!$this->valueStore->has($key_name)) {
|
||||
$this->valueStore->put($key_name, $new_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ID for a mapping
|
||||
*
|
||||
* @param $entity
|
||||
* @param $old_id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getMapping($entity, $old_id)
|
||||
{
|
||||
$key_name = $entity.'_'.$old_id;
|
||||
if (!$this->valueStore->has($key_name)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $this->valueStore->get($key_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the value store
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->valueStore->flush();
|
||||
}
|
||||
}
|
||||
@@ -1,256 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Importer\Utils;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
|
||||
/**
|
||||
* Real basic to interface with an importer
|
||||
*/
|
||||
class ImporterDB
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $batchSize;
|
||||
|
||||
/**
|
||||
* @var PDO
|
||||
*/
|
||||
private $conn;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $dsn;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $creds;
|
||||
|
||||
public function __construct($creds)
|
||||
{
|
||||
$this->creds = $creds;
|
||||
$this->dsn = 'mysql:'.implode(';', [
|
||||
'host='.$this->creds['host'],
|
||||
'port='.$this->creds['port'],
|
||||
'dbname='.$this->creds['name'],
|
||||
]);
|
||||
|
||||
Log::info('Using DSN: '.$this->dsn);
|
||||
|
||||
$this->batchSize = config('installer.importer.batch_size', 20);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->close();
|
||||
}
|
||||
|
||||
public function connect()
|
||||
{
|
||||
try {
|
||||
$this->conn = new PDO($this->dsn, $this->creds['user'], $this->creds['pass']);
|
||||
$this->conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
|
||||
} catch (PDOException $e) {
|
||||
Log::error($e);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
if ($this->conn) {
|
||||
$this->conn = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the table name with the prefix
|
||||
*
|
||||
* @param $table
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function tableName($table)
|
||||
{
|
||||
if ($this->creds['table_prefix'] !== false) {
|
||||
return $this->creds['table_prefix'].$table;
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a table exist? Try to get the column information on it.
|
||||
* The result will be 'false' if that table isn't there
|
||||
*
|
||||
* @param $table
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function tableExists($table): bool
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$sql = 'SHOW COLUMNS FROM '.$this->tableName($table);
|
||||
$result = $this->conn->query($sql);
|
||||
if (!$result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the names of the columns for a particular table
|
||||
*
|
||||
* @param $table
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getColumns($table)
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$sql = 'SHOW COLUMNS FROM '.$this->tableName($table);
|
||||
$result = $this->conn->query($sql)->fetchAll();
|
||||
|
||||
$rows = [];
|
||||
foreach ($result as $row) {
|
||||
$rows[] = $row->Field;
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $table
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getTotalRows($table)
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$sql = 'SELECT COUNT(*) FROM '.$this->tableName($table);
|
||||
$rows = $this->conn->query($sql)->fetchColumn();
|
||||
|
||||
Log::info('Found '.$rows.' rows in '.$table);
|
||||
|
||||
return (int) $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read rows from a table with a given assoc array. Simple
|
||||
*
|
||||
* @param string $table
|
||||
* @param array $attrs
|
||||
*
|
||||
* @return false|\PDOStatement
|
||||
*/
|
||||
public function findBy($table, array $attrs)
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$where = [];
|
||||
foreach ($attrs as $col => $value) {
|
||||
$where[] = $col.'=\''.$value.'\'';
|
||||
}
|
||||
|
||||
$where = implode(' AND ', $where);
|
||||
|
||||
$sql = implode(' ', [
|
||||
'SELECT',
|
||||
'*',
|
||||
'FROM',
|
||||
$this->tableName($table),
|
||||
'WHERE',
|
||||
$where,
|
||||
]);
|
||||
|
||||
return $this->conn->query($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read all the rows in a table, but read them in a batched manner
|
||||
*
|
||||
* @param string $table The name of the table
|
||||
* @param string $order_by Column to order by
|
||||
* @param int $start_offset
|
||||
* @param string $fields
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function readRows($table, $order_by = 'id', $start_offset = 0, $fields = '*')
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$offset = $start_offset;
|
||||
// $total_rows = $this->getTotalRows($table);
|
||||
|
||||
$rows = [];
|
||||
$result = $this->readRowsOffset($table, $this->batchSize, $offset, $order_by, $fields);
|
||||
if ($result === false || $result === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
foreach ($result as $row) {
|
||||
$rows[] = $row;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error('foreach rows error: '.$e->getMessage());
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @param int $limit Number of rows to read
|
||||
* @param int $offset Where to start from
|
||||
* @param $order_by
|
||||
* @param string $fields
|
||||
*
|
||||
* @return false|\PDOStatement|void
|
||||
*/
|
||||
public function readRowsOffset($table, $limit, $offset, $order_by, $fields = '*')
|
||||
{
|
||||
if (is_array($fields)) {
|
||||
$fields = implode(',', $fields);
|
||||
}
|
||||
|
||||
$sql = implode(' ', [
|
||||
'SELECT',
|
||||
$fields,
|
||||
'FROM',
|
||||
$this->tableName($table),
|
||||
'ORDER BY '.$order_by.' ASC',
|
||||
'LIMIT '.$limit,
|
||||
'OFFSET '.$offset,
|
||||
]);
|
||||
|
||||
try {
|
||||
$result = $this->conn->query($sql);
|
||||
if (!$result || $result->rowCount() === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
return $result;
|
||||
} catch (PDOException $e) {
|
||||
// Without incrementing the offset, it should re-run the same query
|
||||
Log::error('Error readRowsOffset: '.$e->getMessage());
|
||||
|
||||
if (strpos($e->getMessage(), 'server has gone away') !== false) {
|
||||
$this->connect();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error readRowsOffset: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "phpvms/importer",
|
||||
"type": "laravel-library",
|
||||
"description": "The importer module for phpVMS",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nabeel Shahzad",
|
||||
"email": ""
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"composer/installers": "~1.0"
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Modules\\Importer\\Providers\\ImporterServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Modules\\Importer\\": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": "Importer",
|
||||
"alias": "importer",
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
"active": 1,
|
||||
"order": 0,
|
||||
"providers": [
|
||||
"Modules\\Importer\\Providers\\ImporterServiceProvider"
|
||||
],
|
||||
"aliases": {},
|
||||
"files": [],
|
||||
"requires": []
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'php' => [
|
||||
'version' => '7.3',
|
||||
],
|
||||
|
||||
'cache' => [
|
||||
// Default driver to use when no driver is present
|
||||
'default' => 'file',
|
||||
'drivers' => [
|
||||
// 'Zend OPcache' => 'opcache',
|
||||
// 'apc' => 'apc',
|
||||
],
|
||||
],
|
||||
|
||||
'extensions' => [
|
||||
// 'bcmath',
|
||||
'fileinfo',
|
||||
'openssl',
|
||||
'pdo',
|
||||
'mbstring',
|
||||
'tokenizer',
|
||||
'json',
|
||||
'curl',
|
||||
],
|
||||
|
||||
// Make sure these are writable
|
||||
'permissions' => [
|
||||
base_path('bootstrap/cache'),
|
||||
public_path('uploads'),
|
||||
storage_path(),
|
||||
storage_path('app/public'),
|
||||
storage_path('app/public/avatars'),
|
||||
storage_path('app/public/uploads'),
|
||||
storage_path('framework'),
|
||||
storage_path('framework/cache'),
|
||||
storage_path('framework/sessions'),
|
||||
storage_path('framework/views'),
|
||||
storage_path('logs'),
|
||||
],
|
||||
];
|
||||
@@ -1,357 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Installer\Http\Controllers;
|
||||
|
||||
use App\Contracts\Controller;
|
||||
use App\Services\AirlineService;
|
||||
use App\Services\AnalyticsService;
|
||||
use App\Services\Installer\DatabaseService;
|
||||
use App\Services\Installer\InstallerService;
|
||||
use App\Services\Installer\MigrationService;
|
||||
use App\Services\Installer\SeederService;
|
||||
use App\Services\UserService;
|
||||
use App\Support\Countries;
|
||||
use App\Support\Utils;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Laracasts\Flash\Flash;
|
||||
use Modules\Installer\Services\ConfigService;
|
||||
use Modules\Installer\Services\RequirementsService;
|
||||
|
||||
class InstallerController extends Controller
|
||||
{
|
||||
private $airlineSvc;
|
||||
private $analyticsSvc;
|
||||
private $dbSvc;
|
||||
private $envSvc;
|
||||
private $migrationSvc;
|
||||
private $reqSvc;
|
||||
private $seederSvc;
|
||||
private $userService;
|
||||
|
||||
/**
|
||||
* InstallerController constructor.
|
||||
*
|
||||
* @param AirlineService $airlineSvc
|
||||
* @param AnalyticsService $analyticsSvc
|
||||
* @param DatabaseService $dbService
|
||||
* @param ConfigService $envService
|
||||
* @param MigrationService $migrationSvc
|
||||
* @param RequirementsService $reqSvc
|
||||
* @param SeederService $seederSvc
|
||||
* @param UserService $userService
|
||||
*/
|
||||
public function __construct(
|
||||
AirlineService $airlineSvc,
|
||||
AnalyticsService $analyticsSvc,
|
||||
DatabaseService $dbService,
|
||||
ConfigService $envService,
|
||||
MigrationService $migrationSvc,
|
||||
RequirementsService $reqSvc,
|
||||
SeederService $seederSvc,
|
||||
UserService $userService
|
||||
) {
|
||||
$this->airlineSvc = $airlineSvc;
|
||||
$this->analyticsSvc = $analyticsSvc;
|
||||
$this->dbSvc = $dbService;
|
||||
$this->envSvc = $envService;
|
||||
$this->migrationSvc = $migrationSvc;
|
||||
$this->reqSvc = $reqSvc;
|
||||
$this->seederSvc = $seederSvc;
|
||||
$this->userService = $userService;
|
||||
|
||||
Utils::disableDebugToolbar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if (config('app.key') !== 'base64:zdgcDqu9PM8uGWCtMxd74ZqdGJIrnw812oRMmwDF6KY=') {
|
||||
return view('installer::errors/already-installed');
|
||||
}
|
||||
|
||||
return view('installer::install/index-start');
|
||||
}
|
||||
|
||||
protected function testDb(Request $request)
|
||||
{
|
||||
$this->dbSvc->checkDbConnection(
|
||||
$request->post('db_conn'),
|
||||
$request->post('db_host'),
|
||||
$request->post('db_port'),
|
||||
$request->post('db_name'),
|
||||
$request->post('db_user'),
|
||||
$request->post('db_pass')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the database connection
|
||||
*/
|
||||
public function dbtest(Request $request)
|
||||
{
|
||||
$status = 'success'; // success|warn|danger
|
||||
$message = 'Database connection looks good!';
|
||||
|
||||
try {
|
||||
$this->testDb($request);
|
||||
} catch (\Exception $e) {
|
||||
$status = 'danger';
|
||||
$message = 'Failed! '.$e->getMessage();
|
||||
}
|
||||
|
||||
return view('installer::install/dbtest', [
|
||||
'status' => $status,
|
||||
'message' => $message,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any of the items has been marked as failed
|
||||
*
|
||||
* @param array $arr
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function allPassed(array $arr): bool
|
||||
{
|
||||
foreach ($arr as $item) {
|
||||
if ($item['passed'] === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 1. Check the modules and permissions
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function step1(Request $request)
|
||||
{
|
||||
$php_version = $this->reqSvc->checkPHPVersion();
|
||||
$extensions = $this->reqSvc->checkExtensions();
|
||||
$directories = $this->reqSvc->checkPermissions();
|
||||
|
||||
// Only pass if all the items in the ext and dirs are passed
|
||||
$statuses = [
|
||||
$php_version['passed'] === true,
|
||||
$this->allPassed($extensions) === true,
|
||||
$this->allPassed($directories) === true,
|
||||
];
|
||||
|
||||
// Make sure there are no false values
|
||||
$passed = !\in_array(false, $statuses, true);
|
||||
|
||||
return view('installer::install/steps/step1-requirements', [
|
||||
'php' => $php_version,
|
||||
'extensions' => $extensions,
|
||||
'directories' => $directories,
|
||||
'passed' => $passed,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 2. Database Setup
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function step2(Request $request)
|
||||
{
|
||||
$db_types = ['mysql' => 'mysql', 'sqlite' => 'sqlite'];
|
||||
return view('installer::install/steps/step2-db', [
|
||||
'db_types' => $db_types,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 2a. Create the .env
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function envsetup(Request $request)
|
||||
{
|
||||
$log_str = $request->post();
|
||||
$log_str['db_pass'] = '';
|
||||
|
||||
Log::info('ENV setup', $log_str);
|
||||
|
||||
// Before writing out the env file, test the DB credentials
|
||||
try {
|
||||
$this->testDb($request);
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Testing db before writing configs failed');
|
||||
Log::error($e->getMessage());
|
||||
|
||||
Flash::error($e->getMessage());
|
||||
return redirect(route('installer.step2'))->withInput();
|
||||
}
|
||||
|
||||
// Now write out the env file
|
||||
$attrs = [
|
||||
'SITE_NAME' => $request->post('site_name'),
|
||||
'SITE_URL' => $request->post('site_url'),
|
||||
'DB_CONN' => $request->post('db_conn'),
|
||||
'DB_HOST' => $request->post('db_host'),
|
||||
'DB_PORT' => $request->post('db_port'),
|
||||
'DB_NAME' => $request->post('db_name'),
|
||||
'DB_USER' => $request->post('db_user'),
|
||||
'DB_PASS' => $request->post('db_pass'),
|
||||
'DB_PREFIX' => $request->post('db_prefix'),
|
||||
];
|
||||
|
||||
/*
|
||||
* Create the config files and then redirect so that the
|
||||
* framework can pickup all those configs, etc, before we
|
||||
* setup the database and stuff
|
||||
*/
|
||||
try {
|
||||
$this->envSvc->createConfigFiles($attrs);
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Config files failed to write');
|
||||
Log::error($e->getMessage());
|
||||
|
||||
Flash::error($e->getMessage());
|
||||
return redirect(route('installer.step2'))->withInput();
|
||||
}
|
||||
|
||||
// Needs to redirect so it can load the new .env
|
||||
Log::info('Redirecting to database setup');
|
||||
return redirect(route('installer.dbsetup'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 2b. Setup the database
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function dbsetup(Request $request)
|
||||
{
|
||||
$console_out = '';
|
||||
|
||||
try {
|
||||
$console_out .= $this->dbSvc->setupDB();
|
||||
$console_out .= $this->migrationSvc->runAllMigrations();
|
||||
$this->seederSvc->syncAllSeeds();
|
||||
} catch (QueryException $e) {
|
||||
Log::error('Error on db setup: '.$e->getMessage());
|
||||
dd($e);
|
||||
$this->envSvc->removeConfigFiles();
|
||||
Flash::error($e->getMessage());
|
||||
return redirect(route('installer.step2'))->withInput();
|
||||
}
|
||||
|
||||
$console_out = trim($console_out);
|
||||
|
||||
return view('installer::install/steps/step2a-db_output', [
|
||||
'console_output' => $console_out,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 3. Setup the admin user and initial settings
|
||||
*/
|
||||
public function step3(Request $request)
|
||||
{
|
||||
return view('installer::install/steps/step3-user', [
|
||||
'countries' => Countries::getSelectList(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 3 submit
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* @throws \Prettus\Validator\Exceptions\ValidatorException
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function usersetup(Request $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'airline_name' => 'required',
|
||||
'airline_icao' => 'required|size:3|unique:airlines,icao',
|
||||
'airline_country' => 'required',
|
||||
'name' => 'required',
|
||||
'email' => 'required|email|unique:users,email',
|
||||
'password' => 'required|confirmed',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return redirect('install/step3')
|
||||
->withErrors($validator)
|
||||
->withInput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the first airline
|
||||
*/
|
||||
$attrs = [
|
||||
'icao' => $request->get('airline_icao'),
|
||||
'name' => $request->get('airline_name'),
|
||||
'country' => $request->get('airline_country'),
|
||||
];
|
||||
|
||||
$airline = $this->airlineSvc->createAirline($attrs);
|
||||
|
||||
/**
|
||||
* Create the user, and associate to the airline
|
||||
* Ensure the seed data at least has one airport
|
||||
* KAUS, for giggles, though.
|
||||
*/
|
||||
$attrs = [
|
||||
'name' => $request->get('name'),
|
||||
'email' => $request->get('email'),
|
||||
'api_key' => Utils::generateApiKey(),
|
||||
'airline_id' => $airline->id,
|
||||
'password' => Hash::make($request->get('password')),
|
||||
];
|
||||
|
||||
$user = $this->userService->createUser($attrs, ['admin']);
|
||||
Log::info('User registered: ', $user->toArray());
|
||||
|
||||
// Set the initial admin e-mail address
|
||||
setting_save('general.admin_email', $user->email);
|
||||
|
||||
// Save telemetry setting
|
||||
setting_save('general.telemetry', get_truth_state($request->get('telemetry')));
|
||||
|
||||
// Try sending telemetry info
|
||||
$this->analyticsSvc->sendInstall();
|
||||
|
||||
return view('installer::install/steps/step3a-completed', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Final step
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function complete(Request $request)
|
||||
{
|
||||
$installerSvc = app(InstallerService::class);
|
||||
$installerSvc->disableInstallerModules();
|
||||
|
||||
return redirect('/login');
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Installer\Providers;
|
||||
|
||||
use App\Contracts\Modules\ServiceProvider;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
class InstallerServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Boot the application events.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
$this->registerRoutes();
|
||||
$this->registerTranslations();
|
||||
$this->registerConfig();
|
||||
$this->registerViews();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the routes
|
||||
*/
|
||||
protected function registerRoutes()
|
||||
{
|
||||
Route::group([
|
||||
'as' => 'installer.',
|
||||
'prefix' => 'install',
|
||||
'middleware' => ['web'],
|
||||
'namespace' => 'Modules\Installer\Http\Controllers',
|
||||
], function () {
|
||||
Route::get('/', 'InstallerController@index')->name('index');
|
||||
Route::post('/dbtest', 'InstallerController@dbtest')->name('dbtest');
|
||||
|
||||
Route::get('/step1', 'InstallerController@step1')->name('step1');
|
||||
Route::post('/step1', 'InstallerController@step1')->name('step1post');
|
||||
|
||||
Route::get('/step2', 'InstallerController@step2')->name('step2');
|
||||
Route::post('/envsetup', 'InstallerController@envsetup')->name('envsetup');
|
||||
Route::get('/dbsetup', 'InstallerController@dbsetup')->name('dbsetup');
|
||||
|
||||
Route::get('/step3', 'InstallerController@step3')->name('step3');
|
||||
Route::post('/usersetup', 'InstallerController@usersetup')->name('usersetup');
|
||||
|
||||
Route::get('/complete', 'InstallerController@complete')->name('complete');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register config.
|
||||
*/
|
||||
protected function registerConfig()
|
||||
{
|
||||
$this->mergeConfigFrom(__DIR__.'/../Config/config.php', 'installer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register views.
|
||||
*/
|
||||
public function registerViews()
|
||||
{
|
||||
$viewPath = resource_path('views/modules/installer');
|
||||
$sourcePath = __DIR__.'/../Resources/views';
|
||||
|
||||
$this->publishes([
|
||||
$sourcePath => $viewPath,
|
||||
], 'views');
|
||||
|
||||
$paths = array_map(
|
||||
function ($path) {
|
||||
return $path.'/modules/installer';
|
||||
},
|
||||
\Config::get('view.paths')
|
||||
);
|
||||
|
||||
$paths[] = $sourcePath;
|
||||
$this->loadViewsFrom($paths, 'installer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register translations.
|
||||
*/
|
||||
public function registerTranslations()
|
||||
{
|
||||
$langPath = resource_path('lang/modules/installer');
|
||||
|
||||
if (is_dir($langPath)) {
|
||||
$this->loadTranslationsFrom($langPath, 'installer');
|
||||
} else {
|
||||
$this->loadTranslationsFrom(__DIR__.'/../Resources/lang', 'installer');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*/
|
||||
public function provides(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>@yield('title') - installer</title>
|
||||
|
||||
<link rel="shortcut icon" type="image/png" href="{{ public_asset('/assets/img/favicon.png') }}"/>
|
||||
|
||||
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no'
|
||||
name='viewport'/>
|
||||
<meta name="base-url" content="{!! url('') !!}">
|
||||
<meta name="api-key" content="{!! Auth::check() ? Auth::user()->api_key: '' !!}">
|
||||
<meta name="csrf-token" content="{!! csrf_token() !!}">
|
||||
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700,200" rel="stylesheet"/>
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css" rel="stylesheet"/>
|
||||
|
||||
<link href="{{ public_asset('/assets/frontend/css/bootstrap.min.css') }}" rel="stylesheet"/>
|
||||
<link href="{{ public_asset('/assets/frontend/css/now-ui-kit.css') }}" rel="stylesheet"/>
|
||||
<link href="{{ public_asset('/assets/installer/css/vendor.css') }}" rel="stylesheet"/>
|
||||
<link href="{{ public_asset('/assets/frontend/css/styles.css') }}" rel="stylesheet"/>
|
||||
|
||||
<link rel="stylesheet"
|
||||
href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
|
||||
|
||||
<style>
|
||||
.table tr:first-child td {
|
||||
border-top: 0px;
|
||||
}
|
||||
@yield('css')
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="login-page" style="background: #067ec1;">
|
||||
<div class="page-header clear-filter">
|
||||
<div class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-8 ml-auto mr-auto content-center">
|
||||
<div class="p-10" style="padding: 10px 0;">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<img src="{{ public_asset('/assets/img/logo_blue_bg.svg') }}" width="135px" style="" alt=""/>
|
||||
</div>
|
||||
<div class="col-8 text-right">
|
||||
<h4 class="text-white mb-0 mr-0 ml-0" style="margin-top: 5px;">@yield('title')</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card card-login card-plain" style="background: #FFF">
|
||||
<div class="card-body">
|
||||
@include('installer::flash.message')
|
||||
@yield('content')
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{--<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>--}}
|
||||
|
||||
<script src="{{ public_mix('/assets/global/js/vendor.js') }}"></script>
|
||||
<script src="{{ public_mix('/assets/installer/js/vendor.js') }}"></script>
|
||||
<script src="{{ public_mix('/assets/installer/js/app.js') }}"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
|
||||
|
||||
<script>
|
||||
hljs.configure({languages: ['sh']});
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
$(".select2").select2();
|
||||
|
||||
$('pre code').each(function (i, block) {
|
||||
hljs.fixMarkup(block);
|
||||
hljs.highlightBlock(block);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@yield('scripts')
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,11 +0,0 @@
|
||||
@extends('installer::app')
|
||||
|
||||
@section('content')
|
||||
<h2>phpVMS already installed!</h2>
|
||||
<p>phpVMS has already been installed! Please remove the modules/Installer folder.</p>
|
||||
{{ Form::open(['url' => '/', 'method' => 'get']) }}
|
||||
<p style="text-align: right">
|
||||
{{ Form::submit('Go to your site >>', ['class' => 'btn btn-success']) }}
|
||||
</p>
|
||||
{{ Form::close() }}
|
||||
@endsection
|
||||
@@ -1,6 +0,0 @@
|
||||
@if($errors->has($field))
|
||||
<p class="text-danger" style="margin-top: 10px;">{{ $errors->first($field) }}</p>
|
||||
{{--<div class="alert alert-danger" role="alert" style="margin-top: 10px;">
|
||||
{{ $errors->first($field) }}
|
||||
</div>--}}
|
||||
@endif
|
||||
@@ -1,19 +0,0 @@
|
||||
@if (session()->has('flash_notification.message'))
|
||||
@if (session()->has('flash_notification.overlay'))
|
||||
@include('flash::modal', [
|
||||
'modalClass' => 'flash-modal',
|
||||
'title' => session('flash_notification.title'),
|
||||
'body' => session('flash_notification.message')
|
||||
])
|
||||
@else
|
||||
<div class="alert
|
||||
alert-{{ session('flash_notification.level') }}
|
||||
{{ session()->has('flash_notification.important') ? 'alert-important' : '' }}">
|
||||
@if(session()->has('flash_notification.important'))
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
@endif
|
||||
|
||||
{{ session('flash_notification.message') }}
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
@@ -1,8 +0,0 @@
|
||||
<div class="alert alert-{{ $status }}" role="alert">
|
||||
<div class="container">
|
||||
<div class="alert-icon">
|
||||
<i class="now-ui-icons ui-1_bell-53"></i>
|
||||
</div>
|
||||
{{ $message }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,11 +0,0 @@
|
||||
@extends('installer::app')
|
||||
@section('title', 'Install phpVMS')
|
||||
|
||||
@section('content')
|
||||
<p>Press continue to start</p>
|
||||
{{ Form::open(['route' => 'installer.step1post', 'method' => 'post']) }}
|
||||
<p style="text-align: right">
|
||||
{{ Form::submit('Start >>', ['class' => 'btn btn-success']) }}
|
||||
</p>
|
||||
{{ Form::close() }}
|
||||
@endsection
|
||||
@@ -1,64 +0,0 @@
|
||||
@extends('installer::app')
|
||||
@section('title', 'Requirements Check')
|
||||
|
||||
@section('content')
|
||||
<div style="align-content: center;">
|
||||
{{ Form::open(['route' => 'installer.step2', 'method' => 'GET']) }}
|
||||
<table class="table" width="25%">
|
||||
<tr>
|
||||
<td colspan="2"><h4>php version</h4></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PHP Version: {{ $php['version'] }}</td>
|
||||
<td style="text-align:right;">
|
||||
@if($php['passed'] === true)
|
||||
<span class="badge badge-success">OK</span>
|
||||
@else
|
||||
<span class="badge badge-danger">Failed</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><h4>PHP Extensions</h4></td>
|
||||
</tr>
|
||||
@foreach($extensions as $ext)
|
||||
<tr>
|
||||
<td>{{ $ext['ext'] }}</td>
|
||||
<td style="text-align:right;">
|
||||
@if($ext['passed'] === true)
|
||||
<span class="badge badge-success">OK</span>
|
||||
@else
|
||||
<span class="badge badge-danger">Failed</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<h4>Directory Permissions</h4>
|
||||
<p>Make sure these directories have read and write permissions</p>
|
||||
</td>
|
||||
</tr>
|
||||
@foreach($directories as $dir)
|
||||
<tr>
|
||||
<td>{{ $dir['dir'] }}</td>
|
||||
<td style="text-align:right;">
|
||||
@if($dir['passed'] === true)
|
||||
<span class="badge badge-success">OK</span>
|
||||
@else
|
||||
<span class="badge badge-danger">Failed</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</table>
|
||||
@if($passed === true)
|
||||
<p style="text-align: right">
|
||||
{{ Form::submit('Database Setup >>', ['class' => 'btn btn-success']) }}
|
||||
</p>
|
||||
@endif
|
||||
{{ Form::close() }}
|
||||
</div>
|
||||
@endsection
|
||||
@@ -1,161 +0,0 @@
|
||||
@extends('installer::app')
|
||||
@section('title', 'Database Setup')
|
||||
@section('content')
|
||||
<div style="align-content: center;">
|
||||
{{ Form::open(['route' => 'installer.envsetup', 'method' => 'POST']) }}
|
||||
<table class="table" width="25%">
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><h4>Site Config</h4></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Site Name</td>
|
||||
<td style="text-align:center;">
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'site_name', 'phpvms', ['class' => 'form-control']) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Site URL</td>
|
||||
<td style="text-align:center;">
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'site_url', Request::root(), ['class' => 'form-control']) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<h4>Database Config</h4>
|
||||
<p>Enter the target database information</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><p>Database Type</p></td>
|
||||
<td style="text-align:center;">
|
||||
<div class="form-group">
|
||||
{{ Form::select('db_conn', $db_types, null, ['class' => 'form-control', 'id' => 'db_conn']) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tbody id="mysql_settings" class="settings_panel">
|
||||
<tr>
|
||||
<td>Database Host</td>
|
||||
<td style="text-align:center;">
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'db_host', '127.0.0.1', ['class' => 'form-control']) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Database Port</td>
|
||||
<td style="text-align:center;">
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'db_port', '3306', ['class' => 'form-control']) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Database Name</td>
|
||||
<td style="text-align:center;">
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'db_name', 'phpvms', ['class' => 'form-control']) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Database User</td>
|
||||
<td style="text-align:center;">
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'db_user', null, ['class' => 'form-control']) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Database Password</td>
|
||||
<td style="text-align:center;">
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'db_pass', null, ['class' => 'form-control']) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2" style="text-align: right;">
|
||||
{{ Form::submit('Test Database Credentials', ['class' => 'btn btn-info', 'id' => 'dbtest_button']) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tbody id="sqlite_settings" class="settings_panel">
|
||||
|
||||
</tbody>
|
||||
|
||||
<tr>
|
||||
<td>Database Prefix</td>
|
||||
<td style="text-align:center;">
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'db_prefix', '', ['class' => 'form-control']) }}
|
||||
<p>Set this if you're sharing the database with another application.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<div id="dbtest"></div>
|
||||
<p style="text-align: right">
|
||||
{{ Form::submit('Setup Database >>', ['class' => 'btn btn-success']) }}
|
||||
</p>
|
||||
{{ Form::close() }}
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
function changeForm(selected) {
|
||||
$("tbody.settings_panel").hide();
|
||||
$("tbody#" + selected + "_settings").show();
|
||||
}
|
||||
|
||||
$(document).ready(() => {
|
||||
|
||||
const selValue = $("#db_conn option:selected").text();
|
||||
changeForm(selValue);
|
||||
|
||||
$("#db_conn").change((e) => {
|
||||
const selValue = $("#db_conn option:selected").text();
|
||||
changeForm(selValue);
|
||||
});
|
||||
|
||||
$("#dbtest_button").click((e) => {
|
||||
e.preventDefault();
|
||||
const opts = {
|
||||
method: 'POST',
|
||||
url: '/install/dbtest',
|
||||
data: {
|
||||
_token: "{{ csrf_token() }}",
|
||||
db_conn: 'mysql',
|
||||
db_host: $("input[name=db_host]").val(),
|
||||
db_port: $("input[name=db_port]").val(),
|
||||
db_name: $("input[name=db_name]").val(),
|
||||
db_user: $("input[name=db_user]").val(),
|
||||
db_pass: $("input[name=db_pass]").val(),
|
||||
},
|
||||
};
|
||||
|
||||
phpvms.request(opts).then(response => {
|
||||
$("#dbtest").html(response.data);
|
||||
});
|
||||
})
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -1,19 +0,0 @@
|
||||
@extends('installer::app')
|
||||
@section('title', 'Database Setup Completed')
|
||||
@section('content')
|
||||
<div style="align-content: center;">
|
||||
{{ Form::open(['route' => 'installer.step3', 'method' => 'GET']) }}
|
||||
|
||||
<pre class="lang-sh">
|
||||
<code class="lang-sh">
|
||||
{{--<code class="language-bash">--}}
|
||||
{{ $console_output }}
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<p style="text-align: right">
|
||||
{{ Form::submit('Continue >>', ['class' => 'btn btn-success']) }}
|
||||
</p>
|
||||
{{ Form::close() }}
|
||||
</div>
|
||||
@endsection
|
||||
@@ -1,119 +0,0 @@
|
||||
@extends('installer::app')
|
||||
@section('title', 'User Setup')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div style="align-content: center;">
|
||||
{{ Form::open(['route' => 'installer.usersetup', 'method' => 'POST']) }}
|
||||
<table class="table" width="25%">
|
||||
|
||||
<tr>
|
||||
<td colspan="2" style="text-align: right">
|
||||
<a href="{{ route('importer.index') }}">Importing from a legacy install?</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><h4>Airline Information</h4></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><p>Airline ICAO</p></td>
|
||||
<td>
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'airline_icao', null, ['class' => 'form-control']) }}
|
||||
@include('installer::flash/check_error', ['field' => 'airline_icao'])
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><p>Airline Name</p></td>
|
||||
<td>
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'airline_name', null, ['class' => 'form-control']) }}
|
||||
@include('installer::flash/check_error', ['field' => 'airline_name'])
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><p>Airline Country</p></td>
|
||||
<td>
|
||||
<div class="form-group">
|
||||
{{ Form::select('airline_country', $countries, null, ['class' => 'form-control select2' ]) }}
|
||||
@include('installer::flash/check_error', ['field' => 'airline_country'])
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><h4>First User</h4></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><p>Name</p></td>
|
||||
<td>
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'name', null, ['class' => 'form-control']) }}
|
||||
@include('installer::flash/check_error', ['field' => 'name'])
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><p>Email</p></td>
|
||||
<td>
|
||||
<div class="form-group">
|
||||
{{ Form::input('text', 'email', null, ['class' => 'form-control']) }}
|
||||
@include('installer::flash/check_error', ['field' => 'email'])
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><p>Password</p></td>
|
||||
<td>
|
||||
{{ Form::password('password', ['class' => 'form-control']) }}
|
||||
@include('installer::flash/check_error', ['field' => 'password'])
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td width="40%"><p>Password Confirm</p></td>
|
||||
<td>
|
||||
{{ Form::password('password_confirmation', ['class' => 'form-control']) }}
|
||||
@include('installer::flash/check_error', ['field' => 'password_confirmation'])
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><h4>Options</h4></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><p>Analytics</p></td>
|
||||
<td>
|
||||
<div class="form-group">
|
||||
{{ Form::hidden('telemetry', 0) }}
|
||||
{{ Form::checkbox('telemetry', 1, true, ['class' => 'form-control']) }}
|
||||
<br/>
|
||||
<p>
|
||||
Allows collection of analytics. They won't identify you, and helps us to track
|
||||
the PHP and database versions that are used, and help to figure out problems
|
||||
and slowdowns when vaCentral integration is enabled.
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id="dbtest"></div>
|
||||
<p style="text-align: right">
|
||||
{{ Form::submit('Complete Setup >>', ['class' => 'btn btn-success']) }}
|
||||
</p>
|
||||
{{ Form::close() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -1,20 +0,0 @@
|
||||
@extends('installer::app')
|
||||
@section('title', 'Installation Completed!')
|
||||
|
||||
@section('content')
|
||||
<div style="align-content: center;">
|
||||
{{ Form::open(['route' => 'installer.complete', 'method' => 'GET']) }}
|
||||
|
||||
<h4>Installer Completed!</h4>
|
||||
|
||||
<p>Edit the <span class="code">config.php</span> to fill in some additional settings. </p>
|
||||
<p>Click the button to proceed to the login screen!</p>
|
||||
|
||||
<p style="text-align: right">
|
||||
{{ Form::submit('Install Complete! Continue to Log-In >>',
|
||||
['class' => 'btn btn-success'])
|
||||
}}
|
||||
</p>
|
||||
{{ Form::close() }}
|
||||
</div>
|
||||
@endsection
|
||||
@@ -1,252 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Installer\Services;
|
||||
|
||||
use App\Contracts\Service;
|
||||
use Exception;
|
||||
use function extension_loaded;
|
||||
use Illuminate\Encryption\Encrypter;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use function is_bool;
|
||||
use Nwidart\Modules\Support\Stub;
|
||||
use PDO;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileException;
|
||||
|
||||
class ConfigService extends Service
|
||||
{
|
||||
/**
|
||||
* Create the .env file
|
||||
*
|
||||
* @param $attrs
|
||||
*
|
||||
* @throws \Symfony\Component\HttpFoundation\File\Exception\FileException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function createConfigFiles($attrs): bool
|
||||
{
|
||||
$opts = [
|
||||
'APP_ENV' => 'dev',
|
||||
'APP_KEY' => $this->createAppKey(),
|
||||
'SITE_NAME' => '',
|
||||
'SITE_URL' => 'http://phpvms.test',
|
||||
'CACHE_PREFIX' => '',
|
||||
'DB_CONN' => '',
|
||||
'DB_HOST' => '',
|
||||
'DB_PORT' => 3306,
|
||||
'DB_NAME' => '',
|
||||
'DB_USER' => '',
|
||||
'DB_PASS' => '',
|
||||
'DB_PREFIX' => '',
|
||||
'DB_EMULATE_PREPARES' => false,
|
||||
];
|
||||
|
||||
$opts = array_merge($opts, $attrs);
|
||||
|
||||
$opts = $this->determinePdoOptions($opts);
|
||||
$opts = $this->configCacheDriver($opts);
|
||||
$opts = $this->configQueueDriver($opts);
|
||||
|
||||
$this->writeConfigFiles($opts);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the environment file and update certain keys/values
|
||||
*
|
||||
* @param array $kvp
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function updateKeysInEnv(array $kvp)
|
||||
{
|
||||
$app = app();
|
||||
|
||||
$env_file = file_get_contents($app->environmentFilePath());
|
||||
foreach ($kvp as $key => $value) {
|
||||
$key = strtoupper($key);
|
||||
|
||||
// cast for any boolean values
|
||||
if (is_bool($value)) {
|
||||
$value = $value === true ? 'true' : 'false';
|
||||
}
|
||||
|
||||
// surround by quotes if there are any spaces in the value
|
||||
if (strpos($value, ' ') !== false) {
|
||||
$value = '"'.$value.'"';
|
||||
}
|
||||
|
||||
Log::info('Replacing "'.$key.'" with '.$value);
|
||||
|
||||
$env_file = preg_replace(
|
||||
'/^'.$key.'(.*)?/m',
|
||||
$key.'='.$value,
|
||||
$env_file
|
||||
);
|
||||
}
|
||||
|
||||
file_put_contents($app->environmentFilePath(), $env_file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a fresh new APP_KEY
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function createAppKey(): string
|
||||
{
|
||||
return base64_encode(Encrypter::generateKey(config('app.cipher')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a few options within the PDO driver, depending on the version
|
||||
* of mysql/maria, etc used. ATM, only make a change for MariaDB
|
||||
*
|
||||
* @param $opts
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function determinePdoOptions($opts)
|
||||
{
|
||||
if ($opts['DB_CONN'] !== 'mysql') {
|
||||
return $opts;
|
||||
}
|
||||
|
||||
$dsn = "mysql:host=$opts[DB_HOST];port=$opts[DB_PORT];";
|
||||
Log::info('Connection string: '.$dsn);
|
||||
|
||||
$conn = new PDO($dsn, $opts['DB_USER'], $opts['DB_PASS']);
|
||||
$version = strtolower($conn->getAttribute(PDO::ATTR_SERVER_VERSION));
|
||||
Log::info('Detected DB Version: '.$version);
|
||||
|
||||
// If it's mariadb, enable the emulation for prepared statements
|
||||
// seems to be throwing a problem on 000webhost
|
||||
// https://github.com/nabeelio/phpvms/issues/132
|
||||
if (strpos($version, 'mariadb') !== false) {
|
||||
Log::info('Detected MariaDB, setting DB_EMULATE_PREPARES to true');
|
||||
$opts['DB_EMULATE_PREPARES'] = true;
|
||||
}
|
||||
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine is APC is installed, if so, then use it as a cache driver
|
||||
*
|
||||
* @param $opts
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function configCacheDriver($opts)
|
||||
{
|
||||
// Set the cache prefix
|
||||
$opts['CACHE_PREFIX'] = uniqid($opts['SITE_NAME'].'_');
|
||||
|
||||
// Figure out what cache driver to initially use, depending on
|
||||
// what is installed. It won't detect redis or anything, though
|
||||
foreach (config('installer.cache.drivers') as $ext => $driver) {
|
||||
if (extension_loaded($ext)) {
|
||||
Log::info('Detected extension "'.$ext.'", setting driver to "'.$driver.'"');
|
||||
$opts['CACHE_DRIVER'] = $driver;
|
||||
return $opts;
|
||||
}
|
||||
}
|
||||
|
||||
Log::info('No extension detected, using file cache');
|
||||
$opts['CACHE_DRIVER'] = config('installer.cache.default');
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a queue driver that's not the default "sync"
|
||||
* driver, if a database is being used
|
||||
*
|
||||
* @param $opts
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function configQueueDriver($opts)
|
||||
{
|
||||
// If we're setting up a database, then also setup
|
||||
// the default queue driver to use the database
|
||||
if ($opts['DB_CONN'] === 'mysql' || $opts['DB_CONN'] === 'postgres') {
|
||||
$opts['QUEUE_DRIVER'] = 'database';
|
||||
} else {
|
||||
$opts['QUEUE_DRIVER'] = 'sync';
|
||||
}
|
||||
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the config files
|
||||
*/
|
||||
public function removeConfigFiles()
|
||||
{
|
||||
$env_file = App::environmentFilePath();
|
||||
$config_file = App::environmentPath().'/config.php';
|
||||
|
||||
if (file_exists($env_file)) {
|
||||
try {
|
||||
unlink($env_file);
|
||||
} catch (Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (file_exists($config_file)) {
|
||||
try {
|
||||
unlink($config_file);
|
||||
} catch (Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the template file name and write it out
|
||||
*
|
||||
* @param $opts
|
||||
*
|
||||
* @throws \Symfony\Component\HttpFoundation\File\Exception\FileException
|
||||
*/
|
||||
protected function writeConfigFiles($opts)
|
||||
{
|
||||
Stub::setBasePath(resource_path('/stubs/installer'));
|
||||
|
||||
$env_file = App::environmentFilePath();
|
||||
|
||||
if (file_exists($env_file) && !is_writable($env_file)) {
|
||||
Log::error('Permissions on existing env.php is not writable');
|
||||
|
||||
throw new FileException('Can\'t write to the env.php file! Check the permissions');
|
||||
}
|
||||
|
||||
/*
|
||||
* First write out the env file
|
||||
*/
|
||||
try {
|
||||
$stub = new Stub('/env.stub', $opts);
|
||||
$stub->render();
|
||||
$stub->saveTo(App::environmentPath(), App::environmentFile());
|
||||
} catch (Exception $e) {
|
||||
throw new FileException('Couldn\'t write env.php. ('.$e.')');
|
||||
}
|
||||
|
||||
/*
|
||||
* Next write out the config file. If there's an error here,
|
||||
* then throw an exception but delete the env file first
|
||||
*/
|
||||
try {
|
||||
$stub = new Stub('/config.stub', $opts);
|
||||
$stub->render();
|
||||
$stub->saveTo(App::environmentPath(), 'config.php');
|
||||
} catch (Exception $e) {
|
||||
unlink(App::environmentPath().'/'.App::environmentFile());
|
||||
|
||||
throw new FileException('Couldn\'t write config.php. ('.$e.')');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Installer\Services;
|
||||
|
||||
use App\Contracts\Service;
|
||||
|
||||
class RequirementsService extends Service
|
||||
{
|
||||
/**
|
||||
* Check the PHP version that it meets the minimum requirement
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function checkPHPVersion(): array
|
||||
{
|
||||
$passed = false;
|
||||
if (version_compare(PHP_VERSION, config('installer.php.version')) >= 0) {
|
||||
$passed = true;
|
||||
}
|
||||
|
||||
return ['version' => PHP_VERSION, 'passed' => $passed];
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the minimal extensions required are loaded
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function checkExtensions(): array
|
||||
{
|
||||
$extensions = [];
|
||||
foreach (config('installer.extensions') as $ext) {
|
||||
$pass = true;
|
||||
if (!\extension_loaded($ext)) {
|
||||
$pass = false;
|
||||
}
|
||||
|
||||
$extensions[] = [
|
||||
'ext' => $ext,
|
||||
'passed' => $pass,
|
||||
];
|
||||
}
|
||||
|
||||
return $extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the permissions for the directories specified
|
||||
* Make sure they exist and are writable
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function checkPermissions(): array
|
||||
{
|
||||
clearstatcache();
|
||||
|
||||
$directories = [];
|
||||
foreach (config('installer.permissions') as $path) {
|
||||
$pass = true;
|
||||
|
||||
if (!file_exists($path)) {
|
||||
$pass = false;
|
||||
}
|
||||
|
||||
if (!is_writable($path)) {
|
||||
$pass = false;
|
||||
}
|
||||
|
||||
$directories[] = [
|
||||
'dir' => $path,
|
||||
'passed' => $pass,
|
||||
];
|
||||
}
|
||||
|
||||
return $directories;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"name": "phpvms/installer",
|
||||
"license": "",
|
||||
"type": "laravel-library",
|
||||
"description": "The installer module for phpVMS",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nabeel Shahzad",
|
||||
"email": "nabeel@phpvms.net"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"composer/installers": "~1.0"
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Modules\\Installer\\Providers\\InstallerServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Modules\\Installer\\": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": "Installer",
|
||||
"alias": "installer",
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
"active": 1,
|
||||
"order": 0,
|
||||
"providers": [
|
||||
"Modules\\Installer\\Providers\\InstallerServiceProvider"
|
||||
],
|
||||
"aliases": {},
|
||||
"files": [],
|
||||
"requires": []
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
];
|
||||
@@ -1,150 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Updater\Http\Controllers;
|
||||
|
||||
use App\Contracts\Controller;
|
||||
use App\Repositories\KvpRepository;
|
||||
use App\Services\AnalyticsService;
|
||||
use App\Services\Installer\InstallerService;
|
||||
use App\Services\Installer\MigrationService;
|
||||
use App\Services\Installer\SeederService;
|
||||
use Codedge\Updater\UpdaterManager;
|
||||
use function count;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class UpdateController extends Controller
|
||||
{
|
||||
private $analyticsSvc;
|
||||
private $installerSvc;
|
||||
private $kvpRepo;
|
||||
private $migrationSvc;
|
||||
private $seederSvc;
|
||||
private $updateManager;
|
||||
|
||||
/**
|
||||
* @param AnalyticsService $analyticsSvc
|
||||
* @param InstallerService $installerSvc
|
||||
* @param KvpRepository $kvpRepo
|
||||
* @param MigrationService $migrationSvc
|
||||
* @param SeederService $seederSvc
|
||||
* @param UpdaterManager $updateManager
|
||||
*/
|
||||
public function __construct(
|
||||
AnalyticsService $analyticsSvc,
|
||||
InstallerService $installerSvc,
|
||||
KvpRepository $kvpRepo,
|
||||
MigrationService $migrationSvc,
|
||||
SeederService $seederSvc,
|
||||
UpdaterManager $updateManager
|
||||
) {
|
||||
$this->analyticsSvc = $analyticsSvc;
|
||||
$this->migrationSvc = $migrationSvc;
|
||||
$this->seederSvc = $seederSvc;
|
||||
$this->installerSvc = $installerSvc;
|
||||
$this->kvpRepo = $kvpRepo;
|
||||
$this->updateManager = $updateManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('updater::index-start');
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 1. Check if there's an update available. Check if there
|
||||
* are any unrun migrations
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function step1(Request $request)
|
||||
{
|
||||
$this->installerSvc->clearCaches();
|
||||
|
||||
if ($this->installerSvc->isUpgradePending()) {
|
||||
Log::info('Upgrade is pending');
|
||||
}
|
||||
|
||||
return view('updater::steps/step1-update-available');
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 2 Run all of the migrations
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function run_migrations(Request $request)
|
||||
{
|
||||
Log::info('Update: run_migrations', $request->post());
|
||||
|
||||
$migrations = $this->migrationSvc->migrationsAvailable();
|
||||
if (count($migrations) === 0) {
|
||||
$this->seederSvc->syncAllSeeds();
|
||||
return view('updater::steps/step3-update-complete');
|
||||
}
|
||||
|
||||
$output = $this->migrationSvc->runAllMigrations();
|
||||
$this->seederSvc->syncAllSeeds();
|
||||
|
||||
return view('updater::steps/step2-migrations-done', [
|
||||
'console_output' => $output,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Final step
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function complete(Request $request)
|
||||
{
|
||||
return redirect('/login');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the update page with the latest version
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function updater(Request $request)
|
||||
{
|
||||
$version = $this->kvpRepo->get('latest_version_tag');
|
||||
|
||||
return view('updater::downloader/downloader', [
|
||||
'version' => $version,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the actual update and then forward the user to the updater page
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function update_download(Request $request)
|
||||
{
|
||||
$version = $this->kvpRepo->get('latest_version_tag');
|
||||
if (empty($version)) {
|
||||
return view('updater::steps/step1-no-update');
|
||||
}
|
||||
|
||||
$release = $this->updateManager->source('github')->fetch($version);
|
||||
$this->updateManager->source('github')->update($release);
|
||||
$this->analyticsSvc->sendUpdate();
|
||||
|
||||
Log::info('Update completed to '.$version.', redirecting');
|
||||
return redirect('/update');
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Updater\Lib;
|
||||
|
||||
use App\Services\VersionService;
|
||||
use Codedge\Updater\Contracts\GithubRepositoryTypeContract;
|
||||
use Codedge\Updater\SourceRepositoryTypes\GithubRepositoryType;
|
||||
use Codedge\Updater\Traits\SupportPrivateAccessToken;
|
||||
use Codedge\Updater\Traits\UseVersionFile;
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Str;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Use SourceRepositoryTypes/GithubRepositoryTypes/GithubTagType.php as a reference
|
||||
* Just replace the new update checks, etc, with the VersionService stubs. They're
|
||||
* essentially the same except for the current version checks and all that. Adds some
|
||||
* additional logging too, but the base update method is from GithubRepositoryType
|
||||
*/
|
||||
final class VmsRepositoryType extends GithubRepositoryType implements GithubRepositoryTypeContract
|
||||
{
|
||||
use UseVersionFile;
|
||||
use SupportPrivateAccessToken;
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* @var VersionService
|
||||
*/
|
||||
protected $versionSvc;
|
||||
|
||||
public function __construct(array $config, Client $client)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->client = $client;
|
||||
$this->storagePath = Str::finish($this->config['download_path'], DIRECTORY_SEPARATOR);
|
||||
$this->versionSvc = app(VersionService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check repository if a newer version than the installed one is available.
|
||||
*
|
||||
* @param string $currentVersion
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws Exception
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNewVersionAvailable(string $currentVersion = ''): bool
|
||||
{
|
||||
return $this->versionSvc->isNewVersionAvailable($currentVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest version that has been published in a certain repository.
|
||||
* Example: 2.6.5 or v2.6.5.
|
||||
*
|
||||
* @param string $prepend Prepend a string to the latest version
|
||||
* @param string $append Append a string to the latest version
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getVersionAvailable(string $prepend = '', string $append = ''): string
|
||||
{
|
||||
return $this->versionSvc->getLatestVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the latest version. If you do not want the latest version, specify one and pass it.
|
||||
*
|
||||
* @param string $version
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function fetch($version = ''): void
|
||||
{
|
||||
$response = $this->getRepositoryReleases();
|
||||
$releaseCollection = collect(\GuzzleHttp\json_decode($response->getBody()->getContents()));
|
||||
|
||||
if ($releaseCollection->isEmpty()) {
|
||||
throw new Exception('Cannot find a release to update. Please check the repository you\'re pulling from');
|
||||
}
|
||||
|
||||
if (!File::exists($this->storagePath)) {
|
||||
File::makeDirectory($this->storagePath, 493, true, true);
|
||||
}
|
||||
|
||||
if (!empty($version)) {
|
||||
$release = $releaseCollection->where('name', $version)->first();
|
||||
} else {
|
||||
$release = $releaseCollection->first();
|
||||
}
|
||||
|
||||
Log::info('Found release='.$release->name.', path='.$release->zipball_url);
|
||||
$storageFolder = $this->storagePath.$release->name.'-'.now()->timestamp;
|
||||
$storageFilename = $storageFolder.'.zip';
|
||||
|
||||
if (!$this->isSourceAlreadyFetched($release->name)) {
|
||||
$this->downloadRelease($this->client, $release->zipball_url, $storageFilename);
|
||||
$this->unzipArchive($storageFilename, $storageFolder);
|
||||
$this->createReleaseFolder($storageFolder, $release->name);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getRepositoryReleases(): ResponseInterface
|
||||
{
|
||||
$url = self::GITHUB_API_URL
|
||||
.'/repos/'.$this->config['repository_vendor']
|
||||
.'/'.$this->config['repository_name']
|
||||
.'/tags';
|
||||
|
||||
$headers = [];
|
||||
return $this->client->request('GET', $url, ['headers' => $headers]);
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Updater\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class UpdateServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function boot()
|
||||
{
|
||||
$this->registerRoutes();
|
||||
$this->registerTranslations();
|
||||
$this->registerConfig();
|
||||
$this->registerViews();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the routes
|
||||
*/
|
||||
protected function registerRoutes()
|
||||
{
|
||||
Route::group([
|
||||
'as' => 'update.',
|
||||
'prefix' => 'update',
|
||||
'middleware' => ['web', 'auth', 'ability:admin,admin-access'],
|
||||
'namespace' => 'Modules\Updater\Http\Controllers',
|
||||
], function () {
|
||||
Route::get('/', 'UpdateController@index')->name('index');
|
||||
|
||||
Route::get('/step1', 'UpdateController@step1')->name('step1');
|
||||
Route::post('/step1', 'UpdateController@step1')->name('step1post');
|
||||
|
||||
Route::post('/run-migrations', 'UpdateController@run_migrations')->name('run_migrations');
|
||||
Route::get('/complete', 'UpdateController@complete')->name('complete');
|
||||
|
||||
// Routes for the update downloader
|
||||
Route::get('/downloader', 'UpdateController@updater')->name('updater');
|
||||
Route::post('/downloader', 'UpdateController@update_download')->name('update_download');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register config.
|
||||
*/
|
||||
protected function registerConfig()
|
||||
{
|
||||
$this->mergeConfigFrom(__DIR__.'/../Config/config.php', 'updater');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register views.
|
||||
*/
|
||||
public function registerViews()
|
||||
{
|
||||
$viewPath = resource_path('views/modules/updater');
|
||||
$sourcePath = __DIR__.'/../Resources/views';
|
||||
|
||||
$this->publishes([
|
||||
$sourcePath => $viewPath,
|
||||
], 'views');
|
||||
|
||||
$paths = array_map(
|
||||
function ($path) {
|
||||
return $path.'/modules/updater';
|
||||
},
|
||||
\Config::get('view.paths')
|
||||
);
|
||||
|
||||
$paths[] = $sourcePath;
|
||||
$this->loadViewsFrom($paths, 'updater');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register translations.
|
||||
*/
|
||||
public function registerTranslations()
|
||||
{
|
||||
$langPath = resource_path('lang/modules/updater');
|
||||
|
||||
if (is_dir($langPath)) {
|
||||
$this->loadTranslationsFrom($langPath, 'updater');
|
||||
} else {
|
||||
$this->loadTranslationsFrom(__DIR__.'/../Resources/lang', 'updater');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*/
|
||||
public function provides(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>@yield('title') - installer</title>
|
||||
|
||||
<link rel="shortcut icon" type="image/png" href="{{ public_asset('/assets/img/favicon.png') }}"/>
|
||||
|
||||
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no'
|
||||
name='viewport'/>
|
||||
<meta name="base-url" content="{!! url('') !!}">
|
||||
<meta name="api-key" content="{!! Auth::check() ? Auth::user()->api_key: '' !!}">
|
||||
<meta name="csrf-token" content="{!! csrf_token() !!}">
|
||||
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700,200" rel="stylesheet"/>
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css" rel="stylesheet"/>
|
||||
|
||||
<link href="{{ public_asset('/assets/frontend/css/bootstrap.min.css') }}" rel="stylesheet"/>
|
||||
<link href="{{ public_asset('/assets/frontend/css/now-ui-kit.css') }}" rel="stylesheet"/>
|
||||
<link href="{{ public_asset('/assets/installer/css/vendor.css') }}" rel="stylesheet"/>
|
||||
<link href="{{ public_asset('/assets/frontend/css/styles.css') }}" rel="stylesheet"/>
|
||||
|
||||
<link rel="stylesheet"
|
||||
href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
|
||||
|
||||
<style>
|
||||
.table tr:first-child td {
|
||||
border-top: 0px;
|
||||
}
|
||||
@yield('css')
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="login-page" style="background: #067ec1;">
|
||||
<div class="page-header clear-filter">
|
||||
<div class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-8 ml-auto mr-auto content-center">
|
||||
<div class="p-10" style="padding: 10px 0;">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<img src="{{ public_asset('/assets/img/logo_blue_bg.svg') }}" width="135px" style="" alt=""/>
|
||||
</div>
|
||||
<div class="col-8 text-right">
|
||||
<h4 class="text-white mb-0 mr-0 ml-0" style="margin-top: 5px;">@yield('title')</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card card-login card-plain" style="background: #FFF">
|
||||
<div class="card-body">
|
||||
@include('updater::flash.message')
|
||||
@yield('content')
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{--<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>--}}
|
||||
|
||||
<script src="{{ public_mix('/assets/global/js/vendor.js') }}"></script>
|
||||
<script src="{{ public_mix('/assets/installer/js/vendor.js') }}"></script>
|
||||
<script src="{{ public_mix('/assets/installer/js/app.js') }}"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
|
||||
|
||||
<script>
|
||||
hljs.configure({languages: ['sh']});
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
$(".select2").select2();
|
||||
|
||||
$('pre code').each(function (i, block) {
|
||||
hljs.fixMarkup(block);
|
||||
hljs.highlightBlock(block);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@yield('scripts')
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,12 +0,0 @@
|
||||
@extends('updater::app')
|
||||
@section('title', 'Update phpVMS')
|
||||
|
||||
@section('content')
|
||||
<h2>phpvms updater</h2>
|
||||
<p>Click run to complete the update to version {{ $version }}</p>
|
||||
{{ Form::open(['route' => 'update.update_download', 'method' => 'post']) }}
|
||||
<p style="text-align: right">
|
||||
{{ Form::submit('Run >>', ['class' => 'btn btn-success']) }}
|
||||
</p>
|
||||
{{ Form::close() }}
|
||||
@endsection
|
||||
@@ -1,6 +0,0 @@
|
||||
@if($errors->has($field))
|
||||
<p class="text-danger" style="margin-top: 10px;">{{ $errors->first($field) }}</p>
|
||||
{{--<div class="alert alert-danger" role="alert" style="margin-top: 10px;">
|
||||
{{ $errors->first($field) }}
|
||||
</div>--}}
|
||||
@endif
|
||||
@@ -1,11 +0,0 @@
|
||||
@foreach (session('flash_notification', collect())->toArray() as $message)
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<div class="container">
|
||||
<div class="alert-icon">
|
||||
<i class="now-ui-icons ui-2_like"></i>
|
||||
</div>
|
||||
{{ $message['message'] }}
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
{{ session()->forget('flash_notification') }}
|
||||
@@ -1,12 +0,0 @@
|
||||
@extends('updater::app')
|
||||
@section('title', 'Update phpVMS')
|
||||
|
||||
@section('content')
|
||||
<h2>phpvms updater</h2>
|
||||
<p>Press continue to check if there are any updates available.</p>
|
||||
{{ Form::open(['route' => 'update.step1post', 'method' => 'post']) }}
|
||||
<p style="text-align: right">
|
||||
{{ Form::submit('Start >>', ['class' => 'btn btn-success']) }}
|
||||
</p>
|
||||
{{ Form::close() }}
|
||||
@endsection
|
||||
@@ -1,13 +0,0 @@
|
||||
@extends('updater::app')
|
||||
@section('title', 'Update phpVMS')
|
||||
|
||||
@section('content')
|
||||
<h2>phpvms updater</h2>
|
||||
<p>It seems like you're up to date!</p>
|
||||
{{ Form::open(['route' => 'update.complete', 'method' => 'GET']) }}
|
||||
|
||||
<p style="text-align: right">
|
||||
{{ Form::submit('Complete >>', ['class' => 'btn btn-success']) }}
|
||||
</p>
|
||||
{{ Form::close() }}
|
||||
@endsection
|
||||
@@ -1,12 +0,0 @@
|
||||
@extends('updater::app')
|
||||
@section('title', 'Update phpVMS')
|
||||
|
||||
@section('content')
|
||||
<h2>phpvms updater</h2>
|
||||
<p>Click run to complete the update!.</p>
|
||||
{{ Form::open(['route' => 'update.run_migrations', 'method' => 'post']) }}
|
||||
<p style="text-align: right">
|
||||
{{ Form::submit('Run >>', ['class' => 'btn btn-success']) }}
|
||||
</p>
|
||||
{{ Form::close() }}
|
||||
@endsection
|
||||
@@ -1,18 +0,0 @@
|
||||
@extends('updater::app')
|
||||
@section('title', 'Update Completed')
|
||||
@section('content')
|
||||
<div style="align-content: center;">
|
||||
{{ Form::open(['route' => 'update.complete', 'method' => 'GET']) }}
|
||||
|
||||
<pre class="lang-sh">
|
||||
<code class="lang-sh">
|
||||
{{ $console_output }}
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<p style="text-align: right">
|
||||
{{ Form::submit('Complete >>', ['class' => 'btn btn-success']) }}
|
||||
</p>
|
||||
{{ Form::close() }}
|
||||
</div>
|
||||
@endsection
|
||||
@@ -1,13 +0,0 @@
|
||||
@extends('updater::app')
|
||||
@section('title', 'Update Completed')
|
||||
|
||||
@section('content')
|
||||
<h2>phpvms updater</h2>
|
||||
<p>Update completed!.</p>
|
||||
|
||||
{{ Form::open(['route' => 'update.complete', 'method' => 'GET']) }}
|
||||
<p style="text-align: right">
|
||||
{{ Form::submit('Finish >>', ['class' => 'btn btn-success']) }}
|
||||
</p>
|
||||
{{ Form::close() }}
|
||||
@endsection
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"name": "phpvms/updater",
|
||||
"license": "",
|
||||
"type": "laravel-library",
|
||||
"description": "The installer module for phpVMS",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nabeel Shahzad",
|
||||
"email": "nabeel@phpvms.net"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"composer/installers": "~1.0"
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Modules\\Updater\\Providers\\UpdateServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Modules\\Installer\\": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": "Updater",
|
||||
"alias": "updater",
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
"active": 1,
|
||||
"order": 0,
|
||||
"providers": [
|
||||
"Modules\\Updater\\Providers\\UpdateServiceProvider"
|
||||
],
|
||||
"aliases": {},
|
||||
"files": [],
|
||||
"requires": []
|
||||
}
|
||||
Reference in New Issue
Block a user