Skip to content

Commit

Permalink
Add support for Feature flags. (#2282)
Browse files Browse the repository at this point in the history
  • Loading branch information
ildyria committed Mar 1, 2024
1 parent 57e3503 commit c937eab
Show file tree
Hide file tree
Showing 14 changed files with 216 additions and 42 deletions.
5 changes: 3 additions & 2 deletions app/Actions/Diagnostics/Pipes/Infos/InstallTypeInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Actions\Diagnostics\Pipes\Infos;

use App\Actions\Diagnostics\Diagnostics;
use App\Assets\Features;
use App\Contracts\DiagnosticPipe;
use App\Metadata\Versions\InstalledVersion;

Expand All @@ -29,8 +30,8 @@ public function handle(array &$data, \Closure $next): array
$data[] = Diagnostics::line('APP_DEBUG:', config('app.debug') === true ? 'true' : 'false'); // check if debug is on (will help in case of error 500)
$data[] = Diagnostics::line('APP_URL:', config('app.url') !== 'http://localhost' ? 'set' : 'default'); // Some people leave that value by default... It is now breaking their visual.
$data[] = Diagnostics::line('APP_DIR:', config('app.dir_url') !== '' ? 'set' : 'default'); // Some people leave that value by default... It is now breaking their visual.
$data[] = Diagnostics::line('LOG_VIEWER_ENABLED:', config('log-viewer.enabled', true) === true ? 'true' : 'false');
$data[] = Diagnostics::line('LIVEWIRE_ENABLED:', config('app.livewire', true) === true ? 'true' : 'false');
$data[] = Diagnostics::line('LOG_VIEWER_ENABLED:', Features::when('log-viewer', 'true', 'false'));
$data[] = Diagnostics::line('LIVEWIRE_ENABLED:', Features::when('livewire', 'true', 'false'));
$data[] = '';

return $next($data);
Expand Down
140 changes: 140 additions & 0 deletions app/Assets/Features.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<?php

namespace App\Assets;

use App\Exceptions\Internal\FeaturesDoesNotExistsException;

/**
* Manage enabled and disabled features.
*/
final class Features
{
/**
* Determine whether a feature is active.
*
* @param string $featureName to check
*
* @return bool is active
*/
public static function active(string $featureName): bool
{
self::exists($featureName);

return config('features.' . $featureName) === true;
}

/**
* Determine whether a feature is inactive.
*
* @param string $featureName to check
*
* @return bool is inactive
*/
public static function inactive(string $featureName): bool
{
self::exists($featureName);

return config('features.' . $featureName) === false;
}

/**
* Determine if all of the given features are active.
*
* @param array<int,string> $featureNames to check
*
* @return bool is inactive
*/
public static function allAreActive(array $featureNames): bool
{
return array_reduce(
$featureNames,
fn ($bool, $featureName) => $bool && self::active($featureName),
true);
}

/**
* Determine if any of the given features are active.
*
* @param array<int,string> $featureNames to check
*
* @return bool is inactive
*/
public static function someAreActive(array $featureNames): bool
{
return array_reduce(
$featureNames,
fn (bool $bool, string $featureName) => $bool || self::active($featureName),
false);
}

/**
* Determine if all of the given features are inactive.
*
* @param array<int,string> $featureNames to check
*
* @return bool is inactive
*/
public static function allAreInactive(array $featureNames): bool
{
return array_reduce(
$featureNames,
fn (bool $bool, string $featureName) => $bool && self::inactive($featureName),
true);
}

/**
* Determine if any of the given features are inactive.
*
* @param array<int,string> $featureNames to check
*
* @return bool is inactive
*/
public static function someAreInactive(array $featureNames): bool
{
return array_reduce(
$featureNames,
fn (bool $bool, string $featureName) => $bool || self::inactive($featureName),
false);
}

/**
* Determine whether a feature is active.
*
* @template T
*
* @param string|array<int,string> $featureNames to check
* @param T|\Closure(): T $valIfTrue what happens or Value if we features are enabled
* @param T|\Closure(): T $valIfFalse what happens or Value if we features are disabled
*
* @return T
*/
public static function when(string|array $featureNames, mixed $valIfTrue, mixed $valIfFalse): mixed
{
// Sadly phpstan does not do the type inference as it would do in a if statement.
$retValue = match (is_array($featureNames)) {
true => self::allAreActive($featureNames) ? $valIfTrue : $valIfFalse, // @phpstan-ignore-line
// Parameter #1 $featureNames of static method App\Assets\Features::allAreActive() expects array<int, string>, array<int, string>|string given.
false => self::active($featureNames) ? $valIfTrue : $valIfFalse, // @phpstan-ignore-line
// Parameter #1 $featureName of static method App\Assets\Features::active() expects string, array<int, string>|string given.
};

return is_callable($retValue) ? $retValue() : $retValue;
}

/**
* Assert whether the feature exists or not.
* Throws an exception if not.
*
* @param string $featureName name of the feature to check
*
* @return void
*
* @throws FeaturesDoesNotExistsException
*/
private static function exists(string $featureName): void
{
if (!is_bool(config('features.' . $featureName))) {
throw new FeaturesDoesNotExistsException(sprintf('No feature with name %s found.', $featureName));
}
}
}
11 changes: 11 additions & 0 deletions app/Exceptions/Internal/FeaturesDoesNotExistsException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace App\Exceptions\Internal;

class FeaturesDoesNotExistsException extends LycheeLogicException
{
public function __construct(string $msg)
{
parent::__construct($msg);
}
}
5 changes: 3 additions & 2 deletions app/Http/Controllers/RedirectController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Http\Controllers;

use App\Actions\Album\Unlock;
use App\Assets\Features;
use App\Contracts\Exceptions\LycheeException;
use App\Exceptions\Internal\FrameworkException;
use App\Factories\AlbumFactory;
Expand Down Expand Up @@ -75,7 +76,7 @@ public function photo(Request $request, string $albumID, ?string $photoID): Symf
}

