Update oauth setting, use config_cache

This commit is contained in:
Daniel Supernault 2024-03-12 03:55:51 -06:00
parent 5071aaf408
commit ce228f7fa4
No known key found for this signature in database
GPG key ID: 23740873EE6F76A1
7 changed files with 101 additions and 103 deletions

View file

@ -2,30 +2,20 @@
namespace App\Http\Controllers\Admin; namespace App\Http\Controllers\Admin;
use DB, Cache; use App\Http\Controllers\PixelfedDirectoryController;
use App\{
DiscoverCategory,
DiscoverCategoryHashtag,
Hashtag,
Media,
Profile,
Status,
StatusHashtag,
User
};
use App\Models\ConfigCache; use App\Models\ConfigCache;
use App\Services\AccountService; use App\Services\AccountService;
use App\Services\ConfigCacheService; use App\Services\ConfigCacheService;
use App\Services\StatusService; use App\Services\StatusService;
use Carbon\Carbon; use App\Status;
use App\User;
use Cache;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Validation\Rule; use Illuminate\Support\Facades\Http;
use League\ISO3166\ISO3166;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Str;
use App\Http\Controllers\PixelfedDirectoryController; use League\ISO3166\ISO3166;
trait AdminDirectoryController trait AdminDirectoryController
{ {
@ -41,34 +31,34 @@ trait AdminDirectoryController
$res['countries'] = collect((new ISO3166)->all())->pluck('name'); $res['countries'] = collect((new ISO3166)->all())->pluck('name');
$res['admins'] = User::whereIsAdmin(true) $res['admins'] = User::whereIsAdmin(true)
->where('2fa_enabled', true) ->where('2fa_enabled', true)
->get()->map(function($user) { ->get()->map(function ($user) {
return [ return [
'uid' => (string) $user->id, 'uid' => (string) $user->id,
'pid' => (string) $user->profile_id, 'pid' => (string) $user->profile_id,
'username' => $user->username, 'username' => $user->username,
'created_at' => $user->created_at 'created_at' => $user->created_at,
]; ];
}); });
$config = ConfigCache::whereK('pixelfed.directory')->first(); $config = ConfigCache::whereK('pixelfed.directory')->first();
if($config) { if ($config) {
$data = $config->v ? json_decode($config->v, true) : []; $data = $config->v ? json_decode($config->v, true) : [];
$res = array_merge($res, $data); $res = array_merge($res, $data);
} }
if(empty($res['summary'])) { if (empty($res['summary'])) {
$summary = ConfigCache::whereK('app.short_description')->pluck('v'); $summary = ConfigCache::whereK('app.short_description')->pluck('v');
$res['summary'] = $summary ? $summary[0] : null; $res['summary'] = $summary ? $summary[0] : null;
} }
if(isset($res['banner_image']) && !empty($res['banner_image'])) { if (isset($res['banner_image']) && ! empty($res['banner_image'])) {
$res['banner_image'] = url(Storage::url($res['banner_image'])); $res['banner_image'] = url(Storage::url($res['banner_image']));
} }
if(isset($res['favourite_posts'])) { if (isset($res['favourite_posts'])) {
$res['favourite_posts'] = collect($res['favourite_posts'])->map(function($id) { $res['favourite_posts'] = collect($res['favourite_posts'])->map(function ($id) {
return StatusService::get($id); return StatusService::get($id);
}) })
->filter(function($post) { ->filter(function ($post) {
return $post && isset($post['account']); return $post && isset($post['account']);
}) })
->values(); ->values();
@ -94,12 +84,12 @@ trait AdminDirectoryController
'account_deletion' => (bool) config_cache('pixelfed.account_deletion'), 'account_deletion' => (bool) config_cache('pixelfed.account_deletion'),
]; ];
if(config_cache('pixelfed.directory.testimonials')) { if (config_cache('pixelfed.directory.testimonials')) {
$testimonials = collect(json_decode(config_cache('pixelfed.directory.testimonials'),true)) $testimonials = collect(json_decode(config_cache('pixelfed.directory.testimonials'), true))
->map(function($t) { ->map(function ($t) {
return [ return [
'profile' => AccountService::get($t['profile_id']), 'profile' => AccountService::get($t['profile_id']),
'body' => $t['body'] 'body' => $t['body'],
]; ];
}); });
$res['testimonials'] = $testimonials; $res['testimonials'] = $testimonials;
@ -109,7 +99,7 @@ trait AdminDirectoryController
'media_types' => [ 'media_types' => [
'required', 'required',
function ($attribute, $value, $fail) { function ($attribute, $value, $fail) {
if (!in_array('image/jpeg', $value->toArray()) || !in_array('image/png', $value->toArray())) { if (! in_array('image/jpeg', $value->toArray()) || ! in_array('image/png', $value->toArray())) {
$fail('You must enable image/jpeg and image/png support.'); $fail('You must enable image/jpeg and image/png support.');
} }
}, },
@ -120,7 +110,7 @@ trait AdminDirectoryController
'max_account_size' => 'required_if:enforce_account_limit,true|integer|min:1000000', 'max_account_size' => 'required_if:enforce_account_limit,true|integer|min:1000000',
'max_album_length' => 'required|integer|min:4|max:20', 'max_album_length' => 'required|integer|min:4|max:20',
'account_deletion' => 'required|accepted', 'account_deletion' => 'required|accepted',
'max_caption_length' => 'required|integer|min:500|max:10000' 'max_caption_length' => 'required|integer|min:500|max:10000',
]); ]);
$res['requirements_validator'] = $validator->errors(); $res['requirements_validator'] = $validator->errors();
@ -146,11 +136,11 @@ trait AdminDirectoryController
foreach (new \DirectoryIterator($path) as $io) { foreach (new \DirectoryIterator($path) as $io) {
$name = $io->getFilename(); $name = $io->getFilename();
$skip = ['vendor']; $skip = ['vendor'];
if($io->isDot() || in_array($name, $skip)) { if ($io->isDot() || in_array($name, $skip)) {
continue; continue;
} }
if($io->isDir()) { if ($io->isDir()) {
$langs->push(['code' => $name, 'name' => locale_get_display_name($name)]); $langs->push(['code' => $name, 'name' => locale_get_display_name($name)]);
} }
} }
@ -160,24 +150,25 @@ trait AdminDirectoryController
$submissionState = Http::withoutVerifying() $submissionState = Http::withoutVerifying()
->post('https://pixelfed.org/api/v1/directory/check-submission', [ ->post('https://pixelfed.org/api/v1/directory/check-submission', [
'domain' => config('pixelfed.domain.app') 'domain' => config('pixelfed.domain.app'),
]); ]);
$res['submission_state'] = $submissionState->json(); $res['submission_state'] = $submissionState->json();
return $res; return $res;
} }
protected function validVal($res, $val, $count = false, $minLen = false) protected function validVal($res, $val, $count = false, $minLen = false)
{ {
if(!isset($res[$val])) { if (! isset($res[$val])) {
return false; return false;
} }
if($count) { if ($count) {
return count($res[$val]) >= $count; return count($res[$val]) >= $count;
} }
if($minLen) { if ($minLen) {
return strlen($res[$val]) >= $minLen; return strlen($res[$val]) >= $minLen;
} }
@ -194,11 +185,11 @@ trait AdminDirectoryController
'favourite_posts' => 'array|max:12', 'favourite_posts' => 'array|max:12',
'favourite_posts.*' => 'distinct', 'favourite_posts.*' => 'distinct',
'privacy_pledge' => 'sometimes', 'privacy_pledge' => 'sometimes',
'banner_image' => 'sometimes|mimes:jpg,png|dimensions:width=1920,height:1080|max:5000' 'banner_image' => 'sometimes|mimes:jpg,png|dimensions:width=1920,height:1080|max:5000',
]); ]);
$config = ConfigCache::firstOrNew([ $config = ConfigCache::firstOrNew([
'k' => 'pixelfed.directory' 'k' => 'pixelfed.directory',
]); ]);
$res = $config->v ? json_decode($config->v, true) : []; $res = $config->v ? json_decode($config->v, true) : [];
@ -208,24 +199,25 @@ trait AdminDirectoryController
$res['contact_email'] = $request->input('contact_email'); $res['contact_email'] = $request->input('contact_email');
$res['privacy_pledge'] = (bool) $request->input('privacy_pledge'); $res['privacy_pledge'] = (bool) $request->input('privacy_pledge');
if($request->filled('location')) { if ($request->filled('location')) {
$exists = (new ISO3166)->name($request->location); $exists = (new ISO3166)->name($request->location);
if($exists) { if ($exists) {
$res['location'] = $request->input('location'); $res['location'] = $request->input('location');
} }
} }
if($request->hasFile('banner_image')) { if ($request->hasFile('banner_image')) {
collect(Storage::files('public/headers')) collect(Storage::files('public/headers'))
->filter(function($name) { ->filter(function ($name) {
$protected = [ $protected = [
'public/headers/.gitignore', 'public/headers/.gitignore',
'public/headers/default.jpg', 'public/headers/default.jpg',
'public/headers/missing.png' 'public/headers/missing.png',
]; ];
return !in_array($name, $protected);
return ! in_array($name, $protected);
}) })
->each(function($name) { ->each(function ($name) {
Storage::delete($name); Storage::delete($name);
}); });
$path = $request->file('banner_image')->storePublicly('public/headers'); $path = $request->file('banner_image')->storePublicly('public/headers');
@ -240,9 +232,10 @@ trait AdminDirectoryController
ConfigCacheService::put('pixelfed.directory', $config->v); ConfigCacheService::put('pixelfed.directory', $config->v);
$updated = json_decode($config->v, true); $updated = json_decode($config->v, true);
if(isset($updated['banner_image'])) { if (isset($updated['banner_image'])) {
$updated['banner_image'] = url(Storage::url($updated['banner_image'])); $updated['banner_image'] = url(Storage::url($updated['banner_image']));
} }
return $updated; return $updated;
} }
@ -253,7 +246,7 @@ trait AdminDirectoryController
'open_registration' => (bool) config_cache('pixelfed.open_registration'), 'open_registration' => (bool) config_cache('pixelfed.open_registration'),
'curated_onboarding' => (bool) config_cache('instance.curated_registration.enabled'), 'curated_onboarding' => (bool) config_cache('instance.curated_registration.enabled'),
'activitypub_enabled' => config_cache('federation.activitypub.enabled'), 'activitypub_enabled' => config_cache('federation.activitypub.enabled'),
'oauth_enabled' => config_cache('pixelfed.oauth_enabled'), 'oauth_enabled' => (bool) config_cache('pixelfed.oauth_enabled'),
'media_types' => Str::of(config_cache('pixelfed.media_types'))->explode(','), 'media_types' => Str::of(config_cache('pixelfed.media_types'))->explode(','),
'image_quality' => config_cache('pixelfed.image_quality'), 'image_quality' => config_cache('pixelfed.image_quality'),
'optimize_image' => config_cache('pixelfed.optimize_image'), 'optimize_image' => config_cache('pixelfed.optimize_image'),
@ -274,7 +267,7 @@ trait AdminDirectoryController
'media_types' => [ 'media_types' => [
'required', 'required',
function ($attribute, $value, $fail) { function ($attribute, $value, $fail) {
if (!in_array('image/jpeg', $value->toArray()) || !in_array('image/png', $value->toArray())) { if (! in_array('image/jpeg', $value->toArray()) || ! in_array('image/png', $value->toArray())) {
$fail('You must enable image/jpeg and image/png support.'); $fail('You must enable image/jpeg and image/png support.');
} }
}, },
@ -285,10 +278,10 @@ trait AdminDirectoryController
'max_account_size' => 'required_if:enforce_account_limit,true|integer|min:1000000', 'max_account_size' => 'required_if:enforce_account_limit,true|integer|min:1000000',
'max_album_length' => 'required|integer|min:4|max:20', 'max_album_length' => 'required|integer|min:4|max:20',
'account_deletion' => 'required|accepted', 'account_deletion' => 'required|accepted',
'max_caption_length' => 'required|integer|min:500|max:10000' 'max_caption_length' => 'required|integer|min:500|max:10000',
]); ]);
if(!$validator->validate()) { if (! $validator->validate()) {
return response()->json($validator->errors(), 422); return response()->json($validator->errors(), 422);
} }
@ -297,6 +290,7 @@ trait AdminDirectoryController
$data = (new PixelfedDirectoryController())->buildListing(); $data = (new PixelfedDirectoryController())->buildListing();
$res = Http::withoutVerifying()->post('https://pixelfed.org/api/v1/directory/submission', $data); $res = Http::withoutVerifying()->post('https://pixelfed.org/api/v1/directory/submission', $data);
return 200; return 200;
} }
@ -304,7 +298,7 @@ trait AdminDirectoryController
{ {
$bannerImage = ConfigCache::whereK('app.banner_image')->first(); $bannerImage = ConfigCache::whereK('app.banner_image')->first();
$directory = ConfigCache::whereK('pixelfed.directory')->first(); $directory = ConfigCache::whereK('pixelfed.directory')->first();
if(!$bannerImage && !$directory || empty($directory->v)) { if (! $bannerImage && ! $directory || empty($directory->v)) {
return; return;
} }
$directoryArr = json_decode($directory->v, true); $directoryArr = json_decode($directory->v, true);
@ -312,12 +306,12 @@ trait AdminDirectoryController
$protected = [ $protected = [
'public/headers/.gitignore', 'public/headers/.gitignore',
'public/headers/default.jpg', 'public/headers/default.jpg',
'public/headers/missing.png' 'public/headers/missing.png',
]; ];
if(!$path || in_array($path, $protected)) { if (! $path || in_array($path, $protected)) {
return; return;
} }
if(Storage::exists($directoryArr['banner_image'])) { if (Storage::exists($directoryArr['banner_image'])) {
Storage::delete($directoryArr['banner_image']); Storage::delete($directoryArr['banner_image']);
} }
@ -328,12 +322,13 @@ trait AdminDirectoryController
$bannerImage->save(); $bannerImage->save();
Cache::forget('api:v1:instance-data-response-v1'); Cache::forget('api:v1:instance-data-response-v1');
ConfigCacheService::put('pixelfed.directory', $directory); ConfigCacheService::put('pixelfed.directory', $directory);
return $bannerImage->v; return $bannerImage->v;
} }
public function directoryGetPopularPosts(Request $request) public function directoryGetPopularPosts(Request $request)
{ {
$ids = Cache::remember('admin:api:popular_posts', 86400, function() { $ids = Cache::remember('admin:api:popular_posts', 86400, function () {
return Status::whereLocal(true) return Status::whereLocal(true)
->whereScope('public') ->whereScope('public')
->whereType('photo') ->whereType('photo')
@ -343,21 +338,21 @@ trait AdminDirectoryController
->pluck('id'); ->pluck('id');
}); });
$res = $ids->map(function($id) { $res = $ids->map(function ($id) {
return StatusService::get($id); return StatusService::get($id);
}) })
->filter(function($post) { ->filter(function ($post) {
return $post && isset($post['account']); return $post && isset($post['account']);
}) })
->values(); ->values();
return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); return response()->json($res, 200, [], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
} }
public function directoryGetAddPostByIdSearch(Request $request) public function directoryGetAddPostByIdSearch(Request $request)
{ {
$this->validate($request, [ $this->validate($request, [
'q' => 'required|integer' 'q' => 'required|integer',
]); ]);
$id = $request->input('q'); $id = $request->input('q');
@ -380,11 +375,12 @@ trait AdminDirectoryController
$profile_id = $request->input('profile_id'); $profile_id = $request->input('profile_id');
$testimonials = ConfigCache::whereK('pixelfed.directory.testimonials')->firstOrFail(); $testimonials = ConfigCache::whereK('pixelfed.directory.testimonials')->firstOrFail();
$existing = collect(json_decode($testimonials->v, true)) $existing = collect(json_decode($testimonials->v, true))
->filter(function($t) use($profile_id) { ->filter(function ($t) use ($profile_id) {
return $t['profile_id'] !== $profile_id; return $t['profile_id'] !== $profile_id;
}) })
->values(); ->values();
ConfigCacheService::put('pixelfed.directory.testimonials', $existing); ConfigCacheService::put('pixelfed.directory.testimonials', $existing);
return $existing; return $existing;
} }
@ -392,13 +388,13 @@ trait AdminDirectoryController
{ {
$this->validate($request, [ $this->validate($request, [
'username' => 'required', 'username' => 'required',
'body' => 'required|string|min:5|max:500' 'body' => 'required|string|min:5|max:500',
]); ]);
$user = User::whereUsername($request->input('username'))->whereNull('status')->firstOrFail(); $user = User::whereUsername($request->input('username'))->whereNull('status')->firstOrFail();
$configCache = ConfigCache::firstOrCreate([ $configCache = ConfigCache::firstOrCreate([
'k' => 'pixelfed.directory.testimonials' 'k' => 'pixelfed.directory.testimonials',
]); ]);
$testimonials = $configCache->v ? collect(json_decode($configCache->v, true)) : collect([]); $testimonials = $configCache->v ? collect(json_decode($configCache->v, true)) : collect([]);
@ -409,7 +405,7 @@ trait AdminDirectoryController
$testimonials->push([ $testimonials->push([
'profile_id' => (string) $user->profile_id, 'profile_id' => (string) $user->profile_id,
'username' => $request->input('username'), 'username' => $request->input('username'),
'body' => $request->input('body') 'body' => $request->input('body'),
]); ]);
$configCache->v = json_encode($testimonials->toArray()); $configCache->v = json_encode($testimonials->toArray());
@ -417,8 +413,9 @@ trait AdminDirectoryController
ConfigCacheService::put('pixelfed.directory.testimonials', $configCache->v); ConfigCacheService::put('pixelfed.directory.testimonials', $configCache->v);
$res = [ $res = [
'profile' => AccountService::get($user->profile_id), 'profile' => AccountService::get($user->profile_id),
'body' => $request->input('body') 'body' => $request->input('body'),
]; ];
return $res; return $res;
} }
@ -426,7 +423,7 @@ trait AdminDirectoryController
{ {
$this->validate($request, [ $this->validate($request, [
'profile_id' => 'required', 'profile_id' => 'required',
'body' => 'required|string|min:5|max:500' 'body' => 'required|string|min:5|max:500',
]); ]);
$profile_id = $request->input('profile_id'); $profile_id = $request->input('profile_id');
@ -434,15 +431,16 @@ trait AdminDirectoryController
$user = User::whereProfileId($profile_id)->firstOrFail(); $user = User::whereProfileId($profile_id)->firstOrFail();
$configCache = ConfigCache::firstOrCreate([ $configCache = ConfigCache::firstOrCreate([
'k' => 'pixelfed.directory.testimonials' 'k' => 'pixelfed.directory.testimonials',
]); ]);
$testimonials = $configCache->v ? collect(json_decode($configCache->v, true)) : collect([]); $testimonials = $configCache->v ? collect(json_decode($configCache->v, true)) : collect([]);
$updated = $testimonials->map(function($t) use($profile_id, $body) { $updated = $testimonials->map(function ($t) use ($profile_id, $body) {
if($t['profile_id'] == $profile_id) { if ($t['profile_id'] == $profile_id) {
$t['body'] = $body; $t['body'] = $body;
} }
return $t; return $t;
}) })
->values(); ->values();

View file

@ -131,7 +131,7 @@ class ApiV1Controller extends Controller
*/ */
public function apps(Request $request) public function apps(Request $request)
{ {
abort_if(! config_cache('pixelfed.oauth_enabled'), 404); abort_if(! (bool) config_cache('pixelfed.oauth_enabled'), 404);
$this->validate($request, [ $this->validate($request, [
'client_name' => 'required', 'client_name' => 'required',

View file

@ -2,9 +2,9 @@
namespace App\Providers; namespace App\Providers;
use Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Laravel\Passport\Passport; use Laravel\Passport\Passport;
use Gate;
class AuthServiceProvider extends ServiceProvider class AuthServiceProvider extends ServiceProvider
{ {
@ -24,11 +24,11 @@ class AuthServiceProvider extends ServiceProvider
*/ */
public function boot() public function boot()
{ {
if(config('app.env') === 'production' && config('pixelfed.oauth_enabled') == true) { if (config('app.env') === 'production' && (bool) config_cache('pixelfed.oauth_enabled') == true) {
Passport::tokensExpireIn(now()->addDays(config('instance.oauth.token_expiration', 356))); Passport::tokensExpireIn(now()->addDays(config('instance.oauth.token_expiration', 356)));
Passport::refreshTokensExpireIn(now()->addDays(config('instance.oauth.refresh_expiration', 400))); Passport::refreshTokensExpireIn(now()->addDays(config('instance.oauth.refresh_expiration', 400)));
Passport::enableImplicitGrant(); Passport::enableImplicitGrant();
if(config('instance.oauth.pat.enabled')) { if (config('instance.oauth.pat.enabled')) {
Passport::personalAccessClientId(config('instance.oauth.pat.id')); Passport::personalAccessClientId(config('instance.oauth.pat.id'));
} }
@ -38,7 +38,7 @@ class AuthServiceProvider extends ServiceProvider
'follow' => 'Ability to follow other profiles', 'follow' => 'Ability to follow other profiles',
'admin:read' => 'Read all data on the server', 'admin:read' => 'Read all data on the server',
'admin:write' => 'Modify all data on the server', 'admin:write' => 'Modify all data on the server',
'push' => 'Receive your push notifications' 'push' => 'Receive your push notifications',
]); ]);
Passport::setDefaultScope([ Passport::setDefaultScope([

View file

@ -66,7 +66,7 @@
</li> </li>
<li> <li>
<strong><span class="badge badge-primary">OAUTH</span> enabled: </strong> <strong><span class="badge badge-primary">OAUTH</span> enabled: </strong>
<span>{{ config_cache('pixelfed.oauth_enabled') ? '✅ true' : '❌ false' }}</span> <span>{{ (bool) config_cache('pixelfed.oauth_enabled') ? '✅ true' : '❌ false' }}</span>
</li> </li>
<li> <li>
<strong><span class="badge badge-primary">OAUTH</span> token_expiration</strong> <strong><span class="badge badge-primary">OAUTH</span> token_expiration</strong>
@ -810,7 +810,7 @@
<tr> <tr>
<td><span class="badge badge-primary">PIXELFED</span></td> <td><span class="badge badge-primary">PIXELFED</span></td>
<td><strong>OAUTH_ENABLED</strong></td> <td><strong>OAUTH_ENABLED</strong></td>
<td><span>{{config_cache('pixelfed.oauth_enabled') ? '✅ true' : '❌ false' }}</span></td> <td><span>{{ (bool) config_cache('pixelfed.oauth_enabled') ? '✅ true' : '❌ false' }}</span></td>
</tr> </tr>
<tr> <tr>
<td><span class="badge badge-primary">PIXELFED</span></td> <td><span class="badge badge-primary">PIXELFED</span></td>

View file

@ -6,7 +6,7 @@
<h3 class="font-weight-bold">Applications</h3> <h3 class="font-weight-bold">Applications</h3>
</div> </div>
<hr> <hr>
@if(config_cache('pixelfed.oauth_enabled') == true) @if((bool) config_cache('pixelfed.oauth_enabled') == true)
<passport-authorized-clients></passport-authorized-clients> <passport-authorized-clients></passport-authorized-clients>
<passport-personal-access-tokens></passport-personal-access-tokens> <passport-personal-access-tokens></passport-personal-access-tokens>
@else @else

View file

@ -6,7 +6,7 @@
<h3 class="font-weight-bold">Developers</h3> <h3 class="font-weight-bold">Developers</h3>
</div> </div>
<hr> <hr>
@if(config_cache('pixelfed.oauth_enabled') == true) @if((bool) config_cache('pixelfed.oauth_enabled') == true)
<passport-clients></passport-clients> <passport-clients></passport-clients>
@else @else
<p class="lead">OAuth has not been enabled on this instance.</p> <p class="lead">OAuth has not been enabled on this instance.</p>

View file

@ -39,7 +39,7 @@
<hr> <hr>
</li> </li>
@if(config_cache('pixelfed.oauth_enabled') == true) @if((bool) config_cache('pixelfed.oauth_enabled') == true)
<li class="nav-item pl-3 {{request()->is('settings/applications')?'active':''}}"> <li class="nav-item pl-3 {{request()->is('settings/applications')?'active':''}}">
<a class="nav-link font-weight-light text-muted" href="{{route('settings.applications')}}">Applications</a> <a class="nav-link font-weight-light text-muted" href="{{route('settings.applications')}}">Applications</a>
</li> </li>