Add frontend
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
bcef1a1cce
commit
2426abab37
6 changed files with 631 additions and 1 deletions
3
Makefile
Normal file
3
Makefile
Normal file
|
@ -0,0 +1,3 @@
|
|||
generate:
|
||||
coffee -c frontend/application.coffee
|
||||
go generate
|
283
assets.go
Normal file
283
assets.go
Normal file
File diff suppressed because one or more lines are too long
80
frontend/application.coffee
Normal file
80
frontend/application.coffee
Normal file
|
@ -0,0 +1,80 @@
|
|||
securePassword = null
|
||||
|
||||
createSecret = () ->
|
||||
secret = $('#formCreateSecret').find('textarea').val()
|
||||
|
||||
if $('#extra').prop 'checked'
|
||||
securePassword = Math.random().toString(36).substring(2)
|
||||
secret = GibberishAES.enc(secret, securePassword)
|
||||
|
||||
$.ajax 'api/create',
|
||||
data:
|
||||
secret: secret
|
||||
dataType: "json"
|
||||
statusCode:
|
||||
201: secretCreated
|
||||
400: somethingWrong
|
||||
500: somethingWrong
|
||||
404: () ->
|
||||
# Mock for interface testing
|
||||
secretCreated
|
||||
secret_id: 'foobar'
|
||||
|
||||
false
|
||||
|
||||
dataNotFound = () ->
|
||||
$('#notfound').show()
|
||||
|
||||
hashLoad = () ->
|
||||
hash = window.location.hash
|
||||
if hash.length == 0
|
||||
return
|
||||
|
||||
parts = hash.split '|'
|
||||
if parts.length == 2
|
||||
hash = parts[0]
|
||||
securePassword = parts[1]
|
||||
|
||||
id = hash.substring(1)
|
||||
$.ajax "api/get/#{id}",
|
||||
dataType: "json"
|
||||
statusCode:
|
||||
404: dataNotFound
|
||||
200: showData
|
||||
|
||||
initBinds = () ->
|
||||
$('#formCreateSecret').bind 'submit', createSecret
|
||||
$('#newSecret').bind 'click', newSecret
|
||||
|
||||
newSecret = () ->
|
||||
location.href = location.href.split('#')[0]
|
||||
false
|
||||
|
||||
secretCreated = (data) ->
|
||||
secretHash = data.secret_id
|
||||
if securePassword != null
|
||||
secretHash = "#{secretHash}|#{securePassword}"
|
||||
url = "#{location.href.split('#')[0]}##{secretHash}"
|
||||
|
||||
$('#panelNewSecret').hide()
|
||||
$('#panelSecretURL').show()
|
||||
$('#panelSecretURL').find('input').val url
|
||||
$('#panelSecretURL').find('input').focus()
|
||||
$('#panelSecretURL').find('input').select()
|
||||
|
||||
showData = (data) ->
|
||||
secret = data.secret
|
||||
if securePassword != null
|
||||
secret = GibberishAES.dec(secret, securePassword)
|
||||
|
||||
$('#panelNewSecret').hide()
|
||||
$('#panelReadSecret').show()
|
||||
$('#panelReadSecret').find('textarea').val secret
|
||||
|
||||
somethingWrong = () ->
|
||||
$('#somethingwrong').show()
|
||||
|
||||
|
||||
$ ->
|
||||
initBinds()
|
||||
hashLoad()
|
102
frontend/application.js
Normal file
102
frontend/application.js
Normal file
|
@ -0,0 +1,102 @@
|
|||
// Generated by CoffeeScript 1.12.4
|
||||
(function() {
|
||||
var createSecret, dataNotFound, hashLoad, initBinds, newSecret, secretCreated, securePassword, showData, somethingWrong;
|
||||
|
||||
securePassword = null;
|
||||
|
||||
createSecret = function() {
|
||||
var secret;
|
||||
secret = $('#formCreateSecret').find('textarea').val();
|
||||
if ($('#extra').prop('checked')) {
|
||||
securePassword = Math.random().toString(36).substring(2);
|
||||
secret = GibberishAES.enc(secret, securePassword);
|
||||
}
|
||||
$.ajax('api/create', {
|
||||
data: {
|
||||
secret: secret
|
||||
},
|
||||
dataType: "json",
|
||||
statusCode: {
|
||||
201: secretCreated,
|
||||
400: somethingWrong,
|
||||
500: somethingWrong,
|
||||
404: function() {
|
||||
return secretCreated({
|
||||
secret_id: 'foobar'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
return false;
|
||||
};
|
||||
|
||||
dataNotFound = function() {
|
||||
return $('#notfound').show();
|
||||
};
|
||||
|
||||
hashLoad = function() {
|
||||
var hash, id, parts;
|
||||
hash = window.location.hash;
|
||||
if (hash.length === 0) {
|
||||
return;
|
||||
}
|
||||
parts = hash.split('|');
|
||||
if (parts.length === 2) {
|
||||
hash = parts[0];
|
||||
securePassword = parts[1];
|
||||
}
|
||||
id = hash.substring(1);
|
||||
return $.ajax("api/get/" + id, {
|
||||
dataType: "json",
|
||||
statusCode: {
|
||||
404: dataNotFound,
|
||||
200: showData
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
initBinds = function() {
|
||||
$('#formCreateSecret').bind('submit', createSecret);
|
||||
return $('#newSecret').bind('click', newSecret);
|
||||
};
|
||||
|
||||
newSecret = function() {
|
||||
location.href = location.href.split('#')[0];
|
||||
return false;
|
||||
};
|
||||
|
||||
secretCreated = function(data) {
|
||||
var secretHash, url;
|
||||
secretHash = data.secret_id;
|
||||
if (securePassword !== null) {
|
||||
secretHash = secretHash + "|" + securePassword;
|
||||
}
|
||||
url = (location.href.split('#')[0]) + "#" + secretHash;
|
||||
$('#panelNewSecret').hide();
|
||||
$('#panelSecretURL').show();
|
||||
$('#panelSecretURL').find('input').val(url);
|
||||
$('#panelSecretURL').find('input').focus();
|
||||
return $('#panelSecretURL').find('input').select();
|
||||
};
|
||||
|
||||
showData = function(data) {
|
||||
var secret;
|
||||
secret = data.secret;
|
||||
if (securePassword !== null) {
|
||||
secret = GibberishAES.dec(secret, securePassword);
|
||||
}
|
||||
$('#panelNewSecret').hide();
|
||||
$('#panelReadSecret').show();
|
||||
return $('#panelReadSecret').find('textarea').val(secret);
|
||||
};
|
||||
|
||||
somethingWrong = function() {
|
||||
return $('#somethingwrong').show();
|
||||
};
|
||||
|
||||
$(function() {
|
||||
initBinds();
|
||||
return hashLoad();
|
||||
});
|
||||
|
||||
}).call(this);
|
138
frontend/index.html
Normal file
138
frontend/index.html
Normal file
|
@ -0,0 +1,138 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
|
||||
<title>OTS - One Time Secrets</title>
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.7/flatly/bootstrap.min.css"
|
||||
integrity="sha256-r1WijW/SNMgOwk5LDk7QRHr6oVYYbYWMw/1kOXfYJfg=" crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
|
||||
integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" />
|
||||
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"
|
||||
integrity="sha256-3Jy/GbSLrg0o9y5Z5n1uw0qxZECH7C6OQpVBgNFYa0g=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"
|
||||
integrity="sha256-g6iAfvZp+nDQ2TdTR/VVKJf3bGro4ub5fvWSWVRi2NE=" crossorigin="anonymous"></script>
|
||||
<![endif]-->
|
||||
|
||||
<style>
|
||||
#somethingwrong, #notfound, #panelReadSecret, #panelSecretURL {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container-fluid">
|
||||
<!-- Brand and toggle get grouped for better mobile display -->
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="#">OTS - One Time Secrets</a>
|
||||
</div>
|
||||
|
||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li><a href="#" id="newSecret"><i class="fa fa-plus"></i> New Secret</a></li>
|
||||
</ul>
|
||||
</div><!-- /.navbar-collapse -->
|
||||
</div><!-- /.container-fluid -->
|
||||
</nav>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-md-push-2">
|
||||
<div class="alert alert-danger" role="alert" id="notfound">
|
||||
<i class="fa fa-question-circle" aria-hidden="true"></i>
|
||||
This is not the secret you are looking for…
|
||||
</div>
|
||||
<div class="alert alert-danger" role="alert" id="somethingwrong">
|
||||
<i class="fa fa-question-circle" aria-hidden="true"></i>
|
||||
Something went wrong. I'm very sorry about this…
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
||||
<div class="panel panel-primary" id="panelNewSecret">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Create a new secret</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form id="formCreateSecret">
|
||||
<div class="form-group">
|
||||
<label for="secret">Secret data:</label>
|
||||
<textarea class="form-control" rows="5" id="secret"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="extra-secure" id="extra" checked>
|
||||
<label for="extra">Make it extra secure (encrypt it before sending to the server)</label>
|
||||
</div>
|
||||
<input class="btn btn-success" type="submit" value="Create the secret!">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-success" id="panelSecretURL">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Secret created!</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
Your secret was created and stored using this URL:
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" readonly>
|
||||
</div>
|
||||
<p>
|
||||
Please remember not to go to this URL yourself as that would destroy the secret. Just pass it to someone else!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-primary" id="panelReadSecret">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Reading your secret...</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" rows="5" readonly></textarea>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<strong>Attention:</strong> You're only seeing this once. As soon as you reload the page the secret will be gone so maybe copy it now…
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"
|
||||
integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>
|
||||
<!-- Include all compiled plugins (below), or include individual files as needed -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"
|
||||
integrity="sha256-U5ZEeKfGNOja007MMD3YBI0A3OSZOQbeG6z2f2Y0hu8=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gibberish-aes/1.0.0/gibberish-aes.min.js"
|
||||
integrity="sha256-H9B/XRDijNiF3agVjtiz1ILVi3csEdGz9YBg5TjWouM=" crossorigin="anonymous"></script>
|
||||
|
||||
<script src="application.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
26
main.go
26
main.go
|
@ -1,10 +1,16 @@
|
|||
package main
|
||||
|
||||
//go:generate go-bindata -pkg $GOPACKAGE -o assets.go ./frontend
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mime"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
http_helpers "github.com/Luzifer/go_helpers/http"
|
||||
"github.com/Luzifer/rconfig"
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
@ -48,6 +54,24 @@ func main() {
|
|||
|
||||
r := mux.NewRouter()
|
||||
api.Register(r.PathPrefix("/api").Subrouter())
|
||||
r.PathPrefix("/").HandlerFunc(assetDelivery)
|
||||
|
||||
log.Fatalf("HTTP server quit: %s", http.ListenAndServe(cfg.Listen, r))
|
||||
log.Fatalf("HTTP server quit: %s", http.ListenAndServe(cfg.Listen, http_helpers.NewHTTPLogHandler(r)))
|
||||
}
|
||||
|
||||
func assetDelivery(res http.ResponseWriter, r *http.Request) {
|
||||
assetName := r.URL.Path
|
||||
if assetName == "/" {
|
||||
assetName = "/index.html"
|
||||
}
|
||||
|
||||
ext := assetName[strings.LastIndex(assetName, "."):len(assetName)]
|
||||
assetData, err := Asset(path.Join("frontend", assetName))
|
||||
if err != nil {
|
||||
http.Error(res, "404 not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
res.Header().Set("Content-Type", mime.TypeByExtension(ext))
|
||||
res.Write(assetData)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue