Skip to content

Commit 1d97b0b

Browse files
committed
Add support for modification of server startup variables and command
1 parent 62313ee commit 1d97b0b

File tree

5 files changed

+199
-6
lines changed

5 files changed

+199
-6
lines changed

app/Http/Controllers/Admin/ServersController.php

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public function getView(Request $request, $id)
5252
'users.email as a_ownerEmail',
5353
'locations.long as a_locationName',
5454
'services.name as a_serviceName',
55+
'services.executable as a_serviceExecutable',
5556
'service_options.name as a_servceOptionName'
5657
)->join('nodes', 'servers.node', '=', 'nodes.id')
5758
->join('users', 'servers.owner', '=', 'users.id')
@@ -68,7 +69,12 @@ public function getView(Request $request, $id)
6869
return view('admin.servers.view', [
6970
'server' => $server,
7071
'assigned' => Models\Allocation::select('id', 'ip', 'port')->where('assigned_to', $id)->orderBy('ip', 'asc')->orderBy('port', 'asc')->get(),
71-
'unassigned' => Models\Allocation::select('id', 'ip', 'port')->where('node', $server->node)->whereNull('assigned_to')->orderBy('ip', 'asc')->orderBy('port', 'asc')->get()
72+
'unassigned' => Models\Allocation::select('id', 'ip', 'port')->where('node', $server->node)->whereNull('assigned_to')->orderBy('ip', 'asc')->orderBy('port', 'asc')->get(),
73+
'startup' => Models\ServiceVariables::select('service_variables.*', 'server_variables.variable_value as a_serverValue')
74+
->join('server_variables', 'server_variables.variable_id', '=', 'service_variables.id')
75+
->where('service_variables.option_id', $server->option)
76+
->where('server_variables.server_id', $server->id)
77+
->get()
7278
]);
7379
}
7480

@@ -325,4 +331,25 @@ public function postToggleInstall(Request $request, $id)
325331
}
326332
}
327333

334+
public function postUpdateServerStartup(Request $request, $id)
335+
{
336+
try {
337+
$server = new ServerRepository;
338+
$server->updateStartup($id, $request->except([
339+
'_token'
340+
]));
341+
Alert::success('Server startup variables were successfully updated.')->flash();
342+
} catch (\Pterodactyl\Exceptions\DisplayException $e) {
343+
Alert::danger($e->getMessage())->flash();
344+
} catch(\Exception $e) {
345+
Log::error($e);
346+
Alert::danger('An unhandled exception occured while attemping to update startup variables for this server. Please try again.')->flash();
347+
} finally {
348+
return redirect()->route('admin.servers.view', [
349+
'id' => $id,
350+
'tab' => 'tab_startup'
351+
])->withInput();
352+
}
353+
}
354+
328355
}

app/Http/Routes/AdminRoutes.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@ public function map(Router $router) {
115115
'uses' => 'Admin\ServersController@postUpdateServerDetails'
116116
]);
117117

