diff --git a/app/Http/Controllers/API/User/InfoController.php b/app/Http/Controllers/API/User/InfoController.php new file mode 100644 index 000000000..2e2e3a03b --- /dev/null +++ b/app/Http/Controllers/API/User/InfoController.php @@ -0,0 +1,58 @@ + + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +namespace Pterodactyl\Http\Controllers\API\User; + +use Auth; +use Dingo; +use Pterodactyl\Models; +use Illuminate\Http\Request; + +use Pterodactyl\Http\Controllers\API\BaseController; + +class InfoController extends BaseController +{ + public function me(Request $request) + { + $servers = Models\Server::getUserServers(); + $response = []; + + foreach($servers as &$server) { + $response = array_merge($response, [[ + 'id' => $server->uuidShort, + 'uuid' => $server->uuid, + 'name' => $server->name, + 'node' => $server->nodeName, + 'ip' => [ + 'set' => $server->ip, + 'alias' => $server->ip_alias + ], + 'port' => $server->port, + 'service' => $server->a_serviceName, + 'option' => $server->a_serviceOptionName + ]]); + } + + return $response; + } +} diff --git a/app/Http/Controllers/API/User/PowerController.php b/app/Http/Controllers/API/User/PowerController.php new file mode 100644 index 000000000..c92637b66 --- /dev/null +++ b/app/Http/Controllers/API/User/PowerController.php @@ -0,0 +1,39 @@ + + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +namespace Pterodactyl\Http\Controllers\API\User; + +use Pterodactyl\Models; +use Illuminate\Http\Request; + +class PowerController extends BaseController +{ + public function __constructor() + { + } + + public function pass(Request $request, $uuid) + { + //$server = Models\Server::where('id', $id)->where(); + } +} diff --git a/app/Http/Controllers/Base/APIController.php b/app/Http/Controllers/Base/APIController.php new file mode 100644 index 000000000..42109857a --- /dev/null +++ b/app/Http/Controllers/Base/APIController.php @@ -0,0 +1,60 @@ + + * Some Modifications (c) 2015 Dylan Seidt + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +namespace Pterodactyl\Http\Controllers\Base; + +use Alert; + +use Pterodactyl\Models; + +use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Http\Controllers\Controller; + +use Illuminate\Http\Request; + +class APIController extends Controller +{ + public function index(Request $request) + { + $keys = Models\APIKey::where('user', $request->user()->id)->get(); + foreach($keys as &$key) { + $key->permissions = Models\APIPermission::where('key_id', $key->id)->get(); + } + + return view('base.api.index', [ + 'keys' => $keys + ]); + + } + + public function new(Request $request) + { + return view('base.api.new'); + } + + public function save(Request $request) + { + + } +} diff --git a/app/Http/Middleware/APISecretToken.php b/app/Http/Middleware/APISecretToken.php index a78728b34..ef150f1f2 100755 --- a/app/Http/Middleware/APISecretToken.php +++ b/app/Http/Middleware/APISecretToken.php @@ -23,12 +23,15 @@ */ namespace Pterodactyl\Http\Middleware; +use Auth; use Crypt; +use Config; use IPTools\IP; use IPTools\Range; use Pterodactyl\Models\APIKey; use Pterodactyl\Models\APIPermission; +use Pterodactyl\Models\User; use Pterodactyl\Services\APILogService; use Illuminate\Http\Request; @@ -51,7 +54,7 @@ class APISecretToken extends Authorization public function __construct() { - // + Config::set('session.driver', 'array'); } public function getAuthorizationMethod() @@ -90,14 +93,11 @@ class APISecretToken extends Authorization } } - foreach(APIPermission::where('key_id', $key->id)->get() as &$row) { - if ($row->permission === '*' || $row->permission === $request->route()->getName()) { - $this->permissionAllowed = true; - continue; - } - } - - if (!$this->permissionAllowed) { + $permission = APIPermission::where('key_id', $key->id) + ->where('permission', $request->route()->getName()) + ->orWhere('permission', '*') + ->first(); + if (!$permission) { APILogService::log($request, 'You do not have permission to access this resource.'); throw new AccessDeniedHttpException('You do not have permission to access this resource.'); } @@ -118,7 +118,7 @@ class APISecretToken extends Authorization // Log the Route Access APILogService::log($request, null, true); - return true; + return Auth::loginUsingId($key->user); } diff --git a/app/Http/Routes/APIRoutes.php b/app/Http/Routes/APIRoutes.php index 15fcab78e..3213a641a 100755 --- a/app/Http/Routes/APIRoutes.php +++ b/app/Http/Routes/APIRoutes.php @@ -32,33 +32,40 @@ class APIRoutes public function map(Router $router) { $api = app('Dingo\Api\Routing\Router'); - $api->version('v1', ['middleware' => 'api.auth'], function ($api) { + $api->version('v1', ['prefix' => 'api/me', 'middleware' => 'api.auth'], function ($api) { + $api->get('/', [ + 'as' => 'api.user', + 'uses' => 'Pterodactyl\Http\Controllers\API\User\InfoController@me' + ]); + }); + + $api->version('v1', ['prefix' => 'api', 'middleware' => 'api.auth'], function ($api) { /** * User Routes */ $api->get('users', [ - 'as' => 'api.users.list', + 'as' => 'api.admin.users.list', 'uses' => 'Pterodactyl\Http\Controllers\API\UserController@list' ]); $api->post('users', [ - 'as' => 'api.users.create', + 'as' => 'api.admin.users.create', 'uses' => 'Pterodactyl\Http\Controllers\API\UserController@create' ]); $api->get('users/{id}', [ - 'as' => 'api.users.view', + 'as' => 'api.admin.users.view', 'uses' => 'Pterodactyl\Http\Controllers\API\UserController@view' ]); $api->patch('users/{id}', [ - 'as' => 'api.users.update', + 'as' => 'api.admin.users.update', 'uses' => 'Pterodactyl\Http\Controllers\API\UserController@update' ]); $api->delete('users/{id}', [ - 'as' => 'api.users.delete', + 'as' => 'api.admin.users.delete', 'uses' => 'Pterodactyl\Http\Controllers\API\UserController@delete' ]); @@ -66,42 +73,42 @@ class APIRoutes * Server Routes */ $api->get('servers', [ - 'as' => 'api.servers.list', + 'as' => 'api.admin.servers.list', 'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@list' ]); $api->post('servers', [ - 'as' => 'api.servers.create', + 'as' => 'api.admin.servers.create', 'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@create' ]); $api->get('servers/{id}', [ - 'as' => 'api.servers.view', + 'as' => 'api.admin.servers.view', 'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@view' ]); $api->patch('servers/{id}/config', [ - 'as' => 'api.servers.config', + 'as' => 'api.admin.servers.config', 'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@config' ]); $api->patch('servers/{id}/build', [ - 'as' => 'api.servers.build', + 'as' => 'api.admin.servers.build', 'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@build' ]); $api->post('servers/{id}/suspend', [ - 'as' => 'api.servers.suspend', + 'as' => 'api.admin.servers.suspend', 'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@suspend' ]); $api->post('servers/{id}/unsuspend', [ - 'as' => 'api.servers.unsuspend', + 'as' => 'api.admin.servers.unsuspend', 'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@unsuspend' ]); $api->delete('servers/{id}/{force?}', [ - 'as' => 'api.servers.delete', + 'as' => 'api.admin.servers.delete', 'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@delete' ]); @@ -109,32 +116,32 @@ class APIRoutes * Node Routes */ $api->get('nodes', [ - 'as' => 'api.nodes.list', + 'as' => 'api.admin.nodes.list', 'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@list' ]); $api->post('nodes', [ - 'as' => 'api.nodes.create', + 'as' => 'api.admin.nodes.create', 'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@create' ]); $api->get('nodes/allocations', [ - 'as' => 'api.nodes.allocations', + 'as' => 'api.admin.nodes.allocations', 'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@allocations' ]); $api->get('nodes/{id}', [ - 'as' => 'api.nodes.view', + 'as' => 'api.admin.nodes.view', 'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@view' ]); $api->get('nodes/{id}/config', [ - 'as' => 'api.nodes.view', + 'as' => 'api.admin.nodes.view', 'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@config' ]); $api->delete('nodes/{id}', [ - 'as' => 'api.nodes.delete', + 'as' => 'api.admin.nodes.delete', 'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@delete' ]); @@ -142,7 +149,7 @@ class APIRoutes * Location Routes */ $api->get('locations', [ - 'as' => 'api.locations.list', + 'as' => 'api.admin.locations.list', 'uses' => 'Pterodactyl\Http\Controllers\API\LocationController@list' ]); @@ -150,12 +157,12 @@ class APIRoutes * Service Routes */ $api->get('services', [ - 'as' => 'api.services.list', + 'as' => 'api.admin.services.list', 'uses' => 'Pterodactyl\Http\Controllers\API\ServiceController@list' ]); $api->get('services/{id}', [ - 'as' => 'api.services.view', + 'as' => 'api.admin.services.view', 'uses' => 'Pterodactyl\Http\Controllers\API\ServiceController@view' ]); diff --git a/app/Http/Routes/BaseRoutes.php b/app/Http/Routes/BaseRoutes.php index 510bd7b18..60ac32a8a 100644 --- a/app/Http/Routes/BaseRoutes.php +++ b/app/Http/Routes/BaseRoutes.php @@ -69,6 +69,27 @@ class BaseRoutes { ]); }); + // API Management Routes + $router->group([ + 'prefix' => 'account/api', + 'middleware' => [ + 'auth', + 'csrf' + ] + ], function () use ($router) { + $router->get('/', [ + 'as' => 'account.api', + 'uses' => 'Base\APIController@index' + ]); + $router->get('/new', [ + 'as' => 'account.api.new', + 'uses' => 'Base\APIController@new' + ]); + $router->post('/new', [ + 'uses' => 'Base\APIController@save' + ]); + }); + // TOTP Routes $router->group([ 'prefix' => 'account/security', diff --git a/app/Http/Routes/ServerRoutes.php b/app/Http/Routes/ServerRoutes.php index 9624a0d4e..5916287e2 100644 --- a/app/Http/Routes/ServerRoutes.php +++ b/app/Http/Routes/ServerRoutes.php @@ -28,6 +28,7 @@ use Illuminate\Routing\Router; class ServerRoutes { public function map(Router $router) { + $router->group([ 'prefix' => 'server/{server}', 'middleware' => [ @@ -36,6 +37,7 @@ class ServerRoutes { 'csrf' ] ], function ($server) use ($router) { + // Index View for Server $router->get('/', [ 'as' => 'server.index', diff --git a/app/Models/Server.php b/app/Models/Server.php index e0665ee13..af160da7b 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -24,7 +24,6 @@ namespace Pterodactyl\Models; use Auth; - use Pterodactyl\Models\Subuser; use Illuminate\Database\Eloquent\Model; @@ -91,7 +90,11 @@ class Server extends Model */ public function __construct() { - self::$user = Auth::user(); + if (!is_null(Auth::user())) { + self::$user = Auth::user(); + } else { + throw new \Exception('Auth::user and Dingo::user cannot both be null.'); + } } /** @@ -133,9 +136,13 @@ class Server extends Model 'locations.short as a_locationShort', 'allocations.ip', 'allocations.ip_alias', - 'allocations.port' + 'allocations.port', + 'services.name as a_serviceName', + 'service_options.name as a_serviceOptionName' )->join('nodes', 'servers.node', '=', 'nodes.id') ->join('locations', 'nodes.location', '=', 'locations.id') + ->join('services', 'servers.service', '=', 'services.id') + ->join('service_options', 'servers.option', '=', 'service_options.id') ->join('allocations', 'servers.allocation', '=', 'allocations.id'); if (self::$user->root_admin !== 1) { diff --git a/app/Models/Subuser.php b/app/Models/Subuser.php index 5c22adfb7..1243a3ddd 100644 --- a/app/Models/Subuser.php +++ b/app/Models/Subuser.php @@ -70,7 +70,11 @@ class Subuser extends Model */ public function __construct() { - self::$user = Auth::user(); + if (!is_null(Auth::user())) { + self::$user = Auth::user(); + } else { + throw new \Exception('Auth::user and Dingo::user cannot both be null.'); + } } /** diff --git a/app/Repositories/UserRepository.php b/app/Repositories/UserRepository.php index f90bab484..01cad4269 100644 --- a/app/Repositories/UserRepository.php +++ b/app/Repositories/UserRepository.php @@ -162,7 +162,8 @@ class UserRepository throw new DisplayException('Cannot delete a user with active servers attached to thier account.'); } - if(Auth::user()->id === $id) { + // @TODO: this should probably be checked outside of this method because we won't always have Auth::user() + if(!is_null(Auth::user()) && Auth::user()->id === $id) { throw new DisplayException('Cannot delete your own account.'); } diff --git a/config/app.php b/config/app.php index b28fb0a69..6dea77b4f 100644 --- a/config/app.php +++ b/config/app.php @@ -188,6 +188,8 @@ return [ 'Crypt' => Illuminate\Support\Facades\Crypt::class, 'DB' => Illuminate\Support\Facades\DB::class, 'Debugbar' => Barryvdh\Debugbar\Facade::class, + 'Dingo' => Dingo\Api\Facade\API::class, + 'DingoRoute'=> Dingo\Api\Facade\Route::class, 'Eloquent' => Illuminate\Database\Eloquent\Model::class, 'Event' => Illuminate\Support\Facades\Event::class, 'File' => Illuminate\Support\Facades\File::class, diff --git a/config/session.php b/config/session.php index f1b004214..59ad9182f 100644 --- a/config/session.php +++ b/config/session.php @@ -29,9 +29,9 @@ return [ | */ - 'lifetime' => 120, + 'lifetime' => 30, - 'expire_on_close' => false, + 'expire_on_close' => true, /* |-------------------------------------------------------------------------- diff --git a/database/migrations/2016_10_14_164802_update_api_keys.php b/database/migrations/2016_10_14_164802_update_api_keys.php new file mode 100644 index 000000000..80d7f9501 --- /dev/null +++ b/database/migrations/2016_10_14_164802_update_api_keys.php @@ -0,0 +1,36 @@ +unsignedInteger('user')->after('id'); + $table->text('memo')->after('allowed_ips')->nullable(); + $table->timestamp('expires_at')->after('memo')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('api_keys', function (Blueprint $table) { + $table->dropColumn('user'); + $table->dropColumn('memo'); + $table->dropColumn('expires_at'); + }); + } +} diff --git a/public/themes/default/css/pterodactyl.css b/public/themes/default/css/pterodactyl.css index d3f5d7241..86cd27ca3 100755 --- a/public/themes/default/css/pterodactyl.css +++ b/public/themes/default/css/pterodactyl.css @@ -263,3 +263,7 @@ li.btn.btn-default.pill:active,li.btn.btn-default.pill:focus,li.btn.btn-default. .left-icon + td { border-left: 0px !important; } + +.fuelux .wizard .step-content > .alert { + margin-bottom: 0 !important; +} diff --git a/resources/views/base/api/index.blade.php b/resources/views/base/api/index.blade.php new file mode 100644 index 000000000..75891d158 --- /dev/null +++ b/resources/views/base/api/index.blade.php @@ -0,0 +1,66 @@ +{{-- Copyright (c) 2015 - 2016 Dane Everitt --}} + +{{-- Permission is hereby granted, free of charge, to any person obtaining a copy --}} +{{-- of this software and associated documentation files (the "Software"), to deal --}} +{{-- in the Software without restriction, including without limitation the rights --}} +{{-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell --}} +{{-- copies of the Software, and to permit persons to whom the Software is --}} +{{-- furnished to do so, subject to the following conditions: --}} + +{{-- The above copyright notice and this permission notice shall be included in all --}} +{{-- copies or substantial portions of the Software. --}} + +{{-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --}} +{{-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, --}} +{{-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE --}} +{{-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER --}} +{{-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, --}} +{{-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE --}} +{{-- SOFTWARE. --}} +@extends('layouts.master') + +@section('title', 'API Access') + +@section('sidebar-server') +@endsection + +@section('content') +
+ + + + + + + + + + + + @foreach ($keys as $key) + + + + + + + + @endforeach + +
Public KeyMemoCreatedExpires
{{ $key->public }}{{ $key->memo }}{{ (new Carbon($key->created_at))->toDayDateTimeString() }} + @if(is_null($key->expires_at)) + Never + @else + {{ (new Carbon($key->expires_at))->toDayDateTimeString() }} + @endif +
+ +
+ +@endsection diff --git a/resources/views/base/api/new.blade.php b/resources/views/base/api/new.blade.php new file mode 100644 index 000000000..e1049a2bb --- /dev/null +++ b/resources/views/base/api/new.blade.php @@ -0,0 +1,238 @@ +{{-- Copyright (c) 2015 - 2016 Dane Everitt --}} + +{{-- Permission is hereby granted, free of charge, to any person obtaining a copy --}} +{{-- of this software and associated documentation files (the "Software"), to deal --}} +{{-- in the Software without restriction, including without limitation the rights --}} +{{-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell --}} +{{-- copies of the Software, and to permit persons to whom the Software is --}} +{{-- furnished to do so, subject to the following conditions: --}} + +{{-- The above copyright notice and this permission notice shall be included in all --}} +{{-- copies or substantial portions of the Software. --}} + +{{-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --}} +{{-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, --}} +{{-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE --}} +{{-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER --}} +{{-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, --}} +{{-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE --}} +{{-- SOFTWARE. --}} +@extends('layouts.master') + +@section('title', 'API Access') + +@section('sidebar-server') +@endsection + +@section('content') +
+
+
+
    +
  • + 1Permissions + +
  • +
  • + 2Admin + +
  • +
  • + 3Security + +
  • +
  • + 4Finish + +
  • +
+
+
+ + +
+
+ {{--
--}} +
+
+
+
+
+
+
+
+
+
+

User Management


+
+
+
+
+
+
+
+
+
+
+
+
+

Server Management


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

Node Management


+
+
+
+
+
+
+
+
+
+
+
+
+

Service Management


+
+
+
+
+

Location Management


+
+
+
+
+
+
+
+ +
+ +

Enter a line delimitated list of IPs that are allowed to access the API using this key. CIDR notation is allowed. Leave blank to allow any IP.

+
+
+
+
+ Whoa +
+
+
+
+ + {{-- --}} +
+ +@endsection diff --git a/resources/views/layouts/master.blade.php b/resources/views/layouts/master.blade.php index 919cd5191..522155139 100644 --- a/resources/views/layouts/master.blade.php +++ b/resources/views/layouts/master.blade.php @@ -184,6 +184,7 @@ @@ -237,6 +238,7 @@ {{ trans('pagination.sidebar.account_controls') }} {{ trans('pagination.sidebar.account_settings') }} {{ trans('pagination.sidebar.account_security') }} + API Access {{ trans('pagination.sidebar.servers') }} @section('sidebar-server')