Files
Virtnet.bond/app/Providers/AppServiceProvider.php

242 lines
9.2 KiB
PHP

<?php
namespace App\Providers;
use App\Classes\Synths\PriceSynth;
use App\Helpers\ExtensionHelper;
use App\Models\EmailLog;
use App\Models\Extension;
use App\Models\OauthClient;
use App\Models\User;
use App\Support\Passport\ScopeRegistry;
use Closure;
use Dedoc\Scramble\Scramble;
use Dedoc\Scramble\Support\Generator\OpenApi;
use Dedoc\Scramble\Support\Generator\SecurityScheme;
use Illuminate\Http\Request;
use Illuminate\Queue\Events\JobFailed;
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Routing\UrlGenerator;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
use Laravel\Passport\Passport;
use League\CommonMark\Extension\Table\TableExtension;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// Service provider for settings
$this->app->register(SettingsProvider::class);
UrlGenerator::macro('alternateHasCorrectSignature', function (Request $request, $absolute = true, Closure|array $ignoreQuery = []) {
// ensure the base path is applied to absolute url
$absoluteUrl = url($request->path()); // forceRootUrl and forceScheme will apply
$url = $absolute ? $absoluteUrl : '/' . $request->path();
$queryString = collect(explode('&', (string) $request->server->get('QUERY_STRING')))
->reject(function ($parameter) use ($ignoreQuery) {
$parameter = Str::before($parameter, '=');
if ($parameter === 'signature') {
return true;
}
if ($ignoreQuery instanceof Closure) {
return $ignoreQuery($parameter);
}
return in_array($parameter, $ignoreQuery);
})
->join('&');
$original = rtrim($url . '?' . $queryString, '?');
$keys = call_user_func($this->keyResolver);
$keys = is_array($keys) ? $keys : [$keys];
foreach ($keys as $key) {
if (
hash_equals(
hash_hmac('sha256', $original, $key),
(string) $request->query('signature', '')
)
) {
return true;
}
}
return false;
});
UrlGenerator::macro('alternateHasValidSignature', function (Request $request, $absolute = true, array $ignoreQuery = []) {
return \URL::alternateHasCorrectSignature($request, $absolute, $ignoreQuery)
&& \URL::signatureHasNotExpired($request);
});
Request::macro('hasValidSignature', function ($absolute = true, array $ignoreQuery = []) {
return \URL::alternateHasValidSignature($this, $absolute, $ignoreQuery);
});
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
// Ensure storage symlink exists for public access
$this->ensureStorageSymlinkExists();
// Change livewire url
\Livewire\Livewire::setUpdateRoute(function ($handle) {
return \Illuminate\Support\Facades\Route::post('/paymenter/update', $handle)->middleware('web')->name('paymenter.');
});
\Livewire\Livewire::propertySynthesizer(PriceSynth::class);
Gate::define('has-permission', function (User $user, string $ability) {
return $user->hasPermission($ability);
});
Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) {
$event->extendSocialite('discord', \SocialiteProviders\Discord\Provider::class);
});
try {
foreach (
collect(Extension::where(function ($query) {
$query->where('enabled', true)->orWhere('type', 'server')->orWhere('type', 'gateway');
})->get())->unique('extension') as $extension
) {
ExtensionHelper::call($extension, 'boot', mayFail: true);
}
} catch (\Exception $e) {
// Fail silently
}
Queue::after(function (JobProcessed $event) {
if ($event->job->resolveName() === 'App\Mail\Mail') {
$payload = json_decode($event->job->getRawBody());
$data = unserialize($payload->data->command);
EmailLog::where('id', $data->mailable->email_log_id)->update([
'sent_at' => now(),
'status' => 'sent',
]);
}
});
Queue::failing(function (JobFailed $event) {
if ($event->job->resolveName() === 'App\Mail\Mail') {
$payload = json_decode($event->job->getRawBody());
$data = unserialize($payload->data->command);
EmailLog::where('id', $data->mailable->email_log_id)->update([
'status' => 'failed',
'error' => $event->exception->getMessage(),
'job_uuid' => $event->job->uuid(),
]);
}
});
Str::macro('markdown', function ($markdown) {
return Str::markdown($markdown, extensions: [
new TableExtension,
]);
});
Passport::clientModel(OauthClient::class);
Passport::ignoreRoutes();
Passport::tokensCan(ScopeRegistry::getAll());
if (class_exists(Scramble::class)) {
Scramble::configure()
->routes(function (\Illuminate\Routing\Route $route) {
return Str::startsWith($route->uri, 'api/v1/admin');
})
->withDocumentTransformers(function (OpenApi $openApi) {
$openApi->secure(
SecurityScheme::http('bearer')
);
});
}
}
/**
* Ensure the storage symlink exists for public file access
*/
protected function ensureStorageSymlinkExists(): void
{
$publicStoragePath = public_path('storage');
$targetStoragePath = storage_path('app/public');
// Check if symlink doesn't exist or is broken
if (!file_exists($publicStoragePath) || !is_link($publicStoragePath)) {
// Remove broken symlink or existing file/directory if it exists
if (file_exists($publicStoragePath)) {
if (is_link($publicStoragePath)) {
@unlink($publicStoragePath);
} elseif (is_dir($publicStoragePath)) {
// If it's a directory, we can't safely remove it automatically
// Skip silently - the user is aware of this situation
return;
} else {
@unlink($publicStoragePath);
}
}
// Ensure target directory exists
if (!file_exists($targetStoragePath)) {
File::makeDirectory($targetStoragePath, 0755, true);
}
// Create the symlink using Laravel's filesystem links
try {
$links = config('filesystems.links', []);
foreach ($links as $link => $target) {
if (file_exists($link)) {
continue;
}
if (PHP_OS_FAMILY === 'Windows') {
// Windows requires administrator privileges for symlinks
// Try to create junction instead
if (function_exists('exec')) {
$command = sprintf(
'mklink /J "%s" "%s"',
str_replace('/', '\\', $link),
str_replace('/', '\\', $target)
);
@exec($command, $output, $return);
// Silently ignore if symlink creation fails on Windows
}
// Skip silently if exec is not available on Windows
} else {
// Unix/Linux - create symlink
symlink($target, $link);
}
}
} catch (\Exception $e) {
// Silently ignore symlink creation failures
}
} elseif (is_link($publicStoragePath)) {
// Check if symlink is broken (points to non-existent target)
$linkTarget = readlink($publicStoragePath);
if ($linkTarget && !file_exists($linkTarget) && $linkTarget !== $targetStoragePath) {
// Symlink is broken, recreate it
@unlink($publicStoragePath);
try {
if (PHP_OS_FAMILY !== 'Windows') {
symlink($targetStoragePath, $publicStoragePath);
}
} catch (\Exception $e) {
\Log::warning('Storage symlink recreation failed: ' . $e->getMessage() . '. Please run: php artisan storage:link');
}
}
}
}
}