Bug fixes

added route caching
added helpers
improved redirecting
This commit is contained in:
Sergio Brighenti 2018-11-13 18:05:17 +01:00
parent 945790bedd
commit 5c362d7e26
12 changed files with 145 additions and 105 deletions

View file

@ -7,7 +7,6 @@ use League\Flysystem\Adapter\Local;
use League\Flysystem\FileNotFoundException;
use League\Flysystem\Filesystem;
use Slim\Container;
use Slim\Http\Request;
use Slim\Http\Response;
abstract class Controller
@ -34,20 +33,6 @@ abstract class Controller
return null;
}
/**
* Generate a human readable file size
* @param $size
* @param int $precision
* @return string
*/
protected function humanFilesize($size, $precision = 2): string
{
for ($i = 0; ($size / 1024) > 0.9; $i++, $size /= 1024) {
}
return round($size, $precision) . ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'][$i];
}
/**
* Get a filesystem instance
* @return Filesystem
@ -57,19 +42,6 @@ abstract class Controller
return new Filesystem(new Local($this->settings['storage_dir']));
}
/**
* @param $path
*/
protected function removeDirectory($path)
{
$files = glob($path . '/*');
foreach ($files as $file) {
is_dir($file) ? $this->removeDirectory($file) : unlink($file);
}
rmdir($path);
return;
}
/**
* @param $id
* @return int
@ -90,4 +62,14 @@ abstract class Controller
return $totalSize;
}
/**
* @param Response $response
* @param string $path
* @return Response
*/
function redirectTo(Response $response, string $path): Response
{
return $response->withRedirect($this->settings['base_url'] . $path);
}
}

View file

