Moved user asset loading into the router.

This commit is contained in:
flash 2020-06-09 18:48:35 +00:00
parent 0e885f6311
commit fa80803dd2
6 changed files with 105 additions and 71 deletions

View file

@ -16,7 +16,11 @@ Router::addRoutes(
Route::get('/', 'index', 'Home'),
// Assets
Route::get('/assets/([a-zA-Z0-9\-]+)\.(css|js)', 'view', 'Assets'),
Route::group('/assets', 'Assets')->addChildren(
Route::get('/([a-zA-Z0-9\-]+)\.(css|js)', 'serveComponent'),
Route::get('/avatar/([0-9]+)(?:\.png)?', 'serveAvatar'),
Route::get('/profile-background/([0-9]+)(?:\.png)?', 'serveProfileBackground'),
),
// Info
Route::get('/info', 'index', 'Info'),
@ -71,6 +75,7 @@ Router::addRoutes(
Route::get('/news/feed.php', 'legacy', 'News'),
Route::get('/news/feed.php/rss', 'legacy', 'News'),
Route::get('/news/feed.php/atom', 'legacy', 'News'),
Route::get('/user-assets.php', 'serveLegacy', 'Assets'),
);
$response = Router::handle($request);
@ -90,7 +95,7 @@ foreach($response->getHeaders() as $name => $lines) {
$responseBody = $response->getBody();
if($responseStatus >= 400 && $responseStatus <= 599 && ($responseBody === null || $responseBody->getSize() < 1))
if($responseStatus >= 400 && $responseStatus <= 599 && $responseBody === null)
echo render_error($responseStatus);
else
echo (string)$responseBody;

View file

@ -1,66 +1,2 @@
<?php
namespace Misuzu;
use Misuzu\Users\User;
use Misuzu\Users\UserNotFoundException;
use Misuzu\Users\Assets\StaticUserImageAsset;
use Misuzu\Users\Assets\UserAssetScalableInterface;
$assetMode = (string)filter_input(INPUT_GET, 'm', FILTER_SANITIZE_STRING);
$assetUserId = (int)filter_input(INPUT_GET, 'u', FILTER_SANITIZE_NUMBER_INT);
$assetDims = filter_input(INPUT_GET, 'r', FILTER_SANITIZE_NUMBER_INT);
$misuzuBypassLockdown = $assetMode === 'avatar';
require_once '../misuzu.php';
try {
$assetUser = User::byId($assetUserId);
} catch(UserNotFoundException $ex) {}
$assetVisible = !isset($assetUser) || !$assetUser->isBanned() || (
parse_url($_SERVER['HTTP_REFERER'] ?? '', PHP_URL_PATH) === url('user-profile')
&& User::hasCurrent() && perms_check_user(MSZ_PERMS_USER, User::getCurrent()->getId(), MSZ_PERM_USER_MANAGE_USERS)
);
switch($assetMode) {
case 'avatar':
if(!$assetVisible) {
$assetInfo = new StaticUserImageAsset(MSZ_PUBLIC . '/images/banned-avatar.png', MSZ_PUBLIC);
break;
}
$assetInfo = new StaticUserImageAsset(MSZ_PUBLIC . '/images/no-avatar.png', MSZ_PUBLIC);
if(!isset($assetUser) || !$assetUser->hasAvatar())
break;
$assetInfo = $assetUser->getAvatarInfo();
break;
case 'background':
if(!$assetVisible || !isset($assetUser) || !$assetUser->hasBackground())
break;
$assetInfo = $assetUser->getBackgroundInfo();
break;
}
if(!isset($assetInfo) || !$assetInfo->isPresent()) {
http_response_code(404);
return;
}
$contentType = $assetInfo->getMimeType();
$publicPath = $assetInfo->getPublicPath();
$fileName = $assetInfo->getFileName();
if($assetDims > 0 && $assetInfo instanceof UserAssetScalableInterface) {
$assetInfo->ensureScaledExists($assetDims);
$publicPath = $assetInfo->getPublicScaledPath($assetDims);
$fileName = $assetInfo->getScaledFileName($assetDims);
}
header(sprintf('X-Accel-Redirect: %s', $publicPath));
header(sprintf('Content-Type: %s', $contentType));
header(sprintf('Content-Disposition: inline; filename="%s"', $fileName));
require_once __DIR__ . '/index.php';

View file

@ -4,6 +4,11 @@ namespace Misuzu\Http\Handlers;
use HttpResponse;
use HttpRequest;
use Misuzu\GitInfo;
use Misuzu\Users\User;
use Misuzu\Users\UserNotFoundException;
use Misuzu\Users\Assets\StaticUserImageAsset;
use Misuzu\Users\Assets\UserImageAssetInterface;
use Misuzu\Users\Assets\UserAssetScalableInterface;
final class AssetsHandler extends Handler {
private const TYPES = [
@ -44,7 +49,7 @@ final class AssetsHandler extends Handler {
return $str;
}
public static function view(HttpResponse $response, HttpRequest $request, string $name, string $type) {
public function serveComponent(HttpResponse $response, HttpRequest $request, string $name, string $type) {
$entityTag = sprintf('W/"%s.%s/%s"', $name, $type, GitInfo::hash());
if(!MSZ_DEBUG && $name === 'debug')
@ -64,4 +69,78 @@ final class AssetsHandler extends Handler {
}
}
}
private function canViewAsset(HttpRequest $request, User $assetUser): bool {
return !$assetUser->isBanned() || (
User::hasCurrent()
&& parse_url($request->getHeaderLine('Referer'), PHP_URL_PATH) === url('user-profile')
&& perms_check_user(MSZ_PERMS_USER, User::getCurrent()->getId(), MSZ_PERM_USER_MANAGE_USERS)
);
}
private function serveUserAsset(HttpResponse $response, HttpRequest $request, UserImageAssetInterface $assetInfo): void {
$contentType = $assetInfo->getMimeType();
$publicPath = $assetInfo->getPublicPath();
$fileName = $assetInfo->getFileName();
if($assetInfo instanceof UserAssetScalableInterface) {
$dimensions = (int)($request->getQueryParam('res', FILTER_SANITIZE_NUMBER_INT) ?? $request->getQueryParam('r', FILTER_SANITIZE_NUMBER_INT));
if($dimensions > 0) {
$assetInfo->ensureScaledExists($dimensions);
$contentType = $assetInfo->getScaledMimeType($dimensions);
$publicPath = $assetInfo->getPublicScaledPath($dimensions);
$fileName = $assetInfo->getScaledFileName($dimensions);
}
}
$response->setHeader('X-Accel-Redirect', $publicPath);
$response->setHeader('Content-Type', $contentType);
$response->setHeader('Content-Disposition', sprintf('inline; filename="%s"', $fileName));
}
public function serveAvatar(HttpResponse $response, HttpRequest $request, int $userId) {
$assetInfo = new StaticUserImageAsset(MSZ_PUBLIC . '/images/no-avatar.png', MSZ_PUBLIC);
try {
$userInfo = User::byId($userId);
if(!$this->canViewAsset($request, $userInfo)) {
$assetInfo = new StaticUserImageAsset(MSZ_PUBLIC . '/images/banned-avatar.png', MSZ_PUBLIC);
} elseif($userInfo->hasAvatar()) {
$assetInfo = $userInfo->getAvatarInfo();
}
} catch(UserNotFoundException $ex) {}
$this->serveUserAsset($response, $request, $assetInfo);
}
public function serveProfileBackground(HttpResponse $response, HttpRequest $request, int $userId) {
try {
$userInfo = User::byId($userId);
} catch(UserNotFoundException $ex) {}
if(empty($userInfo) || !$userInfo->hasBackground() || !$this->canViewAsset($request, $userInfo)) {
$response->setText('');
return 404;
}
$this->serveUserAsset($response, $request, $userInfo->getBackgroundInfo());
}
public function serveLegacy(HttpResponse $response, HttpRequest $request) {
$assetUserId = (int)$request->getQueryParam('u', FILTER_SANITIZE_NUMBER_INT);
switch($request->getQueryParam('m')) {
case 'avatar':
$this->serveAvatar($response, $request, $assetUserId);
return;
case 'background':
$this->serveProfileBackground($response, $request, $assetUserId);
return;
}
$response->setText('');
return 404;
}
}

View file

@ -9,4 +9,6 @@ interface UserAssetScalableInterface {
public function ensureScaledExists(int $dims): void;
public function getPublicScaledPath(int $dims): string;
public function getScaledFileName(int $dims): string;
public function getScaledFileExtension(int $dims): string;
public function getScaledMimeType(int $dims): string;
}

View file

@ -47,7 +47,19 @@ class UserAvatarAsset extends UserImageAsset implements UserAssetScalableInterfa
return sprintf('avatar-%1$d.%2$s', $this->getUser()->getId(), $this->getFileExtension());
}
public function getScaledFileName(int $dims): string {
return sprintf('avatar-%1$d-%3$dx%3$d.%2$s', $this->getUser()->getId(), $this->getFileExtension(), self::clampDimensions($dims));
return sprintf('avatar-%1$d-%3$dx%3$d.%2$s', $this->getUser()->getId(), $this->getScaledFileExtension($dims), self::clampDimensions($dims));
}
public function getScaledMimeType(int $dims): string {
if(!$this->isScaledPresent($dims))
return '';
return mime_content_type($this->getScaledPath($dims));
}
public function getScaledFileExtension(int $dims): string {
$imageSize = getimagesize($this->getScaledPath($dims));
if($imageSize === null)
return 'img';
return self::TYPES_EXT[$imageSize[2]] ?? 'img';
}
public function getRelativePath(): string {

View file

@ -74,8 +74,8 @@ define('MSZ_URLS', [
'user-profile-edit' => ['/profile.php', ['u' => '<user>', 'edit' => '1']],
'user-account-standing' => ['/profile.php', ['u' => '<user>'], 'account-standing'],
'user-avatar' => ['/user-assets.php', ['u' => '<user>', 'm' => 'avatar', 'r' => '<res>']],
'user-background' => ['/user-assets.php', ['u' => '<user>', 'm' => 'background']],
'user-avatar' => ['/assets/avatar/<user>', ['res' => '<res>']],
'user-background' => ['/assets/profile-background/<user>'],
'user-relation-create' => ['/relations.php', ['u' => '<user>', 'm' => '<type>', 'csrf' => '{csrf}']],
'user-relation-none' => ['/relations.php', ['u' => '<user>', 'm' => '[\Misuzu\Users\UserRelation::TYPE_NONE]', 'csrf' => '{csrf}']],