diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index d0dc9ea..a4ff129 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -42,7 +42,7 @@ jobs: with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | - type=ref,event=tag + type=semver,pattern={{version}} - name: Build and push Docker image uses: docker/build-push-action@v6 diff --git a/README.md b/README.md index a5660fa..f3b5fd5 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [](https://github.com/usetrmnl/byos_laravel/actions/workflows/test.yml) TRMNL BYOS Laravel is a self-hostable implementation of a TRMNL server, built with Laravel. -It allows you to manage TRMNL devices, generate screens using native plugins, recipes (55+ from the [community catalog](https://bnussbau.github.io/trmnl-recipe-catalog/)), or the API, and can also act as a proxy for the native cloud service (Core). With over 20k downloads and 100+ stars, it’s the most popular community-driven BYOS. +It allows you to manage TRMNL devices, generate screens using native plugins, recipes (100+ from the [community catalog](https://bnussbau.github.io/trmnl-recipe-catalog/)), or the API, and can also act as a proxy for the native cloud service (Core). With over 20k downloads and 100+ stars, it’s the most popular community-driven BYOS.   diff --git a/app/Models/Plugin.php b/app/Models/Plugin.php index 40569f8..ab83514 100644 --- a/app/Models/Plugin.php +++ b/app/Models/Plugin.php @@ -11,6 +11,7 @@ use App\Liquid\Filters\StandardFilters; use App\Liquid\Filters\StringMarkup; use App\Liquid\Filters\Uniqueness; use App\Liquid\Tags\TemplateTag; +use App\Services\PluginImportService; use Exception; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -372,14 +373,14 @@ class Plugin extends Model * @param array $context The render context data * @return string The rendered HTML * - * @throws LiquidException + * @throws Exception */ private function renderWithExternalLiquidRenderer(string $template, array $context): string { $liquidPath = config('services.trmnl.liquid_path'); if (empty($liquidPath)) { - throw new LiquidException('External liquid renderer path is not configured'); + throw new Exception('External liquid renderer path is not configured'); } // HTML encode the template @@ -389,9 +390,12 @@ class Plugin extends Model $jsonContext = json_encode($context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); if ($jsonContext === false) { - throw new LiquidException('Failed to encode render context as JSON: '.json_last_error_msg()); + throw new Exception('Failed to encode render context as JSON: '.json_last_error_msg()); } + // Validate argument sizes + app(PluginImportService::class)->validateExternalRendererArguments($encodedTemplate, $jsonContext, $liquidPath); + // Execute the external renderer $process = Process::run([ $liquidPath, diff --git a/app/Services/PluginImportService.php b/app/Services/PluginImportService.php index 7c77767..06e6092 100644 --- a/app/Services/PluginImportService.php +++ b/app/Services/PluginImportService.php @@ -139,11 +139,13 @@ class PluginImportService * @param string $zipUrl The URL to the ZIP file * @param User $user The user importing the plugin * @param string|null $zipEntryPath Optional path to specific plugin in monorepo + * @param string|null $preferredRenderer Optional preferred renderer (e.g., 'trmnl-liquid') + * @param string|null $iconUrl Optional icon URL to set on the plugin * @return Plugin The created plugin instance * * @throws Exception If the ZIP file is invalid or required files are missing */ - public function importFromUrl(string $zipUrl, User $user, ?string $zipEntryPath = null, $preferredRenderer = null): Plugin + public function importFromUrl(string $zipUrl, User $user, ?string $zipEntryPath = null, $preferredRenderer = null, ?string $iconUrl = null): Plugin { // Download the ZIP file $response = Http::timeout(60)->get($zipUrl); @@ -233,6 +235,7 @@ class PluginImportService 'configuration_template' => $configurationTemplate, 'data_payload' => json_decode($settings['static_data'] ?? '{}', true), 'preferred_renderer' => $preferredRenderer, + 'icon_url' => $iconUrl, ]); if (! $plugin_updated) { @@ -381,4 +384,58 @@ class PluginImportService 'sharedLiquidPath' => $sharedLiquidPath, ]; } + + /** + * Validate that template and context are within command-line argument limits + * + * @param string $template The liquid template string + * @param string $jsonContext The JSON-encoded context + * @param string $liquidPath The path to the liquid renderer executable + * + * @throws Exception If the template or context exceeds argument limits + */ + public function validateExternalRendererArguments(string $template, string $jsonContext, string $liquidPath): void + { + // MAX_ARG_STRLEN on Linux is typically 131072 (128KB) for individual arguments + // ARG_MAX is the total size of all arguments (typically 2MB on modern systems) + $maxIndividualArgLength = 131072; // 128KB - MAX_ARG_STRLEN limit + $maxTotalArgLength = $this->getMaxArgumentLength(); + + // Check individual argument sizes (template and context are the largest) + if (mb_strlen($template) > $maxIndividualArgLength || mb_strlen($jsonContext) > $maxIndividualArgLength) { + throw new Exception('Context too large for external liquid renderer. Reduce the size of the Payload or Template.'); + } + + // Calculate total size of all arguments (path + flags + template + context) + // Add overhead for path, flags, and separators (conservative estimate: ~200 bytes) + $totalArgSize = mb_strlen($liquidPath) + mb_strlen('--template') + mb_strlen($template) + + mb_strlen('--context') + mb_strlen($jsonContext) + 200; + + if ($totalArgSize > $maxTotalArgLength) { + throw new Exception('Context too large for external liquid renderer. Reduce the size of the Payload or Template.'); + } + } + + /** + * Get the maximum argument length for command-line arguments + * + * @return int Maximum argument length in bytes + */ + private function getMaxArgumentLength(): int + { + // Try to get ARG_MAX from system using getconf + $argMax = null; + if (function_exists('shell_exec')) { + $result = @shell_exec('getconf ARG_MAX 2>/dev/null'); + if ($result !== null && is_numeric(mb_trim($result))) { + $argMax = (int) mb_trim($result); + } + } + + // Use conservative fallback if ARG_MAX cannot be determined + // ARG_MAX on macOS is typically 262144 (256KB), on Linux it's usually 2097152 (2MB) + // We use 200KB as a conservative limit that works on both systems + // Note: ARG_MAX includes environment variables, so we leave headroom + return $argMax !== null ? min($argMax, 204800) : 204800; + } } diff --git a/composer.lock b/composer.lock index 2f54904..60b450d 100644 --- a/composer.lock +++ b/composer.lock @@ -62,16 +62,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.359.1", + "version": "3.359.6", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "40543e3993fc5094094ac9f9bdc4434bf81cca2d" + "reference": "8d2ab3687196f15209c316080a431911f2e02bb5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/40543e3993fc5094094ac9f9bdc4434bf81cca2d", - "reference": "40543e3993fc5094094ac9f9bdc4434bf81cca2d", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/8d2ab3687196f15209c316080a431911f2e02bb5", + "reference": "8d2ab3687196f15209c316080a431911f2e02bb5", "shasum": "" }, "require": { @@ -153,9 +153,9 @@ "support": { "forum": "https://github.com/aws/aws-sdk-php/discussions", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.359.1" + "source": "https://github.com/aws/aws-sdk-php/tree/3.359.6" }, - "time": "2025-10-29T20:13:06+00:00" + "time": "2025-11-05T19:08:10+00:00" }, { "name": "bnussbau/laravel-trmnl-blade", @@ -685,29 +685,28 @@ }, { "name": "dragonmantank/cron-expression", - "version": "v3.4.0", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "8c784d071debd117328803d86b2097615b457500" + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500", - "reference": "8c784d071debd117328803d86b2097615b457500", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/d61a8a9604ec1f8c3d150d09db6ce98b32675013", + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013", "shasum": "" }, "require": { - "php": "^7.2|^8.0", - "webmozart/assert": "^1.0" + "php": "^8.2|^8.3|^8.4|^8.5" }, "replace": { "mtdowling/cron-expression": "^1.0" }, "require-dev": { - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0" + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.32|^2.1.31", + "phpunit/phpunit": "^8.5.48|^9.0" }, "type": "library", "extra": { @@ -738,7 +737,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.6.0" }, "funding": [ { @@ -746,7 +745,7 @@ "type": "github" } ], - "time": "2024-10-09T13:47:03+00:00" + "time": "2025-10-31T18:51:33+00:00" }, { "name": "egulias/email-validator", @@ -1618,16 +1617,16 @@ }, { "name": "laravel/framework", - "version": "v12.36.1", + "version": "v12.37.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "cad110d7685fbab990a6bb8184d0cfd847d7c4d8" + "reference": "3c3c4ad30f5b528b164a7c09aa4ad03118c4c125" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/cad110d7685fbab990a6bb8184d0cfd847d7c4d8", - "reference": "cad110d7685fbab990a6bb8184d0cfd847d7c4d8", + "url": "https://api.github.com/repos/laravel/framework/zipball/3c3c4ad30f5b528b164a7c09aa4ad03118c4c125", + "reference": "3c3c4ad30f5b528b164a7c09aa4ad03118c4c125", "shasum": "" }, "require": { @@ -1833,7 +1832,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-10-29T14:20:57+00:00" + "time": "2025-11-04T15:39:33+00:00" }, { "name": "laravel/prompts", @@ -2984,16 +2983,16 @@ }, { "name": "livewire/volt", - "version": "v1.8.0", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/livewire/volt.git", - "reference": "2d9783a340d612d32f4ffd38070780ca7d7e9205" + "reference": "4b289eef2f15398987a923d9f813cad6a6a19ea4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/livewire/volt/zipball/2d9783a340d612d32f4ffd38070780ca7d7e9205", - "reference": "2d9783a340d612d32f4ffd38070780ca7d7e9205", + "url": "https://api.github.com/repos/livewire/volt/zipball/4b289eef2f15398987a923d9f813cad6a6a19ea4", + "reference": "4b289eef2f15398987a923d9f813cad6a6a19ea4", "shasum": "" }, "require": { @@ -3052,7 +3051,7 @@ "issues": "https://github.com/livewire/volt/issues", "source": "https://github.com/livewire/volt" }, - "time": "2025-10-29T15:52:35+00:00" + "time": "2025-10-30T02:46:00+00:00" }, { "name": "maennchen/zipstream-php", @@ -3408,25 +3407,25 @@ }, { "name": "nette/schema", - "version": "v1.3.2", + "version": "v1.3.3", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "da801d52f0354f70a638673c4a0f04e16529431d" + "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d", - "reference": "da801d52f0354f70a638673c4a0f04e16529431d", + "url": "https://api.github.com/repos/nette/schema/zipball/2befc2f42d7c715fd9d95efc31b1081e5d765004", + "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004", "shasum": "" }, "require": { "nette/utils": "^4.0", - "php": "8.1 - 8.4" + "php": "8.1 - 8.5" }, "require-dev": { "nette/tester": "^2.5.2", - "phpstan/phpstan-nette": "^1.0", + "phpstan/phpstan-nette": "^2.0@stable", "tracy/tracy": "^2.8" }, "type": "library", @@ -3436,6 +3435,9 @@ } }, "autoload": { + "psr-4": { + "Nette\\": "src" + }, "classmap": [ "src/" ] @@ -3464,9 +3466,9 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.3.2" + "source": "https://github.com/nette/schema/tree/v1.3.3" }, - "time": "2024-10-06T23:10:23+00:00" + "time": "2025-10-30T22:57:59+00:00" }, { "name": "nette/utils", @@ -7732,64 +7734,6 @@ ], "time": "2024-11-21T01:49:47+00:00" }, - { - "name": "webmozart/assert", - "version": "1.12.1", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-date": "*", - "ext-filter": "*", - "php": "^7.2 || ^8.0" - }, - "suggest": { - "ext-intl": "", - "ext-simplexml": "", - "ext-spl": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.12.1" - }, - "time": "2025-10-29T15:56:20+00:00" - }, { "name": "wnx/sidecar-browsershot", "version": "v2.6.1", @@ -7880,16 +7824,16 @@ "packages-dev": [ { "name": "brianium/paratest", - "version": "v7.14.1", + "version": "v7.14.2", "source": { "type": "git", "url": "https://github.com/paratestphp/paratest.git", - "reference": "e1a93c38a94f4808faf75552e835666d3a6f8bb2" + "reference": "de06de1ae1203b11976c6ca01d6a9081c8b33d45" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/e1a93c38a94f4808faf75552e835666d3a6f8bb2", - "reference": "e1a93c38a94f4808faf75552e835666d3a6f8bb2", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/de06de1ae1203b11976c6ca01d6a9081c8b33d45", + "reference": "de06de1ae1203b11976c6ca01d6a9081c8b33d45", "shasum": "" }, "require": { @@ -7903,7 +7847,7 @@ "phpunit/php-code-coverage": "^12.4.0", "phpunit/php-file-iterator": "^6", "phpunit/php-timer": "^8", - "phpunit/phpunit": "^12.4.0", + "phpunit/phpunit": "^12.4.1", "sebastian/environment": "^8.0.3", "symfony/console": "^6.4.20 || ^7.3.4", "symfony/process": "^6.4.20 || ^7.3.4" @@ -7913,7 +7857,7 @@ "ext-pcntl": "*", "ext-pcov": "*", "ext-posix": "*", - "phpstan/phpstan": "^2.1.30", + "phpstan/phpstan": "^2.1.31", "phpstan/phpstan-deprecation-rules": "^2.0.3", "phpstan/phpstan-phpunit": "^2.0.7", "phpstan/phpstan-strict-rules": "^2.0.7", @@ -7957,7 +7901,7 @@ ], "support": { "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.14.1" + "source": "https://github.com/paratestphp/paratest/tree/v7.14.2" }, "funding": [ { @@ -7969,7 +7913,7 @@ "type": "paypal" } ], - "time": "2025-10-06T08:26:52+00:00" + "time": "2025-10-24T07:20:53+00:00" }, { "name": "doctrine/deprecations", @@ -8368,16 +8312,16 @@ }, { "name": "larastan/larastan", - "version": "v3.7.2", + "version": "v3.8.0", "source": { "type": "git", "url": "https://github.com/larastan/larastan.git", - "reference": "a761859a7487bd7d0cb8b662a7538a234d5bb5ae" + "reference": "d13ef96d652d1b2a8f34f1760ba6bf5b9c98112e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/larastan/larastan/zipball/a761859a7487bd7d0cb8b662a7538a234d5bb5ae", - "reference": "a761859a7487bd7d0cb8b662a7538a234d5bb5ae", + "url": "https://api.github.com/repos/larastan/larastan/zipball/d13ef96d652d1b2a8f34f1760ba6bf5b9c98112e", + "reference": "d13ef96d652d1b2a8f34f1760ba6bf5b9c98112e", "shasum": "" }, "require": { @@ -8391,7 +8335,7 @@ "illuminate/pipeline": "^11.44.2 || ^12.4.1", "illuminate/support": "^11.44.2 || ^12.4.1", "php": "^8.2", - "phpstan/phpstan": "^2.1.28" + "phpstan/phpstan": "^2.1.29" }, "require-dev": { "doctrine/coding-standard": "^13", @@ -8404,7 +8348,8 @@ "phpunit/phpunit": "^10.5.35 || ^11.5.15" }, "suggest": { - "orchestra/testbench": "Using Larastan for analysing a package needs Testbench" + "orchestra/testbench": "Using Larastan for analysing a package needs Testbench", + "phpmyadmin/sql-parser": "Install to enable Larastan's optional phpMyAdmin-based SQL parser automatically" }, "type": "phpstan-extension", "extra": { @@ -8445,7 +8390,7 @@ ], "support": { "issues": "https://github.com/larastan/larastan/issues", - "source": "https://github.com/larastan/larastan/tree/v3.7.2" + "source": "https://github.com/larastan/larastan/tree/v3.8.0" }, "funding": [ { @@ -8453,20 +8398,20 @@ "type": "github" } ], - "time": "2025-09-19T09:03:05+00:00" + "time": "2025-10-27T23:09:14+00:00" }, { "name": "laravel/boost", - "version": "v1.6.0", + "version": "v1.7.1", "source": { "type": "git", "url": "https://github.com/laravel/boost.git", - "reference": "29d1c7c5a816d2b55c39f50bb07bdbca6c595b09" + "reference": "355f7c27952862aab3f61adec27773fd4d41a582" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/boost/zipball/29d1c7c5a816d2b55c39f50bb07bdbca6c595b09", - "reference": "29d1c7c5a816d2b55c39f50bb07bdbca6c595b09", + "url": "https://api.github.com/repos/laravel/boost/zipball/355f7c27952862aab3f61adec27773fd4d41a582", + "reference": "355f7c27952862aab3f61adec27773fd4d41a582", "shasum": "" }, "require": { @@ -8475,7 +8420,7 @@ "illuminate/contracts": "^10.49.0|^11.45.3|^12.28.1", "illuminate/routing": "^10.49.0|^11.45.3|^12.28.1", "illuminate/support": "^10.49.0|^11.45.3|^12.28.1", - "laravel/mcp": "^0.2.0|^0.3.0", + "laravel/mcp": "^0.3.2", "laravel/prompts": "0.1.25|^0.3.6", "laravel/roster": "^0.2.9", "php": "^8.1" @@ -8519,7 +8464,7 @@ "issues": "https://github.com/laravel/boost/issues", "source": "https://github.com/laravel/boost" }, - "time": "2025-10-28T17:43:53+00:00" + "time": "2025-11-05T21:41:46+00:00" }, { "name": "laravel/mcp", @@ -9107,16 +9052,16 @@ }, { "name": "pestphp/pest", - "version": "v4.1.2", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/pestphp/pest.git", - "reference": "08b09f2e98fc6830050c0237968b233768642d46" + "reference": "477d20a54fd9329ddfb0f8d4eb90dca7bc81b027" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest/zipball/08b09f2e98fc6830050c0237968b233768642d46", - "reference": "08b09f2e98fc6830050c0237968b233768642d46", + "url": "https://api.github.com/repos/pestphp/pest/zipball/477d20a54fd9329ddfb0f8d4eb90dca7bc81b027", + "reference": "477d20a54fd9329ddfb0f8d4eb90dca7bc81b027", "shasum": "" }, "require": { @@ -9128,12 +9073,12 @@ "pestphp/pest-plugin-mutate": "^4.0.1", "pestphp/pest-plugin-profanity": "^4.1.0", "php": "^8.3.0", - "phpunit/phpunit": "^12.4.0", + "phpunit/phpunit": "^12.4.1", "symfony/process": "^7.3.4" }, "conflict": { "filp/whoops": "<2.18.3", - "phpunit/phpunit": ">12.4.0", + "phpunit/phpunit": ">12.4.1", "sebastian/exporter": "<7.0.0", "webmozart/assert": "<1.11.0" }, @@ -9207,7 +9152,7 @@ ], "support": { "issues": "https://github.com/pestphp/pest/issues", - "source": "https://github.com/pestphp/pest/tree/v4.1.2" + "source": "https://github.com/pestphp/pest/tree/v4.1.3" }, "funding": [ { @@ -9219,7 +9164,7 @@ "type": "github" } ], - "time": "2025-10-05T19:09:49+00:00" + "time": "2025-10-29T22:45:27+00:00" }, { "name": "pestphp/pest-plugin", @@ -10365,16 +10310,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.4.0", + "version": "12.4.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f62aab5794e36ccd26860db2d1bbf89ac19028d9" + "reference": "fc5413a2e6d240d2f6d9317bdf7f0a24e73de194" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f62aab5794e36ccd26860db2d1bbf89ac19028d9", - "reference": "f62aab5794e36ccd26860db2d1bbf89ac19028d9", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fc5413a2e6d240d2f6d9317bdf7f0a24e73de194", + "reference": "fc5413a2e6d240d2f6d9317bdf7f0a24e73de194", "shasum": "" }, "require": { @@ -10442,7 +10387,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.4.0" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.4.1" }, "funding": [ { @@ -10466,7 +10411,7 @@ "type": "tidelift" } ], - "time": "2025-10-03T04:28:03+00:00" + "time": "2025-10-09T14:08:29+00:00" }, { "name": "rector/rector", @@ -11585,6 +11530,64 @@ } ], "time": "2024-03-03T12:36:25+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.12.1", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^7.2 || ^8.0" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.12.1" + }, + "time": "2025-10-29T15:56:20+00:00" } ], "aliases": [], diff --git a/resources/views/livewire/catalog/index.blade.php b/resources/views/livewire/catalog/index.blade.php index 5bdae10..94d0d2a 100644 --- a/resources/views/livewire/catalog/index.blade.php +++ b/resources/views/livewire/catalog/index.blade.php @@ -83,7 +83,13 @@ new class extends Component { $this->installingPlugin = $pluginId; try { - $importedPlugin = $pluginImportService->importFromUrl($plugin['zip_url'], auth()->user(), $plugin['zip_entry_path'] ?? null); + $importedPlugin = $pluginImportService->importFromUrl( + $plugin['zip_url'], + auth()->user(), + $plugin['zip_entry_path'] ?? null, + null, + $plugin['logo_url'] ?? null + ); $this->dispatch('plugin-installed'); Flux::modal('import-from-catalog')->close(); diff --git a/resources/views/livewire/catalog/trmnl.blade.php b/resources/views/livewire/catalog/trmnl.blade.php index 4d1a60f..248ab9f 100644 --- a/resources/views/livewire/catalog/trmnl.blade.php +++ b/resources/views/livewire/catalog/trmnl.blade.php @@ -21,7 +21,7 @@ new class extends Component { private function loadNewest(): void { try { - $this->recipes = Cache::remember('trmnl_recipes_newest', 300, function () { + $this->recipes = Cache::remember('trmnl_recipes_newest', 43200, function () { $response = Http::get('https://usetrmnl.com/recipes.json', [ 'sort-by' => 'newest', ]); @@ -92,10 +92,14 @@ new class extends Component { try { $zipUrl = "https://usetrmnl.com/api/plugin_settings/{$recipeId}/archive"; + $recipe = collect($this->recipes)->firstWhere('id', $recipeId); + $plugin = $pluginImportService->importFromUrl( $zipUrl, auth()->user(), - preferredRenderer: config('services.trmnl.liquid_enabled') ? 'trmnl-liquid' : null + null, + config('services.trmnl.liquid_enabled') ? 'trmnl-liquid' : null, + $recipe['icon_url'] ?? null ); $this->dispatch('plugin-installed'); @@ -196,14 +200,14 @@ new class extends Component {