@ -24,10 +24,10 @@ class DashboardController extends Controller
if ($request->getParam('afterInstall') !== null && is_dir('install')) {
Session::alert('Installation completed successfully!', 'success');
$this->removeDirectory('install');
removeDirectory('install');
}
return $response->withRedirect('/home');
return $this->redirectTo($response,'/home');
}
/**
@ -61,7 +61,7 @@ class DashboardController extends Controller
}
$media->mimetype = $mime;
$media->extension = $extension;
$media->size = $this->humanFilesize($size);
$media->size = humanFileSize($size);
}
return $this->view->render(
@ -101,7 +101,7 @@ class DashboardController extends Controller
'usersCount' => $usersCount,
'mediasCount' => $mediasCount,
'orphanFilesCount' => $orphanFilesCount,
'totalSize' => $this->humanFilesize($totalSize),
'totalSize' => humanFileSize($totalSize),
'post_max_size' => ini_get('post_max_size'),
'upload_max_filesize' => ini_get('upload_max_filesize'),
]);
@ -129,6 +129,6 @@ class DashboardController extends Controller
public function applyTheme(Request $request, Response $response): Response
{
file_put_contents('static/bootstrap/css/bootstrap.min.css', file_get_contents($request->getParam('css')));
return $response->withRedirect('/system')->withAddedHeader('Cache-Control', 'no-cache, must-revalidate');
return $this->redirectTo($response,'/system')->withAddedHeader('Cache-Control', 'no-cache, must-revalidate');
}
}

View file

@ -19,7 +19,7 @@ class LoginController extends Controller
public function show(Request $request, Response $response): Response
{
if (Session::get('logged', false)) {
return $response->withRedirect('/home');
return $this->redirectTo($response, '/home');
}
return $this->view->render($response, 'auth/login.twig');
}
@ -36,19 +36,19 @@ class LoginController extends Controller
if (!$result || !password_verify($request->getParam('password'), $result->password)) {
Session::alert('Wrong credentials', 'danger');
return $response->withRedirect('/login');
return $this->redirectTo($response, '/login');
}
if (!$result->active) {
Session::alert('Your account is disabled.', 'danger');
return $response->withRedirect('/login');
return $this->redirectTo($response, '/login');
}
Session::set('logged', true);
Session::set('user_id', $result->id);
Session::set('username', $result->username);
Session::set('admin', $result->is_admin);
Session::set('used_space', $this->humanFilesize($this->getUsedSpaceByUser($result->id)));
Session::set('used_space', humanFileSize($this->getUsedSpaceByUser($result->id)));
Session::alert("Welcome, $result->username!", 'info');
$this->logger->info("User $result->username logged in.");
@ -57,7 +57,7 @@ class LoginController extends Controller
return $response->withRedirect(Session::get('redirectTo'));
}
return $response->withRedirect('/home');
return $this->redirectTo($response,'/home');
}
/**
@ -70,7 +70,7 @@ class LoginController extends Controller
Session::clear();
Session::set('logged', false);
Session::alert('Goodbye!', 'warning');
return $response->withRedirect('/login');
return $this->redirectTo($response,'/login');
}
}

View file

@ -223,7 +223,7 @@ class UploadController extends Controller
} finally {
$this->database->query('DELETE FROM `uploads` WHERE `id` = ?', $args['id']);
$this->logger->info('User ' . Session::get('username') . ' deleted a media.', [$args['id']]);
Session::set('used_space', $this->humanFilesize($this->getUsedSpaceByUser(Session::get('user_id'))));
Session::set('used_space', humanFileSize($this->getUsedSpaceByUser(Session::get('user_id'))));
}
} else {
throw new UnauthorizedException();

View file

@ -58,22 +58,22 @@ class UserController extends Controller
{
if ($request->getParam('email') === null) {
Session::alert('The email is required.', 'danger');
return $response->withRedirect('/user/create');
return $this->redirectTo($response,'/user/create');
}
if ($request->getParam('username') === null) {
Session::alert('The username is required.', 'danger');
return $response->withRedirect('/user/create');
return $this->redirectTo($response,'/user/create');
}
if ($request->getParam('password') === null) {
Session::alert('The password is required.', 'danger');
return $response->withRedirect('/user/create');
return $this->redirectTo($response,'/user/create');
}
if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `username` = ?', $request->getParam('username'))->fetch()->count > 0) {
Session::alert('The username already taken.', 'danger');
return $response->withRedirect('/user/create');
return $this->redirectTo($response,'/user/create');
}
do {
@ -95,7 +95,7 @@ class UserController extends Controller
Session::alert("User '{$request->getParam('username')}' created!", 'success');
$this->logger->info('User ' . Session::get('username') . ' created a new user.', [array_diff($request->getParams(), ['password'])]);
return $response->withRedirect('/users');
return $this->redirectTo($response,'/users');
}
/**
@ -135,22 +135,22 @@ class UserController extends Controller
if ($request->getParam('email') === null) {
Session::alert('The email is required.', 'danger');
return $response->withRedirect('/user/' . $args['id'] . '/edit');
return $this->redirectTo($response,'/user/' . $args['id'] . '/edit');
}
if ($request->getParam('username') === null) {
Session::alert('The username is required.', 'danger');
return $response->withRedirect('/user/' . $args['id'] . '/edit');
return $this->redirectTo($response,'/user/' . $args['id'] . '/edit');
}
if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `username` = ? AND `username` <> ?', [$request->getParam('username'), $user->username])->fetch()->count > 0) {
Session::alert('The username already taken.', 'danger');
return $response->withRedirect('/user/' . $args['id'] . '/edit');
return $this->redirectTo($response,'/user/' . $args['id'] . '/edit');
}
if ($user->id === Session::get('user_id') && $request->getParam('is_admin') === null) {
Session::alert('You cannot demote yourself.', 'danger');
return $response->withRedirect('/user/' . $args['id'] . '/edit');
return $this->redirectTo($response,'/user/' . $args['id'] . '/edit');
}
if ($request->getParam('password') !== null && !empty($request->getParam('password'))) {
@ -175,7 +175,7 @@ class UserController extends Controller
Session::alert("User '{$request->getParam('username')}' updated!", 'success');
$this->logger->info('User ' . Session::get('username') . " updated $user->id.", [$user, array_diff($request->getParams(), ['password'])]);
return $response->withRedirect('/users');
return $this->redirectTo($response,'/users');
}
@ -196,7 +196,7 @@ class UserController extends Controller
if ($user->id === Session::get('user_id')) {
Session::alert('You cannot delete yourself.', 'danger');
return $response->withRedirect('/users');
return $this->redirectTo($response,'/users');
}
$this->database->query('DELETE FROM `users` WHERE `id` = ?', $user->id);
@ -204,7 +204,7 @@ class UserController extends Controller
Session::alert('User deleted.', 'success');
$this->logger->info('User ' . Session::get('username') . " deleted $user->id.");
return $response->withRedirect('/users');
return $this->redirectTo($response,'/users');
}
/**
@ -253,7 +253,7 @@ class UserController extends Controller
if ($request->getParam('email') === null) {
Session::alert('The email is required.', 'danger');
return $response->withRedirect('/profile');
return $this->redirectTo($response,'/profile');
}
if ($request->getParam('password') !== null && !empty($request->getParam('password'))) {
@ -272,7 +272,7 @@ class UserController extends Controller
Session::alert('Profile updated successfully!', 'success');
$this->logger->info('User ' . Session::get('username') . " updated profile of $user->id.");
return $response->withRedirect('/profile');
return $this->redirectTo($response,'/profile');
}
/**

View file

@ -28,14 +28,14 @@ class AuthMiddleware
{
if (!Session::get('logged', false)) {
Session::set('redirectTo', (isset($_SERVER['HTTPS']) ? 'https' : 'http') . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]");
return $response->withRedirect('/login');
return $response->withRedirect($this->container->settings['base_url'] . '/login');
}
if (!$this->container->database->query('SELECT `id`, `active` FROM `users` WHERE `id` = ? LIMIT 1', [Session::get('user_id')])->fetch()->active) {
Session::alert('Your account is not active anymore.', 'danger');
Session::set('logged', false);
Session::set('redirectTo', (isset($_SERVER['HTTPS']) ? 'https' : 'http') . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]");
return $response->withRedirect('/login');
return $response->withRedirect($this->container->settings['base_url'] . '/login');
}
return $next($request, $response);

51
app/helpers.php Normal file
View file

@ -0,0 +1,51 @@
<?php
require __DIR__ . '/../vendor/autoload.php';
if (!function_exists('humanFileSize')) {
/**
* Generate a human readable file size
* @param $size
* @param int $precision
* @return string
*/
function humanFileSize($size, $precision = 2): string
{
for ($i = 0; ($size / 1024) > 0.9; $i++, $size /= 1024) {
}
return round($size, $precision) . ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'][$i];
}
}
if (!function_exists('removeDirectory')) {
/**
* Remove a directory and it's content
* @param $path
*/
function removeDirectory($path)
{
$files = glob($path . '/*');
foreach ($files as $file) {
is_dir($file) ? removeDirectory($file) : unlink($file);
}
rmdir($path);
return;
}
}
if (!function_exists('cleanDirectory')) {
/**
* Removes all directory contents
* @param $path
*/
function cleanDirectory($path)
{
$directoryIterator = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS);
$iteratorIterator = new RecursiveIteratorIterator($directoryIterator, RecursiveIteratorIterator::CHILD_FIRST);
foreach ($iteratorIterator as $file) {
if ($file->getFilename() !== '.gitkeep') {
$file->isDir() ? rmdir($file) : unlink($file);
}
}
}
}

