Skip to content

Commit

Permalink
Use separate helper to handle user avatar
Browse files Browse the repository at this point in the history
  • Loading branch information
nanaya committed Nov 14, 2023
1 parent 55e68d0 commit e2f9e83
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 44 deletions.
3 changes: 2 additions & 1 deletion app/Http/Controllers/AccountController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

use App\Exceptions\ImageProcessorException;
use App\Exceptions\ModelNotSavedException;
use App\Libraries\User\AvatarHelper;
use App\Libraries\User\CountryChange;
use App\Libraries\User\CountryChangeTarget;
use App\Libraries\UserVerification;
Expand Down Expand Up @@ -73,7 +74,7 @@ public function avatar()
$user = auth()->user();

try {
$user->setAvatar(Request::file('avatar_file'));
AvatarHelper::set($user, Request::file('avatar_file'));
} catch (ImageProcessorException $e) {
return error_popup($e->getMessage());
}
Expand Down
94 changes: 53 additions & 41 deletions app/Libraries/User/AvatarHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,65 +3,77 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

namespace App\Models\Traits;
declare(strict_types=1);

namespace App\Libraries\User;

use App\Libraries\ImageProcessor;
use App\Libraries\StorageUrl;
use ErrorException;
use App\Models\User;

trait UserAvatar
class AvatarHelper
{
private static function avatarDisk(): string
public static function set(User $user, ?\SplFileInfo $src): bool
{
return \Config::get('osu.avatar.storage');
}
$id = $user->getKey();
$storage = \Storage::disk(static::disk());

public function setAvatar($file)
{
$storage = \Storage::disk(static::avatarDisk());
if ($file === null) {
$storage->delete($this->user_id);
if ($src === null) {
$storage->delete($id);
} else {
$filePath = $file->getRealPath();
$processor = new ImageProcessor($filePath, [256, 256], 100000);
$srcPath = $src->getRealPath();
$processor = new ImageProcessor($srcPath, [256, 256], 100000);
$processor->process();

$storage->put($this->user_id, file_get_contents($filePath), 'public');

$entry = $this->user_id.'_'.time().'.'.$processor->ext();
$storage->putFileAs('/', $src, $id, 'public');
$entry = $id.'_'.time().'.'.$processor->ext();
}

if (present(\Config::get('osu.avatar.cache_purge_prefix'))) {
try {
$ctx = [
'http' => [
'method' => \Config::get('osu.avatar.cache_purge_method') ?? 'GET',
'header' => present(\Config::get('osu.avatar.cache_purge_authorization_key'))
? 'Authorization: '.\Config::get('osu.avatar.cache_purge_authorization_key')
: null,
],
];
$prefix = \Config::get('osu.avatar.cache_purge_prefix');
$suffix = $ctx['http']['method'] === 'GET' ? '?'.time() : ''; // Bypass CloudFlare cache if using GET
$url = $prefix.$this->user_id.$suffix;
file_get_contents($url, false, stream_context_create($ctx));
} catch (ErrorException $e) {
// ignores 404 errors, throws everything else
if (!ends_with($e->getMessage(), "404 Not Found\r\n")) {
throw $e;
}
}
}
static::purgeCache($id);

return $this->update(['user_avatar' => $entry ?? '']);
return $user->update(['user_avatar' => $entry ?? '']);
}

protected function getUserAvatar()
public static function url(User $user): string
{
$value = $this->getRawAttribute('user_avatar');
$value = $user->getRawAttribute('user_avatar');

return present($value)
? StorageUrl::make(static::avatarDisk(), strtr($value, '_', '?'))
? StorageUrl::make(static::disk(), strtr($value, '_', '?'))
: \Config::get('osu.avatar.default');
}

private static function disk(): string
{
return \Config::get('osu.avatar.storage');
}

private static function purgeCache(int $id): void
{
$prefix = presence(\Config::get('osu.avatar.cache_purge_prefix'));

if ($prefix === null) {
return;
}

$method = \Config::get('osu.avatar.cache_purge_method') ?? 'GET';
$auth = \Config::get('osu.avatar.cache_purge_authorization_key');
$ctx = [
'http' => [
'method' => $method,
'header' => present($auth) ? "Authorization: {$auth}" : null,
],
];
$suffix = $method === 'GET' ? '?'.time() : ''; // Bypass CloudFlare cache if using GET
$url = "{$prefix}{$id}{$suffix}";

try {
file_get_contents($url, false, stream_context_create($ctx));
} catch (\ErrorException $e) {
// ignores 404 errors, throws everything else
if (!ends_with($e->getMessage(), "404 Not Found\r\n")) {
throw $e;
}
}
}
}
5 changes: 3 additions & 2 deletions app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use App\Libraries\Elasticsearch\Indexable;
use App\Libraries\Session\Store as SessionStore;
use App\Libraries\Transactions\AfterCommit;
use App\Libraries\User\AvatarHelper;
use App\Libraries\User\DatadogLoginAttempt;
use App\Libraries\User\ProfileBeatmapset;
use App\Libraries\User\UsernamesForDbLookup;
Expand Down Expand Up @@ -212,7 +213,7 @@
*/
class User extends Model implements AfterCommit, AuthenticatableContract, HasLocalePreference, Indexable, Traits\ReportableInterface
{
use Authenticatable, HasApiTokens, Memoizes, Traits\Es\UserSearch, Traits\Reportable, Traits\UserAvatar, Traits\UserScoreable, Traits\UserStore, Validatable;
use Authenticatable, HasApiTokens, Memoizes, Traits\Es\UserSearch, Traits\Reportable, Traits\UserScoreable, Traits\UserStore, Validatable;

const PLAYSTYLES = [
'mouse' => 1,
Expand Down Expand Up @@ -837,7 +838,7 @@ public function getAttribute($key)
'displayed_last_visit' => $this->getDisplayedLastVisit(),
'osu_playstyle' => $this->getOsuPlaystyle(),
'playmode' => $this->getPlaymode(),
'user_avatar' => $this->getUserAvatar(),
'user_avatar' => AvatarHelper::url($this),
'user_colour' => $this->getUserColour(),
'user_rank' => $this->getUserRank(),
'user_website' => $this->getUserWebsite(),
Expand Down

0 comments on commit e2f9e83

Please sign in to comment.