Update quota counters on upload and deletion
This commit is contained in:
parent
b2e0d683a2
commit
4297683e74
7 changed files with 141 additions and 62 deletions
|
@ -1,9 +1,15 @@
|
|||
## v.3.1 (WIP)
|
||||
+ The theme is now re-applied after every system update.
|
||||
+ Added registration system.
|
||||
+ Added password recovery system.
|
||||
+ Added ability to export all media of an account.
|
||||
+ Added ability to choose between default and raw url on copy.
|
||||
+ Added hide by default option.
|
||||
+ Added user disk quota.
|
||||
+ Fixed bug html files raws are rendered in a browser.
|
||||
+ The theme is now re-applied after every system update.
|
||||
+ Updated system settings page.
|
||||
+ Updated translations.
|
||||
+ Small fixes and improvements.
|
||||
|
||||
## v.3.0.2
|
||||
+ Fixed error with migrate command.
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Database\DB;
|
||||
use App\Web\Media;
|
||||
use League\Flysystem\FileNotFoundException;
|
||||
use League\Flysystem\Filesystem;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
|
@ -102,29 +105,7 @@ class AdminController extends Controller
|
|||
*/
|
||||
public function recalculateUserQuota(Response $response): Response
|
||||
{
|
||||
$uploads = $this->database->query('SELECT `id`,`user_id`, `storage_path` FROM `uploads`')->fetchAll();
|
||||
|
||||
$usersQuotas = [];
|
||||
|
||||
$filesystem = $this->storage;
|
||||
foreach ($uploads as $upload) {
|
||||
if (!array_key_exists($upload->user_id, $usersQuotas)) {
|
||||
$usersQuotas[$upload->user_id] = 0;
|
||||
}
|
||||
try {
|
||||
$usersQuotas[$upload->user_id] += $filesystem->getSize($upload->storage_path);
|
||||
} catch (FileNotFoundException $e) {
|
||||
$this->database->query('DELETE FROM `uploads` WHERE `id` = ?', $upload->id);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($usersQuotas as $userId => $quota) {
|
||||
$this->database->query('UPDATE `users` SET `current_disk_quota`=? WHERE `id` = ?', [
|
||||
$quota,
|
||||
$userId,
|
||||
]);
|
||||
}
|
||||
|
||||
Media::recalculateQuotas($this->database, $this->storage);
|
||||
$this->session->alert(lang('quota_recalculated'));
|
||||
return redirect($response, route('system'));
|
||||
}
|
||||
|
|
|
@ -73,6 +73,38 @@ abstract class Controller
|
|||
return $totalSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param $userId
|
||||
* @param $fileSize
|
||||
* @param bool $dec
|
||||
* @return bool
|
||||
* @throws HttpNotFoundException
|
||||
* @throws HttpUnauthorizedException
|
||||
*/
|
||||
protected function updateUserQuota(Request $request, $userId, $fileSize, $dec = false)
|
||||
{
|
||||
$user = $this->getUser($request, $userId);
|
||||
|
||||
if ($dec) {
|
||||
$tot = max($user->current_disk_quota - $fileSize, 0);
|
||||
} else {
|
||||
$tot = $user->current_disk_quota + $fileSize;
|
||||
|
||||
$quotaEnabled = $this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'quota_enabled\'')->fetch()->value ?? 'off';
|
||||
if ($quotaEnabled === 'on' && $user->max_disk_quota > 0 && $user->max_disk_quota < $tot) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->database->query('UPDATE `users` SET `current_disk_quota`=? WHERE `id` = ?', [
|
||||
$tot,
|
||||
$user->id
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param $id
|
||||
|
|
|
@ -189,6 +189,7 @@ class MediaController extends Controller
|
|||
* @throws HttpUnauthorizedException
|
||||
*
|
||||
* @throws HttpNotFoundException
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
public function delete(Request $request, Response $response, int $id): Response
|
||||
{
|
||||
|
@ -199,7 +200,8 @@ class MediaController extends Controller
|
|||
}
|
||||
|
||||
if ($this->session->get('admin', false) || $media->user_id === $this->session->get('user_id')) {
|
||||
$this->deleteMedia($request, $media->storage_path, $id);
|
||||
$size = $this->deleteMedia($request, $media->storage_path, $id);
|
||||
$this->updateUserQuota($request, $media->user_id, $size, true);
|
||||
$this->logger->info('User '.$this->session->get('username').' deleted a media.', [$id]);
|
||||
$this->session->set('used_space', humanFileSize($this->getUsedSpaceByUser($this->session->get('user_id'))));
|
||||
} else {
|
||||
|
@ -248,7 +250,8 @@ class MediaController extends Controller
|
|||
}
|
||||
|
||||
if ($this->session->get('admin', false) || $user->id === $media->user_id) {
|
||||
$this->deleteMedia($request, $media->storage_path, $media->mediaId);
|
||||
$size = $this->deleteMedia($request, $media->storage_path, $media->mediaId);
|
||||
$this->updateUserQuota($request, $media->user_id, $size, true);
|
||||
$this->logger->info('User '.$user->username.' deleted a media via token.', [$media->mediaId]);
|
||||
} else {
|
||||
throw new HttpUnauthorizedException($request);
|
||||
|
@ -262,12 +265,15 @@ class MediaController extends Controller
|
|||
* @param string $storagePath
|
||||
* @param int $id
|
||||
*
|
||||
* @return bool|false|int
|
||||
* @throws HttpNotFoundException
|
||||
*/
|
||||
protected function deleteMedia(Request $request, string $storagePath, int $id)
|
||||
{
|
||||
try {
|
||||
$size = $this->storage->getSize($storagePath);
|
||||
$this->storage->delete($storagePath);
|
||||
return $size;
|
||||
} catch (FileNotFoundException $e) {
|
||||
throw new HttpNotFoundException($request);
|
||||
} finally {
|
||||
|
|
|
@ -39,6 +39,7 @@ class UploadController extends Controller
|
|||
*
|
||||
* @return Response
|
||||
* @throws FileExistsException
|
||||
* @throws \Exception
|
||||
*
|
||||
*/
|
||||
public function upload(Request $request, Response $response): Response
|
||||
|
@ -96,33 +97,44 @@ class UploadController extends Controller
|
|||
return json($response, $json, 401);
|
||||
}
|
||||
|
||||
do {
|
||||
$code = humanRandomString();
|
||||
} while ($this->database->query('SELECT COUNT(*) AS `count` FROM `uploads` WHERE `code` = ?', $code)->fetch()->count > 0);
|
||||
if (!$this->updateUserQuota($request, $user->id, $file->getSize())) {
|
||||
$json['message'] = 'User disk quota exceeded.';
|
||||
|
||||
$published = 1;
|
||||
if (($this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'hide_by_default\'')->fetch()->value ?? 'off') === 'on') {
|
||||
$published = 0;
|
||||
return json($response, $json, 507);
|
||||
}
|
||||
|
||||
$fileInfo = pathinfo($file->getClientFilename());
|
||||
$storagePath = "$user->user_code/$code.$fileInfo[extension]";
|
||||
try {
|
||||
do {
|
||||
$code = humanRandomString();
|
||||
} while ($this->database->query('SELECT COUNT(*) AS `count` FROM `uploads` WHERE `code` = ?', $code)->fetch()->count > 0);
|
||||
|
||||
$this->storage->writeStream($storagePath, $file->getStream()->detach());
|
||||
$published = 1;
|
||||
if (($this->database->query('SELECT `value` FROM `settings` WHERE `key` = \'hide_by_default\'')->fetch()->value ?? 'off') === 'on') {
|
||||
$published = 0;
|
||||
}
|
||||
|
||||
$this->database->query('INSERT INTO `uploads`(`user_id`, `code`, `filename`, `storage_path`, `published`) VALUES (?, ?, ?, ?, ?)', [
|
||||
$user->id,
|
||||
$code,
|
||||
$file->getClientFilename(),
|
||||
$storagePath,
|
||||
$published,
|
||||
]);
|
||||
$fileInfo = pathinfo($file->getClientFilename());
|
||||
$storagePath = "$user->user_code/$code.$fileInfo[extension]";
|
||||
|
||||
$json['message'] = 'OK.';
|
||||
$json['url'] = urlFor("/{$user->user_code}/{$code}.{$fileInfo['extension']}");
|
||||
$this->storage->writeStream($storagePath, $file->getStream()->detach());
|
||||
|
||||
$this->logger->info("User $user->username uploaded new media.", [$this->database->getPdo()->lastInsertId()]);
|
||||
$this->database->query('INSERT INTO `uploads`(`user_id`, `code`, `filename`, `storage_path`, `published`) VALUES (?, ?, ?, ?, ?)', [
|
||||
$user->id,
|
||||
$code,
|
||||
$file->getClientFilename(),
|
||||
$storagePath,
|
||||
$published,
|
||||
]);
|
||||
|
||||
return json($response, $json, 201);
|
||||
$json['message'] = 'OK.';
|
||||
$json['url'] = urlFor("/{$user->user_code}/{$code}.{$fileInfo['extension']}");
|
||||
|
||||
$this->logger->info("User $user->username uploaded new media.", [$this->database->getPdo()->lastInsertId()]);
|
||||
|
||||
return json($response, $json, 201);
|
||||
} catch (\Exception $e) {
|
||||
$this->updateUserQuota($request, $user->id, $file->getSize(), false);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
37
app/Web/Media.php
Normal file
37
app/Web/Media.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Web;
|
||||
|
||||
|
||||
use App\Database\DB;
|
||||
use League\Flysystem\FileNotFoundException;
|
||||
use League\Flysystem\Filesystem;
|
||||
|
||||
class Media
|
||||
{
|
||||
public static function recalculateQuotas(DB $db, Filesystem $filesystem)
|
||||
{
|
||||
$uploads = $db->query('SELECT `id`,`user_id`, `storage_path` FROM `uploads`')->fetchAll();
|
||||
|
||||
$usersQuotas = [];
|
||||
|
||||
foreach ($uploads as $upload) {
|
||||
if (!array_key_exists($upload->user_id, $usersQuotas)) {
|
||||
$usersQuotas[$upload->user_id] = 0;
|
||||
}
|
||||
try {
|
||||
$usersQuotas[$upload->user_id] += $filesystem->getSize($upload->storage_path);
|
||||
} catch (FileNotFoundException $e) {
|
||||
$db->query('DELETE FROM `uploads` WHERE `id` = ?', $upload->id);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($usersQuotas as $userId => $quota) {
|
||||
$db->query('UPDATE `users` SET `current_disk_quota`=? WHERE `id` = ?', [
|
||||
$quota,
|
||||
$userId,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ require __DIR__.'/../vendor/autoload.php';
|
|||
use App\Database\DB;
|
||||
use App\Database\Migrator;
|
||||
use App\Factories\ViewFactory;
|
||||
use App\Web\Media;
|
||||
use App\Web\Session;
|
||||
use App\Web\View;
|
||||
use DI\Bridge\Slim\Bridge;
|
||||
|
@ -25,16 +26,16 @@ define('BASE_DIR', realpath(__DIR__.'/../').DIRECTORY_SEPARATOR);
|
|||
// default config
|
||||
$config = [
|
||||
'base_url' => str_replace('/install/', '', (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http')."://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"),
|
||||
'debug' => true,
|
||||
'db' => [
|
||||
'debug' => true,
|
||||
'db' => [
|
||||
'connection' => 'sqlite',
|
||||
'dsn' => realpath(__DIR__.'/../').implode(DIRECTORY_SEPARATOR, ['resources', 'database', 'xbackbone.db']),
|
||||
'username' => null,
|
||||
'password' => null,
|
||||
'dsn' => realpath(__DIR__.'/../').implode(DIRECTORY_SEPARATOR, ['resources', 'database', 'xbackbone.db']),
|
||||
'username' => null,
|
||||
'password' => null,
|
||||
],
|
||||
'storage' => [
|
||||
'driver' => 'local',
|
||||
'path' => realpath(__DIR__.'/../').DIRECTORY_SEPARATOR.'storage',
|
||||
'path' => realpath(__DIR__.'/../').DIRECTORY_SEPARATOR.'storage',
|
||||
],
|
||||
];
|
||||
|
||||
|
@ -45,7 +46,7 @@ if (file_exists(__DIR__.'/../config.php')) {
|
|||
$builder = new ContainerBuilder();
|
||||
|
||||
$builder->addDefinitions([
|
||||
'config' => value($config),
|
||||
'config' => value($config),
|
||||
View::class => factory(function (Container $container) {
|
||||
return ViewFactory::createInstallerInstance($container);
|
||||
}),
|
||||
|
@ -93,7 +94,7 @@ $app->get('/', function (Response $response, View $view, Session $session) use (
|
|||
]);
|
||||
})->setName('install');
|
||||
|
||||
$app->post('/', function (Request $request, Response $response, Filesystem $storage, Session $session) use (&$config) {
|
||||
$app->post('/', function (Request $request, Response $response, Filesystem $storage, Session $session, DB $db) use (&$config) {
|
||||
|
||||
// Check if there is a previous installation, if not, setup the config file
|
||||
$installed = true;
|
||||
|
@ -185,8 +186,6 @@ $app->post('/', function (Request $request, Response $response, Filesystem $stor
|
|||
$firstMigrate = true;
|
||||
}
|
||||
|
||||
$db = new DB(dsnFromConfig($config), $config['db']['username'], $config['db']['password']);
|
||||
|
||||
$migrator = new Migrator($db, __DIR__.'/../resources/schemas', $firstMigrate);
|
||||
$migrator->migrate();
|
||||
} catch (PDOException $e) {
|
||||
|
@ -200,6 +199,18 @@ $app->post('/', function (Request $request, Response $response, Filesystem $stor
|
|||
$db->query("INSERT INTO `users` (`email`, `username`, `password`, `is_admin`, `user_code`) VALUES (?, 'admin', ?, 1, ?)", [param($request, 'email'), password_hash(param($request, 'password'), PASSWORD_DEFAULT), humanRandomString(5)]);
|
||||
}
|
||||
|
||||
// re-apply the previous theme if is present
|
||||
$css = $db->query('SELECT `value` FROM `settings` WHERE `key` = \'css\'')->fetch()->value;
|
||||
if ($css) {
|
||||
$content = file_get_contents($css);
|
||||
if ($content !== false) {
|
||||
file_put_contents(BASE_DIR.'static/bootstrap/css/bootstrap.min.css', $content);
|
||||
}
|
||||
}
|
||||
|
||||
// recalculate user quota
|
||||
Media::recalculateQuotas($db, $storage);
|
||||
|
||||
// if is upgrading and existing installation, put it out maintenance
|
||||
if ($installed) {
|
||||
unset($config['maintenance']);
|
||||
|
@ -217,12 +228,6 @@ $app->post('/', function (Request $request, Response $response, Filesystem $stor
|
|||
return redirect($response, '/install');
|
||||
}
|
||||
|
||||
// re-apply the previous theme if is present
|
||||
$css = $db->query('SELECT `value` FROM `settings` WHERE `key` = \'css\'')->fetch()->value;
|
||||
if ($css) {
|
||||
file_put_contents(BASE_DIR.'static/bootstrap/css/bootstrap.min.css', file_get_contents($css));
|
||||
}
|
||||
|
||||
// post install cleanup
|
||||
cleanDirectory(__DIR__.'/../resources/cache');
|
||||
cleanDirectory(__DIR__.'/../resources/sessions');
|
||||
|
|
Loading…
Reference in a new issue