Don't check file permissions on every request, handle failures (#9264)

* Don't check file permissions on every request, handle failures
Improve error page visually

* only print minimal mkdir

* invert file_exists check, whoops

* docblock

* revert accidental changes

* rename variable

* Change database errors to use the new layout

* Add support url to the default layout

* Replaced \n for && in fix for user perms

* fix web output
This commit is contained in:
Tony Murray
2018-10-18 21:08:46 -05:00
committed by GitHub
parent aa14f86c2d
commit 607a567090
13 changed files with 382 additions and 229 deletions

View File

@@ -41,9 +41,10 @@ class DatabaseConnectException extends \Exception
'message' => 'Error connecting to database: ' . $this->getMessage(),
]);
} else {
$message = "<h2 style='color:darkred'>Error connecting to database.</h2>";
$message .= $this->getMessage();
return response($message);
return response()->view('errors.generic', [
'title' => 'Error connecting to database.',
'content' => $this->getMessage(),
]);
}
}
}

View File

@@ -132,6 +132,13 @@ class ValidationResult
return $this->fix;
}
/**
* The commands (generally) to fix the issue.
* If there are multiple, use an array.
*
* @param string|array $fix
* @return ValidationResult $this
*/
public function setFix($fix)
{
$this->fix = $fix;
@@ -146,7 +153,10 @@ class ValidationResult
c_echo(str_pad('[' . $this->getStatusText($this->status) . ']', 12) . $this->message . PHP_EOL);
if (isset($this->fix)) {
c_echo("\t[%BFIX%n] %B$this->fix%n\n");
c_echo("\t[%BFIX%n]: \n");
foreach ((array)$this->fix as $fix) {
c_echo("\t%B$fix%n\n");
}
}
if (!empty($this->list)) {

View File

@@ -67,9 +67,11 @@ class User extends BaseValidation
$rrd_dir = Config::get('rrd_dir', "$dir/rrd");
// generic fix
$fix = "sudo chown -R $lnms_username:$lnms_groupname $dir\n" .
"sudo setfacl -d -m g::rwx $rrd_dir $log_dir $dir/bootstrap/cache/ $dir/storage/\n" .
"sudo chmod -R ug=rwX $rrd_dir $log_dir $dir/bootstrap/cache/ $dir/storage/\n";
$fix = [
"sudo chown -R $lnms_username:$lnms_groupname $dir",
"sudo setfacl -d -m g::rwx $rrd_dir $log_dir $dir/bootstrap/cache/ $dir/storage/",
"sudo chmod -R ug=rwX $rrd_dir $log_dir $dir/bootstrap/cache/ $dir/storage/",
];
$find_result = rtrim(`find $dir \! -user $lnms_username -o \! -group $lnms_groupname 2> /dev/null`);
if (!empty($find_result)) {

View File

@@ -30,8 +30,8 @@ use App\Models\Notification;
use Auth;
use Cache;
use Carbon\Carbon;
use Dotenv\Dotenv;
use LibreNMS\Config;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
use Toastr;
class Checks
@@ -39,97 +39,13 @@ class Checks
public static function preBoot()
{
// check php extensions
$missing = self::missingPhpExtensions();
if (!empty($missing)) {
if ($missing = self::missingPhpExtensions()) {
self::printMessage(
"Missing PHP extensions. Please install and enable them on your LibreNMS server.",
$missing,
true
);
}
// check file/folder permissions
$check_folders = [
self::basePath('bootstrap/cache'),
self::basePath('storage'),
self::basePath('storage/framework/sessions'),
self::basePath('storage/framework/views'),
self::basePath('storage/framework/cache'),
self::basePath('logs'),
];
$check_files = [
self::basePath('logs/librenms.log'), // This file is important because Laravel needs to be able to write to it
];
// check that each is writable
$check_folders = array_filter($check_folders, function ($path) {
return !is_writable($path);
});
$check_files = array_filter($check_files, function ($path) {
return file_exists($path) xor is_writable($path);
});
if (!empty($check_folders) || !empty($check_files)) {
// only operate on parent directories, not files
$check = array_unique(array_merge($check_folders, array_map('dirname', $check_files)));
// load .env, it isn't loaded
$dotenv = new Dotenv(__DIR__ . '/../');
$dotenv->load();
$user = env('LIBRENMS_USER', 'librenms');
$group = env('LIBRENMS_GROUP', $user);
// build chown message
$dirs = implode(' ', $check);
$chown_commands = [
"chown -R $user:$group $dirs",
"setfacl -R -m g::rwx $dirs",
"setfacl -d -m g::rwx $dirs",
];
$current_groups = explode(' ', trim(exec('groups')));
if (!in_array($group, $current_groups)) {
$current_user = trim(exec('whoami'));
$chown_commands[] = "usermod -a -G $group $current_user";
}
//check for missing directories
$missing = array_filter($check, function ($file) {
return !file_exists($file);
});
if (!empty($missing)) {
array_unshift($chown_commands, 'mkdir -p ' . implode(' ', $missing));
}
$short_dirs = implode(', ', array_map(function ($dir) {
return str_replace(self::basePath(), '', $dir);
}, $check));
self::printMessage(
"Error: $short_dirs not writable! Run these commands as root on your LibreNMS server to fix:",
$chown_commands
);
// build SELinux output
$selinux_commands = [];
foreach ($check as $dir) {
$selinux_commands[] = "semanage fcontext -a -t httpd_sys_content_t '$dir(/.*)?'";
$selinux_commands[] = "semanage fcontext -a -t httpd_sys_rw_content_t '$dir(/.*)?'";
$selinux_commands[] = "restorecon -RFvv $dir";
}
self::printMessage(
"If using SELinux you may also need:",
$selinux_commands,
true
);
}
}
/**
@@ -207,12 +123,6 @@ class Checks
}
}
private static function basePath($path = '')
{
$base_dir = realpath(__DIR__ . '/..');
return "$base_dir/$path";
}
private static function missingPhpExtensions()
{
// allow mysqli, but prefer mysqlnd
@@ -226,4 +136,99 @@ class Checks
return !extension_loaded($module);
});
}
/**
* Check exception for errors related to not being able to write to the filesystem
*
* @param \Exception $e
* @return bool|SymfonyResponse
*/
public static function filePermissionsException($e)
{
if ($e instanceof \ErrorException) {
// cannot write to storage directory
if (starts_with($e->getMessage(), 'file_put_contents(') && str_contains($e->getMessage(), '/storage/')) {
return self::filePermissionsResponse();
}
}
if ($e instanceof \Exception) {
// cannot write to bootstrap directory
if ($e->getMessage() == 'The bootstrap/cache directory must be present and writable.') {
return self::filePermissionsResponse();
}
}
if ($e instanceof \UnexpectedValueException) {
// monolog cannot init log file
if (str_contains($e->getFile(), 'Monolog/Handler/StreamHandler.php')) {
return self::filePermissionsResponse();
}
}
return false;
}
/**
* Generate a semi generic list of commands for the user to run to fix file permissions
* and render it to a nice html response
*
* @return SymfonyResponse
*/
private static function filePermissionsResponse()
{
$user = config('librenms.user');
$group = config('librenms.group');
$install_dir = base_path();
$commands = [];
$dirs = [
base_path('bootstrap/cache'),
base_path('storage'),
Config::get('log_dir', base_path('logs')),
Config::get('rrd_dir', base_path('rrd')),
];
// check if folders are missing
$mkdirs = [
base_path('bootstrap/cache'),
base_path('storage/framework/sessions'),
base_path('storage/framework/views'),
base_path('storage/framework/cache'),
Config::get('log_dir', base_path('logs')),
Config::get('rrd_dir', base_path('rrd')),
];
$mk_dirs = array_filter($mkdirs, function ($file) {
return !file_exists($file);
});
if (!empty($mk_dirs)) {
$commands[] = 'sudo mkdir -p ' . implode(' ', $mk_dirs);
}
// always print chwon/setfacl/chmod commands
$commands[] = "sudo chown -R $user:$group $install_dir";
$commands[] = 'sudo setfacl -d -m g::rwx ' . implode(' ', $dirs);
$commands[] = 'sudo chmod -R ug=rwX ' . implode(' ', $dirs);
// check if webserver is in the librenms group
$current_groups = explode(' ', trim(exec('groups')));
if (!in_array($group, $current_groups)) {
$current_user = trim(exec('whoami'));
$commands[] = "usermod -a -G $group $current_user";
}
// selinux:
$commands[] = '<h4>If using SELinux you may also need:</h4>';
foreach ($dirs as $dir) {
$commands[] = "semanage fcontext -a -t httpd_sys_rw_content_t '$dir(/.*)?'";
}
$commands[] = "restorecon -RFv $install_dir";
// use pre-compiled template because we probably can't compile it.
$template = file_get_contents(base_path('resources/views/errors/static/file_permissions.html'));
$content = str_replace('!!!!CONTENT!!!!', '<p>' . implode('</p><p>', $commands) . '</p>', $template);
return SymfonyResponse::create($content);
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Exceptions;
use App\Checks;
use Exception;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\QueryException;
@@ -36,19 +37,14 @@ class Handler extends ExceptionHandler
protected function convertExceptionToResponse(Exception $e)
{
if ($e instanceof QueryException) {
// connect exception, convert to our standard connection exception
if (config('app.debug')) {
// get message form PDO exception, it doesn't contain the query
$message = $e->getMessage();
} else {
$message = $e->getPrevious()->getMessage();
}
// handle database exceptions
if ($db_response = $this->dbExceptionToResponse($e)) {
return $db_response;
}
if (in_array($e->getCode(), [1044, 1045, 2002])) {
throw new DatabaseConnectException($message, $e->getCode(), $e);
}
return response('Unhandled MySQL Error [' . $e->getCode() . "] $message");
// check for exceptions relating to not being able to write to the filesystem
if ($fs_response = Checks::filePermissionsException($e)) {
return $fs_response;
}
// show helpful response if debugging, otherwise print generic error so we don't leak information
@@ -74,4 +70,28 @@ class Handler extends ExceptionHandler
return redirect()->guest(route('login'));
}
protected function dbExceptionToResponse(Exception $e)
{
if ($e instanceof QueryException) {
// connect exception, convert to our standard connection exception
if (config('app.debug')) {
// get message form PDO exception, it doesn't contain the query
$message = $e->getMessage();
} else {
$message = $e->getPrevious()->getMessage();
}
if (in_array($e->getCode(), [1044, 1045, 2002])) {
// this Exception has it's own render function
throw new DatabaseConnectException($message, $e->getCode(), $e);
}
return response()->view('errors.generic', [
'title' => 'Unhandled MySQL Error [' . $e->getCode() . ']',
'content' => $message
]);
}
return false;
}
}

View File

@@ -88,7 +88,11 @@ foreach ($validator->getAllResults() as $group => $results) {
if ($result->hasFix() || $result->hasList()) {
echo '<div class="panel-body">';
if ($result->hasFix()) {
echo 'Fix: <code>' . linkify($result->getFix()) . '</code>';
echo 'Fix: <code>';
foreach ((array)$result->getFix() as $fix) {
echo '<br />' . linkify($fix) . PHP_EOL;
}
echo '</code>';
if ($result->hasList()) {
echo '<br /><br />';
}

View File

@@ -1,7 +1,7 @@
@extends('layouts.librenmsv1')
@section('javascript')
<script src="{{ asset('js/jquery.qrcode.min.js') }}"></script>
<script src="js/jquery.qrcode.min.js"></script>
@endsection
@section('content')

View File

@@ -0,0 +1,16 @@
@extends('layouts.error')
@section('title')
@lang('Whoops, the web server could not write required files to the filesystem.')
@endsection
@section('content')
<h3>@lang('Running the following commands will fix the issue most of the time:')</h3>
@foreach($commands as $command)
<p>{{ $command }}</p>
@endforeach
<hr class="separator"/>
<p>@lang("If that doesn't fix the issue. You can find how to get help at") <a href="https://docs.librenms.org/Support">https://docs.librenms.org/Support</a>.</p>
@endsection

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,78 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="robots" content="noindex,nofollow" />
<style> body { background-color: #F9F9F9; color: #222; font: 14px/1.4 Helvetica, Arial, sans-serif; margin: 0; padding-bottom: 45px; }
a { cursor: pointer; text-decoration: none; }
a:hover { text-decoration: underline; }
abbr[title] { border-bottom: none; cursor: help; text-decoration: none; }
code, pre { font: 13px/1.5 Consolas, Monaco, Menlo, "Ubuntu Mono", "Liberation Mono", monospace; }
table, tr, th, td { background: #FFF; border-collapse: collapse; vertical-align: top; }
table { background: #FFF; border: 1px solid #E0E0E0; box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; width: 100%; }
table th, table td { border: solid #E0E0E0; border-width: 1px 0; padding: 8px 10px; }
table th { background-color: #E0E0E0; font-weight: bold; text-align: left; }
.hidden-xs-down { display: none; }
.block { display: block; }
.break-long-words { -ms-word-break: break-all; word-break: break-all; word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; }
.text-muted { color: #999; }
.container { max-width: 1024px; margin: 0 auto; padding: 0 15px; }
.container::after { content: ""; display: table; clear: both; }
.exception-summary { background: #863836; border-bottom: 2px solid rgba(0, 0, 0, 0.1); border-top: 1px solid rgba(0, 0, 0, .3); flex: 0 0 auto; margin-bottom: 30px; }
.exception-message-wrapper { display: flex; align-items: center; min-height: 70px; }
.exception-message { flex-grow: 1; padding: 30px 0; }
.exception-message, .exception-message a { color: #FFF; font-size: 21px; font-weight: 400; margin: 0; }
.exception-message.long { font-size: 18px; }
.exception-message a { border-bottom: 1px solid rgba(255, 255, 255, 0.5); font-size: inherit; text-decoration: none; }
.exception-message a:hover { border-bottom-color: #ffffff; }
.exception-illustration { flex-basis: 64px; flex-shrink: 0; height: 66px; margin-left: 15px; opacity: .7; }
.trace + .trace { margin-top: 30px; }
.trace-head .trace-class { color: #222; font-size: 18px; font-weight: bold; line-height: 1.3; margin: 0; position: relative; }
.trace-message { font-size: 14px; font-weight: normal; margin: .5em 0 0; }
.trace-file-path, .trace-file-path a { color: #222; margin-top: 3px; font-size: 13px; }
.trace-class { color: #B0413E; }
.trace-type { padding: 0 2px; }
.trace-method { color: #B0413E; font-weight: bold; }
.trace-arguments { color: #777; font-weight: normal; padding-left: 2px; }
hr.separator { border: 0; margin: 1.8em 0; height: 1px; background: #333 linear-gradient(to right, #ccc, #333, #ccc); }
@media (min-width: 575px) {
.hidden-xs-down { display: initial; }
}</style>
</head>
<body>
<div class="exception-summary">
<div class="container">
<div class="exception-message-wrapper">
<h1 class="break-long-words exception-message">Whoops, the web server could not write required files to the filesystem.</h1>
<div class="exception-illustration hidden-xs-down">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path d="M35.758 22.094l6.148 6.148-2.42 2.459-6.147-6.148zm11.245-11.226l6.149 6.148-2.44 2.46-6.148-6.148zm-.396 22.094l6.188 6.148-2.44 2.46-6.346-6.149zm11.245-11.225L64 27.885l-2.42 2.459-6.148-6.148zm-24.335-4.562l6.188-6.148 2.44 2.44-6.149 6.148zm10.868 10.868l6.149-6.148 2.459 2.42-6.148 6.148zm10.849 10.869l6.148-6.188 2.46 2.44-6.347 6.147zM22.272 28.46l6.148-6.148 2.46 2.42-6.149 6.147zm10.849 10.848l6.187-6.148 2.42 2.42-6.148 6.148zm10.868 10.869l6.148-6.188 2.46 2.44-6.189 6.346z" fill="#fff"></path><path d="M13.288 44.544l6.188 6.148-2.42 2.46-6.188-6.188zM24.533 33.32l6.148 6.148-2.42 2.44-6.148-6.149zm-.337 22.093l6.148 6.149L27.904 64l-6.148-6.148zM35.4 44.187l6.148 6.148-2.42 2.44-6.147-6.148zM2.44 33.676l6.149 6.148-2.44 2.42L0 36.095zM13.685 22.45l6.148 6.148-2.44 2.44-6.148-6.149zM24.89 11.225l6.148 6.148-2.38 2.46-6.148-6.148zM36.095 0l6.188 6.148-2.44 2.44-6.346-6.149zM22.67 6.346L28.817.198l2.44 2.42-6.149 6.148zM11.424 17.592l6.148-6.149 2.459 2.44-6.148 6.148zM.198 28.837l6.148-6.148 2.44 2.38-6.148 6.187zm10.829 10.828l6.148-6.148 2.46 2.44-6.149 6.148zm10.868 10.869l6.148-6.149 2.44 2.42-6.148 6.148zm10.829 10.868l6.188-6.148 2.44 2.38-6.149 6.188z" fill="#db202e"></path></svg>
</div>
</div>
</div>
</div>
<div class="container">
<h3>Running the following commands will fix the issue most of the time:</h3>
!!!!CONTENT!!!!
<hr class="separator"/>
<p>If that doesn't fix the issue. You can find how to get help at <a href="https://docs.librenms.org/Support">https://docs.librenms.org/Support</a>.</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,75 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="robots" content="noindex,nofollow" />
<style> body { background-color: #F9F9F9; color: #222; font: 14px/1.4 Helvetica, Arial, sans-serif; margin: 0; padding-bottom: 45px; }
a { cursor: pointer; text-decoration: none; }
a:hover { text-decoration: underline; }
abbr[title] { border-bottom: none; cursor: help; text-decoration: none; }
code, pre { font: 13px/1.5 Consolas, Monaco, Menlo, "Ubuntu Mono", "Liberation Mono", monospace; }
table, tr, th, td { background: #FFF; border-collapse: collapse; vertical-align: top; }
table { background: #FFF; border: 1px solid #E0E0E0; box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; width: 100%; }
table th, table td { border: solid #E0E0E0; border-width: 1px 0; padding: 8px 10px; }
table th { background-color: #E0E0E0; font-weight: bold; text-align: left; }
.hidden-xs-down { display: none; }
.block { display: block; }
.break-long-words { -ms-word-break: break-all; word-break: break-all; word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; }
.text-muted { color: #999; }
.container { max-width: 1024px; margin: 0 auto; padding: 0 15px; }
.container::after { content: ""; display: table; clear: both; }
.exception-summary { background: #863836; border-bottom: 2px solid rgba(0, 0, 0, 0.1); border-top: 1px solid rgba(0, 0, 0, .3); flex: 0 0 auto; margin-bottom: 30px; }
.exception-message-wrapper { display: flex; align-items: center; min-height: 70px; }
.exception-message { flex-grow: 1; padding: 30px 0; }
.exception-message, .exception-message a { color: #FFF; font-size: 21px; font-weight: 400; margin: 0; }
.exception-message.long { font-size: 18px; }
.exception-message a { border-bottom: 1px solid rgba(255, 255, 255, 0.5); font-size: inherit; text-decoration: none; }
.exception-message a:hover { border-bottom-color: #ffffff; }
.exception-illustration { flex-basis: 64px; flex-shrink: 0; height: 66px; margin-left: 15px; opacity: .7; }
.trace + .trace { margin-top: 30px; }
.trace-head .trace-class { color: #222; font-size: 18px; font-weight: bold; line-height: 1.3; margin: 0; position: relative; }
.trace-message { font-size: 14px; font-weight: normal; margin: .5em 0 0; }
.trace-file-path, .trace-file-path a { color: #222; margin-top: 3px; font-size: 13px; }
.trace-class { color: #B0413E; }
.trace-type { padding: 0 2px; }
.trace-method { color: #B0413E; font-weight: bold; }
.trace-arguments { color: #777; font-weight: normal; padding-left: 2px; }
hr.separator { border: 0; margin: 1.8em 0; height: 1px; background: #333 linear-gradient(to right, #ccc, #333, #ccc); }
@media (min-width: 575px) {
.hidden-xs-down { display: initial; }
}</style>
</head>
<body>
<div class="exception-summary">
<div class="container">
<div class="exception-message-wrapper">
<h1 class="break-long-words exception-message">@yield('title')</h1>
<div class="exception-illustration hidden-xs-down">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path d="M35.758 22.094l6.148 6.148-2.42 2.459-6.147-6.148zm11.245-11.226l6.149 6.148-2.44 2.46-6.148-6.148zm-.396 22.094l6.188 6.148-2.44 2.46-6.346-6.149zm11.245-11.225L64 27.885l-2.42 2.459-6.148-6.148zm-24.335-4.562l6.188-6.148 2.44 2.44-6.149 6.148zm10.868 10.868l6.149-6.148 2.459 2.42-6.148 6.148zm10.849 10.869l6.148-6.188 2.46 2.44-6.347 6.147zM22.272 28.46l6.148-6.148 2.46 2.42-6.149 6.147zm10.849 10.848l6.187-6.148 2.42 2.42-6.148 6.148zm10.868 10.869l6.148-6.188 2.46 2.44-6.189 6.346z" fill="#fff"></path><path d="M13.288 44.544l6.188 6.148-2.42 2.46-6.188-6.188zM24.533 33.32l6.148 6.148-2.42 2.44-6.148-6.149zm-.337 22.093l6.148 6.149L27.904 64l-6.148-6.148zM35.4 44.187l6.148 6.148-2.42 2.44-6.147-6.148zM2.44 33.676l6.149 6.148-2.44 2.42L0 36.095zM13.685 22.45l6.148 6.148-2.44 2.44-6.148-6.149zM24.89 11.225l6.148 6.148-2.38 2.46-6.148-6.148zM36.095 0l6.188 6.148-2.44 2.44-6.346-6.149zM22.67 6.346L28.817.198l2.44 2.42-6.149 6.148zM11.424 17.592l6.148-6.149 2.459 2.44-6.148 6.148zM.198 28.837l6.148-6.148 2.44 2.38-6.148 6.187zm10.829 10.828l6.148-6.148 2.46 2.44-6.149 6.148zm10.868 10.869l6.148-6.149 2.44 2.42-6.148 6.148zm10.829 10.868l6.188-6.148 2.44 2.38-6.149 6.188z" fill="#db202e"></path></svg>
</div>
</div>
</div>
</div>
<div class="container">
@yield('content')
<hr class="separator"/>
<p>@lang("If you need additional help, you can find how to get help at") <a href="https://docs.librenms.org/Support">https://docs.librenms.org/Support</a>.</p>
</div>
</body>
</html>

View File

@@ -8,12 +8,12 @@
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@if(!LibreNMS\Config::get('favicon', false))
<link rel="apple-touch-icon" sizes="180x180" href="{{ asset('images/apple-touch-icon.png') }}">
<link rel="icon" type="image/png" href="{{ asset('images/favicon-32x32.png') }}" sizes="32x32">
<link rel="icon" type="image/png" href="{{ asset('images/favicon-16x16.png') }}" sizes="16x16">
<link rel="manifest" href="{{ asset('images/manifest.json') }}">
<link rel="mask-icon" href="{{ asset('images/safari-pinned-tab.svg') }}" color="#5bbad5">
<link rel="shortcut icon" href="{{ asset('images/favicon.ico') }}">
<link rel="apple-touch-icon" sizes="180x180" href="images/apple-touch-icon.png">
<link rel="icon" type="image/png" href="images/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="images/favicon-16x16.png" sizes="16x16">
<link rel="manifest" href="images/manifest.json">
<link rel="mask-icon" href="images/safari-pinned-tab.svg" color="#5bbad5">
<link rel="shortcut icon" href="images/favicon.ico">
<meta name="csrf-token" content="{{ csrf_token() }}">
<meta name="msapplication-config" content="images/browserconfig.xml">
<meta name="theme-color" content="#ffffff">
@@ -21,53 +21,53 @@
<link rel="shortcut icon" href="{{ LibreNMS\Config::get('favicon') }}" />
@endif
<link href="{{ asset('css/bootstrap.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/bootstrap-datetimepicker.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/bootstrap-switch.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/toastr.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/jquery-ui.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/jquery.bootgrid.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/tagmanager.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/mktree.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/vis.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/font-awesome.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/jquery.gridster.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/leaflet.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/MarkerCluster.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/MarkerCluster.Default.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/leaflet.awesome-markers.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/select2.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/select2-bootstrap.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/query-builder.default.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset(LibreNMS\Config::get('stylesheet', 'css/styles.css')) }}?ver=20180512" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/' . LibreNMS\Config::get('site_style', 'light') . '.css?ver=632417642') }}" rel="stylesheet" type="text/css" />
<link href="css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<link href="css/bootstrap-datetimepicker.min.css" rel="stylesheet" type="text/css" />
<link href="css/bootstrap-switch.min.css" rel="stylesheet" type="text/css" />
<link href="css/toastr.min.css" rel="stylesheet" type="text/css" />
<link href="css/jquery-ui.min.css" rel="stylesheet" type="text/css" />
<link href="css/jquery.bootgrid.min.css" rel="stylesheet" type="text/css" />
<link href="css/tagmanager.css" rel="stylesheet" type="text/css" />
<link href="css/mktree.css" rel="stylesheet" type="text/css" />
<link href="css/vis.min.css" rel="stylesheet" type="text/css" />
<link href="css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<link href="css/jquery.gridster.min.css" rel="stylesheet" type="text/css" />
<link href="css/leaflet.css" rel="stylesheet" type="text/css" />
<link href="css/MarkerCluster.css" rel="stylesheet" type="text/css" />
<link href="css/MarkerCluster.Default.css" rel="stylesheet" type="text/css" />
<link href="css/leaflet.awesome-markers.css" rel="stylesheet" type="text/css" />
<link href="css/select2.min.css" rel="stylesheet" type="text/css" />
<link href="css/select2-bootstrap.min.css" rel="stylesheet" type="text/css" />
<link href="css/query-builder.default.min.css" rel="stylesheet" type="text/css" />
<link href="{{ LibreNMS\Config::get('stylesheet', 'css/styles.css') }}?ver=20180512" rel="stylesheet" type="text/css" />
<link href="css/{{ LibreNMS\Config::get('site_style', 'light') }}.css?ver=632417642" rel="stylesheet" type="text/css" />
@foreach(LibreNMS\Config::get('webui.custom_css', []) as $custom_css)
<link href="{{ asset($custom_css) }}" rel="stylesheet" type="text/css" />
<link href="{{ $custom_css }}" rel="stylesheet" type="text/css" />
@endforeach
@yield('css')
<script src="{{ asset('js/jquery.min.js') }}"></script>
<script src="{{ asset('js/bootstrap.min.js') }}"></script>
<script src="{{ asset('js/bootstrap-hover-dropdown.min.js') }}"></script>
<script src="{{ asset('js/bootstrap-switch.min.js') }}"></script>
<script src="{{ asset('js/hogan-2.0.0.js') }}"></script>
<script src="{{ asset('js/jquery.cycle2.min.js') }}"></script>
<script src="{{ asset('js/moment.min.js') }}"></script>
<script src="{{ asset('js/bootstrap-datetimepicker.min.js') }}"></script>
<script src="{{ asset('js/typeahead.bundle.min.js') }}"></script>
<script src="{{ asset('js/jquery-ui.min.js') }}"></script>
<script src="{{ asset('js/tagmanager.js') }}"></script>
<script src="{{ asset('js/mktree.js') }}"></script>
<script src="{{ asset('js/jquery.bootgrid.min.js') }}"></script>
<script src="{{ asset('js/handlebars.min.js') }}"></script>
<script src="{{ asset('js/pace.min.js') }}"></script>
<script src="{{ asset('js/qrcode.min.js') }}"></script>
<script src="js/jquery.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/bootstrap-hover-dropdown.min.js"></script>
<script src="js/bootstrap-switch.min.js"></script>
<script src="js/hogan-2.0.0.js"></script>
<script src="js/jquery.cycle2.min.js"></script>
<script src="js/moment.min.js"></script>
<script src="js/bootstrap-datetimepicker.min.js"></script>
<script src="js/typeahead.bundle.min.js"></script>
<script src="js/jquery-ui.min.js"></script>
<script src="js/tagmanager.js"></script>
<script src="js/mktree.js"></script>
<script src="js/jquery.bootgrid.min.js"></script>
<script src="js/handlebars.min.js"></script>
<script src="js/pace.min.js"></script>
<script src="js/qrcode.min.js"></script>
@if(LibreNMS\Config::get('enable_lazy_load', true))
<script src="{{ asset('js/jquery.lazyload.min.js') }}"></script>
<script src="{{ asset('js/lazyload.js') }}"></script>
<script src="js/jquery.lazyload.min.js"></script>
<script src="js/lazyload.js"></script>
@endif
<script src="{{ asset('js/select2.min.js') }}"></script>
<script src="{{ asset('js/librenms.js?ver=20180512') }}"></script>
<script src="js/select2.min.js"></script>
<script src="js/librenms.js?ver=20180512"></script>
<script type="text/javascript">
<!-- Begin
@@ -79,9 +79,9 @@
}
// End -->
</script>
<script type="text/javascript" src="{{ asset('js/overlib_mini.js') }}"></script>
<script type="text/javascript" src="{{ asset('js/toastr.min.js') }}"></script>
<script type="text/javascript" src="{{ asset('js/boot.js') }}"></script>
<script type="text/javascript" src="js/overlib_mini.js"></script>
<script type="text/javascript" src="js/toastr.min.js"></script>
<script type="text/javascript" src="js/boot.js"></script>
@yield('javascript')
</head>
<body>

View File

@@ -95,7 +95,7 @@ if (str_contains(`tail config.php`, '?>')) {
// Composer checks
if (!file_exists('vendor/autoload.php')) {
print_fail('Composer has not been run, dependencies are missing', 'composer install --no-dev');
print_fail('Composer has not been run, dependencies are missing', './scripts/composer_wrapper.php install --no-dev');
exit;
}