├── .dockerignore ├── .gitignore ├── README.md ├── docker-compose-test.yml ├── docker-compose.yml ├── integration ├── .formatter.exs ├── .gitignore ├── Dockerfile ├── README.md ├── lib │ └── integration.ex ├── mix.exs ├── mix.lock └── test │ ├── integration_test.exs │ └── test_helper.exs └── www ├── .formatter.exs ├── .gitignore ├── Dockerfile ├── bin ├── debug └── start ├── config └── config.exs ├── lib ├── public │ └── favicon.ico ├── www.apib ├── www.ex └── www │ ├── application.ex │ ├── env.ex │ ├── home_page.ex │ └── home_page.html.eex ├── mix.exs ├── mix.lock ├── priv └── localhost │ ├── certificate.pem │ ├── certificate_key.pem │ └── certificate_signing_request.pem └── test ├── test_helper.exs └── www_test.exs /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | _build 3 | cover 4 | deps 5 | doc 6 | test 7 | tmp 8 | erl_crash.dump 9 | *.ez 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc 12 | 13 | # If the VM crashes, it generates a dump, let's ignore it too. 14 | erl_crash.dump 15 | 16 | # Also ignore archive artifacts (built via "mix archive.build"). 17 | *.ez 18 | *.tar.gz 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Elixir on Docker 2 | 3 | **Quickly get started developing clustered Elixir applications for cloud environments.** 4 | 5 | [See a walkthrough of developing a chat application from this template](http://crowdhailer.me/talks/2017-10-31/live-coded-chat-app-in-45-minutes/) 6 | 7 | This project template provides a set of sensible defaults for a new application designed to be cloud native. 8 | It includes: 9 | 10 | - A main service `www`, this serves the main content offered by your application. 11 | - Secure HTTP/2 content delivery with [Ace](https://github.com/crowdhailer/ace) web server. 12 | - Code reloading in development environment using [ExSync](https://github.com/falood/exsync). 13 | - Automatic clustering. Setup for [docker-cloud](http://cloud.docker.com/) other environments coming soon. 14 | - Web based metrics, monitoring and observing with [Wobserver](https://github.com/shinyscorpion/wobserver). 15 | - Documentation driven development with [Raxx.ApiBlueprint](https://hex.pm/packages/raxx_api_blueprint). 16 | - Integration test suit running from the `integration` service. 17 | 18 | ## Get Started 19 | 20 | To use this template docker and docker-compose need to be installed on your machine. 21 | 22 | ### Clone this repository 23 | 24 | *Change project-name to your projects name.* 25 | 26 | ``` 27 | git clone 28 | cd 29 | ``` 30 | 31 | Delete git history. 32 | 33 | ``` 34 | rm -r .git 35 | ``` 36 | 37 | ### Fetch dependencies 38 | 39 | ``` 40 | docker-compose run --rm www mix deps.get 41 | ``` 42 | 43 | *All mix tasks for a service can be run this way, such as tests for a single service.* 44 | 45 | ### Run all services 46 | 47 | ``` 48 | docker-compose up 49 | ``` 50 | 51 | *Use `-d` to run in the background.* 52 | *Use `--build` to ensure images are rebuilt.* 53 | *Use `docker-compose down` to stop all services.* 54 | 55 | - HTTP endpoint available at: [http://localhost:8080/](http://localhost:8080/) 56 | - HTTPs endpoint available at: [https://localhost:8443/](https://localhost:8443/) 57 | - Wobserver dashboard available at: [http://localhost:4001/](http://localhost:4001/) 58 | 59 | 60 | ### Run integration tests 61 | 62 | ``` 63 | docker-compose \ 64 | -f docker-compose.yml \ 65 | -f docker-compose-test.yml \ 66 | run integration /bin/bash -c "mix deps.get; mix test" 67 | ``` 68 | 69 | *The `-f` flag specifies a compose file to use when starting services.* 70 | 71 | ### Attach iex session 72 | 73 | ```sh 74 | docker ps 75 | # Find container-id to attach to. 76 | 77 | docker exec -it sh bin/debug 78 | 79 | # in iex shell 80 | iex(debug@)1> Node.connect(:"app@") 81 | ``` 82 | -------------------------------------------------------------------------------- /docker-compose-test.yml: -------------------------------------------------------------------------------- 1 | version: '3.1' 2 | 3 | services: 4 | integration: 5 | build: 6 | context: "./integration" 7 | dockerfile: "Dockerfile" 8 | volumes: 9 | - ./integration:/home/elixir/app 10 | depends_on: 11 | - "www" 12 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.1' 2 | 3 | services: 4 | www: 5 | build: 6 | context: "./www" 7 | dockerfile: "Dockerfile" 8 | environment: 9 | - ERLANG_COOKIE=changeme 10 | - PEER_LIST=app@www-1 app@www-2 11 | volumes: 12 | - ./www:/home/elixir/app 13 | ports: 14 | - "8080:8080" 15 | - "8443:8443" 16 | - "4001:4001" 17 | -------------------------------------------------------------------------------- /integration/.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /integration/.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | integration-*.tar 24 | 25 | -------------------------------------------------------------------------------- /integration/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM elixir:1.6.1 2 | 3 | RUN apt-get update && apt-get install -y inotify-tools 4 | 5 | WORKDIR "/opt/app" 6 | 7 | RUN mix local.hex --force && mix local.rebar --force 8 | 9 | # NOTE integration project has no config files to COPY 10 | COPY mix.exs mix.lock ./ 11 | RUN mix do deps.get, deps.compile 12 | 13 | COPY . ./ 14 | 15 | CMD ["mix", "test"] 16 | -------------------------------------------------------------------------------- /integration/README.md: -------------------------------------------------------------------------------- 1 | # Integration 2 | 3 | Service to run integration tests over other services in the stack. 4 | -------------------------------------------------------------------------------- /integration/lib/integration.ex: -------------------------------------------------------------------------------- 1 | defmodule Integration do 2 | end 3 | -------------------------------------------------------------------------------- /integration/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Integration.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :integration, 7 | version: "0.1.0", 8 | elixir: "~> 1.5", 9 | start_permanent: Mix.env() == :prod, 10 | deps: deps() 11 | ] 12 | end 13 | 14 | def application do 15 | [ 16 | extra_applications: [:logger] 17 | ] 18 | end 19 | 20 | defp deps do 21 | [ 22 | {:httpoison, "~> 1.0"} 23 | ] 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /integration/mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "certifi": {:hex, :certifi, "2.0.0", "a0c0e475107135f76b8c1d5bc7efb33cd3815cb3cf3dea7aefdd174dabead064", [], [], "hexpm"}, 3 | "hackney": {:hex, :hackney, "1.11.0", "4951ee019df102492dabba66a09e305f61919a8a183a7860236c0fde586134b6", [:rebar3], [{:certifi, "2.0.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, 4 | "httpoison": {:hex, :httpoison, "1.0.0", "1f02f827148d945d40b24f0b0a89afe40bfe037171a6cf70f2486976d86921cd", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, 5 | "idna": {:hex, :idna, "5.1.0", "d72b4effeb324ad5da3cab1767cb16b17939004e789d8c0ad5b70f3cea20c89a", [], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, 6 | "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [], [], "hexpm"}, 7 | "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [], [], "hexpm"}, 8 | "server_sent_event": {:hex, :server_sent_event, "0.2.1", "4aa61ad29355351872cf9742269b75ecfb908bf64bad830dc4010d815037f9c7", [], [], "hexpm"}, 9 | "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [], [], "hexpm"}, 10 | "unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [], [], "hexpm"}, 11 | } 12 | -------------------------------------------------------------------------------- /integration/test/integration_test.exs: -------------------------------------------------------------------------------- 1 | defmodule IntegrationTest do 2 | use ExUnit.Case 3 | 4 | test "Home page is available" do 5 | {:ok, response} = HTTPoison.get("www:8080/") 6 | assert response.status_code == 200 7 | end 8 | 9 | test "Favicon is available" do 10 | {:ok, response} = HTTPoison.get("www:8080/favicon.ico") 11 | assert response.status_code == 200 12 | end 13 | 14 | @tag :skip 15 | test "Random page is not found" do 16 | {:ok, response} = HTTPoison.get("www:8080/random") 17 | assert response.status_code == 404 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /integration/test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /www/.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /www/.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | www-*.tar 24 | 25 | -------------------------------------------------------------------------------- /www/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM elixir:1.6.1 2 | 3 | RUN apt-get update && apt-get install -y inotify-tools 4 | 5 | WORKDIR "/opt/app" 6 | 7 | RUN mix local.hex --force && mix local.rebar --force 8 | 9 | COPY config/* config/ 10 | COPY mix.exs mix.lock ./ 11 | RUN mix do deps.get, deps.compile 12 | 13 | COPY . ./ 14 | 15 | CMD ["sh", "bin/start"] 16 | -------------------------------------------------------------------------------- /www/bin/debug: -------------------------------------------------------------------------------- 1 | iex --sname debug --cookie $ERLANG_COOKIE 2 | -------------------------------------------------------------------------------- /www/bin/start: -------------------------------------------------------------------------------- 1 | elixir --sname app --cookie $ERLANG_COOKIE -S mix run --no-halt 2 | -------------------------------------------------------------------------------- /www/config/config.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | config :exsync, 4 | extensions: [".erl", ".hrl", ".ex", ".tpl", ".apib", ".eex"], 5 | addition_dirs: ["/priv", "/lib"] 6 | -------------------------------------------------------------------------------- /www/lib/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CrowdHailer/elixir-on-docker/1a357e31644abe156e4e4506344afa8091deeb6c/www/lib/public/favicon.ico -------------------------------------------------------------------------------- /www/lib/www.apib: -------------------------------------------------------------------------------- 1 | FORMAT: 1A 2 | 3 | # My new Project 4 | 5 | Get started on your new application here 6 | 7 | # Index [/] 8 | 9 | ## Home Page [GET] 10 | 11 | Main page for everything. 12 | -------------------------------------------------------------------------------- /www/lib/www.ex: -------------------------------------------------------------------------------- 1 | defmodule WWW do 2 | 3 | @external_resource "lib/www.apib" 4 | use Raxx.ApiBlueprint, "./www.apib" 5 | use Raxx.Static, "./public" 6 | use Raxx.Logger 7 | end 8 | -------------------------------------------------------------------------------- /www/lib/www/application.ex: -------------------------------------------------------------------------------- 1 | defmodule WWW.Application do 2 | @moduledoc false 3 | 4 | use Application 5 | require Logger 6 | 7 | def start(_type, _args) do 8 | import Supervisor.Spec, warn: false 9 | 10 | env = WWW.Env.read() 11 | 12 | Logger.info("`#{__MODULE__}` started on node `#{Node.self()}`") 13 | {:ok, peer_list} = env.peer_list 14 | 15 | Logger.info("Connecting to peers: `[#{Enum.join(peer_list, ", ")}]`") 16 | Enum.each(peer_list, &Node.connect/1) 17 | Logger.info("Connected peers: `[#{Enum.join(Node.list(), ", ")}]`") 18 | 19 | cleartext_options = [port: 8080, cleartext: true] 20 | secure_options = [port: 8443, certfile: certificate_path(), keyfile: certificate_key_path()] 21 | 22 | children = [ 23 | supervisor(Ace.HTTP.Service, [{WWW, :config}, cleartext_options], id: :www_cleartext), 24 | supervisor(Ace.HTTP.Service, [{WWW, :config}, secure_options], id: :www_secure) 25 | ] 26 | 27 | opts = [strategy: :one_for_one, name: WWW.Supervisor] 28 | Supervisor.start_link(children, opts) 29 | end 30 | 31 | defp certificate_path() do 32 | Application.app_dir(:www, "priv/localhost/certificate.pem") 33 | end 34 | 35 | defp certificate_key_path() do 36 | Application.app_dir(:www, "priv/localhost/certificate_key.pem") 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /www/lib/www/env.ex: -------------------------------------------------------------------------------- 1 | defmodule WWW.Env do 2 | settings = [ 3 | peer_list: {:list, :atom} 4 | ] 5 | 6 | @enforce_keys Keyword.keys(settings) 7 | 8 | defstruct @enforce_keys 9 | 10 | def read() do 11 | values = 12 | for {setting, type} <- unquote(settings) do 13 | case fetch(setting) do 14 | {:ok, value} -> 15 | {setting, cast(type, value)} 16 | 17 | {:error, reason} -> 18 | {setting, {:error, reason}} 19 | end 20 | end 21 | 22 | struct(__MODULE__, values) 23 | end 24 | 25 | defp cast({:list, type}, raw) do 26 | Enum.reduce_while(String.split(raw), {:ok, []}, fn part, {:ok, values} -> 27 | case cast(type, part) do 28 | {:ok, next} -> 29 | {:cont, {:ok, values ++ [next]}} 30 | 31 | {:error, reason} -> 32 | {:halt, {:error, reason}} 33 | end 34 | end) 35 | end 36 | 37 | defp cast(:string, raw) do 38 | {:ok, raw} 39 | end 40 | 41 | defp cast(:atom, raw) do 42 | {:ok, String.to_atom(raw)} 43 | end 44 | 45 | defp cast(:integer, raw) do 46 | case Integer.parse(raw) do 47 | {integer, ""} -> 48 | {:ok, integer} 49 | 50 | _ -> 51 | {:error, {:invalid_integer, raw}} 52 | end 53 | end 54 | 55 | defp fetch(name) do 56 | case System.get_env("#{name}" |> String.upcase()) do 57 | nil -> 58 | {:error, {:no_env_variable, name}} 59 | 60 | value -> 61 | {:ok, value} 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /www/lib/www/home_page.ex: -------------------------------------------------------------------------------- 1 | defmodule WWW.HomePage do 2 | use Raxx.Server 3 | 4 | require EEx 5 | 6 | EEx.function_from_file(:defp, :home_page, Path.join(__DIR__, "./home_page.html.eex"), []) 7 | 8 | @impl Raxx.Server 9 | def handle_request(_request, _config) do 10 | body = home_page() 11 | 12 | Raxx.response(:ok) 13 | |> Raxx.set_header("content-type", "text/html") 14 | |> Raxx.set_body(body) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /www/lib/www/home_page.html.eex: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Elixir on Docker 5 | 6 | 7 |