118+
// Change Server Details
119+
$router->post('/view/{id}/startup', [
120+
'as' => 'admin.servers.post.startup',
121+
'uses' => 'Admin\ServersController@postUpdateServerStartup'
122+
]);
123+
118124
// Rebuild Server
119125
$router->post('/view/{id}/rebuild', [
120126
'uses' => 'Admin\ServersController@postUpdateServerToggleBuild'

app/Repositories/ServerRepository.php

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,111 @@ public function changeBuild($id, array $data)
522522

523523
}
524524

525+
public function updateStartup($id, array $data)
526+
{
527+
528+
$server = Models\Server::findOrFail($id);
529+
530+
DB::beginTransaction();
531+
532+
// Check the startup
533+
if (isset($data['startup'])) {
534+
$server->startup = $data['startup'];
535+
$server->save();
536+
}
537+
538+
// Check those Variables
539+
$variables = Models\ServiceVariables::select('service_variables.*', 'server_variables.variable_value as a_currentValue')
540+
->join('server_variables', 'server_variables.variable_id', '=', 'service_variables.id')
541+
->where('option_id', $server->option)->get();
542+
543+
$variableList = [];
544+
if ($variables) {
545+
foreach($variables as &$variable) {
546+
// Move on if the new data wasn't even sent
547+
if (!isset($data[$variable->env_variable])) {
548+
$variableList = array_merge($variableList, [[
549+
'id' => $variable->id,
550+
'env' => $variable->env_variable,
551+
'val' => $variable->a_currentValue
552+
]]);
553+
continue;
554+
}
555+
556+
// Update Empty but skip validation
557+
if (empty($data[$variable->env_variable])) {
558+
$variableList = array_merge($variableList, [[
559+
'id' => $variable->id,
560+
'env' => $variable->env_variable,
561+
'val' => null
562+
]]);
563+
continue;
564+
}
565+
566+
// Is the variable required?
567+
// @TODO: is this even logical to perform this check?
568+
if (isset($data[$variable->env_variable]) && empty($data[$variable->env_variable])) {
569+
if ($variable->required === 1) {
570+
throw new DisplayException('A required service option variable field (' . $variable->env_variable . ') was included in this request but was left blank.');
571+
}
572+
}
573+
574+
// Check aganist Regex Pattern
575+
if (!is_null($variable->regex) && !preg_match($variable->regex, $data[$variable->env_variable])) {
576+
throw new DisplayException('Failed to validate service option variable field (' . $variable->env_variable . ') aganist regex (' . $variable->regex . ').');
577+
}
578+
579+
$variableList = array_merge($variableList, [[
580+
'id' => $variable->id,
581+
'env' => $variable->env_variable,
582+
'val' => $data[$variable->env_variable]
583+
]]);
584+
}
585+
}
586+
587+
// Add Variables
588+
$environmentVariables = [];
589+
$environmentVariables = array_merge($environmentVariables, [
590+
'STARTUP' => $server->startup
591+
]);
592+
foreach($variableList as $item) {
593+
$environmentVariables = array_merge($environmentVariables, [
594+
$item['env'] => $item['val']
595+
]);
596+
$var = Models\ServerVariables::where('server_id', $server->id)->where('variable_id', $item['id'])->update([
597+
'variable_value' => $item['val']
598+
]);
599+
}
600+
601+
try {
602+
603+
$node = Models\Node::getByID($server->node);
604+
$client = Models\Node::guzzleRequest($server->node);
605+
606+
$client->request('PATCH', '/server', [
607+
'headers' => [
608+
'X-Access-Server' => $server->uuid,
609+
'X-Access-Token' => $node->daemonSecret
610+
],
611+
'json' => [
612+
'build' => [
613+
'env|overwrite' => $environmentVariables
614+
]
615+
]
616+
]);
617+
618+
DB::commit();
619+
return true;
620+
} catch (\GuzzleHttp\Exception\TransferException $ex) {
621+
DB::rollBack();
622+
throw new DisplayException('An error occured while attempting to update the server configuration: ' . $ex->getMessage());
623+
} catch (\Exception $e) {
624+
DB::rollBack();
625+
throw $e;
626+
}
627+
628+
}
629+
525630
public function deleteServer($id, $force)
526631
{
527632
$server = Models\Server::findOrFail($id);

public/js/admin.min.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ function randomKey(length) {
88

99
return text;
1010
}
11+
function escapeRegExp(str) {
12+
return str.replace(/^\/|\/$/g, '');
13+
}
1114
$(document).ready(function () {
1215
$.urlParam=function(name){var results=new RegExp("[\\?&]"+name+"=([^&#]*)").exec(decodeURIComponent(window.location.href));if(results==null){return null}else{return results[1]||0}};function getPageName(url){var index=url.lastIndexOf("/")+1;var filenameWithExtension=url.substr(index);var filename=filenameWithExtension.split(".")[0];return filename}
1316
function centerModal(element) {

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

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,52 @@
223223
</div>
224224
</div>
225225
<div class="tab-pane" id="tab_startup">
226-
<div class="panel panel-default">
227-
<div class="panel-heading"></div>
228-
<div class="panel-body">
229-
Startup
226+
<form action="{{ route('admin.servers.post.startup', $server->id) }}" method="POST">
227+
<div class="panel panel-default">
228+
<div class="panel-heading"></div>
229+
<div class="panel-body">
230+
<div class="row">
231+
<div class="col-md-12">
232+
<div class="alert alert-info">Changing any of the values below will require a restart for them to take effect.</div>
233+
<label class="control-label">Server Startup Command</label>
234+
<div class="input-group">
235+
<span class="input-group-addon">{{ $server->a_serviceExecutable }}</span>
236+
<input type="text" class="form-control" name="startup" value="{{ old('startup', $server->startup) }}" />
237+
</div>
238+
<p class="text-muted"><small>The following data replacers are avaliable for the startup command: <code>@{{SERVER_MEMORY}}</code>, <code>@{{SERVER_IP}}</code>, and <code>@{{SERVER_PORT}}</code>. They will be replaced with the allocated memory, server ip, and server port respectively.</small></p>
239+
</div>
240+
</div>
241+
</div>
242+
<div class="panel-heading" style="border-top: 1px solid #ddd;"></div>
243+
<div class="panel-body">
244+
<div class="row">
245+
@foreach($startup as $item)
246+
<div class="form-group col-md-6">
247+
<label class="control-label">
248+
@if($item->required === 1)<span class="label label-primary">Required</span> @endif
249+
@if($item->user_viewable === 0)<span data-toggle="tooltip" data-placement="top" title="Not Visible to Users" class="label label-danger"><i class="fa fa-eye-slash"></i></span> @endif
250+
@if($item->user_editable === 0)<span data-toggle="tooltip" data-placement="top" title="Not Editable by Users" class="label label-danger"><i class="fa fa-edit"></i></span> @endif
251+
{{ $item->name }}
252+
</label>
253+
<div>
254+
<input type="text" name="{{ $item->env_variable }}" class="form-control" value="{{ old($item->env_variable, $item->a_serverValue) }}" data-action="matchRegex" data-regex="{{ $item->regex }}" />
255+
</div>
256+
<p class="text-muted"><small>{{ $item->description }}<br />Regex: <code>{{ $item->regex }}</code><br />Access as: <code>&#123;&#123;{{$item->env_variable}}&#125;&#125;</code></small></p>
257+
</div>
258+
@endforeach
259+
</div>
260+
</div>
261+
<div class="panel-heading" style="border-top: 1px solid #ddd;"></div>
262+
<div class="panel-body">
263+
<div class="row">
264+
<div class="col-md-12">
265+
{!! csrf_field() !!}
266+
<input type="submit" class="btn btn-primary btn-sm" value="Update Startup Arguments" />
267+
</div>
268+
</div>
269+
</div>
230270
</div>
231-
</div>
271+
</form>
232272
</div>
233273
<div class="tab-pane" id="tab_manage">
234274
<div class="panel panel-default">
@@ -315,11 +355,23 @@
315355
</div>
316356
<script>
317357
$(document).ready(function () {
358+
$('[data-toggle="tooltip"]').tooltip();
318359
$('#sidebar_links').find("a[href='/admin/servers']").addClass('active');
319360
$('input[name="default"]').on('change', function (event) {
320361
$('select[name="remove_additional[]"]').find('option:disabled').prop('disabled', false);
321362
$('select[name="remove_additional[]"]').find('option[value="' + $(this).val() + '"]').prop('disabled', true).prop('selected', false);
322363
});
364+
$('[data-action="matchRegex"]').keyup(function (event) {
365+
if (!$(this).data('regex')) return;
366+
var input = $(this).val();
367+
var regex = new RegExp(escapeRegExp($(this).data('regex')));
368+
console.log(regex);
369+
if (!regex.test(input)) {
370+
$(this).parent().parent().removeClass('has-success').addClass('has-error');
371+
} else {
372+
$(this).parent().parent().removeClass('has-error').addClass('has-success');
373+
}
374+
});
323375
$('form[data-attr="deleteServer"]').submit(function (event) {
324376
event.preventDefault();
325377
swal({

0 commit comments

Comments
 (0)