diff --git a/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php b/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php index fe0ca5610..c6acb66d3 100644 --- a/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php +++ b/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php @@ -17,6 +17,16 @@ class AuthenticateServerAccess */ private $repository; + /** + * Routes that this middleware should not apply to if the user is an admin. + * + * @var string[] + */ + protected $except = [ + 'api:client:server.view', + 'api:client:server.ws', + ]; + /** * AuthenticateServerAccess constructor. * @@ -36,6 +46,8 @@ class AuthenticateServerAccess */ public function handle(Request $request, Closure $next) { + /** @var \Pterodactyl\Models\User $user */ + $user = $request->user(); $server = $request->route()->parameter('server'); if (! $server instanceof Server) { @@ -45,9 +57,9 @@ class AuthenticateServerAccess // At the very least, ensure that the user trying to make this request is the // server owner, a subuser, or a root admin. We'll leave it up to the controllers // to authenticate more detailed permissions if needed. - if ($request->user()->id !== $server->owner_id && ! $request->user()->root_admin) { + if ($user->id !== $server->owner_id && ! $user->root_admin) { // Check for subuser status. - if (! $server->subusers->contains('user_id', $request->user()->id)) { + if (! $server->subusers->contains('user_id', $user->id)) { throw new NotFoundHttpException(trans('exceptions.api.resource_not_found')); } } @@ -57,7 +69,11 @@ class AuthenticateServerAccess } if (! $server->isInstalled()) { - throw new ConflictHttpException('Server has not completed the installation process.'); + // Throw an exception for all server routes; however if the user is an admin and requesting the + // server details, don't throw the exception for them. + if (! $user->root_admin || ($user->root_admin && ! $request->routeIs($this->except))) { + throw new ConflictHttpException('Server has not completed the installation process.'); + } } $request->attributes->set('server', $server); diff --git a/resources/scripts/components/server/ServerConsole.tsx b/resources/scripts/components/server/ServerConsole.tsx index 0e2883cc5..5f5addf56 100644 --- a/resources/scripts/components/server/ServerConsole.tsx +++ b/resources/scripts/components/server/ServerConsole.tsx @@ -12,6 +12,7 @@ import SuspenseSpinner from '@/components/elements/SuspenseSpinner'; import TitledGreyBox from '@/components/elements/TitledGreyBox'; import Can from '@/components/elements/Can'; import PageContentBlock from '@/components/elements/PageContentBlock'; +import ContentContainer from '@/components/elements/ContentContainer'; type PowerAction = 'start' | 'stop' | 'restart' | 'kill'; @@ -123,36 +124,47 @@ export default () => { / {server.limits.disk} MB

- -
- - - - - - - - sendPowerCommand(action)}/> - + {!server.isInstalling ? + +
+ + + + + + + + sendPowerCommand(action)}/> + +
+
+ : +
+ +

+ This server is currently running its installation process and most actions are + unavailable. +

+
- + }
diff --git a/resources/scripts/routers/ServerRouter.tsx b/resources/scripts/routers/ServerRouter.tsx index 7fde7c805..cef080969 100644 --- a/resources/scripts/routers/ServerRouter.tsx +++ b/resources/scripts/routers/ServerRouter.tsx @@ -20,12 +20,16 @@ import Spinner from '@/components/elements/Spinner'; import ServerError from '@/components/screens/ServerError'; import { httpErrorToHuman } from '@/api/http'; import NotFound from '@/components/screens/NotFound'; +import { useStoreState } from 'easy-peasy'; +import ServerInstallingBar from '@/components/elements/ServerInstallingBar'; +import useServer from '@/plugins/useServer'; import ScreenBlock from '@/components/screens/ScreenBlock'; const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) => { + const { rootAdmin } = useStoreState(state => state.user.data!); const [ error, setError ] = useState(''); const [ installing, setInstalling ] = useState(false); - const server = ServerContext.useStoreState(state => state.server.data); + const server = useServer(); const getServer = ServerContext.useStoreActions(actions => actions.server.getServer); const clearServerState = ServerContext.useStoreActions(actions => actions.clearServerState); @@ -33,6 +37,10 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) clearServerState(); }, []); + useEffect(() => { + setInstalling(server?.isInstalling !== false); + }, [ server?.isInstalling ]); + useEffect(() => { setError(''); setInstalling(false); @@ -55,19 +63,12 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) {!server ? - !installing ? - error ? - - : -
- -
+ error ? + : - +
+ +
: <> @@ -95,29 +96,43 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>)
- - - - - - ( - - - - )} - exact - /> - - - - - - - - - + {(installing && (!rootAdmin || (rootAdmin && !location.pathname.endsWith(`/server/${server.id}`)))) ? + + : + <> + + + + + + ( + + + + )} + exact + /> + + + + + + + + + + + } } diff --git a/routes/api-client.php b/routes/api-client.php index 5ad045801..9f6b2c075 100644 --- a/routes/api-client.php +++ b/routes/api-client.php @@ -11,17 +11,17 @@ use Pterodactyl\Http\Middleware\Api\Client\Server\AuthenticateServerAccess; | Endpoint: /api/client | */ -Route::get('/', 'ClientController@index')->name('api.client.index'); +Route::get('/', 'ClientController@index')->name('api:client.index'); Route::get('/permissions', 'ClientController@permissions'); Route::group(['prefix' => '/account'], function () { - Route::get('/', 'AccountController@index')->name('api.client.account'); + Route::get('/', 'AccountController@index')->name('api:client.account'); Route::get('/two-factor', 'TwoFactorController@index'); Route::post('/two-factor', 'TwoFactorController@store'); Route::delete('/two-factor', 'TwoFactorController@delete'); - Route::put('/email', 'AccountController@updateEmail')->name('api.client.account.update-email'); - Route::put('/password', 'AccountController@updatePassword')->name('api.client.account.update-password'); + Route::put('/email', 'AccountController@updateEmail')->name('api:client.account.update-email'); + Route::put('/password', 'AccountController@updatePassword')->name('api:client.account.update-password'); Route::get('/api-keys', 'ApiKeyController@index'); Route::post('/api-keys', 'ApiKeyController@store'); @@ -37,30 +37,29 @@ Route::group(['prefix' => '/account'], function () { | */ Route::group(['prefix' => '/servers/{server}', 'middleware' => [AuthenticateServerAccess::class]], function () { - Route::get('/', 'Servers\ServerController@index')->name('api.client.servers.view'); - Route::get('/websocket', 'Servers\WebsocketController')->name('api.client.servers.websocket'); - Route::get('/resources', 'Servers\ResourceUtilizationController') - ->name('api.client.servers.resources'); + Route::get('/', 'Servers\ServerController@index')->name('api:client:server.view'); + Route::get('/websocket', 'Servers\WebsocketController')->name('api:client:server.ws'); + Route::get('/resources', 'Servers\ResourceUtilizationController')->name('api:client:server.resources'); - Route::post('/command', 'Servers\CommandController@index')->name('api.client.servers.command'); - Route::post('/power', 'Servers\PowerController@index')->name('api.client.servers.power'); + Route::post('/command', 'Servers\CommandController@index'); + Route::post('/power', 'Servers\PowerController@index'); Route::group(['prefix' => '/databases'], function () { - Route::get('/', 'Servers\DatabaseController@index')->name('api.client.servers.databases'); + Route::get('/', 'Servers\DatabaseController@index'); Route::post('/', 'Servers\DatabaseController@store'); Route::post('/{database}/rotate-password', 'Servers\DatabaseController@rotatePassword'); - Route::delete('/{database}', 'Servers\DatabaseController@delete')->name('api.client.servers.databases.delete'); + Route::delete('/{database}', 'Servers\DatabaseController@delete'); }); Route::group(['prefix' => '/files'], function () { - Route::get('/list', 'Servers\FileController@listDirectory')->name('api.client.servers.files.list'); - Route::get('/contents', 'Servers\FileController@getFileContents')->name('api.client.servers.files.contents'); + Route::get('/list', 'Servers\FileController@listDirectory'); + Route::get('/contents', 'Servers\FileController@getFileContents'); Route::get('/download', 'Servers\FileController@download'); - Route::put('/rename', 'Servers\FileController@renameFile')->name('api.client.servers.files.rename'); - Route::post('/copy', 'Servers\FileController@copyFile')->name('api.client.servers.files.copy'); - Route::post('/write', 'Servers\FileController@writeFileContents')->name('api.client.servers.files.write'); - Route::post('/delete', 'Servers\FileController@delete')->name('api.client.servers.files.delete'); - Route::post('/create-folder', 'Servers\FileController@createFolder')->name('api.client.servers.files.create-folder'); + Route::put('/rename', 'Servers\FileController@renameFile'); + Route::post('/copy', 'Servers\FileController@copyFile'); + Route::post('/write', 'Servers\FileController@writeFileContents'); + Route::post('/delete', 'Servers\FileController@delete'); + Route::post('/create-folder', 'Servers\FileController@createFolder'); }); Route::group(['prefix' => '/schedules'], function () { @@ -76,7 +75,7 @@ Route::group(['prefix' => '/servers/{server}', 'middleware' => [AuthenticateServ }); Route::group(['prefix' => '/network'], function () { - Route::get('/', 'Servers\NetworkController@index')->name('api.client.servers.network'); + Route::get('/', 'Servers\NetworkController@index'); }); Route::group(['prefix' => '/users'], function () {