switched css to bulma, removed all javascript
This commit is contained in:
parent
fe944519ac
commit
2be199a214
File diff suppressed because one or more lines are too long
|
@ -1,9 +1,14 @@
|
|||
body {
|
||||
font-family: "arial";
|
||||
font-size: 22px;
|
||||
/* font-family: "arial"; */
|
||||
/* font-size: 22px; */
|
||||
font-smooth: always;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
padding-top: 65px;
|
||||
/* padding-top: 65px; */
|
||||
}
|
||||
|
||||
/* horizontal margins on wide screens */
|
||||
@media (min-width: 768px) {
|
||||
.container { width: 36em; }
|
||||
}
|
||||
|
||||
code {
|
||||
|
@ -31,94 +36,52 @@ code {
|
|||
|
||||
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; */
|
||||
/* } */
|
||||
|
||||
html,body {
|
||||
font-family: 'Open Sans', serif;
|
||||
font-size: 14px;
|
||||
font-weight: 300;
|
||||
}
|
||||
.hero.is-success {
|
||||
background: #F2F6FA;
|
||||
}
|
||||
.hero .nav, .hero.is-success .nav {
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.box {
|
||||
margin-top: 5rem;
|
||||
}
|
||||
.avatar {
|
||||
margin-top: -70px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
.avatar img {
|
||||
padding: 5px;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
-webkit-box-shadow: 0 2px 3px rgba(10,10,10,.1), 0 0 0 1px rgba(10,10,10,.1);
|
||||
box-shadow: 0 2px 3px rgba(10,10,10,.1), 0 0 0 1px rgba(10,10,10,.1);
|
||||
}
|
||||
input {
|
||||
font-weight: 300;
|
||||
}
|
||||
p {
|
||||
letter-spacing: 0.01rem;
|
||||
font-style: normal;
|
||||
line-height: 1.5;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
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"
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.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;
|
||||
p.subtitle {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,302 @@
|
|||
;; 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.bulma
|
||||
(:require [clojure.java.io :as io]
|
||||
[clojure.data.csv :as csv]
|
||||
[yaml.core :as yaml]
|
||||
[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)
|
||||
|
||||
|
||||
;; TODO: navbars for bulma
|
||||
(def navbar-brand [:div {:class "navbar"}])
|
||||
(def navbar-guest [:div {:class "navbar"}])
|
||||
(def navbar-account [:div {:class "navbar"}])
|
||||
|
||||
(defn button
|
||||
([url text] (button url text [:p]))
|
||||
|
||||
([url text field] (button url text field "button"))
|
||||
|
||||
([url text field type]
|
||||
(hf/form-to [:post url]
|
||||
field ;; can be an hidden key/value field (project,
|
||||
;; person, etc using hf/hidden-field)
|
||||
[:p {:class "control"}
|
||||
(hf/submit-button {:class (str "button " 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 render
|
||||
([body]
|
||||
{:headers {"Content-Type"
|
||||
"text/html; charset=utf-8"}
|
||||
:body (page/html5
|
||||
(render-head)
|
||||
[:body ;; {:class "static"}
|
||||
;; navbar-guest
|
||||
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)
|
||||
body
|
||||
(render-footer)])}))
|
||||
|
||||
(defn render-success
|
||||
"render a successful message without ending the page"
|
||||
[succ]
|
||||
[:div {:class "notification is-primary"} succ])
|
||||
|
||||
(defn render-error
|
||||
"render an error message without ending the page"
|
||||
[err]
|
||||
[:div {:class "notification is-danger"} err])
|
||||
|
||||
(defn render-error-page
|
||||
([] (render-error-page {} "Unknown"))
|
||||
([err] (render-error-page {} err))
|
||||
([session error]
|
||||
(render
|
||||
[:div {:class "container"}
|
||||
(render-error error)])))
|
||||
|
||||
|
||||
(defn render-head
|
||||
([] (render-head
|
||||
"toaster.do" ;; default title
|
||||
"From Docker to VM or ARM SDcard in a few clicks, powered by Devuan and the DECODE project" "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"}]
|
||||
|
||||
[:title title]
|
||||
[:link {:href "https://fonts.googleapis.com/css?family=Open+Sans:300,400,700"
|
||||
:rel "stylesheet"}]
|
||||
;; cascade style sheets
|
||||
(page/include-css "/static/css/bulma.min.css")
|
||||
(page/include-css "/static/css/json-html.css")
|
||||
;; (page/include-css "/static/css/fa-regular.min.css")
|
||||
;; (page/include-css "/static/css/fontawesome.min.css")
|
||||
|
||||
(page/include-css "https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css")
|
||||
(page/include-css "/static/css/toaster.css")]))
|
||||
|
||||
(defn render-footer []
|
||||
[:footer {:class "footer"}
|
||||
[:div {:class "content has-text-centered"}
|
||||
[:p {:class "has-text-grey"} "toaster.do transforms your Docker prototype into "
|
||||
"an installable " [:a {:href "https://devuan.org"} "Devuan GNU+Linux"]
|
||||
" image, choose any supported target architecture!"]
|
||||
[:div {:class "columns is-variable is-8"}
|
||||
[:div {:class "column is-one-quarter is-narrow-touch"}
|
||||
[:a {:href "https://www.dyne.org"}
|
||||
[:figure {:class "image"}
|
||||
[:img {:src "/static/img/swbydyne.png"
|
||||
:alt "Software by Dyne.org"
|
||||
:title "Software by Dyne.org"}]]]]
|
||||
[:div {:class "column auto"}]
|
||||
[:div {:class "column is-one-quarter"}
|
||||
[:figure {:class "image"}
|
||||
[:img {:src "/static/img/AGPLv3.png" ;; :style "margin-top: 2.5em"
|
||||
:alt "Affero GPLv3 License"
|
||||
:title "Affero GPLv3 License"}]]]]]])
|
||||
|
||||
(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"}]])
|
||||
|
||||
(def brand-img "/static/img/whale_toast.jpg")
|
||||
(defn- hero-login-box [body]
|
||||
[:section {:class "hero is-fullheight"}
|
||||
[:div {:class "hero-body"}
|
||||
[:div {:class "container has-text-centered"}
|
||||
body
|
||||
]]])
|
||||
|
||||
(def login-form
|
||||
(hero-login-box
|
||||
[:div {:class "column is-4 is-offset-4"}
|
||||
[:h3 {:class "title has-text-grey"} "toaster.do"]
|
||||
[:h4 {:class "subtitle has-text-grey"}
|
||||
"from Docker to VM in a few clicks, powered by "
|
||||
[:a {:href "https://decodeproject.eu"} "DECODEproject.EU"]]
|
||||
[:p {:class "subtitle has-text-grey"}
|
||||
"please login to operate"]
|
||||
[:div {:class "box"}
|
||||
[:figure {:class "avatar"}
|
||||
[:img {:src brand-img }]]
|
||||
[:form {:action "/login"
|
||||
:method "post"}
|
||||
[:div {:class "field"}
|
||||
;; [:label {:class "label is-large"} "Email"]
|
||||
[:div {:class "control has-icons-left"}
|
||||
[:input {:type "email" :name "username"
|
||||
:placeholder "Email"
|
||||
:class "input is-large"}]
|
||||
[:span {:class "icon is-small is-left"}
|
||||
[:i {:class "fa fa-envelope fa-xs"}]]]]
|
||||
[:div {:class "field"}
|
||||
;; [:label {:class "label is-large"} "Password"]
|
||||
[:div {:class "control has-icons-left"}
|
||||
[:input {:type "password" :name "password"
|
||||
:placeholder "Password"
|
||||
:class "input is-large"}]
|
||||
[:span {:class "icon is-small is-left"}
|
||||
[:i {:class "fa fa-lock fa-xs"}]]]]
|
||||
[:div {:class "field"}
|
||||
[:p {:class "control"}
|
||||
[:input {:type "submit" :value "Login"
|
||||
:class "button is-block is-info is-large has-icons-left is-fullwidth"}]]]]]
|
||||
[:p {:class "subtitle has-text-grey"}
|
||||
"...or " [:a {:href "/signup"} "signup for a new account"]]]))
|
||||
|
||||
(def signup-form
|
||||
(hero-login-box
|
||||
[:div {:class "column is-4 is-offset-4"}
|
||||
[:h3 {:class "title has-text-grey"} "toaster.do"]
|
||||
[:h4 {:class "subtitle has-text-grey"}
|
||||
"sign up for a new account"]
|
||||
;; [:p {:class "subtitle has-text-grey"}]
|
||||
[:div {:class "box"}
|
||||
[:figure {:class "avatar"}
|
||||
[:img {:src brand-img}]]
|
||||
[:form {:action "/signup"
|
||||
:method "post"}
|
||||
[:div {:class "field"}
|
||||
[:div {:class "control"}
|
||||
[:input {:type "text" :name "name"
|
||||
:placeholder "Name"
|
||||
:class "input"}]]]
|
||||
[:div {:class "field"}
|
||||
[:div {:class "control"}
|
||||
[:input {:type "email" :name "email"
|
||||
:placeholder "Email"
|
||||
:class "input"}]]]
|
||||
[:div {:class "field"}
|
||||
[:div {:class "control"}
|
||||
[:input {:type "password" :name "password"
|
||||
:placeholder "Password"
|
||||
:class "input"}]]]
|
||||
[:div {:class "field"}
|
||||
[:div {:class "control"}
|
||||
[:input {:type "password" :name "repeat-password"
|
||||
:placeholder "Repeat password"
|
||||
:class "input"}]]]
|
||||
[:div {:class "field"}
|
||||
[:div {:class "control"}
|
||||
[:input {:type "submit" :value "Sign In"
|
||||
:class "button is-block is-info is-large is-fullwidth"}]]]]]]))
|
|
@ -18,178 +18,182 @@
|
|||
|
||||
(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]
|
||||
[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]]
|
||||
[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]
|
||||
[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]
|
||||
[toaster.views :as views]
|
||||
[toaster.jobs :as job])
|
||||
[toaster.session :as s]
|
||||
[toaster.config :as conf]
|
||||
[toaster.bulma :as web]
|
||||
[toaster.ring :as ring]
|
||||
[toaster.views :as views]
|
||||
[toaster.jobs :as job])
|
||||
(:gen-class))
|
||||
|
||||
(defonce config (conf/load-config "toaster" conf/default-settings))
|
||||
|
||||
|
||||
(defn- login-page [request form]
|
||||
(f/attempt-all
|
||||
[acct (s/check-account request)]
|
||||
(web/render acct
|
||||
[:div {:class "container"}
|
||||
[:h1 {:class "title"}
|
||||
(str "Already logged in with account: "
|
||||
(:email acct))]
|
||||
[:h2 {:class "subtitle"}
|
||||
[:a {:href "/logout"} "Logout"]]])
|
||||
(f/when-failed [e]
|
||||
(web/render form))))
|
||||
|
||||
(defroutes
|
||||
app-routes
|
||||
|
||||
(GET "/" request (web/render "Hello there!")) ;; web/readme))
|
||||
(GET "/" request (login-page request web/login-form))
|
||||
|
||||
;; NEW ROUTES HERE
|
||||
(GET "/upload" request
|
||||
(->> (fn [req conf acct]
|
||||
(web/render acct [:div
|
||||
views/dockerfile-upload-form
|
||||
(views/list-jobs acct)]))
|
||||
(s/check request)))
|
||||
(->> (fn [req conf acct]
|
||||
(web/render acct [:div
|
||||
views/dockerfile-upload-form
|
||||
(views/list-jobs acct)]))
|
||||
(s/check request)))
|
||||
|
||||
(POST "/dockerfile" request
|
||||
(->> (fn [req conf acct]
|
||||
(web/render acct (views/dockerfile-upload-post req conf acct)))
|
||||
(s/check request)))
|
||||
|
||||
(GET "/edit" request
|
||||
(->> (fn [req conf acct]
|
||||
(web/render acct views/dockerfile-edit-form))
|
||||
(s/check request)))
|
||||
(->> (fn [req conf acct]
|
||||
(web/render acct (views/dockerfile-upload-post req conf acct)))
|
||||
(s/check request)))
|
||||
|
||||
(GET "/list" request
|
||||
(->> (fn [req conf acct]
|
||||
(web/render acct (views/list-jobs acct)))
|
||||
(s/check request)))
|
||||
(->> (fn [req conf acct]
|
||||
(web/render acct (views/list-jobs acct)))
|
||||
(s/check request)))
|
||||
|
||||
(POST "/remove" request
|
||||
(->> (fn [req conf acct]
|
||||
(web/render acct (views/remove-job req conf acct)))
|
||||
(s/check request)))
|
||||
(->> (fn [req conf acct]
|
||||
(web/render acct (views/remove-job req conf acct)))
|
||||
(s/check request)))
|
||||
|
||||
(POST "/start" request
|
||||
(->> (fn [req conf acct]
|
||||
(web/render acct (views/start-job req conf acct)))
|
||||
(s/check request)))
|
||||
(->> (fn [req conf acct]
|
||||
(web/render acct (views/start-job req conf acct)))
|
||||
(s/check request)))
|
||||
|
||||
(POST "/view" request
|
||||
(->> (fn [req conf acct]
|
||||
(web/render acct (views/view-job req conf acct)))
|
||||
(s/check request)))
|
||||
(->> (fn [req conf acct]
|
||||
(web/render acct (views/view-job req conf acct)))
|
||||
(s/check request)))
|
||||
|
||||
(GET "/error" request
|
||||
(->> (fn [req conf acct]
|
||||
(web/render acct [:div
|
||||
(web/render-error "Generic Error Page")
|
||||
(views/list-jobs acct)]))
|
||||
(s/check request)))
|
||||
(->> (fn [req conf acct]
|
||||
(web/render acct [:div
|
||||
(web/render-error "Generic Error Page")
|
||||
(views/list-jobs acct)]))
|
||||
(s/check request)))
|
||||
|
||||
;; 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))))
|
||||
(GET "/login" request (login-page request 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 (views/list-jobs logged))))
|
||||
;; (web/render
|
||||
;; logged
|
||||
;; [:div
|
||||
;; [:h1 "Logged in: " username]
|
||||
;; views/welcome-menu])))
|
||||
(f/when-failed [e]
|
||||
(web/render-error-page
|
||||
(str "Login failed: " (f/message e))))))
|
||||
(GET "/session" request
|
||||
(-> (:session request) web/render-yaml web/render))
|
||||
(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 (views/list-jobs logged))))
|
||||
;; (web/render
|
||||
;; logged
|
||||
;; [:div
|
||||
;; [:h1 "Logged in: " username]
|
||||
;; views/welcome-menu])))
|
||||
(f/when-failed [e]
|
||||
(web/render (web/render-error
|
||||
(str "Login failed: " (f/message e)))))))
|
||||
|
||||
(GET "/logout" request
|
||||
(conj {:session {:config config}}
|
||||
(web/render [:h1 "Logged out."])))
|
||||
(GET "/signup" request
|
||||
(web/render web/signup-form))
|
||||
(conj {:session {:config config}}
|
||||
(web/render [:div {:class "container"}
|
||||
[:h1 {:class "title"} "Logged out."]])))
|
||||
|
||||
(GET "/signup" request (login-page request 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
|
||||
(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))))))
|
||||
(web/render-error
|
||||
"Repeat password didnt match")))
|
||||
(f/when-failed [e]
|
||||
(web/render
|
||||
(web/render-error
|
||||
(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)])])))
|
||||
(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)]))
|
||||
;; 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"))
|
||||
(route/not-found (web/render (web/render-error "Page Not Found")))
|
||||
|
||||
) ;; end of routes
|
||||
|
||||
(def app
|
||||
|
@ -206,7 +210,7 @@
|
|||
(defn -main []
|
||||
(println "Starting ring server")
|
||||
(ring/init ring/app-defaults)
|
||||
;(run-jetty app {:port 6060
|
||||
; :host "localhost"
|
||||
; :join? true})
|
||||
;(run-jetty app {:port 6060
|
||||
; :host "localhost"
|
||||
; :join? true})
|
||||
)
|
||||
|
|
|
@ -1,148 +1,155 @@
|
|||
(ns toaster.views
|
||||
(:require
|
||||
[clojure.java.io :as io]
|
||||
[clojure.string :as str]
|
||||
[clojure.contrib.humanize :as humanize :refer [datetime]]
|
||||
;; [clojure.data.json :as json :refer [read-str]]
|
||||
[toaster.webpage :as web]
|
||||
[toaster.session :as s]
|
||||
[toaster.ring :as ring]
|
||||
[toaster.jobs :as job]
|
||||
[failjure.core :as f]
|
||||
[auxiliary.string :refer [strcasecmp]]
|
||||
[toaster.config :as conf]
|
||||
[taoensso.timbre :as log :refer [debug]]
|
||||
[me.raynes.conch :as sh :refer [with-programs]]
|
||||
[clj-time.core :as time]
|
||||
[clj-time.coerce :as tc]
|
||||
[clj-time.local :as tl]
|
||||
[clj-storage.core :as db]
|
||||
[hiccup.form :as hf]))
|
||||
(:require
|
||||
[clojure.java.io :as io]
|
||||
[clojure.string :as str]
|
||||
[clojure.contrib.humanize :as humanize :refer [datetime]]
|
||||
;; [clojure.data.json :as json :refer [read-str]]
|
||||
[toaster.bulma :as web]
|
||||
[toaster.session :as s]
|
||||
[toaster.ring :as ring]
|
||||
[toaster.jobs :as job]
|
||||
[failjure.core :as f]
|
||||
[auxiliary.string :refer [strcasecmp]]
|
||||
[toaster.config :as conf]
|
||||
[taoensso.timbre :as log :refer [debug]]
|
||||
[me.raynes.conch :as sh :refer [with-programs]]
|
||||
[clj-time.core :as time]
|
||||
[clj-time.coerce :as tc]
|
||||
[clj-time.local :as tl]
|
||||
[clj-storage.core :as db]
|
||||
[hiccup.form :as hf]))
|
||||
|
||||
(def dockerfile-upload-form
|
||||
[:div {:class "container-fluid"}
|
||||
[:h1 "Upload a Dockerfile to toast"]
|
||||
[:p " Choose the file in your computer and click 'Submit' to
|
||||
proceed to validation."]
|
||||
[:div {:class "form-group"}
|
||||
[:form {:action "dockerfile" :method "post"
|
||||
:class "form-shell"
|
||||
:enctype "multipart/form-data"}
|
||||
[:fieldset {:class "fieldset btn btn-default btn-file btn-lg"}
|
||||
[:input {:name "file" :type "file"}]]
|
||||
;; [:fieldset {:class "fieldset-submit"}
|
||||
[:input {:class "btn btn-primary btn-lg"
|
||||
:id "field-submit" :type "submit"
|
||||
:name "submit" :value "submit"}]]]])
|
||||
[:div {:class "container"}
|
||||
[:h1 {:class "title"} "Upload a Dockerfile to toast"]
|
||||
[:p " Choose the file in your computer and click 'Submit' to
|
||||
proceed to validation."]
|
||||
[:form {:action "dockerfile" :method "post"
|
||||
:class "form-shell"
|
||||
:enctype "multipart/form-data"}
|
||||
[:div {:class "file has-label is-fullwidth"}
|
||||
[:label {:class "file-label"}
|
||||
[:input {:class "file-input" :name "file" :type "file"}]
|
||||
[:span {:class "file-cta"}
|
||||
[:span {:class "file-icon"}
|
||||
[:i {:class "fa fa-upload"}]]
|
||||
[:span {:class "file-label"}
|
||||
"Docker file..."]]
|
||||
[:span {:class "file-name"}]]]
|
||||
;; [:fieldset {:class "fieldset-submit"}
|
||||
|
||||
[:div {:class "field"}
|
||||
[:div {:class "control"}
|
||||
[:input {:class "button is-block is-info is-large is-fullwidth"
|
||||
:type "submit"
|
||||
:name "submit" :value "submit"}]]]]])
|
||||
|
||||
(def dockerfile-edit-form
|
||||
[:div {:class "container-fluid"}
|
||||
[:h1 "Edit your Dockerfile to toast"]
|
||||
[:div {:class "form-group"}
|
||||
[:form {:action "dockerfile" :method "post"
|
||||
:class "form-shell"
|
||||
:enctype "multipart/form-data"}
|
||||
[:fieldset {:class "fieldset btn btn-default btn-file btn-lg"}
|
||||
[:textarea {:name "editor" :id "editor"
|
||||
:rows 30 :cols 72} "FROM: dyne/devuan:ascii"]
|
||||
[:input {:class "btn btn-primary btn-lg"
|
||||
:id "field-submit" :type "submit"
|
||||
:name "submit" :value "submit"}]]]]
|
||||
[:script "var editor = ace.edit(\"editor\");
|
||||
editor.setTheme(\"ace/theme/monokai\");
|
||||
editor.session.setMode(\"ace/mode/dockerfile\");"]])
|
||||
[:div {:class "container"}
|
||||
[:h1 {:class "title"} "Edit your Dockerfile to toast"]
|
||||
[:div {:class "form-group"}
|
||||
[:form {:action "dockerfile" :method "post"
|
||||
:class "form-shell"
|
||||
:enctype "multipart/form-data"}
|
||||
[:fieldset {:class "fieldset btn btn-default btn-file btn-lg"}
|
||||
[:textarea {:name "editor" :id "editor"
|
||||
:rows 30 :cols 72} "FROM: dyne/devuan:ascii"]
|
||||
[:input {:class "btn btn-primary btn-lg"
|
||||
:id "field-submit" :type "submit"
|
||||
:name "submit" :value "submit"}]]]]
|
||||
[:script "var editor = ace.edit(\"editor\");
|
||||
editor.setTheme(\"ace/theme/monokai\");
|
||||
editor.session.setMode(\"ace/mode/dockerfile\");"]])
|
||||
|
||||
|
||||
|
||||
(def welcome-menu
|
||||
[:div {:class "container-fluid"}
|
||||
[:div {:class "row-fluid"}
|
||||
[:div {:class "row"} [:a {:href "/list"} "List all your toaster jobs"]]
|
||||
[:div {:class "row"} [:a {:href "/upload"} "Upload a new toaster job"]]]])
|
||||
|
||||
(defn dockerfile-upload-post [request config account]
|
||||
(let
|
||||
[tempfile (get-in request [:params :file :tempfile])
|
||||
filename (get-in request [:params :file :filename])
|
||||
params (:params request)]
|
||||
(cond
|
||||
(> (get-in params [:file :size]) 64000)
|
||||
;; max upload size in bytes
|
||||
;; TODO: put in config
|
||||
(web/render-error params "File too big in upload (64KB limit).")
|
||||
:else
|
||||
(let [file (io/copy tempfile (io/file "/tmp" filename))
|
||||
path (str "/tmp/" filename)]
|
||||
(io/delete-file tempfile)
|
||||
(if (not (.exists (io/file path)))
|
||||
(web/render-error
|
||||
(log/spy :error
|
||||
[:h1 (str "Uploaded file not found: " filename)]))
|
||||
;; file is now in 'tmp' var
|
||||
[:div {:class "container-fluid"}
|
||||
[:h1 "Job uploaded and added"]
|
||||
[:p "Log messages:"]
|
||||
(web/render-yaml (job/add path config account))])))))
|
||||
(let
|
||||
[tempfile (get-in request [:params :file :tempfile])
|
||||
filename (get-in request [:params :file :filename])
|
||||
params (:params request)]
|
||||
(cond
|
||||
(> (get-in params [:file :size]) 64000)
|
||||
;; max upload size in bytes
|
||||
;; TODO: put in config
|
||||
(web/render-error params "File too big in upload (64KB limit).")
|
||||
:else
|
||||
(let [file (io/copy tempfile (io/file "/tmp" filename))
|
||||
path (str "/tmp/" filename)]
|
||||
(io/delete-file tempfile)
|
||||
(if (not (.exists (io/file path)))
|
||||
(web/render-error
|
||||
(log/spy :error
|
||||
[:h1 (str "Uploaded file not found: " filename)]))
|
||||
;; file is now in 'tmp' var
|
||||
[:div {:class "container"}
|
||||
[:h1 {:class "title"} "Job uploaded and added"]
|
||||
[:p "Log messages:"]
|
||||
(web/render-yaml (job/add path config account))])))))
|
||||
|
||||
(defn list-jobs [account]
|
||||
(f/attempt-all
|
||||
[joblist (db/query @ring/jobs {:email (:email account)})]
|
||||
[:div {:class "container-fluid"}
|
||||
[:h1 (str "List all toaster jobs for " (:name account))]
|
||||
[:table {:class "sortable table"}
|
||||
[:thead nil
|
||||
[:tr nil [:th nil "Date"] [:th nil "Type"] [:th nil "Status"] [:th nil "Actions"]]]
|
||||
[:tbody nil
|
||||
(for [j joblist]
|
||||
(let [type (:type j) ;; (-> j (str/split #"-") second)
|
||||
tstamp (:timestamp j) ;; (-> j (str/split #"-") last)
|
||||
jobid (:jobid j)
|
||||
joburl (str/replace jobid #"@" "AT")]
|
||||
[:tr nil
|
||||
[:td {:class "date"} (-> tstamp Long/valueOf tc/from-long humanize/datetime)]
|
||||
[:td {:class "job"} [:a {:href (str "https://sdk.dyne.org:4443/view/web-sdk-builds/job/" joburl)} type]]
|
||||
[:td {:class "status"} [:a {:href (str "https://sdk.dyne.org:4443/job/" joburl "/lastBuild/console")}
|
||||
[:img {:src (str "https://sdk.dyne.org:4443/job/" joburl "/badge/icon")}]]]
|
||||
[:td {:class "actions"}
|
||||
[:div {:class "btn-toolbar" :role "toolbar" :arial-label "Actions"}
|
||||
(web/button "/view" "\uD83D\uDC41" (hf/hidden-field "jobid" jobid))
|
||||
(web/button "/start" "▶" (hf/hidden-field "jobid" jobid))
|
||||
(web/button "/remove" "\uD83D\uDDD1" (hf/hidden-field "jobid" jobid))]]]
|
||||
))]]]
|
||||
(f/when-failed [e]
|
||||
(web/render-error
|
||||
(str "Job list failure: " (f/message e))))))
|
||||
(f/attempt-all
|
||||
[joblist (db/query @ring/jobs {:email (:email account)})]
|
||||
[:div {:class "container has-text-centered"}
|
||||
[:h1 {:class "title"} (str "List all toaster jobs for " (:name account))]
|
||||
|
||||
[:div {:class "box"}
|
||||
[:table {:class "table is-fullwidth is-hoverable"}
|
||||
[:thead nil
|
||||
[:tr nil [:th nil "Date"] [:th nil "Type"] [:th nil "Status"] [:th nil "Actions"]]]
|
||||
[:tbody nil
|
||||
(for [j joblist]
|
||||
(let [type (:type j) ;; (-> j (str/split #"-") second)
|
||||
tstamp (:timestamp j) ;; (-> j (str/split #"-") last)
|
||||
jobid (:jobid j)
|
||||
joburl (str/replace jobid #"@" "AT")]
|
||||
[:tr nil
|
||||
[:td {:class "date"} (-> tstamp Long/valueOf tc/from-long humanize/datetime)]
|
||||
[:td {:class "job"} [:a {:href (str "https://sdk.dyne.org:4443/view/web-sdk-builds/job/" joburl)} type]]
|
||||
[:td {:class "status"} [:a {:href (str "https://sdk.dyne.org:4443/job/" joburl "/lastBuild/console")}
|
||||
[:img {:src (str "https://sdk.dyne.org:4443/job/" joburl "/badge/icon")}]]]
|
||||
[:td {:class "actions"}
|
||||
[:div {:class "field is-grouped"}
|
||||
(web/button "/view" "\uD83D\uDC41" (hf/hidden-field "jobid" jobid))
|
||||
(web/button "/start" "▶" (hf/hidden-field "jobid" jobid))
|
||||
(web/button "/remove" "\uD83D\uDDD1" (hf/hidden-field "jobid" jobid))]]]
|
||||
))]]]]
|
||||
(f/when-failed [e]
|
||||
(web/render-error
|
||||
(str "Job list failure: " (f/message e))))))
|
||||
|
||||
|
||||
(defn remove-job [request config account]
|
||||
(f/attempt-all
|
||||
[jobid (s/param request :jobid)
|
||||
jobfound (db/query @ring/jobs {:jobid jobid})
|
||||
r_rmjob (db/delete! @ring/jobs jobid)
|
||||
r_sync (job/trash jobid config)]
|
||||
[:h1 (str "Job removed: " jobid)]
|
||||
(f/when-failed [e]
|
||||
(web/render-error (str "Failure removing job: " (f/message e))))))
|
||||
[jobid (s/param request :jobid)
|
||||
jobfound (db/query @ring/jobs {:jobid jobid})
|
||||
r_rmjob (db/delete! @ring/jobs jobid)
|
||||
r_sync (job/trash jobid config)]
|
||||
[:span
|
||||
[:div {:class "notification is-primary has-text-centered"}
|
||||
(str "Job removed: " jobid)]
|
||||
(list-jobs account)]
|
||||
(f/when-failed [e]
|
||||
(web/render-error (str "Failure removing job: " (f/message e))))))
|
||||
|
||||
(defn start-job [request config account]
|
||||
(f/attempt-all
|
||||
[jobid (s/param request :jobid)
|
||||
jobfound (db/query @ring/jobs {:jobid jobid})
|
||||
r_sync (job/start jobid config)]
|
||||
[:div {:class "container-fluid"}
|
||||
[:h1 (str "Job started: " jobid)]
|
||||
[:p [:a {:href (str "https://sdk.dyne.org:4443/view/web-sdk-builds/job/"
|
||||
(str/replace jobid #"@" "AT"))}]]]
|
||||
(f/when-failed [e]
|
||||
(web/render-error (str "Failure starting job: " (f/message e))))))
|
||||
[jobid (s/param request :jobid)
|
||||
jobfound (db/query @ring/jobs {:jobid jobid})
|
||||
r_sync (job/start jobid config)]
|
||||
[:div {:class "container"}
|
||||
[:h1 (str "Job started: " jobid)]
|
||||
[:p [:a {:href (str "https://sdk.dyne.org:4443/view/web-sdk-builds/job/"
|
||||
(str/replace jobid #"@" "AT"))}]]]
|
||||
(f/when-failed [e]
|
||||
(web/render-error (str "Failure starting job: " (f/message e))))))
|
||||
|
||||
(defn view-job [request config account]
|
||||
(f/attempt-all
|
||||
[jobid (s/param request :jobid)
|
||||
jobfound (db/fetch @ring/jobs jobid)
|
||||
dockerfile (log/spy (-> jobfound :dockerfile))]
|
||||
(web/render account [:div
|
||||
[:h1 (str "Viewing job: " jobid)]
|
||||
[:pre dockerfile]])
|
||||
(f/when-failed [e]
|
||||
(web/render-error (str "Failure viewing job: " (f/message e))))))
|
||||
(f/attempt-all
|
||||
[jobid (s/param request :jobid)
|
||||
jobfound (db/fetch @ring/jobs jobid)
|
||||
dockerfile (log/spy (-> jobfound :dockerfile))]
|
||||
[:div {:class "container"}
|
||||
[:h1 {:class "title"} (str "Viewing job: " jobid)]
|
||||
[:pre dockerfile]]
|
||||
(f/when-failed [e]
|
||||
(web/render-error (str "Failure viewing job: " (f/message e))))))
|
||||
|
|
|
@ -301,7 +301,7 @@
|
|||
;; (defonce readme
|
||||
;; (slurp (io/resource "public/static/README.html")))
|
||||
|
||||
(defonce login-form
|
||||
(def login-form
|
||||
[:div
|
||||
[:h1 "Login for your account"
|
||||
[:form {:action "/login"
|
||||
|
@ -318,7 +318,7 @@
|
|||
:class "btn btn-primary btn-lg btn-block"
|
||||
:style "margin-top: 1em"}]]]])
|
||||
|
||||
(defonce signup-form
|
||||
(def signup-form
|
||||
[:div
|
||||
[:h1 "Sign Up for a toaster account"
|
||||
[:form {:action "/signup"
|
||||
|
|
Loading…
Reference in New Issue