base frontend website with authentication
This commit is contained in:
parent
507e941540
commit
4c033ac86c
|
@ -0,0 +1,45 @@
|
||||||
|
(defproject toaster "0.1.0-SNAPSHOT"
|
||||||
|
:description "Basic compojure based authenticated website"
|
||||||
|
:url "http://dyne.org"
|
||||||
|
:min-lein-version "2.0.0"
|
||||||
|
:dependencies [[org.clojure/clojure "1.9.0"]
|
||||||
|
[org.clojure/data.csv "0.1.4"]
|
||||||
|
[compojure "1.6.1"]
|
||||||
|
[ring/ring-defaults "0.3.2"]
|
||||||
|
[ring-middleware-accept "2.0.3"]
|
||||||
|
;; mustache templates
|
||||||
|
[de.ubercode.clostache/clostache "1.4.0"]
|
||||||
|
;; error handling
|
||||||
|
[failjure "1.3.0"]
|
||||||
|
;; logging done right with timbre
|
||||||
|
[com.taoensso/timbre "4.10.0"]
|
||||||
|
;; authentication library
|
||||||
|
[org.clojars.dyne/just-auth "0.4.0"]
|
||||||
|
;; web forms made easy
|
||||||
|
[formidable "0.1.10"]
|
||||||
|
;; parsing configs if any
|
||||||
|
[io.forward/yaml "1.0.9"]
|
||||||
|
;; Data validation
|
||||||
|
[prismatic/schema "1.1.9"]
|
||||||
|
;; filesystem utilities
|
||||||
|
[me.raynes/fs "1.4.6"]
|
||||||
|
;; time from joda-time
|
||||||
|
[clj-time "0.14.4"]]
|
||||||
|
:aliases {"test" "midje"}
|
||||||
|
:source-paths ["src"]
|
||||||
|
:resource-paths ["resources"]
|
||||||
|
:plugins [[lein-ring "0.12.4"]]
|
||||||
|
:ring {:init toaster.ring/init
|
||||||
|
:handler toaster.handler/app}
|
||||||
|
:uberwar {:init toaster.ring/init
|
||||||
|
:handler toaster.handler/app}
|
||||||
|
:mail toaster.handler
|
||||||
|
:profiles { :dev {:dependencies [[javax.servlet/servlet-api "2.5"]
|
||||||
|
[ring/ring-mock "0.3.2"]
|
||||||
|
[midje "1.9.2"]]
|
||||||
|
:plugins [[lein-midje "3.1.3"]]
|
||||||
|
:aot :all
|
||||||
|
:main toaster.handler}
|
||||||
|
:uberjar {:aot :all
|
||||||
|
:main toaster.handler}}
|
||||||
|
)
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
init:
|
||||||
|
success: "Initialisation completed"
|
||||||
|
failure: "Initialization failed"
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,5 @@
|
||||||
|
/*!
|
||||||
|
* Font Awesome Free 5.0.6 by @fontawesome - http://fontawesome.com
|
||||||
|
* License - http://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||||
|
*/
|
||||||
|
@font-face{font-family:Font Awesome\ 5 Free;font-style:normal;font-weight:400;src:url(/static/fonts/fa-regular-400.eot);src:url(/static/fonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(/static/fonts/fa-regular-400.woff2) format("woff2"),url(/static/fonts/fa-regular-400.woff) format("woff"),url(/static/fonts/fa-regular-400.ttf) format("truetype"),url(/static/fonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-family:Font Awesome\ 5 Free;font-weight:400}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,46 @@
|
||||||
|
.jsondiffpatch-annotated-delta {
|
||||||
|
font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, Courier, monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 0 0 12px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-annotated-delta pre {
|
||||||
|
font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, Courier, monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-annotated-delta td {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-annotated-delta td pre:hover {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
td.jsondiffpatch-delta-note {
|
||||||
|
font-style: italic;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-delta-note > div {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-delta-note pre {
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-annotated-delta .jsondiffpatch-delta-note {
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-annotated-delta tr:hover {
|
||||||
|
background: #ffc;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-annotated-delta tr:hover > td.jsondiffpatch-delta-note {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-error {
|
||||||
|
background: red;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
.jsondiffpatch-delta {
|
||||||
|
font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, Courier, monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 0 0 12px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-delta pre {
|
||||||
|
font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, Courier, monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
ul.jsondiffpatch-delta {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0 0 0 20px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-delta ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0 0 0 20px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-added .jsondiffpatch-property-name,
|
||||||
|
.jsondiffpatch-added .jsondiffpatch-value pre,
|
||||||
|
.jsondiffpatch-modified .jsondiffpatch-right-value pre,
|
||||||
|
.jsondiffpatch-textdiff-added {
|
||||||
|
background: #bbffbb;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-deleted .jsondiffpatch-property-name,
|
||||||
|
.jsondiffpatch-deleted pre,
|
||||||
|
.jsondiffpatch-modified .jsondiffpatch-left-value pre,
|
||||||
|
.jsondiffpatch-textdiff-deleted {
|
||||||
|
background: #ffbbbb;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-unchanged,
|
||||||
|
.jsondiffpatch-movedestination {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-unchanged,
|
||||||
|
.jsondiffpatch-movedestination > .jsondiffpatch-value {
|
||||||
|
transition: all 0.5s;
|
||||||
|
-webkit-transition: all 0.5s;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-unchanged-showing .jsondiffpatch-unchanged,
|
||||||
|
.jsondiffpatch-unchanged-showing .jsondiffpatch-movedestination > .jsondiffpatch-value {
|
||||||
|
max-height: 100px;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-unchanged-hidden .jsondiffpatch-unchanged,
|
||||||
|
.jsondiffpatch-unchanged-hidden .jsondiffpatch-movedestination > .jsondiffpatch-value {
|
||||||
|
max-height: 0;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-unchanged-hiding .jsondiffpatch-movedestination > .jsondiffpatch-value,
|
||||||
|
.jsondiffpatch-unchanged-hidden .jsondiffpatch-movedestination > .jsondiffpatch-value {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-unchanged-visible .jsondiffpatch-unchanged,
|
||||||
|
.jsondiffpatch-unchanged-visible .jsondiffpatch-movedestination > .jsondiffpatch-value {
|
||||||
|
max-height: 100px;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-unchanged-hiding .jsondiffpatch-unchanged,
|
||||||
|
.jsondiffpatch-unchanged-hiding .jsondiffpatch-movedestination > .jsondiffpatch-value {
|
||||||
|
max-height: 0;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-unchanged-showing .jsondiffpatch-arrow,
|
||||||
|
.jsondiffpatch-unchanged-hiding .jsondiffpatch-arrow {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-value {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-property-name {
|
||||||
|
display: inline-block;
|
||||||
|
padding-right: 5px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-property-name:after {
|
||||||
|
content: ': ';
|
||||||
|
}
|
||||||
|
.jsondiffpatch-child-node-type-array > .jsondiffpatch-property-name:after {
|
||||||
|
content: ': [';
|
||||||
|
}
|
||||||
|
.jsondiffpatch-child-node-type-array:after {
|
||||||
|
content: '],';
|
||||||
|
}
|
||||||
|
div.jsondiffpatch-child-node-type-array:before {
|
||||||
|
content: '[';
|
||||||
|
}
|
||||||
|
div.jsondiffpatch-child-node-type-array:after {
|
||||||
|
content: ']';
|
||||||
|
}
|
||||||
|
.jsondiffpatch-child-node-type-object > .jsondiffpatch-property-name:after {
|
||||||
|
content: ': {';
|
||||||
|
}
|
||||||
|
.jsondiffpatch-child-node-type-object:after {
|
||||||
|
content: '},';
|
||||||
|
}
|
||||||
|
div.jsondiffpatch-child-node-type-object:before {
|
||||||
|
content: '{';
|
||||||
|
}
|
||||||
|
div.jsondiffpatch-child-node-type-object:after {
|
||||||
|
content: '}';
|
||||||
|
}
|
||||||
|
.jsondiffpatch-value pre:after {
|
||||||
|
content: ',';
|
||||||
|
}
|
||||||
|
li:last-child > .jsondiffpatch-value pre:after,
|
||||||
|
.jsondiffpatch-modified > .jsondiffpatch-left-value pre:after {
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
.jsondiffpatch-modified .jsondiffpatch-value {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-modified .jsondiffpatch-right-value {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-moved .jsondiffpatch-value {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-moved .jsondiffpatch-moved-destination {
|
||||||
|
display: inline-block;
|
||||||
|
background: #ffffbb;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-moved .jsondiffpatch-moved-destination:before {
|
||||||
|
content: ' => ';
|
||||||
|
}
|
||||||
|
ul.jsondiffpatch-textdiff {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-textdiff-location {
|
||||||
|
color: #bbb;
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 60px;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-textdiff-line {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.jsondiffpatch-textdiff-line-number:after {
|
||||||
|
content: ',';
|
||||||
|
}
|
||||||
|
.jsondiffpatch-error {
|
||||||
|
background: red;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
|
||||||
|
|
||||||
|
/* Tomorrow Comment */
|
||||||
|
.hljs-comment,
|
||||||
|
.hljs-quote {
|
||||||
|
color: #8e908c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Red */
|
||||||
|
.hljs-variable,
|
||||||
|
.hljs-template-variable,
|
||||||
|
.hljs-tag,
|
||||||
|
.hljs-name,
|
||||||
|
.hljs-selector-id,
|
||||||
|
.hljs-selector-class,
|
||||||
|
.hljs-regexp,
|
||||||
|
.hljs-deletion {
|
||||||
|
color: #c82829;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Orange */
|
||||||
|
.hljs-number,
|
||||||
|
.hljs-built_in,
|
||||||
|
.hljs-builtin-name,
|
||||||
|
.hljs-literal,
|
||||||
|
.hljs-type,
|
||||||
|
.hljs-params,
|
||||||
|
.hljs-meta,
|
||||||
|
.hljs-link {
|
||||||
|
color: #f5871f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Yellow */
|
||||||
|
.hljs-attribute {
|
||||||
|
color: #eab700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Green */
|
||||||
|
.hljs-string,
|
||||||
|
.hljs-symbol,
|
||||||
|
.hljs-bullet,
|
||||||
|
.hljs-addition {
|
||||||
|
color: #718c00;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Blue */
|
||||||
|
.hljs-title,
|
||||||
|
.hljs-section {
|
||||||
|
color: #4271ae;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tomorrow Purple */
|
||||||
|
.hljs-keyword,
|
||||||
|
.hljs-selector-tag {
|
||||||
|
color: #8959a8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
background: white;
|
||||||
|
color: #4d4d4c;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-emphasis {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
/* css for [json-html.core] */
|
||||||
|
|
||||||
|
|
||||||
|
.jh-type-string, .jh-type-bool, .jh-type-number{
|
||||||
|
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 20px;
|
||||||
|
color: #333;
|
||||||
|
float: left;
|
||||||
|
width: 140px;
|
||||||
|
padding-top: 5px;
|
||||||
|
text-align: right;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-root, .jh-type-object, .jh-type-array, .jh-key, .jh-value, .jh-root tr{
|
||||||
|
-webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
|
||||||
|
-moz-box-sizing: border-box; /* Firefox, other Gecko */
|
||||||
|
box-sizing: border-box; /* Opera/IE 8+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-key, .jh-value{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-empty-collection:before {
|
||||||
|
content: '[]';
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-empty-map:before {
|
||||||
|
content: '{}';
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-empty-set:before {
|
||||||
|
content: '#{}';
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-empty-string:before {
|
||||||
|
content: '""';
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-value{
|
||||||
|
border-left: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-type-bool, .jh-type-number{
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-type-string{
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-type-date{
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-array-key{
|
||||||
|
font-size: small;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-object-key, .jh-array-key{
|
||||||
|
color: #444;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-type-object > tbody > tr:nth-child(odd), .jh-type-array > tbody > tr:nth-child(odd){
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-type-object > tbody > tr:nth-child(even), .jh-type-array > tbody > tr:nth-child(even){
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-type-object, .jh-type-array{
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-root{
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
margin: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
th.jh-key{
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-type-object > tbody > tr, .jh-type-array > tr{
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-type-object > tbody > tr:last-child, .jh-type-array > tbody > tr:last-child{
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-type-object > tbody > tr:hover, .jh-type-array > tbody > tr:hover{
|
||||||
|
border: 1px solid #F99927;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jh-empty{
|
||||||
|
color: #999;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
body {
|
||||||
|
font-family: "arial";
|
||||||
|
font-size: 22px;
|
||||||
|
font-smooth: always;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
padding-top: 65px;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-size: .8em;
|
||||||
|
/* display: block; */
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .btn { */
|
||||||
|
/* margin: 0 1.5em 0 1.5em; */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
.edit-project {
|
||||||
|
margin-top: 2em;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4 { }
|
||||||
|
|
||||||
|
p {
|
||||||
|
letter-spacing: 0.01rem;
|
||||||
|
font-style: normal;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
max-width: 100%;
|
||||||
|
vertical-align: middle;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sortable tables */
|
||||||
|
table.sortable thead {
|
||||||
|
background-color:#eee;
|
||||||
|
color:#666666;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.sortable td {
|
||||||
|
font-size: .8em;
|
||||||
|
font-family: "arial"
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-form {
|
||||||
|
clear: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
/* padding: .5em; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.secrets {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password {
|
||||||
|
font-size: 1.5;
|
||||||
|
padding: 1em;
|
||||||
|
/* display: inline-block; */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.navbar-text {
|
||||||
|
margin-top: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password .content {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slices {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.slices ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slices .content {
|
||||||
|
font-size: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode {
|
||||||
|
float: left;
|
||||||
|
margin-left: -19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.card {
|
||||||
|
border: solid 3px #888;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 1.5em 2.2em .8em 2.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card .gravatar {
|
||||||
|
margin-top: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.balance {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wallet-details {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,101 @@
|
||||||
|
;; Copyright (C) 2015-2018 Dyne.org foundation
|
||||||
|
|
||||||
|
;; Sourcecode designed, written and maintained by
|
||||||
|
;; Denis Roio <jaromil@dyne.org>
|
||||||
|
|
||||||
|
;; This program is free software: you can redistribute it and/or modify
|
||||||
|
;; it under the terms of the GNU Affero General Public License as published by
|
||||||
|
;; the Free Software Foundation, either version 3 of the License, or
|
||||||
|
;; (at your option) any later version.
|
||||||
|
|
||||||
|
;; This program is distributed in the hope that it will be useful,
|
||||||
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
;; GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
;; You should have received a copy of the GNU Affero General Public License
|
||||||
|
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
(ns toaster.config
|
||||||
|
(:require [clojure.pprint :refer [pprint]]
|
||||||
|
[clojure.string :refer [upper-case]]
|
||||||
|
[clojure.java.io :as io]
|
||||||
|
[clojure.walk :refer [keywordize-keys]]
|
||||||
|
[auxiliary.core :as aux]
|
||||||
|
[taoensso.timbre :as log]
|
||||||
|
[failjure.core :as f]
|
||||||
|
[schema.core :as s]
|
||||||
|
[yaml.core :as yaml]
|
||||||
|
[cheshire.core :refer :all]))
|
||||||
|
|
||||||
|
;; (s/defschema Config
|
||||||
|
;; {s/Keyword
|
||||||
|
;; (s/optional-key :webserver) {:port s/Num
|
||||||
|
;; :host s/Str
|
||||||
|
;; :anti-forgery s/Bool
|
||||||
|
;; :ssl-redirect s/Bool}
|
||||||
|
;; (s/optional-key :just-auth) {:email-server s/Str
|
||||||
|
;; :email-user s/Str
|
||||||
|
;; :email-pass s/Str
|
||||||
|
;; :email-address s/Str}
|
||||||
|
;; }
|
||||||
|
;; :appname s/Str
|
||||||
|
;; :paths [s/Str]
|
||||||
|
;; :filename s/Str
|
||||||
|
;; })
|
||||||
|
|
||||||
|
(def run-mode (atom :web))
|
||||||
|
|
||||||
|
(def default-settings {:webserver
|
||||||
|
{:anti-forgery false
|
||||||
|
:ssl-redirect false}})
|
||||||
|
|
||||||
|
(defn yaml-read [path]
|
||||||
|
(if (.exists (io/as-file path))
|
||||||
|
(-> path yaml/from-file keywordize-keys)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn- config-read
|
||||||
|
"Read configurations from standard locations, overriding defaults or
|
||||||
|
system-wide with user specific paths. Requires the application name
|
||||||
|
and optionally default values."
|
||||||
|
([appname] (config-read appname {}))
|
||||||
|
([appname defaults & flags]
|
||||||
|
(let [home (System/getenv "HOME")
|
||||||
|
pwd (System/getenv "PWD" )
|
||||||
|
file (str appname ".yaml")
|
||||||
|
conf (-> {:appname appname
|
||||||
|
:filename file
|
||||||
|
:paths [(str "/etc/" appname "/" file)
|
||||||
|
(str home "/." appname "/" file)
|
||||||
|
(str pwd "/" file)
|
||||||
|
;; TODO: this should be resources
|
||||||
|
(str pwd "/resources/" file)
|
||||||
|
(str pwd "/test-resources/" file)]}
|
||||||
|
(conj defaults))]
|
||||||
|
(loop [[p & paths] (:paths conf)
|
||||||
|
res defaults]
|
||||||
|
(let [res (merge res
|
||||||
|
(if (.exists (io/as-file p))
|
||||||
|
(conj res (yaml-read p))))]
|
||||||
|
(if (empty? paths) (conj conf {(keyword appname) res})
|
||||||
|
(recur paths res)))))))
|
||||||
|
|
||||||
|
(defn- spy "Print out a config structure nicely formatted"
|
||||||
|
[edn]
|
||||||
|
(if (log/may-log? :debug)
|
||||||
|
(binding [*out* *err*] (pprint edn)))
|
||||||
|
edn)
|
||||||
|
|
||||||
|
(defn q [conf path] ;; query a variable inside the config
|
||||||
|
{:pre [(coll? path)]}
|
||||||
|
;; (try ;; adds an extra check every time configuration is read
|
||||||
|
;; (s/validate Config conf)
|
||||||
|
;; (catch Exception ex
|
||||||
|
;; (f/fail (log/spy :error ["Invalid configuration: " conf ex]))))
|
||||||
|
(get-in conf path))
|
||||||
|
|
||||||
|
(defn load-config [name default]
|
||||||
|
(log/info (str "Loading configuration: " name))
|
||||||
|
(->> (config-read name default)))
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
;; Copyright (C) 2015-2018 Dyne.org foundation
|
||||||
|
|
||||||
|
;; Sourcecode designed, written and maintained by
|
||||||
|
;; Denis Roio <jaromil@dyne.org>
|
||||||
|
|
||||||
|
;; This program is free software: you can redistribute it and/or modify
|
||||||
|
;; it under the terms of the GNU Affero General Public License as published by
|
||||||
|
;; the Free Software Foundation, either version 3 of the License, or
|
||||||
|
;; (at your option) any later version.
|
||||||
|
|
||||||
|
;; This program is distributed in the hope that it will be useful,
|
||||||
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
;; GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
;; You should have received a copy of the GNU Affero General Public License
|
||||||
|
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
(ns toaster.handler
|
||||||
|
(:require
|
||||||
|
[clojure.string :as str]
|
||||||
|
[clojure.java.io :as io]
|
||||||
|
[clojure.data.json :as json]
|
||||||
|
[compojure.core :refer :all]
|
||||||
|
[compojure.handler :refer :all]
|
||||||
|
[compojure.route :as route]
|
||||||
|
[compojure.response :as response]
|
||||||
|
|
||||||
|
[ring.adapter.jetty :refer :all]
|
||||||
|
[ring.middleware.session :refer :all]
|
||||||
|
[ring.middleware.accept :refer [wrap-accept]]
|
||||||
|
[ring.middleware.defaults :refer [wrap-defaults site-defaults]]
|
||||||
|
|
||||||
|
[failjure.core :as f]
|
||||||
|
[taoensso.timbre :as log]
|
||||||
|
[just-auth.core :as auth]
|
||||||
|
|
||||||
|
[toaster.session :as s]
|
||||||
|
[toaster.config :as conf]
|
||||||
|
[toaster.webpage :as web]
|
||||||
|
[toaster.ring :as ring])
|
||||||
|
(:import java.io.File)
|
||||||
|
(:gen-class))
|
||||||
|
|
||||||
|
(defonce config (conf/load-config "toaster" conf/default-settings))
|
||||||
|
|
||||||
|
(defroutes app-routes
|
||||||
|
|
||||||
|
(GET "/" request (web/render "Hello World!"));; web/readme))
|
||||||
|
|
||||||
|
;; NEW ROUTES HERE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;; JUST-AUTH ROUTES
|
||||||
|
(GET "/login" request
|
||||||
|
(f/attempt-all
|
||||||
|
[acct (s/check-account request)]
|
||||||
|
(web/render acct
|
||||||
|
[:div
|
||||||
|
[:h1 (str "Already logged in with account: "
|
||||||
|
(:email acct))]
|
||||||
|
[:h2 [:a {:href "/logout"} "Logout"]]])
|
||||||
|
(f/when-failed [e]
|
||||||
|
(web/render web/login-form))))
|
||||||
|
(POST "/login" request
|
||||||
|
(f/attempt-all
|
||||||
|
[username (s/param request :username)
|
||||||
|
password (s/param request :password)
|
||||||
|
logged (auth/sign-in
|
||||||
|
@ring/auth username password {})]
|
||||||
|
;; TODO: pass :ip-address in last argument map
|
||||||
|
(let [session {:session {:config config
|
||||||
|
:auth logged}}]
|
||||||
|
(conj session
|
||||||
|
(web/render
|
||||||
|
logged
|
||||||
|
[:div
|
||||||
|
[:h1 "Logged in: " username]
|
||||||
|
(web/render-yaml session)])))
|
||||||
|
(f/when-failed [e]
|
||||||
|
(web/render-error-page
|
||||||
|
(str "Login failed: " (f/message e))))))
|
||||||
|
(GET "/session" request
|
||||||
|
(-> (:session request) web/render-yaml web/render))
|
||||||
|
(GET "/logout" request
|
||||||
|
(conj {:session {:config config}}
|
||||||
|
(web/render [:h1 "Logged out."])))
|
||||||
|
(GET "/signup" request
|
||||||
|
(web/render web/signup-form))
|
||||||
|
(POST "/signup" request
|
||||||
|
(f/attempt-all
|
||||||
|
[name (s/param request :name)
|
||||||
|
email (s/param request :email)
|
||||||
|
password (s/param request :password)
|
||||||
|
repeat-password (s/param request :repeat-password)
|
||||||
|
activation {:activation-uri
|
||||||
|
(get-in request [:headers "host"])}]
|
||||||
|
(web/render
|
||||||
|
(if (= password repeat-password)
|
||||||
|
(f/try*
|
||||||
|
(f/if-let-ok?
|
||||||
|
[signup (auth/sign-up @ring/auth
|
||||||
|
name
|
||||||
|
email
|
||||||
|
password
|
||||||
|
activation
|
||||||
|
[])]
|
||||||
|
[:div
|
||||||
|
[:h2 (str "Account created: "
|
||||||
|
name " <" email ">")]
|
||||||
|
[:h3 "Account pending activation."]]
|
||||||
|
(web/render-error
|
||||||
|
(str "Failure creating account: "
|
||||||
|
(f/message signup)))))
|
||||||
|
(web/render-error
|
||||||
|
"Repeat password didnt match")))
|
||||||
|
(f/when-failed [e]
|
||||||
|
(web/render-error-page
|
||||||
|
(str "Sign-up failure: " (f/message e))))))
|
||||||
|
(GET "/activate/:email/:activation-id"
|
||||||
|
[email activation-id :as request]
|
||||||
|
(let [activation-uri
|
||||||
|
(str "http://"
|
||||||
|
(get-in request [:headers "host"])
|
||||||
|
"/activate/" email "/" activation-id)]
|
||||||
|
(web/render
|
||||||
|
[:div
|
||||||
|
(f/if-let-failed?
|
||||||
|
[act (auth/activate-account
|
||||||
|
@ring/auth email
|
||||||
|
{:activation-link activation-uri})]
|
||||||
|
(web/render-error
|
||||||
|
[:div
|
||||||
|
[:h1 "Failure activating account"]
|
||||||
|
[:h2 (f/message act)]
|
||||||
|
[:p (str "Email: " email " activation-id: " activation-id)]])
|
||||||
|
[:h1 (str "Account activated - " email)])])))
|
||||||
|
;; -- end of JUST-AUTH
|
||||||
|
|
||||||
|
(POST "/" request
|
||||||
|
;; generic endpoint for canceled operations
|
||||||
|
(web/render (s/check-account request)
|
||||||
|
[:div {:class (str "alert alert-danger") :role "alert"}
|
||||||
|
(s/param request :message)]))
|
||||||
|
|
||||||
|
(route/resources "/")
|
||||||
|
(route/not-found (web/render-error-page "Page Not Found"))
|
||||||
|
) ;; end of routes
|
||||||
|
|
||||||
|
(def app
|
||||||
|
(-> (wrap-defaults app-routes ring/app-defaults)
|
||||||
|
(wrap-accept {:mime ["text/html"]
|
||||||
|
;; preference in language, fallback to english
|
||||||
|
:language ["en" :qs 0.5
|
||||||
|
"it" :qs 1
|
||||||
|
"nl" :qs 1
|
||||||
|
"hr" :qs 1]})
|
||||||
|
(wrap-session)))
|
||||||
|
|
||||||
|
;; for uberjar
|
||||||
|
(defn -main []
|
||||||
|
(println "Starting standalone jetty server on http://localhost:6060")
|
||||||
|
(run-jetty app {:port 6060
|
||||||
|
:host "localhost"
|
||||||
|
:join? true}))
|
|
@ -0,0 +1,93 @@
|
||||||
|
;; Copyright (C) 2015-2017 Dyne.org foundation
|
||||||
|
|
||||||
|
;; Sourcecode designed, written and maintained by
|
||||||
|
;; Denis Roio <jaromil@dyne.org>
|
||||||
|
|
||||||
|
;; This program is free software: you can redistribute it and/or modify
|
||||||
|
;; it under the terms of the GNU Affero General Public License as published by
|
||||||
|
;; the Free Software Foundation, either version 3 of the License, or
|
||||||
|
;; (at your option) any later version.
|
||||||
|
|
||||||
|
;; This program is distributed in the hope that it will be useful,
|
||||||
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
;; GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
;; You should have received a copy of the GNU Affero General Public License
|
||||||
|
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
(ns toaster.ring
|
||||||
|
(:require
|
||||||
|
[clojure.java.io :as io]
|
||||||
|
[toaster.config :as conf]
|
||||||
|
[taoensso.timbre :as log]
|
||||||
|
[failjure.core :as f]
|
||||||
|
[clj-storage.db.mongo :refer [get-mongo-db create-mongo-store]]
|
||||||
|
[just-auth.core :as auth]
|
||||||
|
[just-auth.db.just-auth :as auth-db]
|
||||||
|
|
||||||
|
[auxiliary.translation :as trans]
|
||||||
|
[compojure.core :refer :all]
|
||||||
|
[compojure.handler :refer :all]
|
||||||
|
[ring.middleware.session :refer :all]
|
||||||
|
[ring.middleware.accept :refer [wrap-accept]]
|
||||||
|
[ring.middleware.defaults :refer [wrap-defaults site-defaults]]))
|
||||||
|
|
||||||
|
(def config (atom {}))
|
||||||
|
(def db (atom {}))
|
||||||
|
(def accts (atom {}))
|
||||||
|
(def auth (atom {}))
|
||||||
|
|
||||||
|
(defn init []
|
||||||
|
(log/merge-config! {:level :debug
|
||||||
|
;; #{:trace :debug :info :warn :error :fatal :report}
|
||||||
|
|
||||||
|
;; Control log filtering by
|
||||||
|
;; namespaces/patterns. Useful for turning off
|
||||||
|
;; logging in noisy libraries, etc.:
|
||||||
|
;; :ns-whitelist ["agiladmin.*" "just-auth.*"]
|
||||||
|
:ns-blacklist ["org.eclipse.jetty.*"
|
||||||
|
"org.mongodb.driver.cluster"]})
|
||||||
|
|
||||||
|
;; load configuration
|
||||||
|
(reset! config (conf/load-config
|
||||||
|
(or (System/getenv "toaster_conf") "toaster")
|
||||||
|
conf/default-settings))
|
||||||
|
|
||||||
|
(let [justauth-conf (get-in @config [:toaster :just-auth])]
|
||||||
|
;; connect database (TODO: take parameters from configuration)
|
||||||
|
(reset! db (get-mongo-db (:mongo-url justauth-conf)))
|
||||||
|
|
||||||
|
;; create authentication stores in db
|
||||||
|
(f/attempt-all
|
||||||
|
[auth-conf (get-in @config [:toaster :just-auth])
|
||||||
|
auth-stores (auth-db/create-auth-stores @db)]
|
||||||
|
|
||||||
|
[(trans/init "lang/auth-en.yml" "lang/english.yaml")
|
||||||
|
(reset! accts auth-stores)
|
||||||
|
(reset! auth (auth/email-based-authentication
|
||||||
|
auth-stores
|
||||||
|
;; TODO: replace with email taken from config
|
||||||
|
(dissoc (:just-auth (:toaster (conf/load-config
|
||||||
|
"toaster" conf/default-settings)))
|
||||||
|
:mongo-url :mongo-user :mongo-pass)
|
||||||
|
{:criteria #{:email :ip-address}
|
||||||
|
:type :block
|
||||||
|
:time-window-secs 10
|
||||||
|
:threshold 5}))]
|
||||||
|
;; (select-keys auth-stores [:account-store
|
||||||
|
;; :password-recovery-store])
|
||||||
|
(f/when-failed [e]
|
||||||
|
(log/error (str (trans/locale [:init :failure])
|
||||||
|
" - " (f/message e))))))
|
||||||
|
(log/info (str (trans/locale [:init :success])))
|
||||||
|
(log/debug @auth))
|
||||||
|
|
||||||
|
(def app-defaults
|
||||||
|
(-> site-defaults
|
||||||
|
(assoc-in [:cookies] true)
|
||||||
|
(assoc-in [:security :anti-forgery]
|
||||||
|
(get-in @config [:webserver :anti-forgery]))
|
||||||
|
(assoc-in [:security :ssl-redirect]
|
||||||
|
(get-in @config [:webserver :ssl-redirect]))
|
||||||
|
(assoc-in [:security :hsts] true)))
|
|
@ -0,0 +1,60 @@
|
||||||
|
(ns toaster.session
|
||||||
|
(:refer-clojure :exclude [get])
|
||||||
|
(:require
|
||||||
|
[toaster.config :as conf]
|
||||||
|
[taoensso.timbre :as log]
|
||||||
|
[failjure.core :as f]
|
||||||
|
[just-auth.core :as auth]
|
||||||
|
[toaster.ring :as ring]
|
||||||
|
[toaster.webpage :as web]))
|
||||||
|
|
||||||
|
(defn param [request param]
|
||||||
|
(let [value
|
||||||
|
(get-in request
|
||||||
|
(conj [:params] param))]
|
||||||
|
(if (nil? value)
|
||||||
|
(f/fail (str "Parameter not found: " param))
|
||||||
|
value)))
|
||||||
|
|
||||||
|
;; TODO: not working?
|
||||||
|
(defn get [req arrk]
|
||||||
|
{:pre (coll? arrk)}
|
||||||
|
(if-let [value (get-in req (conj [:session] arrk))]
|
||||||
|
value
|
||||||
|
(f/fail (str "Value not found in session: " (str arrk)))))
|
||||||
|
|
||||||
|
(defn check-config [request]
|
||||||
|
;; reload configuration from file all the time if in debug mode
|
||||||
|
(if-let [session (:session request)]
|
||||||
|
(if (contains? session :config)
|
||||||
|
(:config session)
|
||||||
|
(conf/load-config "toaster" conf/default-settings))
|
||||||
|
(f/fail "Session not found. ")))
|
||||||
|
|
||||||
|
(defn check-account [request]
|
||||||
|
;; check if login is present in session
|
||||||
|
(f/attempt-all
|
||||||
|
[login (get-in request [:session :auth :email])
|
||||||
|
user (auth/get-account @ring/auth login)]
|
||||||
|
user
|
||||||
|
(f/when-failed [e]
|
||||||
|
(->> e f/message
|
||||||
|
(str "Unauthorized access. ")
|
||||||
|
f/fail))))
|
||||||
|
|
||||||
|
(defn check-database []
|
||||||
|
(if-let [db @ring/db]
|
||||||
|
db
|
||||||
|
(f/fail "No connection to database. ")))
|
||||||
|
|
||||||
|
(defn check [request fun]
|
||||||
|
(f/attempt-all
|
||||||
|
[db (check-database)
|
||||||
|
config (check-config request)
|
||||||
|
account (check-account request)]
|
||||||
|
(fun request config account)
|
||||||
|
(f/when-failed [e]
|
||||||
|
(web/render
|
||||||
|
[:div
|
||||||
|
(web/render-error (f/message e))
|
||||||
|
web/login-form]))))
|
|
@ -0,0 +1,343 @@
|
||||||
|
;; Copyright (C) 2015-2018 Dyne.org foundation
|
||||||
|
|
||||||
|
;; Sourcecode designed, written and maintained by
|
||||||
|
;; Denis Roio <jaromil@dyne.org>
|
||||||
|
|
||||||
|
;; This program is free software: you can redistribute it and/or modify
|
||||||
|
;; it under the terms of the GNU Affero General Public License as published by
|
||||||
|
;; the Free Software Foundation, either version 3 of the License, or
|
||||||
|
;; (at your option) any later version.
|
||||||
|
|
||||||
|
;; This program is distributed in the hope that it will be useful,
|
||||||
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
;; GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
;; You should have received a copy of the GNU Affero General Public License
|
||||||
|
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
(ns toaster.webpage
|
||||||
|
(:require [clojure.java.io :as io]
|
||||||
|
[clojure.data.json :as json]
|
||||||
|
[clojure.data.csv :as csv]
|
||||||
|
[yaml.core :as yaml]
|
||||||
|
[toaster.config :as conf]
|
||||||
|
[taoensso.timbre :as log]
|
||||||
|
[failjure.core :as f]
|
||||||
|
[toaster.ring :as ring]
|
||||||
|
[hiccup.page :as page]
|
||||||
|
[hiccup.form :as hf]))
|
||||||
|
|
||||||
|
(declare render)
|
||||||
|
(declare render-head)
|
||||||
|
(declare navbar-guest)
|
||||||
|
(declare navbar-account)
|
||||||
|
(declare render-footer)
|
||||||
|
(declare render-yaml)
|
||||||
|
(declare render-edn)
|
||||||
|
(declare render-error)
|
||||||
|
(declare render-error-page)
|
||||||
|
(declare render-static)
|
||||||
|
|
||||||
|
(defn q [req]
|
||||||
|
"wrapper to retrieve parameters"
|
||||||
|
;; TODO: sanitise and check for irregular chars
|
||||||
|
(get-in req (conj [:params] req)))
|
||||||
|
|
||||||
|
(defn button
|
||||||
|
([url text] (button url text [:p]))
|
||||||
|
|
||||||
|
([url text field] (button url text field "btn-secondary btn-lg"))
|
||||||
|
|
||||||
|
([url text field type]
|
||||||
|
(hf/form-to [:post url]
|
||||||
|
field ;; can be an hidden key/value field (project,
|
||||||
|
;; person, etc using hf/hidden-field)
|
||||||
|
(hf/submit-button {:class (str "btn " type)} text))))
|
||||||
|
|
||||||
|
(defn button-cancel-submit [argmap]
|
||||||
|
[:div
|
||||||
|
{:class
|
||||||
|
(str "row col-md-6 btn-group btn-group-lg "
|
||||||
|
(:btn-group-class argmap))
|
||||||
|
:role "group"}
|
||||||
|
(button
|
||||||
|
(:cancel-url argmap) "Cancel"
|
||||||
|
(:cancel-params argmap)
|
||||||
|
"btn-primary btn-lg btn-danger col-md-3")
|
||||||
|
(button
|
||||||
|
(:submit-url argmap) "Submit"
|
||||||
|
(:submit-params argmap)
|
||||||
|
"btn-primary btn-lg btn-success col-md-3")])
|
||||||
|
|
||||||
|
|
||||||
|
(defn reload-session [request]
|
||||||
|
;; TODO: validation of all data loaded via prismatic schema
|
||||||
|
(conf/load-config "toaster" conf/default-settings)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
(defn render
|
||||||
|
([body]
|
||||||
|
{:headers {"Content-Type"
|
||||||
|
"text/html; charset=utf-8"}
|
||||||
|
:body (page/html5
|
||||||
|
(render-head)
|
||||||
|
[:body ;; {:class "static"}
|
||||||
|
navbar-guest
|
||||||
|
[:div {:class "container-fluid"} body]
|
||||||
|
(render-footer)])})
|
||||||
|
([account body]
|
||||||
|
{:headers {"Content-Type"
|
||||||
|
"text/html; charset=utf-8"}
|
||||||
|
:body (page/html5
|
||||||
|
(render-head)
|
||||||
|
[:body (if (empty? account)
|
||||||
|
navbar-guest
|
||||||
|
navbar-account)
|
||||||
|
[:div {:class "container-fluid"} body]
|
||||||
|
(render-footer)])}))
|
||||||
|
|
||||||
|
|
||||||
|
(defn render-error
|
||||||
|
"render an error message without ending the page"
|
||||||
|
[err]
|
||||||
|
[:div {:class "alert alert-danger" :role "alert"}
|
||||||
|
[:span {:class "far fa-meh"
|
||||||
|
:aria-hidden "true" :style "padding: .5em"}]
|
||||||
|
[:span {:class "sr-only"} "Error:" ]
|
||||||
|
err])
|
||||||
|
|
||||||
|
(defn render-error-page
|
||||||
|
([] (render-error-page {} "Unknown"))
|
||||||
|
([err] (render-error-page {} err))
|
||||||
|
([session error]
|
||||||
|
(render
|
||||||
|
[:div {:class "container-fluid"}
|
||||||
|
(render-error error)
|
||||||
|
(if-not (empty? session)
|
||||||
|
[:div {:class "config"}
|
||||||
|
[:h2 "Environment dump:"]
|
||||||
|
(render-yaml session)])])))
|
||||||
|
|
||||||
|
|
||||||
|
(defn render-head
|
||||||
|
([] (render-head
|
||||||
|
"toaster" ;; default title
|
||||||
|
"toaster"
|
||||||
|
"https://toaster.dyne.org")) ;; default desc
|
||||||
|
|
||||||
|
([title desc url]
|
||||||
|
[:head [:meta {:charset "utf-8"}]
|
||||||
|
[:meta {:http-equiv "X-UA-Compatible" :content "IE=edge,chrome=1"}]
|
||||||
|
[:meta
|
||||||
|
{:name "viewport"
|
||||||
|
:content "width=device-width, initial-scale=1, maximum-scale=1"}]
|
||||||
|
|
||||||
|
[:title title]
|
||||||
|
|
||||||
|
;; javascript scripts
|
||||||
|
(page/include-js "/static/js/jquery-3.2.1.min.js")
|
||||||
|
(page/include-js "/static/js/bootstrap.min.js")
|
||||||
|
|
||||||
|
;; cascade style sheets
|
||||||
|
(page/include-css "/static/css/bootstrap.min.css")
|
||||||
|
(page/include-css "/static/css/json-html.css")
|
||||||
|
(page/include-css "/static/css/highlight-tomorrow.css")
|
||||||
|
(page/include-css "/static/css/formatters-styles/html.css")
|
||||||
|
(page/include-css "/static/css/formatters-styles/annotated.css")
|
||||||
|
(page/include-css "/static/css/fa-regular.min.css")
|
||||||
|
(page/include-css "/static/css/fontawesome.min.css")
|
||||||
|
(page/include-css "/static/css/toaster.css")]))
|
||||||
|
|
||||||
|
(def navbar-guest
|
||||||
|
[:nav
|
||||||
|
{:class "navbar navbar-default navbar-fixed-top navbar-expand-md navbar-expand-lg"}
|
||||||
|
[:div {:class "navbar-header"}
|
||||||
|
[:button {:class "navbar-toggle" :type "button"
|
||||||
|
:data-toggle "collapse"
|
||||||
|
:data-target "#navbarResponsive"
|
||||||
|
:aria-controls "navbarResponsive"
|
||||||
|
:aria-expanded "false"
|
||||||
|
:aria-label "Toggle navigation"}
|
||||||
|
[:span {:class "sr-only"} "Toggle navigation"]
|
||||||
|
[:span {:class "icon-bar"}]
|
||||||
|
[:span {:class "icon-bar"}]
|
||||||
|
[:span {:class "icon-bar"}]]
|
||||||
|
[:a {:class "navbar-brand far fa-handshake" :href "/"} "toaster"]]
|
||||||
|
[:div {:class "collapse navbar-collapse" :id "navbarResponsive"}
|
||||||
|
[:ul {:class "nav navbar-nav hidden-sm hidden-md ml-auto"}
|
||||||
|
;; --
|
||||||
|
[:li {:class "divider" :role "separator"}]
|
||||||
|
[:li {:class "nav-item"}
|
||||||
|
[:a {:class "nav-link far fa-address-card"
|
||||||
|
:href "/login"} " Login"]]
|
||||||
|
]]])
|
||||||
|
|
||||||
|
(def navbar-account
|
||||||
|
[:nav {:class "navbar navbar-default navbar-fixed-top navbar-expand-lg"}
|
||||||
|
|
||||||
|
[:div {:class "navbar-header"}
|
||||||
|
[:button {:class "navbar-toggle" :type "button"
|
||||||
|
:data-toggle "collapse" :data-target "#navbarResponsive"
|
||||||
|
:aria-controls "navbarResponsive" :aria-expanded "false"
|
||||||
|
:aria-label "Toggle navigation"}
|
||||||
|
[:span {:class "sr-only"} "Toggle navigation"]
|
||||||
|
[:span {:class "icon-bar"}]
|
||||||
|
[:span {:class "icon-bar"}]
|
||||||
|
[:span {:class "icon-bar"}]]
|
||||||
|
[:a {:class "navbar-brand far fa-handshake" :href "/"} "toaster"]]
|
||||||
|
|
||||||
|
[:div {:class "collapse navbar-collapse" :id "navbarResponsive"}
|
||||||
|
[:ul {:class "nav navbar-nav hidden-sm hidden-md ml-auto"}
|
||||||
|
;; --
|
||||||
|
[:li {:class "divider" :role "separator"}]
|
||||||
|
;; LIST OF RELEVANT LINKS AFTER LOGIN
|
||||||
|
;; [:li {:class "nav-item"}
|
||||||
|
;; [:a {:class "nav-link far fa-address-card"
|
||||||
|
;; :href "/persons/list"} " Persons"]]
|
||||||
|
;; [:li {:class "nav-item"}
|
||||||
|
;; [:a {:class "nav-link far fa-paper-plane"
|
||||||
|
;; :href "/projects/list"} " Projects"]]
|
||||||
|
;; [:li {:class "nav-item"}
|
||||||
|
;; [:a {:class "nav-link far fa-plus-square"
|
||||||
|
;; :href "/timesheets"} " Upload"]]
|
||||||
|
;; [:li {:class "nav-item"}
|
||||||
|
;; [:a {:class "nav-link far fa-save"
|
||||||
|
;; :href "/reload"} " Reload"]]
|
||||||
|
;; --
|
||||||
|
[:li {:role "separator" :class "divider"} ]
|
||||||
|
[:li {:class "nav-item"}
|
||||||
|
[:a {:class "nav-link far fa-file-code"
|
||||||
|
:href "/config"} " Configuration"]]
|
||||||
|
]]])
|
||||||
|
|
||||||
|
(defn render-footer []
|
||||||
|
[:footer {:class "row" :style "margin: 20px"}
|
||||||
|
[:hr]
|
||||||
|
|
||||||
|
[:div {:class "footer col-lg-3"}
|
||||||
|
[:img {:src "/static/img/AGPLv3.png" :style "margin-top: 2.5em"
|
||||||
|
:alt "Affero GPLv3 License"
|
||||||
|
:title "Affero GPLv3 License"} ]]
|
||||||
|
|
||||||
|
[:div {:class "footer col-lg-3"}
|
||||||
|
[:a {:href "https://www.dyne.org"}
|
||||||
|
[:img {:src "/static/img/swbydyne.png"
|
||||||
|
:alt "Software by Dyne.org"
|
||||||
|
:title "Software by Dyne.org"}]]]
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
(defn render-static [body]
|
||||||
|
(page/html5 (render-head)
|
||||||
|
[:body {:class "fxc static"}
|
||||||
|
|
||||||
|
navbar-guest
|
||||||
|
|
||||||
|
[:div {:class "container"} body]
|
||||||
|
|
||||||
|
(render-footer)
|
||||||
|
]))
|
||||||
|
|
||||||
|
|
||||||
|
;; highlight functions do no conversion, take the format they highlight
|
||||||
|
;; render functions take edn and convert to the highlight format
|
||||||
|
;; download functions all take an edn and convert it in target format
|
||||||
|
;; edit functions all take an edn and present an editor in the target format
|
||||||
|
|
||||||
|
|
||||||
|
(defn render-yaml
|
||||||
|
"renders an edn into an highlighted yaml"
|
||||||
|
[data]
|
||||||
|
[:span
|
||||||
|
[:pre [:code {:class "yaml"}
|
||||||
|
(yaml/generate-string data)]]
|
||||||
|
[:script "hljs.initHighlightingOnLoad();"]])
|
||||||
|
|
||||||
|
(defn highlight-yaml
|
||||||
|
"renders a yaml text in highlighted html"
|
||||||
|
[data]
|
||||||
|
[:span
|
||||||
|
[:pre [:code {:class "yaml"}
|
||||||
|
data]]
|
||||||
|
[:script "hljs.initHighlightingOnLoad();"]])
|
||||||
|
|
||||||
|
|
||||||
|
(defn highlight-json
|
||||||
|
"renders a json text in highlighted html"
|
||||||
|
[data]
|
||||||
|
[:span
|
||||||
|
[:pre [:code {:class "json"}
|
||||||
|
data]]
|
||||||
|
[:script "hljs.initHighlightingOnLoad();"]])
|
||||||
|
|
||||||
|
(defn download-csv
|
||||||
|
"takes an edn, returns a csv plain/text for download"
|
||||||
|
[data]
|
||||||
|
{:headers {"Content-Type"
|
||||||
|
"text/plain; charset=utf-8"}
|
||||||
|
:body (with-out-str (csv/write-csv *out* data))})
|
||||||
|
|
||||||
|
(defn edit-edn
|
||||||
|
"renders an editor for the edn in yaml format"
|
||||||
|
[data]
|
||||||
|
[:div;; {:class "form-group"}
|
||||||
|
[:textarea {:class "form-control"
|
||||||
|
:rows "20" :data-editor "yaml"
|
||||||
|
:id "config" :name "editor"}
|
||||||
|
(yaml/generate-string data)]
|
||||||
|
[:script {:src "/static/js/ace.js"
|
||||||
|
:type "text/javascript" :charset "utf-8"}]
|
||||||
|
[:script {:type "text/javascript"}
|
||||||
|
(slurp (io/resource "public/static/js/ace-embed.js"))]
|
||||||
|
;; code to embed the ace editor on all elements in page
|
||||||
|
;; that contain the attribute "data-editor" set to the
|
||||||
|
;; mode language of choice
|
||||||
|
[:input {:class "btn btn-success btn-lg pull-top"
|
||||||
|
:type "submit" :value "submit"}]])
|
||||||
|
|
||||||
|
;; (defonce readme
|
||||||
|
;; (slurp (io/resource "public/static/README.html")))
|
||||||
|
|
||||||
|
(defonce login-form
|
||||||
|
[:div
|
||||||
|
[:h1 "Login for your account"
|
||||||
|
[:form {:action "/login"
|
||||||
|
:method "post"}
|
||||||
|
[:input {:type "text" :name "username"
|
||||||
|
:placeholder "Username"
|
||||||
|
:class "form-control"
|
||||||
|
:style "margin-top: 1em"}]
|
||||||
|
[:input {:type "password" :name "password"
|
||||||
|
:placeholder "Password"
|
||||||
|
:class "form-control"
|
||||||
|
:style "margin-top: 1em"}]
|
||||||
|
[:input {:type "submit" :value "Login"
|
||||||
|
:class "btn btn-primary btn-lg btn-block"
|
||||||
|
:style "margin-top: 1em"}]]]])
|
||||||
|
|
||||||
|
(defonce signup-form
|
||||||
|
[:div
|
||||||
|
[:h1 "Sign Up for a toaster account"
|
||||||
|
[:form {:action "/signup"
|
||||||
|
:method "post"}
|
||||||
|
[:input {:type "text" :name "name"
|
||||||
|
:placeholder "Name"
|
||||||
|
:class "form-control"
|
||||||
|
:style "margin-top: 1em"}]
|
||||||
|
[:input {:type "text" :name "email"
|
||||||
|
:placeholder "Email"
|
||||||
|
:class "form-control"
|
||||||
|
:style "margin-top: 1em"}]
|
||||||
|
[:input {:type "password" :name "password"
|
||||||
|
:placeholder "Password"
|
||||||
|
:class "form-control"
|
||||||
|
:style "margin-top: 1em"}]
|
||||||
|
[:input {:type "password" :name "repeat-password"
|
||||||
|
:placeholder "Repeat password"
|
||||||
|
:class "form-control"
|
||||||
|
:style "margin-top: 1em"}]
|
||||||
|
[:input {:type "submit" :value "Sign In"
|
||||||
|
:class "btn btn-primary btn-lg btn-block"
|
||||||
|
:style "margin-top: 1em"}]]]])
|
|
@ -0,0 +1,16 @@
|
||||||
|
webserver:
|
||||||
|
anti-forgery: false
|
||||||
|
ssl-redirect: false
|
||||||
|
|
||||||
|
admin:
|
||||||
|
email: jaromil@dyne.org
|
||||||
|
|
||||||
|
just-auth:
|
||||||
|
email-server: "mail.dyne.org"
|
||||||
|
email-user: "xxxxxxxxxxxxxxxx"
|
||||||
|
email-pass: "xxxxxxxxxxxxxxxx"
|
||||||
|
email-address: "xxxxxxxxxxxxxxxx"
|
||||||
|
email-admin: "xxxxxxxxxxxxxxxx"
|
||||||
|
mongo-url: mongodb://localhost:27017/toaster
|
||||||
|
mongo-user: toaster
|
||||||
|
mongo-pass: toaster
|
Loading…
Reference in New Issue