Add basic database listing for server

This commit is contained in:
Dane Everitt 2018-08-21 21:47:01 -07:00
parent 04f56ffe99
commit 17796fb1c4
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
13 changed files with 255 additions and 23 deletions

View file

@ -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();
}
}

View 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));
}
}

View file

@ -76,7 +76,7 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor
*/ */
public function getDatabasesForServer(int $server): Collection 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());
} }
/** /**

View 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');
}
}

View file

@ -51,9 +51,7 @@
</div> </div>
</div> </div>
--> -->
<div class="bg-white p-6 rounded border border-grey-light"> <router-view></router-view>
<router-view></router-view>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,13 +0,0 @@
<template>
</template>
<script>
export default {
name: "ServerDatabases"
}
</script>
<style scoped>
</style>

View file

@ -1,7 +1,7 @@
export {default as Server} from './Server'; export {default as Server} from './Server';
export {default as ServerAllocations} from './ServerAllocations'; export {default as ServerAllocations} from './ServerAllocations';
export {default as ConsolePage} from './subpages/Console'; 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 FileManagerPage} from './subpages/FileManager';
export {default as ServerSchedules} from './ServerSchedules'; export {default as ServerSchedules} from './ServerSchedules';
export {default as ServerSettings} from './ServerSettings'; export {default as ServerSettings} from './ServerSettings';

View file

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="bg-white p-6 rounded border border-grey-light">
<div class="text-xs font-mono"> <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="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> <div class="mb-2 text-grey-light" ref="terminal" v-if="connected"></div>

View file

@ -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"/> &bull;&bull;&bull;&bull;&bull;&bull;
</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>

View file

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="bg-white p-6 rounded border border-grey-light">
<div class="filemanager-breadcrumbs"> <div class="filemanager-breadcrumbs">
/<span class="px-1">home</span><!-- /<span class="px-1">home</span><!--
-->/<router-link :to="{ name: 'server-files' }" class="px-1">container</router-link><!-- -->/<router-link :to="{ name: 'server-files' }" class="px-1">container</router-link><!--

File diff suppressed because one or more lines are too long

View file

@ -14,7 +14,7 @@ import {
ServerAllocations, ServerAllocations,
ConsolePage, ConsolePage,
FileManagerPage, FileManagerPage,
ServerDatabases, DatabasesPage,
ServerSchedules, ServerSchedules,
ServerSettings, ServerSettings,
ServerSubusers ServerSubusers
@ -44,7 +44,7 @@ const routes = [
{ name: 'server-files', path: 'files/:path(.*)?', component: FileManagerPage }, { name: 'server-files', path: 'files/:path(.*)?', component: FileManagerPage },
{ name: 'server-subusers', path: 'subusers', component: ServerSubusers }, { name: 'server-subusers', path: 'subusers', component: ServerSubusers },
{ name: 'server-schedules', path: 'schedules', component: ServerSchedules }, { 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-allocations', path: 'allocations', component: ServerAllocations },
{ name: 'server-settings', path: 'settings', component: ServerSettings }, { name: 'server-settings', path: 'settings', component: ServerSettings },
] ]

View file

@ -34,4 +34,8 @@ Route::group(['prefix' => '/servers/{server}', 'middleware' => [AuthenticateClie
Route::post('/command', 'Servers\CommandController@index')->name('api.client.servers.command'); Route::post('/command', 'Servers\CommandController@index')->name('api.client.servers.command');
Route::post('/power', 'Servers\PowerController@index')->name('api.client.servers.power'); 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');
});
}); });