consistency run through

This commit is contained in:
Tony Murray
2020-06-21 11:27:07 -05:00
parent e267ec1cab
commit 691a73caa6
12 changed files with 150 additions and 75 deletions

View File

@@ -71,6 +71,10 @@ class ChecksController extends InstallationController implements InstallerStep
public function complete(): bool
{
if ($this->stepCompleted('checks')) {
return true;
}
if (!$this->checkPhpVersion()) {
return false;
}

View File

@@ -107,7 +107,7 @@ class DatabaseController extends InstallationController implements InstallerStep
public function complete(): bool
{
if (session('install.database')) {
if ($this->stepCompleted('database')) {
return true;
}

View File

@@ -27,6 +27,7 @@ namespace App\Http\Controllers\Install;
use App\Http\Controllers\Controller;
use LibreNMS\DB\Eloquent;
use LibreNMS\Interfaces\InstallerStep;
class InstallationController extends Controller
{
@@ -39,10 +40,16 @@ class InstallationController extends Controller
'finish' => \App\Http\Controllers\Install\FinalizeController::class,
];
public function redirectToFirst()
{
$step = collect($this->filterActiveSteps())->keys()->first(null, 'checks');
return redirect()->route("install.$step");
}
public function redirectToIncomplete()
{
foreach ($this->stepStatus() as $step => $complete) {
if (!$complete) {
foreach ($this->filterActiveSteps() as $step => $controller) {
if (!$controller->complete()) {
return redirect()->route("install.$step");
}
}
@@ -63,21 +70,19 @@ class InstallationController extends Controller
/**
* Init step info and return false if previous steps have not been completed.
*
* @return bool
* @return bool if all previous steps have been completed
*/
final protected function initInstallStep()
{
if (is_string(config('librenms.install'))) {
$this->steps = array_intersect_key($this->steps, array_flip(explode(',', config('librenms.install'))));
}
$this->filterActiveSteps();
$this->configureDatabase();
foreach ($this->stepStatus() as $step => $completed) {
foreach ($this->stepStatus() as $step => $complete) {
if ($step == $this->step) {
return true;
}
if (!$completed) {
if (!$complete) {
return false;
}
}
@@ -87,13 +92,21 @@ class InstallationController extends Controller
final protected function markStepComplete()
{
session(["install.$this->step" => true]);
session()->save();
if (!$this->stepCompleted($this->step)) {
session(["install.$this->step" => true]);
session()->save();
}
}
final protected function stepCompleted(string $step)
{
return (bool)session("install.$step");
}
final protected function formatData($data = [])
{
$data['steps'] = $this->hydrateControllers();
$data['step'] = $this->step;
return $data;
}
@@ -114,18 +127,32 @@ class InstallationController extends Controller
}
}
protected function filterActiveSteps()
{
if (is_string(config('librenms.install'))) {
$this->steps = array_intersect_key($this->steps, array_flip(explode(',', config('librenms.install'))));
}
return $this->steps;
}
private function hydrateControllers()
{
$this->steps = array_map(function ($class) {
return is_object($class) ? $class : app()->make($class);
}, $this->steps);
return $this->steps;
}
private function stepStatus()
{
$this->hydrateControllers();
return array_map(function ($controller) {
return $controller->complete();
}, $this->steps);
return array_map(function (InstallerStep $controller) {
return [
'enabled' => $controller->enabled(),
'complete' => $controller->complete(),
];
}, $this->steps);
}
}

View File

@@ -26,9 +26,9 @@
namespace App\Http\Controllers\Install;
use App\Models\User;
use Illuminate\Database\QueryException;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use LibreNMS\DB\Eloquent;
use LibreNMS\Interfaces\InstallerStep;
class MakeUserController extends InstallationController implements InstallerStep
@@ -65,11 +65,20 @@ class MakeUserController extends InstallationController implements InstallerStep
]);
try {
$user = new User($request->only(['username', 'password', 'email']));
$user->level = 10;
$user->setPassword($request->get('password'));
$res = $user->save();
$message = $res ? trans('install.user.success') : trans('install.user.failure');
// only allow the first admin to be created
if (!$this->complete()) {
$this->configureDatabase();
$user = new User($request->only(['username', 'password', 'email']));
$user->level = 10; // admin
$user->setPassword($request->get('password'));
$res = $user->save();
$message = trans('install.user.failure');
if ($res) {
$message = trans('install.user.success');
$this->markStepComplete();
}
}
} catch (\Exception $e) {
$message = $e->getMessage();
}
@@ -79,12 +88,27 @@ class MakeUserController extends InstallationController implements InstallerStep
public function complete(): bool
{
return Eloquent::isConnected() && User::adminOnly()->exists();
if ($this->stepCompleted('user')) {
return true;
}
try {
if ($this->stepCompleted('database')) {
$exists = User::adminOnly()->exists();
if ($exists) {
$this->markStepComplete();
}
return $exists;
}
} catch (QueryException $e) {
//
}
return false;
}
public function enabled(): bool
{
return (bool)session('install.database');
return $this->stepCompleted('database');
}
public function icon(): string

View File

@@ -53,6 +53,10 @@ class CheckInstalled
// redirect to install if not installed
return redirect()->route('install');
} elseif ($installed && $is_install_route) {
// in case someone refreshes on the finish step
if ($request->routeIs('install.finish')) {
return redirect()->route('home');
}
throw new AuthorizationException('This should only be called during install');
}

View File

@@ -1,7 +1,5 @@
@extends('layouts.install')
@section('title', trans('install.checks.title'))
@section('content')
<div class="row">
<div class=" col-8 offset-2">

View File

@@ -1,7 +1,5 @@
@extends('layouts.install')
@section('title', trans('install.database.title'))
@section('content')
<div class="row">
<div class="col-12">
@@ -73,7 +71,7 @@
</div>
</div>
</div>
<div class="row" @if(!$valid_credentials) style="display: none" @endif>
<div id="migrate-step" class="row" @if(!$valid_credentials) style="display: none" @endif>
<div class="col-12">
<div class="card">
<div id="db-form-header"
@@ -95,12 +93,16 @@
<i class="fa fa-lg fa-chevron-down rotate-if-collapsed fa-pull-right"></i>
</div>
<div id="migrate-container" class="card-body collapse @if(!$migrated) show @endif">
<div class="mb-2 text-right">
<button id="migrate-btn" type="button" class="btn btn-primary">
@lang('install.migrate.migrate')
</button>
<div class="row">
<div class="col-md-8">
<div id="migrate-warning" class="alert alert-warning">@lang('install.migrate.building_interrupt')</div>
</div>
<div class="col-md-4 text-right">
<button id="migrate-btn" type="button" class="btn btn-primary mt-1 mb-4">
@lang('install.migrate.migrate')
</button>
</div>
</div>
<div id="migrate-warning" class="alert alert-warning">@lang('install.migrate.building_interrupt')</div>
<textarea readonly id="db-update" class="form-control" rows="20" placeholder="@lang('install.migrate.wait')"></textarea>
</div>
</div>
@@ -123,7 +125,7 @@
success: function (response) {
if (response.result === 'ok') {
$('#credential-status>i').attr('class', 'fa fa-lg fa-check-circle text-success');
$('#migration-output').show();
$('#migrate-step').show();
$('#db-form-container').collapse('hide')
} else {
$('#credential-status>i').attr('class', 'fa fa-lg fa-times-circle text-danger')
@@ -168,8 +170,7 @@
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
$('#migrate-warning').hide();
checkStepStatus(function (status) {
console.log(status);
if (status.database) {
if (status.database.complete) {
$('#migrate-status>i').attr('class', 'fa fa-lg fa-check-circle text-success');
$('#migrate-container').collapse('hide');
}
@@ -186,10 +187,6 @@
#db-update {
resize: vertical;
}
#retry-btn {
display: none;
}
#migrate-warning {
display: none;
}

View File

@@ -1,7 +1,5 @@
@extends('layouts.install')
@section('title', trans('install.finish.title'))
@section('content')
<div class="card mb-2">
<div class="card-header" data-toggle="collapse" data-target="#env-file-text" aria-expanded="false">

View File

@@ -1,7 +1,5 @@
@extends('layouts.install')
@section('title', trans('install.user.title'))
@section('content')
<div class="row">
<div class="col-12">
@@ -28,7 +26,7 @@
@error('email')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<button type="submit" class="btn btn-success float-right">@lang('install.user.button')</button>
<button type="submit" class="btn btn-primary float-right">@lang('install.user.button')</button>
</form>
</div>
</div>

View File

@@ -1,7 +1,5 @@
@extends('layouts.install')
@section('title', trans('install.user.created'))
@section('content')
<div class="row">
<div class="col-12 text-center p-5">

View File

@@ -17,7 +17,7 @@
.primary-panel {
padding: 0;
border:0;
border: 0;
box-shadow: 3px 3px 30px #222;
min-height: 540px;
}
@@ -35,6 +35,7 @@
.card-img-top {
background-color: #EEEEEE;
}
#progress-icons {
background: linear-gradient(to bottom, #EEEEEE 50%, white 50%)
}
@@ -51,16 +52,16 @@
display: inline-block;
width: 100%;
background-color: lightgray;
box-shadow:
inset 0 6px 4px -5px black,
inset 0 -6px 4px -7px black;
box-shadow: inset 0 6px 4px -5px black,
inset 0 -6px 4px -7px black;
}
.install-progress.loop {
box-shadow:
inset 0 6px 4px -5px black,
inset 0 -6px 4px -7px black,
inset 8px 0 4px -6px grey; /* missing button shadow */
box-shadow: inset 0 6px 4px -5px black,
inset 0 -6px 4px -7px black,
inset 8px 0 4px -6px grey; /* missing button shadow */
}
.install-progress.complete {
background-color: #db202e;
}
@@ -72,9 +73,11 @@
.rotate-if-collapsed {
transition: .4s transform ease-in-out;
}
[data-toggle="collapse"] {
cursor: pointer;
}
[data-toggle="collapse"][aria-expanded="true"] .rotate-if-collapsed {
transform: rotate(180deg);
}
@@ -88,30 +91,33 @@
<img class="card-img-top p-4" src="{{ asset(\LibreNMS\Config::get('title_image', "images/librenms_logo_light.svg")) }}" alt="LibreNMS">
<div id="progress-icons" class="d-flex flex-row justify-content-around">
<div class="install-progress complete"></div>
@foreach($steps as $step => $controller)
@foreach($steps as $name => $controller)
<div>
<a href="{{ route('install.' . $step) }}"
class="install-enable-{{ $step }} btn btn-info btn-circle @if(!$controller->enabled($steps)) disabled @endif"
title="@lang("install.$step.title")"
<a href="{{ route('install.' . $name) }}"
id="install-step-{{ $name }}"
class="install-step btn btn-circle
@if($step === $name) btn-outline-info @else btn-info @endif
@if(!$controller->enabled()) disabled @endif"
title="@lang("install.$name.title")"
>
<i class="fa fa-lg {{ $controller->icon() }}"></i>
</a>
</div>
<div id="progress-{{ $step }}-bar" class="install-progress loop @if($controller->complete()) complete @endif"></div>
<div id="progress-{{ $name }}-bar" class="install-progress loop @if($controller->complete()) complete @endif"></div>
@endforeach
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-12 text-center">
<h2 id="step-title">@yield('title')</h2>
<h2 id="step-title">@lang("install.$step.title")</h2>
</div>
</div>
<div class="row">
<div id="error-box" class="col-12">
@if(!empty($messages))
@foreach($messages as $message)
<div class="alert alert-danger">{{ $message }}</div>
<div class="alert alert-danger">{{ $message }}</div>
@endforeach
@endif
</div>
@@ -121,22 +127,43 @@
</div>
</div>
<script>
var step = '{{ $step }}';
function checkStepStatus(callback) {
$.ajax('{{ route('install.action.steps') }}')
.success(function (data) {
Object.keys(data).forEach(function (key) {
if (data[key]) {
$('.install-enable-' + key).removeClass('disabled');
} else {
$('.install-enable-' + key).addClass('disabled');
}
});
.success(function (data) {
var primary;
Object.keys(data).forEach(function (key) {
var classes = 'btn btn-circle';
classes += (key === step ? ' btn-outline-info' : ' btn-info');
if (callback && typeof callback === "function") {
callback(data);
}
})
// mark buttons enabled
if (!data[key].enabled) {
classes += ' disabled';
} else if (!data[key].complete && !primary) {
// if this step is the first enabled, but not complete, mark it as primary
primary = key
}
$('#install-step-' + key).attr('class', classes);
});
if (primary) {
$('#install-step-' + primary)
.removeClass('btn-info')
.removeClass('btn-outline-info')
.addClass(primary === step ? 'btn-outline-primary' : 'btn-primary');
} else {
// all complete
$('.install-progress').addClass('complete')
}
if (callback && typeof callback === "function") {
callback(data);
}
})
}
checkStepStatus();
</script>
@yield('scripts')
</body>

View File

@@ -33,7 +33,7 @@ Route::group(['middleware' => ['auth'], 'guard' => 'auth'], function () {
Route::get('about', 'AboutController@index');
Route::get('authlog', 'UserController@authlog');
Route::get('overview', 'OverviewController@index')->name('overview');
Route::get('/', 'OverviewController@index');
Route::get('/', 'OverviewController@index')->name('home');
Route::match(['get', 'post'], 'device/{device}/{tab?}/{vars?}', 'DeviceController@index')
->name('device')->where(['vars' => '.*']);
@@ -150,7 +150,7 @@ Route::group(['middleware' => ['auth'], 'guard' => 'auth'], function () {
// installation routes
Route::group(['prefix' => 'install', 'namespace' => 'Install'], function () {
Route::get('/', 'InstallationController@redirectToIncomplete')->name('install');
Route::get('/', 'InstallationController@redirectToFirst')->name('install');
Route::get('/checks', 'ChecksController@index')->name('install.checks');
Route::get('/database', 'DatabaseController@index')->name('install.database');
Route::get('/user', 'MakeUserController@index')->name('install.user');