// If we are using livewire by default, we redirect to Livewire url intead.
if (config('app.livewire') === true) {
if (Features::active('livewire')) {
return $photoID === null ?
redirect(route('livewire-gallery-album', ['albumId' => $albumID])) :
redirect(route('livewire-gallery-photo', ['albumId' => $albumID, 'photoId' => $photoID]));
Expand All @@ -98,7 +99,7 @@ public function photo(Request $request, string $albumID, ?string $photoID): Symf
public function view(): View|SymfonyResponse
{
$base_route = Configs::getValueAsBool('landing_page_enable') ? route('landing') : route('livewire-gallery');
if (config('app.legacy_v4_redirect') === false) {
if (Features::active('legacy_v4_redirect') === false) {
return redirect($base_route);
}

Expand Down
3 changes: 2 additions & 1 deletion app/Http/Middleware/DisableCSP.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Http\Middleware;

use App\Assets\Features;
use App\Contracts\Exceptions\LycheeException;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Http\Request;
Expand Down Expand Up @@ -49,7 +50,7 @@ public function handle(Request $request, \Closure $next): mixed
}

// disable unsafe-eval if we are on a Livewire page
if (config('app.livewire', false) === true || Str::startsWith($request->getRequestUri(), $dir_url . '/livewire/')) {
if (Features::active('livewire') || Str::startsWith($request->getRequestUri(), $dir_url . '/livewire/')) {
$this->handleLivewire();
}

Expand Down
2 changes: 0 additions & 2 deletions app/Livewire/Components/Menus/LeftMenu.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ class LeftMenu extends Component implements Openable
use InteractWithModal;
use UseOpenable;

#[Locked] public bool $has_log_viewer = false;
#[Locked] public bool $has_dev_tools = false;
#[Locked] public ?string $clockwork_url = null;
#[Locked] public ?string $doc_api_url = null;
Expand All @@ -62,7 +61,6 @@ public function logout(): void
public function render(): View
{
$this->loadDevMenu();
$this->has_log_viewer = config('log-viewer.enabled', true);

return view('livewire.components.left-menu');
}
Expand Down
7 changes: 4 additions & 3 deletions app/Models/Extensions/Thumb.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Models\Extensions;

use App\Assets\Features;
use App\DTO\AbstractDTO;
use App\DTO\SortingCriterion;
use App\Enum\ColumnSortingPhotoType;
Expand Down Expand Up @@ -39,7 +40,7 @@ protected function __construct(string $id, string $type, string $thumbUrl, ?stri
public static function sizeVariantsFilter(HasMany $relation): HasMany
{
$svAlbumThumbs = [SizeVariantType::THUMB, SizeVariantType::THUMB2X];
if (config('app.livewire', true) === true) {
if (Features::active('livewire')) {
$svAlbumThumbs = [SizeVariantType::SMALL, SizeVariantType::SMALL2X, SizeVariantType::THUMB, SizeVariantType::THUMB2X];
}

Expand Down Expand Up @@ -88,14 +89,14 @@ public static function createFromQueryable(Relation|Builder $photoQueryable, Sor
*/
public static function createFromPhoto(?Photo $photo): ?Thumb
{
$thumb = (config('app.livewire', true) === true && $photo?->size_variants->getSmall() !== null)
$thumb = (Features::active('livewire') && $photo?->size_variants->getSmall() !== null)
? $photo->size_variants->getSmall()
: $photo?->size_variants->getThumb();
if ($thumb === null) {
return null;
}

$thumb2x = (config('app.livewire', true) === true && $photo?->size_variants->getSmall() !== null)
$thumb2x = (Features::active('livewire') && $photo?->size_variants->getSmall() !== null)
? $photo->size_variants->getSmall2x()
: $photo?->size_variants->getThumb2x();

Expand Down
2 changes: 1 addition & 1 deletion app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public function boot()
/**
* We force URL to HTTPS if requested in .env via APP_FORCE_HTTPS.
*/
if (config('app.force_https') === true) {
if (config('features.force_https') === true) {
URL::forceScheme('https');
}

Expand Down
27 changes: 1 addition & 26 deletions config/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,6 @@ function renv(string $cst, ?string $default = null): string

'env' => env('APP_ENV', 'production'),

/*
|--------------------------------------------------------------------------
| Application Environment
|--------------------------------------------------------------------------
|
| This value determines whether livewire front-end is enabled as it is
| currently under development.
|
*/

'livewire' => (bool) env('LIVEWIRE_ENABLED', true),

/*
|--------------------------------------------------------------------------
| Application Debug Mode
Expand Down Expand Up @@ -92,20 +80,6 @@ function renv(string $cst, ?string $default = null): string

'asset_url' => null,

'legacy_v4_redirect' => env('LEGACY_V4_REDIRECT', false),

/*
|--------------------------------------------------------------------------
| Application URL
|--------------------------------------------------------------------------
|
| When running behind a proxy, it may be necessary for the urls to be
| set as https for the reverse translation. You should set this if you
| want to force the https scheme.
*/

'force_https' => (bool) env('APP_FORCE_HTTPS', false),

/*
|--------------------------------------------------------------------------
| Application Timezone
Expand Down Expand Up @@ -280,6 +254,7 @@ function renv(string $cst, ?string $default = null): string
'aliases' => Facade::defaultAliases()->merge([
'DebugBar' => Barryvdh\Debugbar\Facades\Debugbar::class,
'Helpers' => App\Facades\Helpers::class,
'Features' => App\Assets\Features::class,
// Aliases for easier access in the blade templates
'Configs' => App\Models\Configs::class,
'AlbumPolicy' => App\Policies\AlbumPolicy::class,
Expand Down
44 changes: 44 additions & 0 deletions config/features.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

return [
/*
|--------------------------------------------------------------------------
| Use Livewire Front-end
|--------------------------------------------------------------------------
|
| This value determines whether livewire front-end is enabled as it is
| currently under development.
|
*/
'livewire' => (bool) env('LIVEWIRE_ENABLED', true),

/*
|--------------------------------------------------------------------------
| Force HTTPS
|--------------------------------------------------------------------------
|
| When running behind a proxy, it may be necessary for the urls to be
| set as https for the reverse translation. You should set this if you
| want to force the https scheme.
*/
'force_https' => (bool) env('APP_FORCE_HTTPS', false),

/*
|--------------------------------------------------------------------------
| Enable v4 redirections
|--------------------------------------------------------------------------
|
| When using new front-end old links to /#albumID/PhotoID are broken.
| This provides here a way to avoid those.
*/
'legacy_v4_redirect' => (bool) env('LEGACY_V4_REDIRECT', false),

/*
|--------------------------------------------------------------------------
| Log Viewer
|--------------------------------------------------------------------------
| Log Viewer can be disabled, so it's no longer accessible via browser.
|
*/
'log-viewer' => (bool) env('LOG_VIEWER_ENABLED', true),
];
2 changes: 1 addition & 1 deletion resources/views/install/setup-success.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
@section('content')
<strong>Admin account has been created.</strong>
<div class="buttons">
<a href="{{ config('app.livewire') !== true ? route('home') : route('livewire-index') }}" class="button" >
<a href="{{ Features::when('livewire', fn () => route('livewire-index'), fn () => route('home')) }}" class="button" >
Open Lychee <i class="fa fa-angle-right fa-fw" aria-hidden="true"></i>
</a>
</div>
Expand Down
2 changes: 1 addition & 1 deletion resources/views/livewire/components/left-menu.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class="z-40 w-0 h-screen transition-width duration-500 select-none" aria-label="
{{ __('lychee.SHARING') }}</x-leftbar.leftbar-item>
@endcan
@can(SettingsPolicy::CAN_SEE_LOGS, [App\Models\Configs::class])
@if($has_log_viewer)
@if(Features::active('log-viewer'))
<x-leftbar.leftbar-item href="{{ route('log-viewer.index') }}" icon="excerpt">
{{ __('lychee.LOGS') }}
</x-leftbar.leftbar-item>
Expand Down
Loading

0 comments on commit c937eab

Please sign in to comment.