Rewrite installer without 3rd party installer lib.

This commit is contained in:
Nabeel Shahzad
2017-12-14 16:38:29 -06:00
parent 7966d6c5aa
commit 277a5f2d33
22 changed files with 581 additions and 174 deletions

View File

@@ -9,6 +9,12 @@ APP_LOG_LEVEL=debug
APP_LOG_MAX_FILES=7
DB_CONNECTION=sqlite
DB_HOST=
DB_PORT=
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=
DB_PREFIX=
CACHE_DRIVER=array
CACHE_PREFIX=

View File

@@ -4,11 +4,11 @@ APP_DEBUG=false
APP_LOG_LEVEL=info
APP_URL=http://localhost
DB_CONNECTION=travis
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=phpvms
DB_USERNAME=root
DB_USERNAME=
DB_PASSWORD=
CACHE_DRIVER=array

1
.gitignore vendored
View File

@@ -18,6 +18,7 @@ storage/*.sqlite
.env.php
.env
.env.php
.env.generated
.vagrant
#Homestead.yaml
Homestead.json

View File

@@ -103,6 +103,11 @@ class CreateDatabase extends Command
}
}
protected function create_postgres($dbkey)
{
$this->error('Not supported yet!');
}
/**
* Execute the console command.
*
@@ -123,8 +128,15 @@ class CreateDatabase extends Command
if (config($dbkey . 'driver') === 'mysql') {
$this->create_mysql($dbkey);
} elseif (config($dbkey . 'driver') === 'sqlite') {
}
elseif (config($dbkey . 'driver') === 'sqlite') {
$this->create_sqlite($dbkey);
}
// TODO: Eventually
elseif (config($dbkey . 'driver') === 'postgres') {
$this->create_postgres($dbkey);
}
}
}

View File

@@ -1,9 +1,8 @@
<?php
return [
'fetch' => PDO::FETCH_ASSOC,
'default' => env('DB_CONNECTION', 'local'),
'default' => env('DB_CONNECTION', 'sqlite'),
'connections' => [
'mysql' => [
'driver' => 'mysql',
@@ -12,23 +11,17 @@ return [
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
//'unix_socket' => env('DB_SOCKET', ''),
'prefix' => env('DB_PREFIX', ''),
'timezone' => '+00:00',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => true,
'strict' => false,
'engine' => null,
],
'sqlite' => [
'driver' => 'sqlite',
'database' => env('DB_DATABASE', storage_path('db.sqlite')),
'timezone' => '+00:00',
'prefix' => '',
],
'local' => [
'driver' => 'sqlite',
'database' => storage_path('local.sqlite'),
'database' => storage_path('db.sqlite'),
'timezone' => '+00:00',
'prefix' => '',
],
@@ -54,7 +47,7 @@ return [
'host' => env('REDIS_HOST', 'localhost'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
'database' => env('REDIS_DATABASE', 1),
],
]
];

View File

@@ -1,142 +0,0 @@
<?php
use Illuminate\Validation\Rule;
return [
/*
|--------------------------------------------------------------------------
| Server Requirements
|--------------------------------------------------------------------------
|
| This is the default Laravel server requirements, you can add as many
| as your application require, we check if the extension is enabled
| by looping through the array and run "extension_loaded" on it.
|
*/
'core' => [
'minPhpVersion' => '7.0.0'
],
'requirements' => [
'php' => [
'openssl',
'pdo',
'mbstring',
'tokenizer',
'JSON',
'cURL',
],
'apache' => [
'mod_rewrite',
],
],
/*
|--------------------------------------------------------------------------
| Folders Permissions
|--------------------------------------------------------------------------
|
| This is the default Laravel folders permissions, if your application
| requires more permissions just add them to the array list bellow.
|
*/
'permissions' => [
'storage/framework/' => '755',
'storage/logs/' => '755',
'bootstrap/cache/' => '755'
],
/*
|--------------------------------------------------------------------------
| Environment Form Wizard Validation Rules & Messages
|--------------------------------------------------------------------------
|
| This are the default form vield validation rules. Available Rules:
| https://laravel.com/docs/5.4/validation#available-validation-rules
|
*/
'environment' => [
'form' => [
'rules' => [
'app_name' => 'required|string|max:50',
'environment' => 'required|string|max:50',
'environment_custom' => 'required_if:environment,other|max:50',
'app_debug' => [
'required|in:true,false',
//Rule::in(['true', 'false']),
],
'app_log_level' => 'required|string|max:50',
'app_url' => 'required|url',
'database_connection' => 'required|string|max:50',
'database_hostname' => 'string|max:50',
'database_port' => 'numeric',
'database_name' => 'string|max:50',
'database_username' => 'string|max:50',
'database_password' => 'string|max:50',
'broadcast_driver' => 'string|max:50',
'cache_driver' => 'string|max:50',
'session_driver' => 'string|max:50',
'queue_driver' => 'string|max:50',
'redis_hostname' => 'string|max:50',
'redis_password' => 'string|max:50',
'redis_port' => 'numeric',
'mail_driver' => 'string|max:50',
'mail_host' => 'string|max:50',
'mail_port' => 'string|max:50',
'mail_username' => 'string|max:50',
'mail_password' => 'string|max:50',
'mail_encryption' => 'string|max:50',
/*'pusher_app_id' => 'max:50',
'pusher_app_key' => 'max:50',
'pusher_app_secret' => 'max:50',*/
],
],
],
/*
|--------------------------------------------------------------------------
| Installed Middlware Options
|--------------------------------------------------------------------------
| Different available status switch configuration for the
| canInstall middleware located in `canInstall.php`.
|
*/
'installed' => [
'redirectOptions' => [
'route' => [
'name' => 'welcome',
'data' => [],
],
'abort' => [
'type' => '404',
],
'dump' => [
'data' => 'Dumping a not found message.',
]
],
],
/*
|--------------------------------------------------------------------------
| Selected Installed Middlware Option
|--------------------------------------------------------------------------
| The selected option fo what happens when an installer intance has been
| Default output is to `/resources/views/error/404.blade.php` if none.
| The available middleware options include:
| route, abort, dump, 404, default, ''
|
*/
'installedAlreadyAction' => '',
/*
|--------------------------------------------------------------------------
| Updater Enabled
|--------------------------------------------------------------------------
| Can the application run the '/update' route with the migrations.
| The default option is set to False if none is present.
| Boolean value
|
*/
'updaterEnabled' => 'true',
];

