switched css to bulma, removed all javascript

This commit is contained in:
Jaromil 2018-10-10 02:04:56 +02:00
parent fe944519ac
commit 2be199a214
6 changed files with 635 additions and 358 deletions

File diff suppressed because one or more lines are too long

View File

@ -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;
}

View File

@ -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"}]]]]]]))

View File

@ -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 " &lt;" email "&gt;")]
[: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 " &lt;" email "&gt;")]
[: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})
)

View File

@ -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))))))

View File

@ -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"