mirror of https://github.com/Duxez/PteroPack.git
parent
4948388722
commit
6746f0f78b
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Pterodactyl\Http\Controllers\Api\Client\Servers\CurseForge;
|
||||||
|
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use Nette\NotImplementedException;
|
||||||
|
use Pterodactyl\Exceptions\DisplayException;
|
||||||
|
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
|
||||||
|
|
||||||
|
class ModpackController extends ClientApiController {
|
||||||
|
private $http_client;
|
||||||
|
private $minecraft_game_id;
|
||||||
|
private $modpack_class_id;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
if(!config('curseforge.api_key')) {
|
||||||
|
throw new DisplayException('CurseForge API key is not set.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->http_client = new Client([
|
||||||
|
'base_uri' => 'https://api.curseforge.com/v1/',
|
||||||
|
'headers' => [
|
||||||
|
'x-api-key' => config('curseforge.api_key'),
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->minecraft_game_id = config('curseforge.minecraft_game_id');
|
||||||
|
$this->modpack_class_id = config('curseforge.minecraft_modpack_class_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index() {
|
||||||
|
$result = $this->http_client->get("mods/search?gameid=$this->minecraft_game_id&classid=$this->modpack_class_id&sortField=2");
|
||||||
|
|
||||||
|
if($result->getStatusCode() !== 200) {
|
||||||
|
throw new DisplayException('Failed to fetch modpacks from CurseForge.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result->getBody()->getContents();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show() {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function install() {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
|
||||||
|
export interface Modpack {
|
||||||
|
id: number;
|
||||||
|
gameId: number;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
links: Link;
|
||||||
|
summary: string;
|
||||||
|
status: number;
|
||||||
|
downloadCount: number;
|
||||||
|
isFeatured: boolean;
|
||||||
|
primaryCategoryId: number;
|
||||||
|
categories: Category[];
|
||||||
|
classId: number;
|
||||||
|
authors: Author[];
|
||||||
|
logo: Image;
|
||||||
|
screenshots: Image[];
|
||||||
|
mainFileId: number;
|
||||||
|
latestFiles: File[];
|
||||||
|
latestFileIndexes: FileIndex[];
|
||||||
|
latestEarlyAccessFileIndexes: FileIndex[];
|
||||||
|
dateCreated: string;
|
||||||
|
dateModified: string;
|
||||||
|
dateReleased: string;
|
||||||
|
allowModDistribution: boolean;
|
||||||
|
gamePopularityRank: number;
|
||||||
|
isAvailable: boolean;
|
||||||
|
thumbsUpCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Link {
|
||||||
|
websiteUrl: string;
|
||||||
|
wikiUrl: string|null;
|
||||||
|
issuesUrl: string|null;
|
||||||
|
sourceUrl: string|null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Category {
|
||||||
|
id: number;
|
||||||
|
gameId: number;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
url: string;
|
||||||
|
iconUrl: string;
|
||||||
|
dateModified: string;
|
||||||
|
isClass: boolean;
|
||||||
|
classId: number;
|
||||||
|
parentCategoryId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Author {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Image {
|
||||||
|
id: number;
|
||||||
|
modId: number;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
thumbnailUrl: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface File {
|
||||||
|
id: number;
|
||||||
|
gameId: number;
|
||||||
|
modId: number;
|
||||||
|
isAvailable: boolean;
|
||||||
|
displayName: string;
|
||||||
|
fileName: string;
|
||||||
|
releaseType: number;
|
||||||
|
fileStatus: number;
|
||||||
|
hashes: Hash[];
|
||||||
|
fileDate: string;
|
||||||
|
fileLength: number;
|
||||||
|
downloadCount: number;
|
||||||
|
downloadUrl: string;
|
||||||
|
gameVersion: string[];
|
||||||
|
sortableGameVersion: GameVersion[];
|
||||||
|
dependencies: any[];
|
||||||
|
alternateFileId: number;
|
||||||
|
isServerPack: boolean;
|
||||||
|
fileFingerprint: number;
|
||||||
|
modules: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Hash {
|
||||||
|
value: string;
|
||||||
|
algorithm: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GameVersion {
|
||||||
|
gameVersionName: string;
|
||||||
|
gameVersionPadded: string;
|
||||||
|
gameVersion: string;
|
||||||
|
gameVersionReleaseDate: string;
|
||||||
|
gameVersionTypeID: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FileIndex {
|
||||||
|
gameVersion: string;
|
||||||
|
fileId: number;
|
||||||
|
filename: string;
|
||||||
|
releaseType: number;
|
||||||
|
gameVersionTypeId: number;
|
||||||
|
modLoader: number;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
import http from '@/api/http'
|
||||||
|
import { Modpack } from './Modpack';
|
||||||
|
|
||||||
|
export default (uuid: string): Promise<any> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
http.get(`/api/client/servers/${uuid}/modpacks`)
|
||||||
|
.then((response) =>
|
||||||
|
resolve((response.data.data || []).map((item: any) => rawDataToModpackData(item))))
|
||||||
|
.catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export const rawDataToModpackData = (data: any): Modpack => ({
|
||||||
|
id: data.id,
|
||||||
|
gameId: data.gameId,
|
||||||
|
name: data.name,
|
||||||
|
slug: data.slug,
|
||||||
|
links: data.links,
|
||||||
|
summary: data.summary,
|
||||||
|
status: data.status,
|
||||||
|
downloadCount: data.downloadCount,
|
||||||
|
isFeatured: data.isFeatured,
|
||||||
|
primaryCategoryId: data.primaryCategoryId,
|
||||||
|
categories: data.categories,
|
||||||
|
classId: data.classId,
|
||||||
|
authors: data.authors,
|
||||||
|
logo: data.logo,
|
||||||
|
screenshots: data.screenshots,
|
||||||
|
mainFileId: data.mainFileId,
|
||||||
|
latestFiles: data.latestFiles,
|
||||||
|
latestFileIndexes: data.latestFileIndexes,
|
||||||
|
latestEarlyAccessFileIndexes: data.latestEarlyAccessFileIndexes,
|
||||||
|
dateCreated: data.dateCreated,
|
||||||
|
dateModified: data.dateModified,
|
||||||
|
dateReleased: data.dateReleased,
|
||||||
|
allowModDistribution: data.allowModDistribution,
|
||||||
|
gamePopularityRank: data.gamePopularityRank,
|
||||||
|
isAvailable: data.isAvailable,
|
||||||
|
thumbsUpCount: data.thumbsUpCount,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import ServerContentBlock from "@/components/elements/ServerContentBlock";
|
||||||
|
import GreyRowBox from '@/components/elements/GreyRowBox';
|
||||||
|
import ModpackItem from './ModpackItem';
|
||||||
|
import { ServerContext } from '@/state/server';
|
||||||
|
import { useDeepMemoize } from '@/plugins/useDeepMemoize';
|
||||||
|
import { use } from 'i18next';
|
||||||
|
import getModpacks from '@/api/server/modpacks/getModpacks';
|
||||||
|
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||||
|
import tw from 'twin.macro';
|
||||||
|
import databases from '@/state/server/databases';
|
||||||
|
import Spinner from '@/components/elements/Spinner';
|
||||||
|
import Fade from '@/components/elements/Fade';
|
||||||
|
import { Modpack } from '@/api/server/modpacks/Modpack';
|
||||||
|
|
||||||
|
export default() => {
|
||||||
|
const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
const [modpacks, setModpacks] = useState<Modpack[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoading(!modpacks.length);
|
||||||
|
|
||||||
|
getModpacks(uuid)
|
||||||
|
.then((modpacks) => setModpacks(modpacks))
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ServerContentBlock title="Modpacks">
|
||||||
|
<FlashMessageRender byKey={'modpacks'} css={tw`mb-4`} />
|
||||||
|
{!modpacks.length && loading ? (
|
||||||
|
<Spinner size={'large'} centered />
|
||||||
|
) : (
|
||||||
|
<Fade timeout={150}>
|
||||||
|
<div className='grid grid-cols-3 gap-4'>
|
||||||
|
{modpacks.length > 0 ? (
|
||||||
|
modpacks.map((modpack, index) => (
|
||||||
|
<ModpackItem
|
||||||
|
key={modpack.id}
|
||||||
|
modpack={modpack}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<p css={tw`text-center text-sm text-neutral-300`}>
|
||||||
|
Couldn't fetch modpacks.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Fade>
|
||||||
|
)}
|
||||||
|
</ServerContentBlock>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { Modpack } from '@/api/server/modpacks/Modpack';
|
||||||
|
import GreyRowBox from '@/components/elements/GreyRowBox';
|
||||||
|
import TitledGreyBox from '@/components/elements/TitledGreyBox';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
modpack: Modpack;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default({modpack}: Props) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<TitledGreyBox title={modpack.name}>
|
||||||
|
<GreyRowBox>
|
||||||
|
<img style={{width: '50px', marginRight: '10px'}} src={modpack.logo.url} />
|
||||||
|
<p>{modpack.summary}</p>
|
||||||
|
</GreyRowBox>
|
||||||
|
</TitledGreyBox>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
use Pterodactyl\Http\Controllers\Api\Client;
|
||||||
|
use Pterodactyl\Http\Middleware\Activity\ServerSubject;
|
||||||
|
use Pterodactyl\Http\Middleware\Activity\AccountSubject;
|
||||||
|
use Pterodactyl\Http\Middleware\RequireTwoFactorAuthentication;
|
||||||
|
use Pterodactyl\Http\Middleware\Api\Client\Server\ResourceBelongsToServer;
|
||||||
|
use Pterodactyl\Http\Middleware\Api\Client\Server\AuthenticateServerAccess;
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Client Control API
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Endpoint: /api/client
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
Route::get('/', [Client\ClientController::class, 'index'])->name('api:client.index');
|
||||||
|
Route::get('/permissions', [Client\ClientController::class, 'permissions']);
|
||||||
|
|
||||||
|
Route::prefix('/account')->middleware(AccountSubject::class)->group(function () {
|
||||||
|
Route::prefix('/')->withoutMiddleware(RequireTwoFactorAuthentication::class)->group(function () {
|
||||||
|
Route::get('/', [Client\AccountController::class, 'index'])->name('api:client.account');
|
||||||
|
Route::get('/two-factor', [Client\TwoFactorController::class, 'index']);
|
||||||
|
Route::post('/two-factor', [Client\TwoFactorController::class, 'store']);
|
||||||
|
Route::delete('/two-factor', [Client\TwoFactorController::class, 'delete']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Route::put('/email', [Client\AccountController::class, 'updateEmail'])->name('api:client.account.update-email');
|
||||||
|
Route::put('/password', [Client\AccountController::class, 'updatePassword'])->name('api:client.account.update-password');
|
||||||
|
|
||||||
|
Route::get('/activity', Client\ActivityLogController::class)->name('api:client.account.activity');
|
||||||
|
|
||||||
|
Route::get('/api-keys', [Client\ApiKeyController::class, 'index']);
|
||||||
|
Route::post('/api-keys', [Client\ApiKeyController::class, 'store']);
|
||||||
|
Route::delete('/api-keys/{identifier}', [Client\ApiKeyController::class, 'delete']);
|
||||||
|
|
||||||
|
Route::prefix('/ssh-keys')->group(function () {
|
||||||
|
Route::get('/', [Client\SSHKeyController::class, 'index']);
|
||||||
|
Route::post('/', [Client\SSHKeyController::class, 'store']);
|
||||||
|
Route::post('/remove', [Client\SSHKeyController::class, 'delete']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Client Control API
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Endpoint: /api/client/servers/{server}
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
Route::group([
|
||||||
|
'prefix' => '/servers/{server}',
|
||||||
|
'middleware' => [
|
||||||
|
ServerSubject::class,
|
||||||
|
AuthenticateServerAccess::class,
|
||||||
|
ResourceBelongsToServer::class,
|
||||||
|
],
|
||||||
|
], function () {
|
||||||
|
Route::get('/', [Client\Servers\ServerController::class, 'index'])->name('api:client:server.view');
|
||||||
|
Route::get('/websocket', Client\Servers\WebsocketController::class)->name('api:client:server.ws');
|
||||||
|
Route::get('/resources', Client\Servers\ResourceUtilizationController::class)->name('api:client:server.resources');
|
||||||
|
Route::get('/activity', Client\Servers\ActivityLogController::class)->name('api:client:server.activity');
|
||||||
|
|
||||||
|
Route::post('/command', [Client\Servers\CommandController::class, 'index']);
|
||||||
|
Route::post('/power', [Client\Servers\PowerController::class, 'index']);
|
||||||
|
|
||||||
|
Route::group(['prefix' => '/modpacks'], function () {
|
||||||
|
Route::get('/', [Client\Servers\CurseForge\ModpackController::class, 'index']);
|
||||||
|
Route::get('/{modpack}', [Client\Servers\CurseForge\ModpackController::class, 'show']);
|
||||||
|
Route::get('/{modpack}/install', [Client\Servers\CurseForge\CurseForgeModpackController::class, 'install']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Route::group(['prefix' => '/databases'], function () {
|
||||||
|
Route::get('/', [Client\Servers\DatabaseController::class, 'index']);
|
||||||
|
Route::post('/', [Client\Servers\DatabaseController::class, 'store']);
|
||||||
|
Route::post('/{database}/rotate-password', [Client\Servers\DatabaseController::class, 'rotatePassword']);
|
||||||
|
Route::delete('/{database}', [Client\Servers\DatabaseController::class, 'delete']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Route::group(['prefix' => '/files'], function () {
|
||||||
|
Route::get('/list', [Client\Servers\FileController::class, 'directory']);
|
||||||
|
Route::get('/contents', [Client\Servers\FileController::class, 'contents']);
|
||||||
|
Route::get('/download', [Client\Servers\FileController::class, 'download']);
|
||||||
|
Route::put('/rename', [Client\Servers\FileController::class, 'rename']);
|
||||||
|
Route::post('/copy', [Client\Servers\FileController::class, 'copy']);
|
||||||
|
Route::post('/write', [Client\Servers\FileController::class, 'write']);
|
||||||
|
Route::post('/compress', [Client\Servers\FileController::class, 'compress']);
|
||||||
|
Route::post('/decompress', [Client\Servers\FileController::class, 'decompress']);
|
||||||
|
Route::post('/delete', [Client\Servers\FileController::class, 'delete']);
|
||||||
|
Route::post('/create-folder', [Client\Servers\FileController::class, 'create']);
|
||||||
|
Route::post('/chmod', [Client\Servers\FileController::class, 'chmod']);
|
||||||
|
Route::post('/pull', [Client\Servers\FileController::class, 'pull'])->middleware(['throttle:10,5']);
|
||||||
|
Route::get('/upload', Client\Servers\FileUploadController::class);
|
||||||
|
});
|
||||||
|
|
||||||
|
Route::group(['prefix' => '/schedules'], function () {
|
||||||
|
Route::get('/', [Client\Servers\ScheduleController::class, 'index']);
|
||||||
|
Route::post('/', [Client\Servers\ScheduleController::class, 'store']);
|
||||||
|
Route::get('/{schedule}', [Client\Servers\ScheduleController::class, 'view']);
|
||||||
|
Route::post('/{schedule}', [Client\Servers\ScheduleController::class, 'update']);
|
||||||
|
Route::post('/{schedule}/execute', [Client\Servers\ScheduleController::class, 'execute']);
|
||||||
|
Route::delete('/{schedule}', [Client\Servers\ScheduleController::class, 'delete']);
|
||||||
|
|
||||||
|
Route::post('/{schedule}/tasks', [Client\Servers\ScheduleTaskController::class, 'store']);
|
||||||
|
Route::post('/{schedule}/tasks/{task}', [Client\Servers\ScheduleTaskController::class, 'update']);
|
||||||
|
Route::delete('/{schedule}/tasks/{task}', [Client\Servers\ScheduleTaskController::class, 'delete']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Route::group(['prefix' => '/network'], function () {
|
||||||
|
Route::get('/allocations', [Client\Servers\NetworkAllocationController::class, 'index']);
|
||||||
|
Route::post('/allocations', [Client\Servers\NetworkAllocationController::class, 'store']);
|
||||||
|
Route::post('/allocations/{allocation}', [Client\Servers\NetworkAllocationController::class, 'update']);
|
||||||
|
Route::post('/allocations/{allocation}/primary', [Client\Servers\NetworkAllocationController::class, 'setPrimary']);
|
||||||
|
Route::delete('/allocations/{allocation}', [Client\Servers\NetworkAllocationController::class, 'delete']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Route::group(['prefix' => '/users'], function () {
|
||||||
|
Route::get('/', [Client\Servers\SubuserController::class, 'index']);
|
||||||
|
Route::post('/', [Client\Servers\SubuserController::class, 'store']);
|
||||||
|
Route::get('/{user}', [Client\Servers\SubuserController::class, 'view']);
|
||||||
|
Route::post('/{user}', [Client\Servers\SubuserController::class, 'update']);
|
||||||
|
Route::delete('/{user}', [Client\Servers\SubuserController::class, 'delete']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Route::group(['prefix' => '/backups'], function () {
|
||||||
|
Route::get('/', [Client\Servers\BackupController::class, 'index']);
|
||||||
|
Route::post('/', [Client\Servers\BackupController::class, 'store']);
|
||||||
|
Route::get('/{backup}', [Client\Servers\BackupController::class, 'view']);
|
||||||
|
Route::get('/{backup}/download', [Client\Servers\BackupController::class, 'download']);
|
||||||
|
Route::post('/{backup}/lock', [Client\Servers\BackupController::class, 'toggleLock']);
|
||||||
|
Route::post('/{backup}/restore', [Client\Servers\BackupController::class, 'restore']);
|
||||||
|
Route::delete('/{backup}', [Client\Servers\BackupController::class, 'delete']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Route::group(['prefix' => '/startup'], function () {
|
||||||
|
Route::get('/', [Client\Servers\StartupController::class, 'index']);
|
||||||
|
Route::put('/variable', [Client\Servers\StartupController::class, 'update']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Route::group(['prefix' => '/settings'], function () {
|
||||||
|
Route::post('/rename', [Client\Servers\SettingsController::class, 'rename']);
|
||||||
|
Route::post('/reinstall', [Client\Servers\SettingsController::class, 'reinstall']);
|
||||||
|
Route::put('/docker-image', [Client\Servers\SettingsController::class, 'dockerImage']);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue