diff --git a/.eslintrc.yml b/.eslintrc.yml index aee50cc9..ae8facba 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -29,5 +29,10 @@ rules: { 'camelcase': 'off', 'class-methods-use-this': 'off', 'prefer-destructuring': 'off', + 'vuejs-accessibility/click-events-have-key-events': 'off', + 'vuejs-accessibility/form-control-has-label': 'off', + 'vuejs-accessibility/label-has-for': 'off', + 'vuejs-accessibility/no-autofocus': 'off', + 'vuejs-accessibility/alt-text': 'off', 'semi': ['error', 'never'] } diff --git a/.gitignore b/.gitignore index f86db243..e75b0b1a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /public/storage /public/modpacks /public/modpacks-data +/public/launcher /public/js /public/css /public/mix-manifest.json diff --git a/README.md b/README.md index 4c32767e..ed320917 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,13 @@ foo@bar:~/MultigamingPanel$ npm run prod && php artisan migrate ###### Step 6 Configure your webserver to point to the public/ sub-directory +###### Step 6 Open the required ports +```console +foo@bar:~/MultigamingPanel$ ufw allow 443 +foo@bar:~/MultigamingPanel$ ufw allow 6001 +foo@bar:~/MultigamingPanel$ ufw allow 6660 +``` + ## Emodyz Sponsors You can sponsor us through various means. diff --git a/app/Http/Controllers/ModPackController.php b/app/Http/Controllers/ModPackController.php index 2c98390b..89c9dc49 100644 --- a/app/Http/Controllers/ModPackController.php +++ b/app/Http/Controllers/ModPackController.php @@ -125,7 +125,7 @@ public function edit(Request $request, Modpack $modpack): Response|ModPackResour ->get() ->map(fn(Server $server) => $server->only(['id', 'name', 'logo_url', 'game'])); - $modpack->servers; + $modpack->load('servers'); return inertia('ModPacks/Edit', compact('servers', 'modpack')); } @@ -209,6 +209,11 @@ public function cancelUpdate(Modpack $modpack): Response } $modpack->batch->cancel(); + + $modpack->update([ + 'job_batch_id' => null, + ]); + return response()->noContent(); } } diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index 2c0dd562..14bb2dc8 100644 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -4,7 +4,10 @@ use App\Actions\Emodyz\Settings\EditSettings; use App\Http\Requests\Settings\EditVoiceSettingsRequest; +use App\Services\Bridge\BridgeClientService; use App\Settings\VoiceSettings; +use Exception; +use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Inertia\Response; @@ -17,18 +20,32 @@ public function __construct() $this->middleware('can:settings-edit')->only(['edit']); $this->middleware('can:settings-edit_voice')->only(['updateVoice']); + + $this->middleware('can:settings-cp_update_check')->only(['checkForCpUpdate']); + + $this->middleware('can:settings-cp_upgrade')->only(['checkForCpUpdate']); } /** * Display a listing of the resource. - * - * @param VoiceSettings $voiceSettings - * @return \Illuminate\Http\Response|Response|ResponseFactory + * @param VoiceSettings $voiceSettings + * @return Response|ResponseFactory */ public function edit(VoiceSettings $voiceSettings) { + $version = 'unknown'; + try { + $bridgeClient = new BridgeClientService(); + + $version = $bridgeClient->getControlPanelVersion(); + } catch (Exception $e) { + flash('BRIDGE ERROR', $e->getMessage(), 'error'); + } + + return inertia('Settings/Edit', [ - 'voiceSettings' => $voiceSettings->toArray() + 'currentVersion' => $version, + 'voiceSettings' => $voiceSettings->toArray() ]); } @@ -39,12 +56,59 @@ public function edit(VoiceSettings $voiceSettings) * @param EditSettings $editor * @return RedirectResponse */ - public function updateVoice(EditVoiceSettingsRequest $request, EditSettings $editor): RedirectResponse - { + public function updateVoice(EditVoiceSettingsRequest $request, EditSettings $editor): RedirectResponse + { $editor->editVoiceSettings($request->all()); flash('Voice Settings', "Your voice settings has been successfully saved.")->success(); return redirect()->back(); - } + } + + /** + * Check for control panel updates + * + * @return JsonResponse + */ + public function checkForCpUpdate(): JsonResponse + { + $target = 'none'; + try { + $bridgeClient = new BridgeClientService(); + + $target = $bridgeClient->checkForControlPanelUpdate(); + } catch (Exception $e) { + flash('BRIDGE ERROR', $e->getMessage(), 'error'); + } + + if ($target !== 'none') { + flash('A new version of the Control Panel is available.', $target, 'info'); + } + + return response()->json([ + 'target' => $target + ]); + } + + /** + * Initiates the upgrade process for the control panel by calling the cli using the gRpc Bridge + * + * @return JsonResponse + */ + public function upgradeCp(): JsonResponse + { + try { + $bridgeClient = new BridgeClientService(); + + $bridgeClient->upgradeControlPanel(); + } catch (Exception $e) { + flash('BRIDGE ERROR', $e->getMessage(), 'error'); + } + + flash('Control Panel update started.', 'the application will be offline for a few minutes ', 'warning'); + + return response()->json([ + 'upgrade' => 'started' + ]); + } } diff --git a/app/Jobs/ProcessModPackFile.php b/app/Jobs/ProcessModPackFile.php index 6411e94d..3e1a8b33 100644 --- a/app/Jobs/ProcessModPackFile.php +++ b/app/Jobs/ProcessModPackFile.php @@ -4,6 +4,7 @@ use App\Events\ModPack\ModPackProcessProgress; use App\Models\Modpack; +use App\Services\Modpacks\ModpackUpdaterService; use Illuminate\Bus\Batchable; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; @@ -27,8 +28,8 @@ class ProcessModPackFile implements ShouldQueue /** * Create a new job instance. * - * @param Modpack $modpack - * @param string $filePath + * @param Modpack $modpack + * @param string $filePath */ public function __construct(Modpack $modpack, string $filePath) { @@ -36,6 +37,7 @@ public function __construct(Modpack $modpack, string $filePath) $this->filePath = $filePath; } + /** * Execute the job. * @@ -44,10 +46,13 @@ public function __construct(Modpack $modpack, string $filePath) */ public function handle() { - if ($this->batch()->cancelled()) { + if ($this->batch()->cancelled() || $this->batch()->hasFailures()) { + info('Batch canceled or has failures, file skipped '. $this->filePath); return; } + info('Processing file '. $this->filePath); + $disk = $this->modpack->disk; $filePath = Storage::disk($disk)->path($this->filePath); @@ -55,25 +60,21 @@ public function handle() $fileUrl = Storage::disk($disk)->url($this->filePath); $fileHash = hash_file('sha256', $filePath); $fileName = basename($filePath); - $filePath = (string)Str::of($this->filePath)->replaceFirst( + $filePath = (string) Str::of($this->filePath)->replaceFirst( $this->modpack->path, $this->modpack->name ); - $filePathPrevented = Str::of($filePath) - ->replace('.', '-'); - $this->modpack->forceFill([ - "manifest_info->size" => $this->modpack->manifest_info['size'] + $fileSize, - "manifest_info->files" => $this->modpack->manifest_info['files'] + 1, - "manifest->{$filePathPrevented}" => [ - 'url' => $fileUrl, - 'size' => $fileSize, - 'name' => $fileName, - 'path' => $filePath, - 'sha256' => $fileHash - ] - ])->saveOrFail(); + ModpackUpdaterService::fileProcessed( + modPack: $this->modpack, + fileName: $fileName, + fileSize: $fileSize, + fileUrl: $fileUrl, + filePath: $filePath, + fileHash: $fileHash + ); ModPackProcessProgress::broadcast($this->modpack, $this->batch()->progress()); } } + diff --git a/app/Listeners/Modpack/StartModPackUpdate.php b/app/Listeners/Modpack/StartModPackUpdate.php index 6a8c057e..f96035d9 100644 --- a/app/Listeners/Modpack/StartModPackUpdate.php +++ b/app/Listeners/Modpack/StartModPackUpdate.php @@ -8,10 +8,13 @@ use App\Events\ModPack\ModPackProcessStarted; use App\Events\ModPack\ModPackUpdateRequested; use App\Jobs\ProcessModPackFile; +use App\Services\Modpacks\ModpackUpdaterService; +use Exception; use Illuminate\Bus\Batch; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Support\Facades\Bus; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use Throwable; @@ -29,9 +32,6 @@ class StartModPackUpdate implements ShouldQueue public function handle(ModPackUpdateRequested $event) { $modpack = $event->modPack; - $savedManifest = $modpack->manifest; - $savedManifestInfo = $modpack->manifest_info; - $modpack->cleanManifest(); $files = collect(Storage::disk($modpack->disk)->files($modpack->path, true)); if ($files->isEmpty()) { @@ -39,27 +39,33 @@ public function handle(ModPackUpdateRequested $event) return true; } + ModpackUpdaterService::flush($modpack); + $jobs = $files->map(fn($file) => new ProcessModPackFile($modpack, $file)); $batch = Bus::batch($jobs->toArray()) - ->then(function (Batch $batch) use ($modpack, $savedManifest, $savedManifestInfo) { + ->then(function (Batch $batch) use ($modpack) { if ($batch->canceled()) { + ModPackProcessCanceled::broadcast($modpack); + return; + } + + [$manifest, $manifestInfo] = ModpackUpdaterService::getUpdate($modpack); + + if (!empty($manifest) && !empty($manifestInfo)) { $modpack->update([ - 'manifest' => $savedManifest, - 'manifest_info' => $savedManifestInfo + 'manifest' => collect($manifest)->map(fn ($value) => json_decode($value)), + 'manifest_info' => $manifestInfo, + 'manifest_last_update' => now()->toDateTimeString() ]); - ModPackProcessCanceled::broadcast($modpack); + ModPackProcessDone::broadcast($modpack); + Log::info('Job done'); return; } - $modpack->update([ - 'manifest_last_update' => now()->toDateTimeString() - ]); - ModPackProcessDone::broadcast($modpack); - })->catch(function () use ($modpack, $savedManifest, $savedManifestInfo) { - $modpack->update([ - 'manifest' => $savedManifest, - 'manifest_info' => $savedManifestInfo - ]); + + throw new Exception('Job not finalized correctly...'); + })->catch(function () use ($modpack) { ModPackProcessFailed::broadcast($modpack); + Log::alert('Job failed'); })->finally(function () use ($modpack) { $modpack->update([ 'job_batch_id' => null, diff --git a/app/Models/Modpack.php b/app/Models/Modpack.php index 407a7d83..ab7409fb 100644 --- a/app/Models/Modpack.php +++ b/app/Models/Modpack.php @@ -89,18 +89,4 @@ public function getBatchAttribute(): ?Batch } return Bus::findBatch($this->job_batch_id); } - - /** - * void - */ - public function cleanManifest() - { - $this->update([ - 'manifest' => [], - 'manifest_info' => [ - "size" => 0, - "files" => 0 - ] - ]); - } } diff --git a/app/Services/Bridge/BridgeClientService.php b/app/Services/Bridge/BridgeClientService.php new file mode 100644 index 00000000..90ab4120 --- /dev/null +++ b/app/Services/Bridge/BridgeClientService.php @@ -0,0 +1,78 @@ +client = new BridgeClient('host.docker.internal:6660', [ + 'credentials' => \Grpc\ChannelCredentials::createSsl( + Storage::disk('bridge')->get('ca.pem'), + Storage::disk('bridge')->get('cert.key'), + Storage::disk('bridge')->get('cert.crt') + ), + ]); + } + + /** + * @throws Exception + * @return string + */ + public function getControlPanelVersion(): string { + $request = new EmptyMessage(); + list($response, $status) = $this->client->getCpVersion($request)->wait(); + if ($status->code !== \Grpc\STATUS_OK) { + throw new Exception('Error while retrieving current version: ' . $status->code . ", " . $status->details . PHP_EOL); + } + return $response->getVersion(); + } + + /** + * @throws Exception + * @return string + */ + public function checkForControlPanelUpdate(): string { + $request = new EmptyMessage(); + list($response, $status) = $this->client->CheckForCpUpdate($request)->wait(); + if ($status->code !== \Grpc\STATUS_OK) { + throw new Exception('Error while checking for Control Panel updates: ' . $status->code . ", " . $status->details . PHP_EOL); + } + return $response->getTarget(); + } + + /** + * @throws Exception + * @return void + */ + public function upgradeControlPanel(): void { + $request = new UpgradeCpRequest(); + + $target = $this->checkForControlPanelUpdate(); + if ($target === 'none') { + throw new Exception('Error while initiating Control Panel upgrade: No valid upgrade target found'); + } + + $request->setVersion($target); + list($response, $status) = $this->client->UpgradeCp($request)->wait(); + if ($status->code !== \Grpc\STATUS_OK) { + throw new Exception('Error while initiating Control Panel upgrade: ' . $status->code . ", " . $status->details . PHP_EOL); + } + } +} diff --git a/app/Services/Modpacks/ModpackUpdaterService.php b/app/Services/Modpacks/ModpackUpdaterService.php new file mode 100644 index 00000000..a73bd3d3 --- /dev/null +++ b/app/Services/Modpacks/ModpackUpdaterService.php @@ -0,0 +1,63 @@ +id"; + } + + private static function getManifestInfoKey(Modpack $modpack): string + { + return "modPack:manifestInfoUpdate:$modpack->id"; + } + + + static public function flush(Modpack $modpack): void + { + Redis::del(self::getManifestKey($modpack)); + Redis::del(self::getManifestInfoKey($modpack)); + } + + static public function getUpdate(Modpack $modpack): array + { + return [ + Redis::hGetAll(self::getManifestKey($modpack)), + Redis::hGetAll(self::getManifestInfoKey($modpack)), + ]; + } + + public static function fileProcessed( + Modpack $modPack, + string $fileName, + int $fileSize, + string $fileUrl, + string $filePath, + string $fileHash, + ) + { + $manifestKey = self::getManifestKey($modPack); + $manifestInfoKey = self::getManifestInfoKey($modPack); + + $safeFilePath = Str::of($filePath) + ->replace('.', '-'); + + + Redis::hIncrBy($manifestInfoKey, 'size', $fileSize); + Redis::hIncrBy($manifestInfoKey, 'files', 1); + + Redis::hSet($manifestKey, $safeFilePath, json_encode([ + 'url' => $fileUrl, + 'size' => $fileSize, + 'name' => $fileName, + 'path' => $filePath, + 'sha256' => $fileHash + ])); + } +} diff --git a/bridge/Bridge/BridgeClient.php b/bridge/Bridge/BridgeClient.php index 1470a611..a4a3b024 100644 --- a/bridge/Bridge/BridgeClient.php +++ b/bridge/Bridge/BridgeClient.php @@ -32,4 +32,47 @@ public function SayHello(\Bridge\HelloRequest $argument, $metadata, $options); } + /** + * Get the current version of the control panel + * @param \Bridge\EmptyMessage $argument input argument + * @param array $metadata metadata + * @param array $options call options + * @return \Grpc\UnaryCall + */ + public function GetCpVersion(\Bridge\EmptyMessage $argument, + $metadata = [], $options = []) { + return $this->_simpleRequest('/bridge.Bridge/GetCpVersion', + $argument, + ['\Bridge\GetCpVersionReply', 'decode'], + $metadata, $options); + } + + /** + * @param \Bridge\EmptyMessage $argument input argument + * @param array $metadata metadata + * @param array $options call options + * @return \Grpc\UnaryCall + */ + public function CheckForCpUpdate(\Bridge\EmptyMessage $argument, + $metadata = [], $options = []) { + return $this->_simpleRequest('/bridge.Bridge/CheckForCpUpdate', + $argument, + ['\Bridge\CheckForCpUpdateReply', 'decode'], + $metadata, $options); + } + + /** + * @param \Bridge\UpgradeCpRequest $argument input argument + * @param array $metadata metadata + * @param array $options call options + * @return \Grpc\UnaryCall + */ + public function UpgradeCp(\Bridge\UpgradeCpRequest $argument, + $metadata = [], $options = []) { + return $this->_simpleRequest('/bridge.Bridge/UpgradeCp', + $argument, + ['\Bridge\EmptyMessage', 'decode'], + $metadata, $options); + } + } diff --git a/bridge/Bridge/BridgeStub.php b/bridge/Bridge/BridgeStub.php index 9021acb8..be35546e 100644 --- a/bridge/Bridge/BridgeStub.php +++ b/bridge/Bridge/BridgeStub.php @@ -23,6 +23,49 @@ public function SayHello( return null; } + /** + * Get the current version of the control panel + * @param \Bridge\EmptyMessage $request client request + * @param \Grpc\ServerContext $context server request context + * @return \Bridge\GetCpVersionReply for response data, null if if error occured + * initial metadata (if any) and status (if not ok) should be set to $context + */ + public function GetCpVersion( + \Bridge\EmptyMessage $request, + \Grpc\ServerContext $context + ): ?\Bridge\GetCpVersionReply { + $context->setStatus(\Grpc\Status::unimplemented()); + return null; + } + + /** + * @param \Bridge\EmptyMessage $request client request + * @param \Grpc\ServerContext $context server request context + * @return \Bridge\CheckForCpUpdateReply for response data, null if if error occured + * initial metadata (if any) and status (if not ok) should be set to $context + */ + public function CheckForCpUpdate( + \Bridge\EmptyMessage $request, + \Grpc\ServerContext $context + ): ?\Bridge\CheckForCpUpdateReply { + $context->setStatus(\Grpc\Status::unimplemented()); + return null; + } + + /** + * @param \Bridge\UpgradeCpRequest $request client request + * @param \Grpc\ServerContext $context server request context + * @return \Bridge\EmptyMessage for response data, null if if error occured + * initial metadata (if any) and status (if not ok) should be set to $context + */ + public function UpgradeCp( + \Bridge\UpgradeCpRequest $request, + \Grpc\ServerContext $context + ): ?\Bridge\EmptyMessage { + $context->setStatus(\Grpc\Status::unimplemented()); + return null; + } + /** * Get the method descriptors of the service for server registration * @@ -37,6 +80,24 @@ public final function getMethodDescriptors(): array '\Bridge\HelloRequest', \Grpc\MethodDescriptor::UNARY_CALL ), + '/bridge.Bridge/GetCpVersion' => new \Grpc\MethodDescriptor( + $this, + 'GetCpVersion', + '\Bridge\EmptyMessage', + \Grpc\MethodDescriptor::UNARY_CALL + ), + '/bridge.Bridge/CheckForCpUpdate' => new \Grpc\MethodDescriptor( + $this, + 'CheckForCpUpdate', + '\Bridge\EmptyMessage', + \Grpc\MethodDescriptor::UNARY_CALL + ), + '/bridge.Bridge/UpgradeCp' => new \Grpc\MethodDescriptor( + $this, + 'UpgradeCp', + '\Bridge\UpgradeCpRequest', + \Grpc\MethodDescriptor::UNARY_CALL + ), ]; } diff --git a/bridge/Bridge/CheckForCpUpdateReply.php b/bridge/Bridge/CheckForCpUpdateReply.php new file mode 100644 index 00000000..62a8da38 --- /dev/null +++ b/bridge/Bridge/CheckForCpUpdateReply.php @@ -0,0 +1,58 @@ +bridge.CheckForCpUpdateReply + */ +class CheckForCpUpdateReply extends \Google\Protobuf\Internal\Message +{ + /** + * Generated from protobuf field string target = 1; + */ + protected $target = ''; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type string $target + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Bridge::initOnce(); + parent::__construct($data); + } + + /** + * Generated from protobuf field string target = 1; + * @return string + */ + public function getTarget() + { + return $this->target; + } + + /** + * Generated from protobuf field string target = 1; + * @param string $var + * @return $this + */ + public function setTarget($var) + { + GPBUtil::checkString($var, True); + $this->target = $var; + + return $this; + } + +} + diff --git a/bridge/Bridge/EmptyMessage.php b/bridge/Bridge/EmptyMessage.php new file mode 100644 index 00000000..4e6836c9 --- /dev/null +++ b/bridge/Bridge/EmptyMessage.php @@ -0,0 +1,31 @@ +bridge.EmptyMessage + */ +class EmptyMessage extends \Google\Protobuf\Internal\Message +{ + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Bridge::initOnce(); + parent::__construct($data); + } + +} + diff --git a/bridge/Bridge/GetCpVersionReply.php b/bridge/Bridge/GetCpVersionReply.php new file mode 100644 index 00000000..8715fd69 --- /dev/null +++ b/bridge/Bridge/GetCpVersionReply.php @@ -0,0 +1,58 @@ +bridge.GetCpVersionReply + */ +class GetCpVersionReply extends \Google\Protobuf\Internal\Message +{ + /** + * Generated from protobuf field string version = 1; + */ + protected $version = ''; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type string $version + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Bridge::initOnce(); + parent::__construct($data); + } + + /** + * Generated from protobuf field string version = 1; + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * Generated from protobuf field string version = 1; + * @param string $var + * @return $this + */ + public function setVersion($var) + { + GPBUtil::checkString($var, True); + $this->version = $var; + + return $this; + } + +} + diff --git a/bridge/Bridge/UpgradeCpRequest.php b/bridge/Bridge/UpgradeCpRequest.php new file mode 100644 index 00000000..3e7de7c4 --- /dev/null +++ b/bridge/Bridge/UpgradeCpRequest.php @@ -0,0 +1,58 @@ +bridge.UpgradeCpRequest + */ +class UpgradeCpRequest extends \Google\Protobuf\Internal\Message +{ + /** + * Generated from protobuf field string version = 1; + */ + protected $version = ''; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type string $version + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Bridge::initOnce(); + parent::__construct($data); + } + + /** + * Generated from protobuf field string version = 1; + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * Generated from protobuf field string version = 1; + * @param string $var + * @return $this + */ + public function setVersion($var) + { + GPBUtil::checkString($var, True); + $this->version = $var; + + return $this; + } + +} + diff --git a/bridge/GPBMetadata/Bridge.php b/bridge/GPBMetadata/Bridge.php index 3f1723b9..ac482aab 100644 Binary files a/bridge/GPBMetadata/Bridge.php and b/bridge/GPBMetadata/Bridge.php differ diff --git a/composer.json b/composer.json index 57d436d4..bb78a0a7 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "fideloper/proxy": "^4.4", "fruitcake/laravel-cors": "^2.0", "guzzlehttp/guzzle": "^7.0.1", - "inertiajs/inertia-laravel": "^0.4.4", + "inertiajs/inertia-laravel": "^0.5.4", "laravel/framework": "^8.48", "laravel/jetstream": "^1.5", "laravel/sail": "^1.4", @@ -30,7 +30,7 @@ "laravel/tinker": "^2.5", "mattiasgeniar/php-percentages": "^1.3", "spatie/laravel-settings": "^1.0", - "tightenco/ziggy": "^0.9.4", + "tightenco/ziggy": "^v1.4.6", "ukfast/laravel-health-check": "^1.10" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 993b4f9d..4c85bf5a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a552e7dc82aa01c47bc023c80a146522", + "content-hash": "5c22502393a3a79b3233fda61ab8850e", "packages": [ { "name": "asm89/stack-cors", @@ -2024,26 +2024,27 @@ }, { "name": "inertiajs/inertia-laravel", - "version": "v0.4.5", + "version": "v0.5.4", "source": { "type": "git", "url": "https://github.com/inertiajs/inertia-laravel.git", - "reference": "406b15af162e78be5c7793b25aadd5a183eea84b" + "reference": "6a050ce04a710ac4809161558ac09fe49f13075e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/406b15af162e78be5c7793b25aadd5a183eea84b", - "reference": "406b15af162e78be5c7793b25aadd5a183eea84b", + "url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/6a050ce04a710ac4809161558ac09fe49f13075e", + "reference": "6a050ce04a710ac4809161558ac09fe49f13075e", "shasum": "" }, "require": { "ext-json": "*", - "laravel/framework": "^5.4|^6.0|^7.0|^8.0", - "php": "^7.2|^8.0" + "laravel/framework": "^6.0|^7.0|^8.74|^9.0", + "php": "^7.2|~8.0.0|~8.1.0" }, "require-dev": { - "orchestra/testbench": "^4.0|^5.0|^6.0", - "phpunit/phpunit": "^8.0|^9.0", + "mockery/mockery": "^1.3.3", + "orchestra/testbench": "^4.0|^5.0|^6.4|^7.0", + "phpunit/phpunit": "^8.0|^9.5.8", "roave/security-advisories": "dev-master" }, "type": "library", @@ -2080,7 +2081,7 @@ ], "support": { "issues": "https://github.com/inertiajs/inertia-laravel/issues", - "source": "https://github.com/inertiajs/inertia-laravel/tree/v0.4.5" + "source": "https://github.com/inertiajs/inertia-laravel/tree/v0.5.4" }, "funding": [ { @@ -2088,7 +2089,7 @@ "type": "github" } ], - "time": "2021-10-27T09:37:59+00:00" + "time": "2022-01-18T10:59:08+00:00" }, { "name": "jaybizzle/crawler-detect", @@ -8381,23 +8382,25 @@ }, { "name": "tightenco/ziggy", - "version": "0.9.4", + "version": "v1.4.6", "source": { "type": "git", "url": "https://github.com/tighten/ziggy.git", - "reference": "82ea6ec6cb6ab3545b0245310b2a424316fe48d8" + "reference": "a9e0e078ae6f0768836bc640a80f4cf99fa3d08f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tighten/ziggy/zipball/82ea6ec6cb6ab3545b0245310b2a424316fe48d8", - "reference": "82ea6ec6cb6ab3545b0245310b2a424316fe48d8", + "url": "https://api.github.com/repos/tighten/ziggy/zipball/a9e0e078ae6f0768836bc640a80f4cf99fa3d08f", + "reference": "a9e0e078ae6f0768836bc640a80f4cf99fa3d08f", "shasum": "" }, "require": { + "ext-json": "*", "laravel/framework": ">=5.4@dev" }, "require-dev": { - "orchestra/testbench": "^5.0" + "orchestra/testbench": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "phpunit/phpunit": "^6.0 || ^7.0 || ^8.0 || ^9.0" }, "type": "library", "extra": { @@ -8424,10 +8427,14 @@ { "name": "Jake Bathman", "email": "jake@tighten.co" + }, + { + "name": "Jacob Baker-Kretzmar", + "email": "jacob@tighten.co" } ], "description": "Generates a Blade directive exporting all of your named Laravel routes. Also provides a nice route() helper function in JavaScript.", - "homepage": "https://github.com/tightenco/ziggy", + "homepage": "https://github.com/tighten/ziggy", "keywords": [ "Ziggy", "javascript", @@ -8436,9 +8443,9 @@ ], "support": { "issues": "https://github.com/tighten/ziggy/issues", - "source": "https://github.com/tighten/ziggy/tree/0.9.4" + "source": "https://github.com/tighten/ziggy/tree/v1.4.6" }, - "time": "2020-06-05T14:42:41+00:00" + "time": "2022-04-08T15:12:23+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", diff --git a/config/cerberus.php b/config/cerberus.php index b4649619..bdc31d4d 100644 --- a/config/cerberus.php +++ b/config/cerberus.php @@ -141,5 +141,13 @@ 'slug' => 'settings-edit_voice', 'description' => 'a user can edit voice provider settings' ], + [ + 'slug' => 'settings-cp_update_check', + 'description' => 'a user can check if an update of the control panel is available' + ], + [ + 'slug' => 'settings-cp_upgrade', + 'description' => 'a user can initiate the upgrade process for the control panel.' + ], ] ]; diff --git a/config/filesystems.php b/config/filesystems.php index 68a17b3b..a6637c5f 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -77,6 +77,13 @@ 'root' => storage_path('bridge'), 'visibility' => 'private', ], + + 'launcher' => [ + 'driver' => 'local', + 'root' => storage_path('launcher'), + 'url' => env('APP_URL').'/launcher', + 'visibility' => 'public', + ], ], /* @@ -93,6 +100,7 @@ 'links' => [ public_path('storage') => storage_path('app/public'), public_path('modpacks-data') => storage_path('app/modpacks'), + public_path('launcher') => storage_path('launcher'), ], ]; diff --git a/database/seeders/ServerSeeder.php b/database/seeders/ServerSeeder.php index 285a109f..70edc12f 100644 --- a/database/seeders/ServerSeeder.php +++ b/database/seeders/ServerSeeder.php @@ -22,8 +22,8 @@ public function run() $minecraft = Game::whereIdentifier('minecraft')->first(); $server = Server::factory()->create([ - 'name' => 'GrandTheftArma', - 'ip' => 's1.grandtheftarma.com', + 'name' => 'LIVEYOURLIFE - Altis', + 'ip' => 'altis.lyl.gg', 'port' => 2302, 'game_id' => $arma3->id ]); diff --git a/package.json b/package.json index bf996247..33cf4747 100644 --- a/package.json +++ b/package.json @@ -13,60 +13,67 @@ "⚠️ The project will not build unless you specifically set the version of postcss-loader to >= 4.1.0" ], "devDependencies": { - "@inertiajs/inertia": "^0.10.1", - "@inertiajs/inertia-vue": "^0.7.2", - "@inertiajs/progress": "^0.2.6", - "@tailwindcss/aspect-ratio": "^0.2.0", - "@tailwindcss/forms": "^0.2.1", - "@tailwindcss/typography": "^0.3.1", + "@inertiajs/inertia": "^0.11.0", + "@inertiajs/inertia-vue": "^0.8.0", + "@inertiajs/progress": "^0.2.7", + "@tailwindcss/aspect-ratio": "^0.4.0", + "@tailwindcss/forms": "^0.5.2", + "@tailwindcss/typography": "^0.5.3", "@types/chart.js": "^2.9.30", "@types/lodash": "^4.14.165", "@types/marked": "^1.2.1", "@types/qs": "^6.9.5", - "@types/ziggy-js": "^0.9.0", - "@typescript-eslint/eslint-plugin": "^4.14.0", - "@typescript-eslint/parser": "^4.14.2-alpha.1", - "@vue/cli-service": "^4.5.10", - "@vue/eslint-config-airbnb": "^5.3.0", - "@vue/eslint-config-typescript": "^7.0.0", - "autoprefixer": "^10.1.0", + "@types/ziggy-js": "^1.3.2", + "@typescript-eslint/eslint-plugin": "^5.30.5", + "@typescript-eslint/parser": "^5.30.5", + "@vue/cli-service": "^5.0.8", + "@vue/eslint-config-airbnb": "^6.0.0", + "@vue/eslint-config-typescript": "^9.1.0", + "autoprefixer": "^10.4.7", "axios": "^0.21.1", "chart.js": "2.9.3", "collect.js": "^4.28.6", "cross-env": "^7.0", "css-loader": "^5.0.1", "eslint": "^7.18.0", - "eslint-plugin-import": "^2.18.2", - "eslint-plugin-vue": "^7.0.0", - "eslint-webpack-plugin": "^2.4.1", - "fork-ts-checker-notifier-webpack-plugin": "^4.0.0", - "fork-ts-checker-webpack-plugin": "^6.1.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-vue": "^8.7.1", + "eslint-plugin-vuejs-accessibility": "^1.1.0", + "eslint-webpack-plugin": "^3.0.0", + "fork-ts-checker-notifier-webpack-plugin": "^6.0.0", + "fork-ts-checker-webpack-plugin": "^7.2.11", "fuse.js": "^6.4.6", "laravel-echo": "^1.9.0", - "laravel-mix": "^6.0.0", + "laravel-mix": "^6.0.49", "lodash": "^4.17.19", "marked": "^1.2.7", "moment": "^2.29.1", "moment-timezone": "^0.5.32", "portal-vue": "^2.1.7", - "postcss": "^8.2.1", + "postcss": "^8.4.14", "postcss-loader": "^4.1.0", "pusher-js": "^7.0.1", "qs": "^6.9.4", "resolve-url-loader": "^3.1.2", "sass": "^1.32.6", "sass-loader": "^10.1.1", - "tailwindcss": "^2.2.7", + "tailwindcss": "^3.1.5", "ts-loader": "^8.0.12", "typescript": "^4.1.3", "v-click-outside": "^3.1.2", - "vue": "^2.6.14", + "vue": "^2.7.4", "vue-class-component": "^7.2.6", - "vue-loader": "^15.9.6", + "vue-loader": "^15.10.0", "vue-lodash": "^2.1.2", - "vue-property-decorator": "^9.0.2", - "vue-template-compiler": "^2.6.14", + "vue-property-decorator": "^9.1.2", + "vue-template-compiler": "^2.7.4", "webpack": "^5.11.0", - "ziggy-js": "^1.3.5" + "ziggy-js": "^1.4.6" + }, + "resolutions": { + "vue-loader": "^15.10.0" + }, + "dependencies": { + "retry-axios": "^3.0.0" } } diff --git a/resources/css/app.css b/resources/css/app.css index 09da47cf..03e3bb2b 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -3,54 +3,50 @@ @tailwind utilities; @layer components { - @variants dark { - .form-input { - @apply border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:border-transparent - dark:bg-gray-900 dark:text-gray-100 dark:border-gray-700 dark:placeholder-gray-600; - } + .form-input { + @apply border border-zinc-300 focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:border-transparent + dark:bg-zinc-900 dark:text-zinc-100 dark:border-zinc-700 dark:placeholder-zinc-600; + } - .form-checkbox { - @apply rounded-md p-3 border border-gray-300 bg-gray-50 dark:bg-gray-900 dark:checked:bg-indigo-400 dark:border-gray-700 checked:border-transparent - focus:outline-none focus:ring-indigo-300 focus:ring-offset-0 hover:cursor-pointer; - } + .form-checkbox { + @apply rounded-md p-3 border border-zinc-300 bg-zinc-50 dark:bg-zinc-900 dark:checked:bg-indigo-400 dark:border-zinc-700 checked:border-transparent + focus:outline-none focus:ring-indigo-300 focus:ring-offset-0 hover:cursor-pointer; + } - .link-brand { - @apply text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300 focus:outline-none; - } + .link-brand { + @apply text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300 focus:outline-none; + } - .link-success { - @apply text-emerald-600 hover:text-emerald-900 dark:text-emerald-500 dark:hover:text-emerald-400 focus:outline-none; - } + .link-success { + @apply text-emerald-600 hover:text-emerald-900 dark:text-emerald-500 dark:hover:text-emerald-400 focus:outline-none; + } - .link-danger { - @apply text-red-600 hover:text-red-900 dark:text-red-500 dark:hover:text-red-400 focus:outline-none; - } + .link-danger { + @apply text-red-600 hover:text-red-900 dark:text-red-500 dark:hover:text-red-400 focus:outline-none; + } - .breadcrumb { - @apply font-semibold text-xl text-gray-800 dark:text-gray-100 leading-tight; - } + .breadcrumb { + @apply font-semibold text-xl text-zinc-800 dark:text-zinc-100 leading-tight; + } - .breadcrumb-separator { - @apply text-gray-400 dark:text-gray-500; - } + .breadcrumb-separator { + @apply text-zinc-400 dark:text-zinc-500; } } @layer utilities { - @variants responsive { - .h-page { - height: calc(100vh - 14.6rem); - max-height: 30rem; - } + .h-page { + height: calc(100vh - 14.6rem); + max-height: 30rem; + } - .w-page { - width: 80rem; - max-width: 80rem; - } + .w-page { + width: 80rem; + max-width: 80rem; + } - .w-prose { - width: calc(65ch + 1.5rem) - } + .w-prose { + width: calc(65ch + 1.5rem) } } @@ -58,17 +54,17 @@ body { --scrollbarBG: theme('colors.white'); - --thumbBG: theme('colors.gray.300'); - --thumbBG_Hover: theme('colors.gray.400'); - --scrollbarBG_Hover: theme('colors.gray.50'); + --thumbBG: theme('colors.zinc.300'); + --thumbBG_Hover: theme('colors.zinc.400'); + --scrollbarBG_Hover: theme('colors.zinc.50'); --scrollbarBG_Shadow: none; } body.dark { - --scrollbarBG: theme('colors.gray.800'); - --thumbBG: theme('colors.gray.700'); - --thumbBG_Hover: theme('colors.gray.600'); - --scrollbarBG_Hover: theme('colors.gray.600'); + --scrollbarBG: theme('colors.zinc.800'); + --thumbBG: theme('colors.zinc.700'); + --thumbBG_Hover: theme('colors.zinc.600'); + --scrollbarBG_Hover: theme('colors.zinc.600'); --scrollbarBG_Shadow: inset 0 0 6px rgba(0, 0, 0, 0.5); } diff --git a/resources/js/Jetstream/ActionMessage.vue b/resources/js/Jetstream/ActionMessage.vue index aa399622..1d8810e1 100644 --- a/resources/js/Jetstream/ActionMessage.vue +++ b/resources/js/Jetstream/ActionMessage.vue @@ -7,7 +7,7 @@ >
diff --git a/resources/js/Jetstream/ActionSection.vue b/resources/js/Jetstream/ActionSection.vue index ad145bce..ee83b184 100644 --- a/resources/js/Jetstream/ActionSection.vue +++ b/resources/js/Jetstream/ActionSection.vue @@ -10,7 +10,7 @@
-
+
diff --git a/resources/js/Jetstream/Button.vue b/resources/js/Jetstream/Button.vue index e6541d64..c98e0ebe 100644 --- a/resources/js/Jetstream/Button.vue +++ b/resources/js/Jetstream/Button.vue @@ -3,11 +3,11 @@ @click="$emit('click', $event)" :disabled="disabled" :type="type" - class="disabled:cursor-not-allowed disabled:opacity-75 inline-flex items-center px-4 py-2 bg-gray-800 border - border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 - focus:outline-none focus:border-gray-900 focus:ring focus:ring-gray-400 transition ease-in-out duration-150 - dark:text-gray-100 dark:bg-indigo-500 dark:hover:bg-indigo-400 dark:active:bg-indigo-500 dark:focus:border-indigo-400 - dark:focus:ring-indigo-400 dark:disabled:bg-gray-700" + class="disabled:cursor-not-allowed disabled:opacity-75 inline-flex items-center px-4 py-2 bg-zinc-800 border + border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-zinc-700 active:bg-zinc-900 + focus:outline-none focus:border-zinc-900 focus:ring focus:ring-zinc-400 transition ease-in-out duration-150 + dark:text-zinc-100 dark:bg-indigo-500 dark:hover:bg-indigo-400 dark:active:bg-indigo-500 dark:focus:border-indigo-400 + dark:focus:ring-indigo-400 dark:disabled:bg-zinc-700" > diff --git a/resources/js/Jetstream/ConfirmationModal.vue b/resources/js/Jetstream/ConfirmationModal.vue index dcf5260d..60ca59aa 100644 --- a/resources/js/Jetstream/ConfirmationModal.vue +++ b/resources/js/Jetstream/ConfirmationModal.vue @@ -5,7 +5,7 @@ :closeable="closeable" @close="close" > -
+
-
+
diff --git a/resources/js/Jetstream/ConfirmsPassword.vue b/resources/js/Jetstream/ConfirmsPassword.vue index 9911a237..dceaa397 100644 --- a/resources/js/Jetstream/ConfirmsPassword.vue +++ b/resources/js/Jetstream/ConfirmsPassword.vue @@ -95,8 +95,7 @@ export default class ConfirmsPassword extends Mixins(Route) { startConfirmingPassword() { this.form.error = '' - axios.get(this.route('password.confirmation') - .url()) + axios.get(this.route('password.confirmation')) .then((response) => { if (response.data.confirmed) { this.$emit('confirmed') @@ -114,10 +113,12 @@ export default class ConfirmsPassword extends Mixins(Route) { confirmPassword() { this.form.processing = true - axios.post(this.route('password.confirm') - .url(), { - password: this.form.password, - }) + axios.post( + this.route('password.confirm'), + { + password: this.form.password, + }, + ) // eslint-disable-next-line no-unused-vars .then((response) => { this.confirmingPassword = false diff --git a/resources/js/Jetstream/DangerButton.vue b/resources/js/Jetstream/DangerButton.vue index b700f0e5..8207bde8 100644 --- a/resources/js/Jetstream/DangerButton.vue +++ b/resources/js/Jetstream/DangerButton.vue @@ -4,7 +4,7 @@ class="inline-flex items-center justify-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 focus:outline-none focus:border-red-700 focus:ring focus:ring-red-400 active:bg-red-600 transition ease-in-out duration-150 dark:bg-red-800 dark:hover:bg-red-700 - dark:focus:ring-red-500 dark:text-gray-100" + dark:focus:ring-red-500 dark:text-zinc-100" > diff --git a/resources/js/Jetstream/DialogModal.vue b/resources/js/Jetstream/DialogModal.vue index 43530062..e5c1fa7c 100644 --- a/resources/js/Jetstream/DialogModal.vue +++ b/resources/js/Jetstream/DialogModal.vue @@ -5,17 +5,17 @@ :closeable="closeable" @close="close" > -
-
+
+
-
+
-
+
diff --git a/resources/js/Jetstream/Dropdown.vue b/resources/js/Jetstream/Dropdown.vue index 8b6be962..551870b1 100644 --- a/resources/js/Jetstream/Dropdown.vue +++ b/resources/js/Jetstream/Dropdown.vue @@ -49,7 +49,7 @@ const widths: any = { export default class Dropdown extends Vue { open: boolean = false - contentClasses: any = ['py-1', 'bg-white', 'dark:bg-gray-700'] + contentClasses: any = ['py-1', 'bg-white', 'dark:bg-zinc-700'] align: any = 'right' diff --git a/resources/js/Jetstream/DropdownLink.vue b/resources/js/Jetstream/DropdownLink.vue index aba03b81..7f6f1122 100644 --- a/resources/js/Jetstream/DropdownLink.vue +++ b/resources/js/Jetstream/DropdownLink.vue @@ -3,8 +3,8 @@ @@ -12,8 +12,8 @@ diff --git a/resources/js/Jetstream/FormSection.vue b/resources/js/Jetstream/FormSection.vue index 90df5493..e636c720 100644 --- a/resources/js/Jetstream/FormSection.vue +++ b/resources/js/Jetstream/FormSection.vue @@ -12,7 +12,7 @@
-
+
@@ -20,7 +20,7 @@
diff --git a/resources/js/Jetstream/Label.vue b/resources/js/Jetstream/Label.vue index 6c072ee1..3bafd214 100644 --- a/resources/js/Jetstream/Label.vue +++ b/resources/js/Jetstream/Label.vue @@ -1,5 +1,5 @@