added request contents to the web interface

This commit is contained in:
Gonçalo Valério 2019-01-06 22:37:10 +00:00
parent 4046e5b59d
commit 8449230620
5 changed files with 158 additions and 20 deletions

View File

@ -1,34 +1,81 @@
function logger(message) {
let datetime = new Date().toISOString();
console.log(`[Webhook_logger] [${datetime}] ${message}`);
}
function getCallbackCode() {
let urlParams = new URLSearchParams(window.location.search);
return urlParams.get("cb");
}
function setCallbackUrl(callbackCode) {
let protocol = document.location.protocol;
let host = document.location.host;
let submitURL = `${protocol}//${host}/${getCallbackCode()}`;
document.getElementById("callback-uuid-field").value = submitURL;
}
function setupConnection() {
/*
Setup a connection to receive all the information
about the webhooks in real-time.
*/
var callbackCode = getCallbackCode();
console.log(callbackCode);
var webhookSocket = new WebSocket(
let callbackCode = getCallbackCode();
let webhookSocket = new WebSocket(
"ws://" + window.location.host + "/ws/callback/" + callbackCode + "/"
);
webhookSocket.onmessage = function(event) {
/*
Parses the information adds it to the UI state
Parses the information adds it to the UI state
*/
console.log(event);
logger("Message Received");
requestList.addRequest(JSON.parse(event.data));
};
webhookSocket.onopen = function(event) {
console.log("[Webhook_logger] Connection stablished");
logger("Connection stablished");
};
webhookSocket.onclose = function(event) {
console.log("[Webhook_logger] Connection lost");
console.log("[Webhook_logger] Trying to reconnect");
logger("Connection lost");
logger("Trying to reconnect");
setupConnection();
};
}
function getCallbackCode() {
var urlParams = new URLSearchParams(window.location.search);
return urlParams.get("cb");
}
/*
Setup a simple component to handle the display of new content.
Only supports two functions:
- Add new content
- Clean existing content
*/
var requestList = new Vue({
el: "#app",
delimiters: ["[[", "]]"],
data: {
requests: []
},
methods: {
addRequest: function(request) {
request.displayFull = false;
this.requests.unshift(request);
},
toggleDetail: function(index) {
let req = this.requests[index];
req.displayFull = !req.displayFull;
},
removeItem: function(index) {
this.requests.splice(index, 1);
},
clean: function() {
this.requests = [];
}
}
});
/*
Prepare the page for action
*/
setCallbackUrl();
setupConnection();

View File

@ -4,9 +4,76 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css"
/>
<title>Webhook Logger</title>
</head>
<body>
<section class="section">
<div class="container">
<h1 class="title">Webhook Logger</h1>
<h2 class="subtitle">
Easily test and inspect
<a href="https://en.wikipedia.org/wiki/Webhook">webhooks</a>
</h2>
<p>Use the following URL as your webhook callback:</p>
<div class="field">
<div class="control">
<input
id="callback-uuid-field"
class="input is-large is-info"
type="text"
placeholder="Large input"
readonly
/>
</div>
</div>
</div>
</section>
<section class="section" id="app">
<div class="container">
<div v-if="requests.length === 0">No requests received yet</div>
<div class="card" v-for="(request, index) in requests">
<header class="card-header">
<p class="card-header-title" v-on:click="toggleDetail(index)">
<span class="tag is-info">[[request.method]]</span>
<span> from: [[request.ip_address]]</span>
</p>
<p class="card-header-icon">
<span class="tag"
>[[new Date(request.received_at).toLocaleString()]]</span
>
<button class="delete" v-on:click="removeItem(index)"></button>
</p>
</header>
<div class="card-content" v-if="request.displayFull">
<div class="content">
<h2>Headers</h2>
<table>
<thead>
<th>Header</th>
<th>Content</th>
</thead>
<tbody>
<tr v-for="header in request.headers">
<td>[[header.name]]</td>
<td>[[header.value]]</td>
</tr>
</tbody>
</table>
<h2>Query Params</h2>
<div class="box">[[request.query_params]]</div>
<h2>Body</h2>
<div class="box">[[request.body]]</div>
</div>
</div>
</div>
</div>
</section>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="{% static 'js/viewer.js' %}"></script>
</body>

9
callbacks/utils.py Normal file
View File

@ -0,0 +1,9 @@
from django.http import HttpRequest
def get_ip_address(request: HttpRequest) -> str:
forwarded = request.META.get("X-FORWARDED-FOR")
if forwarded:
return forwarded.split(",")[0]
else:
return request.META.get("REMOTE_ADDR")

View File

@ -8,6 +8,8 @@ from django.utils import timezone
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
from .utils import get_ip_address
class HomeView(RedirectView):
"""Initial page, just sends the visitor to an unique url"""
@ -37,15 +39,28 @@ class CallbackView(View):
def dispatch(self, request, *args, **kwargs):
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
kwargs["uuid"], {"type": "new_request", "data": self.request_data(request)}
kwargs["uuid"], {"type": "new_request", "data": self._request_data(request)}
)
return HttpResponse()
def request_data(self, request):
def _request_data(self, request):
body = request.body.decode("utf-8")
return {
"method": request.method,
"ip_address": get_ip_address(request),
"query_params": request.GET,
"body": request.POST,
"headers": request.META,
"body": body,
"headers": self._received_headers(),
"received_at": timezone.now().isoformat(),
}
def _received_headers(self):
request = self.request
headers = []
for key, value in request.META.items():
if key.startswith("HTTP"):
original_header = (
key.replace("HTTP_", "").replace("_", "-").capitalize()
)
headers.append({"name": original_header, "value": value})
return headers

View File

@ -17,7 +17,7 @@ from django.urls import path
from callbacks.views import HomeView, CheckView, CallbackView
urlpatterns = [
path('check', CheckView.as_view(), name='callback-check'),
path('submit/<uuid>', CallbackView.as_view(), name='callback-submit'),
path('', HomeView.as_view(), name='callback-home')
path("check", CheckView.as_view(), name="callback-check"),
path("<uuid>", CallbackView.as_view(), name="callback-submit"),
path("", HomeView.as_view(), name="callback-home"),
]