Skip to content

Commit fc90543

Browse files
committed
Support modifying the primary allocation for a server
1 parent bfb28f9 commit fc90543

File tree

17 files changed

+230
-87
lines changed

17 files changed

+230
-87
lines changed

app/Http/Controllers/Api/Client/Servers/NetworkController.php

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33
namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
44

55
use Pterodactyl\Models\Server;
6+
use Pterodactyl\Exceptions\DisplayException;
7+
use Pterodactyl\Repositories\Eloquent\ServerRepository;
8+
use Illuminate\Database\Eloquent\ModelNotFoundException;
69
use Pterodactyl\Repositories\Eloquent\AllocationRepository;
710
use Pterodactyl\Transformers\Api\Client\AllocationTransformer;
811
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
912
use Pterodactyl\Http\Requests\Api\Client\Servers\Network\GetNetworkRequest;
13+
use Pterodactyl\Http\Requests\Api\Client\Servers\Network\SetPrimaryAllocationRequest;
1014

1115
class NetworkController extends ClientApiController
1216
{
@@ -15,16 +19,25 @@ class NetworkController extends ClientApiController
1519
*/
1620
private $repository;
1721

22+
/**
23+
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
24+
*/
25+
private $serverRepository;
26+
1827
/**
1928
* NetworkController constructor.
2029
*
2130
* @param \Pterodactyl\Repositories\Eloquent\AllocationRepository $repository
31+
* @param \Pterodactyl\Repositories\Eloquent\ServerRepository $serverRepository
2232
*/
23-
public function __construct(AllocationRepository $repository)
24-
{
33+
public function __construct(
34+
AllocationRepository $repository,
35+
ServerRepository $serverRepository
36+
) {
2537
parent::__construct();
2638

2739
$this->repository = $repository;
40+
$this->serverRepository = $serverRepository;
2841
}
2942

3043
/**
@@ -37,11 +50,40 @@ public function __construct(AllocationRepository $repository)
3750
*/
3851
public function index(GetNetworkRequest $request, Server $server): array
3952
{
40-
$allocations = $this->repository->findWhere([
41-
['server_id', '=', $server->id],
42-
]);
53+
return $this->fractal->collection($server->allocations)
54+
->transformWith($this->getTransformer(AllocationTransformer::class))
55+
->toArray();
56+
}
57+
58+
/**
59+
* Set the primary allocation for a server.
60+
*
61+
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Network\SetPrimaryAllocationRequest $request
62+
* @param \Pterodactyl\Models\Server $server
63+
* @return array
64+
*
65+
* @throws \Pterodactyl\Exceptions\DisplayException
66+
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
67+
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
68+
*/
69+
public function storePrimary(SetPrimaryAllocationRequest $request, Server $server): array
70+
{
71+
try {
72+
/** @var \Pterodactyl\Models\Allocation $allocation */
73+
$allocation = $this->repository->findFirstWhere([
74+
'server_id' => $server->id,
75+
'ip' => $request->input('ip'),
76+
'port' => $request->input('port'),
77+
]);
78+
} catch (ModelNotFoundException $exception) {
79+
throw new DisplayException(
80+
'The IP and port you selected are not available for this server.'
81+
);
82+
}
83+
84+
$this->serverRepository->update($server->id, ['allocation_id' => $allocation->id]);
4385

44-
return $this->fractal->collection($allocations)
86+
return $this->fractal->item($allocation)
4587
->transformWith($this->getTransformer(AllocationTransformer::class))
4688
->toArray();
4789
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Network;
4+
5+
use Pterodactyl\Models\Permission;
6+
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
7+
8+
class SetPrimaryAllocationRequest extends ClientApiRequest
9+
{
10+
/**
11+
* @return string
12+
*/
13+
public function permission(): string
14+
{
15+
return Permission::ACTION_ALLOCIATION_UPDATE;
16+
}
17+
18+
/**
19+
* @return array
20+
*/
21+
public function rules(): array
22+
{
23+
return [
24+
'ip' => 'required|string',
25+
'port' => 'required|numeric|min:1024|max:65535',
26+
];
27+
}
28+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"sockette": "^2.0.6",
3535
"styled-components": "^5.1.1",
3636
"styled-components-breakpoint": "^3.0.0-preview.20",
37+
"swr": "^0.2.3",
3738
"uuid": "^3.3.2",
3839
"xterm": "^3.14.4",
3940
"xterm-addon-attach": "^0.1.0",

resources/scripts/api/http.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,15 @@ export interface FractalResponseData {
7575
object: string;
7676
attributes: {
7777
[k: string]: any;
78-
relationships?: {
79-
[k: string]: FractalResponseData;
80-
};
78+
relationships?: Record<string, FractalResponseData | FractalResponseList>;
8179
};
8280
}
8381

82+
export interface FractalResponseList {
83+
object: 'list';
84+
data: FractalResponseData[];
85+
}
86+
8487
export interface PaginatedResult<T> {
8588
items: T[];
8689
pagination: PaginationDataSet;

resources/scripts/api/server/getServer.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import http from '@/api/http';
1+
import http, { FractalResponseData, FractalResponseList } from '@/api/http';
2+
import { rawDataToServerAllocation } from '@/api/transformers';
23

34
export interface Allocation {
45
ip: string;
@@ -35,7 +36,7 @@ export interface Server {
3536
isInstalling: boolean;
3637
}
3738

38-
export const rawDataToServerObject = (data: any): Server => ({
39+
export const rawDataToServerObject = ({ attributes: data }: FractalResponseData): Server => ({
3940
id: data.identifier,
4041
uuid: data.uuid,
4142
name: data.name,
@@ -45,23 +46,18 @@ export const rawDataToServerObject = (data: any): Server => ({
4546
port: data.sftp_details.port,
4647
},
4748
description: data.description ? ((data.description.length > 0) ? data.description : null) : null,
48-
allocations: (data.allocations || []).map((datum: any) => ({
49-
ip: datum.ip,
50-
alias: datum.ip_alias,
51-
port: datum.port,
52-
isDefault: datum.is_default,
53-
})),
5449
limits: { ...data.limits },
5550
featureLimits: { ...data.feature_limits },
5651
isSuspended: data.is_suspended,
5752
isInstalling: data.is_installing,
53+
allocations: ((data.relationships?.allocations as FractalResponseList | undefined)?.data || []).map(rawDataToServerAllocation),
5854
});
5955

6056
export default (uuid: string): Promise<[ Server, string[] ]> => {
6157
return new Promise((resolve, reject) => {
6258
http.get(`/api/client/servers/${uuid}`)
6359
.then(({ data }) => resolve([
64-
rawDataToServerObject(data.attributes),
60+
rawDataToServerObject(data),
6561
// eslint-disable-next-line camelcase
6662
data.meta?.is_server_owner ? [ '*' ] : (data.meta?.user_permissions || []),
6763
]))
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import http from '@/api/http';
2+
import { rawDataToServerAllocation } from '@/api/transformers';
3+
import { Allocation } from '@/api/server/getServer';
4+
5+
export default async (uuid: string): Promise<Allocation[]> => {
6+
const { data } = await http.get(`/api/client/servers/${uuid}/network`);
7+
8+
return (data.data || []).map(rawDataToServerAllocation);
9+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Allocation } from '@/api/server/getServer';
2+
import http from '@/api/http';
3+
import { rawDataToServerAllocation } from '@/api/transformers';
4+
5+
export default async (uuid: string, ip: string, port: number): Promise<Allocation> => {
6+
const { data } = await http.put(`/api/client/servers/${uuid}/network/primary`, { ip, port });
7+
8+
return rawDataToServerAllocation(data);
9+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Allocation } from '@/api/server/getServer';
2+
import { FractalResponseData } from '@/api/http';
3+
4+
export const rawDataToServerAllocation = (data: FractalResponseData): Allocation => ({
5+
ip: data.attributes.ip,
6+
alias: data.attributes.ip_alias,
7+
port: data.attributes.port,
8+
isDefault: data.attributes.is_default,
9+
});

resources/scripts/components/elements/Button.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ const ButtonStyle = styled.button<Omit<Props, 'isLoading'>>`
6868
&:hover:not(:disabled) {
6969
${tw`border-neutral-500 text-neutral-100`};
7070
${props => props.color === 'red' && tw`bg-red-500 border-red-600 text-red-50`};
71+
${props => props.color === 'primary' && tw`bg-primary-500 border-primary-600 text-primary-50`};
7172
${props => props.color === 'green' && tw`bg-green-500 border-green-600 text-green-50`};
7273
}
7374
`};

resources/scripts/components/elements/PageContentBlock.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@ import React from 'react';
22
import ContentContainer from '@/components/elements/ContentContainer';
33
import { CSSTransition } from 'react-transition-group';
44
import tw from 'twin.macro';
5+
import FlashMessageRender from '@/components/FlashMessageRender';
56

6-
const PageContentBlock: React.FC<{ className?: string }> = ({ children, className }) => (
7+
const PageContentBlock: React.FC<{ showFlashKey?: string; className?: string }> = ({ children, showFlashKey, className }) => (
78
<CSSTransition timeout={150} classNames={'fade'} appear in>
89
<>
910
<ContentContainer css={tw`my-10`} className={className}>
11+
{showFlashKey &&
12+
<FlashMessageRender byKey={showFlashKey} css={tw`mb-4`}/>
13+
}
1014
{children}
1115
</ContentContainer>
1216
<ContentContainer css={tw`mb-4`}>

0 commit comments

Comments
 (0)