View File

@@ -1,5 +1,23 @@
<?php
use Illuminate\Validation\Rule;
return [
'name' => 'Installer'
'php' => [
'version' => '7.0.0'
],
'extensions' => [
'openssl',
'pdo',
'mbstring',
'tokenizer',
'JSON',
'cURL',
],
'permissions' => [
'storage/framework/' => 'writeable',
'storage/logs/' => 'writeable',
'bootstrap/cache/' => 'writable'
],
];

View File

@@ -2,18 +2,131 @@
namespace Modules\Installer\Http\Controllers;
use App\Http\Controllers\AppBaseController;
use Log;
use Illuminate\Http\Request;
use App\Http\Controllers\AppBaseController;
use Modules\Installer\Services\DatabaseService;
use Modules\Installer\Services\EnvironmentService;
use Modules\Installer\Services\RequirementsService;
class InstallerController extends AppBaseController
{
protected $dbService, $envService, $reqService;
public function __construct(
DatabaseService $dbService,
EnvironmentService $envService,
RequirementsService $reqService
) {
$this->dbService = $dbService;
$this->envService = $envService;
$this->reqService = $reqService;
}
/**
* Display a listing of the resource.
*/
public function index()
{
return view('installer::index');
return view('installer::index-start');
}
/**
* Check the database connection
*/
public function dbtest(Request $request)
{
$status = 'success'; # success|warn|danger
$message = 'Database connection looks good!';
try {
$this->dbService->checkDbConnection(
$request->input('db_conn'),
$request->input('db_host'),
$request->input('db_port'),
$request->input('db_name'),
$request->input('db_user'),
$request->input('db_pass')
);
} catch (\Exception $e) {
$status = 'danger';
$message = 'Failed! ' . $e->getMessage();
}
return view('installer::flash/message', [
'status' => $status,
'message' => $message,
]);
}
/**
* Step 1. Check the modules and permissions
*/
public function step1(Request $request)
{
$passed = true;
$php_version = $this->reqService->checkPHPVersion();
if($php_version['passed'] === false) {
$passed = false;
}
$extensions = $this->reqService->checkExtensions();
foreach ($extensions as $ext) {
if($ext['passed'] === false) {
$passed = false;
}
}
return view('installer::steps/step1-requirements', [
'php' => $php_version,
'extensions' => $extensions,
'passed' => $passed,
]);
}
/**
* Step 2. Database Setup
*/
public function step2(Request $request)
{
$db_types = ['mysql' => 'mysql', 'sqlite' => 'sqlite'];
return view('installer::steps/step2-db', [
'db_types' => $db_types,
]);
}
/**
* Step 2a. Do the config and setup
*/
public function dbsetup(Request $request)
{
$log = [];
Log::info('DB Setup', $request->toArray());
$log[] = 'Creating environment file';
$this->envService->createEnvFile(
$request->input('db_conn'),
$request->input('db_host'),
$request->input('db_port'),
$request->input('db_name'),
$request->input('db_user'),
$request->input('db_pass')
);
$log[] = 'Creating database';
$this->dbService->setupDB();
return redirect('/');
}
/**
* Step 3. Setup the admin user and initial settings
*/
public function step3(Request $request)
{
}
}

