diff --git a/macchiato-app/Dockerfile b/macchiato-app/Dockerfile new file mode 100644 index 0000000..fb3db68 --- /dev/null +++ b/macchiato-app/Dockerfile @@ -0,0 +1,21 @@ +FROM mhart/alpine-node:latest + +MAINTAINER Your Name + +# Create app directory +RUN mkdir -p /macchiato-web3-example +WORKDIR /macchiato-web3-example + +# Install app dependencies +COPY package.json /macchiato-web3-example +RUN npm install pm2 -g +RUN npm install + +# Bundle app source +COPY target/release/macchiato-web3-example.js /macchiato-web3-example/macchiato-web3-example.js +COPY public /macchiato-web3-example/public + +ENV HOST 0.0.0.0 + +EXPOSE 3000 +CMD [ "pm2-docker", "/macchiato-web3-example/macchiato-web3-example.js" ] diff --git a/macchiato-app/README.md b/macchiato-app/README.md new file mode 100644 index 0000000..8e8cd84 --- /dev/null +++ b/macchiato-app/README.md @@ -0,0 +1,14 @@ +# macchiato-web3-example +Example app built with Macchiato framework and cljs-web3 library + +## Run + +1. Start figwheel: `lein build` +2. Run node: `node target/out/macchiato-web3-example.js` +3. Start test rpc: `testrpc -p 8545` + + +## Available endpoints + +* `curl http://localhost:3000/accounts` eth accounts +* `curl http://localhost:3000/balance` eth balance of the first account diff --git a/macchiato-app/env/dev/macchiato_web3_example/app.cljs b/macchiato-app/env/dev/macchiato_web3_example/app.cljs new file mode 100644 index 0000000..6e85e49 --- /dev/null +++ b/macchiato-app/env/dev/macchiato_web3_example/app.cljs @@ -0,0 +1,13 @@ + (ns ^:figwheel-always macchiato-web3-example.app + (:require + [macchiato-web3-example.core :as core] + [cljs.nodejs :as node] + [mount.core :as mount])) + +(mount/in-cljc-mode) + +(cljs.nodejs/enable-util-print!) + +(.on js/process "uncaughtException" #(js/console.error %)) + +(set! *main-cli-fn* core/server) diff --git a/macchiato-app/env/dev/user.clj b/macchiato-app/env/dev/user.clj new file mode 100644 index 0000000..3e860b1 --- /dev/null +++ b/macchiato-app/env/dev/user.clj @@ -0,0 +1,11 @@ +(ns user + (:require [figwheel-sidecar.repl-api :as ra])) + +(defn start-fw [] + (ra/start-figwheel!)) + +(defn stop-fw [] + (ra/stop-figwheel!)) + +(defn cljs [] + (ra/cljs-repl)) diff --git a/macchiato-app/env/prod/macchiato_web3_example/app.cljs b/macchiato-app/env/prod/macchiato_web3_example/app.cljs new file mode 100644 index 0000000..046a4e1 --- /dev/null +++ b/macchiato-app/env/prod/macchiato_web3_example/app.cljs @@ -0,0 +1,11 @@ + (ns macchiato-web3-example.app + (:require + [macchiato-web3-example.core :as core] + [cljs.nodejs] + [mount.core :as mount])) + +(mount/in-cljc-mode) + +(cljs.nodejs/enable-util-print!) + +(set! *main-cli-fn* core/server) diff --git a/macchiato-app/env/test/macchiato_web3_example/app.cljs b/macchiato-app/env/test/macchiato_web3_example/app.cljs new file mode 100644 index 0000000..d9170e9 --- /dev/null +++ b/macchiato-app/env/test/macchiato_web3_example/app.cljs @@ -0,0 +1,8 @@ +(ns macchiato-web3-example.app + (:require + [doo.runner :refer-macros [doo-tests]] + [macchiato-web3-example.core-test])) + +(doo-tests 'macchiato-web3-example.core-test) + + diff --git a/macchiato-app/package-lock.json b/macchiato-app/package-lock.json new file mode 100644 index 0000000..0f4d849 --- /dev/null +++ b/macchiato-app/package-lock.json @@ -0,0 +1,227 @@ +{ + "name": "macchiato-web3-example", + "version": "0.1.0-SNAPSHOT", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "bignumber.js": { + "version": "github:debris/bignumber.js#c7a38de919ed75e6fb6ba38051986e294b328df9" + }, + "concat-stream": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.0.6", + "typedarray": "0.0.6" + } + }, + "content-type": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", + "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0=" + }, + "cookies": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.6.2.tgz", + "integrity": "sha1-asGwUolSCOj8TE9fhqntMbnLXM8=", + "requires": { + "depd": "1.1.1", + "keygrip": "1.0.2" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "crypto-js": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.8.tgz", + "integrity": "sha1-cV8HC/YBTyrpkqmLOSkli3E/CNU=" + }, + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" + }, + "etag": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz", + "integrity": "sha1-A9MLX2fdbmMtKUXTDWZScxo01dg=" + }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "requires": { + "pend": "1.2.0" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "keygrip": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.2.tgz", + "integrity": "sha1-rTKXxVcGneqLz+ek+kkbdcXd65E=" + }, + "lru": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lru/-/lru-3.1.0.tgz", + "integrity": "sha1-6n+4VG2DczOWoTCR12z+tMBoN9U=", + "requires": { + "inherits": "2.0.3" + } + }, + "multiparty": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/multiparty/-/multiparty-4.1.2.tgz", + "integrity": "sha1-VPjslxIFL6Hf2OyXUFbIIw1vI3A=", + "requires": { + "fd-slicer": "1.0.1" + } + }, + "options": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", + "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=" + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + }, + "qs": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.0.tgz", + "integrity": "sha1-9AOyZPI7wBIox0ExtAfxjV6l1EI=" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" + }, + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } + }, + "scmp": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/scmp/-/scmp-0.0.3.tgz", + "integrity": "sha1-NkjfLXKUZB5/eGc//CloHZutkHM=" + }, + "simple-encryptor": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-encryptor/-/simple-encryptor-1.1.0.tgz", + "integrity": "sha1-ZTuxkmyPD8K41wOFd8yEYFMmOug=", + "requires": { + "scmp": "0.0.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "source-map-support": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.6.tgz", + "integrity": "sha1-MlUqpktFg5KoXqs7C17mFScWeus=", + "requires": { + "source-map": "0.5.7" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "ultron": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", + "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=" + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "utf8": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz", + "integrity": "sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "web3": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/web3/-/web3-0.14.0.tgz", + "integrity": "sha1-lxPLGvK/+9SjL+fMkQ2utCdnEVo=", + "requires": { + "bignumber.js": "github:debris/bignumber.js#c7a38de919ed75e6fb6ba38051986e294b328df9", + "crypto-js": "3.1.8", + "utf8": "2.1.2", + "xmlhttprequest": "1.8.0" + } + }, + "ws": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.1.tgz", + "integrity": "sha1-CC3bbGQehdS7RR8D1S8G6r2x8Bg=", + "requires": { + "options": "0.0.6", + "ultron": "1.0.2" + } + }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" + } + } +} diff --git a/macchiato-app/package.json b/macchiato-app/package.json new file mode 100644 index 0000000..04cd9f7 --- /dev/null +++ b/macchiato-app/package.json @@ -0,0 +1,25 @@ +{ + "private": true, + "name": "macchiato-web3-example", + "description": "FIXME: write this!", + "version": "0.1.0-SNAPSHOT", + "dependencies": { + "random-bytes": "1.0.0", + "multiparty": "4.1.2", + "source-map-support": "0.4.6", + "ws": "1.1.1", + "web3": "0.14.0", + "cookies": "0.6.2", + "etag": "1.7.0", + "lru": "3.1.0", + "qs": "6.3.0", + "content-type": "1.0.2", + "url": "0.11.0", + "simple-encryptor": "1.1.0", + "concat-stream": "1.5.2" + }, + "main": "target/out/macchiato-web3-example.js", + "scripts": { + "start": "node target/out/macchiato-web3-example.js" + } +} diff --git a/macchiato-app/project.clj b/macchiato-app/project.clj new file mode 100644 index 0000000..8037bd4 --- /dev/null +++ b/macchiato-app/project.clj @@ -0,0 +1,87 @@ +(defproject macchiato-web3-example "0.1.0-SNAPSHOT" + :description "FIXME: write this!" + :url "http://example.com/FIXME" + :dependencies [[bidi "2.1.2"] + [com.cemerick/piggieback "0.2.2"] + [com.taoensso/timbre "4.10.0"] + [macchiato/hiccups "0.4.1"] + [macchiato/core "0.2.2"] + [macchiato/env "0.0.6"] + [mount "0.1.11"] + [cljs-web3 "0.19.0-0-7"] + [org.clojure/clojure "1.8.0"] + [org.clojure/clojurescript "1.9.908"]] + :min-lein-version "2.0.0" + :jvm-opts ^:replace ["-Xmx1g" "-server"] + :plugins [[lein-doo "0.1.7"] + [macchiato/lein-npm "0.6.3"] + [lein-figwheel "0.5.13"] + [lein-cljsbuild "1.1.5"]] + :npm {:dependencies [[source-map-support "0.4.6"] + [web3 "0.14.0"]] + :write-package-json true} + :source-paths ["src" "target/classes"] + :clean-targets ["target"] + :target-path "target" + :profiles + {:dev + {:npm {:package {:main "target/out/macchiato-web3-example.js" + :scripts {:start "node target/out/macchiato-web3-example.js"}}} + :dependencies [[figwheel-sidecar "0.5.13"]] + :cljsbuild + {:builds {:dev + {:source-paths ["env/dev" "src"] + :figwheel true + :compiler {:main macchiato-web3-example.app + :output-to "target/out/macchiato-web3-example.js" + :output-dir "target/out" + :target :nodejs + :optimizations :none + :pretty-print true + :source-map true + :source-map-timestamp false}}}} + :figwheel + {:http-server-root "public" + :nrepl-port 7000 + :reload-clj-files {:clj false :cljc true} + :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]} + :source-paths ["env/dev"] + :repl-options {:init-ns user}} + :test + {:cljsbuild + {:builds + {:test + {:source-paths ["env/test" "src" "test"] + :compiler {:main macchiato-web3-example.app + :output-to "target/test/macchiato-web3-example.js" + :target :nodejs + :optimizations :none + :pretty-print true + :source-map true}}}} + :doo {:build "test"}} + :release + {:npm {:package {:main "target/release/macchiato-web3-example.js" + :scripts {:start "node target/release/macchiato-web3-example.js"}}} + :cljsbuild + {:builds + {:release + {:source-paths ["env/prod" "src"] + :compiler {:main macchiato-web3-example.app + :output-to "target/release/macchiato-web3-example.js" + :language-in :ecmascript5 + :target :nodejs + :optimizations :simple + :pretty-print false}}}}}} + :aliases + {"build" ["do" + ["clean"] + ["npm" "install"] + ["figwheel" "dev"]] + "package" ["do" + ["clean"] + ["npm" "install"] + ["with-profile" "release" "npm" "init" "-y"] + ["with-profile" "release" "cljsbuild" "once"]] + "test" ["do" + ["npm" "install"] + ["with-profile" "test" "doo" "node"]]}) diff --git a/macchiato-app/public/css/site.css b/macchiato-app/public/css/site.css new file mode 100644 index 0000000..5ab89cd --- /dev/null +++ b/macchiato-app/public/css/site.css @@ -0,0 +1,34 @@ +.body-container { + font-family: 'Helvetica Neue', Verdana, Helvetica, Arial, sans-serif; + max-width: 600px; + margin: 0 auto; + padding-top: 72px; + -webkit-font-smoothing: antialiased; + font-size: 1.125em; + color: #333; + line-height: 1.5em; +} + +h1, h2, h3 { + color: #000; +} +h1 { + font-size: 2.5em +} + +h2 { + font-size: 2em +} + +h3 { + font-size: 1.5em +} + +a { + text-decoration: none; + color: #09f; +} + +a:hover { + text-decoration: underline; +} diff --git a/macchiato-app/src/macchiato_web3_example/config.cljs b/macchiato-app/src/macchiato_web3_example/config.cljs new file mode 100644 index 0000000..29dc57d --- /dev/null +++ b/macchiato-app/src/macchiato_web3_example/config.cljs @@ -0,0 +1,6 @@ +(ns macchiato-web3-example.config + (:require [macchiato.env :as config] + [mount.core :refer [defstate]])) + +(defstate env :start (config/env)) + diff --git a/macchiato-app/src/macchiato_web3_example/core.cljs b/macchiato-app/src/macchiato_web3_example/core.cljs new file mode 100644 index 0000000..087986f --- /dev/null +++ b/macchiato-app/src/macchiato_web3_example/core.cljs @@ -0,0 +1,19 @@ +(ns macchiato-web3-example.core + (:require + [macchiato-web3-example.config :refer [env]] + [macchiato-web3-example.middleware :refer [wrap-defaults]] + [macchiato-web3-example.routes :refer [router]] + [macchiato.server :as http] + [macchiato.middleware.session.memory :as mem] + [mount.core :as mount :refer [defstate]] + [taoensso.timbre :refer-macros [log trace debug info warn error fatal]])) + +(defn server [] + (mount/start) + (let [host (or (:host @env) "127.0.0.1") + port (or (some-> @env :port js/parseInt) 3000)] + (http/start + {:handler (wrap-defaults router) + :host host + :port port + :on-success #(info "macchiato-web3-example started on" host ":" port)}))) diff --git a/macchiato-app/src/macchiato_web3_example/middleware.cljs b/macchiato-app/src/macchiato_web3_example/middleware.cljs new file mode 100644 index 0000000..44002b2 --- /dev/null +++ b/macchiato-app/src/macchiato_web3_example/middleware.cljs @@ -0,0 +1,8 @@ +(ns macchiato-web3-example.middleware + (:require + [macchiato.middleware.defaults :as defaults])) + +(defn wrap-defaults [handler] + (defaults/wrap-defaults handler defaults/site-defaults)) + + diff --git a/macchiato-app/src/macchiato_web3_example/routes.cljs b/macchiato-app/src/macchiato_web3_example/routes.cljs new file mode 100644 index 0000000..25267b6 --- /dev/null +++ b/macchiato-app/src/macchiato_web3_example/routes.cljs @@ -0,0 +1,56 @@ +(ns macchiato-web3-example.routes + (:require + [bidi.bidi :as bidi] + [macchiato.util.response :as r] + [cljs.nodejs :as node] + [cljs-web3.core :as web3] + [cljs-web3.eth :as web3-eth]) + (:require-macros + [hiccups.core :refer [html]])) + +;; (enable-console-print!) + +(def Web3 (node/require "web3")) +(def web3 (web3/create-web3 Web3 "http://localhost:8545/")) + +(defn- clj->json [m] + (->> m + clj->js + (.stringify js/JSON))) + +(defn- eth-accounts [] + (web3-eth/accounts web3)) + +(defn- account-balance [address] + (web3/from-wei Web3 + (web3-eth/get-balance web3 address) + :ether)) + +(defn- accounts [req res raise] + (-> {:accounts (eth-accounts)} + clj->json + (r/ok) + (r/content-type "application/json") + (res))) + +(defn- balance [req res raise] + (-> {:balance + {:eth (-> (eth-accounts) first account-balance)}} + clj->json + (r/ok) + (r/content-type "application/json") + (res))) + +(defn not-found [req res raise] + (-> (r/not-found) + (r/content-type "application/json") + (res))) + +(def routes + ["/" {"accounts" accounts + "balance" balance}]) + +(defn router [req res raise] + (if-let [{:keys [handler route-params]} (bidi/match-route* routes (:uri req) req)] + (handler (assoc req :route-params route-params) res raise) + (not-found req res raise))) diff --git a/macchiato-app/system.properties b/macchiato-app/system.properties new file mode 100644 index 0000000..5e8606c --- /dev/null +++ b/macchiato-app/system.properties @@ -0,0 +1 @@ +java.runtime.version=1.8 diff --git a/macchiato-app/test/macchiato_web3_example/core_test.cljs b/macchiato-app/test/macchiato_web3_example/core_test.cljs new file mode 100644 index 0000000..a825801 --- /dev/null +++ b/macchiato-app/test/macchiato_web3_example/core_test.cljs @@ -0,0 +1,9 @@ +(ns macchiato-web3-example.core-test + (:require + [cljs.test :refer-macros [is are deftest testing use-fixtures]] + [macchiato-web3-example.core])) + +(deftest test-core + (is (= true true))) + +