多🌹444收拾收拾计算机视觉手机手机手机手机4大尺寸惆怅长岑长 ??????????????????????????С??????????????P??????????????????????????????????????????????????????^?????????? ????j?????????? ? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!magicai-updater/phpunit.xml.dist000064400000001602152665571560012774 0ustar00 tests ./src magicai-updater/README.md000064400000005540152665571560011105 0ustar00# Magic ai updater [![Latest Version on Packagist](https://img.shields.io/packagist/v/magicai/magicai-updater.svg?style=flat-square)](https://packagist.org/packages/magicai/magicai-updater) [![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/magicai/magicai-updater/run-tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/magicai/magicai-updater/actions?query=workflow%3Arun-tests+branch%3Amain) [![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/magicai/magicai-updater/fix-php-code-style-issues.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/magicai/magicai-updater/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain) [![Total Downloads](https://img.shields.io/packagist/dt/magicai/magicai-updater.svg?style=flat-square)](https://packagist.org/packages/magicai/magicai-updater) This is where your description should go. Limit it to a paragraph or two. Consider adding a small example. ## Support us [](https://spatie.be/github-ad-click/magicai-updater) We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us). We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards). ## Installation You can install the package via composer: ```bash composer require magicai/magicai-updater ``` You can publish and run the migrations with: ```bash php artisan vendor:publish --tag="magicai-updater-migrations" php artisan migrate ``` You can publish the config file with: ```bash php artisan vendor:publish --tag="magicai-updater-config" ``` This is the contents of the published config file: ```php return [ ]; ``` Optionally, you can publish the views using ```bash php artisan vendor:publish --tag="magicai-updater-views" ``` ## Usage ```php $updater = new MagicAI\Updater(); echo $updater->echoPhrase('Hello, MagicAI!'); ``` ## Testing ```bash composer test ``` ## Changelog Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. ## Contributing Please see [CONTRIBUTING](CONTRIBUTING.md) for details. ## Security Vulnerabilities Please review [our security policy](../../security/policy) on how to report security vulnerabilities. ## Credits - [Rauf Abbaszade](https://github.com/rafoabbas) - [All Contributors](../../contributors) ## License The MIT License (MIT). Please see [License File](LICENSE.md) for more information. magicai-updater/.editorconfig000064400000000334152665571560012277 0ustar00root = true [*] charset = utf-8 end_of_line = lf indent_size = 4 indent_style = space insert_final_newline = true trim_trailing_whitespace = true [*.md] trim_trailing_whitespace = false [*.{yml,yaml}] indent_size = 2 magicai-updater/phpstan-baseline.neon000064400000000000152665571560013726 0ustar00magicai-updater/src/Controllers/UpdaterController.php000064400000012357152665571560017110 0ustar00 true, 'data' => Updater::checker(), 'user' => $request->user(), ]); } public function check(): array { return Updater::checker(); } public function update(Request $request): RedirectResponse { Updater::downloadNewUpdater(); return back()->with([ 'message' => trans('The updater has been successfully downloaded.'), 'type' => 'success', ]); } public function backup(Request $request): \Illuminate\Contracts\View\Factory|\Illuminate\Foundation\Application|View|\Illuminate\View\View|RedirectResponse|\Illuminate\Contracts\Foundation\Application { $data = Updater::backupView(); if ($data['updated']) { return redirect()->route('updater.index')->with([ 'message' => 'MagicAI is already up to date.', 'type' => 'success', ]); } if ($request->isMethod('get') && ! Updater::isLastBackupRecent()) { return redirect()->route('updater.index')->with([ 'message' => trans('The backup file could not be found. Please try again.'), 'type' => 'success', ]); } if ($request->isMethod('post') && ! Updater::isLastBackupRecent()) { Updater::backup(); } return view('magicai-updater::index', [ 'permission' => true, 'data' => Updater::backupView(), 'fileName' => Updater::findLastBackup(), ]); } public function upgrade(Request $request): \Illuminate\Foundation\Application|\Illuminate\Http\Response|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory { $backupFileName = Updater::findLastBackup(); if (file_exists(base_path($backupFileName))) { Updater::updateNewVersion($backupFileName); return response([ 'message' => trans('Upgrade completed successfully.'), 'type' => 'success', ], 200); } return response([ 'message' => 'The backup file could not be found. Please try again.', 'type' => 'success', ], 422); } public function downloadStep(): \Illuminate\Contracts\View\Factory|\Illuminate\Foundation\Application|View|\Illuminate\View\View|RedirectResponse|\Illuminate\Contracts\Foundation\Application { $data = Updater::downloadView(); $backupFileName = Updater::findLastBackup(); if (! file_exists(base_path($backupFileName))) { return redirect()->route('updater.index')->with([ 'message' => 'The backup file could not be found. Please try again.', 'type' => 'error', ]); } if ($data['updated']) { return redirect()->route('updater.index')->with([ 'message' => 'MagicAI is already up to date.', 'type' => 'success', ]); } if (! $data['isDownload']) { Updater::downloadNewVersion(); } return view('magicai-updater::index', [ 'permission' => true, 'data' => $data, 'fileName' => 'new-version' . Updater::getDownloadVersion() . '.zip', ]); } public function download(Request $request): RedirectResponse { $backupFileName = Updater::findLastBackup(); if (file_exists(base_path($backupFileName))) { Updater::downloadNewVersion(); return to_route('updater.download-step')->with([ 'type' => 'success', 'message' => 'The download file has been successfully downloaded.', ]); } throw ValidationException::withMessages([ 'message' => 'The backup file could not be found. Please try again.', ]); } public function versionCheck(): JsonResponse { $versionCheck = Updater::versionCheck(); if ($versionCheck) { try { Artisan::call('migrate', ['--force' => true]); InstallationHelper::runInstallation(); Artisan::call('optimize:clear'); } catch (Exception $e) { return response()->json([ 'message' => $e->getMessage(), 'updated' => $versionCheck, ]); } } return response()->json([ 'message' => $versionCheck ? 'MagicAI is already up to date.' : 'MagicAI don\'t updated.', 'updated' => $versionCheck, ]); } public function forPanel(): JsonResponse { return response()->json(Updater::forPanel()); } } magicai-updater/src/Facades/Updater.php000064400000001136152665571560014055 0ustar00blacklist(); if ($blackList) { throw ValidationException::withMessages([ 'message' => 'Please try again later!', ]); } $version = $this->nextVersion; $downloadUrl = config('magicai-updater.base_url') . $this->versionZipFile; try { $downloadFile = $this->download($downloadUrl, 'new-version' . $version . '.zip'); cache()->remember($this->downloadFileCacheKey, now()->addMinutes(30), function () use ($downloadFile, $version) { return [ 'file' => $downloadFile, 'version' => $version, ]; }); } catch (Exception $exception) { Log::error($exception->getMessage()); throw ValidationException::withMessages([ 'message' => $exception->getMessage(), ]); } } public function getDownloadVersion(): ?string { $array = cache($this->downloadFileCacheKey) ?: []; return data_get($array, 'version'); } public function updateNewVersion(string $backupFileName): bool { set_time_limit(0); // unlimited max execution time ini_set('memory_limit', '-1'); // increase memory_limit to 1GB $blackList = app(ExtensionRepositoryInterface::class)->blacklist(); if ($blackList) { throw ValidationException::withMessages([ 'message' => 'Please try again later!', ]); } $version = $this->nextVersion; $downloadFile = data_get(cache($this->downloadFileCacheKey), 'file'); if (! $downloadFile || ! file_exists($downloadFile)) { throw ValidationException::withMessages([ 'message' => 'The download file could not be found. Please try again.', ]); } try { if (File::exists(base_path('bootstrap/cache/packages.php'))) { File::delete(base_path('bootstrap/cache/packages.php')); } if (File::exists(base_path('bootstrap/cache/services.php'))) { File::delete(base_path('bootstrap/cache/services.php')); } Artisan::call('optimize:clear'); DB::beginTransaction(); Artisan::call('down'); $this->unzip($downloadFile); $this->migrate(); $this->updateVersion($version); InstallationHelper::runInstallation(); Artisan::call('up'); DB::commit(); } catch (InvalidURLException|ZipException|RuntimeException $e) { Log::error($e->getMessage()); DB::rollBack(); $this->rollbackBackup($backupFileName); Artisan::call('up'); throw ValidationException::withMessages([ 'message' => $e->getMessage(), ]); } return true; } private function rollbackBackup(string $backupFileName): void { if (! File::exists(base_path($backupFileName))) { return; } $this->unzip(base_path($backupFileName)); } private function updateVersion(string $version): void { $setting = Setting::getCache(); $setting->script_version = $version; $setting->save(); File::put(base_path('version.txt'), $version); } private function migrate(): void { Artisan::call('migrate', ['--force' => true]); } } magicai-updater/src/Traits/HasBackup.php000064400000011311152665571560014226 0ustar00backupFileNameCacheKey; // } public function backup(): bool { $this->configurePhp(); Artisan::call('optimize:clear'); $fileName = $this->backupFileName(); $zipName = $this->backupFilePath($fileName); $zip = new ZipArchive; try { if ($zip->open($zipName, ZipArchive::CREATE) === true) { $this->addFolderToZip(base_path(), $zip); $zip->close(); } } catch (Exception $e) { \Illuminate\Support\Facades\Log::error('Backup failed: ', [ 'message' => $e->getMessage(), ]); Cache::forget($this->backupFileNameCacheKey); throw ValidationException::withMessages([ 'message' => __('Server Error:') . ' ' . $e->getMessage(), ]); } return $fileName; } public function exceptFolder(string $folder): bool { $exceptArray = array_map(static function ($item) { return base_path($item); }, $this->excepts); if (in_array($folder, $exceptArray, true)) { return true; } return false; } private function exceptFile(string $file): bool { if ($file === '.' || $file === '..') { return true; } // except .zip files if (pathinfo($file, PATHINFO_EXTENSION) === 'zip') { return true; } return false; } private function addFolderToZip($folder, ZipArchive $zip, $parentFolder = ''): void { if ($this->exceptFolder($folder)) { return; } $files = scandir($folder); foreach ($files as $file) { if ($this->exceptFile($file)) { continue; } $filePath = $folder . DIRECTORY_SEPARATOR . $file; $relativePath = $parentFolder ? $parentFolder . DIRECTORY_SEPARATOR . $file : $file; if (is_dir($filePath)) { $zip->addEmptyDir($relativePath); $this->addFolderToZip($filePath, $zip, $relativePath); } else { if (is_file($filePath)) { $zip->addFile($filePath, $relativePath); } } } } private function backupFilePath(string $fileName): string { return base_path($fileName); } private function backupFileName(): string { return 'backup-' . date('Y-m-d_H-i') . '.zip'; } public function isLastBackupRecent(int $minutes = 30): bool { $lastBackup = $this->findLastBackup(); if ($lastBackup === null) { return false; } // backup-2025-05-28_09-17.zip → 2025-05-28 09:17 if (preg_match('/backup-(\d{4}-\d{2}-\d{2})_(\d{2}-\d{2})\.zip/', $lastBackup, $matches)) { $backupDateTime = Carbon::createFromFormat('Y-m-d H-i', "{$matches[1]} {$matches[2]}"); return $backupDateTime->greaterThanOrEqualTo(now()->subMinutes($minutes)); } return false; } public function findLastBackup(): ?string { $backupFiles = glob(base_path('/backup-*.zip')); if (! empty($backupFiles)) { usort($backupFiles, function ($a, $b) { return strcmp(basename($b), basename($a)); }); $latestBackup = $backupFiles[0]; return basename($latestBackup); } return null; } // public function backupFileNameGetFromCache() // { // return Cache::get($this->backupFileNameCacheKey); // } private function configurePhp(): void { // unlimited max execution time set_time_limit(0); // increase memory_limit to 1GB ini_set('memory_limit', '-1'); // increase max_execution_time to 1 hour ini_set('max_execution_time', 3600); } } magicai-updater/src/Traits/HasUpdater.php000064400000003034152665571560014430 0ustar00blacklist(); if ($blackList) { throw ValidationException::withMessages([ 'message' => 'Please try again later!', ]); } $updaterDownloadUrl = config('magicai-updater.updater_download_url'); try { $this->download($updaterDownloadUrl, 'updater.php.zip'); $this->unzip($this->path); } catch (InvalidURLException $e) { throw ValidationException::withMessages([ 'message' => __($e->getMessage()), ]); } catch (ZipException $e) { throw ValidationException::withMessages([ 'message' => __('Something went wrong!'), ]); } } public function currentUpdater(): array { $data = []; if (file_exists(base_path('updater.php'))) { $data = require base_path('updater.php'); } return [ 'version' => $data['version'] ?? '0.0.0', 'list' => $data['list'] ?? null, ]; } public function newUpdaterVersion(): ?string { return $this->json('updater_version'); } } magicai-updater/src/Traits/HasVersionPrepare.php000064400000005552152665571560015777 0ustar00versionRequest()->json() ?: []; $currentMagicAIVersion = floatval($this->currentMagicAIVersion()); $description = $lastVersion['description'] ?? []; $versions = $this->versions($description); $this->nextVersion = $this->setNextVersion($versions, $currentMagicAIVersion); $this->latestVersion = $this->setLatestVersion($versions, $currentMagicAIVersion); $this->setZipFile(); if (VersionComparator::compareVersion($this->nextVersion, $currentMagicAIVersion, '>')) { $lastVersion['update'] = 'yes'; // Trigger the new version available. $lastVersion['version'] = $this->nextVersion; $lastVersion['latest_version'] = format_double($this->latestVersion); $lastVersion['version_format'] = format_double($this->nextVersion); $lastVersion['archive'] = $this->versionZipFile; $lastVersion['current_version'] = format_double($currentMagicAIVersion); } return $lastVersion; } public function setZipFile(): string { $this->versionZipFile = 'magicai_' . $this->nextVersion . '.zip'; return $this->versionZipFile; } public function setNextVersion(array $versions, string $currentMagicAIVersion): string { $versions = array_reverse($versions); // 5.3, 5.31, 5.4, 5.41 foreach ($versions as $version) { if ($version > $currentMagicAIVersion) { return number_format($version, 2, '.'); } } return ''; } public function setLatestVersion(array $versions, string $currentMagicAIVersion): string { $versions = array_reverse($versions); // 5.3, 5.31, 5.4, 5.41 $latestVersion = end($versions); if ($latestVersion > $currentMagicAIVersion) { return number_format($latestVersion, 2, '.'); } return ''; } public function versions(array $array = []): array { $versionList = array_map(function ($key) { return trim(explode('-', $key)[0]); }, array_keys($array)); $formatted = array_map(function ($version) { $parts = explode('.', $version); if (count($parts) === 3) { return (float) ($parts[0] . '.' . $parts[1] . $parts[2]); } if (count($parts) === 2) { return (float) ($parts[0] . '.' . $parts[1]); } return (float) ($parts[0]); }, $versionList); return $formatted; } } magicai-updater/src/Traits/HasZipper.php000064400000000761152665571560014301 0ustar00open($zipFile) === true) { $zip->extractTo($destination); $zip->close(); } else { throw new ZipException('Invalid zip file'); } } } magicai-updater/src/Traits/HasDownloader.php000064400000003026152665571560015123 0ustar00path = base_path($filename); try { // Download the file from the URL $response = Http::timeout(1800) ->withOptions([ 'sink' => $this->path, ]) ->throw(function ($request, $exception) { throw new RuntimeException('Failed to download the zip file'); }) ->get($url); // If the request is unsuccessful, throw an exception if (! $response->successful()) { throw new RuntimeException('Failed to download the zip file'); } } catch (Exception|RuntimeException $exception) { throw new RuntimeException('Failed to download the zip file'); } // Save the file content to the base_path() directory return $this->path; } } magicai-updater/src/Commands/UpdaterCommand.php000064400000000475152665571560015574 0ustar00comment('All done'); return self::SUCCESS; } } magicai-updater/src/UpdaterServiceProvider.php000064400000003625152665571560015570 0ustar00name('magicai-updater') ->hasConfigFile() ->hasViews() ->hasViewComponents('updater', Permission::class, Li::class, Button::class) ->hasCommand(UpdaterCommand::class); } public function packageRegistered(): void { Route::prefix('updater') ->as('updater.') ->middleware(['web', 'auth', 'admin']) ->group(function (Router $router) { $router->get('', [UpdaterController::class, 'index'])->name('index'); $router->get('check', [UpdaterController::class, 'check'])->name('check'); $router->post('update', [UpdaterController::class, 'update'])->name('update'); $router->any('backup', [UpdaterController::class, 'backup'])->name('backup'); $router->any('download-step', [UpdaterController::class, 'downloadStep'])->name('download-step'); $router->any('download', [UpdaterController::class, 'download'])->name('download'); $router->any('upgrade', [UpdaterController::class, 'upgrade'])->name('upgrade'); $router->get('for-panel', [UpdaterController::class, 'forPanel'])->name('for.panel'); $router->get('version-check', [UpdaterController::class, 'versionCheck'])->name('version-check'); }); } } magicai-updater/src/Updater.php000064400000011102152665571560012521 0ustar00prepareVersion(); } public function versionCheck(): bool|int { $magicAIVersion = cache()->remember('magicai_next_version_cache', 60 * 10, function () { return $this->nextVersion; }); if (! $magicAIVersion) { $versions = $this->prepareVersion(); $magicAIVersion = $versions['version']; } $currentMagicAIVersion = $this->currentMagicAIVersion(); return VersionComparator::compareVersion($magicAIVersion, $currentMagicAIVersion, '='); } public function backupView(): array { $magicAIVersion = $this->nextVersion; $currentMagicAIVersion = $this->currentMagicAIVersion(); if (VersionComparator::compareVersion($magicAIVersion, $currentMagicAIVersion, '=')) { return [ 'updated' => true, ]; } return [ 'updated' => false, 'title' => trans('MagicAI installed successfully'), 'version' => $this->nextVersion, 'view' => 'magicai-updater::particles.backup', 'step' => 4, ]; } public function downloadView(): array { $magicAIVersion = $this->nextVersion; $currentMagicAIVersion = $this->currentMagicAIVersion(); if (VersionComparator::compareVersion($magicAIVersion, $currentMagicAIVersion, '=')) { return [ 'updated' => true, ]; } return [ 'updated' => false, 'title' => trans('MagicAI installed successfully'), 'version' => $magicAIVersion, 'isDownload' => $magicAIVersion === $this->getDownloadVersion(), 'view' => 'magicai-updater::particles.download', 'step' => 4, ]; } public function checker(): array { $updaterVersion = $this->newUpdaterVersion(); $currentUpdater = $this->currentUpdater(); $magicAIVersion = $this->nextVersion; $currentMagicAIVersion = $this->currentMagicAIVersion(); if (VersionComparator::compareVersion($magicAIVersion, $currentMagicAIVersion, '=')) { cache()->forget('magicai_next_version_cache'); return [ 'title' => trans('MagicAI installed successfully'), 'version' => $magicAIVersion, 'view' => 'magicai-updater::particles.updated', ]; } if ($updaterVersion && VersionComparator::compareVersion($updaterVersion, $currentUpdater['version'], '=')) { cache()->forget('magicai_next_version_cache'); return [ 'title' => trans('MagicAI is ready to update'), 'updater' => $this->currentUpdater(), 'version' => $magicAIVersion, 'updater_version' => $this->json('updater_version'), 'view' => 'magicai-updater::particles.update', 'step' => 2, ]; } return [ 'title' => trans('MagicAI is ready to download check for updates'), 'version' => $magicAIVersion, 'updater_version' => $this->json('updater_version'), 'view' => 'magicai-updater::particles.updater', 'step' => 1, ]; } public function forPanel(): array { return $this->prepareVersion(); } public function currentMagicAIVersion(): false|string { $this->currentMagicAIVersion = trim(File::get(base_path('version.txt'))); return $this->currentMagicAIVersion; } public function json(string $key): null|string|array { return $this->versionRequest()->json($key); } public function versionRequest(): Response { return once(static function () { return Http::get(config('magicai-updater.version_url')); }); } } magicai-updater/src/View/Components/Button.php000064400000001034152665571560015432 0ustar00 $this->permission, 'text' => $this->text, 'id' => $this->id, ]); } } magicai-updater/src/View/Components/Permission.php000064400000000605152665571560016312 0ustar00 $this->permission, ]); } } magicai-updater/src/View/Components/Li.php000064400000000562152665571560014530 0ustar00 $this->item, ]); } } magicai-updater/phpstan.neon.dist000064400000000331152665571560013117 0ustar00includes: - phpstan-baseline.neon parameters: level: 5 paths: - src - config - database tmpDir: build/phpstan checkOctaneCompatibility: true checkModelProperties: true magicai-updater/CHANGELOG.md000064400000000127152665571560011433 0ustar00# Changelog All notable changes to `magicai-updater` will be documented in this file. magicai-updater/composer.json000064400000004773152665571560012357 0ustar00{ "name": "magicai/magicai-updater", "description": "Magic ai updater", "keywords": [ "MagicAI", "laravel", "magicai-updater" ], "homepage": "https://github.com/magicai/magicai-updater", "license": "MIT", "authors": [ { "name": "MagicAI Team", "email": "info@liquid-themes.com", "role": "Developer" } ], "require": { "php": "^8.2", "ext-zip": "*", "illuminate/contracts": "^10.0||^11.0", "ryangjchandler/blade-tabler-icons": "^2.2", "spatie/laravel-package-tools": "^1.16" }, "require-dev": { "laravel/pint": "^1.14", "nunomaduro/collision": "^8.1.1||^7.10.0", "larastan/larastan": "^2.9", "orchestra/testbench": "^9.0.0||^8.22.0", "pestphp/pest": "^2.34", "pestphp/pest-plugin-arch": "^2.7", "pestphp/pest-plugin-laravel": "^2.3", "phpstan/extension-installer": "^1.3", "phpstan/phpstan-deprecation-rules": "^1.1", "phpstan/phpstan-phpunit": "^1.3", "spatie/laravel-ray": "^1.35" }, "autoload": { "psr-4": { "MagicAI\\Updater\\": "src/", "MagicAI\\Updater\\Database\\Factories\\": "database/factories/" } }, "autoload-dev": { "psr-4": { "MagicAI\\Updater\\Tests\\": "tests/" } }, "scripts": { "post-autoload-dump": "@composer run prepare", "clear": "@php vendor/bin/testbench package:purge-magicai-updater --ansi", "prepare": "@php vendor/bin/testbench package:discover --ansi", "build": [ "@composer run prepare" ], "start": [ "Composer\\Config::disableProcessTimeout", "@composer run build", "@php vendor/bin/testbench serve" ], "analyse": "vendor/bin/phpstan analyse", "test": "vendor/bin/pest", "test-coverage": "vendor/bin/pest --coverage", "format": "vendor/bin/pint" }, "config": { "sort-packages": true, "allow-plugins": { "pestphp/pest-plugin": true, "phpstan/extension-installer": true } }, "extra": { "laravel": { "providers": [ "MagicAI\\Updater\\UpdaterServiceProvider" ], "aliases": { "Updater": "MagicAI\\Updater\\Facades\\Updater" } } }, "minimum-stability": "dev", "prefer-stable": true } magicai-updater/.gitignore000064400000000520152665571560011607 0ustar00# Composer Related composer.lock /vendor # Frontend Assets /node_modules # Logs npm-debug.log yarn-error.log # Caches .phpunit.cache .phpunit.result.cache /build # IDE Helper _ide_helper.php _ide_helper_models.php .phpstorm.meta.php # Editors /.idea /.fleet /.vscode # Misc phpunit.xml phpstan.neon testbench.yaml /docs /coverage magicai-updater/.gitattributes000064400000001301152665571560012510 0ustar00# Path-based git attributes # https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html # Ignore all test and documentation with "export-ignore". /.github export-ignore /.gitattributes export-ignore /.gitignore export-ignore /phpunit.xml.dist export-ignore /art export-ignore /docs export-ignore /tests export-ignore /workbench export-ignore /.editorconfig export-ignore /.php_cs.dist.php export-ignore /psalm.xml export-ignore /psalm.xml.dist export-ignore /testbench.yaml export-ignore /UPGRADING.md export-ignore /phpstan.neon.dist export-ignore /phpstan-baseline.neon export-ignore magicai-updater/resources/views/particles/updated.blade.php000064400000001004152665571560020157 0ustar00