View File

@@ -1,3 +1,9 @@
<?php
Route::get('/', 'InstallerController@index');
Route::get('/', 'InstallerController@index')->name('index');
Route::get('/step1', 'InstallerController@step1')->name('step1');
Route::get('/step2', 'InstallerController@step2')->name('step2');
Route::get('/step3', 'InstallerController@step3')->name('step3');
Route::post('/dbtest', 'InstallerController@dbtest')->name('dbtest');
Route::post('/dbsetup', 'InstallerController@dbsetup')->name('dbsetup');

View File

@@ -6,7 +6,7 @@
<link rel="apple-touch-icon" sizes="76x76" href="/assets/frontend/img/apple-icon.png">
<link rel="icon" type="image/png" href="/assets/frontend/img/favicon.png">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<title>phpvms installer</title>
<title>@yield('title') - installer</title>
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no'
name='viewport'/>
<!-- Fonts and icons -->
@@ -39,15 +39,22 @@
</a>
</p>
</div>
<div class="collapse navbar-collapse justify-content-end" id="navigation"></div>
<div class="justify-content-center" id="navigation" style="margin-left: 50px; color: white; font-size: 20px;">
@yield('title')
</div>
</div>
</nav>
<!-- End Navbar -->
<div class="clearfix" style="height: 25px;"></div>
<div class="wrapper">
<div class="clear"></div>
<div class="container-fluid" style="width: 85%!important;">
@yield('content')
<div class="container" style="width: 50%">
<div class="row">
<div class="col-12">
@include('flash::message')
@yield('content')
</div>
</div>
</div>
<div class="clearfix" style="height: 200px;"></div>
</div>

View File

@@ -0,0 +1,8 @@
<div class="alert alert-{!! $status !!}" role="alert">
<div class="container">
<div class="alert-icon">
<i class="now-ui-icons ui-2_like"></i>
</div>
{!! $message !!}
</div>
</div>

View File

@@ -0,0 +1,11 @@
@extends('installer::app')
@section('content')
<h2>phpvms installer</h2>
<p>Press continue to start</p>
{!! Form::open(['route' => 'installer.step1', 'method' => 'post']) !!}
<p style="text-align: right">
{!! Form::submit('Start >>', ['class' => 'btn btn-success']) !!}
</p>
{!! Form::close() !!}
@endsection

View File

@@ -1,6 +0,0 @@
@extends('installer::app')
@section('content')
<h2>phpvms installer</h2>
<p>This view is loaded from module: {!! config('installer.name') !!}</p>
@endsection

View File

