Refactoring and improvements

This commit is contained in:
Sergio Brighenti 2019-01-10 23:22:19 +01:00
parent 8e26b35e2a
commit ca361c8eef
23 changed files with 305 additions and 230 deletions

View file

@ -1,3 +1,7 @@
## v2.4
+ Added function to remove orphaned files.
+ Internal refactoring and improvements
## v2.3.1
+ Fixed en lang.
+ Fixed forced background with dark themes.

View file

@ -0,0 +1,68 @@
<?php
namespace App\Controllers;
use League\Flysystem\FileNotFoundException;
use Slim\Http\Request;
use Slim\Http\Response;
class AdminController extends Controller
{
/**
* @param Request $request
* @param Response $response
* @return Response
* @throws FileNotFoundException
*/
public function system(Request $request, Response $response): Response
{
$usersCount = $this->database->query('SELECT COUNT(*) AS `count` FROM `users`')->fetch()->count;
$mediasCount = $this->database->query('SELECT COUNT(*) AS `count` FROM `uploads`')->fetch()->count;
$orphanFilesCount = $this->database->query('SELECT COUNT(*) AS `count` FROM `uploads` WHERE `user_id` IS NULL')->fetch()->count;
$medias = $this->database->query('SELECT `users`.`user_code`, `uploads`.`code`, `uploads`.`storage_path` FROM `uploads` LEFT JOIN `users` ON `uploads`.`user_id` = `users`.`id`')->fetchAll();
$totalSize = 0;
$filesystem = storage();
foreach ($medias as $media) {
$totalSize += $filesystem->getSize($media->storage_path);
}
return $this->view->render($response, 'dashboard/system.twig', [
'usersCount' => $usersCount,
'mediasCount' => $mediasCount,
'orphanFilesCount' => $orphanFilesCount,
'totalSize' => humanFileSize($totalSize),
'post_max_size' => ini_get('post_max_size'),
'upload_max_filesize' => ini_get('upload_max_filesize'),
]);
}
/**
* @param Request $request
* @param Response $response
* @return Response
*/
public function deleteOrphanFiles(Request $request, Response $response): Response
{
$orphans = $this->database->query('SELECT * FROM `uploads` WHERE `user_id` IS NULL')->fetchAll();
$filesystem = storage();
$deleted = 0;
foreach ($orphans as $orphan) {
try {
$filesystem->delete($orphan->storage_path);
$deleted++;
} catch (FileNotFoundException $e) {
}
}
$this->session->alert(lang('deleted_orphans', [$deleted]));
return redirect($response, 'system');
}
}

View file

