Skip to content

Commit c09170a

Browse files
authored
Merge pull request pterodactyl#245 from schrej/feature/wings-auto-setup
Configuration retrival for wings via token
2 parents 2dbacaf + 9f2ca17 commit c09170a

File tree

9 files changed

+232
-42
lines changed

9 files changed

+232
-42
lines changed

app/Http/Controllers/Admin/NodesController.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use DB;
2828
use Log;
2929
use Alert;
30+
use Carbon;
3031
use Validator;
3132
use Pterodactyl\Models;
3233
use Illuminate\Http\Request;
@@ -82,6 +83,7 @@ public function postNew(Request $request)
8283
'_token',
8384
]));
8485
Alert::success('Successfully created new node. <strong>Before you can add any servers you need to first assign some IP addresses and ports.</strong>')->flash();
86+
Alert::info('<strong>To simplify the node setup you can generate a token on the configuration tab.</strong>')->flash();
8587

8688
return redirect()->route('admin.nodes.view', [
8789
'id' => $new,
@@ -276,4 +278,24 @@ public function deleteNode(Request $request, $id)
276278
'tab' => 'tab_delete',
277279
]);
278280
}
281+
282+
public function getConfigurationToken(Request $request, $id)
283+
{
284+
// Check if Node exists. Will lead to 404 if not.
285+
Models\Node::findOrFail($id);
286+
287+
// Create a token
288+
$token = new Models\NodeConfigurationToken();
289+
$token->node = $id;
290+
$token->token = str_random(32);
291+
$token->expires_at = Carbon::now()->addMinutes(5); // Expire in 5 Minutes
292+
$token->save();
293+
294+
$token_response = [
295+
'token' => $token->token,
296+
'expires_at' => $token->expires_at->toDateTimeString(),
297+
];
298+
299+
return response()->json($token_response, 200);
300+
}
279301
}

app/Http/Controllers/Remote/RemoteController.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
namespace Pterodactyl\Http\Controllers\Remote;
2626

27+
use Carbon\Carbon;
2728
use Pterodactyl\Models;
2829
use Illuminate\Http\Request;
2930
use Pterodactyl\Http\Controllers\Controller;
@@ -107,4 +108,29 @@ public function event(Request $request)
107108

108109
return response('', 201);
109110
}
111+
112+
public function getConfiguration(Request $request, $tokenString)
113+
{
114+
// Try to query the token and the node from the database
115+
try {
116+
$token = Models\NodeConfigurationToken::where('token', $tokenString)->firstOrFail();
117+
$node = Models\Node::findOrFail($token->node);
118+
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
119+
return response()->json(['error' => 'token_invalid'], 403);
120+
}
121+
122+
// Check if token is expired
123+
if ($token->expires_at->lt(Carbon::now())) {
124+
$token->delete();
125+
126+
return response()->json(['error' => 'token_expired'], 403);
127+
}
128+
129+
// Delete the token, it's one-time use
130+
$token->delete();
131+
132+
// Manually as getConfigurationAsJson() returns it in correct format already
133+
return response($node->getConfigurationAsJson(), 200)
134+
->header('Content-Type', 'application/json');
135+
}
110136
}

app/Http/Routes/AdminRoutes.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,11 @@ public function map(Router $router)
286286
'as' => 'admin.nodes.delete',
287287
'uses' => 'Admin\NodesController@deleteNode',
288288
]);
289+
290+
$router->get('/{id}/configurationtoken', [
291+
'as' => 'admin.nodes.configuration-token',
292+
'uses' => 'Admin\NodesController@getConfigurationToken',
293+
]);
289294
});
290295

291296
// Location Routes

app/Http/Routes/RemoteRoutes.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ public function map(Router $router)
4646
'as' => 'remote.event',
4747
'uses' => 'Remote\RemoteController@event',
4848
]);
49+
50+
$router->get('configuration/{token}', [
51+
'as' => 'remote.configuration',
52+
'uses' => 'Remote\RemoteController@getConfiguration',
53+
]);
4954
});
5055
}
5156
}

app/Models/Node.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,61 @@ public static function guzzleRequest($node)
117117

118118
return self::$guzzle[$node];
119119
}
120+
121+
/**
122+
* Returns the configuration in JSON format.
123+
*
124+
* @param bool $pretty Wether to pretty print the JSON or not
125+
* @return string The configration in JSON format
126+
*/
127+
public function getConfigurationAsJson($pretty = false)
128+
{
129+
$config = [
130+
'web' => [
131+
'host' => '0.0.0.0',
132+
'listen' => $this->daemonListen,
133+
'ssl' => [
134+
'enabled' => $this->scheme === 'https',
135+
'certificate' => '/etc/letsencrypt/live/localhost/fullchain.pem',
136+
'key' => '/etc/letsencrypt/live/localhost/privkey.pem',
137+
],
138+
],
139+
'docker' => [
140+
'socket' => '/var/run/docker.sock',
141+
'autoupdate_images' => true,
142+
],
143+
'sftp' => [
144+
'path' => $this->daemonBase,
145+
'port' => $this->daemonSFTP,
146+
'container' => 'ptdl-sftp',
147+
],
148+
'query' => [
149+
'kill_on_fail' => true,
150+
'fail_limit' => 5,
151+
],
152+
'logger' => [
153+
'path' => 'logs/',
154+
'src' => false,
155+
'level' => 'info',
156+
'period' => '1d',
157+
'count' => 3,
158+
],
159+
'remote' => [
160+
'base' => config('app.url'),
161+
'download' => route('remote.download'),
162+
'installed' => route('remote.install'),
163+
],
164+
'uploads' => [
165+
'size_limit' => $this->upload_size,
166+
],
167+
'keys' => [$this->daemonSecret],
168+
];
169+
170+
$json_options = JSON_UNESCAPED_SLASHES;
171+
if ($pretty) {
172+
$json_options |= JSON_PRETTY_PRINT;
173+
}
174+
175+
return json_encode($config, $json_options);
176+
}
120177
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
/**
3+
* Pterodactyl - Panel
4+
* Copyright (c) 2015 - 2016 Dane Everitt <dane@daneeveritt.com>.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
namespace Pterodactyl\Models;
26+
27+
use Illuminate\Database\Eloquent\Model;
28+
29+
class NodeConfigurationToken extends Model
30+
{
31+
/**
32+
* The table associated with the model.
33+
*
34+
* @var string
35+
*/
36+
protected $table = 'node_configuration_tokens';
37+
38+
/**
39+
* Fields that are not mass assignable.
40+
*
41+
* @var array
42+
*/
43+
protected $guarded = ['id', 'created_at', 'updated_at'];
44+
45+
/**
46+
* The attributes that should be mutated to dates.
47+
*
48+
* @var array
49+
*/
50+
protected $dates = ['created_at', 'updated_at', 'expires_at'];
51+
}

