mirror of
https://github.com/bitinflow/expose.git
synced 2026-03-13 13:35:54 +00:00
Dashboard UI updates, allow multiple expose servers, exclude subdomains
This commit is contained in:
42
app/Commands/ServerAwareCommand.php
Normal file
42
app/Commands/ServerAwareCommand.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Commands;
|
||||||
|
|
||||||
|
use App\Logger\CliRequestLogger;
|
||||||
|
use Illuminate\Console\Parser;
|
||||||
|
use LaravelZero\Framework\Commands\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||||
|
|
||||||
|
abstract class ServerAwareCommand extends Command
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$inheritedSignature = '{--server=default} {--server-host=} {--server-port=}';
|
||||||
|
|
||||||
|
$this->getDefinition()->addOptions(Parser::parse($inheritedSignature)[2]);
|
||||||
|
|
||||||
|
$this->configureConnectionLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configureConnectionLogger()
|
||||||
|
{
|
||||||
|
app()->bind(CliRequestLogger::class, function () {
|
||||||
|
return new CliRequestLogger(new ConsoleOutput());
|
||||||
|
});
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getServerHost()
|
||||||
|
{
|
||||||
|
return $this->option('server-host') ?? config('expose.servers.'.$this->option('server').'.host', 'localhost');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getServerPort()
|
||||||
|
{
|
||||||
|
return $this->option('server-port') ?? config('expose.servers.'.$this->option('server').'.port', 8080);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,31 +4,17 @@ namespace App\Commands;
|
|||||||
|
|
||||||
use App\Client\Factory;
|
use App\Client\Factory;
|
||||||
use App\Logger\CliRequestLogger;
|
use App\Logger\CliRequestLogger;
|
||||||
use LaravelZero\Framework\Commands\Command;
|
|
||||||
use React\EventLoop\LoopInterface;
|
use React\EventLoop\LoopInterface;
|
||||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||||
|
|
||||||
class ShareCommand extends Command
|
class ShareCommand extends ServerAwareCommand
|
||||||
{
|
{
|
||||||
protected $signature = 'share {host} {--subdomain=} {--auth=} {--server-host=} {--server-port=} {--dns=}';
|
protected $signature = 'share {host} {--subdomain=} {--auth=} {--dns=}';
|
||||||
|
|
||||||
protected $description = 'Share a local url with a remote expose server';
|
protected $description = 'Share a local url with a remote expose server';
|
||||||
|
|
||||||
protected function configureConnectionLogger()
|
|
||||||
{
|
|
||||||
app()->bind(CliRequestLogger::class, function () {
|
|
||||||
return new CliRequestLogger(new ConsoleOutput());
|
|
||||||
});
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->configureConnectionLogger();
|
|
||||||
|
|
||||||
$serverHost = $this->option('server-host') ?? config('expose.host', 'localhost');
|
|
||||||
$serverPort = $this->option('server-port') ?? config('expose.port', 8080);
|
|
||||||
$auth = $this->option('auth') ?? config('expose.auth_token', '');
|
$auth = $this->option('auth') ?? config('expose.auth_token', '');
|
||||||
|
|
||||||
if (strstr($this->argument('host'), 'host.docker.internal')) {
|
if (strstr($this->argument('host'), 'host.docker.internal')) {
|
||||||
@@ -41,8 +27,8 @@ class ShareCommand extends Command
|
|||||||
|
|
||||||
(new Factory())
|
(new Factory())
|
||||||
->setLoop(app(LoopInterface::class))
|
->setLoop(app(LoopInterface::class))
|
||||||
->setHost($serverHost)
|
->setHost($this->getServerHost())
|
||||||
->setPort($serverPort)
|
->setPort($this->getServerPort())
|
||||||
->setAuth($auth)
|
->setAuth($auth)
|
||||||
->createClient()
|
->createClient()
|
||||||
->share($this->argument('host'), explode(',', $this->option('subdomain')))
|
->share($this->argument('host'), explode(',', $this->option('subdomain')))
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace App\Commands;
|
|||||||
|
|
||||||
class ShareCurrentWorkingDirectoryCommand extends ShareCommand
|
class ShareCurrentWorkingDirectoryCommand extends ShareCommand
|
||||||
{
|
{
|
||||||
protected $signature = 'share-cwd {host?} {--subdomain=} {--auth=} {--server-host=} {--server-port=}';
|
protected $signature = 'share-cwd {host?} {--subdomain=} {--auth=}';
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,37 +8,24 @@ use LaravelZero\Framework\Commands\Command;
|
|||||||
use React\EventLoop\LoopInterface;
|
use React\EventLoop\LoopInterface;
|
||||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||||
|
|
||||||
class ShareFilesCommand extends Command
|
class ShareFilesCommand extends ServerAwareCommand
|
||||||
{
|
{
|
||||||
protected $signature = 'share-files {folder=.} {--name=} {--subdomain=} {--auth=} {--server-host=} {--server-port=}';
|
protected $signature = 'share-files {folder=.} {--name=} {--subdomain=} {--auth=}';
|
||||||
|
|
||||||
protected $description = 'Share a local folder with a remote expose server';
|
protected $description = 'Share a local folder with a remote expose server';
|
||||||
|
|
||||||
protected function configureConnectionLogger()
|
|
||||||
{
|
|
||||||
app()->bind(CliRequestLogger::class, function () {
|
|
||||||
return new CliRequestLogger(new ConsoleOutput());
|
|
||||||
});
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
if (! is_dir($this->argument('folder'))) {
|
if (! is_dir($this->argument('folder'))) {
|
||||||
throw new \InvalidArgumentException('The folder '.$this->argument('folder').' does not exist.');
|
throw new \InvalidArgumentException('The folder '.$this->argument('folder').' does not exist.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->configureConnectionLogger();
|
|
||||||
|
|
||||||
$serverHost = $this->option('server-host') ?? config('expose.host', 'localhost');
|
|
||||||
$serverPort = $this->option('server-port') ?? config('expose.port', 8080);
|
|
||||||
$auth = $this->option('auth') ?? config('expose.auth_token', '');
|
$auth = $this->option('auth') ?? config('expose.auth_token', '');
|
||||||
|
|
||||||
(new Factory())
|
(new Factory())
|
||||||
->setLoop(app(LoopInterface::class))
|
->setLoop(app(LoopInterface::class))
|
||||||
->setHost($serverHost)
|
->setHost($this->getServerHost())
|
||||||
->setPort($serverPort)
|
->setPort($this->getServerPort())
|
||||||
->setAuth($auth)
|
->setAuth($auth)
|
||||||
->createClient()
|
->createClient()
|
||||||
->shareFolder(
|
->shareFolder(
|
||||||
|
|||||||
@@ -3,36 +3,22 @@
|
|||||||
namespace App\Commands;
|
namespace App\Commands;
|
||||||
|
|
||||||
use App\Client\Factory;
|
use App\Client\Factory;
|
||||||
use App\Logger\CliRequestLogger;
|
|
||||||
use LaravelZero\Framework\Commands\Command;
|
|
||||||
use React\EventLoop\LoopInterface;
|
use React\EventLoop\LoopInterface;
|
||||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
|
||||||
|
|
||||||
class SharePortCommand extends Command
|
class SharePortCommand extends ServerAwareCommand
|
||||||
{
|
{
|
||||||
protected $signature = 'share-port {port} {--auth=}';
|
protected $signature = 'share-port {port} {--auth=}';
|
||||||
|
|
||||||
protected $description = 'Share a local port with a remote expose server';
|
protected $description = 'Share a local port with a remote expose server';
|
||||||
|
|
||||||
protected function configureConnectionLogger()
|
|
||||||
{
|
|
||||||
app()->bind(CliRequestLogger::class, function () {
|
|
||||||
return new CliRequestLogger(new ConsoleOutput());
|
|
||||||
});
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->configureConnectionLogger();
|
|
||||||
|
|
||||||
$auth = $this->option('auth') ?? config('expose.auth_token', '');
|
$auth = $this->option('auth') ?? config('expose.auth_token', '');
|
||||||
|
|
||||||
(new Factory())
|
(new Factory())
|
||||||
->setLoop(app(LoopInterface::class))
|
->setLoop(app(LoopInterface::class))
|
||||||
->setHost(config('expose.host', 'localhost'))
|
->setHost($this->getServerHost())
|
||||||
->setPort(config('expose.port', 8080))
|
->setPort($this->getServerPort())
|
||||||
->setAuth($auth)
|
->setAuth($auth)
|
||||||
->createClient()
|
->createClient()
|
||||||
->sharePort($this->argument('port'))
|
->sharePort($this->argument('port'))
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ class ControlMessageController implements MessageComponentInterface
|
|||||||
|
|
||||||
$controlConnection = $this->connectionManager->findControlConnectionForSubdomain($subdomain);
|
$controlConnection = $this->connectionManager->findControlConnectionForSubdomain($subdomain);
|
||||||
|
|
||||||
if (! is_null($controlConnection) || $subdomain === config('expose.admin.subdomain')) {
|
if (! is_null($controlConnection) || $subdomain === config('expose.admin.subdomain') || in_array($subdomain, config('expose.admin.reserved_subdomains', []))) {
|
||||||
$message = config('expose.admin.messages.subdomain_taken');
|
$message = config('expose.admin.messages.subdomain_taken');
|
||||||
$message = str_replace(':subdomain', $subdomain, $message);
|
$message = str_replace(':subdomain', $subdomain, $message);
|
||||||
|
|
||||||
@@ -279,7 +279,7 @@ class ControlMessageController implements MessageComponentInterface
|
|||||||
$connection->send(json_encode([
|
$connection->send(json_encode([
|
||||||
'event' => 'authenticationFailed',
|
'event' => 'authenticationFailed',
|
||||||
'data' => [
|
'data' => [
|
||||||
'message' => config('expose.admin.messages.custom_subdomain_unauthorized'),
|
'message' => config('expose.admin.messages.tcp_port_sharing_unauthorized'),
|
||||||
],
|
],
|
||||||
]));
|
]));
|
||||||
$connection->close();
|
$connection->close();
|
||||||
|
|||||||
@@ -4,30 +4,20 @@ return [
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Host
|
| Servers
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| The expose server to connect to. By default, expose is using the free
|
| The available Expose servers that your client can connect to.
|
||||||
| sharedwithexpose.com server, offered by Beyond Code. You will need a free
|
| When sharing sites or TCP ports, you can specify the server
|
||||||
| Beyond Code account in order to authenticate with the server.
|
| that should be used using the `--server=` option.
|
||||||
| Feel free to host your own server and change this value.
|
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
'host' => 'sharedwithexpose.com',
|
'servers' => [
|
||||||
|
'default' => [
|
||||||
/*
|
'host' => 'sharedwithexpose.com',
|
||||||
|--------------------------------------------------------------------------
|
'port' => 443,
|
||||||
| Port
|
],
|
||||||
|--------------------------------------------------------------------------
|
],
|
||||||
|
|
|
||||||
| The port that expose will try to connect to. If you want to bypass
|
|
||||||
| firewalls and have proper SSL encrypted tunnels, make sure to use
|
|
||||||
| port 443 and use a reverse proxy for Expose.
|
|
||||||
|
|
|
||||||
| The free default server is already running on port 443.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
'port' => 443,
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@@ -218,6 +208,17 @@ return [
|
|||||||
*/
|
*/
|
||||||
'subdomain' => 'expose',
|
'subdomain' => 'expose',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Reserved Subdomain
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Specify any subdomains that you don't want to be able to register
|
||||||
|
| on your expose server.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
'reserved_subdomains' => [],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Subdomain Generator
|
| Subdomain Generator
|
||||||
@@ -277,6 +278,8 @@ return [
|
|||||||
|
|
||||||
'custom_subdomain_unauthorized' => 'You are not allowed to specify custom subdomains. Please upgrade to Expose Pro. Assigning a random subdomain instead.',
|
'custom_subdomain_unauthorized' => 'You are not allowed to specify custom subdomains. Please upgrade to Expose Pro. Assigning a random subdomain instead.',
|
||||||
|
|
||||||
|
'tcp_port_sharing_unauthorized' => 'You are not allowed to share TCP ports. Please upgrade to Expose Pro.',
|
||||||
|
|
||||||
'no_free_tcp_port_available' => 'There are no free TCP ports available on this server. Please try again later.',
|
'no_free_tcp_port_available' => 'There are no free TCP ports available on this server. Please try again later.',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Expose Dashboard :: {{ subdomains|join(", ") }}</title>
|
<title>Expose Dashboard :: {{ subdomains|join(", ") }}</title>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
|
||||||
<script src="https://unpkg.com/tailwindcss-jit-cdn"></script>
|
<script src="https://unpkg.com/tailwindcss-jit-cdn"></script>
|
||||||
<script type="tailwind-config">
|
<script type="tailwind-config">
|
||||||
{
|
{
|
||||||
@@ -15,6 +15,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<style type="postcss">
|
||||||
|
::selection {
|
||||||
|
@apply bg-pink-500 bg-opacity-50;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
|
||||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.1.0/build/styles/github.min.css">
|
<link rel="stylesheet" href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.1.0/build/styles/github.min.css">
|
||||||
<script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.1.0/build/highlight.min.js" async></script>
|
<script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.1.0/build/highlight.min.js" async></script>
|
||||||
@@ -95,15 +100,13 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.even\:bg-gray-50:nth-child(even) {
|
[v-cloak] {display: none}
|
||||||
background-color: #f7fafc;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="dark:bg-gray-900 dark:text-white">
|
<body class="dark:bg-gray-900 dark:text-white">
|
||||||
<div id="app" class="container px-8 mx-auto dark:bg-gray-900">
|
<div id="app" class="container px-8 mx-auto dark:bg-gray-900" v-cloak>
|
||||||
<header class="text-pink-500 font-semibold pt-12 pb-6 flex items-center justify-center">
|
<header class="text-pink-500 font-semibold pt-12 pb-6 flex items-center justify-center">
|
||||||
<a href="https://beyondco.de" target="_blank" class="inline-flex items-center self-start">
|
<a href="https://expose.beyondco.de" target="_blank" class="inline-flex items-center self-start">
|
||||||
<img src="https://beyondco.de/apps/icons/expose.png" class="h-12">
|
<img src="https://beyondco.de/apps/icons/expose.png" class="h-12">
|
||||||
<p class="ml-4 font-headline text-lg">Expose</p>
|
<p class="ml-4 font-headline text-lg">Expose</p>
|
||||||
</a>
|
</a>
|
||||||
@@ -113,9 +116,32 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</span>
|
</span>
|
||||||
</header>
|
</header>
|
||||||
<div class="p-5 flex flex-col md:flex-row">
|
<div class="pt-8 flex flex-col md:flex-row">
|
||||||
<div class="w-full md:w-1/3 flex flex-col mr-5">
|
<div class="w-full md:w-1/3 flex flex-col mr-5">
|
||||||
<div class="-my-2 py-2 overflow-x-auto sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8">
|
<div class="-my-2 py-2 overflow-x-auto sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8">
|
||||||
|
<div class="flex items-center pb-4">
|
||||||
|
<span
|
||||||
|
@click.prevent="followLogs = !followLogs"
|
||||||
|
class="mr-3 cursor-pointer" id="annual-billing-label">
|
||||||
|
<span class="text-sm font-medium dark:text-gray-200 text-gray-900">Follow requests:</span>
|
||||||
|
</span>
|
||||||
|
<button type="button"
|
||||||
|
:class="{
|
||||||
|
'bg-pink-500': followLogs,
|
||||||
|
'dark:bg-gray-700 bg-gray-200': ! followLogs,
|
||||||
|
}"
|
||||||
|
@click.prevent="followLogs = !followLogs"
|
||||||
|
class=" relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-pink-500" role="switch" aria-checked="false" aria-labelledby="annual-billing-label">
|
||||||
|
<span class="sr-only">Use setting</span>
|
||||||
|
<!-- Enabled: "translate-x-5", Not Enabled: "translate-x-0" -->
|
||||||
|
<span aria-hidden="true"
|
||||||
|
:class="{
|
||||||
|
'translate-x-5': followLogs,
|
||||||
|
'translate-x-0': ! followLogs,
|
||||||
|
}"
|
||||||
|
class="pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div class="flex mb-4">
|
<div class="flex mb-4">
|
||||||
<span class="h-8 inline-flex rounded-md shadow-sm">
|
<span class="h-8 inline-flex rounded-md shadow-sm">
|
||||||
<button @click.prevent="clearLogs"
|
<button @click.prevent="clearLogs"
|
||||||
@@ -128,7 +154,7 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div class="ml-4 flex-grow relative rounded-md shadow-sm">
|
<div class="ml-4 flex-grow relative rounded-md shadow-sm">
|
||||||
<input class="h-8 rounded-md dark:bg-gray-800 p-2 block w-full sm:text-sm sm:leading-5" v-model="search" placeholder="Search" />
|
<input class="h-8 rounded-md dark:bg-gray-800 p-2 block w-full sm:text-sm sm:leading-5 focus:outline-none focus:ring focus:ring-pink-500 focus:ring-opacity-75" v-model="search" placeholder="Search" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -149,8 +175,11 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody class="bg-white dark:bg-gray-700">
|
<tbody class="bg-white dark:bg-gray-700">
|
||||||
<tr v-for="log in filteredLogs"
|
<tr v-for="log in filteredLogs"
|
||||||
:class="{'dark:bg-gray-800 bg-gray-100': currentLog === log}"
|
:class="{
|
||||||
@click="setLog(log)">
|
'dark:bg-pink-500 dark:bg-opacity-25 bg-gray-100': currentLog === log,
|
||||||
|
'even:bg-gray-50 dark:even:bg-gray-800': currentLog !== log
|
||||||
|
}"
|
||||||
|
@click="setLog(log.id)">
|
||||||
<td class="cursor-pointer px-6 py-4 whitespace-normal border-b dark:border-gray-800 border-gray-200 text-sm leading-5 font-medium text-gray-900 dark:text-gray-200">
|
<td class="cursor-pointer px-6 py-4 whitespace-normal border-b dark:border-gray-800 border-gray-200 text-sm leading-5 font-medium text-gray-900 dark:text-gray-200">
|
||||||
<p class="flex">
|
<p class="flex">
|
||||||
<span class="text-xs">@{ log.request.method }</span>
|
<span class="text-xs">@{ log.request.method }</span>
|
||||||
@@ -164,27 +193,31 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<span class="text-xs text-gray-600 dark:text-gray-50">@{ log.performed_at }</span>
|
<span class="text-xs text-gray-600 dark:text-gray-50">@{ log.performed_at }</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="cursor-pointer px-6 py-4 whitespace-nowrap border-b dark:border-gray-800 border-gray-200 text-sm leading-5 text-gray-500 dark:text-gray-200 ">
|
<td class="cursor-pointer px-6 py-4 border-b dark:border-gray-800 border-gray-200 text-sm leading-5 text-gray-500 dark:text-gray-200 ">
|
||||||
<div v-if="log.response">
|
<div v-if="log.response">
|
||||||
<span
|
<span
|
||||||
v-if="log.response.status >= 200 && log.response.status < 300"
|
v-if="log.response.status >= 200 && log.response.status < 300"
|
||||||
|
:title="`${log.response.status} - ${log.response.reason}`"
|
||||||
class="inline-flex items-center px-3 py-0.5 rounded-full text-xs font-medium leading-5 bg-green-100 text-green-800">
|
class="inline-flex items-center px-3 py-0.5 rounded-full text-xs font-medium leading-5 bg-green-100 text-green-800">
|
||||||
@{ log.response.status } - @{ log.response.reason }
|
@{ log.response.status }
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
v-if="log.response.status >= 300 && log.response.status < 400"
|
v-if="log.response.status >= 300 && log.response.status < 400"
|
||||||
|
:title="`${log.response.status} - ${log.response.reason}`"
|
||||||
class="inline-flex items-center px-3 py-0.5 rounded-full text-xs font-medium leading-5 bg-blue-100 text-blue-800">
|
class="inline-flex items-center px-3 py-0.5 rounded-full text-xs font-medium leading-5 bg-blue-100 text-blue-800">
|
||||||
@{ log.response.status } - @{ log.response.reason }
|
@{ log.response.status }
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
v-if="log.response.status >= 400 && log.response.status < 500"
|
v-if="log.response.status >= 400 && log.response.status < 500"
|
||||||
|
:title="`${log.response.status} - ${log.response.reason}`"
|
||||||
class="inline-flex items-center px-3 py-0.5 rounded-full text-xs font-medium leading-5 bg-yellow-100 text-yellow-800">
|
class="inline-flex items-center px-3 py-0.5 rounded-full text-xs font-medium leading-5 bg-yellow-100 text-yellow-800">
|
||||||
@{ log.response.status } - @{ log.response.reason }
|
@{ log.response.status }
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
v-if="log.response.status >= 500"
|
v-if="log.response.status >= 500"
|
||||||
|
:title="`${log.response.status} - ${log.response.reason}`"
|
||||||
class="inline-flex items-center px-3 py-0.5 rounded-full text-xs font-medium leading-5 bg-red-100 text-red-800">
|
class="inline-flex items-center px-3 py-0.5 rounded-full text-xs font-medium leading-5 bg-red-100 text-red-800">
|
||||||
@{ log.response.status } - @{ log.response.reason }
|
@{ log.response.status }
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
@@ -312,7 +345,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-for="parameter in currentLog.request.post"
|
<div v-for="parameter in currentLog.request.post"
|
||||||
:key="'post_' + parameter.name"
|
:key="'post_' + parameter.name"
|
||||||
class="even:bg-gray-50 odd:bg-gray-50 px-4 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
class="even:bg-gray-100 bg-gray-50 px-4 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
<dt class="text-sm leading-5 font-medium text-gray-700">
|
<dt class="text-sm leading-5 font-medium text-gray-700">
|
||||||
@{ parameter.name }
|
@{ parameter.name }
|
||||||
</dt>
|
</dt>
|
||||||
@@ -337,7 +370,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-for="(value, header) in currentLog.request.headers"
|
<div v-for="(value, header) in currentLog.request.headers"
|
||||||
:key="header"
|
:key="header"
|
||||||
class="even:bg-gray-50 odd:bg-gray-50 dark:even:bg-gray-700 dark:odd:bg-gray-800 px-4 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
class="even:bg-gray-50 bg-gray-50 dark:even:bg-gray-700 dark:bg-gray-800 px-4 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
<dt class="text-sm leading-5 font-medium dark:text-gray-200 text-gray-700">
|
<dt class="text-sm leading-5 font-medium dark:text-gray-200 text-gray-700">
|
||||||
@{ header }
|
@{ header }
|
||||||
</dt>
|
</dt>
|
||||||
@@ -396,14 +429,14 @@
|
|||||||
<nav class="flex">
|
<nav class="flex">
|
||||||
<a href="#"
|
<a href="#"
|
||||||
@click.prevent="setActiveTab('raw')"
|
@click.prevent="setActiveTab('raw')"
|
||||||
:class="{'outline-none text-gray-700 bg-gray-100': activeTab === 'raw'}"
|
:class="{'outline-none text-white bg-pink-500': activeTab === 'raw'}"
|
||||||
class="px-3 py-2 font-medium text-sm leading-5 rounded-md text-gray-500 hover:text-gray-700">
|
class="px-3 py-2 font-medium text-sm leading-5 rounded-md text-gray-500 hover:text-gray-700 dark:hover:text-gray-100">
|
||||||
Raw
|
Raw
|
||||||
</a>
|
</a>
|
||||||
<a href="#"
|
<a href="#"
|
||||||
@click.prevent="setActiveTab('preview')"
|
@click.prevent="setActiveTab('preview')"
|
||||||
:class="{'outline-none text-gray-700 bg-gray-100': activeTab === 'preview'}"
|
:class="{'outline-none text-white bg-pink-500': activeTab === 'preview'}"
|
||||||
class="ml-4 px-3 py-2 font-medium text-sm leading-5 rounded-md text-gray-500 hover:text-gray-700 focus:outline-none focus:text-gray-700 focus:bg-gray-100">
|
class="ml-4 px-3 py-2 font-medium text-sm leading-5 rounded-md text-gray-500 hover:text-gray-100 focus:outline-nonedark:hover:text-gray-100">
|
||||||
Preview
|
Preview
|
||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -437,14 +470,21 @@
|
|||||||
|
|
||||||
data: {
|
data: {
|
||||||
search: '',
|
search: '',
|
||||||
currentLog: null,
|
currentLogId: null,
|
||||||
view: 'request',
|
view: 'request',
|
||||||
activeTab: 'raw',
|
activeTab: 'raw',
|
||||||
logs: [],
|
logs: [],
|
||||||
maxLogs: {{ max_logs }},
|
maxLogs: {{ max_logs }},
|
||||||
|
highlightNextLog: false,
|
||||||
|
followLogs: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
currentLog: function() {
|
||||||
|
return this.logs.find(log => {
|
||||||
|
return log.id === this.currentLogId;
|
||||||
|
});
|
||||||
|
},
|
||||||
filteredLogs: function() {
|
filteredLogs: function() {
|
||||||
if (this.search === '') {
|
if (this.search === '') {
|
||||||
return this.logs;
|
return this.logs;
|
||||||
@@ -498,8 +538,8 @@
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
},
|
},
|
||||||
setLog: function (log) {
|
setLog: function (id) {
|
||||||
this.currentLog = log;
|
this.currentLogId = id;
|
||||||
this.formatJsonResponse();
|
this.formatJsonResponse();
|
||||||
|
|
||||||
this.$nextTick(function () {
|
this.$nextTick(function () {
|
||||||
@@ -529,6 +569,7 @@
|
|||||||
this.view = view;
|
this.view = view;
|
||||||
},
|
},
|
||||||
replay: function (log) {
|
replay: function (log) {
|
||||||
|
this.highlightNextLog = true;
|
||||||
fetch('/api/replay/' + log.id);
|
fetch('/api/replay/' + log.id);
|
||||||
},
|
},
|
||||||
connect: function () {
|
connect: function () {
|
||||||
@@ -544,6 +585,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.logs = this.logs.splice(0, this.maxLogs);
|
this.logs = this.logs.splice(0, this.maxLogs);
|
||||||
|
|
||||||
|
if (this.highlightNextLog || this.followLogs) {
|
||||||
|
this.setLog(this.logs[0].id);
|
||||||
|
|
||||||
|
this.highlightNextLog = false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
loadLogs: function (log) {
|
loadLogs: function (log) {
|
||||||
|
|||||||
Reference in New Issue
Block a user