@ -2,12 +2,18 @@
namespace App\Controllers;
use League\Flysystem\Adapter\Local;
use App\Database\DB;
use App\Web\Session;
use League\Flysystem\FileNotFoundException;
use League\Flysystem\Filesystem;
use Monolog\Logger;
use Slim\Container;
/**
* @property Session|null session
* @property mixed|null view
* @property DB|null database
* @property Logger|null logger
*/
abstract class Controller
{

View file

@ -2,8 +2,6 @@
namespace App\Controllers;
use App\Web\Session;
use League\Flysystem\FileNotFoundException;
use Slim\Http\Request;
use Slim\Http\Response;
@ -23,7 +21,7 @@ class DashboardController extends Controller
{
if ($request->getParam('afterInstall') !== null && is_dir('install')) {
Session::alert(lang('installed'), 'success');
$this->session->alert(lang('installed'), 'success');
removeDirectory('install');
}
@ -41,31 +39,30 @@ class DashboardController extends Controller
$page = isset($args['page']) ? (int)$args['page'] : 0;
$page = max(0, --$page);
if (Session::get('admin', false)) {
if ($this->session->get('admin', false)) {
$medias = $this->database->query('SELECT `uploads`.*, `users`.`user_code`, `users`.`username` FROM `uploads` LEFT JOIN `users` ON `uploads`.`user_id` = `users`.`id` ORDER BY `timestamp` DESC LIMIT ? OFFSET ?', [self::PER_PAGE_ADMIN, $page * self::PER_PAGE_ADMIN])->fetchAll();
$pages = $this->database->query('SELECT COUNT(*) AS `count` FROM `uploads`')->fetch()->count / self::PER_PAGE_ADMIN;
} else {
$medias = $this->database->query('SELECT `uploads`.*,`users`.`user_code`, `users`.`username` FROM `uploads` INNER JOIN `users` ON `uploads`.`user_id` = `users`.`id` WHERE `user_id` = ? ORDER BY `timestamp` DESC LIMIT ? OFFSET ?', [Session::get('user_id'), self::PER_PAGE, $page * self::PER_PAGE])->fetchAll();
$pages = $this->database->query('SELECT COUNT(*) AS `count` FROM `uploads` WHERE `user_id` = ?', Session::get('user_id'))->fetch()->count / self::PER_PAGE;
$medias = $this->database->query('SELECT `uploads`.*,`users`.`user_code`, `users`.`username` FROM `uploads` INNER JOIN `users` ON `uploads`.`user_id` = `users`.`id` WHERE `user_id` = ? ORDER BY `timestamp` DESC LIMIT ? OFFSET ?', [$this->session->get('user_id'), self::PER_PAGE, $page * self::PER_PAGE])->fetchAll();
$pages = $this->database->query('SELECT COUNT(*) AS `count` FROM `uploads` WHERE `user_id` = ?', $this->session->get('user_id'))->fetch()->count / self::PER_PAGE;
}
$filesystem = storage();
foreach ($medias as $media) {
$extension = pathinfo($media->filename, PATHINFO_EXTENSION);
try {
$media->size = humanFileSize($filesystem->getSize($media->storage_path));
$mime = $filesystem->getMimetype($media->storage_path);
$media->mimetype = $filesystem->getMimetype($media->storage_path);
} catch (FileNotFoundException $e) {
$mime = null;
$media->size = null;
$media->mimetype = null;
}
$media->extension = $extension;
$media->mimetype = $mime;
$media->extension = pathinfo($media->filename, PATHINFO_EXTENSION);
}
return $this->view->render(
$response,
Session::get('admin', false) ? 'dashboard/admin.twig' : 'dashboard/home.twig',
$this->session->get('admin', false) ? 'dashboard/admin.twig' : 'dashboard/home.twig',
[
'medias' => $medias,
'next' => $page < floor($pages),
@ -74,69 +71,4 @@ class DashboardController extends Controller
]
);
}
/**
* @param Request $request
* @param Response $response
* @return Response
* @throws FileNotFoundException
*/
public function system(Request $request, Response $response): Response
{
$usersCount = $this->database->query('SELECT COUNT(*) AS `count` FROM `users`')->fetch()->count;
$mediasCount = $this->database->query('SELECT COUNT(*) AS `count` FROM `uploads`')->fetch()->count;
$orphanFilesCount = $this->database->query('SELECT COUNT(*) AS `count` FROM `uploads` WHERE `user_id` IS NULL')->fetch()->count;
$medias = $this->database->query('SELECT `users`.`user_code`, `uploads`.`code`, `uploads`.`storage_path` FROM `uploads` LEFT JOIN `users` ON `uploads`.`user_id` = `users`.`id`')->fetchAll();
$totalSize = 0;
$filesystem = storage();
foreach ($medias as $media) {
$totalSize += $filesystem->getSize($media->storage_path);
}
return $this->view->render($response, 'dashboard/system.twig', [
'usersCount' => $usersCount,
'mediasCount' => $mediasCount,
'orphanFilesCount' => $orphanFilesCount,
'totalSize' => humanFileSize($totalSize),
'post_max_size' => ini_get('post_max_size'),
'upload_max_filesize' => ini_get('upload_max_filesize'),
]);
}
/**
* @param Request $request
* @param Response $response
* @return Response
*/
public function getThemes(Request $request, Response $response): Response
{
$apiJson = json_decode(file_get_contents('https://bootswatch.com/api/4.json'));
$out = [];
$out['Default - Bootstrap 4 default theme'] = 'https://bootswatch.com/_vendor/bootstrap/dist/css/bootstrap.min.css';
foreach ($apiJson->themes as $theme) {
$out["{$theme->name} - {$theme->description}"] = $theme->cssMin;
}
return $response->withJson($out);
}
public function applyTheme(Request $request, Response $response): Response
{
if (!is_writable('static/bootstrap/css/bootstrap.min.css')) {
Session::alert(lang('cannot_write_file'), 'danger');
return redirect($response, route('system'));
}
file_put_contents('static/bootstrap/css/bootstrap.min.css', file_get_contents($request->getParam('css')));
return redirect($response, 'system')
->withAddedHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
->withAddedHeader('Pragma', 'no-cache')
->withAddedHeader('Expire', '0');
}
}

View file

@ -3,8 +3,6 @@
namespace App\Controllers;
use App\Database\DB;
use App\Web\Session;
use Slim\Http\Request;
use Slim\Http\Response;
@ -18,7 +16,7 @@ class LoginController extends Controller
*/
public function show(Request $request, Response $response): Response
{
if (Session::get('logged', false)) {
if ($this->session->get('logged', false)) {
return redirect($response, 'home');
}
return $this->view->render($response, 'auth/login.twig');
@ -32,29 +30,29 @@ class LoginController extends Controller
public function login(Request $request, Response $response): Response
{
$result = DB::query('SELECT `id`, `email`, `username`, `password`,`is_admin`, `active` FROM `users` WHERE `username` = ? OR `email` = ? LIMIT 1', [$request->getParam('username'), $request->getParam('username')])->fetch();
$result = $this->database->query('SELECT `id`, `email`, `username`, `password`,`is_admin`, `active` FROM `users` WHERE `username` = ? OR `email` = ? LIMIT 1', [$request->getParam('username'), $request->getParam('username')])->fetch();
if (!$result || !password_verify($request->getParam('password'), $result->password)) {
Session::alert(lang('bad_login'), 'danger');
$this->session->alert(lang('bad_login'), 'danger');
return redirect($response, 'login');
}
if (!$result->active) {
Session::alert(lang('account_disabled'), 'danger');
$this->session->alert(lang('account_disabled'), 'danger');
return redirect($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', humanFileSize($this->getUsedSpaceByUser($result->id)));
$this->session->set('logged', true);
$this->session->set('user_id', $result->id);
$this->session->set('username', $result->username);
$this->session->set('admin', $result->is_admin);
$this->session->set('used_space', humanFileSize($this->getUsedSpaceByUser($result->id)));
Session::alert(lang('welcome', [$result->username]), 'info');
$this->session->alert(lang('welcome', [$result->username]), 'info');
$this->logger->info("User $result->username logged in.");
if (Session::has('redirectTo')) {
return $response->withRedirect(Session::get('redirectTo'));
if ($this->session->has('redirectTo')) {
return $response->withRedirect($this->session->get('redirectTo'));
}
return redirect($response, 'home');
@ -67,9 +65,9 @@ class LoginController extends Controller
*/
public function logout(Request $request, Response $response): Response
{
Session::clear();
Session::set('logged', false);
Session::alert(lang('goodbye'), 'warning');
$this->session->clear();
$this->session->set('logged', false);
$this->session->alert(lang('goodbye'), 'warning');
return redirect($response, 'login.show');
}

View file

@ -0,0 +1,44 @@
<?php
namespace App\Controllers;
use Slim\Http\Request;
use Slim\Http\Response;
class ThemeController extends Controller
{
/**
* @param Request $request
* @param Response $response
* @return Response
*/
public function getThemes(Request $request, Response $response): Response
{
$apiJson = json_decode(file_get_contents('https://bootswatch.com/api/4.json'));
$out = [];
$out['Default - Bootstrap 4 default theme'] = 'https://bootswatch.com/_vendor/bootstrap/dist/css/bootstrap.min.css';
foreach ($apiJson->themes as $theme) {
$out["{$theme->name} - {$theme->description}"] = $theme->cssMin;
}
return $response->withJson($out);
}
public function applyTheme(Request $request, Response $response): Response
{
if (!is_writable('static/bootstrap/css/bootstrap.min.css')) {
$this->session->alert(lang('cannot_write_file'), 'danger');
return redirect($response, route('system'));
}
file_put_contents('static/bootstrap/css/bootstrap.min.css', file_get_contents($request->getParam('css')));
return redirect($response, 'system')
->withAddedHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
->withAddedHeader('Pragma', 'no-cache')
->withAddedHeader('Expire', '0');
}
}

View file

@ -2,9 +2,7 @@
namespace App\Controllers;
use App\Exceptions\UnauthorizedException;
use App\Web\Session;
use Intervention\Image\ImageManagerStatic as Image;
use League\Flysystem\FileExistsException;
use League\Flysystem\FileNotFoundException;
@ -84,7 +82,7 @@ class UploadController extends Controller
{
$media = $this->getMedia($args['userCode'], $args['mediaCode']);
if (!$media || (!$media->published && Session::get('user_id') !== $media->user_id && !Session::get('admin', false))) {
if (!$media || (!$media->published && $this->session->get('user_id') !== $media->user_id && !$this->session->get('admin', false))) {
throw new NotFoundException($request, $response);
}
@ -102,7 +100,7 @@ class UploadController extends Controller
} else if (in_array($type, ['image', 'video'])) {
$url = urlFor("/$args[userCode]/$args[mediaCode]/raw");
$header = "<{$url}>; rel=preload; as={$type}";
if (Session::get('logged', false)) {
if ($this->session->get('logged', false)) {
$header .= '; nopush';
}
$response = $response->withHeader('Link', $header);
@ -139,16 +137,16 @@ class UploadController extends Controller
$user = $this->database->query('SELECT `id`, `active` FROM `users` WHERE `token` = ? LIMIT 1', $args['token'])->fetch();
if (!$user) {
Session::alert(lang('token_not_found'), 'danger');
$this->session->alert(lang('token_not_found'), 'danger');
return $response->withRedirect($request->getHeaderLine('HTTP_REFERER'));
}
if (!$user->active) {
Session::alert(lang('account_disabled'), 'danger');
$this->session->alert(lang('account_disabled'), 'danger');
return $response->withRedirect($request->getHeaderLine('HTTP_REFERER'));
}
if (Session::get('admin', false) || $user->id === $media->user_id) {
if ($this->session->get('admin', false) || $user->id === $media->user_id) {
try {
storage()->delete($media->storage_path);
@ -197,7 +195,7 @@ class UploadController extends Controller
{
$media = $this->getMedia($args['userCode'], $args['mediaCode']);
if (!$media || !$media->published && Session::get('user_id') !== $media->user_id && !Session::get('admin', false)) {
if (!$media || !$media->published && $this->session->get('user_id') !== $media->user_id && !$this->session->get('admin', false)) {
throw new NotFoundException($request, $response);
}
return $this->streamMedia($request, $response, storage(), $media);
@ -216,7 +214,7 @@ class UploadController extends Controller
{
$media = $this->getMedia($args['userCode'], $args['mediaCode']);
if (!$media || !$media->published && Session::get('user_id') !== $media->user_id && !Session::get('admin', false)) {
if (!$media || !$media->published && $this->session->get('user_id') !== $media->user_id && !$this->session->get('admin', false)) {
throw new NotFoundException($request, $response);
}
return $this->streamMedia($request, $response, storage(), $media, 'attachment');
@ -231,10 +229,10 @@ class UploadController extends Controller
*/
public function togglePublish(Request $request, Response $response, $args): Response
{
if (Session::get('admin')) {
if ($this->session->get('admin')) {
$media = $this->database->query('SELECT * FROM `uploads` WHERE `id` = ? LIMIT 1', $args['id'])->fetch();
} else {
$media = $this->database->query('SELECT * FROM `uploads` WHERE `id` = ? AND `user_id` = ? LIMIT 1', [$args['id'], Session::get('user_id')])->fetch();
$media = $this->database->query('SELECT * FROM `uploads` WHERE `id` = ? AND `user_id` = ? LIMIT 1', [$args['id'], $this->session->get('user_id')])->fetch();
}
if (!$media) {
@ -262,7 +260,7 @@ class UploadController extends Controller
throw new NotFoundException($request, $response);
}
if (Session::get('admin', false) || $media->user_id === Session::get('user_id')) {
if ($this->session->get('admin', false) || $media->user_id === $this->session->get('user_id')) {
try {
storage()->delete($media->storage_path);
@ -270,8 +268,8 @@ class UploadController extends Controller
throw new NotFoundException($request, $response);
} 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', humanFileSize($this->getUsedSpaceByUser(Session::get('user_id'))));
$this->logger->info('User ' . $this->session->get('username') . ' deleted a media.', [$args['id']]);
$this->session->set('used_space', humanFileSize($this->getUsedSpaceByUser($this->session->get('user_id'))));
}
} else {
throw new UnauthorizedException();
@ -324,7 +322,6 @@ class UploadController extends Controller
->withHeader('Content-Disposition', $disposition . ';filename="scaled-' . pathinfo($media->filename)['filename'] . '.png"')
->write($image);
} else {
ob_end_clean();
return $response
->withHeader('Content-Type', $mime)
->withHeader('Content-Disposition', $disposition . '; filename="' . $media->filename . '"')

View file

@ -4,7 +4,6 @@ namespace App\Controllers;
use App\Exceptions\UnauthorizedException;
use App\Web\Session;
use Slim\Exception\NotFoundException;
use Slim\Http\Request;
use Slim\Http\Response;
@ -57,27 +56,27 @@ class UserController extends Controller
public function store(Request $request, Response $response): Response
{
if ($request->getParam('email') === null) {
Session::alert(lang('email_required'), 'danger');
$this->session->alert(lang('email_required'), 'danger');
return redirect($response, 'user.create');
}
if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `email` = ?', $request->getParam('email'))->fetch()->count > 0) {
Session::alert(lang('email_taken'), 'danger');
$this->session->alert(lang('email_taken'), 'danger');
return redirect($response, 'user.create');
}
if ($request->getParam('username') === null) {
Session::alert(lang('username_required'), 'danger');
$this->session->alert(lang('username_required'), 'danger');
return redirect($response, 'user.create');
}
if ($request->getParam('password') === null) {
Session::alert(lang('password_required'), 'danger');
$this->session->alert(lang('password_required'), 'danger');
return redirect($response, 'user.create');
}
if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `username` = ?', $request->getParam('username'))->fetch()->count > 0) {
Session::alert(lang('username_taken'), 'danger');
$this->session->alert(lang('username_taken'), 'danger');
return redirect($response, 'user.create');
}
@ -97,8 +96,8 @@ class UserController extends Controller
$token,
]);
Session::alert(lang('user_created', [$request->getParam('username')]), 'success');
$this->logger->info('User ' . Session::get('username') . ' created a new user.', [array_diff($request->getParams(), ['password'])]);
$this->session->alert(lang('user_created', [$request->getParam('username')]), 'success');
$this->logger->info('User ' . $this->session->get('username') . ' created a new user.', [array_diff($request->getParams(), ['password'])]);
return redirect($response, 'user.index');
}
@ -140,27 +139,27 @@ class UserController extends Controller
}
if ($request->getParam('email') === null) {
Session::alert(lang('email_required'), 'danger');
$this->session->alert(lang('email_required'), 'danger');
return redirect($response, 'user.edit', ['id' => $args['id']]);
}
if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `email` = ? AND `email` <> ?', [$request->getParam('email'), $user->email])->fetch()->count > 0) {
Session::alert(lang('email_taken'), 'danger');
$this->session->alert(lang('email_taken'), 'danger');
return redirect($response, 'user.edit', ['id' => $args['id']]);
}
if ($request->getParam('username') === null) {
Session::alert(lang('username_required'), 'danger');
$this->session->alert(lang('username_required'), 'danger');
return redirect($response, 'user.edit', ['id' => $args['id']]);
}
if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `username` = ? AND `username` <> ?', [$request->getParam('username'), $user->username])->fetch()->count > 0) {
Session::alert(lang('username_taken'), 'danger');
$this->session->alert(lang('username_taken'), 'danger');
return redirect($response, 'user.edit', ['id' => $args['id']]);
}
if ($user->id === Session::get('user_id') && $request->getParam('is_admin') === null) {
Session::alert(lang('cannot_demote'), 'danger');
if ($user->id === $this->session->get('user_id') && $request->getParam('is_admin') === null) {
$this->session->alert(lang('cannot_demote'), 'danger');
return redirect($response, 'user.edit', ['id' => $args['id']]);
}
@ -183,8 +182,8 @@ class UserController extends Controller
]);
}
Session::alert(lang('user_updated', [$request->getParam('username')]), 'success');
$this->logger->info('User ' . Session::get('username') . " updated $user->id.", [$user, array_diff($request->getParams(), ['password'])]);
$this->session->alert(lang('user_updated', [$request->getParam('username')]), 'success');
$this->logger->info('User ' . $this->session->get('username') . " updated $user->id.", [$user, array_diff($request->getParams(), ['password'])]);
return redirect($response, 'user.index');
@ -205,15 +204,15 @@ class UserController extends Controller
throw new NotFoundException($request, $response);
}
if ($user->id === Session::get('user_id')) {
Session::alert(lang('cannot_delete'), 'danger');
if ($user->id === $this->session->get('user_id')) {
$this->session->alert(lang('cannot_delete'), 'danger');
return redirect($response, 'user.index');
}
$this->database->query('DELETE FROM `users` WHERE `id` = ?', $user->id);
Session::alert(lang('user_deleted'), 'success');
$this->logger->info('User ' . Session::get('username') . " deleted $user->id.");
$this->session->alert(lang('user_deleted'), 'success');
$this->logger->info('User ' . $this->session->get('username') . " deleted $user->id.");
return redirect($response, 'user.index');
}
@ -227,13 +226,13 @@ class UserController extends Controller
*/
public function profile(Request $request, Response $response): Response
{
$user = $this->database->query('SELECT * FROM `users` WHERE `id` = ? LIMIT 1', Session::get('user_id'))->fetch();
$user = $this->database->query('SELECT * FROM `users` WHERE `id` = ? LIMIT 1', $this->session->get('user_id'))->fetch();
if (!$user) {
throw new NotFoundException($request, $response);
}
if ($user->id !== Session::get('user_id') && !Session::get('admin', false)) {
if ($user->id !== $this->session->get('user_id') && !$this->session->get('admin', false)) {
throw new UnauthorizedException();
}
@ -259,17 +258,17 @@ class UserController extends Controller
throw new NotFoundException($request, $response);
}
if ($user->id !== Session::get('user_id') && !Session::get('admin', false)) {
if ($user->id !== $this->session->get('user_id') && !$this->session->get('admin', false)) {
throw new UnauthorizedException();
}
if ($request->getParam('email') === null) {
Session::alert(lang('email_required'), 'danger');
$this->session->alert(lang('email_required'), 'danger');
return redirect($response, 'profile');
}
if ($this->database->query('SELECT COUNT(*) AS `count` FROM `users` WHERE `email` = ? AND `email` <> ?', [$request->getParam('email'), $user->email])->fetch()->count > 0) {
Session::alert(lang('email_taken'), 'danger');
$this->session->alert(lang('email_taken'), 'danger');
return redirect($response, 'profile');
}
@ -286,8 +285,8 @@ class UserController extends Controller
]);
}
Session::alert(lang('profile_updated'), 'success');
$this->logger->info('User ' . Session::get('username') . " updated profile of $user->id.");
$this->session->alert(lang('profile_updated'), 'success');
$this->logger->info('User ' . $this->session->get('username') . " updated profile of $user->id.");
return redirect($response, 'profile');
}
@ -308,7 +307,7 @@ class UserController extends Controller
throw new NotFoundException($request, $response);
}
if ($user->id !== Session::get('user_id') && !Session::get('admin', false)) {
if ($user->id !== $this->session->get('user_id') && !$this->session->get('admin', false)) {
throw new UnauthorizedException();
}
@ -319,7 +318,7 @@ class UserController extends Controller
$user->id,
]);
$this->logger->info('User ' . Session::get('username') . " refreshed token of user $user->id.");
$this->logger->info('User ' . $this->session->get('username') . " refreshed token of user $user->id.");
$response->getBody()->write($token);
@ -342,12 +341,12 @@ class UserController extends Controller
throw new NotFoundException($request, $response);
}
if ($user->id !== Session::get('user_id') && !Session::get('admin', false)) {
if ($user->id !== $this->session->get('user_id') && !$this->session->get('admin', false)) {
throw new UnauthorizedException();
}
if ($user->token === null || $user->token === '') {
Session::alert('You don\'t have a personal upload token. (Click the update token button and try again)', 'danger');
$this->session->alert('You don\'t have a personal upload token. (Click the update token button and try again)', 'danger');
return $response->withRedirect($request->getHeaderLine('HTTP_REFERER'));
}
@ -386,12 +385,12 @@ class UserController extends Controller
throw new NotFoundException($request, $response);
}
if ($user->id !== Session::get('user_id') && !Session::get('admin', false)) {
if ($user->id !== $this->session->get('user_id') && !$this->session->get('admin', false)) {
throw new UnauthorizedException();
}
if ($user->token === null || $user->token === '') {
Session::alert('You don\'t have a personal upload token. (Click the update token button and try again)', 'danger');
$this->session->alert('You don\'t have a personal upload token. (Click the update token button and try again)', 'danger');
return $response->withRedirect($request->getHeaderLine('HTTP_REFERER'));
}

View file

@ -39,7 +39,7 @@ class DB
}
}
public function doQuery(string $query, $parameters = [])
public function query(string $query, $parameters = [])
{
if (!is_array($parameters)) {
$parameters = [$parameters];
@ -87,7 +87,7 @@ class DB
* @param array $parameters
* @return bool|\PDOStatement|string
*/
public static function query(string $query, $parameters = [])
public static function doQuery(string $query, $parameters = [])
{
return self::getInstance()->doQuery($query, $parameters);

View file

@ -3,20 +3,11 @@
namespace App\Middleware;
use App\Exceptions\UnauthorizedException;
use App\Web\Session;
use Slim\Http\Request;
use Slim\Http\Response;
class AdminMiddleware
class AdminMiddleware extends Middleware
{
/** @var \Slim\Container */
private $container;
public function __construct($container)
{
$this->container = $container;
}
/**
* @param Request $request
* @param Response $response
@ -26,10 +17,10 @@ class AdminMiddleware
*/
public function __invoke(Request $request, Response $response, callable $next)
{
if (!$this->container->database->query('SELECT `id`, `is_admin` FROM `users` WHERE `id` = ? LIMIT 1', [Session::get('user_id')])->fetch()->is_admin) {
Session::alert('Your account is not admin anymore.', 'danger');
Session::set('admin', false);
Session::set('redirectTo', (isset($_SERVER['HTTPS']) ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]");
if (!$this->database->query('SELECT `id`, `is_admin` FROM `users` WHERE `id` = ? LIMIT 1', [$this->session->get('user_id')])->fetch()->is_admin) {
$this->session->alert('Your account is not admin anymore.', 'danger');
$this->session->set('admin', false);
$this->session->set('redirectTo', (isset($_SERVER['HTTPS']) ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]");
throw new UnauthorizedException();
}

View file

@ -2,21 +2,12 @@
namespace App\Middleware;
use App\Web\Session;
use Slim\Http\Request;
use Slim\Http\Response;
class AuthMiddleware
class AuthMiddleware extends Middleware
{
/** @var \Slim\Container */
private $container;
public function __construct($container)
{
$this->container = $container;
}
/**
* @param Request $request
* @param Response $response
@ -25,15 +16,15 @@ class AuthMiddleware
*/
public function __invoke(Request $request, Response $response, callable $next)
{
if (!Session::get('logged', false)) {
Session::set('redirectTo', (isset($_SERVER['HTTPS']) ? 'https' : 'http') . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]");
if (!$this->session->get('logged', false)) {
$this->session->set('redirectTo', (isset($_SERVER['HTTPS']) ? 'https' : 'http') . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]");
return redirect($response, 'login.show');
}
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]");
if (!$this->database->query('SELECT `id`, `active` FROM `users` WHERE `id` = ? LIMIT 1', [$this->session->get('user_id')])->fetch()->active) {
$this->session->alert('Your account is not active anymore.', 'danger');
$this->session->set('logged', false);
$this->session->set('redirectTo', (isset($_SERVER['HTTPS']) ? 'https' : 'http') . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]");
return redirect($response, 'login.show');
}

View file

@ -0,0 +1,38 @@
<?php
namespace App\Middleware;
use Slim\Container;
use Slim\Http\Request;
use Slim\Http\Response;
abstract class Middleware
{
/** @var Container */
protected $container;
public function __construct(Container $container)
{
$this->container = $container;
}
/**
* @param $name
* @return mixed|null
* @throws \Interop\Container\Exception\ContainerException
*/
public function __get($name)
{
if ($this->container->has($name)) {
return $this->container->get($name);
}
return null;
}
/**
* @param Request $request
* @param Response $response
* @param callable $next
*/
public abstract function __invoke(Request $request, Response $response, callable $next);
}

View file

@ -7,12 +7,12 @@ class Session
{
/**
* Start a session if is not already started in the current context
* Session constructor.
* @param string $name
* @param string $path
* @throws \Exception
*/
public static function init(string $name, $path = ''): void
public function __construct(string $name, $path = '')
{
if (session_status() === PHP_SESSION_NONE) {
if (!is_writable($path) && $path !== '') {
@ -30,7 +30,7 @@ class Session
* Destroy the current session
* @return bool
*/
public static function destroy(): bool
public function destroy(): bool
{
return session_destroy();
}
@ -38,7 +38,7 @@ class Session
/**
* Clear all session stored values
*/
public static function clear(): void
public function clear(): void
{
$_SESSION = [];
}
@ -48,7 +48,7 @@ class Session
* @param $key
* @return bool
*/
public static function has($key): bool
public function has($key): bool
{
return isset($_SESSION[$key]);
}
@ -57,7 +57,7 @@ class Session
* Get the content of the current session
* @return array
*/
public static function all(): array
public function all(): array
{
return $_SESSION;
}
@ -68,7 +68,7 @@ class Session
* @param null $default
* @return mixed
*/
public static function get($key, $default = null)
public function get($key, $default = null)
{
return self::has($key) ? $_SESSION[$key] : $default;
}
@ -78,7 +78,7 @@ class Session
* @param $key
* @param $value
*/
public static function set($key, $value): void
public function set($key, $value): void
{
$_SESSION[$key] = $value;
}
@ -88,7 +88,7 @@ class Session
* @param $message
* @param string $type
*/
public static function alert($message, string $type = 'info'): void
public function alert($message, string $type = 'info'): void
{
$_SESSION['_flash'][] = [$type => $message];
}
@ -98,7 +98,7 @@ class Session
* Retrieve flash alerts
* @return array
*/
public static function getAlert()
public function getAlert()
{
$flash = self::get('_flash');
self::set('_flash', []);

View file

@ -125,7 +125,8 @@ if (!function_exists('lang')) {
*/
function lang(string $key, $args = []): string
{
return \App\Web\Lang::getInstance()->get($key, $args);
global $app;
return $app->getContainer()->get('lang')->get($key, $args);
}
}

View file

@ -2,9 +2,10 @@
// Auth routes
$app->group('', function () {
$this->get('/home[/page/{page}]', \App\Controllers\DashboardController::class . ':home')->setName('home');
$this->get('/system', \App\Controllers\DashboardController::class . ':system')->add(\App\Middleware\AdminMiddleware::class)->setName('system');
$this->get('/system/themes', \App\Controllers\DashboardController::class . ':getThemes')->add(\App\Middleware\AdminMiddleware::class)->setName('theme');
$this->post('/system/theme/apply', \App\Controllers\DashboardController::class . ':applyTheme')->add(\App\Middleware\AdminMiddleware::class)->setName('theme.apply');
$this->get('/system/deleteOrphanFiles', \App\Controllers\AdminController::class . ':deleteOrphanFiles')->add(\App\Middleware\AdminMiddleware::class)->setName('system.deleteOrphanFiles');
$this->get('/system/themes', \App\Controllers\ThemeController::class . ':getThemes')->add(\App\Middleware\AdminMiddleware::class)->setName('theme');
$this->post('/system/theme/apply', \App\Controllers\ThemeController::class . ':applyTheme')->add(\App\Middleware\AdminMiddleware::class)->setName('theme.apply');
$this->get('/system', \App\Controllers\AdminController::class . ':system')->add(\App\Middleware\AdminMiddleware::class)->setName('system');
$this->group('', function () {
$this->get('/users[/page/{page}]', \App\Controllers\UserController::class . ':index')->setName('user.index');
@ -38,5 +39,5 @@ $app->post('/upload', \App\Controllers\UploadController::class . ':upload')->set
$app->get('/{userCode}/{mediaCode}', \App\Controllers\UploadController::class . ':show')->setName('public');
$app->get('/{userCode}/{mediaCode}/delete/{token}', \App\Controllers\UploadController::class . ':show')->setName('public.delete.show');
$app->post('/{userCode}/{mediaCode}/delete/{token}', \App\Controllers\UploadController::class . ':deleteByToken')->setName('public.delete');
$app->get('/{userCode}/{mediaCode}/raw', \App\Controllers\UploadController::class . ':showRaw')->setName('public.raw');
$app->get('/{userCode}/{mediaCode}/download', \App\Controllers\UploadController::class . ':download')->setName('public.download');
$app->get('/{userCode}/{mediaCode}/raw', \App\Controllers\UploadController::class . ':showRaw')->setName('public.raw')->setOutputBuffering(false);
$app->get('/{userCode}/{mediaCode}/download', \App\Controllers\UploadController::class . ':download')->setName('public.download')->setOutputBuffering(false);

View file

@ -24,7 +24,7 @@ if (!file_exists($config['db']['dsn']) && DB::driver() === 'sqlite') {
}
try {
DB::query('SELECT 1 FROM `migrations` LIMIT 1');
DB::doQuery('SELECT 1 FROM `migrations` LIMIT 1');
} catch (PDOException $exception) {
$firstMigrate = true;
}
@ -44,7 +44,7 @@ $names = array_map(function ($path) {
$in = str_repeat('?, ', count($names) - 1) . '?';
$inMigrationsTable = DB::query("SELECT * FROM `migrations` WHERE `name` IN ($in)", $names)->fetchAll();
$inMigrationsTable = DB::doQuery("SELECT * FROM `migrations` WHERE `name` IN ($in)", $names)->fetchAll();
foreach ($files as $file) {
@ -67,14 +67,14 @@ foreach ($files as $file) {
try {
DB::raw()->exec($sql);
if (!$exists) {
DB::query('INSERT INTO `migrations` VALUES (?,?)', [basename($file), 1]);
DB::doQuery('INSERT INTO `migrations` VALUES (?,?)', [basename($file), 1]);
} else {
DB::query('UPDATE `migrations` SET `migrated`=? WHERE `name`=?', [1, basename($file)]);
DB::doQuery('UPDATE `migrations` SET `migrated`=? WHERE `name`=?', [1, basename($file)]);
}
echo "Migrated '$file'" . PHP_EOL;
} catch (PDOException $exception) {
if (!$exists) {
DB::query('INSERT INTO `migrations` VALUES (?,?)', [basename($file), 0]);
DB::doQuery('INSERT INTO `migrations` VALUES (?,?)', [basename($file), 0]);
}
echo "Error migrating '$file' (" . $exception->getMessage() . ')' . PHP_EOL;
echo $exception->getTraceAsString() . PHP_EOL;
@ -83,7 +83,7 @@ foreach ($files as $file) {
}
if (isset($argv[1]) && $argv[1] === '--install') {
DB::query("INSERT INTO `users` (`email`, `username`, `password`, `is_admin`, `user_code`) VALUES ('admin@example.com', 'admin', ?, 1, ?)", [password_hash('admin', PASSWORD_DEFAULT), substr(md5(microtime()), rand(0, 26), 5)]);
DB::doQuery("INSERT INTO `users` (`email`, `username`, `password`, `is_admin`, `user_code`) VALUES ('admin@example.com', 'admin', ?, 1, ?)", [password_hash('admin', PASSWORD_DEFAULT), substr(md5(microtime()), rand(0, 26), 5)]);
}
echo 'Done.' . PHP_EOL;

View file

@ -47,18 +47,18 @@ $container['logger'] = function ($container) {
return $logger;
};
// Session init
Session::init('xbackbone_session', __DIR__ . '/../resources/sessions');
// Set the database dsn
$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();
$container['session'] = function ($container) {
return new Session('xbackbone_session', __DIR__ . '/../resources/sessions');
};
Lang::build(Lang::recognize(), __DIR__. '/../resources/lang/');
$container['database'] = function ($container) use (&$config) {
$dsn = $config['db']['connection'] === 'sqlite' ? __DIR__ . '/../' . $config['db']['dsn'] : $config['db']['dsn'];
return new DB($config['db']['connection'] . ':' . $dsn, $config['db']['username'], $config['db']['password']);
};
$container['lang'] = function ($container) {
return Lang::build(Lang::recognize(), __DIR__ . '/../resources/lang/');
};
$container['view'] = function ($container) use (&$config) {
$view = new \Slim\Views\Twig(__DIR__ . '/../resources/templates', [
@ -75,9 +75,9 @@ $container['view'] = function ($container) use (&$config) {
$view->getEnvironment()->addGlobal('config', $config);
$view->getEnvironment()->addGlobal('request', $container->get('request'));
$view->getEnvironment()->addGlobal('alerts', Session::getAlert());
$view->getEnvironment()->addGlobal('session', Session::all());
$view->getEnvironment()->addGlobal('current_lang', Lang::getInstance()->getLang());
$view->getEnvironment()->addGlobal('alerts', $container->get('session')->getAlert());
$view->getEnvironment()->addGlobal('session', $container->get('session')->all());
$view->getEnvironment()->addGlobal('current_lang', $container->get('lang')->getLang());
$view->getEnvironment()->addGlobal('PLATFORM_VERSION', PLATFORM_VERSION);
$view->getEnvironment()->addFunction(new Twig_Function('route', 'route'));

View file

@ -1,6 +1,6 @@
{
"name": "sergix44/xbackbone",
"version": "2.3.1",
"version": "2.4",
"description": "A lightweight ShareX PHP backend",
"type": "project",
"require": {

View file

@ -24,7 +24,9 @@ $config = [
$container = new Container(['settings' => $config]);
Session::init('xbackbone_session');
$container['session'] = function ($container) {
return new Session('xbackbone_session');
};
$container['view'] = function ($container) use (&$config) {
$view = new \Slim\Views\Twig([__DIR__ . '/templates', __DIR__ . '/../resources/templates'], [
@ -41,8 +43,8 @@ $container['view'] = function ($container) use (&$config) {
$view->getEnvironment()->addGlobal('config', $config);
$view->getEnvironment()->addGlobal('request', $container->get('request'));
$view->getEnvironment()->addGlobal('alerts', Session::getAlert());
$view->getEnvironment()->addGlobal('session', Session::all());
$view->getEnvironment()->addGlobal('alerts', $container->get('session')->getAlert());
$view->getEnvironment()->addGlobal('session', $container->get('session')->all());
$view->getEnvironment()->addGlobal('PLATFORM_VERSION', PLATFORM_VERSION);
return $view;
};
@ -56,7 +58,7 @@ function migrate($config)
}
try {
DB::query('SELECT 1 FROM `migrations` LIMIT 1');
DB::doQuery('SELECT 1 FROM `migrations` LIMIT 1');
} catch (PDOException $exception) {
$firstMigrate = true;
}
@ -73,7 +75,7 @@ function migrate($config)
$in = str_repeat('?, ', count($names) - 1) . '?';
$inMigrationsTable = DB::query("SELECT * FROM `migrations` WHERE `name` IN ($in)", $names)->fetchAll();
$inMigrationsTable = DB::doQuery("SELECT * FROM `migrations` WHERE `name` IN ($in)", $names)->fetchAll();
foreach ($files as $file) {
@ -96,13 +98,13 @@ function migrate($config)
try {
DB::raw()->exec($sql);
if (!$exists) {
DB::query('INSERT INTO `migrations` VALUES (?,?)', [basename($file), 1]);
DB::doQuery('INSERT INTO `migrations` VALUES (?,?)', [basename($file), 1]);
} else {
DB::query('UPDATE `migrations` SET `migrated`=? WHERE `name`=?', [1, basename($file)]);
DB::doQuery('UPDATE `migrations` SET `migrated`=? WHERE `name`=?', [1, basename($file)]);
}
} catch (PDOException $exception) {
if (!$exists) {
DB::query('INSERT INTO `migrations` VALUES (?,?)', [basename($file), 0]);
DB::doQuery('INSERT INTO `migrations` VALUES (?,?)', [basename($file), 0]);
}
throw $exception;
}
@ -114,15 +116,15 @@ $app = new App($container);
$app->get('/', function (Request $request, Response $response) {
if (!is_writable(__DIR__ . '/../resources/cache')) {
Session::alert('The cache folder is not writable (' . __DIR__ . '/../resources/cache' . ')', 'danger');
$this->session->alert('The cache folder is not writable (' . __DIR__ . '/../resources/cache' . ')', 'danger');
}
if (!is_writable(__DIR__ . '/../resources/database')) {
Session::alert('The database folder is not writable (' . __DIR__ . '/../resources/database' . ')', 'danger');
$this->session->alert('The database folder is not writable (' . __DIR__ . '/../resources/database' . ')', 'danger');
}
if (!is_writable(__DIR__ . '/../resources/sessions')) {
Session::alert('The sessions folder is not writable (' . __DIR__ . '/../resources/sessions' . ')', 'danger');
$this->session->alert('The sessions folder is not writable (' . __DIR__ . '/../resources/sessions' . ')', 'danger');
}
$installed = file_exists(__DIR__ . '/../config.php');
@ -146,18 +148,18 @@ $app->post('/', function (Request $request, Response $response) use (&$config) {
try {
storage($config['storage_dir']);
} catch (LogicException $exception) {
Session::alert('The storage folder is not readable (' . $config['storage_dir'] . ')', 'danger');
$this->session->alert('The storage folder is not readable (' . $config['storage_dir'] . ')', 'danger');
return redirect($response, './');
} finally {
if (!is_writable($config['storage_dir'])) {
Session::alert('The storage folder is not writable (' . $config['storage_dir'] . ')', 'danger');
$this->session->alert('The storage folder is not writable (' . $config['storage_dir'] . ')', 'danger');
return redirect($response, './');
}
}
$ret = file_put_contents(__DIR__ . '/../config.php', '<?php' . PHP_EOL . 'return ' . var_export($config, true) . ';');
if ($ret === false) {
Session::alert('The config folder is not writable (' . __DIR__ . '/../config.php' . ')', 'danger');
$this->session->alert('The config folder is not writable (' . __DIR__ . '/../config.php' . ')', 'danger');
return redirect($response, './');
}
}
@ -169,12 +171,12 @@ $app->post('/', function (Request $request, Response $response) use (&$config) {
migrate($config);
} catch (PDOException $exception) {
Session::alert("Cannot connect to the database: {$exception->getMessage()} [{$exception->getCode()}]", 'danger');
$this->session->alert("Cannot connect to the database: {$exception->getMessage()} [{$exception->getCode()}]", 'danger');
return redirect($response, './');
}
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)]);
DB::doQuery("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)]);
}
cleanDirectory(__DIR__ . '/../resources/cache');

View file

@ -85,4 +85,5 @@ return [
'cannot_delete' => 'You cannot delete yourself.',
'cannot_demote' => 'You cannot demote yourself.',
'cannot_write_file' => 'The destination path is not writable.',
'deleted_orphans' => 'Successfully deleted %d orphaned files.',
];

View file

@ -85,4 +85,5 @@ return [
'cannot_delete' => 'Non puoi eliminare te stesso.',
'cannot_demote' => 'Non puoi degradare te stesso. ',
'cannot_write_file' => 'Il percorso di destinazione non è scrivibile.',
'deleted_orphans' => 'Eliminati %d file orfani.',
];

View file

@ -1,5 +1,5 @@
<footer class="footer">
<div class="container-fluid">
<div class="text-muted">Proudly powered by <a href="https://github.com/SergiX44/XBackBone" target="_blank">XBackBone{% if session.logged %} v{{ PLATFORM_VERSION }}{% endif %}</a></div>
<div class="text-muted">Proudly powered by <a href="https://github.com/SergiX44/XBackBone">XBackBone{% if session.logged %} v{{ PLATFORM_VERSION }}{% endif %}</a></div>
</div>
</footer>

View file

@ -5,6 +5,7 @@
{% block content %}
{% include 'comp/navbar.twig' %}
<div class="container">
{% include 'comp/alert.twig' %}
<div class="row">
<div class="col-xl-3 col-sm-6 mb-3">
<div class="card bg-success text-white h-100 shadow-sm">