diff --git a/app/Http/Controllers/Api/Client/Servers/AddonController.php b/app/Http/Controllers/Api/Client/Servers/AddonManager/AddonController.php similarity index 75% rename from app/Http/Controllers/Api/Client/Servers/AddonController.php rename to app/Http/Controllers/Api/Client/Servers/AddonManager/AddonController.php index 95d1ee5ab9..295aa141e1 100755 --- a/app/Http/Controllers/Api/Client/Servers/AddonController.php +++ b/app/Http/Controllers/Api/Client/Servers/AddonManager/AddonController.php @@ -1,12 +1,14 @@ json(['error' => 'Unsupported egg'], 400); } - $indexDirectory = $serverFileRepo->getDirectory("$addonDirectory.index/"); - $addons = []; + $cacheKey = "server_{$server->id}_addons"; + $taskmaster = new Taskmaster(); + $taskmaster->autoDetectWorkers(8); - foreach ($indexDirectory as $file) { - $filename = $file['name']; - $content = $serverFileRepo->getContent("$addonDirectory.index/$filename"); - $addonData = Toml::parse($content); - $addons[] = $addonData; - } + $addons = Cache::remember($cacheKey, now()->addMinutes(30), function () use ($serverFileRepo, $addonDirectory, $taskmaster) { + $addons = []; + $tasks = []; + $indexDirectory = $serverFileRepo->getDirectory("$addonDirectory.index/"); + + foreach ($indexDirectory as $file) { + $filename = $file['name']; + $filePath = "$addonDirectory.index/$filename"; + + // Create a new task for each file and run them + $task = new ReadAndParseTask($serverFileRepo, $filePath); + $tasks[] = $task; + $taskmaster->runTask($task); + } + + $taskmaster->wait(); + + // Collect results from all tasks + foreach ($tasks as $task) { + $addons[] = $task->getResult(); + } + + $taskmaster->stop(); + + return $addons; + }); return response()->json($addons); } diff --git a/app/Http/Controllers/Api/Client/Servers/AddonManager/ReadAndParseTask.php b/app/Http/Controllers/Api/Client/Servers/AddonManager/ReadAndParseTask.php new file mode 100644 index 0000000000..bfc3750988 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Servers/AddonManager/ReadAndParseTask.php @@ -0,0 +1,22 @@ +serverFileRepo = $serverFileRepo; + $this->filePath = $filePath; + } + + public function run(): mixed + { + $content = $this->serverFileRepo->getContent($this->filePath); + return Toml::parse($content); + } +} diff --git a/composer.json b/composer.json index db058bc8b6..ea0cd0812a 100755 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "ext-pdo_mysql": "*", "ext-posix": "*", "ext-zip": "*", + "aternos/taskmaster": "^1.2", "aws/aws-sdk-php": "~3.260.1", "doctrine/dbal": "~3.6.0", "guzzlehttp/guzzle": "~7.5.0", diff --git a/composer.lock b/composer.lock index 09cb0abf4f..750c1bcc75 100755 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,56 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "98035bd4fc462b77aaaa2973dfc379b1", + "content-hash": "4467b3f0d0ce595f0707fce8c743da68", "packages": [ + { + "name": "aternos/taskmaster", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/aternosorg/taskmaster.git", + "reference": "fb5660fe3f8d58871b9b9f980b17324071833502" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aternosorg/taskmaster/zipball/fb5660fe3f8d58871b9b9f980b17324071833502", + "reference": "fb5660fe3f8d58871b9b9f980b17324071833502", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpbench/phpbench": "^1.2", + "phpunit/phpunit": "^10.4" + }, + "suggest": { + "ext-parallel": "Required for thread workers", + "ext-pcntl": "Required for fork workers" + }, + "type": "library", + "autoload": { + "psr-4": { + "Aternos\\Taskmaster\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Neid", + "email": "matthias@aternos.org" + } + ], + "description": "Object-oriented PHP library for running tasks in parallel", + "support": { + "issues": "https://github.com/aternosorg/taskmaster/issues", + "source": "https://github.com/aternosorg/taskmaster/tree/v1.2.0" + }, + "time": "2024-07-10T11:51:17+00:00" + }, { "name": "aws/aws-crt-php", "version": "v1.0.4", diff --git a/package-lock.json b/package-lock.json old mode 100644 new mode 100755 diff --git a/resources/scripts/components/elements/AddonItem.tsx b/resources/scripts/components/elements/AddonItem.tsx old mode 100644 new mode 100755 diff --git a/resources/scripts/components/server/addons/AddonsContainer.tsx b/resources/scripts/components/server/addons/AddonsContainer.tsx index 5607a1e867..a7f50e0ab8 100755 --- a/resources/scripts/components/server/addons/AddonsContainer.tsx +++ b/resources/scripts/components/server/addons/AddonsContainer.tsx @@ -1,7 +1,7 @@ +import { Addon, index as getIndex, search, limit, providers } from '@/api/server/addons'; import ServerContentBlock from '@/components/elements/ServerContentBlock'; import AddonItem from '@/components/elements/AddonItem'; import Spinner from '@/components/elements/Spinner'; -import { Addon, index as getIndex, search, limit, providers } from '@/api/server/addons'; import React, { useState, useEffect } from 'react'; import { ServerContext } from '@/state/server'; import Select from '@/components/elements/Select'; @@ -25,19 +25,23 @@ const AddonContainer = () => { const handler = setTimeout(async () => { const fetchAddons = async () => { setLoading(true); - const searchData = (await search(uuid, searchTerm, pageSize, provider)).data; - const indexData = (await getIndex(uuid)).data; + try { + const searchData = (await search(uuid, searchTerm, pageSize, provider)).data; + const indexData = (await getIndex(uuid)).data; - const addonsData = searchData.hits.map((hit) => { - const isInstalled = indexData.some((indexed) => { - if (!indexed.update.modrinth) return false; - return indexed.update.modrinth['mod-id'] === hit.project_id; - }); + const addonsData = searchData.hits.map((hit) => { + const isInstalled = indexData.some((indexed) => { + if (!indexed.update.modrinth) return false; + return indexed.update.modrinth['mod-id'] === hit.project_id; + }); - return { ...hit, isInstalled }; - }); + return { ...hit, isInstalled }; + }); - setAddons(addonsData); + setAddons(addonsData); + } catch (error) { + console.error(error); + } setLoading(false); }; diff --git a/resources/scripts/components/server/console/Console.tsx b/resources/scripts/components/server/console/Console.tsx old mode 100644 new mode 100755 diff --git a/routes/api-client.php b/routes/api-client.php index c99c662968..32600fe6d8 100755 --- a/routes/api-client.php +++ b/routes/api-client.php @@ -91,10 +91,10 @@ }); Route::group(['prefix' => '/addons'], function () { - Route::get('/', [Client\Servers\AddonController::class, 'index']); - Route::get('/search', [Client\Servers\AddonController::class, 'search']); - Route::get('/download', [Client\Servers\AddonController::class, 'download']); - Route::get('/versions', [Client\Servers\AddonController::class, 'getVersions']); + Route::get('/', [Client\Servers\AddonManager\AddonController::class, 'index']); + Route::get('/search', [Client\Servers\AddonManager\AddonController::class, 'search']); + Route::get('/download', [Client\Servers\AddonManager\AddonController::class, 'download']); + Route::get('/versions', [Client\Servers\AddonManager\AddonController::class, 'getVersions']); }); Route::group(['prefix' => '/schedules'], function () {