mirror of
https://github.com/bitinflow/expose.git
synced 2026-03-13 13:35:54 +00:00
wip
This commit is contained in:
177
app/Logger/LoggedRequest.php
Normal file
177
app/Logger/LoggedRequest.php
Normal file
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace App\Logger;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use Laminas\Http\Request;
|
||||
use Laminas\Http\Response;
|
||||
use Riverline\MultiPartParser\StreamedPart;
|
||||
|
||||
class LoggedRequest implements \JsonSerializable
|
||||
{
|
||||
/** @var string */
|
||||
protected $rawRequest;
|
||||
|
||||
/** @var Request */
|
||||
protected $parsedRequest;
|
||||
|
||||
/** @var string */
|
||||
protected $rawResponse;
|
||||
|
||||
/** @var Response */
|
||||
protected $parsedResponse;
|
||||
|
||||
/** @var string */
|
||||
protected $id;
|
||||
|
||||
/** @var Carbon */
|
||||
protected $startTime;
|
||||
|
||||
/** @var Carbon */
|
||||
protected $stopTime;
|
||||
|
||||
/** @var string */
|
||||
protected $subdomain;
|
||||
|
||||
public function __construct(string $rawRequest, Request $parsedRequest)
|
||||
{
|
||||
$this->id = (string)Str::uuid();
|
||||
$this->startTime = now();
|
||||
$this->rawRequest = $rawRequest;
|
||||
$this->parsedRequest = $parsedRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
$data = [
|
||||
'id' => $this->id,
|
||||
'performed_at' => $this->startTime->toDateTimeString(),
|
||||
'duration' => $this->startTime->diffInMilliseconds($this->stopTime, false),
|
||||
'subdomain' => $this->detectSubdomain(),
|
||||
'request' => [
|
||||
'raw' => $this->isBinary($this->rawRequest) ? 'BINARY' : $this->rawRequest,
|
||||
'method' => $this->parsedRequest->getMethod(),
|
||||
'uri' => $this->parsedRequest->getUri()->getPath(),
|
||||
'headers' => $this->parsedRequest->getHeaders()->toArray(),
|
||||
'body' => $this->isBinary($this->rawRequest) ? 'BINARY' : $this->parsedRequest->getContent(),
|
||||
'query' => $this->parsedRequest->getQuery()->toArray(),
|
||||
'post' => $this->getPost(),
|
||||
],
|
||||
];
|
||||
|
||||
if ($this->parsedResponse) {
|
||||
$data['response'] = [
|
||||
'raw' => $this->shouldReturnBody() ? $this->rawResponse : 'BINARY',
|
||||
'status' => $this->parsedResponse->getStatusCode(),
|
||||
'headers' => $this->parsedResponse->getHeaders()->toArray(),
|
||||
'reason' => $this->parsedResponse->getReasonPhrase(),
|
||||
'body' => $this->shouldReturnBody() ? $this->parsedResponse->getBody() : 'BINARY',
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function isBinary(string $string): bool
|
||||
{
|
||||
return preg_match('~[^\x20-\x7E\t\r\n]~', $string) > 0;
|
||||
}
|
||||
|
||||
protected function shouldReturnBody()
|
||||
{
|
||||
$contentType = Arr::get($this->parsedResponse->getHeaders()->toArray(), 'Content-Type');
|
||||
|
||||
return $contentType === 'application/json' || Str::is('text/*', $contentType) || Str::is('*javascript*', $contentType);
|
||||
}
|
||||
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->parsedRequest;
|
||||
}
|
||||
|
||||
public function setResponse(string $rawResponse, Response $response)
|
||||
{
|
||||
$this->parsedResponse = $response;
|
||||
|
||||
$this->rawResponse = $rawResponse;
|
||||
|
||||
$this->stopTime = now();
|
||||
}
|
||||
|
||||
public function id()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getRequestData()
|
||||
{
|
||||
return $this->rawRequest;
|
||||
}
|
||||
|
||||
protected function getResponseBody()
|
||||
{
|
||||
return \Laminas\Http\Response::fromString($this->rawResponse)->getBody();
|
||||
}
|
||||
|
||||
protected function getPost()
|
||||
{
|
||||
$postData = [];
|
||||
|
||||
$contentType = Arr::get($this->parsedRequest->getHeaders()->toArray(), 'Content-Type');
|
||||
|
||||
switch ($contentType) {
|
||||
case 'application/x-www-form-urlencoded':
|
||||
parse_str($this->parsedRequest->getContent(), $postData);
|
||||
$postData = collect($postData)->map(function ($key, $value) {
|
||||
return [
|
||||
'name' => $key,
|
||||
'value' => $value,
|
||||
];
|
||||
})->toArray();
|
||||
break;
|
||||
case 'application/json':
|
||||
$postData = collect(json_decode($this->parsedRequest->getContent(), true))->map(function ($key, $value) {
|
||||
return [
|
||||
'name' => $key,
|
||||
'value' => $value,
|
||||
];
|
||||
})->toArray();
|
||||
|
||||
break;
|
||||
default:
|
||||
$stream = fopen('php://temp', 'rw');
|
||||
fwrite($stream, $this->rawRequest);
|
||||
rewind($stream);
|
||||
|
||||
try {
|
||||
$document = new StreamedPart($stream);
|
||||
if ($document->isMultiPart()) {
|
||||
$postData = collect($document->getParts())->map(function (StreamedPart $part) {
|
||||
return [
|
||||
'name' => $part->getName(),
|
||||
'value' => $part->isFile() ? null : $part->getBody(),
|
||||
'is_file' => $part->isFile(),
|
||||
'filename' => $part->isFile() ? $part->getFileName() : null,
|
||||
'mime_type' => $part->isFile() ? $part->getMimeType() : null,
|
||||
];
|
||||
})->toArray();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
//
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $postData;
|
||||
}
|
||||
|
||||
protected function detectSubdomain()
|
||||
{
|
||||
return Arr::get($this->parsedRequest->getHeaders()->toArray(), 'X-Original-Host');
|
||||
}
|
||||
}
|
||||
65
app/Logger/Logger.php
Normal file
65
app/Logger/Logger.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Logger;
|
||||
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class Logger
|
||||
{
|
||||
/** @var \Symfony\Component\Console\Output\OutputInterface */
|
||||
protected $consoleOutput;
|
||||
|
||||
/** @var bool */
|
||||
protected $enabled = false;
|
||||
|
||||
/** @var bool */
|
||||
protected $verbose = false;
|
||||
|
||||
public function __construct(OutputInterface $consoleOutput)
|
||||
{
|
||||
$this->consoleOutput = $consoleOutput;
|
||||
}
|
||||
|
||||
public function enable($enabled = true)
|
||||
{
|
||||
$this->enabled = $enabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function verbose($verbose = false)
|
||||
{
|
||||
$this->verbose = $verbose;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function info(string $message)
|
||||
{
|
||||
$this->line($message, 'info');
|
||||
}
|
||||
|
||||
protected function warn(string $message)
|
||||
{
|
||||
if (! $this->consoleOutput->getFormatter()->hasStyle('warning')) {
|
||||
$style = new OutputFormatterStyle('yellow');
|
||||
|
||||
$this->consoleOutput->getFormatter()->setStyle('warning', $style);
|
||||
}
|
||||
|
||||
$this->line($message, 'warning');
|
||||
}
|
||||
|
||||
protected function error(string $message)
|
||||
{
|
||||
$this->line($message, 'error');
|
||||
}
|
||||
|
||||
protected function line(string $message, string $style)
|
||||
{
|
||||
$styled = $style ? "<$style>$message</$style>" : $message;
|
||||
|
||||
$this->consoleOutput->writeln($styled);
|
||||
}
|
||||
}
|
||||
64
app/Logger/RequestLogger.php
Normal file
64
app/Logger/RequestLogger.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Logger;
|
||||
|
||||
use Clue\React\Buzz\Browser;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
use Laminas\Http\Request;
|
||||
use Laminas\Http\Response;
|
||||
use function GuzzleHttp\Psr7\stream_for;
|
||||
|
||||
class RequestLogger
|
||||
{
|
||||
protected $requests = [];
|
||||
protected $responses = [];
|
||||
|
||||
public function __construct(Browser $browser)
|
||||
{
|
||||
$this->client = $browser;
|
||||
}
|
||||
|
||||
public function findLoggedRequest(string $id): ?LoggedRequest
|
||||
{
|
||||
return collect($this->requests)->first(function (LoggedRequest $loggedRequest) use ($id) {
|
||||
return $loggedRequest->id() === $id;
|
||||
});
|
||||
}
|
||||
|
||||
public function logRequest(string $rawRequest, Request $request)
|
||||
{
|
||||
array_unshift($this->requests, new LoggedRequest($rawRequest, $request));
|
||||
|
||||
$this->requests = array_slice($this->requests, 0, 10);
|
||||
|
||||
$this->pushLogs();
|
||||
}
|
||||
|
||||
public function logResponse(Request $request, string $rawResponse, Response $response)
|
||||
{
|
||||
$loggedRequest = collect($this->requests)->first(function (LoggedRequest $loggedRequest) use ($request) {
|
||||
return $loggedRequest->getRequest() === $request;
|
||||
});
|
||||
if ($loggedRequest) {
|
||||
$loggedRequest->setResponse($rawResponse, $response);
|
||||
|
||||
$this->pushLogs();
|
||||
}
|
||||
}
|
||||
|
||||
public function getData()
|
||||
{
|
||||
return $this->requests;
|
||||
}
|
||||
|
||||
protected function pushLogs()
|
||||
{
|
||||
$this
|
||||
->client
|
||||
->post(
|
||||
'http://127.0.0.1:4040/logs',
|
||||
['Content-Type' => 'application/json'],
|
||||
json_encode($this->getData(), JSON_INVALID_UTF8_IGNORE)
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user