Make the application modular to have a "main" and "backend" part.
This commit is contained in:
parent
884f721002
commit
e5b0e1fcfd
28 changed files with 112 additions and 7 deletions
50
app/views/main/auth/index.volt
Normal file
50
app/views/main/auth/index.volt
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
|
||||
|
||||
<div class="login-container section">
|
||||
|
||||
<h3>Login</h3>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<p class="text-center"><strong>Heads up!</strong> Signup is currently not available.</p>
|
||||
<p>
|
||||
Login using username/password can be setup after registration.
|
||||
First time users can only register with third party services below
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<span class="spacer"></span>
|
||||
|
||||
<form class="form" method="post" action="">
|
||||
<div class="form-group">
|
||||
{{ form.render('Email') }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{ form.render('Password') }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{ form.render('Login') }}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<span class="spacer"></span>
|
||||
|
||||
<div class="oauth">
|
||||
|
||||
<a class="button button-github" href="{{ url(['for': 'oauth', 'strategy': 'github']) }}">
|
||||
{{ icon('brand/github') }} GitHub
|
||||
</a>
|
||||
|
||||
<a class="button button-google" href="{{ url(['for': 'oauth', 'strategy': 'google']) }}">
|
||||
{{ icon('brand/google') }} Google
|
||||
</a>
|
||||
|
||||
<a class="button button-gitlab" href="{{ url(['for': 'oauth', 'strategy': 'gitlab']) }}">
|
||||
{{ icon('brand/gitlab') }} Gitlab
|
||||
</a>
|
||||
|
||||
<a class="button button-linkedin" href="{{ url(['for': 'oauth', 'strategy': 'linkedin']) }}">
|
||||
{{ icon('brand/linkedin') }} LinkedIn
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
43
app/views/main/auth/register.volt
Normal file
43
app/views/main/auth/register.volt
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
|
||||
<div class="register">
|
||||
|
||||
<h2>Account registration</h2>
|
||||
|
||||
<div class="alert alert-info alert-dismissible" role="alert">
|
||||
|
||||
<strong>Information!</strong>
|
||||
|
||||
<p>
|
||||
The form is prepared with the information provided by <strong>{{ provider }}</strong>.
|
||||
Please check the information and make changes if necessary before continue.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<span class="spacer"></span>
|
||||
|
||||
<form class="form form-horizontal" method="post">
|
||||
|
||||
<div class="form-group">
|
||||
{{ form.renderDecorated('email') }}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
{{ form.renderDecorated('username') }}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
{{ form.renderDecorated('first-name', ['length': 4]) }}
|
||||
{{ form.renderDecorated('last-name', ['length': 4]) }}
|
||||
</div>
|
||||
|
||||
<span class="spacer"></span>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-xs-12 col-xs-offset-2">
|
||||
{{ form.render('submit') }}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
14
app/views/main/callback/created.volt
Normal file
14
app/views/main/callback/created.volt
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
<div class="section">
|
||||
|
||||
<h1>Callback created</h1>
|
||||
|
||||
<p>Set this link as callback url for the service you want to debug:</p>
|
||||
|
||||
<strong>{{ serverUrl() }}{{ url(['for': 'cb-endpoint', 'id': id]) }}</strong>
|
||||
|
||||
<a class="button button-default" href="{{ url('/callback/show/' ~ id) }}">
|
||||
{{ icon('solid/eye') }} View
|
||||
</a>
|
||||
|
||||
</div>
|
||||
69
app/views/main/callback/list.volt
Normal file
69
app/views/main/callback/list.volt
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
|
||||
<div class="section">
|
||||
|
||||
<div class="clearfix">
|
||||
<h2 class="pull-left">Callbacks</h2>
|
||||
|
||||
<div class="pull-right">
|
||||
<a class="button button-large button-primary" href="{{ url('/callback/new') }}">
|
||||
{{ icon('solid/plus') }} New
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if page.items|length > 0 %}
|
||||
|
||||
<div class="callback-list">
|
||||
|
||||
{% for item in page.items %}
|
||||
<div class="callback-list-item">
|
||||
|
||||
<div class="callback-list-item-header">
|
||||
<a class="callback-list-item-name" href="/callback/show/{{ item.public_id }}">{{ item.name|e }}</a>
|
||||
|
||||
{% if item.countRequests() > 0 %}
|
||||
<span class="badge badge-primary">
|
||||
{{ item.countRequests() }} Requests
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="callback-list-item-info">
|
||||
<span>{{ icon('clock') }} Created at: {{ item.created_at }}</span>
|
||||
|
||||
<span>
|
||||
{{ icon('paper-plane') }}
|
||||
|
||||
{% if item.countRequests() > 0 %}
|
||||
Last request: {{ item.last_request }}
|
||||
{% else %}
|
||||
No requests yet.
|
||||
{% endif %}
|
||||
</span>
|
||||
|
||||
<span>
|
||||
{{ icon('solid/link') }}
|
||||
{{ serverUrl() }}{{ url(['for': 'cb-endpoint', 'id': item.public_id]) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<a class="callback-list-item-arrow" href="/callback/show/{{ item.public_id }}">
|
||||
{{ icon('solid/arrow-alt-circle-right') }}
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
|
||||
<div class="blankslate">
|
||||
<h3>No callbacks made yet.</h3>
|
||||
<p><a href="{{ url('/callback/new') }}">Create</a> a callback to begin!</p>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
<nav class="text-center" aria-label="Page navigation">
|
||||
{{ partial('pagination') }}
|
||||
</nav>
|
||||
</div>
|
||||
39
app/views/main/callback/new.volt
Normal file
39
app/views/main/callback/new.volt
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
<div class="section center-block" style="width: 400px">
|
||||
|
||||
<h2>Create callback</h2>
|
||||
|
||||
<!--
|
||||
<form class="form-horizontal" method="post">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="name">Name</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="name" id="name">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<input type="submit" class="button button-brand" value="Create">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
-->
|
||||
|
||||
<form class="form-horizontal" method="post">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="name">Name</label>
|
||||
<div class="col-sm-10">
|
||||
{{ form.render('Name') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
{{ form.render('Create') }}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
140
app/views/main/callback/show.volt
Normal file
140
app/views/main/callback/show.volt
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
|
||||
<div class="section">
|
||||
<div class="clearfix">
|
||||
<h3 class="pull-left">{{ item.name|e }}</h3>
|
||||
<h5 class="pull-right"><strong>Created at:</strong> {{ item.created_at }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="panel-group request-list" id="request-list" role="tablist" aria-multiselectable="true">
|
||||
|
||||
{% for index, req in page.items %}
|
||||
|
||||
<div class="panel request-list-item">
|
||||
<a id="request-item-head-{{ index }}" class="request-list-item-header collapsed"
|
||||
href="#request-item-body-{{ index }}" aria-controls="request-item-body-{{ index }}"
|
||||
data-parent="#request-list" data-toggle="collapse" aria-expanded="true" >
|
||||
|
||||
<div class="request-list-item-header-row">
|
||||
|
||||
<span class="request-list-item-header-method">
|
||||
{{ req.getMethod() }}
|
||||
</span>
|
||||
|
||||
<span class="request-list-item-header-uri">
|
||||
<span class="url">{{ urlStyle(req.getUri()) }}</span>
|
||||
</span>
|
||||
|
||||
<span class="request-list-item-header-timestamp">
|
||||
{{ icon('clock') }} {{ req.getTimestamp() }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="request-list-item-header-row">
|
||||
|
||||
<span class="request-list-item-header-type">
|
||||
{{ icon('file-alt') }} {{ req.getType() }}
|
||||
</span>
|
||||
|
||||
<span class="request-list-item-header-size">
|
||||
{{ icon('solid/database') }} {{ req.getSize() }} b
|
||||
</span>
|
||||
|
||||
<span class="request-list-item-header-ip">
|
||||
{{ icon('compass') }} {{ req.getSourceIp() }}
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div id="request-item-body-{{ index }}" class="collapse"
|
||||
role="tabpanel" aria-labelledby="request-item-head-{{ index }}">
|
||||
|
||||
<div class="request-list-item-detail">
|
||||
|
||||
{% if req.getUriQuery()|length > 0 %}
|
||||
|
||||
<button class="request-list-item-detail-button" type="button"
|
||||
data-toggle="collapse" data-target="#request-list-item-detail-query-{{ index }}"
|
||||
aria-expanded="false" aria-controls="request-list-item-detail-query-{{ index }}">
|
||||
Query
|
||||
</button>
|
||||
|
||||
<div id="request-list-item-detail-query-{{ index }}" class="collapse in">
|
||||
<table class="request-list-item-detail-headers">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="request-list-item-detail-headers-key">Key</th>
|
||||
<th class="request-list-item-detail-headers-value">Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, val in req.getUriQuery() %}
|
||||
<tr>
|
||||
<td><strong>{{ key|e }}</strong></td>
|
||||
<td>{{ val|e }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
<button class="request-list-item-detail-button" type="button"
|
||||
data-toggle="collapse" data-target="#request-list-item-detail-headers-{{ index }}"
|
||||
aria-expanded="false" aria-controls="request-list-item-detail-headers-{{ index }}">
|
||||
Headers
|
||||
</button>
|
||||
|
||||
<div id="request-list-item-detail-headers-{{ index }}" class="collapse">
|
||||
<table class="request-list-item-detail-headers">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="request-list-item-detail-headers-key">Key</th>
|
||||
<th class="request-list-item-detail-headers-value">Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, val in req.getHeaders() %}
|
||||
<tr>
|
||||
<td><strong>{{ key|e }}</strong></td>
|
||||
<td>{{ val|e }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<button class="request-list-item-detail-button" type="button"
|
||||
data-toggle="collapse" data-target="#request-list-item-detail-body-{{ index }}"
|
||||
aria-expanded="false" aria-controls="request-list-item-detail-body-{{ index }}">
|
||||
Body
|
||||
</button>
|
||||
|
||||
<div id="request-list-item-detail-body-{{ index }}" class="request-list-item-detail-body collapse in">
|
||||
|
||||
{% if (req.getBody()|length < 1) %}
|
||||
<div class="blankslate blankslate-sm">
|
||||
<h3>Empty body</h3>
|
||||
</div>
|
||||
{% else %}
|
||||
<pre>{{ req.getBody() }}</pre>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<div class="blankslate">
|
||||
<h3>No requests made yet.</h3>
|
||||
<p>No http requests has been made to this callback.</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
|
||||
<nav class="text-center" aria-label="Page navigation">
|
||||
{{ partial('pagination') }}
|
||||
</nav>
|
||||
</div>
|
||||
8
app/views/main/error/error.volt
Normal file
8
app/views/main/error/error.volt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
<span class="text-center">
|
||||
<h1>An Error Occurred!</h1>
|
||||
|
||||
<p>The server freaked out while computing the bits. Try again later.</p>
|
||||
|
||||
<p>If the problem persist, contact <a href="mailto:henrik.hautakoski@gmail.com">this guy</a></p>
|
||||
</span>
|
||||
11
app/views/main/error/show404.volt
Normal file
11
app/views/main/error/show404.volt
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
{% set google_url = "http://google.com?q=" ~ url | escape %}
|
||||
|
||||
<span class="text-center">
|
||||
<h1>404 Not Found</h1>
|
||||
<p>
|
||||
This page does not exist! You can search for it on
|
||||
<a target="_blank" href="{{ google_url }}">google</a>
|
||||
if you want.
|
||||
</p>
|
||||
</span>
|
||||
64
app/views/main/index/about.volt
Normal file
64
app/views/main/index/about.volt
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<div class="about">
|
||||
|
||||
<div class="about-main">
|
||||
<div class="section">
|
||||
|
||||
<div class="section-header">
|
||||
<h1>About</h1>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
httpcb was created because I needed to debug some API's with
|
||||
the callback concept over the years (The problem is you can't call
|
||||
your local webserver most of the time because it's behind NAT and
|
||||
your development server is not configured to handle public traffic etc.)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
There is ofcourse alot of similar applications out there
|
||||
(alot of people run into the <i>"local dev server"</i>-problem).
|
||||
However. They where mostly annoying with trail periods, super
|
||||
advanced UI and limited amount of requests. etc. So i decided to
|
||||
make my own.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Later this project evolved to be my
|
||||
<i>"try that new thing"</i>-project.
|
||||
So now, it serves as both a quick tool to check the HTTP request
|
||||
some API will send you and a place where i can try out new web technologies.
|
||||
And because the whole point of this application is that it's on a public webserver.
|
||||
Why not let others use it if they want!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
So if you want you can send me a <a href="mailto:henrik.hautakoski@gmail.com">email</a>
|
||||
if you find a bug, request som future or just to let me know that i helped someone with debugging.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="about-reference">
|
||||
|
||||
<div class="section">
|
||||
|
||||
<h4 class="text-center">Built with</h4>
|
||||
|
||||
<div class="phalcon">
|
||||
<a target="_blank" href="http://phalconphp.com">
|
||||
<img class="img-responsive" src="/img/phalcon-php.png" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="text-center">
|
||||
<a target="_blank" href="http://getbootstrap.com">
|
||||
<img height="40" src="/img/bootstrap-solid.svg" />
|
||||
Bootstrap
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
32
app/views/main/index/index.volt
Normal file
32
app/views/main/index/index.volt
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
<div class="feature-section">
|
||||
|
||||
<div class="section-header">
|
||||
<h1>How Does It Work?</h1>
|
||||
</div>
|
||||
|
||||
<div class="steps">
|
||||
<div class="steps-step1">
|
||||
<div class="steps-img"></div>
|
||||
<h3>1. Create a new Callback</h3>
|
||||
<p>Register a callback and you will be given a unique url</p>
|
||||
</div>
|
||||
|
||||
<div class="steps-step2">
|
||||
<div class="steps-img"></div>
|
||||
|
||||
<h3>2. Set endpoint URL</h3>
|
||||
<p>Configure your API that you want to test to send to that url</p>
|
||||
</div>
|
||||
|
||||
<div class="steps-step3">
|
||||
<div class="steps-img"></div>
|
||||
<h3>3. Monitor traffic</h3>
|
||||
<p>Make API Calls and watch what your API will send you</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<a href="/callback/new" class="button button-large button-brand">Get started</a>
|
||||
</div>
|
||||
</div>
|
||||
31
app/views/main/user/activity.volt
Normal file
31
app/views/main/user/activity.volt
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
<div class="section">
|
||||
|
||||
<h3>Activity Log</h3>
|
||||
|
||||
<table class="table table-condensed table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Ip</th>
|
||||
<th>Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for item in page.items %}
|
||||
<tr>
|
||||
<td>{{ item.getTimestamp() }}</td>
|
||||
<td>{{ item.getIp() }}</td>
|
||||
<td>{{ item.getMessage() }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<nav class="text-center" aria-label="Page navigation">
|
||||
{{ partial('pagination') }}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
||||
116
app/views/main/user/settings.volt
Normal file
116
app/views/main/user/settings.volt
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
{%
|
||||
set social_links = [
|
||||
'github' : [ 'connected': user.getGithubId() > 0 ],
|
||||
'gitlab' : [ 'connected': user.getGitlabId() > 0, 'class': 'text-gitlab' ],
|
||||
'google' : [ 'connected': user.getGoogleId() > 0, 'class': 'text-google' ],
|
||||
'linkedin' : [ 'connected': user.getLinkedinId() | length, 'class': 'text-linkedin' ]
|
||||
]
|
||||
%}
|
||||
|
||||
<div class="section">
|
||||
|
||||
<form class="form-horizontal" method="post" action="">
|
||||
|
||||
<div class="form-group">
|
||||
{{ form.renderDecorated('username', [ 'length': 7 ]) }}
|
||||
{{ form.renderDecorated('id', [ 'length': 2, 'label-length' : 1 ]) }}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
{{ form.renderDecorated('name') }}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
{{ form.renderDecorated('email') }}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<h4>Password</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if form.has('passwordCurrent') %}
|
||||
<div class="form-group">
|
||||
{{ form.renderDecorated('passwordCurrent') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="form-group">
|
||||
{{ form.renderDecorated('passwordNew') }}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
{{ form.renderDecorated('passwordConfirm') }}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
|
||||
<h4>Social sign-in</h4>
|
||||
|
||||
<hr />
|
||||
|
||||
{% for name,info in social_links %}
|
||||
<div class="col-sm-2 text-center">
|
||||
{% set class = info['class'] | default(false) %}
|
||||
<div{{ class ? ' class="%s"'|format(class) : '' }}>{{ icon('brand/' ~ name, [ '3x' ]) }}</div>
|
||||
{% if info['connected'] %}
|
||||
<a href="{{ url(['for': 'oauth-disconnect', 'provider': name]) }}">Disconnect</a>
|
||||
{% else %}
|
||||
<a href="{{ url(['for': 'oauth', 'strategy': name]) }}">Connect</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<hr />
|
||||
{{ form.render('Save') }}
|
||||
|
||||
<button class="button button-danger pull-right" type="button" data-toggle="modal" data-target="#deleteModal">
|
||||
Delete Account
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title pull-left" id="deleteModalLabel">Delete account</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form method="post" action="/user/delete">
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
Deleting your account is a non-reversible action.
|
||||
All data associated with your account will be lost in the process.
|
||||
</p>
|
||||
{% if user.password|length > 0 %}
|
||||
<p>Enter your <kbd>password</kbd> to confirm:</p>
|
||||
|
||||
<input type="password" name="currentpw" class="form-control" />
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="button button-default" data-dismiss="modal">Close</button>
|
||||
<input type="submit" name="deleteAcc" class="button button-danger" value="Delete account">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in a new issue