Add basic database listing for server
This commit is contained in:
parent
04f56ffe99
commit
17796fb1c4
13 changed files with 255 additions and 23 deletions
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Transformers\Api\Client\DatabaseTransformer;
|
||||
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
|
||||
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\GetDatabasesRequest;
|
||||
|
||||
class DatabaseController extends ClientApiController
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* DatabaseController constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(DatabaseRepositoryInterface $repository)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\GetDatabasesRequest $request
|
||||
* @return array
|
||||
*/
|
||||
public function index(GetDatabasesRequest $request): array
|
||||
{
|
||||
$databases = $this->repository->getDatabasesForServer($request->getModel(Server::class)->id);
|
||||
|
||||
return $this->fractal->collection($databases)
|
||||
->transformWith($this->getTransformer(DatabaseTransformer::class))
|
||||
->toArray();
|
||||
}
|
||||
}
|
20
app/Http/Requests/Api/Client/Servers/GetDatabasesRequest.php
Normal file
20
app/Http/Requests/Api/Client/Servers/GetDatabasesRequest.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Servers;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
|
||||
class GetDatabasesRequest extends ClientApiRequest
|
||||
{
|
||||
/**
|
||||
* Determine if this user has permission to view all of the databases available
|
||||
* to this server.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return $this->user()->can('view-databases', $this->getModel(Server::class));
|
||||
}
|
||||
}
|
|
@ -76,7 +76,7 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor
|
|||
*/
|
||||
public function getDatabasesForServer(int $server): Collection
|
||||
{
|
||||
return $this->getBuilder()->where('server_id', $server)->get($this->getColumns());
|
||||
return $this->getBuilder()->with('host')->where('server_id', $server)->get($this->getColumns());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
69
app/Transformers/Api/Client/DatabaseTransformer.php
Normal file
69
app/Transformers/Api/Client/DatabaseTransformer.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Transformers\Api\Client;
|
||||
|
||||
use Pterodactyl\Models\Database;
|
||||
use League\Fractal\Resource\Item;
|
||||
use Illuminate\Contracts\Encryption\Encrypter;
|
||||
|
||||
class DatabaseTransformer extends BaseClientTransformer
|
||||
{
|
||||
protected $availableIncludes = ['password'];
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Encryption\Encrypter
|
||||
*/
|
||||
private $encrypter;
|
||||
|
||||
/**
|
||||
* Handle dependency injection.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
|
||||
*/
|
||||
public function handle(Encrypter $encrypter)
|
||||
{
|
||||
$this->encrypter = $encrypter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getResourceName(): string
|
||||
{
|
||||
return Database::RESOURCE_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Pterodactyl\Models\Database $model
|
||||
* @return array
|
||||
*/
|
||||
public function transform(Database $model): array
|
||||
{
|
||||
$model->loadMissing('host');
|
||||
|
||||
return [
|
||||
'host' => [
|
||||
'address' => $model->getRelation('host')->host,
|
||||
'port' => $model->getRelation('host')->port,
|
||||
],
|
||||
'name' => $model->database,
|
||||
'username' => $model->username,
|
||||
'connections_from' => $model->remote,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Include the database password in the request.
|
||||
*
|
||||
* @param \Pterodactyl\Models\Database $model
|
||||
* @return \League\Fractal\Resource\Item
|
||||
*/
|
||||
public function includePassword(Database $model): Item
|
||||
{
|
||||
return $this->item($model, function (Database $model) {
|
||||
return [
|
||||
'password' => $this->encrypter->decrypt($model->password),
|
||||
];
|
||||
}, 'database_password');
|
||||
}
|
||||
}
|
|
@ -51,12 +51,10 @@
|
|||
</div>
|
||||
</div>
|
||||
-->
|
||||
<div class="bg-white p-6 rounded border border-grey-light">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fixed pin-r pin-b m-6 max-w-sm" v-show="connectionError">
|
||||
<div class="alert error">
|
||||
There was an error while attempting to connect to the Daemon websocket. Error reported was: "{{connectionError.message}}"
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ServerDatabases"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,7 +1,7 @@
|
|||
export {default as Server} from './Server';
|
||||
export {default as ServerAllocations} from './ServerAllocations';
|
||||
export {default as ConsolePage} from './subpages/Console';
|
||||
export {default as ServerDatabases} from './ServerDatabases';
|
||||
export {default as DatabasesPage} from './subpages/Databases';
|
||||
export {default as FileManagerPage} from './subpages/FileManager';
|
||||
export {default as ServerSchedules} from './ServerSchedules';
|
||||
export {default as ServerSettings} from './ServerSettings';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="bg-white p-6 rounded border border-grey-light">
|
||||
<div class="text-xs font-mono">
|
||||
<div class="rounded-t p-2 bg-black overflow-scroll w-full" style="min-height: 16rem;max-height:64rem;">
|
||||
<div class="mb-2 text-grey-light" ref="terminal" v-if="connected"></div>
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
<template>
|
||||
<div>
|
||||
<div v-if="loading">
|
||||
<div class="spinner spinner-xl blue"></div>
|
||||
</div>
|
||||
<div class="bg-white p-6 rounded border border-grey-light" v-else-if="!databases.length">
|
||||
<div class="flex items-center">
|
||||
<database-icon class="flex-none text-grey-darker"></database-icon>
|
||||
<div class="flex-1 px-4 text-grey-darker">
|
||||
<p>You have no databases.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="bg-white p-6 rounded border border-grey-light mb-6" v-for="database in databases" :key="database.name">
|
||||
<div class="flex items-center text-grey-darker">
|
||||
<database-icon class="flex-none text-green"></database-icon>
|
||||
<div class="flex-1 px-4">
|
||||
<p class="uppercase text-xs text-grey pb-1 select-none">Database Name</p>
|
||||
<p>{{database.name}}</p>
|
||||
</div>
|
||||
<div class="flex-1 px-4">
|
||||
<p class="uppercase text-xs text-grey pb-1 select-none">Username</p>
|
||||
<p>{{database.username}}</p>
|
||||
</div>
|
||||
<div class="flex-1 px-4">
|
||||
<p class="uppercase text-xs text-grey pb-1 select-none">Password</p>
|
||||
<p>
|
||||
<code class="text-sm cursor-pointer" v-on:click="revealPassword(database)">
|
||||
<span class="select-none" v-if="!database.showPassword">
|
||||
<lock-icon class="h-3"/> ••••••
|
||||
</span>
|
||||
<span v-else>{{database.password}}</span>
|
||||
</code>
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex-1 px-4">
|
||||
<p class="uppercase text-xs text-grey pb-1 select-none">Server</p>
|
||||
<p><code class="text-sm">{{database.host.address}}:{{database.host.port}}</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { DatabaseIcon, LockIcon } from 'vue-feather-icons';
|
||||
import map from 'lodash/map';
|
||||
|
||||
export default {
|
||||
name: 'databases-page',
|
||||
components: { DatabaseIcon, LockIcon },
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
loading: true,
|
||||
databases: [],
|
||||
};
|
||||
},
|
||||
|
||||
mounted: function () {
|
||||
this.getDatabases();
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Get all of the databases that exist for this server.
|
||||
*/
|
||||
getDatabases: function () {
|
||||
this.clearFlashes();
|
||||
this.loading = true;
|
||||
|
||||
window.axios.get(this.route('api.client.servers.databases', {
|
||||
server: this.$route.params.id,
|
||||
include: 'password'
|
||||
}))
|
||||
.then(response => {
|
||||
this.databases = map(response.data.data, (object) => {
|
||||
const data = object.attributes;
|
||||
|
||||
data.password = data.relationships.password.attributes.password;
|
||||
data.showPassword = false;
|
||||
delete data.relationships;
|
||||
|
||||
return data;
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
this.error('There was an error encountered while attempting to fetch databases for this server.');
|
||||
console.error(err);
|
||||
})
|
||||
.then(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the password for a given database object.
|
||||
*
|
||||
* @param {Object} database
|
||||
*/
|
||||
revealPassword: function (database) {
|
||||
this.databases.forEach((d) => {
|
||||
d.showPassword = d === database ? d.showPassword : false;
|
||||
});
|
||||
|
||||
database.showPassword = !database.showPassword;
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="bg-white p-6 rounded border border-grey-light">
|
||||
<div class="filemanager-breadcrumbs">
|
||||
/<span class="px-1">home</span><!--
|
||||
-->/<router-link :to="{ name: 'server-files' }" class="px-1">container</router-link><!--
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -14,7 +14,7 @@ import {
|
|||
ServerAllocations,
|
||||
ConsolePage,
|
||||
FileManagerPage,
|
||||
ServerDatabases,
|
||||
DatabasesPage,
|
||||
ServerSchedules,
|
||||
ServerSettings,
|
||||
ServerSubusers
|
||||
|
@ -44,7 +44,7 @@ const routes = [
|
|||
{ name: 'server-files', path: 'files/:path(.*)?', component: FileManagerPage },
|
||||
{ name: 'server-subusers', path: 'subusers', component: ServerSubusers },
|
||||
{ name: 'server-schedules', path: 'schedules', component: ServerSchedules },
|
||||
{ name: 'server-databases', path: 'databases', component: ServerDatabases },
|
||||
{ name: 'server-databases', path: 'databases', component: DatabasesPage },
|
||||
{ name: 'server-allocations', path: 'allocations', component: ServerAllocations },
|
||||
{ name: 'server-settings', path: 'settings', component: ServerSettings },
|
||||
]
|
||||
|
|
|
@ -34,4 +34,8 @@ Route::group(['prefix' => '/servers/{server}', 'middleware' => [AuthenticateClie
|
|||
|
||||
Route::post('/command', 'Servers\CommandController@index')->name('api.client.servers.command');
|
||||
Route::post('/power', 'Servers\PowerController@index')->name('api.client.servers.power');
|
||||
|
||||
Route::group(['prefix' => '/databases'], function () {
|
||||
Route::get('/', 'Servers\DatabaseController@index')->name('api.client.servers.databases');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue