From 6909be331827b32b73905df0a548b4868ab32909 Mon Sep 17 00:00:00 2001 From: Bubka <858858+Bubka@users.noreply.github.com> Date: Fri, 13 Jun 2025 16:46:28 +0200 Subject: [PATCH] Add variant support to the logo fetching feature --- app/Api/v1/Controllers/IconController.php | 6 +- app/Api/v1/Requests/IconFetchRequest.php | 25 +++- app/Services/LogoLib/AbstractLogoLib.php | 57 ++++++++- app/Services/LogoLib/LogoLibInterface.php | 2 +- app/Services/LogoLib/TfaLogoLib.php | 14 +- config/2fauth.php | 1 + .../js/components/formElements/FormSelect.vue | 6 +- resources/js/services/twofaccountService.js | 4 +- resources/js/views/settings/Options.vue | 44 ++++++- .../js/views/twofaccounts/CreateUpdate.vue | 120 +++++++++++------- resources/lang/en/commons.php | 3 +- resources/lang/en/errors.php | 2 +- resources/lang/en/settings.php | 8 +- resources/lang/en/twofaccounts.php | 2 +- 14 files changed, 218 insertions(+), 76 deletions(-) diff --git a/app/Api/v1/Controllers/IconController.php b/app/Api/v1/Controllers/IconController.php index e0a7d8f3..9cd884ae 100644 --- a/app/Api/v1/Controllers/IconController.php +++ b/app/Api/v1/Controllers/IconController.php @@ -57,7 +57,11 @@ class IconController extends Controller ? $validated['iconCollection'] : $request->user()->preferences['iconCollection']; - $icon = LogoLib::driver($iconCollection)->getIcon($validated['service']); + $variant = Arr::has($validated, 'variant') && $validated['variant'] + ? $validated['variant'] + : 'regular'; + + $icon = LogoLib::driver($iconCollection)->getIcon($validated['service'], $variant); return $icon ? response()->json(['filename' => $icon], 201) diff --git a/app/Api/v1/Requests/IconFetchRequest.php b/app/Api/v1/Requests/IconFetchRequest.php index b6372a1c..cfba5a22 100644 --- a/app/Api/v1/Requests/IconFetchRequest.php +++ b/app/Api/v1/Requests/IconFetchRequest.php @@ -24,10 +24,29 @@ class IconFetchRequest extends FormRequest */ public function rules() { - return [ + $rules = [ 'service' => 'string', - 'iconCollection' => 'nullable|string|in:tfa,selfh,dashboardicons', + 'iconCollection' => 'sometimes|required|string|in:tfa,selfh,dashboardicons', + 'variant' => [ + 'sometimes', + 'required', + 'string', + ] ]; + + if ($this->input('iconCollection', null) === 'selfh') { + $rules['variant'][] = 'in:regular,light,dark'; + } + + if ($this->input('iconCollection', null) === 'dashboardicons') { + $rules['variant'][] = 'in:regular,light,dark'; + } + + if ($this->input('iconCollection', null) === 'tfa') { + $rules['variant'][] = 'in:regular'; + } + + return $rules; } /** @@ -40,7 +59,7 @@ class IconFetchRequest extends FormRequest protected function prepareForValidation() { $this->merge([ - 'service' => strip_tags(strval($this->service)), + 'service' => strip_tags(strval($this->input('service'))), ]); } } diff --git a/app/Services/LogoLib/AbstractLogoLib.php b/app/Services/LogoLib/AbstractLogoLib.php index 288ad182..246d45d9 100644 --- a/app/Services/LogoLib/AbstractLogoLib.php +++ b/app/Services/LogoLib/AbstractLogoLib.php @@ -4,6 +4,7 @@ namespace App\Services\LogoLib; use App\Facades\IconStore; use App\Services\LogoLib\LogoLibInterface; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; @@ -25,14 +26,21 @@ abstract class AbstractLogoLib implements LogoLibInterface */ protected string $format = 'svg'; + /** + * Suffix to append to the queried resource to get a specific variant + */ + protected string $variant = ''; + /** * Fetch a logo for the given service and save it as an icon * * @param string|null $serviceName Name of the service to fetch a logo for + * @param string|null $variant The theme variant to fetch (light, dark, etc...) * @return string|null The icon filename or null if no logo has been found */ - public function getIcon(?string $serviceName) : string|null + public function getIcon(?string $serviceName, string $variant = null) : string|null { + $this->setVariant($variant); $logoFilename = $this->getLogo(strval($serviceName)); if (!$logoFilename) { @@ -50,6 +58,20 @@ abstract class AbstractLogoLib implements LogoLibInterface } } + /** + * Sets the variant using passed parameter or default + */ + protected function setVariant(?string $variant) : void + { + if (! $variant || ! in_array($variant, ['regular', 'dark', 'light'])) { + $this->variant = Auth::user() + ? Auth::user()->preferences['iconVariant'] + : 'regular'; + } else { + $this->variant = $variant; + } + } + /** * Return the logo's filename for a given service * @@ -59,7 +81,7 @@ abstract class AbstractLogoLib implements LogoLibInterface protected function getLogo(string $serviceName) { $referenceName = $this->sanitizeServiceName(strval($serviceName)); - $logoFilename = $referenceName . '.' . $this->format; + $logoFilename = $referenceName . $this->suffix() . '.' . $this->format; $cachedFilename = $this->cachePrefix . $logoFilename; if ($referenceName && ! Storage::disk('logos')->exists($cachedFilename)) { @@ -69,6 +91,28 @@ abstract class AbstractLogoLib implements LogoLibInterface return Storage::disk('logos')->exists($cachedFilename) ? $cachedFilename : null; } + /** + * Suffix to append to the reference name to get a specific variant + */ + protected function suffix() : string + { + switch ($this->variant) { + case 'light': + $suffix = '-light'; + break; + + case 'dark': + $suffix = '-dark'; + break; + + default: + $suffix = ''; + break; + } + + return $suffix; + } + /** * Url to use in http request to get a specific logo from the logo lib */ @@ -92,10 +136,13 @@ abstract class AbstractLogoLib implements LogoLibInterface */ protected function fetchLogo(string $logoFilename) : void { + $url = $this->logoUrl($logoFilename); + try { - $response = Http::withOptions([ - 'proxy' => config('2fauth.config.outgoingProxy'), - ])->retry(3, 100)->get($this->logoUrl($logoFilename)); + // $response = Http::withOptions([ + // 'proxy' => config('2fauth.config.outgoingProxy'), + // ])->retry(3, 100)->get($url); + $response = Http::get($url); if ($response->successful()) { $filename = $this->cachePrefix . $logoFilename; diff --git a/app/Services/LogoLib/LogoLibInterface.php b/app/Services/LogoLib/LogoLibInterface.php index aa694278..94f84fa2 100644 --- a/app/Services/LogoLib/LogoLibInterface.php +++ b/app/Services/LogoLib/LogoLibInterface.php @@ -4,5 +4,5 @@ namespace App\Services\LogoLib; interface LogoLibInterface { - public function getIcon(?string $serviceName): string|null; + public function getIcon(?string $serviceName, string $variant = null): string|null; } \ No newline at end of file diff --git a/app/Services/LogoLib/TfaLogoLib.php b/app/Services/LogoLib/TfaLogoLib.php index f984da46..29ee85f1 100644 --- a/app/Services/LogoLib/TfaLogoLib.php +++ b/app/Services/LogoLib/TfaLogoLib.php @@ -24,7 +24,7 @@ class TfaLogoLib extends AbstractLogoLib implements LogoLibInterface /** * @var string */ - const TFA_URL = 'https://2fa.directory/api/v3/tfa.json'; + const TFA_JSON_URL = 'https://2fa.directory/api/v3/tfa.json'; /** * @var string @@ -45,7 +45,7 @@ class TfaLogoLib extends AbstractLogoLib implements LogoLibInterface * @param string|null $serviceName Name of the service to fetch a logo for * @return string|null The icon filename or null if no logo has been found */ - public function getIcon(?string $serviceName) : string|null + public function getIcon(?string $serviceName, string $variant = null) : string|null { $logoFilename = $this->getLogo(strval($serviceName)); @@ -58,6 +58,14 @@ class TfaLogoLib extends AbstractLogoLib implements LogoLibInterface } } + /** + * Suffix to append to the reference name to get a specific variant + */ + protected function suffix() : string + { + return ''; + } + /** * Return the logo's filename for a given service * @@ -104,7 +112,7 @@ class TfaLogoLib extends AbstractLogoLib implements LogoLibInterface try { $response = Http::withOptions([ 'proxy' => config('2fauth.config.outgoingProxy'), - ])->retry(3, 100)->get(self::TFA_URL); + ])->retry(3, 100)->get(self::TFA_JSON_URL); $coll = collect(json_decode(htmlspecialchars_decode($response->body()), true)) /* @phpstan-ignore-line */ ->mapWithKeys(function ($item, $key) { diff --git a/config/2fauth.php b/config/2fauth.php index c4e9fa33..b59eb42e 100644 --- a/config/2fauth.php +++ b/config/2fauth.php @@ -14,6 +14,7 @@ $preferences = [ 'displayMode' => envUnlessEmpty('USERPREF_DEFAULT__DISPLAY_MODE', 'list'), 'showAccountsIcons' => envUnlessEmpty('USERPREF_DEFAULT__SHOW_ACCOUNTS_ICONS', true), 'iconCollection' => envUnlessEmpty('USERPREF_DEFAULT__ICON_COLLECTION', 'selfh'), + 'iconVariant' => envUnlessEmpty('USERPREF_DEFAULT__ICON_VARIANT', 'regular'), 'kickUserAfter' => envUnlessEmpty('USERPREF_DEFAULT__KICK_USER_AFTER', 15), 'activeGroup' => 0, 'rememberActiveGroup' => envUnlessEmpty('USERPREF_DEFAULT__REMEMBER_ACTIVE_GROUP', true), diff --git a/resources/js/components/formElements/FormSelect.vue b/resources/js/components/formElements/FormSelect.vue index a9a70d81..e3452859 100644 --- a/resources/js/components/formElements/FormSelect.vue +++ b/resources/js/components/formElements/FormSelect.vue @@ -1,8 +1,8 @@