@@ -0,0 +1,43 @@
@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:center;">
@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:center;">
@if($ext['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
{{--{!! $php_version !!}
{!! $extensions !!}
{!! $passed !!}--}}
{!! Form::close() !!}
</div>
@endsection

View File

@@ -0,0 +1,116 @@
@extends('installer::app')
@section('title', 'Database Setup')
@section('content')
<div style="align-content: center;">
{!! Form::open(['route' => 'installer.dbsetup', 'method' => 'POST']) !!}
<table class="table" width="25%">
<tr>
<td>Select Database Type</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', null, ['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', '3307', ['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>
</table>
<div id="dbtest"></div>
<p style="text-align: right">
{!! Form::submit('Complete Setup >>', ['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(function() {
var selValue = $("#db_conn option:selected").text();
changeForm(selValue);
$("#db_conn").change(function(e) {
var selValue = $("#db_conn option:selected").text();
changeForm(selValue);
});
$("#dbtest_button").click(function(e) {
e.preventDefault();
var opts = {
db_conn: $("#db_conn option:selected").text(),
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(),
};
console.log(opts);
$.post("{!! route('installer.dbtest') !!}", opts, function(data) {
$("#dbtest").html(data);
})
})
});
</script>
@endsection

View File

@@ -0,0 +1,34 @@
#
# Before you go live, remember to change the APP_ENV to production
# and APP_DEBUG to false
#
APP_ENV=dev
APP_KEY=base64:{!! $app_key !!}
APP_DEBUG=true
APP_LOCALE=en
APP_LOG=daily
APP_LOG_LEVEL=debug
APP_LOG_MAX_FILES=3
APP_URL=http://localhost
DB_CONNECTION={!! $db_conn !!}
DB_HOST={!! $db_host !!}
DB_PORT={!! $db_port !!}
DB_DATABASE={!! $db_name !!}
DB_USERNAME={!! $db_user !!}
DB_PASSWORD={!! $db_pass !!}
DB_PREFIX=
CACHE_DRIVER=array
CACHE_PREFIX=
REDIS_HOST=localhost
REDIS_PASSWORD=
REDIS_PORT=6379
REDIS_DATABASE=1
SESSION_DRIVER=array
QUEUE_DRIVER=sync

View File

@@ -0,0 +1,50 @@
<?php
namespace Modules\Installer\Services;
use Log;
use PDO;
class DatabaseService {
/**
* Check the PHP version that it meets the minimum requirement
* @throws \PDOException
* @return boolean
*/
public function checkDbConnection($type, $host, $port, $name, $user, $pass)
{
Log::info('Testing Connection: '.$type.'::'.$user.':'.$pass.'@'.$host.':'.$port.';'.$name);
if($type === 'mysql') {
$dsn = "mysql:host=$host;port=$port;dbname=$name";
Log::info('Connection string: '. $dsn);
try {
$conn = new PDO($dsn, $user, $pass);
} catch (\PDOException $e) {
throw $e;
}
}
// Needs testing
elseif ($type === 'postgres') {
$dsn = "pgsql:host=$host;port=$port;dbname=$name";
try {
$conn = new PDO($dsn, $user, $pass);
} catch (\PDOException $e) {
throw $e;
}
}
return true;
}
/**
* Setup the database by running the migration commands
*/
public function setupDB()
{
\Artisan::call('database:create');
\Artisan::call('migrate:refresh');
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Modules\Installer\Services;
use Illuminate\Encryption\Encrypter;
use Log;
use PDO;
class EnvironmentService
{
/**
* Check the PHP version that it meets the minimum requirement
* @return boolean
*/
public function createEnvFile($type, $host, $port, $name, $user, $pass)
{
$env_opts = [
'db_conn' => $type,
'db_host' => $host,
'db_port' => $port,
'db_name' => $name,
'db_user' => $user,
'db_pass' => $pass,
];
$env_opts['app_key'] = base64_encode(Encrypter::generateKey(config('app.cipher')));
$this->writeEnvFile($env_opts);
return true;
}
/**
* Get the template file name and write it out
*/
protected function writeEnvFile($env_opts)
{
$app = app();
$env_file = $app->environmentFilePath();
# TODO: Remove this post-testing
$env_file .= '.generated';
$env_contents = view('installer::stubs/env', $env_opts);
Log::info($env_contents);
$fp = fopen($env_file, 'w');
fwrite($fp, $env_contents);
fclose($fp);
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace Modules\Installer\Services;
class RequirementsService {
/**
* 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;
}
}

View File

@@ -42,8 +42,7 @@
</a>
</p>
</div>
<div class="collapse navbar-collapse justify-content-end" id="navigation"
data-nav-image="/assets/frontend/img/blurred-image-1.jpg">
<div class="collapse navbar-collapse justify-content-end" id="navigation">
<ul class="navbar-nav">
{{--<li class="nav-item active">--}}
@if(!Auth::user())

View File

@@ -0,0 +1,24 @@
@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"
aria-hidden="true"
>&times;</button>
@endif
{!! session('flash_notification.message') !!}
</div>
@endif
@endif

View File

@@ -0,0 +1,19 @@
<div id="flash-overlay-modal" class="modal fade {{ $modalClass or '' }}">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">{{ $title }}</h4>
</div>
<div class="modal-body">
<p>{!! $body !!}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>