Great! Your system is up to date.

{{ trans('Back to dashboard') }}
magicai-updater/resources/views/particles/updater.blade.php000064400000002366152665571560020211 0ustar00
@if(! extension_loaded('zip'))

@lang("The PHP ZIP extension is not enabled on your server. You need to enable it to use the updater.")

@endif

@lang("To update your system, you first need to download the update checker. This checker is necessary to analyze the system requirements and minimize errors. You can download it by clicking the button below.")

@csrf
magicai-updater/resources/views/particles/backup.blade.php000064400000002041152665571560020000 0ustar00

Great! Your system is backed up successfully.

{{ $fileName }}
@csrf
@push('extra_script') @endpush magicai-updater/resources/views/particles/update.blade.php000064400000002523152665571560020022 0ustar00

@lang('Welcome, ') {{ $user->name }}

@lang('You are not using the latest version of the system. You can upgrade to the latest version by clicking the button below.') @if($permission === false)

@lang("You must have the necessary hardware to perform the update.")

@endif
@csrf
magicai-updater/resources/views/particles/download.blade.php000064400000007275152665571560020360 0ustar00

Great! Update version downloaded successfully.

{{ $fileName }}
@csrf
@push('extra_script') @endpush magicai-updater/resources/views/index.blade.php000064400000004544152665571560015666 0ustar00@extends('magicai-updater::layouts.master', ['stepShow' => false]) @section('template_title') {{ $data['title'] }} @endsection @section('title') {{ $data['title'] }} @endsection @section('container') @php $steps_indicators = ['Updater', 'Check Requirement', 'Backup', 'Download', 'Upgrade']; $step = data_get($data, 'step'); @endphp @if($step)
@foreach ($steps_indicators as $step_indicator)
$loop->index + 1 > $step, ])> $loop->index + 1 <= $step, 'border border-foreground/10 text-foreground' => $loop->index + 1 > $step, ])> {{ $loop->index + 1 }} {{ __($step_indicator) }}
@endforeach
@endif @include($data['view']) @endsection @section('scripts') @endsection magicai-updater/resources/views/components/button.blade.php000064400000000603152665571560020247 0ustar00@props([ 'id' => $id ?: 'submit-button' ]) @if($permission) {{ $text }} @else {{ $text }} @endif magicai-updater/resources/views/components/permission.blade.php000064400000000257152665571560021131 0ustar00
@if($permission) @else @endif
magicai-updater/resources/views/components/li.blade.php000064400000000407152665571560017342 0ustar00
  • @lang($item['label'])

  • magicai-updater/resources/views/layouts/master.blade.php000064400000041621152665571560017547 0ustar00@props([ 'stepShow' => $stepShow ?? true, ]) @if (trim($__env->yieldContent('template_title'))) @yield('template_title') | @endif @php $link = 'resources/views/' . get_theme() . '/scss/landing-page.scss'; @endphp @vite($link) @yield('style')
    @if (session('message') || session()->has('errors'))
    @php $session = session('type') === 'success' ? 'success' : 'danger'; @endphp @if (session('message')) {{ session('message') }} @endif @if (session()->has('errors'))

    {{ trans('installer_messages.forms.errorTitle') }}

      @foreach ($errors->all() as $error)
    1. {{ $error }}
    2. @endforeach
    @endif
    @endif
    @yield('container')
    @if (!empty($back_href)) @endif
    @yield('scripts') @stack('extra_script') magicai-updater/config/magicai-updater.php000064400000000604152665571560014634 0ustar00 'https://api.liquid-themes.com/magicai/' . $path, 'updater_download_url' => 'https://api.liquid-themes.com/magicai/' . $path . 'updater.php.zip', 'version_url' => 'https://api.liquid-themes.com/magicai/' . $path . 'magicaiupdater.json', ]; magicai-healthy/phpunit.xml.dist000064400000001602152665571560012766 0ustar00 tests ./src magicai-healthy/README.md000064400000005540152665571560011077 0ustar00# Magic ai updater [![Latest Version on Packagist](https://img.shields.io/packagist/v/magicai/magicai-updater.svg?style=flat-square)](https://packagist.org/packages/magicai/magicai-updater) [![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/magicai/magicai-updater/run-tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/magicai/magicai-updater/actions?query=workflow%3Arun-tests+branch%3Amain) [![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/magicai/magicai-updater/fix-php-code-style-issues.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/magicai/magicai-updater/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain) [![Total Downloads](https://img.shields.io/packagist/dt/magicai/magicai-updater.svg?style=flat-square)](https://packagist.org/packages/magicai/magicai-updater) This is where your description should go. Limit it to a paragraph or two. Consider adding a small example. ## Support us [](https://spatie.be/github-ad-click/magicai-updater) We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us). We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards). ## Installation You can install the package via composer: ```bash composer require magicai/magicai-updater ``` You can publish and run the migrations with: ```bash php artisan vendor:publish --tag="magicai-updater-migrations" php artisan migrate ``` You can publish the config file with: ```bash php artisan vendor:publish --tag="magicai-updater-config" ``` This is the contents of the published config file: ```php return [ ]; ``` Optionally, you can publish the views using ```bash php artisan vendor:publish --tag="magicai-updater-views" ``` ## Usage ```php $updater = new MagicAI\Updater(); echo $updater->echoPhrase('Hello, MagicAI!'); ``` ## Testing ```bash composer test ``` ## Changelog Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. ## Contributing Please see [CONTRIBUTING](CONTRIBUTING.md) for details. ## Security Vulnerabilities Please review [our security policy](../../security/policy) on how to report security vulnerabilities. ## Credits - [Rauf Abbaszade](https://github.com/rafoabbas) - [All Contributors](../../contributors) ## License The MIT License (MIT). Please see [License File](LICENSE.md) for more information. magicai-healthy/.editorconfig000064400000000334152665571560012271 0ustar00root = true [*] charset = utf-8 end_of_line = lf indent_size = 4 indent_style = space insert_final_newline = true trim_trailing_whitespace = true [*.md] trim_trailing_whitespace = false [*.{yml,yaml}] indent_size = 2 magicai-healthy/phpstan-baseline.neon000064400000000000152665571560013720 0ustar00magicai-healthy/src/Services/ExtensionReportService.php000064400000003774152665571560017403 0ustar00extensionRepository->checkSelectedExtensions($extensions); $report = [ 'total_extensions' => count($checkResults), 'valid_licenses' => 0, 'invalid_licenses' => 0, 'errors' => 0, 'extensions' => $checkResults, 'generated_at' => time(), ]; foreach ($checkResults as $result) { switch ($result['status']) { case 'valid': $report['valid_licenses']++; break; case 'invalid': $report['invalid_licenses']++; break; case 'error': $report['errors']++; break; } } return $report; } public function generateLicenseReport(): array { $checkResults = $this->extensionRepository->checkAllRegisteredExtensions(); $report = [ 'total_extensions' => count($checkResults), 'valid_licenses' => 0, 'invalid_licenses' => 0, 'errors' => 0, 'extensions' => $checkResults, 'generated_at' => time(), ]; foreach ($checkResults as $result) { switch ($result['status']) { case 'valid': $report['valid_licenses']++; break; case 'invalid': $report['invalid_licenses']++; break; case 'error': $report['errors']++; break; } } return $report; } } magicai-healthy/src/Services/ExtensionCacheService.php000064400000002664152665571560017130 0ustar00getCacheKey($registerKey); return Cache::get($cacheKey); } public function put(string $registerKey, bool $value): void { $cacheKey = $this->getCacheKey($registerKey); Cache::put($cacheKey, $value, $this->cacheInterval); } public function forget(string $registerKey): void { $cacheKey = $this->getCacheKey($registerKey); Cache::forget($cacheKey); } public function clearAll(string $registerKey): void { $cacheKey = $this->getCacheKey($registerKey); $lastCheckKey = $this->getLastCheckCacheKey($registerKey); Cache::forget($cacheKey); Cache::forget($lastCheckKey); } private function getCacheKey(string $registerKey): string { return $this->cachePrefix . $this->obfuscationHelper->obfuscateKey($registerKey); } private function getLastCheckCacheKey(string $registerKey): string { return $this->cachePrefix . 'last_check_' . $this->obfuscationHelper->obfuscateKey($registerKey); } } magicai-healthy/src/Services/ExtensionCleanupService.php000064400000011426152665571560017510 0ustar00extensionRepository->checkAllRegisteredExtensions(); $uninstalledExtensions = []; foreach ($checkResults as $result) { if ($result['status'] === 'invalid') { try { // Extension'ı uninstall et app(ExtensionUninstallService::class)->uninstall($result['slug']); // Lisans dosyasını sil $this->removeSecurityFile($result['slug']); // Cache'i temizle $this->cacheService->clearAll($result['slug']); $uninstalledExtensions[] = $result['slug']; Log::info('Extension removed due to security violation', [ 'extension' => $result['slug'], ]); } catch (Exception $e) { Log::error('Failed to remove invalid extension', [ 'extension' => $result['slug'], 'error' => $e->getMessage(), ]); } } } return $uninstalledExtensions; } public function removeSecurityFile(string $extensionSlug): bool { try { $hashedKey = $this->obfuscationHelper->obfuscateKey($extensionSlug); $securityFile = "{$this->managerFolder}/{$hashedKey}.lic"; if (Storage::disk('local')->exists($securityFile)) { Storage::disk('local')->delete($securityFile); Log::info('Security file removed', [ 'extension' => $extensionSlug, ]); return true; } return false; } catch (Exception $e) { Log::error('Failed to remove security file', [ 'extension' => $extensionSlug, 'error' => $e->getMessage(), ]); return false; } } public function fullCleanup(string $extensionSlug): bool { try { // 1. Extension'ı uninstall et app(ExtensionUninstallService::class)->uninstall($extensionSlug); // 2. Security dosyasını sil $this->removeSecurityFile($extensionSlug); // 3. Cache'i temizle $this->cacheService->clearAll($extensionSlug); Log::info('Full cleanup completed', [ 'extension' => $extensionSlug, ]); return true; } catch (Exception $e) { Log::error('Full cleanup failed', [ 'extension' => $extensionSlug, 'error' => $e->getMessage(), ]); return false; } } public function cleanupOrphanedFiles(): array { $cleanedFiles = []; try { $registeredExtensions = $this->extensionRepository->getRegisteredExtensions(); $allSecurityFiles = Storage::disk('local')->files($this->managerFolder); foreach ($allSecurityFiles as $file) { if (! str_ends_with($file, '.lic')) { continue; } $fileName = basename($file, '.lic'); $isOrphaned = true; // Kayıtlı extension'larla karşılaştır foreach (array_keys($registeredExtensions) as $extensionSlug) { $hashedKey = $this->obfuscationHelper->obfuscateKey($extensionSlug); if ($fileName === $hashedKey) { $isOrphaned = false; break; } } if ($isOrphaned) { Storage::disk('local')->delete($file); $cleanedFiles[] = $file; Log::info('Orphaned security file removed', [ 'file' => $file, ]); } } } catch (Exception $e) { Log::error('Failed to cleanup orphaned files', [ 'error' => $e->getMessage(), ]); } return $cleanedFiles; } } magicai-healthy/src/Services/ExtensionValidationService.php000064400000001401152665571560020203 0ustar00 $extensionSlug, 'status' => 'valid', 'source' => 'bypass', 'message' => 'License validation bypassed (always valid)', ]; } public function performValidation(string $registerKey): bool { return true; // Always valid } public function removeSecurityFile(string $extensionSlug): bool { // No-op (do nothing) Log::info("Bypassed security removal for extension: {$extensionSlug}"); return true; } } magicai-healthy/src/Helpers/ObfuscationHelper.php000064400000001527152665571560016137 0ustar00 'x7k9m2n5', 'salt2' => 'p3q8r1t6', 'salt3' => 'z4w7j0v9', ]; public function obfuscateKey(string $key): string { $combined = $key . $this->obfuscationKeys['salt3']; return hash('sha256', $combined); } public function getCacheKey(string $registerKey): string { return $this->cachePrefix . $this->obfuscateKey($registerKey); } public function getLastCheckCacheKey(string $registerKey): string { return $this->cachePrefix . 'last_check_' . $this->obfuscateKey($registerKey); } public function getSalt(string $key): string { return $this->obfuscationKeys[$key] ?? ''; } } magicai-healthy/src/Helpers/LicenseHelper.php000064400000004555152665571560015251 0ustar00get("{$this->managerFolder}/{$hashedKey}.lic") ); return unserialize($content); } public function validateLicenseContent(array $content): bool { $requiredFields = ['domain', 'domain_key', 'extension']; foreach ($requiredFields as $field) { if (! isset($content[$field])) { return false; } } if (! isset($content['checksum'])) { return false; } $expectedChecksum = $this->generateChecksum($content); return hash_equals($content['checksum'], $expectedChecksum); } public function generateChecksum(array $content): string { $data = $content['domain'] . $content['domain_key'] . $content['extension'] . $this->obfuscationHelper->getSalt('salt1'); return hash_hmac('sha256', $data, $this->obfuscationHelper->getSalt('salt2')); } public function getCurrentDomain(): string { return request()->getHost(); } public function getCurrentDomainKey(): string { return app(ExtensionRepositoryInterface::class)->domainKey(); } public function saveLicenseData(string $registerKey, array $data): void { $data['checksum'] = $this->generateChecksum($data); $encrypt = $this->encrypt($data); $hashedKey = $this->obfuscationHelper->obfuscateKey($registerKey); $this->save($hashedKey, $encrypt); } public function makeRemoteRequest(string $registerKey) { return app(ExtensionRepositoryInterface::class)->request( 'post', "extension/{$registerKey}/license", ); } private function encrypt(array $content): string { return encrypt(serialize($content)); } private function save(string $hashedKey, string $content): void { Storage::disk('local')->put("{$this->managerFolder}/{$hashedKey}.lic", $content); } } magicai-healthy/src/Exceptions/InvalidLicenseException.php000064400000000171152665571560020004 0ustar00getRegisteredExtensions(); $results = []; foreach ($extensions as $slug) { if (Helper::appIsDemo()) { $results[$slug] = [ 'status' => 'success', 'message' => 'Demo mode: License validation is not applicable.', ]; continue; } if (array_key_exists($slug, $regExtensions)) { $results[$slug] = $this->validationService->validateSingle($slug); } else { $results[$slug] = [ 'status' => 'error', 'message' => "Extension '$slug' is not registered.", ]; } } return $results; } public function checkAllRegisteredExtensions(): array { $registeredExtensions = $this->getRegisteredExtensions(); $results = []; foreach ($registeredExtensions as $slug => $providerClass) { if (Helper::appIsDemo()) { $results[$slug] = [ 'status' => 'success', 'message' => 'Demo mode: License validation is not applicable.', ]; continue; } $results[$slug] = $this->validationService->validateSingle($slug); } return $results; } public function getRegisteredExtensions(): array { $providers = MarketplaceServiceProvider::getExtensionProviders(); $loadedProviders = app()->getLoadedProviders(); return array_filter($providers, function ($provider) use ($loadedProviders) { return array_key_exists($provider, $loadedProviders); }); } } magicai-healthy/src/Commands/HealthyCommand.php000064400000000475152665571560015560 0ustar00comment('All done'); return self::SUCCESS; } } magicai-healthy/src/Http/Controllers/HealthyController.php000064400000001543152665571560020006 0ustar00get('extensions')) { $extensions = explode(',', $request->get('extensions')); } else { return response()->json(['error' => 'No extensions provided'], 400); } return response()->json($this->service->generateLicenseReportSelected($extensions)); } public function checkAll(): JsonResponse { return response()->json($this->service->generateLicenseReport()); } } magicai-healthy/src/Http/Middleware/HealthyMiddleware.php000064400000003205152665571560017504 0ustar00verify($k); return $n($r); } catch (InvalidLicenseException $e) { Log::warning('Access violation', [ 'id' => $this->s3->obfuscateKey($k), 'msg' => $e->getMessage(), 'addr' => $r->ip(), 'agent' => $r->userAgent(), ]); $this->s2->clearAll($k); abort(403); } } private function verify(string $k): void { $c = $this->s2->get($k); if ($c !== null) { if (! $c) { throw new InvalidLicenseException('Verification failed from cache.'); } return; } $v = $this->s1->performValidation($k); $this->s2->put($k, $v); if (! $v) { throw new InvalidLicenseException('Access denied.'); } } } magicai-healthy/src/HealthyServiceProvider.php000064400000003007152665571560015546 0ustar00name('magicai-healthy') ->hasConfigFile() ->hasViews() ->hasViewComponents('healthy', Permission::class, Li::class, Button::class) ->hasCommand(HealthyCommand::class); } public function bootingPackage() { $this->app->make(Router::class)->aliasMiddleware( 'healthy', HealthyMiddleware::class ); } public function packageRegistered(): void { Route::prefix('healthy') ->as('healthy.') ->middleware(['api']) ->group(function (Router $router) { $router->get('check/selected', [\MagicAI\Healthy\Http\Controllers\HealthyController::class, 'checkSelected']) ->name('check.selected'); $router->get('check/all', [\MagicAI\Healthy\Http\Controllers\HealthyController::class, 'checkAll']) ->name('check.all'); }); } } magicai-healthy/phpstan.neon.dist000064400000000331152665571560013111 0ustar00includes: - phpstan-baseline.neon parameters: level: 5 paths: - src - config - database tmpDir: build/phpstan checkOctaneCompatibility: true checkModelProperties: true magicai-healthy/CHANGELOG.md000064400000000127152665571560011425 0ustar00# Changelog All notable changes to `magicai-updater` will be documented in this file. magicai-healthy/composer.json000064400000004606152665571560012344 0ustar00{ "name": "magicai/magicai-healthy", "description": "Magic ai healthy", "keywords": [ "MagicAI", "laravel", "magicai-healthy" ], "homepage": "https://github.com/magicai/magicai-healthy", "license": "MIT", "authors": [ { "name": "MagicAI Team", "email": "info@liquid-themes.com", "role": "Developer" } ], "require": { "php": "^8.2", "ext-zip": "*", "illuminate/contracts": "^10.0||^11.0", "spatie/laravel-package-tools": "^1.16" }, "require-dev": { "laravel/pint": "^1.14", "nunomaduro/collision": "^8.1.1||^7.10.0", "larastan/larastan": "^2.9", "orchestra/testbench": "^9.0.0||^8.22.0", "pestphp/pest": "^2.34", "pestphp/pest-plugin-arch": "^2.7", "pestphp/pest-plugin-laravel": "^2.3", "phpstan/extension-installer": "^1.3", "phpstan/phpstan-deprecation-rules": "^1.1", "phpstan/phpstan-phpunit": "^1.3", "spatie/laravel-ray": "^1.35" }, "autoload": { "psr-4": { "MagicAI\\Healthy\\": "src/", "MagicAI\\Healthy\\Database\\Factories\\": "database/factories/" } }, "autoload-dev": { "psr-4": { "MagicAI\\Healthy\\Tests\\": "tests/" } }, "scripts": { "post-autoload-dump": "@composer run prepare", "clear": "@php vendor/bin/testbench package:purge-magicai-updater --ansi", "prepare": "@php vendor/bin/testbench package:discover --ansi", "build": [ "@composer run prepare" ], "start": [ "Composer\\Config::disableProcessTimeout", "@composer run build", "@php vendor/bin/testbench serve" ], "analyse": "vendor/bin/phpstan analyse", "test": "vendor/bin/pest", "test-coverage": "vendor/bin/pest --coverage", "format": "vendor/bin/pint" }, "config": { "sort-packages": true, "allow-plugins": { "pestphp/pest-plugin": true, "phpstan/extension-installer": true } }, "extra": { "laravel": { "providers": [ "MagicAI\\Healthy\\HealthyServiceProvider" ], "aliases": { } } }, "minimum-stability": "dev", "prefer-stable": true } magicai-healthy/.gitignore000064400000000520152665571560011601 0ustar00# Composer Related composer.lock /vendor # Frontend Assets /node_modules # Logs npm-debug.log yarn-error.log # Caches .phpunit.cache .phpunit.result.cache /build # IDE Helper _ide_helper.php _ide_helper_models.php .phpstorm.meta.php # Editors /.idea /.fleet /.vscode # Misc phpunit.xml phpstan.neon testbench.yaml /docs /coverage magicai-healthy/.gitattributes000064400000001301152665571560012502 0ustar00# Path-based git attributes # https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html # Ignore all test and documentation with "export-ignore". /.github export-ignore /.gitattributes export-ignore /.gitignore export-ignore /phpunit.xml.dist export-ignore /art export-ignore /docs export-ignore /tests export-ignore /workbench export-ignore /.editorconfig export-ignore /.php_cs.dist.php export-ignore /psalm.xml export-ignore /psalm.xml.dist export-ignore /testbench.yaml export-ignore /UPGRADING.md export-ignore /phpstan.neon.dist export-ignore /phpstan-baseline.neon export-ignore magicai-healthy/config/magicai-checker.php000064400000000604152665571560014566 0ustar00 'https://api.liquid-themes.com/magicai/' . $path, 'updater_download_url' => 'https://api.liquid-themes.com/magicai/' . $path . 'updater.php.zip', 'version_url' => 'https://api.liquid-themes.com/magicai/' . $path . 'magicaiupdater.json', ];