app/Repositories/NodeRepository.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,9 @@ public function delete($id)
282282
// Delete Allocations
283283
Models\Allocation::where('node', $node->id)->delete();
284284

285+
// Delete configure tokens
286+
Models\NodeConfigurationToken::where('node', $node->id)->delete();
287+
285288
// Delete Node
286289
$node->delete();
287290

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
use Illuminate\Support\Facades\Schema;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Database\Migrations\Migration;
6+
7+
class CreateNodeConfigurationTokensTable extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*
12+
* @return void
13+
*/
14+
public function up()
15+
{
16+
Schema::create('node_configuration_tokens', function (Blueprint $table) {
17+
$table->increments('id');
18+
$table->char('token', 32);
19+
$table->timestamp('expires_at');
20+
$table->integer('node')->unsigned();
21+
$table->foreign('node')->references('id')->on('nodes');
22+
$table->timestamps();
23+
});
24+
}
25+
26+
/**
27+
* Reverse the migrations.
28+
*
29+
* @return void
30+
*/
31+
public function down()
32+
{
33+
Schema::dropIfExists('node_configuration_tokens');
34+
}
35+
}

resources/views/admin/nodes/view.blade.php

Lines changed: 28 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -287,48 +287,13 @@
287287
Below is the configuration file for your daemon on this node. We recommend <strong>not</strong> simply copy and pasting the code below unless you know what you are doing. You should run the <code>auto-installer</code> or <code>auto-updater</code> to setup the daemon.
288288
</div>
289289
<div class="col-md-12">
290-
<pre><code>{
291-
"web": {
292-
"host": "0.0.0.0",
293-
"listen": {{ $node->daemonListen }},
294-
"ssl": {
295-
"enabled": {{ $node->scheme === 'https' ? 'true' : 'false' }},
296-
"certificate": "/etc/letsencrypt/live/{{ $node->fqdn }}/fullchain.pem",
297-
"key": "/etc/letsencrypt/live/{{ $node->fqdn }}/privkey.pem"
298-
}
299-
},
300-
"docker": {
301-
"socket": "/var/run/docker.sock",
302-
"autoupdate_images": true
303-
},
304-
"sftp": {
305-
"path": "{{ $node->daemonBase }}",
306-
"port": {{ $node->daemonSFTP }},
307-
"container": "ptdl-sftp"
308-
},
309-
"query": {
310-
"kill_on_fail": true,
311-
"fail_limit": 5
312-
},
313-
"logger": {
314-
"path": "logs/",
315-
"src": false,
316-
"level": "info",
317-
"period": "1d",
318-
"count": 3
319-
},
320-
"remote": {
321-
"base": "{{ config('app.url') }}",
322-
"download": "{{ route('remote.download') }}",
323-
"installed": "{{ route('remote.install') }}"
324-
},
325-
"uploads": {
326-
"size_limit": {{ $node->upload_size }}
327-
},
328-
"keys": [
329-
"{{ $node->daemonSecret }}"
330-
]
331-
}</code></pre>
290+
<p>To simplify the configuration of nodes it is possible to fetch the config from the panel. A token is required for this process. The button below will generate a token and provide you with the commands necessary for automatic configuration of the node. Be aware that these tokens are only valid for 5 minutes.</p>
291+
<p class="text-center">
292+
<button type="button" id="configTokenBtn" class="btn btn-primary">Generate token</button>
293+
</p>
294+
</div>
295+
<div class="col-md-12">
296+
<pre><code>{{ $node->getConfigurationAsJson(true) }}</code></pre>
332297
</div>
333298
</div>
334299
</div>
@@ -536,6 +501,27 @@
536501
});
537502
});
538503
504+
$('#configTokenBtn').on('click', function (event) {
505+
$.getJSON('{{ route('admin.nodes.configuration-token', $node->id) }}')
506+
.done(function (data) {
507+
swal({
508+
type: 'success',
509+
title: 'Token created.',
510+
text: 'Here is your token: <code>'+data.token+'</code><br />' +
511+
'It will expire at <i>' + data.expires_at + '</i><br /><br />' +
512+
'<p>To auto-configure your node run<br /><small><code>npm run configure -- --panel-url '+window.location.protocol+'//{{ config('app.url') }} --token '+data.token+'</code></small></p>',
513+
html: true
514+
})
515+
})
516+
.fail(function () {
517+
swal({
518+
title: 'Error',
519+
text: 'Something went wrong creating your token.',
520+
type: 'error'
521+
});
522+
})
523+
})
524+
539525
$('.cloneElement').on('click', function (event) {
540526
event.preventDefault();
541527
var rnd = randomKey(10);

0 commit comments

Comments
 (0)