Hello, World!

8 | 9 | 10 | -------------------------------------------------------------------------------- /www/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule WWW.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :www, 7 | version: "0.0.1", 8 | elixir: "~> 1.4", 9 | build_embedded: Mix.env() == :prod, 10 | start_permanent: Mix.env() == :prod, 11 | deps: deps() 12 | ] 13 | end 14 | 15 | def application do 16 | [extra_applications: [:logger], mod: {WWW.Application, []}] 17 | end 18 | 19 | defp deps do 20 | [ 21 | {:ace, "~> 0.15.2"}, 22 | {:raxx_static, "~> 0.5.0"}, 23 | {:raxx_api_blueprint, "~> 0.1.0"}, 24 | {:exsync, "~> 0.2.1"}, 25 | {:wobserver, "~> 0.1.8"} 26 | ] 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /www/mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "ace": {:hex, :ace, "0.15.10", "a6f7271e4d919091b90a70f85d77fc419080bad25c5ffafff2a48231508b2fca", [:mix], [{:hpack, "~> 0.2.3", [hex: :hpack_erl, repo: "hexpm", optional: false]}, {:raxx, "~> 0.14.5", [hex: :raxx, repo: "hexpm", optional: false]}], "hexpm"}, 3 | "certifi": {:hex, :certifi, "2.0.0", "a0c0e475107135f76b8c1d5bc7efb33cd3815cb3cf3dea7aefdd174dabead064", [:rebar3], [], "hexpm"}, 4 | "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, 5 | "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm"}, 6 | "exsync": {:hex, :exsync, "0.2.1", "c24229219ad32b979686baced40a214221e29765b11bac953e708f55dd0f1ae6", [:mix], [{:file_system, "~> 0.2", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm"}, 7 | "file_system": {:hex, :file_system, "0.2.4", "f0bdda195c0e46e987333e986452ec523aed21d784189144f647c43eaf307064", [:mix], [], "hexpm"}, 8 | "hackney": {:hex, :hackney, "1.11.0", "4951ee019df102492dabba66a09e305f61919a8a183a7860236c0fde586134b6", [:rebar3], [{:certifi, "2.0.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, 9 | "hpack": {:hex, :hpack_erl, "0.2.3", "17670f83ff984ae6cd74b1c456edde906d27ff013740ee4d9efaa4f1bf999633", [:rebar3], [], "hexpm"}, 10 | "httpoison": {:hex, :httpoison, "0.13.0", "bfaf44d9f133a6599886720f3937a7699466d23bb0cd7a88b6ba011f53c6f562", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, 11 | "idna": {:hex, :idna, "5.1.0", "d72b4effeb324ad5da3cab1767cb16b17939004e789d8c0ad5b70f3cea20c89a", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, 12 | "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, 13 | "mime": {:hex, :mime, "1.2.0", "78adaa84832b3680de06f88f0997e3ead3b451a440d183d688085be2d709b534", [:mix], [], "hexpm"}, 14 | "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"}, 15 | "plug": {:hex, :plug, "1.4.4", "279b547662272cd835a8ca089717201dd3be51bb4705354eaf1b0346744acc82", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, 16 | "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, 17 | "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"}, 18 | "raxx": {:hex, :raxx, "0.14.7", "77a6680c49848c3f6fb210a0b5691e63c94992a4012dc423f9abd5a141b430f0", [:mix], [], "hexpm"}, 19 | "raxx_api_blueprint": {:hex, :raxx_api_blueprint, "0.1.0", "d832d544a0f9a35ab2f6215259d4c1d6821af3e1749fe50a77e51470f28c8f2a", [:mix], [{:raxx, "~> 0.14.2", [hex: :raxx, repo: "hexpm", optional: false]}], "hexpm"}, 20 | "raxx_static": {:hex, :raxx_static, "0.5.0", "3f812dc5b03cb59ba22d187b72b03a025d065053a0e8593056be997ef7f1696b", [:mix], [{:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:raxx, "~> 0.14.2", [hex: :raxx, repo: "hexpm", optional: false]}], "hexpm"}, 21 | "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], [], "hexpm"}, 22 | "unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"}, 23 | "websocket_client": {:hex, :websocket_client, "1.3.0", "2275d7daaa1cdacebf2068891c9844b15f4fdc3de3ec2602420c2fb486db59b6", [:rebar3], [], "hexpm"}, 24 | "wobserver": {:hex, :wobserver, "0.1.8", "3ed5ea55478627f0593800ab83919b71f41fd426ec344e71cf1d975e6d2065b8", [:mix], [{:cowboy, "~> 1.1", [hex: :cowboy, repo: "hexpm", optional: false]}, {:httpoison, "~> 0.11 or ~> 0.12", [hex: :httpoison, repo: "hexpm", optional: false]}, {:plug, "~> 1.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 2.0 or ~> 3.1", [hex: :poison, repo: "hexpm", optional: false]}, {:websocket_client, "~> 1.2", [hex: :websocket_client, repo: "hexpm", optional: false]}], "hexpm"}, 25 | } 26 | -------------------------------------------------------------------------------- /www/priv/localhost/certificate.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDPjCCAiYCCQDrSpjNLJXcbTANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJV 3 | SzEPMA0GA1UECAwGTG9uZG9uMQ8wDQYDVQQHDAZMb25kb24xHDAaBgNVBAoME1dv 4 | cmtzaG9wIDE0IExpbWl0ZWQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNzA0Mjkx 5 | NzEyMTJaFw0xODA0MjkxNzEyMTJaMGExCzAJBgNVBAYTAlVLMQ8wDQYDVQQIDAZM 6 | b25kb24xDzANBgNVBAcMBkxvbmRvbjEcMBoGA1UECgwTV29ya3Nob3AgMTQgTGlt 7 | aXRlZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 8 | MIIBCgKCAQEAmNvyeepIRsAm6QuUezhFf3KXTygBoYX4oYMfXb6ZklDQ8QAT9Brs 9 | YUW1+QDDHPF3foDa+k4Mm8XL6/yHqQFutnhqqYysd/XovCG1ff7Vq0TpX7GAHGEv 10 | rTj/Q63xqVOmyZINgvi9TfRTIKZ5LIo6O0dV5LSv6cLTXa8bFBxybigTxL+HgzY0 11 | e3kQzuFSYLOxvLCd4j7YnTzOsYY8M49mNRDbja4SkDcRxqV0mJDUkUxayaDWXY06 12 | eY1RtiYHFeZQF/2iEKBnsm62VJcFiq/vnjNkc1SBxqIoXE7BrRe+yLi1TniMds8g 13 | GDUX0QqnhbuG/USuB0ev0VZNg+LiUYA/dQIDAQABMA0GCSqGSIb3DQEBCwUAA4IB 14 | AQAJaUbgakfrvtbD84hqpCGe0LmfCbjUEE5NIpu/TEvTjDgnuPVhwF2VBcVT6w96 15 | YPL3hxt9DsMUsXapaD5v+rGOVJGReKWyl1JN1nqd2BRkYD++6AznOul5WepXOSHO 16 | mCQOVPV2C3M+OYEDgLf9dcrGvpPJdexLLpy/xR1s9ZiNHKYGAfXxU6Va1uUi60lh 17 | g2jjTNkYhthLiqygatEViZ25D/N7GBUtbCLf7YBRDPId5JnAd2sFI4vGeJJZMee/ 18 | RCWKyzC+ttY9AHClpUcLc9YdJUofFfwHjO+jFf1u19WfUxlC3GPh45hSFZ/g+Hz8 19 | OxDc8YfSk8VAjJHu2ap7G9+G 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /www/priv/localhost/certificate_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAmNvyeepIRsAm6QuUezhFf3KXTygBoYX4oYMfXb6ZklDQ8QAT 3 | 9BrsYUW1+QDDHPF3foDa+k4Mm8XL6/yHqQFutnhqqYysd/XovCG1ff7Vq0TpX7GA 4 | HGEvrTj/Q63xqVOmyZINgvi9TfRTIKZ5LIo6O0dV5LSv6cLTXa8bFBxybigTxL+H 5 | gzY0e3kQzuFSYLOxvLCd4j7YnTzOsYY8M49mNRDbja4SkDcRxqV0mJDUkUxayaDW 6 | XY06eY1RtiYHFeZQF/2iEKBnsm62VJcFiq/vnjNkc1SBxqIoXE7BrRe+yLi1TniM 7 | ds8gGDUX0QqnhbuG/USuB0ev0VZNg+LiUYA/dQIDAQABAoIBAQCOfA9ExzbCBGEA 8 | wFOSnDxj9UvHdDI4/ulonBIDzyPVeFGbJAh1dRc8AMAEMEqvUwGgwLndsh0cor5X 9 | 5dgKmJQ7sHk0PDWTyHw9yWok3QMMl7q2AX26dnj7jfKbgquNu7TvlZ3UpMnIvWMz 10 | Pxoag2qOUQtmmWqUio99dzjVgULFHFOufcSfPsM1s71BhcZwcssG2JxmbjsG8r1w 11 | wR80p1VQjWzQdBe5Kgc+ZZZidf1SSW3W65zrdAV3tT9A78hmTdvhdXG9SOTu3Vt8 12 | Eab1eAewfds0FGJtUIzpJe6DCWFBnpAsGbWCf2CMkPRhWcLHVFN8jHO6KpKdqq0b 13 | bjXmZTAhAoGBAMZdXGqpFQWD73nVqrbdyZhtDU8VCAeT0d5XmlNQwvVPyPch50FL 14 | zDt2H9k6+Ko3p8wjSIWdKFvsqYuCFbBI8Lf4b3+Tu2Vi9ajFrnHyGUlB0X1Ny+nt 15 | iXpAUYCsGnp6hylzkNCGMQXQ55oDVjXr8/m/M8QpsGbAicu05HLuene5AoGBAMVF 16 | 0pyZNhjNBiJ/p/9AR1Rao8jWC5e70jxXdqX3kbWzobD1+qNEEP0klpnJGUIMASTb 17 | P99OrY5Fo1bxJecLjrwpQU63d90erAnirq7luIjQnXHAT6lg7CZX4uNW+P/iqc8p 18 | bvmcue6ptWf4HPtqtLpxXzbAlAA6SN5UDpFLbuudAoGAGaVOYnfTwO/K0Uyfkp7g 19 | BnXq55OHgztIQd+/kw/49LBJAjJ+7IE5OWLPQU2PgqpJZmoVYTjtU90oGmJKHY2A 20 | mbhj6fGWo8gEjLpqEE9Fl6QLypB5UZglUwnnv6QAlF8tBF3tlhgTVHYqy02tIrGL 21 | zHk83xqotNAlwJF1i6praPkCgYABMGGLlhTQY3P1A0X08OM9K+quzDN3r6cdu/04 22 | FNzo9nM0CNeA4mkjzXOm66JeVoovOa8R3nyHTf4lCQEMenJayfjdy5dKWuP4j0g0 23 | P6g0EuXQCLOyNqZVuNPiQOTxTeFuITbNBFfOi3FPdhxem48JTKOhRdnegntr85++ 24 | 2nCJtQKBgE9wMkBbQGKQxgV1VtNuLdhBPrCnvFvInX+/M5eaOLmg9IeDWER1vv5m 25 | kHbAa6t+jvi70ZQPrwMmAVT6YawpUuR7Ttaga/QMThw0dSvUuiPLXPno3ZZl9GM4 26 | OwKJHTSsqQf3jIT86Bm3wh375B8Dw0PPJUtraAkE4ioidTKuDnD4 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /www/priv/localhost/certificate_signing_request.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICpjCCAY4CAQAwYTELMAkGA1UEBhMCVUsxDzANBgNVBAgMBkxvbmRvbjEPMA0G 3 | A1UEBwwGTG9uZG9uMRwwGgYDVQQKDBNXb3Jrc2hvcCAxNCBMaW1pdGVkMRIwEAYD 4 | VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCY 5 | 2/J56khGwCbpC5R7OEV/cpdPKAGhhfihgx9dvpmSUNDxABP0GuxhRbX5AMMc8Xd+ 6 | gNr6Tgybxcvr/IepAW62eGqpjKx39ei8IbV9/tWrROlfsYAcYS+tOP9DrfGpU6bJ 7 | kg2C+L1N9FMgpnksijo7R1XktK/pwtNdrxsUHHJuKBPEv4eDNjR7eRDO4VJgs7G8 8 | sJ3iPtidPM6xhjwzj2Y1ENuNrhKQNxHGpXSYkNSRTFrJoNZdjTp5jVG2JgcV5lAX 9 | /aIQoGeybrZUlwWKr++eM2RzVIHGoihcTsGtF77IuLVOeIx2zyAYNRfRCqeFu4b9 10 | RK4HR6/RVk2D4uJRgD91AgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAXlpo5GxS 11 | oAavAybmPwlPYq9UekjsucRUx7q8MdI9+6JFgVZkJ47NZeq1ynuFM0QFGWC3Sbyx 12 | q0yKD3/UHIItta/4nQev366jAfi2Se6IE9RYYcmly36joHUB0MtceLYEGwazJbaB 13 | yq5nI0wgLDlQ318Uh9g+rc0DAohuEm+Guvq5xIVOXrkidhlPOiSHcXtIgGzHbfnz 14 | oir5rhtRgpCgulFq18ZWeQpIZ1UvTz0QbPvnnUZiVDxQVvKHwdRAjtVoJmuD9fuz 15 | F1ZiqKQWe4nqEw5pJuCK5tBdquB9hlWxaJhs/sR4dhcrjvX+8VxbGGaLlJV2mcrA 16 | EheSjS4N675U3w== 17 | -----END CERTIFICATE REQUEST----- 18 | -------------------------------------------------------------------------------- /www/test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /www/test/www_test.exs: -------------------------------------------------------------------------------- 1 | defmodule WWWTest do 2 | use ExUnit.Case 3 | end 4 | --------------------------------------------------------------------------------