Skip to content

Commit 7670cf1

Browse files
committed
Merge pull request pterodactyl#29 from Pterodactyl/add-restful-api
Add initial API Implementation
2 parents 31b3153 + 0389f14 commit 7670cf1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1519
-459
lines changed

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,7 @@ MAIL_PORT=2525
2222
MAIL_USERNAME=null
2323
MAIL_PASSWORD=null
2424
MAIL_ENCRYPTION=null
25+
26+
API_PREFIX=api
27+
API_VERSION=v1
28+
API_DEBUG=false

app/Exceptions/Handler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public function render($request, Exception $e)
5555
$e = new NotFoundHttpException($e->getMessage(), $e);
5656
}
5757

58-
if ($request->isXmlHttpRequest() || $request->ajax() || $request->is('api/*') || $request->is('remote/*')) {
58+
if ($request->isXmlHttpRequest() || $request->ajax() || $request->is('remote/*')) {
5959

6060
$exception = 'An exception occured while attempting to perform this action, please try again.';
6161

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Controllers\API;
4+
5+
use Dingo\Api\Routing\Helpers;
6+
use Illuminate\Routing\Controller;
7+
8+
class BaseController extends Controller
9+
{
10+
use Helpers;
11+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Controllers\API;
4+
5+
use DB;
6+
use Illuminate\Http\Request;
7+
use Pterodactyl\Models\Location;
8+
9+
/**
10+
* @Resource("Servers")
11+
*/
12+
class LocationController extends BaseController
13+
{
14+
15+
public function __construct()
16+
{
17+
//
18+
}
19+
20+
/**
21+
* List All Locations
22+
*
23+
* Lists all locations currently on the system.
24+
*
25+
* @Get("/locations")
26+
* @Versions({"v1"})
27+
* @Response(200)
28+
*/
29+
public function getLocations(Request $request)
30+
{
31+
$locations = Location::select('locations.*', DB::raw('GROUP_CONCAT(nodes.id) as nodes'))
32+
->join('nodes', 'locations.id', '=', 'nodes.location')
33+
->groupBy('locations.id')
34+
->get();
35+
36+
foreach($locations as &$location) {
37+
$location->nodes = explode(',', $location->nodes);
38+
}
39+
40+
return $locations;
41+
}
42+
43+
}
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Controllers\API;
4+
5+
use Illuminate\Http\Request;
6+
7+
use Pterodactyl\Models;
8+
use Pterodactyl\Transformers\NodeTransformer;
9+
use Pterodactyl\Repositories\NodeRepository;
10+
11+
use Pterodactyl\Exceptions\DisplayValidationException;
12+
use Pterodactyl\Exceptions\DisplayException;
13+
use Dingo\Api\Exception\ResourceException;
14+
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
15+
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
16+
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
17+
18+
/**
19+
* @Resource("Servers")
20+
*/
21+
class NodeController extends BaseController
22+
{
23+
24+
public function __construct()
25+
{
26+
//
27+
}
28+
29+
/**
30+
* List All Nodes
31+
*
32+
* Lists all nodes currently on the system.
33+
*
34+
* @Get("/nodes/{?page}")
35+
* @Versions({"v1"})
36+
* @Parameters({
37+
* @Parameter("page", type="integer", description="The page of results to view.", default=1)
38+
* })
39+
* @Response(200)
40+
*/
41+
public function getNodes(Request $request)
42+
{
43+
$nodes = Models\Node::paginate(50);
44+
return $this->response->paginator($nodes, new NodeTransformer);
45+
}
46+
47+
/**
48+
* Create a New Node
49+
*
50+
* @Post("/nodes")
51+
* @Versions({"v1"})
52+
* @Transaction({
53+
* @Request({
54+
* 'name' => 'My API Node',
55+
* 'location' => 1,
56+
* 'public' => 1,
57+
* 'fqdn' => 'daemon.wuzzle.woo',
58+
* 'scheme' => 'https',
59+
* 'memory' => 10240,
60+
* 'memory_overallocate' => 100,
61+
* 'disk' => 204800,
62+
* 'disk_overallocate' => -1,
63+
* 'daemonBase' => '/srv/daemon-data',
64+
* 'daemonSFTP' => 2022,
65+
* 'daemonListen' => 8080
66+
* }, headers={"Authorization": "Bearer <jwt-token>"}),
67+
* @Response(201),
68+
* @Response(422, body={
69+
* "message": "A validation error occured.",
70+
* "errors": {},
71+
* "status_code": 422
72+
* }),
73+
* @Response(503, body={
74+
* "message": "There was an error while attempting to add this node to the system.",
75+
* "status_code": 503
76+
* })
77+
* })
78+
*/
79+
public function postNode(Request $request)
80+
{
81+
try {
82+
$node = new NodeRepository;
83+
$new = $node->create($request->all());
84+
return $this->response->created(route('api.nodes.view', [
85+
'id' => $new
86+
]));
87+
} catch (DisplayValidationException $ex) {
88+
throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true));
89+
} catch (DisplayException $ex) {
90+
throw new ResourceException($ex->getMessage());
91+
} catch (\Exception $e) {
92+
throw new BadRequestHttpException('There was an error while attempting to add this node to the system.');
93+
}
94+
}
95+
96+
/**
97+
* List Specific Node
98+
*
99+
* Lists specific fields about a server or all fields pertaining to that node.
100+
*
101+
* @Get("/nodes/{id}/{?fields}")
102+
* @Versions({"v1"})
103+
* @Parameters({
104+
* @Parameter("id", type="integer", required=true, description="The ID of the node to get information on."),
105+
* @Parameter("fields", type="string", required=false, description="A comma delimidated list of fields to include.")
106+
* })
107+
* @Response(200)
108+
*/
109+
public function getNode(Request $request, $id, $fields = null)
110+
{
111+
$query = Models\Node::where('id', $id);
112+
113+
if (!is_null($request->input('fields'))) {
114+
foreach(explode(',', $request->input('fields')) as $field) {
115+
if (!empty($field)) {
116+
$query->addSelect($field);
117+
}
118+
}
119+
}
120+
121+
try {
122+
if (!$query->first()) {
123+
throw new NotFoundHttpException('No node by that ID was found.');
124+
}
125+
return $query->first();
126+
} catch (NotFoundHttpException $ex) {
127+
throw $ex;
128+
} catch (\Exception $ex) {
129+
throw new BadRequestHttpException('There was an issue with the fields passed in the request.');
130+
}
131+
}
132+
133+
/**
134+
* List Node Allocations
135+
*
136+
* Returns a listing of all node allocations.
137+
*
138+
* @Get("/nodes/{id}/allocations")
139+
* @Versions({"v1"})
140+
* @Parameters({
141+
* @Parameter("id", type="integer", required=true, description="The ID of the node to get allocations for."),
142+
* })
143+
* @Response(200)
144+
*/
145+
public function getNodeAllocations(Request $request, $id)
146+
{
147+
$allocations = Models\Allocation::select('ip', 'port', 'assigned_to')->where('node', $id)->orderBy('ip', 'asc')->orderBy('port', 'asc')->get();
148+
if ($allocations->count() < 1) {
149+
throw new NotFoundHttpException('No allocations where found for the requested node.');
150+
}
151+
return $allocations;
152+
}
153+
154+
/**
155+
* Delete Node
156+
*
157+
* @Delete("/nodes/{id}")
158+
* @Versions({"v1"})
159+
* @Parameters({
160+
* @Parameter("id", type="integer", required=true, description="The ID of the node."),
161+
* })
162+
* @Response(204)
163+
*/
164+
public function deleteNode(Request $request, $id)
165+
{
166+
try {
167+
$node = new NodeRepository;
168+
$node->delete($id);
169+
return $this->response->noContent();
170+
} catch (DisplayException $ex) {
171+
throw new ResourceException($ex->getMessage());
172+
} catch(\Exception $e) {
173+
throw new ServiceUnavailableHttpException('An error occured while attempting to delete this node.');
174+
}
175+
}
176+
177+
}

0 commit comments

Comments
 (0)