Files
2024-12-24 10:33:18 +01:00

430 lines
112 KiB
JSON
Executable File

{
"./app/Transformers/Api/Client/ServerTransformer.php": [
{
"type": "under",
"theme": "any",
"where": "'name' => $server->name,",
"add": " 'nest_id' => $server->nest_id,\n 'egg_id' => $server->egg_id,"
}
],
"./app/Http/Controllers/Admin/Bagou/": [
{
"type": "folder"
}
],
"./app/Http/Controllers/Admin/Bagou/BagouCenterController.php": [
{
"type": "newfile",
"theme": "any",
"add": "<?php\n\nnamespace Pterodactyl\\Http\\Controllers\\Admin\\Bagou;\n\nuse Illuminate\\View\\View;\nuse Prologue\\Alerts\\AlertsMessageBag;\nuse Pterodactyl\\Http\\Controllers\\Controller;\nuse Illuminate\\Support\\Facades\\Http;\n\nclass BagouCenterController extends Controller\n{\n protected $alert;\n\n public function __construct(\n AlertsMessageBag $alert\n ) {\n $this->alert = $alert;\n }\n /**\n * Display licensing system\n */\n public function index(): View\n {\n $apistatus = Http::get('https://api.bagou450.com/api/client/pterodactyl/checkOnline')->object();\n $cdnstatus = Http::get('https://cdn.bagou450.com/status')->object();\n\n return view('admin.bagoucenter.index', ['apistatus' => $apistatus, 'cdnstatus' => $cdnstatus]);\n }\n public function settings(): View\n {\n return view('admin.bagoucenter.support.index');\n }\n}\n"
}
],
"./app/Http/Controllers/Admin/Bagou/BagouLicenseController.php": [
{
"type": "newfile",
"theme": "any",
"add": "<?php\n\nnamespace Pterodactyl\\Http\\Controllers\\Admin\\Bagou;\n\nuse Illuminate\\View\\View;\nuse Pterodactyl\\Models\\Bagoulicense;\nuse Illuminate\\Http\\RedirectResponse;\nuse Prologue\\Alerts\\AlertsMessageBag;\nuse Pterodactyl\\Http\\Controllers\\Controller;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Support\\Facades\\Http;\n\nclass BagouLicenseController extends Controller\n{\n protected $alert;\n\n public function __construct(\n AlertsMessageBag $alert\n ) {\n $this->alert = $alert;\n }\n /**\n * Display licensing system\n */\n public function index(): View\n {\n $addonslist = Http::get('https://api.bagou450.com/api/client/pterodactyl/addonsList')->json();\n $licenses = Bagoulicense::all();\n return view('admin.bagoucenter.license.index', ['addonslist' => $addonslist, 'licenses' => $licenses]);\n }\n\n /**\n * Display license of the addon\n *\n * @throws \\Pterodactyl\\Exceptions\\Repository\\RecordNotFoundException\n */\n public function license(string $addon): View\n {\n $dbaddon = Bagoulicense::where('addon', $addon)->first();\n $addonslist = Http::get('https://api.bagou450.com/api/client/pterodactyl/addonsList')->json();\n $licenses = Bagoulicense::all();\n return view('admin.bagoucenter.license.license', [\n 'addon' => $addon,\n 'enabled' => ($dbaddon)? $dbaddon['enabled'] : 0,\n 'usage' => ($dbaddon) ? $dbaddon['usage'] : null ,\n 'maxusage' => ($dbaddon) ? $dbaddon['maxusage'] : null,\n 'license' => ($dbaddon) ? $dbaddon['license']: 'Your license',\n 'addonslist' => $addonslist,\n 'licenses' => $licenses\n ]);\n }\n\n /**\n * Set a license\n *\n * @throws \\Throwable\n */\n public function setlicense(Request $request, $addon): RedirectResponse\n { \n $license = Http::accept('application/json')->post(\"https://api.bagou450.com/api/client/pterodactyl/license\", [\n 'id' => $request->license,\n 'selectaddon' => $addon\n ])->object();\n if($license->message == 'Transaction is not valid.') {\n $this->alert->danger('License not found please contact me on discord')->flash();\n return redirect()->route('admin.bagoucenter.license.addon', $addon);\n } else if($license->message == 'Too many license usage please contact me on discord') {\n $this->alert->danger('License are already used on too many panel please contact me on discord')->flash();\n return redirect()->route('admin.bagoucenter.license.addon', $addon);\n } else if ($license->message == 'User blacklisted') {\n if(Bagoulicense::where('addon', $addon)->exists()) {\n\n Bagoulicense::where('addon', $addon)->update(['license' => $request->license, 'usage' => 1, 'maxusage' => 1, 'enabled' => false]);\n } else {\n Bagoulicense::create(['addon' => $addon, 'license' => $request->license, 'usage' => 1, 'maxusage' => 1, 'enabled' => false]);\n }\n $this->alert->danger('You are BLACKLISTED (probably because of a paypal dispute)')->flash();\n return redirect()->route('admin.bagoucenter.license.addon', $addon);\n } else if($license->message == 'Not the good addon') {\n $this->alert->danger('This license is not for this addon!')->flash();\n return redirect()->route('admin.bagoucenter.license.addon', $addon, );\n } else if($license->message == 'done' && $license->name == $addon && !$license->blacklisted) {\n if(Bagoulicense::where('addon', $addon)->exists()) {\n\n Bagoulicense::where('addon', $addon)->update(['license' => $request->license, 'usage' => $license->usage, 'maxusage' => $license->maxusage, 'enabled' => true]);\n } else {\n Bagoulicense::create(['addon' => $addon, 'license' => $request->license, 'usage' => $license->usage, 'maxusage' => $license->maxusage, 'enabled' => true]);\n }\n \n $this->alert->success('License updated sucessfully!')->flash();\n return redirect()->route('admin.bagoucenter.license.addon', $addon);\n }\n $this->alert->danger('Error!')->flash();\n return redirect()->route('admin.bagoucenter.license.addon', $addon);\n }\n\n /**\n * Rmove a license\n *\n * @throws \\Throwable\n */\n public function removelicense($addon): RedirectResponse\n { \n if(Bagoulicense::where('addon', $addon)->exists()) {\n $transaction = Bagoulicense::where('addon', $addon)->first();\n if(!$transaction) {\n $this->alert->danger('No license found.')->flash();\n return redirect()->route('admin.bagoucenter.license.addon', $addon);\n }\n $transaction = $transaction['license'];\n $license = Http::delete(\"https://api.bagou450.com/api/client/pterodactyl/license?id=$transaction\")->object();\n Bagoulicense::where('addon', $addon)->delete();\n $this->alert->success('License removed sucessfully')->flash();\n return redirect()->route('admin.bagoucenter.license.addon', $addon); \n } else {\n $this->alert->danger('No license found.')->flash();\n return redirect()->route('admin.bagoucenter.license.addon', $addon); \n }\n\n }\n}\n"
}
],
"./app/Http/Controllers/Admin/Bagou/BagouSettingsController.php": [
{
"type": "newfile",
"theme": "any",
"add": "<?php\n\nnamespace Pterodactyl\\Http\\Controllers\\Admin\\Bagou;\n\nuse Illuminate\\View\\View;\nuse Pterodactyl\\Models\\Bagoulicense;\nuse Prologue\\Alerts\\AlertsMessageBag;\nuse Pterodactyl\\Http\\Controllers\\Controller;\nuse Illuminate\\Support\\Facades\\Http;\n\nclass BagouSettingsController extends Controller\n{\n protected $alert;\n\n public function __construct(\n AlertsMessageBag $alert\n ) {\n $this->alert = $alert;\n }\n /**\n * Display licensing system\n */\n public function index(): View\n {\n $addonslist = Http::get('https://api.bagou450.com/api/client/pterodactyl/addonsList')->json();\n $licenses = Bagoulicense::all();\n return view('admin.bagoucenter.settings.index', [ 'addonslist' => $addonslist,\n 'licenses' => $licenses]);\n }\n\n /**\n * Display licensing system\n */\n public function addon(string $addon): View\n {\n $addonslist = Http::get('https://api.bagou450.com/api/client/pterodactyl/addonsList')->json();\n $licenses = Bagoulicense::all();\n return view($addon['tabroute'], ['addonslist' => $addonslist,\n 'licenses' => $licenses]);\n }\n}\n"
}
],
"./app/Http/Controllers/Admin/Bagou/BagouVersionsController.php": [
{
"type": "newfile",
"theme": "any",
"add": "<?php\n\nnamespace Pterodactyl\\Http\\Controllers\\Admin\\Bagou;\n\nuse Illuminate\\View\\View;\nuse Pterodactyl\\Models\\Bagoulicense;\nuse Prologue\\Alerts\\AlertsMessageBag;\nuse Pterodactyl\\Http\\Controllers\\Controller;\nuse Illuminate\\Support\\Facades\\Http;\n\nclass BagouVersionsController extends Controller\n{\n protected AlertsMessageBag $alert;\n\n public function __construct(\n AlertsMessageBag $alert\n ) {\n $this->alert = $alert;\n }\n /**\n * Display licensing system\n */\n public function index(): View\n {\n $addonslist = Http::get('https://api.bagou450.com/api/client/pterodactyl/addonsList')->json();\n $licenses = Bagoulicense::all();\n return view('admin.bagoucenter.versions.index', ['addonslist' => $addonslist, 'licenses' => $licenses]);\n }\n /**\n * Refresh Versions\n */\n public function refresh(): View\n {\n $licenses = Bagoulicense::all();\n foreach($licenses as $license) {\n $id = $license['license'];\n $version = Http::get(\"https://api.bagou450.com/api/client/pterodactyl/getVersion?id=$id\");\n if($version->failed()) {\n $addonslist = Http::get('https://api.bagou450.com/api/client/pterodactyl/addonsList')->json();\n $this->alert->danger('Error!')->flash();\n return view('admin.bagoucenter.versions.index', ['addonslist' => $addonslist, 'licenses' => $licenses]);\n } else {\n Bagoulicense::where('license', '=', $id)->update(['version' => $version->json()['version']]);\n }\n }\n $addonslist = Http::get('https://api.bagou450.com/api/client/pterodactyl/addonsList')->json();\n $licenses = Bagoulicense::all();\n\n $this->alert->success('Versions updated sucessfully!')->flash();\n return view('admin.bagoucenter.versions.index', ['addonslist' => $addonslist, 'licenses' => $licenses]);\n }\n}\n"
}
],
"./app/Http/Controllers/Api/Client/Servers/McPluginsController.php": [
{
"type": "newfile",
"theme": "any",
"add": "<?php\n\nnamespace Pterodactyl\\Http\\Controllers\\Api\\Client\\Servers;\n\nuse Pterodactyl\\Models\\Server;\nuse Pterodactyl\\Http\\Controllers\\Api\\Client\\ClientApiController;\nuse Pterodactyl\\Facades\\Activity;\nuse Pterodactyl\\Repositories\\Eloquent\\ServerRepository;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Support\\Facades\\Http;\nuse Pterodactyl\\Models\\Mcplugins;\nuse Pterodactyl\\Models\\Bagoulicense;\nuse Symfony\\Component\\HttpKernel\\Exception\\BadRequestHttpException;\nuse Pterodactyl\\Repositories\\Wings\\DaemonFileRepository;\nclass McPluginsController extends ClientApiController\n{\n /**\n * @var \\Pterodactyl\\Repositories\\Eloquent\\ServerRepository\n */\n private $repository;\n \n /**\n * @var \\Pterodactyl\\Repositories\\Wings\\DaemonFileRepository\n */\n private $fileRepository;\n /**\n * @var \\Pterodactyl\\Services\\Servers\\ServerCreationService\n */\n public function __construct(\n ServerRepository $repository,\n DaemonFileRepository $fileRepository\n\n )\n {\n parent::__construct();\n\n $this->repository = $repository;\n $this->fileRepository = $fileRepository;\n\n }\n\n public function plugins(Server $server, Request $request)\n { \n $license = Bagoulicense::where('addon', 326)->first();\n if(!$license) {\n return new BadRequestHttpException('No license for this addons please setup the license trough admin tab.');\n }\n $license = $license['license'];\n //dd(Mcplugins::where('plugin', \"Lib's Disguises [Free]\")->where('server_id', $server->id)->value('installdate')->toIso8601String());\n if ($request->type == '1') {\n if ($request->searchFilter) {\n return $this->addinstalled($server, Http::accept('application/json')->get(\"https://api.bagou450.com/api/client/pterodactyl/plugins/spigot?id=$license&page=$request->page&size=20&search=$request->searchFilter&category=4&sort=downloads&field=name\")->json());\n } else {\n $plugins = Http::accept('application/json')->get(\"https://api.bagou450.com/api/client/pterodactyl/plugins/spigot?id=$license&category=$request->category&size=20&page=$request->page&sort=-downloads&fields=id,name,tag,file,testedVersions,links,external,version,author,category,rating,icon,releaseDate,updateDate,downloads,premium&version=$request->version\")->json();\n return $this->addinstalled($server, $plugins);\n }\n } else if($request->type == '2') {\n if ($request->searchFilter) {\n $url = \"https://api.bagou450.com/api/client/pterodactyl/plugins/bukkit?id=$license&page=$request->page?&searchFilter=$request->searchFilter&search=$request->searchFilter&version=$request->version\";\n } else {\n $url = \"https://api.bagou450.com/api/client/pterodactyl/plugins/bukkit?id=$license&page=$request->page&search=$request->searchFilter&version=$request->version\";\n }\n return $this->addinstalled($server, Http::accept('application/json')->get($url)->json()); \n } else if($request->type == '3') {\n $url = $_SERVER['SERVER_NAME'];\n $page = $request->page*10;\n $plugins = Http::accept('application/json')->get(\"https://api.bagou450.com/api/client/pterodactyl/plugins/polymart?id=$license&page=$request->page&search=$request->searchFilter&version=$request->version\")->object(); \n foreach ($plugins as $key => $plugin) {\n $plugins[$key]->installed = 0;\n $plugins[$key]->installedate = date('Y-m-d H:i');\n if (Mcplugins::where('plugin', $plugins[$key]->title)->where('server_id', $server['id'])->exists())\n {\n $date = Mcplugins::where('plugin', $plugins[$key]->title)->where('server_id', $server['id'])->value('created_at')->toIso8601String();\n $plugins[$key]->installedate = $date;\n $plugins[$key]->installed = 1;\n \n } \n \n }\n\n return json_encode($plugins);\n \n } else if($request->type == '4') {\n $url = $_SERVER['SERVER_NAME'];\n $plugins = file_get_contents(\"/var/www/pterodactyl/public/pluginslist.json\"); \n return json_decode($plugins);\n } else if($request->type == '5') {\n $files = $this->fileRepository\n ->setServer($server)\n ->getDirectory('/plugins');\n $tmparray = [];\n foreach($files as $key => $file) {\n if(!$files[$key]['directory']) {\n $files[$key]['name'] = substr($files[$key]['name'], 0, strrpos($files[$key]['name'], \".\"));;\n array_push($tmparray, $files[$key]);\n }\n }\n return $tmparray;\n\n } else {\n throw new BadRequestHttpException('This url is invalid');\n return;\n }\n }\n public function install(Server $server, Request $request) \n {\n $license = Bagoulicense::where('addon', 326)->first();\n if(!$license) {\n return new BadRequestHttpException('No license for this addons please setup the license trough admin tab.');\n }\n $license = $license['license'];\n if(!Mcplugins::where('plugin', $request->plugin)->where('server_id', $server->id)->exists()) {\n if(str_starts_with($request->url, 'polymart')) {\n $link = Http::get(\"https://api.bagou450.com/api/client/pterodactyl/plugins/download?id=$license&url=$request->url\")->json()['url'];\n\n } else {\n $link = $request->url;\n }\n $this->fileRepository->setServer($server)->putContent(\"plugins/$request->plugin.jar\", file_get_contents($link));\n $plugin = Mcplugins::create([\n 'server_id' => $server->id,\n 'plugin' => $request->plugin,\n ]); \n \n\n Activity::event('server:plugin.install')\n ->property('name', $plugin->plugin)\n ->log();\n \n return;\n } else {\n return new BadRequestHttpException('This plugin is already installed');\n }\n\n }\n public function uninstall(Server $server, Request $request) \n {\n Activity::event('server:plugin.uninstall')\n ->property('name', $request->plugin)\n ->log();\n Mcplugins::where('server_id', $server->id)->where('plugin', $request->plugin)->delete();\n return;\n\n }\n private function addinstalled(Server $server, array $plugins) {\n foreach ($plugins as $key => $plugin) {\n $plugins[$key]['installed'] = 0;\n $plugins[$key]['installedate'] = date('Y-m-d H:i');\n if (Mcplugins::where('plugin', $plugins[$key]['name'])->where('server_id', $server->id)->exists())\n {\n $date = Mcplugins::where('plugin', $plugins[$key]['name'])->where('server_id', $server->id)->value('created_at')->toIso8601String();\n $plugins[$key]['installedate'] = $date;\n $plugins[$key]['installed'] = 1;\n \n }\n \n\n }\n return $plugins;\n }\n public function getVersions(Request $request)\n {\n $license = Bagoulicense::where('addon', 326)->first();\n if(!$license) {\n return new BadRequestHttpException('No license for this addons please setup the license trough admin tab.');\n }\n $license = $license['license'];\n return Http::get(\"https://api.bagou450.com/api/client/pterodactyl/plugins/getVersions?id=$license&type=$request->type&pluginId=$request->pluginId&page=$request->page\");\n }\n public function getMcVersions(Request $request)\n {\n $license = Bagoulicense::where('addon', 326)->first();\n if(!$license) {\n return new BadRequestHttpException('No license for this addons please setup the license trough admin tab.');\n }\n $license = $license['license'];\n\n return Http::accept('application/json')->get('https://api.bagou450.com/api/client/pterodactyl/plugins/getMcVersions', ['id' => $license, 'type' => $request->type]);\n }\n}\n\n"
}
],
"./app/Models/Bagoulicense.php": [
{
"type": "newfile",
"theme": "any",
"add": "<?php\n\nnamespace Pterodactyl\\Models;\n\n/**\n * @property int $id\n * @property string $uuid\n * @property int $nest_id\n * @property string $author\n * @property string $name\n * @property string|null $description\n * @property array|null $features\n * @property string $docker_image -- deprecated, use $docker_images\n * @property string $update_url\n * @property array<string, string> $docker_images\n * @property array|null $file_denylist\n * @property string|null $config_files\n * @property string|null $config_startup\n * @property string|null $config_logs\n * @property string|null $config_stop\n * @property int|null $config_from\n * @property string|null $startup\n * @property bool $script_is_privileged\n * @property string|null $script_install\n * @property string $script_entry\n * @property string $script_container\n * @property int|null $copy_script_from\n * @property \\Carbon\\Carbon $created_at\n * @property \\Carbon\\Carbon $updated_at\n * @property string|null $copy_script_install\n * @property string $copy_script_entry\n * @property string $copy_script_container\n * @property string|null $inherit_config_files\n * @property string|null $inherit_config_startup\n * @property string|null $inherit_config_logs\n * @property string|null $inherit_config_stop\n * @property string $inherit_file_denylist\n * @property array|null $inherit_features\n */\nclass BagouLicense extends Model\n{\n /**\n * The resource name for this model when it is transformed into an\n * API representation using fractal.\n */\n public const RESOURCE_NAME = 'BagouLicense';\n\n /**\n * Defines the current egg export version.\n */\n public const EXPORT_VERSION = 'PTDL_v2';\n\n /**\n * The table associated with the model.\n *\n * @var string\n */\n protected $table = 'bagoulicense';\n\n /**\n * Fields that are not mass assignable.\n *\n * @var array\n */\n protected $fillable = [\n 'addon',\n 'license',\n 'usage',\n 'maxusage',\n 'enabled'\n ];\n\n /**\n * Cast values to correct type.\n *\n * @var array\n */\n protected $casts = [\n 'addon' => 'string',\n 'license' => 'string',\n 'usage' => 'integer',\n 'maxusage' => 'integer',\n 'enabled' => 'boolean'\n ];\n\n /**\n * @var array\n */\n public static array $validationRules = [\n 'addon' => 'required|string',\n 'license' => 'required|string',\n ];\n\n}\n"
}
],
"./app/Models/Mcplugins.php": [
{
"type": "newfile",
"theme": "any",
"add": "<?php\n\nnamespace Pterodactyl\\Models;\n\n/**\n * @property int $id\n * @property string $uuid\n * @property int $nest_id\n * @property string $author\n * @property string $name\n * @property string|null $description\n * @property array|null $features\n * @property string $docker_image -- deprecated, use $docker_images\n * @property string $update_url\n * @property array<string, string> $docker_images\n * @property array|null $file_denylist\n * @property string|null $config_files\n * @property string|null $config_startup\n * @property string|null $config_logs\n * @property string|null $config_stop\n * @property int|null $config_from\n * @property string|null $startup\n * @property bool $script_is_privileged\n * @property string|null $script_install\n * @property string $script_entry\n * @property string $script_container\n * @property int|null $copy_script_from\n * @property \\Carbon\\Carbon $created_at\n * @property \\Carbon\\Carbon $updated_at\n * @property string|null $copy_script_install\n * @property string $copy_script_entry\n * @property string $copy_script_container\n * @property string|null $inherit_config_files\n * @property string|null $inherit_config_startup\n * @property string|null $inherit_config_logs\n * @property string|null $inherit_config_stop\n * @property string $inherit_file_denylist\n * @property array|null $inherit_features\n */\nclass Mcplugins extends Model\n{\n /**\n * The resource name for this model when it is transformed into an\n * API representation using fractal.\n */\n public const RESOURCE_NAME = 'Mcplugins';\n\n /**\n * Defines the current egg export version.\n */\n public const EXPORT_VERSION = 'PTDL_v2';\n\n /**\n * The table associated with the model.\n *\n * @var string\n */\n protected $table = 'mcplugins';\n\n /**\n * Fields that are not mass assignable.\n *\n * @var array\n */\n protected $fillable = [\n 'server_id',\n 'plugin',\n ];\n\n /**\n * Cast values to correct type.\n *\n * @var array\n */\n protected $casts = [\n 'server_id' => 'integer',\n 'plugin' => 'string',\n ];\n\n /**\n * @var array\n */\n public static array $validationRules = [\n 'server_id' => 'required|numeric|exists:servers,id',\n 'plugin' => 'required|string',\n ];\n\n /**\n * Gets the server associated with a plugin.\n *\n * @return \\Illuminate\\Database\\Eloquent\\Relations\\BelongsTo\n */\n public function server()\n {\n return $this->belongsTo(Server::class);\n }\n}\n"
}
],
"./app/Transformers/Api/Client/McPluginsTransformer.php": [
{
"type": "newfile",
"theme": "any",
"add": "<?php\n\nnamespace Pterodactyl\\Transformers\\Api\\Client;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Arr;\nuse Pterodactyl\\Models\\Mcplugins;\n\nclass McPluginsTransformer extends BaseClientTransformer\n{\n /**\n * Transform a file object response from the daemon into a standardized response.\n *\n * @return array\n */\n public function transform(Mcplugins $modal)\n {\n return [\n 'server_id' => $modal->server_id,\n 'plugin' => $modal->plugin,\n ];\n }\n\n public function getResourceName(): string\n {\n return 'Mcplugins';\n }\n}\n"
}
],
"./database/migrations/2021_10_31_102410_create_mcplugins_table.php": [
{
"type": "newfile",
"theme": "any",
"add": "<?php\n\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Database\\Migrations\\Migration;\n\nclass CreateMcPluginsTable extends Migration\n{\n /**\n * Run the migrations.\n *\n * @return void\n */\n public function up()\n {\n Schema::create('mcplugins', function (Blueprint $table) {\n $table->id();\n $table->string('server');\n $table->string('plugin');\n $table->date('installdate');\n });\n }\n /**\n * Reverse the migrations.\n *\n * @return void\n */\n public function down()\n {\n Schema::dropIfExists('mcplugins');\n }\n}\n"
}
],
"./database/migrations/2022_07_04_121110_rename_server_column_to_serverid_on_mcplugins_table.php": [
{
"type": "newfile",
"theme": "any",
"add": "<?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nclass RenameServerColumnToServeridOnMcpluginsTable extends Migration\n{\n /**\n * Run the migrations.\n *\n * @return void\n */\n public function up()\n {\n Schema::table('mcplugins', function (Blueprint $table) {\n $table->renameColumn('server', 'server_id');\n });\n }\n\n /**\n * Reverse the migrations.\n *\n * @return void\n */\n public function down()\n {\n Schema::table('mcplugins', function (Blueprint $table) {\n $table->renameColumn('server_id', 'server');\n });\n }\n}\n"
}
],
"./database/migrations/2022_07_04_130458_add_createdat_and_updatedat_field_to_mcplugins.php": [
{
"type": "newfile",
"theme": "any",
"add": "<?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nclass AddCreatedatAndUpdatedatFieldToMcplugins extends Migration\n{\n /**\n * Run the migrations.\n *\n * @return void\n */\n public function up()\n {\n Schema::table('mcplugins', function (Blueprint $table) {\n if (Schema::hasColumn('mcplugins', 'installdate'))\n {\n $table->dropColumn('installdate');\n }\n $table->timestamp('created_at')->useCurrent();\n $table->timestamp('updated_at')->useCurrent();\n\n });\n }\n\n /**\n * Reverse the migrations.\n *\n * @return void\n */\n public function down()\n {\n Schema::table('mcplugins', function (Blueprint $table) {\n $table->dropColumn('updated_at');\n $table->dropColumn('created_at');\n\n });\n }\n}\n"
}
],
"./database/migrations/2022_07_04_151819_create_bagoulicense_table.php": [
{
"type": "newfile",
"theme": "any",
"add": "<?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nclass CreateBagoulicenseTable extends Migration\n{\n /**\n * Run the migrations.\n *\n * @return void\n */\n public function up()\n {\n Schema::create('bagoulicense', function (Blueprint $table) {\n $table->id();\n $table->string('addon');\n $table->string('license');\n $table->integer('usage');\n $table->integer('maxusage');\n $table->boolean('enabled');\n $table->timestamps();\n });\n }\n\n /**\n * Reverse the migrations.\n *\n * @return void\n */\n public function down()\n {\n Schema::dropIfExists('bagoulicense');\n }\n}\n"
}
],
"./database/migrations/2022_10_24_133158_add_version_field_to_bagoulicense_table": [
{
"type": "newfile",
"theme": "any",
"add": "<?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nclass AddVersionFieldToBagoulicenseTable extends Migration\n{\n /**\n * Run the migrations.\n *\n * @return void\n */\n public function up()\n {\n Schema::table('bagoulicense', function (Blueprint $table) {\n $table->decimal('version')->default(0);\n });\n }\n\n /**\n * Reverse the migrations.\n *\n * @return void\n */\n public function down()\n {\n Schema::table('bagoulicense', function (Blueprint $table) {\n $table->dropColumn('version');\n });\n }\n}"
}
],
"./public/pluginslist.json": [
{
"type": "newfile",
"theme": "any",
"add": "[\n {\n \"name\": \"Name of plugin\",\n \"downloadlink\": \"Need to be a direct download link\",\n \"filename\": \"Name of file\",\n \"links\": [\n {\n \"discussion\": \"Link of the plugins\"\n }\n ],\n \"file\": [\n {\n \"type\": \".jar\"\n }\n ],\n \"icon\": [\n {\n \"url\": \"\"\n }\n ],\n \"tag\": \"Plugin description\"\n },\n {\n \"name\": \"Name of plugin\",\n \"downloadlink\": \"Need to be a direct download link\",\n \"filename\": \"Name of file\",\n \"links\": [\n {\n \"discussion\": \"Link of the plugins\"\n }\n ],\n \"file\": [\n {\n \"type\": \".jar\"\n }\n ],\n \"icon\": [\n {\n \"url\": \"Icon url of plugin\"\n }\n ],\n \"tag\": \"Plugin description\"\n },\n {\n \"name\": \"Clearlagg\",\n \"downloadlink\": \"https://media.forgecdn.net/files/3198/633/Clearlag.jar\",\n \"filename\": \"Clearlag.jar\",\n \"links\": [\n {\n \"discussion\": \"https://dev.bukkit.org/projects/clearlagg\"\n }\n ],\n \"file\": [\n {\n \"type\": \".jar\"\n }\n ],\n \"icon\": [\n {\n \"url\": \"https://media.forgecdn.net/avatars/73/934/636163819268473934.png\"\n }\n ],\n \"tag\": \"A new perfect way to clear common lagg in your server!\"\n }\n]"
}
],
"./resources/scripts/api/server/mcplugins/": [
{
"type": "folder"
}
],
"./resources/scripts/api/server/mcplugins/getMinecraftPlugins.ts": [
{
"type": "newfile",
"theme": "any",
"add": "import useSWR from 'swr';\nimport http, { PaginatedResult } from '@/api/http';\nimport { createContext, useContext } from 'react';\n\ninterface ctx {\n page: number;\n setPage: (value: number | ((s: number) => number)) => void;\n searchFilter: string;\n setSearchFilter: (value: string | ((s: string) => string)) => void;\n version: string;\n setVersion: (value: string | ((s: string) => string)) => void;\n}\n\nexport const Context = createContext<ctx>({\n page: 1,\n setPage: () => 1,\n searchFilter: '',\n setSearchFilter: () => '',\n version: '',\n setVersion: () => '',\n});\n\nexport default (type: string, category: string, uuid: string) => {\n const { page, searchFilter, version } = useContext(Context);\n\n return useSWR<PaginatedResult<any>>(['server:minecraftPlugins', page, searchFilter, version], async () => {\n const { data } = await http.get(`/api/client/servers/${uuid}/plugins`, {\n params: { size: 20, page: page, type, category, searchFilter, version },\n timeout: 60000,\n });\n\n return {\n items: data || [],\n pagination: { total: 13170, count: data.length, perPage: 20, currentPage: page, totalPages: 1317 },\n };\n });\n};"
}
],
"./resources/scripts/api/server/mcplugins/getMinecraftVersions.ts": [
{
"type": "newfile",
"theme": "any",
"add": "import http from '@/api/http';\n\nexport default (uuid: string, type: string): Promise<any> => {\n return new Promise((resolve, reject) => {\n http\n .get(`/api/client/servers/${uuid}/plugins/getMcVersions`, { params: { type } })\n .then(({ data }) => resolve(data))\n .catch(reject);\n });\n};"
}
],
"./resources/scripts/api/server/mcplugins/getPluginsVersions.ts": [
{
"type": "newfile",
"theme": "any",
"add": "import useSWR from 'swr';\nimport http, { PaginatedResult } from '@/api/http';\nimport { createContext, useContext } from 'react';\n\ninterface ctx {\n page: number;\n setPage: (value: number | ((s: number) => number)) => void;\n}\n\nexport const Context = createContext<ctx>({ page: 1, setPage: () => 1 });\n\nexport default (uuid: string, type: string, pluginId: string) => {\n const { page } = useContext(Context);\n\n return useSWR<PaginatedResult<any>>([pluginId, page], async () => {\n const { data } = await http.get(`/api/client/servers/${uuid}/plugins/getVersions`, {\n params: { type, page, pluginId },\n timeout: 60000,\n });\n\n return {\n items: data || [],\n pagination: { total: 13170, count: data.length, perPage: 10, currentPage: page, totalPages: 1317 },\n };\n });\n};"
}
],
"./resources/scripts/api/server/mcplugins/installPlugin.ts": [
{
"type": "newfile",
"theme": "any",
"add": "import http from '@/api/http';\n\nexport default (uuid: string, plugin: string, url: string) => {\n return new Promise<void>((resolve, reject) => {\n http\n .post(`/api/client/servers/${uuid}/plugins/install`, {\n plugin,\n url,\n })\n .then(() => resolve())\n .catch(reject);\n });\n};\n"
}
],
"./resources/scripts/api/server/mcplugins/uninstallPlugin.ts": [
{
"type": "newfile",
"theme": "any",
"add": "import http from '@/api/http';\n\nexport default (uuid: string, plugin: string) => {\n return new Promise<void>((resolve, reject) => {\n http\n .post(`/api/client/servers/${uuid}/plugins/uninstall`, { plugin })\n .then(() => resolve())\n .catch(reject);\n });\n};\n"
}
],
"./resources/scripts/components/server/mcplugins/": [
{
"type": "folder"
}
],
"./resources/scripts/components/server/mcplugins/UploadButton.tsx": [
{
"type": "newfile",
"theme": "any",
"add": "import axios from 'axios';\nimport getFileUploadUrl from '@/api/server/files/getFileUploadUrl';\nimport tw from 'twin.macro';\nimport Button from '@/components/elements/Button';\nimport React, { useEffect, useRef, useState } from 'react';\nimport styled from 'styled-components';\nimport { ModalMask } from '@/components/elements/Modal';\nimport Fade from '@/components/elements/Fade';\nimport useEventListener from '@/plugins/useEventListener';\nimport SpinnerOverlay from '@/components/elements/SpinnerOverlay';\nimport useFlash from '@/plugins/useFlash';\nimport useFileManagerSwr from '@/plugins/useFileManagerSwr';\nimport { ServerContext } from '@/state/server';\nimport { WithClassname } from '@/components/types';\n\nconst InnerContainer = styled.div`\n max-width: 600px;\n ${tw`bg-black w-full border-4 border-primary-500 border-dashed rounded p-10 mx-10`}\n`;\n\nexport default ({ className }: WithClassname) => {\n const fileUploadInput = useRef<HTMLInputElement>(null);\n const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid);\n const [visible, setVisible] = useState(false);\n const [loading, setLoading] = useState(false);\n const { mutate } = useFileManagerSwr();\n const { clearFlashes, clearAndAddHttpError } = useFlash();\n const directory = '/plugins';\n useEventListener(\n 'dragenter',\n (e) => {\n e.stopPropagation();\n setVisible(true);\n },\n true\n );\n\n useEventListener(\n 'dragexit',\n (e) => {\n e.stopPropagation();\n setVisible(false);\n },\n true\n );\n\n useEffect(() => {\n if (!visible) return;\n\n const hide = () => setVisible(false);\n\n window.addEventListener('keydown', hide);\n return () => {\n window.removeEventListener('keydown', hide);\n };\n }, [visible]);\n\n const onFileSubmission = (files: FileList) => {\n const form = new FormData();\n Array.from(files).forEach((file) => form.append('files', file));\n\n setLoading(true);\n clearFlashes('files');\n getFileUploadUrl(uuid)\n .then((url) =>\n axios.post(`${url}&directory=${directory}`, form, {\n headers: {\n 'Content-Type': 'multipart/form-data',\n },\n })\n )\n .then(() => mutate())\n .catch((error) => {\n console.error(error);\n clearAndAddHttpError({ error, key: 'files' });\n })\n .then(() => setVisible(false))\n .then(() => setLoading(false));\n };\n\n return (\n <>\n <Fade appear in={visible} timeout={75} key={'upload_modal_mask'} unmountOnExit>\n <ModalMask\n onClick={() => setVisible(false)}\n onDragOver={(e) => e.preventDefault()}\n onDrop={(e) => {\n e.preventDefault();\n e.stopPropagation();\n\n setVisible(false);\n if (!e.dataTransfer?.files.length) return;\n\n onFileSubmission(e.dataTransfer.files);\n }}\n >\n <div css={tw`w-full flex items-center justify-center`} style={{ pointerEvents: 'none' }}>\n <InnerContainer>\n <p css={tw`text-lg text-neutral-200 text-center`}>Drag and drop files to upload a plugin.</p>\n </InnerContainer>\n </div>\n </ModalMask>\n </Fade>\n <SpinnerOverlay visible={loading} size={'large'} fixed />\n <input\n type={'file'}\n ref={fileUploadInput}\n css={tw`hidden`}\n onChange={(e) => {\n if (!e.currentTarget.files) return;\n\n onFileSubmission(e.currentTarget.files);\n if (fileUploadInput.current) {\n fileUploadInput.current.files = null;\n }\n }}\n />\n <Button\n className={className}\n onClick={() => {\n fileUploadInput.current ? fileUploadInput.current.click() : setVisible(true);\n }}\n >\n Upload\n </Button>\n </>\n );\n};\n"
}
],
"./resources/scripts/components/server/mcplugins/McPluginsRow.tsx": [
{
"type": "newfile",
"theme": "any",
"add": "import React, { useState } from 'react';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faDownload, faExternalLinkAlt, faListAlt, faStar, faTrash } from '@fortawesome/free-solid-svg-icons';\nimport tw from 'twin.macro';\nimport useFlash from '@/plugins/useFlash';\nimport GreyRowBox from '@/components/elements/GreyRowBox';\nimport Button from '@/components/elements/Button';\nimport deleteFiles from '@/api/server/files/deleteFiles';\nimport { ApplicationStore } from '@/state';\nimport { Actions, useStoreActions } from 'easy-peasy';\nimport installPlugin from '@/api/server/mcplugins/installPlugin';\nimport uninstallPlugin from '@/api/server/mcplugins/uninstallPlugin';\nimport Modal from '@/components/elements/Modal';\nimport McPluginsVersionsRow from './McPluginsVersionsRow';\nimport FlashMessageRender from '@/components/FlashMessageRender';\nimport { formatDistanceToNow } from 'date-fns';\nimport getMinecraftPlugins from '@/api/server/mcplugins/getMinecraftPlugins';\n\ninterface Props {\n minecraftPlugins: any;\n className?: string;\n uuid: string;\n type: string;\n category: string;\n nameoftype: string;\n}\n\nexport default ({ minecraftPlugins, className, uuid, type, category, nameoftype }: Props) => {\n const { clearAndAddHttpError } = useFlash();\n const [disable, setDisable] = useState(false);\n const [Installed, setInstalled] = useState(type === '4' ? 1 : minecraftPlugins.installed);\n const { addFlash, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);\n const { mutate } = getMinecraftPlugins(type, category, uuid);\n\n let iconurl = 'https://static.spigotmc.org/styles/spigot/xenresource/resource_icon.png';\n if (type !== '5') {\n if (type === '3') {\n iconurl = minecraftPlugins.thumbnailURL;\n minecraftPlugins.name = minecraftPlugins.title;\n minecraftPlugins.file = { type: 'internal' };\n minecraftPlugins.testedVersions = minecraftPlugins.supportedMinecraftVersions;\n minecraftPlugins.tag = minecraftPlugins.subtitle;\n minecraftPlugins.rating = { average: minecraftPlugins.averageReview };\n minecraftPlugins.downloadlink = `polymart-${minecraftPlugins.id}`;\n } else if (type === '2') {\n iconurl = minecraftPlugins.icon;\n } else if (minecraftPlugins.icon.url !== '' && minecraftPlugins.icon.url !== undefined) {\n iconurl =\n type === '1' ? 'https://www.spigotmc.org/' + minecraftPlugins.icon.url : minecraftPlugins.icon.url;\n }\n }\n const [visible, setVisible] = useState(false);\n const testedVersions = [];\n\n for (const versions in minecraftPlugins.testedVersions) {\n testedVersions.push(minecraftPlugins.testedVersions[versions]);\n }\n const install = () => {\n clearFlashes();\n setDisable(true);\n if (minecraftPlugins.file.type === 'external') {\n addFlash({\n key: 'server:minecraftMcPlugins' + nameoftype,\n type: 'warning',\n message:\n \"This plugins can't be installed because the plugins files was not on spigotmc please install it manualy from this url https://www.spigotmc.org/\" +\n minecraftPlugins.file.url +\n '. You can also see if this plugins are on bukkit with the bukkit tab.',\n });\n setDisable(false);\n mutate();\n } else {\n installPlugin(\n uuid,\n minecraftPlugins.name,\n type === '1'\n ? `https://cdn.spiget.org/file/spiget-resources/${minecraftPlugins.id}.jar`\n : minecraftPlugins.downloadlink\n )\n .then(() => {\n addFlash({\n key: 'server:minecraftMcPlugins' + nameoftype,\n type: 'success',\n message: 'Plugins installed successfully',\n });\n setDisable(false);\n setInstalled(1);\n mutate();\n })\n .catch(function (error) {\n setDisable(false);\n addFlash({\n key: 'server:minecraftMcPlugins' + nameoftype,\n type: 'error',\n message: `This plugin has too large a size, it must be installed manually (${error})`,\n });\n mutate();\n });\n }\n };\n const uninstall = () => {\n clearFlashes();\n setDisable(true);\n if (minecraftPlugins.file.type === 'external') {\n addFlash({\n key: 'server:minecraftMcPlugins' + nameoftype,\n type: 'warning',\n message:\n \"This plugins can't be removed because the plugins files was not on spigotmc please remove it manualy \",\n });\n mutate();\n setDisable(false);\n } else {\n deleteFiles(uuid, '/plugins', [`${minecraftPlugins.name}.jar`])\n .then(() => {\n uninstallPlugin(uuid, minecraftPlugins.name)\n .then(() => {\n addFlash({\n key: 'server:minecraftMcPlugins' + nameoftype,\n type: 'success',\n message: 'Plugins removed successfully',\n });\n setDisable(false);\n setInstalled(0);\n mutate();\n })\n .catch(function (error) {\n setDisable(false);\n clearAndAddHttpError({ key: 'minecraftMcPlugins', error });\n });\n })\n .catch(function (error) {\n setDisable(false);\n mutate();\n clearAndAddHttpError({ key: 'minecraftMcPlugins', error });\n });\n }\n };\n return (\n <GreyRowBox\n css={tw`flex-wrap md:flex-nowrap items-center grid grid-cols-2 sm:grid-rows-1 sm:flex`}\n className={className}\n >\n {['1', '2', '3'].includes(type) && (\n <Modal visible={visible} onDismissed={() => setVisible(false)}>\n <FlashMessageRender\n byKey={type === '1' ? minecraftPlugins.id : minecraftPlugins.name}\n css={tw`mb-4`}\n />\n <McPluginsVersionsRow\n minecraftPlugins={minecraftPlugins}\n nameoftype={nameoftype}\n installed={Installed}\n />\n </Modal>\n )}\n <div css={tw`flex items-center truncate md:w-full md:flex-1 `}>\n <div css={tw`flex flex-col truncate`}>\n <div\n css={tw`flex items-center text-sm mb-1 cursor-pointer `}\n onClick={() =>\n window.open(\n type === '1'\n ? `https://spigotmc.org/resources/bagou.${minecraftPlugins.id}`\n : type === '3'\n ? minecraftPlugins.url\n : type === '2' && minecraftPlugins.links.discussion,\n '_blank'\n )\n }\n >\n <img\n css={tw`w-10 h-10 rounded-lg`}\n alt={minecraftPlugins.name}\n src={iconurl}\n onError={({ currentTarget }) => {\n currentTarget.onerror = null; // prevents looping\n currentTarget.src =\n 'https://static.spigotmc.org/styles/spigot/xenresource/resource_icon.png';\n }}\n />\n\n <p css={tw`ml-2`}>\n {minecraftPlugins.name}\n <br />\n {['1', '3'].includes(type) && (\n <>\n <FontAwesomeIcon\n css={`\n ${minecraftPlugins.rating.average >= 0.5\n ? 'color: yellow;'\n : 'color: darkgray'}\n `}\n icon={faStar}\n />\n <FontAwesomeIcon\n css={`\n ${minecraftPlugins.rating.average >= 1.5\n ? 'color: yellow;'\n : 'color: darkgray'}\n `}\n icon={faStar}\n />\n <FontAwesomeIcon\n css={`\n ${minecraftPlugins.rating.average >= 2.5\n ? 'color: yellow;'\n : 'color: darkgray'}\n `}\n icon={faStar}\n />\n <FontAwesomeIcon\n css={`\n ${minecraftPlugins.rating.average >= 3.5\n ? 'color: yellow;'\n : 'color: darkgray'}\n `}\n icon={faStar}\n />\n <FontAwesomeIcon\n css={`\n ${minecraftPlugins.rating.average >= 4.5\n ? 'color: yellow;'\n : 'color: darkgray'}\n `}\n icon={faStar}\n />\n </>\n )}\n {type === '2' && <span css={tw`text-neutral-500`}>By {minecraftPlugins.author}</span>}\n </p>\n </div>\n {Installed === 1 ? (\n minecraftPlugins.installdate ? (\n <p css={tw`text-sm mt-2`}>Installed the {minecraftPlugins.installdate}</p>\n ) : (\n <p css={tw`text-sm mt-2`}>Installed today</p>\n )\n ) : (\n <>\n {minecraftPlugins.premium ? (\n <p css={tw`mt-1 md:mt-0 text-xs truncate text-yellow-500`}>PREMIUM ADDON</p>\n ) : (\n <>\n {minecraftPlugins.file.type === 'external' ? (\n <p css={tw`mt-1 md:mt-0 text-xs truncate text-yellow-500`}>EXTERNAL DOWNLOAD</p>\n ) : (\n <p\n css={tw`mt-1 md:mt-0 text-xs truncate cursor-help`}\n title={minecraftPlugins.tag}\n >\n {minecraftPlugins.tag}\n </p>\n )}\n </>\n )}\n </>\n )}\n </div>\n </div>\n {['1', '3'].includes(type) && (\n <div\n css={tw`flex-1 hidden md:block md:flex-none md:ml-5 md:w-48 mt-4 md:mt-0 md:text-center cursor-help`}\n title={`Versions: ${type === '3' ? testedVersions : minecraftPlugins.testedVersions.join()}`}\n >\n <p css={tw`text-2xs text-neutral-500 uppercase mt-1`}>Versions</p>\n {type === '3' ? (\n <p>{testedVersions}</p>\n ) : (\n <>\n <p>From: {testedVersions[0]}</p>\n <p>To: {testedVersions[testedVersions.length - 1]}</p>\n </>\n )}\n </div>\n )}\n {type === '2' && (\n <div\n css={tw`flex-1 hidden md:block md:flex-none md:ml-5 md:w-48 mt-4 md:mt-0 md:text-center cursor-help`}\n title={minecraftPlugins.updatedate}\n >\n <p css={tw`text-2xs text-neutral-500 uppercase mt-1`}>Update Date</p>\n <p>\n {formatDistanceToNow(Date.parse(minecraftPlugins.updatedate), {\n includeSeconds: true,\n addSuffix: true,\n })}\n </p>\n </div>\n )}\n <div css={tw`mt-4 md:mt-0 ml-6`} style={{ marginRight: '-0.5rem' }}>\n <>\n {minecraftPlugins.file.type === 'external' || minecraftPlugins.premium ? (\n <Button\n type={'button'}\n color={'primary'}\n onClick={() => {\n window.open(`https://spigotmc.org/resources/bagou.${minecraftPlugins.id}`);\n }}\n title={'Go to'}\n disabled={disable}\n isLoading={disable}\n >\n <FontAwesomeIcon icon={faExternalLinkAlt} /> Go to\n </Button>\n ) : type === '5' ? (\n <Button\n type={'button'}\n color={'red'}\n css={tw`mb-2`}\n onClick={() => {\n uninstall();\n }}\n title={'Uninstall'}\n disabled={disable}\n isLoading={disable}\n >\n <FontAwesomeIcon icon={faTrash} /> Uninstall\n </Button>\n ) : (\n <div css={['1', '2'].includes(type) ? tw`grid grid-rows-2` : tw`grid`}>\n <Button\n type={'button'}\n color={Installed === 0 ? 'green' : 'red'}\n css={tw`mb-2`}\n onClick={() => {\n if (Installed === 0) {\n install();\n } else {\n uninstall();\n }\n }}\n title={Installed === 0 ? 'Install' : 'Uninstall'}\n disabled={disable}\n isLoading={disable}\n >\n <FontAwesomeIcon icon={Installed === 1 ? faTrash : faDownload} />{' '}\n {Installed === 1 ? 'Uninstall' : 'Install'}\n </Button>\n {['1', '2', '3'].includes(type) && (\n <Button\n type={'button'}\n color={'primary'}\n css={tw`mt-2`}\n onClick={() => setVisible(true)}\n title={'View'}\n disabled={disable}\n isLoading={disable}\n >\n <FontAwesomeIcon icon={faListAlt} /> View\n </Button>\n )}\n </div>\n )}\n </>\n </div>\n </GreyRowBox>\n );\n};\n"
}
],
"./resources/scripts/components/server/mcplugins/McPluginsContainer.tsx": [
{
"type": "newfile",
"theme": "any",
"add": "import React, { useContext, useEffect, useState } from 'react';\nimport Spinner from '@/components/elements/Spinner';\nimport useFlash from '@/plugins/useFlash';\nimport FlashMessageRender from '@/components/FlashMessageRender';\nimport tw from 'twin.macro';\nimport ServerContentBlock from '@/components/elements/ServerContentBlock';\nimport McPluginsRow from './McPluginsRow';\nimport { ServerContext } from '@/state/server';\nimport getMinecraftPlugins, { Context as ServerPluginsContext } from '@/api/server/mcplugins/getMinecraftPlugins';\nimport { Form, Formik } from 'formik';\nimport Field from '@/components/elements/Field';\nimport { object, string } from 'yup';\nimport Pagination from '@/components/elements/Pagination';\nimport UploadButton from './UploadButton';\nimport { useParams } from 'react-router-dom';\nimport Select from '@/components/elements/Select';\nimport getMinecraftVersions from '@/api/server/mcplugins/getMinecraftVersions';\ninterface Values {\n search: string;\n}\ninterface ParamTypes {\n name: string;\n type: string;\n category: string;\n}\n\nconst McPluginsSpigotContainer = () => {\n const custom = false;\n\n const { clearFlashes, clearAndAddHttpError } = useFlash();\n let { name, type, category } = useParams<ParamTypes>();\n if (name === undefined) {\n name = 'Spigot';\n type = '1';\n category = '4';\n }\n const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid);\n const serverId = ServerContext.useStoreState((state) => state.server.data!.id);\n const { page, setPage, searchFilter, setSearchFilter, version, setVersion } = useContext(ServerPluginsContext);\n const { data: minecraftPlugins, error, isValidating } = getMinecraftPlugins(type, category, uuid);\n const [versions, setVersions] = useState([{ id: 1 }]);\n if (versions.length === 1) {\n getMinecraftVersions(uuid, name)\n .then((data) => {\n setVersions(data);\n })\n .catch((e) => {\n clearAndAddHttpError({ error: e, key: 'minecraftMcMods' });\n });\n }\n const submit = ({ search }: Values) => {\n clearFlashes('server:minecraftMcPlugins' + name);\n setSearchFilter(search);\n };\n useEffect(() => {\n if (!error) {\n clearFlashes('server:minecraftMcPlugins' + name);\n\n return;\n }\n clearAndAddHttpError({ error, key: 'server:minecraftMcPlugins' + name });\n }, [error]);\n\n if (!minecraftPlugins || (error && isValidating)) {\n return (\n <ServerContentBlock title={'Minecraft ' + name}>\n <div css={tw`flex flex-wrap mb-4 gap-4`}>\n <UploadButton css={tw`w-full sm:w-2/12 inset-x-0 bottom-0 mt-6`} />\n <div css={tw`w-full sm:w-5/12 sm:mt-0 mt-2`}>\n <Formik\n onSubmit={submit}\n initialValues={{\n search: searchFilter,\n }}\n validationSchema={object().shape({\n search: string().optional().min(1),\n })}\n >\n <Form>\n <Field id={'search'} name={'search'} label={'Search'} type={'text'} />\n </Form>\n </Formik>\n </div>\n {versions[0].id !== -1 && (\n <Select\n css={tw`w-full sm:w-1/6 inset-x-0 bottom-0 mt-6`}\n onChange={(e) => setVersion(e.target.value)}\n defaultValue={version}\n >\n <option value={''}>All Versions</option>\n {versions.length !== 1 &&\n versions.map((item, index) => {\n return (\n <option value={item.id} key={index}>\n {item.id}\n </option>\n );\n })}\n </Select>\n )}\n <Select\n css={\n versions[0].id !== -1\n ? tw`w-full sm:w-1/6 inset-x-0 bottom-0 mt-6`\n : tw`w-full sm:w-2/6 inset-x-0 bottom-0 mt-6`\n }\n onChange={(e) => location.replace(`/server/${serverId}/plugins/${e.target.value}`)}\n defaultValue={`${name}/${type}/${category}`}\n >\n <option key={0} value={'Spigot/1/4'}>\n Spigot\n </option>\n <option key={1} value={'Bungeecord-Spigot/1/2'}>\n Bungeecord Spigot\n </option>\n <option key={2} value={'Bungeecord/1/3'}>\n Bungeecord\n </option>\n <option key={3} value={'Bukkit/2/-1'}>\n Bukkit\n </option>\n <option key={4} value={'PolyMart/3/-1'}>\n PolyMart\n </option>\n {custom && (\n <option key={5} value={'Others/4/-1'}>\n Others\n </option>\n )}\n\n <option key={6} value={'Installed/5/-1'}>\n Installed\n </option>\n </Select>\n </div>\n <Spinner size={'large'} centered />\n </ServerContentBlock>\n );\n }\n return (\n <ServerContentBlock title={'Minecraft ' + name}>\n <FlashMessageRender byKey={'server:minecraftMcPlugins' + name} css={tw`mb-4`} />\n <div css={tw`flex flex-wrap mb-4 gap-4`}>\n <UploadButton css={tw`w-full sm:w-2/12 inset-x-0 bottom-0 mt-6`} />\n <div css={tw`w-full sm:w-5/12 sm:mt-0 mt-2`}>\n <Formik\n onSubmit={submit}\n initialValues={{\n search: searchFilter,\n }}\n validationSchema={object().shape({\n search: string().optional().min(1),\n })}\n >\n <Form>\n <Field id={'search'} name={'search'} label={'Search'} type={'text'} />\n </Form>\n </Formik>\n </div>\n {versions[0].id !== -1 && (\n <Select\n css={tw`w-full sm:w-1/6 inset-x-0 bottom-0 mt-6`}\n onChange={(e) => setVersion(e.target.value)}\n defaultValue={version}\n >\n <option value={''}>All Versions</option>\n {versions.length !== 1 &&\n versions.map((item, index) => {\n return (\n <option value={item.id} key={index}>\n {item.id}\n </option>\n );\n })}\n </Select>\n )}\n <Select\n css={\n versions[0].id !== -1\n ? tw`w-full sm:w-1/6 inset-x-0 bottom-0 mt-6`\n : tw`w-full sm:w-2/6 inset-x-0 bottom-0 mt-6`\n }\n onChange={(e) => location.replace(`/server/${serverId}/plugins/${e.target.value}`)}\n defaultValue={`${name}/${type}/${category}`}\n >\n <option key={0} value={'Spigot/1/4'}>\n Spigot\n </option>\n <option key={1} value={'Bungeecord-Spigot/1/2'}>\n Bungeecord Spigot\n </option>\n <option key={2} value={'Bungeecord/1/3'}>\n Bungeecord\n </option>\n <option key={3} value={'Bukkit/2/-1'}>\n Bukkit\n </option>\n <option key={4} value={'PolyMart/3/-1'}>\n PolyMart\n </option>\n {custom && (\n <option key={5} value={'Others/4/-1'}>\n Others\n </option>\n )}\n\n <option key={6} value={'Installed/5/-1'}>\n Installed\n </option>\n </Select>\n </div>\n <div css={tw`grid grid-cols-1 sm:grid-cols-2`}>\n <Pagination data={minecraftPlugins} customcss={tw`mt-4 flex justify-center col-span-2`} onPageSelect={setPage}>\n {({ items }) =>\n !items.length ? (\n <p css={tw`text-center text-sm text-neutral-300 col-span-2`}>\n {page > 1\n ? \"Looks like we've run out of minecraft plugins to show you, try going back a page.\"\n : 'It looks like there are no minecraft plugins matching search criteria.'}\n </p>\n ) : (\n items.map((minecraftPlugins, index) => (\n <McPluginsRow\n key={index}\n category={category}\n minecraftPlugins={minecraftPlugins}\n uuid={uuid}\n type={type}\n nameoftype={name}\n css={tw`mt-2 mr-2`}\n />\n ))\n )\n }\n </Pagination>\n </div>\n </ServerContentBlock>\n );\n};\n\nexport default () => {\n const [page, setPage] = useState<number>(1);\n const [searchFilter, setSearchFilter] = useState<string>('');\n const [version, setVersion] = useState<string>('');\n\n return (\n <ServerPluginsContext.Provider value={{ page, setPage, searchFilter, setSearchFilter, version, setVersion }}>\n <McPluginsSpigotContainer />\n </ServerPluginsContext.Provider>\n );\n};\n"
}
],
"./resources/scripts/components/server/mcplugins/McPluginsVersionsRow.tsx": [
{
"type": "newfile",
"theme": "any",
"add": "import React, { useContext, useEffect, useState } from 'react';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faDownload, faStar, faTrash } from '@fortawesome/free-solid-svg-icons';\nimport tw from 'twin.macro';\nimport useFlash from '@/plugins/useFlash';\nimport Button from '@/components/elements/Button';\nimport deleteFiles from '@/api/server/files/deleteFiles';\nimport { ApplicationStore } from '@/state';\nimport { Actions, useStoreActions } from 'easy-peasy';\nimport installPlugin from '@/api/server/mcplugins/installPlugin';\nimport uninstallPlugin from '@/api/server/mcplugins/uninstallPlugin';\nimport { formatDistanceToNow } from 'date-fns';\nimport { ServerContext } from '@/state/server';\nimport Spinner from '@/components/elements/Spinner';\nimport getPluginsVersions, { Context as PluginsVersionsContext } from '@/api/server/mcplugins/getPluginsVersions';\nimport Pagination from '@/components/elements/Pagination';\nimport GreyRowBox from '@/components/elements/GreyRowBox';\nimport { Dialog } from '@/components/elements/dialog';\n\ninterface Props {\n minecraftPlugins: any;\n nameoftype: string;\n installed: number;\n className?: string;\n}\n\nconst McPluginsVersionsRow = ({ minecraftPlugins, nameoftype, installed }: Props) => {\n const { clearAndAddHttpError } = useFlash();\n const { addFlash, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);\n const [disable, setDisable] = useState(false);\n const [open, setOpen] = useState(false);\n const [link, setLink] = useState('');\n\n const [Installed, setInstalled] = useState(installed);\n const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid);\n const { setPage } = useContext(PluginsVersionsContext);\n\n const {\n data: pluginVersions,\n error,\n isValidating,\n } = getPluginsVersions(\n uuid,\n nameoftype,\n nameoftype === 'Bukkit'\n ? minecraftPlugins.links.discussion.replace('https://dev.bukkit.org/projects/', '')\n : minecraftPlugins.id\n );\n useEffect(() => {\n if (!error) {\n clearFlashes('server:minecraftMcPlugins' + name);\n\n return;\n }\n clearAndAddHttpError({ error, key: 'server:minecraftMcPlugins' + name });\n }, [error]);\n\n if (!pluginVersions || (error && isValidating)) {\n return <Spinner size={'large'} centered />;\n }\n function clear() {\n clearFlashes();\n }\n let iconurl = 'https://static.spigotmc.org/styles/spigot/xenresource/resource_icon.png';\n if (nameoftype === 'PolyMart') {\n iconurl = minecraftPlugins.thumbnailURL;\n } else if (minecraftPlugins.icon.url !== '' && minecraftPlugins.icon.url !== undefined) {\n iconurl = !['Bukkit', 'PolyMart'].includes(nameoftype)\n ? 'https://www.spigotmc.org/' + minecraftPlugins.icon.url\n : minecraftPlugins.icon.url;\n }\n\n const testedVersions = [];\n\n for (const versions in minecraftPlugins.testedVersions) {\n testedVersions.push(minecraftPlugins.testedVersions[versions]);\n }\n const install = () => {\n setDisable(true);\n if (minecraftPlugins.file.type === 'external') {\n addFlash({\n key: 'server:minecraftMcPlugins',\n type: 'warning',\n message:\n \"This plugins can't be installed because the plugins files was not on spigotmc please install it manualy from this url https://www.spigotmc.org/\" +\n minecraftPlugins.file.url +\n '. You can also see if this plugins are on bukkit with the bukkit tab.',\n });\n setDisable(false);\n setTimeout(clear, 15000);\n } else {\n installPlugin(\n uuid,\n minecraftPlugins.name,\n `https://cdn.spiget.org/file/spiget-resources/${minecraftPlugins.id}.jar`\n )\n .then(() => {\n addFlash({\n key: 'server:minecraftMcPlugins',\n type: 'success',\n message: 'Plugins installed successfully',\n });\n setDisable(false);\n setInstalled(1);\n setTimeout(clear, 3000);\n })\n .catch(function (error) {\n setDisable(false);\n addFlash({\n key: 'server:minecraftMcPlugins',\n type: 'error',\n message: `This plugin has too large a size, it must be installed manually (${error})`,\n });\n setTimeout(clear, 3000);\n });\n }\n };\n const uninstall = () => {\n setDisable(true);\n if (minecraftPlugins.file.type === 'external') {\n addFlash({\n key: 'server:minecraftMcPlugins',\n type: 'warning',\n message:\n \"This plugins can't be removed because the plugins files was not on spigotmc please remove it manualy \",\n });\n setDisable(false);\n setTimeout(clear, 15000);\n } else {\n deleteFiles(uuid, '/plugins', [`${minecraftPlugins.name}.jar`])\n .then(() => {\n uninstallPlugin(uuid, minecraftPlugins.name)\n .then(() => {\n addFlash({\n key: 'server:minecraftMcPlugins',\n type: 'success',\n message: 'Plugins removed successfully',\n });\n setDisable(false);\n setInstalled(0);\n setTimeout(clear, 3000);\n })\n .catch(function (error) {\n setDisable(false);\n clearAndAddHttpError({ key: 'minecraftMcPlugins', error });\n });\n })\n .catch(function (error) {\n setDisable(false);\n clearAndAddHttpError({ key: 'minecraftMcPlugins', error });\n });\n }\n };\n const installbukkit = () => {\n setDisable(true);\n installPlugin(uuid, minecraftPlugins.name, link)\n .then(() => {\n addFlash({\n key: minecraftPlugins.name,\n type: 'success',\n message: 'Plugins installed successfully',\n });\n setDisable(false);\n setInstalled(1);\n setTimeout(clear, 3000);\n })\n .catch(function (error) {\n setDisable(false);\n addFlash({\n key: minecraftPlugins.name,\n type: 'error',\n message: `A error occured. (${error})`,\n });\n setTimeout(clear, 3000);\n });\n };\n return (\n <div>\n <Dialog.Confirm\n open={open}\n onClose={() => setOpen(false)}\n title={`Install custom version.`}\n confirm={'Open'}\n onConfirmed={() => {\n if (nameoftype === 'PolyMart') {\n window.open(`https://polymart.org/resource/${minecraftPlugins.id}/updates`);\n } else {\n window.open(link);\n }\n setOpen(false);\n }}\n >\n For install a custom plugin version you need to download it yourself on spigot.\n </Dialog.Confirm>\n {!['Bukkit', 'PolyMart'].includes(nameoftype) && (\n <div css={tw`sm:flex`}>\n <div css={tw`flex items-center text-sm mb-1 cursor-pointer `}>\n <img\n css={tw`w-10 h-10 rounded-lg cursor-pointer mr-4`}\n onClick={() => window.open(`https://spigotmc.org/resources/bagou.${minecraftPlugins.id}`, '_blank')}\n alt={minecraftPlugins.name}\n src={iconurl}\n />\n\n <div>\n <h1 css={tw`text-lg`}>{minecraftPlugins.name}</h1>\n <p css={tw`text-sm`}>{minecraftPlugins.tag}</p>\n </div>\n </div>\n <div css={tw`text-base mx-6`}>\n <p>\n Author:{' '}\n <a\n css={tw`text-blue-400`}\n href={`https://spigotmc.org/members/${minecraftPlugins.author.id}`}\n target={'_blank'}\n rel='noreferrer'\n >\n https://spigotmc.org/members/{minecraftPlugins.author.id}\n </a>\n </p>\n {minecraftPlugins.contributors !== null ?? <p>Contributor: {minecraftPlugins.contributors}</p>}\n <p>Versions: {minecraftPlugins.testedVersions.join()}</p>\n <p>Downloads: {minecraftPlugins.downloads}</p>\n <p>\n Released:{' '}\n {formatDistanceToNow(new Date(minecraftPlugins.releaseDate * 1000), {\n includeSeconds: true,\n addSuffix: true,\n })}\n </p>\n <p>\n Updated:{' '}\n {formatDistanceToNow(new Date(minecraftPlugins.updateDate * 1000), {\n includeSeconds: true,\n addSuffix: true,\n })}\n </p>\n </div>\n <div css={tw`flex-none mt-2 my-auto`}>\n <Button\n type={'button'}\n color={Installed === 1 ? 'red' : 'green'}\n css={tw`sm:w-36 mt-6 w-full`}\n onClick={() => {\n if (Installed === 0) {\n install();\n } else {\n uninstall();\n }\n }}\n title={Installed === 1 ? 'Uninstall' : 'Install'}\n disabled={disable}\n isLoading={disable}\n >\n <FontAwesomeIcon icon={Installed === 1 ? faTrash : faDownload} />{' '}\n {Installed === 1 ? 'Uninstall' : 'Install Latest'}\n </Button>\n </div>\n </div>\n )}\n <p css={tw`mt-2 text-center font-semibold`}>Others Versions</p>\n <div css={tw`grid grid-cols-2 gap-4 mt-2`}>\n <Pagination data={pluginVersions} customcss={tw`mt-4 flex justify-center col-span-2`} onPageSelect={setPage}>\n {({ items }) =>\n !items.length ? (\n <p css={tw`text-center text-sm text-neutral-300 col-span-2`}>\n {'It looks like there are no minecraft plugins versions matching search criteria.'}\n </p>\n ) : (\n items.map((pluginVersion, index) => (\n <GreyRowBox\n key={index}\n css={\n index > 1\n ? tw`flex-wrap md:flex-nowrap items-center mt-2`\n : tw`flex-wrap md:flex-nowrap items-center`\n }\n >\n <div css={tw`flex items-center truncate w-full md:flex-1`}>\n <div css={tw`flex flex-col truncate`}>\n <div css={tw`flex items-center text-sm mb-1`}>{pluginVersion.name}</div>\n {!['Bukkit', 'PolyMart'].includes(nameoftype) && (\n <>\n <p css={tw`mt-1 md:mt-0 text-xs truncate`}>\n Rating:{' '}\n <>\n <FontAwesomeIcon\n css={`\n ${pluginVersion.rating.average >= 0.5 ? 'color: yellow;' : 'color: darkgray'}\n `}\n icon={faStar}\n />\n <FontAwesomeIcon\n css={`\n ${pluginVersion.rating.average >= 1.5 ? 'color: yellow;' : 'color: darkgray'}\n `}\n icon={faStar}\n />\n <FontAwesomeIcon\n css={`\n ${pluginVersion.rating.average >= 2.5 ? 'color: yellow;' : 'color: darkgray'}\n `}\n icon={faStar}\n />\n <FontAwesomeIcon\n css={`\n ${pluginVersion.rating.average >= 3.5 ? 'color: yellow;' : 'color: darkgray'}\n `}\n icon={faStar}\n />\n <FontAwesomeIcon\n css={`\n ${pluginVersion.rating.average >= 4.5 ? 'color: yellow;' : 'color: darkgray'}\n `}\n icon={faStar}\n />\n </>\n </p>\n <p css={tw`mt-1 md:mt-0 text-xs truncate`}>Downloads: {pluginVersion.downloads}</p>\n </>\n )}\n </div>\n </div>\n\n <div css={tw`mt-4 md:mt-0 ml-6`} style={{ marginRight: '-0.5rem' }}>\n <Button\n type={'button'}\n aria-label={'Install'}\n isSecondary\n onClick={() => {\n setLink(\n !['Bukkit', 'PolyMart'].includes(nameoftype)\n ? `https://spigotmc.org/resources/${pluginVersion.resource}/download?version=${pluginVersion.id}`\n : pluginVersion.downloadlink\n );\n if (Installed) {\n uninstall();\n } else if (!['Bukkit'].includes(nameoftype)) {\n setOpen(true);\n } else {\n installbukkit();\n }\n }}\n title='Download and Install'\n disabled={disable}\n isLoading={disable}\n >\n <FontAwesomeIcon icon={Installed ? faTrash : faDownload} />\n </Button>\n </div>\n </GreyRowBox>\n ))\n )\n }\n </Pagination>\n </div>\n </div>\n );\n};\nexport default ({ minecraftPlugins, nameoftype, installed }: Props) => {\n const [page, setPage] = useState<number>(1);\n return (\n <PluginsVersionsContext.Provider value={{ page, setPage }}>\n <McPluginsVersionsRow minecraftPlugins={minecraftPlugins} nameoftype={nameoftype} installed={installed} />\n </PluginsVersionsContext.Provider>\n );\n};"
}
],
"./resources/views/admin/bagoucenter/": [
{
"type": "folder"
}
],
"./resources/views/admin/bagoucenter/index.blade.php": [
{
"type": "newfile",
"theme": "any",
"add": "@extends('layouts.admin')\n@include('partials/admin.settings.nav', ['activeTab' => 'basic'])\n\n@section('title')\n Bagou License\n@endsection\n\n@section('content-header')\n<h1>Bagou Center<small>Manage all bagou450 addons.</small></h1>\n <ol class=\"breadcrumb\">\n <li><a href=\"{{ route('admin.index') }}\">Admin</a></li>\n <li class=\"active\">Bagou Center</li>\n </ol>\n@endsection\n@section('content')\n@include('admin.bagoucenter.nav', ['addon' => null])\n\n <div class=\"row\" style=\"margin-top: 15px;\">\n <div class=\"col-xs-12\">\n <div class=\"box\">\n <div class=\"box-header with-border\">\n <h3 class=\"box-title\">Bagou Center</h3>\n </div>\n <p class=\"box-body\">\n Select a tab above for manage all your Bagou450 addons!</span>.\n <br>\n <span style=\"font-size: 20px;\">Api status: @if ($apistatus === 1) Online <i class=\"fa fa-check-circle\" style=\"color: green;\" aria-hidden=\"true\"></i> @elseif ($apistatus === 2) Maintenance mode <i class=\"fa fa-exclamation-circle\" style=\"color: yellow;\" aria-hidden=\"true\"></i> @else Down <i class=\"fa fa-minus-circle\" style=\"color: red;\" aria-hidden=\"true\"></i> @endif</span>\n <br>\n <span style=\"font-size: 20px;\">CDN status: @if ($cdnstatus === 1) Online <i class=\"fa fa-check-circle\" style=\"color: green;\" aria-hidden=\"true\"></i> @elseif ($cdnstatus === 2) Maintenance mode <i class=\"fa fa-exclamation-circle\" style=\"color: yellow;\" aria-hidden=\"true\"></i> @else Down <i class=\"fa fa-minus-circle\" style=\"color: red;\" aria-hidden=\"true\"></i> @endif</span>\n\n </p>\n </div>\n </div>\n </div>\n@endsection"
}
],
"./resources/views/admin/bagoucenter/license.blade.php": [
{
"type": "newfile",
"theme": "any",
"add": "@extends('layouts.admin')\n@include('partials/admin.settings.nav', ['activeTab' => 'basic'])\n\n@section('title')\n Bagou License\n@endsection\n\n@section('content-header')\n <h1>Bagou License<small>Configure license for Bagou450 Addons.</small></h1>\n <ol class=\"breadcrumb\">\n <li><a href=\"{{ route('admin.index') }}\">Admin</a></li>\n <li class=\"active\">Bagou License</li>\n </ol>\n@endsection\n@section('content')\n@include('admin.bagoulicense.nav', ['addon' => $addon])\n\n <div class=\"row\">\n <div class=\"col-xs-12\">\n <div class=\"box\">\n <div class=\"box-header with-border\">\n <h3 class=\"box-title\">Bagou License</h3>\n </div>\n <form action=\"{{ route('admin.settings') }}\" method=\"POST\">\n <div class=\"box-body\">\n <div class=\"row\">\n <div class=\"form-group col-md-4\">\n <label class=\"control-label\">Company Name</label>\n <div>\n <input type=\"text\" class=\"form-control\" name=\"app:name\" value=\"{{ old('app:name', config('app.name')) }}\" />\n <p class=\"text-muted\"><small>This is the name that is used throughout the panel and in emails sent to clients.</small></p>\n </div>\n </div>\n <div class=\"form-group col-md-4\">\n <label class=\"control-label\">Require 2-Factor Authentication</label>\n <div>\n <div class=\"btn-group\" data-toggle=\"buttons\">\n @php\n $level = old('pterodactyl:auth:2fa_required', config('pterodactyl.auth.2fa_required'));\n @endphp\n <label class=\"btn btn-primary @if ($level == 0) active @endif\">\n <input type=\"radio\" name=\"pterodactyl:auth:2fa_required\" autocomplete=\"off\" value=\"0\" @if ($level == 0) checked @endif> Not Required\n </label>\n <label class=\"btn btn-primary @if ($level == 1) active @endif\">\n <input type=\"radio\" name=\"pterodactyl:auth:2fa_required\" autocomplete=\"off\" value=\"1\" @if ($level == 1) checked @endif> Admin Only\n </label>\n <label class=\"btn btn-primary @if ($level == 2) active @endif\">\n <input type=\"radio\" name=\"pterodactyl:auth:2fa_required\" autocomplete=\"off\" value=\"2\" @if ($level == 2) checked @endif> All Users\n </label>\n </div>\n <p class=\"text-muted\"><small>If enabled, any account falling into the selected grouping will be required to have 2-Factor authentication enabled to use the Panel.</small></p>\n </div>\n </div>\n <div class=\"form-group col-md-4\">\n <label class=\"control-label\">Status</label>\n <div>\n <p>@extends('layouts.admin')\n@include('partials/admin.settings.nav', ['activeTab' => 'basic'])\n\n@section('title')\n Bagou License\n@endsection\n\n@section('content-header')\n<h1>Bagou Center<small>Manage all bagou450 addons.</small></h1>\n <ol class=\"breadcrumb\">\n <li><a href=\"{{ route('admin.index') }}\">Admin</a></li>\n <li class=\"active\">Bagou Center</li>\n </ol>\n@endsection\n@section('content')\n@include('admin.bagoulicense.nav', ['addon' => $addon])\n\n <div class=\"row\">\n <div class=\"col-xs-12\">\n <div class=\"box\">\n <div class=\"box-header with-border\">\n <h3 class=\"box-title\">Bagou License</h3>\n </div>\n <form method=\"POST\">\n <div class=\"box-body\">\n <div class=\"row\">\n <div class=\"form-group col-md-4\">\n <label class=\"control-label\">Addon license (Transaction id)</label>\n <div>\n <input type=\"text\" class=\"form-control\" name=\"license\" value=\"{{ $license }}\" />\n <p class=\"text-muted\"><small>Add here your transaction id if not work contact me on <a href=\"https://discord.bagou450.com\">discord</a></small></p>\n </div>\n </div>\n <div class=\"form-group col-md-4\">\n <label class=\"control-label\">Usage</label>\n <div>\n <p>{{ $usage && $maxusage ? \"$usage / $maxusage panel(s)\" : 'No license' }} <br> {{ $usage && $maxusage ? 'Contact me on discord for increase panel limit.' : 'For use the addon you need to insert a license' }}</p>\n </div>\n </div>\n <div class=\"form-group col-md-4\">\n <label class=\"control-label\">Status</label>\n <div>\n <p style=\"font-size: 50px\">{{$enabled ? '✅' : '\uD83D\uDEAB' }}</p>\n </div>\n </div>\n </div>\n </div>\n <div class=\"box-footer\">\n {!! csrf_field() !!}\n <button type=\"submit\" name=\"_method\" value=\"POST\" style=\"margin-left: 8px\" class=\"btn btn-sm btn-primary pull-right \">Save</button>\n {!! csrf_field() !!}\n <button type=\"submit\" name=\"_method\" value=\"DELETE\" class=\"btn btn-sm btn-danger pull-right \">Remove License</button>\n\n </form>\n </div>\n\n </div>\n </div>\n </div>\n@endsection\n</p>\n </div>\n </div>\n </div>\n </div>\n <div class=\"box-footer\">\n {!! csrf_field() !!}\n <button type=\"submit\" name=\"_method\" value=\"PATCH\" class=\"btn btn-sm btn-primary pull-right\">Save</button>\n </div>\n </form>\n </div>\n </div>\n </div>\n@endsection"
}
],
"./resources/views/admin/bagoucenter/nav.blade.php": [
{
"type": "newfile",
"theme": "any",
"add": "@php\n $router = app('router');\n@endphp\n<div class=\"row\">\n <div class=\"col-xs-12\">\n <div class=\"nav-tabs-custom nav-tabs-floating\" style=\"margin-bottom: 1px;\">\n <ul class=\"nav nav-tabs\">\n <li class=\"{{ $router->currentRouteNamed('admin.bagoucenter') ? 'active' : '' }}\">\n <a href=\"{{ route('admin.bagoucenter') }}\">Overview</a>\n </li>\n <li class=\"{{ $router->currentRouteNamed('admin.bagoucenter.license') || $router->currentRouteNamed('admin.bagoucenter.license.addon')? 'active' : '' }}\">\n <a href=\"{{ route('admin.bagoucenter.license') }}\">License</a>\n </li>\n <li class=\"{{ $router->currentRouteNamed('admin.bagoucenter.versions') || str_starts_with($router->current()->uri, 'admin/bagoucenter/versions') ? 'active' : '' }}\">\n <a href=\"{{ route('admin.bagoucenter.versions') }}\">Version checker</a>\n </li>\n <li class=\"{{ $router->currentRouteNamed('admin.bagoucenter.settings') || str_starts_with($router->current()->uri, 'admin/bagoucenter/settings') ? 'active' : '' }}\">\n <a href=\"{{ route('admin.bagoucenter.settings') }}\">Settings</a>\n </li>\n <li class=\"{{ $router->currentRouteNamed('admin.bagoucenter.support') || str_starts_with($router->current()->uri, 'admin/bagoucenter/support') ? 'active' : '' }}\">\n <a href=\"{{ route('admin.bagoucenter.support') }}\">Support</a>\n </li>\n </ul>\n </div>\n </div>\n</div>"
}
],
"./resources/views/admin/bagoucenter/license": [
{
"type": "folder"
}
],
"./resources/views/admin/bagoucenter/license/index.blade.php": [
{
"type": "newfile",
"theme": "any",
"add": "@extends('layouts.admin')\n@include('partials/admin.settings.nav', ['activeTab' => 'basic'])\n\n@section('title')\n Bagou License\n@endsection\n\n@section('content-header')\n <h1>Bagou Center<small>Manage all bagou450 addons.</small></h1>\n <ol class=\"breadcrumb\">\n <li><a href=\"{{ route('admin.index') }}\">Admin</a></li>\n <li class=\"active\">Bagou Center</li>\n </ol>\n@endsection\n@section('content')\n@include('admin.bagoucenter.nav')\n@include('admin.bagoucenter.license.nav', ['addon' => null, 'addonslist' => $addonslist, 'licenses' => $licenses])\n\n <div class=\"row\">\n <div class=\"col-xs-12\">\n <div class=\"box\">\n <div class=\"box-header with-border\">\n <h3 class=\"box-title\">Bagou License</h3>\n </div>\n <p class=\"box-body\">\n You can manage your license here.\n <br>Actual license status:\n <ul>\n @foreach ($addonslist as $addon)\n @php\n $found = false;\n @endphp\n @foreach($licenses as $license) \n @if($license->addon == $addon['id'])\n @php\n $found = true;\n @endphp\n <li>{{$addon['name']}}: <span style=\"color: green;\">Activated</span></li>\n @endif\n @endforeach\n @if (!$found)\n <li>{{$addon['name']}}: <span style=\"color: red;\">Disabled</span></li>\n @endif\n @endforeach\n </ul>\n </p>\n </div>\n </div>\n </div>\n@endsection"
}
],
"./resources/views/admin/bagoucenter/license/nav.blade.php": [
{
"type": "newfile",
"theme": "any",
"add": "@php\n $router = app('router');\n@endphp\n<div class=\"row\">\n <div class=\"col-xs-12\">\n <div class=\"nav-tabs-custom nav-tabs-floating\">\n <ul class=\"nav nav-tabs\">\n <li class=\"{{ $router->currentRouteNamed('admin.bagoucenter.license') ? 'active' : '' }}\">\n <a href=\"{{ route('admin.bagoucenter.license') }}\">Overview</a>\n </li>\n @foreach ($addonslist as $addonn) \n @if (isset($addonn['id']) && isset($addonn['name']))\n <li class=\"{{ $addon == $addonn['id'] ? 'active' : '' }}\">\n <a href=\"{{ route('admin.bagoucenter.license.addon', $addonn['id']) }}\">{{$addonn['name']}} @if($addonn['new']) <span class=\"label label-success\"> NEW </span> @endif</a>\n </li>\n @endif\n @endforeach\n </ul>\n </div>\n </div>\n</div>"
}
],
"./resources/views/admin/bagoucenter/license/license.blade.php": [
{
"type": "newfile",
"theme": "any",
"add": "a"
}
],
"./resources/views/admin/bagoucenter/settings": [
{
"type": "folder"
}
],
"./resources/views/admin/bagoucenter/settings/index.blade.php": [
{
"type": "newfile",
"theme": "any",
"add": "@extends('layouts.admin')\n@include('partials/admin.settings.nav', ['activeTab' => 'basic'])\n\n@section('title')\n Bagou License\n@endsection\n\n@section('content-header')\n <h1>Bagou Center<small>Manage all bagou450 addons.</small></h1>\n <ol class=\"breadcrumb\">\n <li><a href=\"{{ route('admin.index') }}\">Admin</a></li>\n <li class=\"active\">Bagou Center</li>\n </ol>\n@endsection\n@section('content')\n@include('admin.bagoucenter.nav')\n@include('admin.bagoucenter.settings.nav', ['addon' => null, 'addonslist' => $addonslist, 'licenses' => $licenses])\n\n <div class=\"row\">\n <div class=\"col-xs-12\">\n <div class=\"box\">\n <div class=\"box-header with-border\">\n <h3 class=\"box-title\">Bagou Settings</h3>\n </div>\n <p class=\"box-body\">\n Choose a tab above for manage addon settings\n </p>\n </div>\n </div>\n </div>\n@endsection"
}
],
"./resources/views/admin/bagoucenter/settings/nav.blade.php": [
{
"type": "newfile",
"theme": "any",
"add": "@php\n $router = app('router');\n@endphp\n<div class=\"row\">\n <div class=\"col-xs-12\">\n <div class=\"nav-tabs-custom nav-tabs-floating\">\n <ul class=\"nav nav-tabs\">\n <li class=\"{{ $router->currentRouteNamed('admin.bagoucenter.settings') ? 'active' : '' }}\">\n <a href=\"{{ route('admin.bagoucenter.settings') }}\">Overview</a>\n </li>\n @foreach ($addonslist as $addonn)\n @foreach($licenses as $license) \n @if($license->addon == $addonn['id'] && $addonn['tab'])\n @php\n $found = true;\n @endphp\n <li class=\"{{ $addon == $addonn['id'] ? 'active' : '' }}\">\n <a href=\"{{ route($addonn['tabroute'], $addonn['id']) }}\">{{$addonn['name']}} @if($addonn['new']) <span class=\"label label-success\"> NEW </span> @endif</a>\n </li>\n @endif\n @endforeach\n @endforeach\n </ul>\n </div>\n </div>\n</div>"
}
],
"./resources/views/admin/bagoucenter/support": [
{
"type": "folder"
}
],
"./resources/views/admin/bagoucenter/support/index.blade.php": [
{
"type": "newfile",
"theme": "any",
"add": "@extends('layouts.admin')\n@include('partials/admin.settings.nav', ['activeTab' => 'basic'])\n\n@section('title')\n Bagou License\n@endsection\n\n@section('content-header')\n<h1>Bagou Center<small>Manage all bagou450 addons.</small></h1>\n <ol class=\"breadcrumb\">\n <li><a href=\"{{ route('admin.index') }}\">Admin</a></li>\n <li class=\"active\">Bagou Center</li>\n </ol>\n@endsection\n@section('content')\n@include('admin.bagoucenter.nav', ['addon' => null])\n\n <div class=\"row\" style=\"margin-top: 15px;\">\n <div class=\"col-xs-12\">\n <div class=\"box\">\n <div class=\"box-header with-border\">\n <h3 class=\"box-title\">Bagou Support</h3>\n </div>\n <p class=\"box-body\">\n You can contact me trough many way!</span>.\n <br>\n <span style=\"font-size: 20px;\">Discord: <a href=\"http://discord.bagou450.com\">here</a> (24 hour response time) </span>\n <br>\n <span style=\"font-size: 20px;\">Sms (No call): +33 7 56 89 00 36 (24 hours response time) </span>\n <br>\n <span style=\"font-size: 20px;\">Email: contact@bagou450.com (48 hours response time) </span>\n\n </p>\n </div>\n </div>\n </div>\n@endsection"
}
],
"./resources/views/admin/bagoucenter/versions": [
{
"type": "folder"
}
],
"./resources/views/admin/bagoucenter/versions/index.blade.php": [
{
"type": "newfile",
"theme": "any",
"add": "@extends('layouts.admin')\n@include('partials/admin.settings.nav', ['activeTab' => 'basic'])\n\n@section('title')\n Bagou License\n@endsection\n\n@section('content-header')\n <h1>Bagou Center<small>Manage all bagou450 addons.</small></h1>\n <ol class=\"breadcrumb\">\n <li><a href=\"{{ route('admin.index') }}\">Admin</a></li>\n <li class=\"active\">Bagou Center</li>\n </ol>\n@endsection\n@section('content')\n@include('admin.bagoucenter.nav')\n\n <div class=\"row\" style=\"margin-top: 15px;\">\n <div class=\"col-xs-12\">\n <div class=\"box\">\n <div class=\"box-header with-border\">\n <h3 class=\"box-title\">Bagou Update Checker</h3>\n </div>\n <p class=\"box-body\">\n You can manage your license here.\n <br>Actual license status:\n <ul>\n @foreach ($addonslist as $addon)\n @foreach($licenses as $license) \n @if($license->addon == $addon['id'])\n <li>{{$addon['name']}} {{$license->version}}: @if($addon['version'] == $license->version) <span style=\"color: green;\">Updated</span> @else <span style=\"color: red;\">Outdated (Latest version: {{$addon['version']}})</span> @endif</li>\n @endif\n @endforeach\n @endforeach\n </ul>\n \n </p>\n <div class=\"box-footer\">\n <form method=\"POST\">\n @csrf\n <button type=\"submit\" name=\"_method\" value=\"POST\" style=\"margin-left: 8px\" class=\"btn btn-sm btn-primary pull-right \">Refresh</button>\n</form>\n </div>\n </div>\n \n </div>\n </div>\n@endsection"
}
],
"./resources/scripts/routers/routes.ts": [
{
"type": "under",
"theme": "any",
"where": " {\n path: '/files',\n permission: 'file.*',\n name: 'Files',\n component: FileManagerContainer,\n },",
"add": " {\n path: '/plugins/Spigot/1/4',\n permission: 'plugins.*',\n name: 'Plugins Installer',\n nestId: 1,\n component: McPluginsContainer,\n },\n {\n path: '/plugins/:name/:type/:category',\n permission: 'plugins.*',\n name: undefined,\n nestId: 1,\n component: McPluginsContainer,\n },"
},
{
"type": "under",
"theme": "any",
"where": "import ServerActivityLogContainer from '@/components/server/ServerActivityLogContainer';",
"add": "import McPluginsContainer from '@/components/server/mcplugins/McPluginsContainer';"
},
{
"type": "under",
"theme": "any",
"where": "permission: string | string[] | null;",
"add": " nestId?: number;\n eggId?: number;\n nestIds?: number[];\n eggIds?: number[];"
}
],
"./resources/scripts/routers/ServerRouter.tsx": [
{
"type": "replace",
"theme": "no",
"where": "{routes.server\n .filter((route) => !!route.name)\n .map((route) =>\n route.permission ? (\n <Can key={route.path} action={route.permission} matchAny>\n <NavLink to={to(route.path, true)} exact={route.exact}>\n {route.name}\n </NavLink>\n </Can>\n ) : (\n <NavLink key={route.path} to={to(route.path, true)} exact={route.exact}>\n {route.name}\n </NavLink>\n )\n )}",
"add": "<Navigation />"
},
{
"type": "replace",
"theme": "no",
"where": "{routes.server.map(({ path, permission, component: Component }) => (\n <PermissionRoute key={path} permission={permission} path={to(path)} exact>\n <Spinner.Suspense>\n <Component />\n </Spinner.Suspense>\n </PermissionRoute>\n ))}",
"add": "<ComponentLoader />"
},
{
"type": "under",
"theme": "no",
"where": "import routes from '@/routers/routes';",
"add": "import { Navigation, ComponentLoader } from '@/routers/ServerElements';"
}
],
"./resources/scripts/routers/ServerElements.tsx": [
{
"type": "newfile",
"theme": "no",
"add": "import React from 'react';\nimport { ServerContext } from '@/state/server';\nimport routes from '@/routers/routes';\nimport Can from '@/components/elements/Can';\nimport { NavLink, Route, Switch, useRouteMatch } from 'react-router-dom';\nimport PermissionRoute from '@/components/elements/PermissionRoute';\nimport Spinner from '@/components/elements/Spinner';\nimport { NotFound } from '@/components/elements/ScreenBlock';\nimport TransitionRouter from '@/TransitionRouter';\nimport { useLocation } from 'react-router';\n\ninterface Props {\n route: any;\n}\n\nconst NavItem = ({ route }: Props) => {\n const match = useRouteMatch<{ id: string }>();\n\n const nestId = ServerContext.useStoreState(state => state.server.data?.nestId);\n const eggId = ServerContext.useStoreState(state => state.server.data?.eggId);\n\n const to = (value: string, url = false) => {\n return `${(url ? match.url : match.path).replace(/\\/*$/, '')}/${value.replace(/^\\/+/, '')}`;\n };\n\n return (\n ((route.nestIds && route.nestIds.includes(nestId ?? 0))\n || (route.eggIds && route.eggIds.includes(eggId ?? 0))\n || (route.nestId && route.nestId === nestId)\n || (route.eggId && route.eggId === eggId)\n || (!route.eggIds && !route.nestIds && !route.nestId && !route.eggId)) &&\n <NavLink to={to(route.path, true)} exact={route.exact}>\n {route.name}\n </NavLink>\n );\n}\n\nexport const Navigation = () => {\n return (\n <>\n {routes.server\n .filter((route) => !!route.name)\n .map((route) =>\n route.permission ? (\n <Can key={route.path} action={route.permission} matchAny>\n <NavItem route={route} />\n </Can>\n ) : (\n <React.Fragment key={route.path}>\n <NavItem route={route} />\n </React.Fragment>\n )\n )}\n </>\n );\n}\n\nexport const ComponentLoader = () => {\n const match = useRouteMatch<{ id: string }>();\n const location = useLocation();\n\n const serverNestId = ServerContext.useStoreState(state => state.server.data?.nestId);\n const serverEggId = ServerContext.useStoreState(state => state.server.data?.eggId);\n\n const to = (value: string, url = false) => {\n return `${(url ? match.url : match.path).replace(/\\/*$/, '')}/${value.replace(/^\\/+/, '')}`;\n };\n\n return (\n <>\n <TransitionRouter>\n <Switch location={location}>\n {routes.server.map(({ path, permission, component: Component, nestIds, eggIds, nestId, eggId }) => {\n return (\n ((nestIds && nestIds.includes(serverNestId ?? 0))\n || (eggIds && eggIds.includes(serverEggId ?? 0))\n || (nestId && serverNestId === nestId)\n || (eggId && serverEggId === eggId)\n || (!eggIds && !nestIds && !nestId && !eggId)) &&\n <PermissionRoute key={path} permission={permission} path={to(path)} exact>\n <Spinner.Suspense>\n <Component />\n </Spinner.Suspense>\n </PermissionRoute>\n )\n })}\n <Route path={'*'} component={NotFound} />\n </Switch>\n </TransitionRouter>\n </>\n );\n}"
}
],
"./resources/scripts/api/server/getServer.ts": [
{
"type": "under",
"where": "allocations: Allocation[];",
"theme": "any",
"add": " eggId: number;"
},
{
"type": "under",
"where": "allocations: Allocation[];",
"theme": "any",
"add": " nestId: number;"
},
{
"type": "under",
"theme": "any",
"where": " allocations: ((data.relationships?.allocations as FractalResponseList | undefined)?.data || []).map(\n rawDataToServerAllocation\n ),\n",
"add": " nestId: data.nest_id,"
},
{
"type": "under",
"theme": "any",
"where": " allocations: ((data.relationships?.allocations as FractalResponseList | undefined)?.data || []).map(\n rawDataToServerAllocation\n ),\n",
"add": " eggId: data.egg_id,"
}
],
"./routes/api-client.php": [
{
"type": "above",
"theme": "any",
"add": " Route::group(['prefix' => '/plugins'], function () {\n Route::get('/', [Client\\Servers\\McPluginsController::class, 'plugins']);\n Route::get('/getVersions', [Client\\Servers\\McPluginsController::class, 'getVersions']);\n Route::get('/getMcVersions', [Client\\Servers\\McPluginsController::class, 'getMcVersions']);\n\n Route::post('/install', [Client\\Servers\\McPluginsController::class, 'install']);\n Route::post('/uninstall', [Client\\Servers\\McPluginsController::class, 'uninstall']);\n });",
"where": " Route::group(['prefix' => '/settings'], function () {"
}
],
"./routes/admin.php": [
{
"type": "under",
"theme": "any",
"add": "/*\n|--------------------------------------------------------------------------\n| Bagou Center Controller Routes\n|--------------------------------------------------------------------------\n|\n| Endpoint: /admin/bagoucenter\n|\n*/\nRoute::group(['prefix' => 'bagoucenter'], function () {\n Route::get('/', [Admin\\Bagou\\BagouCenterController::class, 'index'])->name('admin.bagoucenter');\n Route::get('/license/', [Admin\\Bagou\\BagouLicenseController::class, 'index'])->name('admin.bagoucenter.license');\n Route::get('/license/{addon}', [Admin\\Bagou\\BagouLicenseController::class, 'license'])->name('admin.bagoucenter.license.addon');\n \n Route::get('/versions/', [Admin\\Bagou\\BagouVersionsController::class, 'index'])->name('admin.bagoucenter.versions');\n\n Route::get('/settings', [Admin\\Bagou\\BagouSettingsController::class, 'index'])->name('admin.bagoucenter.settings');\n Route::get('/settings/{addon}', [Admin\\Bagou\\BagouSettingsController::class, 'settings'])->name('admin.bagoucenter.settings.addon');\n\n Route::get('/support/', [Admin\\Bagou\\BagouCenterController::class, 'settings'])->name('admin.bagoucenter.support');\n\n Route::post('/license/{addon}', [Admin\\Bagou\\BagouLicenseController::class, 'setlicense']);\n \n Route::post('/versions', [Admin\\Bagou\\BagouVersionsController::class, 'refresh']);\n\n Route::delete('/license/{addon}', [Admin\\Bagou\\BagouLicenseController::class, 'removelicense']);\n \n});",
"where": " Route::delete('/egg/{egg:id}/variables/{variable:id}', [Admin\\Nests\\EggVariableController::class, 'destroy']);\n});"
}
],
"./resources/views/layouts/admin.blade.php": [
{
"type": "above",
"theme": "any",
"where": "<li class=\"header\">MANAGEMENT</li>",
"add": " <li class=\"{{ ! starts_with(Route::currentRouteName(), 'admin.bagoucenter') ?: 'active' }}\">\n <a href=\"{{ route('admin.bagoucenter')}}\">\n <i class=\"fa fa-globe\"></i> <span>Bagou Center</span>\n </a>\n </li>"
}
],
"./resources/lang/en/activity.php": [
{
"type": "above",
"theme": "any",
"add": " 'plugin' => [\n 'install' => 'Installation of :name',\n 'uninstall' => 'Uninstallation of :name',\n ],",
"where": "'subuser' => ["
}
],
"./resources/scripts/components/elements/Pagination.tsx": [
{
"type": "replace",
"theme": "any",
"add": "<div css={customcss ? customcss : tw`mt-4 flex justify-center`}>",
"where": "<div css={tw`mt-4 flex justify-center`}>"
},
{
"type": "replace",
"theme": "any",
"add": "import tw, { TwStyle } from 'twin.macro';",
"where": "import tw from 'twin.macro';"
},
{
"type": "replace",
"theme": "any",
"add": "function Pagination<T>({ data: { items, pagination }, onPageSelect, customcss, children }: Props<T>) {",
"where": "function Pagination<T>({ data: { items, pagination }, onPageSelect, children }: Props<T>) {"
},
{
"type": "under",
"theme": "any",
"add": " customcss?: TwStyle;",
"where": "showGoToFirst?: boolean;"
}
],
"./resources/scripts/components/server/files/UploadButton.tsx": [
{
"type": "replace",
"theme": "any",
"where": "data: ProgressEvent, name: string",
"add": "data: any, name: string"
}
],
"./resources/scripts/components/server/settings/RenameServerBox.tsx": [
{
"type": "replace",
"theme": "any",
"where": " description: string;",
"add": " description: any;"
}
]
}