View file

@ -7,29 +7,18 @@ if (php_sapi_name() !== 'cli') {
die();
}
function cleanDir($path)
{
$directoryIterator = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS);
$iteratorIterator = new RecursiveIteratorIterator($directoryIterator, RecursiveIteratorIterator::CHILD_FIRST);
foreach ($iteratorIterator as $file) {
if ($file->getFilename() !== '.gitkeep') {
$file->isDir() ? rmdir($file) : unlink($file);
}
}
}
$action = isset($argv[1]) ? $argv[1] : 'all';
switch ($action) {
case 'cache':
cleanDir(__DIR__ . '/../resources/cache');
cleanDirectory(__DIR__ . '/../resources/cache');
break;
case 'sessions':
cleanDir(__DIR__ . '/../resources/sessions');
cleanDirectory(__DIR__ . '/../resources/sessions');
break;
case 'all':
cleanDir(__DIR__ . '/../resources/cache');
cleanDir(__DIR__ . '/../resources/sessions');
cleanDirectory(__DIR__ . '/../resources/cache');
cleanDirectory(__DIR__ . '/../resources/sessions');
break;
case 'help':
default:

View file

@ -9,7 +9,7 @@ use Slim\App;
use Slim\Container;
if (!file_exists('config.php') && is_dir('install/')) {
header('Location: /install/');
header('Location: ./install/');
exit();
} else if (!file_exists('config.php') && !is_dir('install/')) {
die('Cannot find the config file.');
@ -23,11 +23,12 @@ $config = array_replace_recursive([
'displayErrorDetails' => false,
'db' => [
'connection' => 'sqlite',
'dsn' => 'resources/database/xbackbone.db',
'dsn' => __DIR__ . '/../resources/database/xbackbone.db',
'username' => null,
'password' => null,
],
], require 'config.php');
'routerCacheFile' => __DIR__ . '/../resources/cache/routes.cache.php',
], require __DIR__ . '/../config.php');
$container = new Container(['settings' => $config]);
@ -43,10 +44,11 @@ $container['logger'] = function ($container) {
};
// Session init
Session::init('xbackbone_session', 'resources/sessions');
Session::init('xbackbone_session', __DIR__ . '/../resources/sessions');
// Set the database dsn
DB::setDsn($config['db']['connection'] . ':' . $config['db']['dsn'], $config['db']['username'], $config['db']['password']);
$dsn = $config['db']['connection'] === 'sqlite' ? __DIR__ . '/../' . $config['db']['dsn'] : $config['db']['dsn'];
DB::setDsn($config['db']['connection'] . ':' . $dsn, $config['db']['username'], $config['db']['password']);
$container['database'] = function ($container) use (&$config) {
return DB::getInstance();
@ -54,8 +56,8 @@ $container['database'] = function ($container) use (&$config) {
$container['view'] = function ($container) use (&$config) {
$view = new \Slim\Views\Twig('resources/templates', [
'cache' => 'resources/cache',
$view = new \Slim\Views\Twig(__DIR__ . '/../resources/templates', [
'cache' => __DIR__ . '/../resources/cache',
'autoescape' => 'html',
'debug' => $config['displayErrorDetails'],
'auto_reload' => $config['displayErrorDetails'],
@ -95,5 +97,23 @@ $container['notFoundHandler'] = function ($container) {
$app = new App($container);
// Permanently redirect paths with a trailing slash to their non-trailing counterpart
$app->add(function (\Slim\Http\Request $request, \Slim\Http\Response $response, callable $next) {
$uri = $request->getUri();
$path = $uri->getPath();
if ($path !== '/' && substr($path, -1) === '/') {
$uri = $uri->withPath(substr($path, 0, -1));
if ($request->getMethod() === 'GET') {
return $response->withRedirect((string)$uri, 301);
} else {
return $next($request->withUri($uri), $response);
}
}
return $next($request, $response);
});
// Load the application routes
require 'app/routes.php';

View file

@ -14,6 +14,9 @@
"ext-pdo": "*"
},
"autoload": {
"files": [
"app/helpers.php"
],
"psr-4": {
"App\\": "app/"
}

View file

@ -11,7 +11,6 @@ use Slim\Http\Response;
define('PLATFORM_VERSION', json_decode(file_get_contents(__DIR__ . '/../composer.json'))->version);
$config = [
'app_name' => 'XBackBone',
'base_url' => isset($_SERVER['HTTPS']) ? 'https://' . $_SERVER['HTTP_HOST'] : 'http://' . $_SERVER['HTTP_HOST'],
'storage_dir' => 'storage',
'displayErrorDetails' => true,
@ -28,7 +27,7 @@ $container = new Container(['settings' => $config]);
Session::init('xbackbone_session');
$container['view'] = function ($container) use (&$config) {
$view = new \Slim\Views\Twig('templates', [
$view = new \Slim\Views\Twig(__DIR__ . '/templates', [
'cache' => false,
'autoescape' => 'html',
'debug' => $config['displayErrorDetails'],
@ -51,7 +50,7 @@ $container['view'] = function ($container) use (&$config) {
function migrate($config)
{
$firstMigrate = false;
if (!file_exists(__DIR__ . '/../' . $config['db']['dsn']) && DB::driver() === 'sqlite') {
if ($config['db']['connection'] === 'sqlite' && !file_exists(__DIR__ . '/../' . $config['db']['dsn'])) {
touch(__DIR__ . '/../' . $config['db']['dsn']);
$firstMigrate = true;
}
@ -110,17 +109,6 @@ function migrate($config)
}
}
function cleanDir($path)
{
$directoryIterator = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS);
$iteratorIterator = new RecursiveIteratorIterator($directoryIterator, RecursiveIteratorIterator::CHILD_FIRST);
foreach ($iteratorIterator as $file) {
if ($file->getFilename() !== '.gitkeep') {
$file->isDir() ? rmdir($file) : unlink($file);
}
}
}
$app = new App($container);
$app->get('/', function (Request $request, Response $response) {
@ -131,7 +119,10 @@ $app->get('/', function (Request $request, Response $response) {
});
$app->post('/', function (Request $request, Response $response) use (&$config) {
$installed = true;
if (!file_exists(__DIR__ . '/../config.php')) {
$installed = false;
$config['base_url'] = $request->getParam('base_url');
$config['storage_dir'] = $request->getParam('storage_dir');
$config['displayErrorDetails'] = false;
@ -144,14 +135,18 @@ $app->post('/', function (Request $request, Response $response) use (&$config) {
file_put_contents(__DIR__ . '/../config.php', '<?php' . PHP_EOL . 'return ' . var_export($config, true) . ';');
}
DB::setDsn($config['db']['connection'] . ':' . __DIR__ . '/../' . $config['db']['dsn'], $config['db']['username'], $config['db']['password']);
$dsn = $config['db']['connection'] === 'sqlite' ? __DIR__ . '/../' . $config['db']['dsn'] : $config['db']['dsn'];
DB::setDsn($config['db']['connection'] . ':' . $dsn, $config['db']['username'], $config['db']['password']);
migrate($config);
if (!$installed) {
DB::query("INSERT INTO `users` (`email`, `username`, `password`, `is_admin`, `user_code`) VALUES (?, 'admin', ?, 1, ?)", [$request->getParam('email'), password_hash($request->getParam('password'), PASSWORD_DEFAULT), substr(md5(microtime()), rand(0, 26), 5)]);
}
cleanDir(__DIR__ . '/../resources/cache');
cleanDir(__DIR__ . '/../resources/sessions');
cleanDirectory(__DIR__ . '/../resources/cache');
cleanDirectory(__DIR__ . '/../resources/sessions');
return $response->withRedirect('../?afterInstall=true');
});

View file

@ -6,16 +6,16 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="A lightweight PHP backend for ShareX">
<link href="{{ config.base_url }}/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="{{ config.base_url }}/static/highlightjs/styles/monokai.css" rel="stylesheet">
<link href="{{ config.base_url }}/static/app/app.css" rel="stylesheet">
<link href="{{ request.uri }}../static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="{{ request.uri }}../static/highlightjs/styles/monokai.css" rel="stylesheet">
<link href="{{ request.uri }}../static/app/app.css" rel="stylesheet">
<script src="{{ config.base_url }}/static/jquery/jquery.min.js"></script>
<script src="{{ config.base_url }}/static/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="{{ config.base_url }}/static/fontawesome/js/all.min.js"></script>
<script src="{{ config.base_url }}/static/highlightjs/highlight.pack.min.js"></script>
<script src="{{ config.base_url }}/static/clipboardjs/clipboard.min.js"></script>
<script src="{{ config.base_url }}/static/app/app.js"></script>
<script src="{{ request.uri }}../static/jquery/jquery.min.js"></script>
<script src="{{ request.uri }}../static/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="{{ request.uri }}../static/fontawesome/js/all.min.js"></script>
<script src="{{ request.uri }}../static/highlightjs/highlight.pack.min.js"></script>
<script src="{{ request.uri }}../static/clipboardjs/clipboard.min.js"></script>
<script src="{{ request.uri }}../static/app/app.js"></script>
<style>
html,
body {