init
This commit is contained in:
commit
08c904ba12
57 changed files with 12621 additions and 0 deletions
4
firmware/.formatter.exs
Normal file
4
firmware/.formatter.exs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Used by "mix format"
|
||||||
|
[
|
||||||
|
inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||||
|
]
|
17
firmware/.gitignore
vendored
Normal file
17
firmware/.gitignore
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# The directory Mix will write compiled artifacts to.
|
||||||
|
/_build/
|
||||||
|
|
||||||
|
# If you run "mix test --cover", coverage assets end up here.
|
||||||
|
/cover/
|
||||||
|
|
||||||
|
# The directory Mix downloads your dependencies sources to.
|
||||||
|
/deps/
|
||||||
|
|
||||||
|
# Where 3rd-party dependencies like ExDoc output generated docs.
|
||||||
|
/doc/
|
||||||
|
|
||||||
|
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||||
|
/.fetch
|
||||||
|
|
||||||
|
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||||
|
erl_crash.dump
|
32
firmware/README.md
Normal file
32
firmware/README.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# Firmware
|
||||||
|
|
||||||
|
**TODO: Add description**
|
||||||
|
|
||||||
|
## Targets
|
||||||
|
|
||||||
|
Nerves applications produce images for hardware targets based on the
|
||||||
|
`MIX_TARGET` environment variable. If `MIX_TARGET` is unset, `mix` builds an
|
||||||
|
image that runs on the host (e.g., your laptop). This is useful for executing
|
||||||
|
logic tests, running utilities, and debugging. Other targets are represented by
|
||||||
|
a short name like `rpi3` that maps to a Nerves system image for that platform.
|
||||||
|
All of this logic is in the generated `mix.exs` and may be customized. For more
|
||||||
|
information about targets see:
|
||||||
|
|
||||||
|
https://hexdocs.pm/nerves/targets.html#content
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
To start your Nerves app:
|
||||||
|
* `export MIX_TARGET=my_target` or prefix every command with
|
||||||
|
`MIX_TARGET=my_target`. For example, `MIX_TARGET=rpi3`
|
||||||
|
* Install dependencies with `mix deps.get`
|
||||||
|
* Create firmware with `mix firmware`
|
||||||
|
* Burn to an SD card with `mix firmware.burn`
|
||||||
|
|
||||||
|
## Learn more
|
||||||
|
|
||||||
|
* Official docs: https://hexdocs.pm/nerves/getting-started.html
|
||||||
|
* Official website: https://nerves-project.org/
|
||||||
|
* Forum: https://elixirforum.com/c/nerves-forum
|
||||||
|
* Discussion Slack elixir-lang #nerves ([Invite](https://elixir-slackin.herokuapp.com/))
|
||||||
|
* Source: https://github.com/nerves-project/nerves
|
43
firmware/config/config.exs
Normal file
43
firmware/config/config.exs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# This file is responsible for configuring your application
|
||||||
|
# and its dependencies with the aid of the Mix.Config module.
|
||||||
|
#
|
||||||
|
# This configuration file is loaded before any dependency and
|
||||||
|
# is restricted to this project.
|
||||||
|
use Mix.Config
|
||||||
|
|
||||||
|
config :firmware, target: Mix.target()
|
||||||
|
|
||||||
|
# Customize non-Elixir parts of the firmware. See
|
||||||
|
# https://hexdocs.pm/nerves/advanced-configuration.html for details.
|
||||||
|
|
||||||
|
config :nerves, :firmware, rootfs_overlay: "rootfs_overlay"
|
||||||
|
|
||||||
|
# Use shoehorn to start the main application. See the shoehorn
|
||||||
|
# docs for separating out critical OTP applications such as those
|
||||||
|
# involved with firmware updates.
|
||||||
|
|
||||||
|
config :shoehorn,
|
||||||
|
init: [:nerves_runtime, :nerves_init_gadget],
|
||||||
|
app: Mix.Project.config()[:app]
|
||||||
|
|
||||||
|
# Use Ringlogger as the logger backend and remove :console.
|
||||||
|
# See https://hexdocs.pm/ring_logger/readme.html for more information on
|
||||||
|
# configuring ring_logger.
|
||||||
|
|
||||||
|
config :logger, backends: [RingLogger]
|
||||||
|
|
||||||
|
config :ui, UiWeb.Endpoint,
|
||||||
|
url: [host: "localhost"],
|
||||||
|
http: [port: 80],
|
||||||
|
secret_key_base: "HEY05EB1dFVSu6KykKHuS4rQPQzSHv4F7mGVB/gnDLrIu75wE/ytBXy2TaL3A6RA",
|
||||||
|
root: Path.dirname(__DIR__),
|
||||||
|
server: true,
|
||||||
|
render_errors: [view: UiWeb.ErrorView, accepts: ~w(html json)],
|
||||||
|
pubsub: [name: Nerves.PubSub, adapter: Phoenix.PubSub.PG2],
|
||||||
|
code_reloader: false
|
||||||
|
|
||||||
|
config :phoenix, :json_library, Jason
|
||||||
|
|
||||||
|
if Mix.target() != :host do
|
||||||
|
import_config "target.exs"
|
||||||
|
end
|
44
firmware/config/target.exs
Normal file
44
firmware/config/target.exs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
use Mix.Config
|
||||||
|
|
||||||
|
# Authorize the device to receive firmware using your public key.
|
||||||
|
# See https://hexdocs.pm/nerves_firmware_ssh/readme.html for more information
|
||||||
|
# on configuring nerves_firmware_ssh.
|
||||||
|
|
||||||
|
keys =
|
||||||
|
[
|
||||||
|
Path.join([System.user_home!(), ".ssh", "id_rsa.pub"]),
|
||||||
|
Path.join([System.user_home!(), ".ssh", "id_ecdsa.pub"]),
|
||||||
|
Path.join([System.user_home!(), ".ssh", "id_ed25519.pub"])
|
||||||
|
]
|
||||||
|
|> Enum.filter(&File.exists?/1)
|
||||||
|
|
||||||
|
if keys == [],
|
||||||
|
do:
|
||||||
|
Mix.raise("""
|
||||||
|
No SSH public keys found in ~/.ssh. An ssh authorized key is needed to
|
||||||
|
log into the Nerves device and update firmware on it using ssh.
|
||||||
|
See your project's config.exs for this error message.
|
||||||
|
""")
|
||||||
|
|
||||||
|
config :nerves_firmware_ssh,
|
||||||
|
authorized_keys: Enum.map(keys, &File.read!/1)
|
||||||
|
|
||||||
|
# Configure nerves_init_gadget.
|
||||||
|
# See https://hexdocs.pm/nerves_init_gadget/readme.html for more information.
|
||||||
|
|
||||||
|
# Setting the node_name will enable Erlang Distribution.
|
||||||
|
# Only enable this for prod if you understand the risks.
|
||||||
|
node_name = if Mix.env() != :prod, do: "firmware"
|
||||||
|
|
||||||
|
config :nerves_init_gadget,
|
||||||
|
ifname: "usb0",
|
||||||
|
address_method: :dhcpd,
|
||||||
|
mdns_domain: "nerves.local",
|
||||||
|
node_name: node_name,
|
||||||
|
node_host: :mdns_domain
|
||||||
|
|
||||||
|
# Import target specific config. This must remain at the bottom
|
||||||
|
# of this file so it overrides the configuration defined above.
|
||||||
|
# Uncomment to use target specific configurations
|
||||||
|
|
||||||
|
# import_config "#{Mix.target()}.exs"
|
18
firmware/lib/firmware.ex
Normal file
18
firmware/lib/firmware.ex
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
defmodule Firmware do
|
||||||
|
@moduledoc """
|
||||||
|
Documentation for Firmware.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Hello world.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> Firmware.hello
|
||||||
|
:world
|
||||||
|
|
||||||
|
"""
|
||||||
|
def hello do
|
||||||
|
:world
|
||||||
|
end
|
||||||
|
end
|
42
firmware/lib/firmware/application.ex
Normal file
42
firmware/lib/firmware/application.ex
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
defmodule Firmware.Application do
|
||||||
|
# See https://hexdocs.pm/elixir/Application.html
|
||||||
|
# for more information on OTP Applications
|
||||||
|
@moduledoc false
|
||||||
|
|
||||||
|
use Application
|
||||||
|
|
||||||
|
def start(_type, _args) do
|
||||||
|
# See https://hexdocs.pm/elixir/Supervisor.html
|
||||||
|
# for other strategies and supported options
|
||||||
|
opts = [strategy: :one_for_one, name: Firmware.Supervisor]
|
||||||
|
children =
|
||||||
|
[
|
||||||
|
# Children for all targets
|
||||||
|
# Starts a worker by calling: Firmware.Worker.start_link(arg)
|
||||||
|
# {Firmware.Worker, arg},
|
||||||
|
] ++ children(target())
|
||||||
|
|
||||||
|
Supervisor.start_link(children, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
# List all child processes to be supervised
|
||||||
|
def children(:host) do
|
||||||
|
[
|
||||||
|
# Children that only run on the host
|
||||||
|
# Starts a worker by calling: Firmware.Worker.start_link(arg)
|
||||||
|
# {Firmware.Worker, arg},
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def children(_target) do
|
||||||
|
[
|
||||||
|
# Children for all targets except host
|
||||||
|
# Starts a worker by calling: Firmware.Worker.start_link(arg)
|
||||||
|
# {Firmware.Worker, arg},
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def target() do
|
||||||
|
Application.get_env(:firmware, :target)
|
||||||
|
end
|
||||||
|
end
|
71
firmware/mix.exs
Normal file
71
firmware/mix.exs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
defmodule Firmware.MixProject do
|
||||||
|
use Mix.Project
|
||||||
|
|
||||||
|
@app :firmware
|
||||||
|
@version "0.1.0"
|
||||||
|
@all_targets [:rpi, :rpi0, :rpi2, :rpi3, :rpi3a, :bbb, :x86_64]
|
||||||
|
|
||||||
|
def project do
|
||||||
|
[
|
||||||
|
app: @app,
|
||||||
|
version: @version,
|
||||||
|
elixir: "~> 1.9",
|
||||||
|
archives: [nerves_bootstrap: "~> 1.6"],
|
||||||
|
start_permanent: Mix.env() == :prod,
|
||||||
|
build_embedded: true,
|
||||||
|
aliases: [loadconfig: [&bootstrap/1]],
|
||||||
|
deps: deps(),
|
||||||
|
releases: [{@app, release()}],
|
||||||
|
preferred_cli_target: [run: :host, test: :host]
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Starting nerves_bootstrap adds the required aliases to Mix.Project.config()
|
||||||
|
# Aliases are only added if MIX_TARGET is set.
|
||||||
|
def bootstrap(args) do
|
||||||
|
Application.start(:nerves_bootstrap)
|
||||||
|
Mix.Task.run("loadconfig", args)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run "mix help compile.app" to learn about applications.
|
||||||
|
def application do
|
||||||
|
[
|
||||||
|
mod: {Firmware.Application, []},
|
||||||
|
extra_applications: [:logger, :runtime_tools]
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run "mix help deps" to learn about dependencies.
|
||||||
|
defp deps do
|
||||||
|
[
|
||||||
|
# Dependencies for all targets
|
||||||
|
{:nerves, "~> 1.5.0", runtime: false},
|
||||||
|
{:shoehorn, "~> 0.6"},
|
||||||
|
{:ring_logger, "~> 0.6"},
|
||||||
|
{:toolshed, "~> 0.2"},
|
||||||
|
{:ui, path: "../ui"},
|
||||||
|
|
||||||
|
# Dependencies for all targets except :host
|
||||||
|
{:nerves_runtime, "~> 0.6", targets: @all_targets},
|
||||||
|
{:nerves_init_gadget, "~> 0.4", targets: @all_targets},
|
||||||
|
|
||||||
|
# Dependencies for specific targets
|
||||||
|
{:nerves_system_rpi, "~> 1.8", runtime: false, targets: :rpi},
|
||||||
|
{:nerves_system_rpi0, "~> 1.8", runtime: false, targets: :rpi0},
|
||||||
|
{:nerves_system_rpi2, "~> 1.8", runtime: false, targets: :rpi2},
|
||||||
|
{:nerves_system_rpi3, "~> 1.8", runtime: false, targets: :rpi3},
|
||||||
|
{:nerves_system_rpi3a, "~> 1.8", runtime: false, targets: :rpi3a},
|
||||||
|
{:nerves_system_bbb, "~> 2.3", runtime: false, targets: :bbb},
|
||||||
|
{:nerves_system_x86_64, "~> 1.8", runtime: false, targets: :x86_64},
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def release do
|
||||||
|
[
|
||||||
|
overwrite: true,
|
||||||
|
cookie: "#{@app}_cookie",
|
||||||
|
include_erts: &Nerves.Release.erts/0,
|
||||||
|
steps: [&Nerves.Release.init/1, :assemble]
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
52
firmware/mix.lock
Normal file
52
firmware/mix.lock
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
%{
|
||||||
|
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
|
||||||
|
"cowboy": {:hex, :cowboy, "2.6.3", "99aa50e94e685557cad82e704457336a453d4abcb77839ad22dbe71f311fcc06", [:rebar3], [{:cowlib, "~> 2.7.3", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"cowlib": {:hex, :cowlib, "2.7.3", "a7ffcd0917e6d50b4d5fb28e9e2085a0ceb3c97dea310505f7460ff5ed764ce9", [:rebar3], [], "hexpm"},
|
||||||
|
"db_connection": {:hex, :db_connection, "2.1.0", "122e2f62c4906bf2e49554f1e64db5030c19229aa40935f33088e7d543aa79d0", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"},
|
||||||
|
"dns": {:hex, :dns, "2.1.2", "81c46d39f7934f0e73368355126e4266762cf227ba61d5889635d83b2d64a493", [:mix], [{:socket, "~> 0.3.13", [hex: :socket, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"ecto": {:hex, :ecto, "3.1.7", "fa21d06ef56cdc2fdaa62574e8c3ba34a2751d44ea34c30bc65f0728421043e5", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
|
"ecto_sql": {:hex, :ecto_sql, "3.1.6", "1e80e30d16138a729c717f73dcb938590bcdb3a4502f3012414d0cbb261045d8", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.1.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.9.1", [hex: :mariaex, repo: "hexpm", optional: true]}, {:myxql, "~> 0.2.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14.0 or ~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm"},
|
||||||
|
"gettext": {:hex, :gettext, "0.17.0", "abe21542c831887a2b16f4c94556db9c421ab301aee417b7c4fbde7fbdbe01ec", [:mix], [], "hexpm"},
|
||||||
|
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
|
"mdns": {:hex, :mdns, "1.0.3", "f08414daf5636bf5cd364611e838818e9250c91a3282a817ad9174b03e757401", [:mix], [{:dns, "~> 2.0", [hex: :dns, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
|
||||||
|
"nerves": {:hex, :nerves, "1.5.0", "8be45730c90583272563629a30ebb1099515a156a1de844f8dc443eaf63f7b8f", [:mix], [{:distillery, "~> 2.1", [hex: :distillery, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
|
"nerves_firmware_ssh": {:hex, :nerves_firmware_ssh, "0.4.4", "12b0d9c84ec9f79c1b0ac0de1c575372ef972d0c58ce21c36bf354062c6222d9", [:mix], [{:nerves_runtime, "~> 0.6", [hex: :nerves_runtime, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"nerves_init_gadget": {:hex, :nerves_init_gadget, "0.6.0", "64eb8877b438678aed6d421737326a8cac3810b425e00abd5c7a3a0e95054988", [:mix], [{:mdns, "~> 1.0", [hex: :mdns, repo: "hexpm", optional: false]}, {:nerves_firmware_ssh, "~> 0.2", [hex: :nerves_firmware_ssh, repo: "hexpm", optional: false]}, {:nerves_network, "~> 0.3", [hex: :nerves_network, repo: "hexpm", optional: false]}, {:nerves_runtime, "~> 0.3", [hex: :nerves_runtime, repo: "hexpm", optional: false]}, {:one_dhcpd, "~> 0.1", [hex: :one_dhcpd, repo: "hexpm", optional: false]}, {:ring_logger, "~> 0.4", [hex: :ring_logger, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"nerves_network": {:hex, :nerves_network, "0.5.5", "4690c362707f76c4072810bd9639b2ae8eb7dd9c21119656308b462a087230aa", [:make, :mix], [{:elixir_make, "~> 0.5", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nerves_network_interface, "~> 0.4.4", [hex: :nerves_network_interface, repo: "hexpm", optional: false]}, {:nerves_wpa_supplicant, "~> 0.5", [hex: :nerves_wpa_supplicant, repo: "hexpm", optional: false]}, {:one_dhcpd, "~> 0.2.0", [hex: :one_dhcpd, repo: "hexpm", optional: false]}, {:system_registry, "~> 0.7", [hex: :system_registry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"nerves_network_interface": {:hex, :nerves_network_interface, "0.4.6", "d50e57daca8154f0f780fd98eb5ae94a005579e0d72d69840e80e228375d88ad", [:make, :mix], [{:elixir_make, "~> 0.5", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"nerves_runtime": {:hex, :nerves_runtime, "0.10.1", "4cefcfbcb99f237def5346e1dc881bda523811ede26adfd3a2204e6f26530146", [:make, :mix], [{:elixir_make, "~> 0.5", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:system_registry, "~> 0.5", [hex: :system_registry, repo: "hexpm", optional: false]}, {:uboot_env, "~> 0.1", [hex: :uboot_env, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"nerves_system_bbb": {:hex, :nerves_system_bbb, "2.3.0", "887b7aafe036ad832bb285c0622cc1d136861857b8d949f1c957c74e1f4efc20", [:mix], [{:nerves, "~> 1.4", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_system_br, "1.8.2", [hex: :nerves_system_br, repo: "hexpm", optional: false]}, {:nerves_system_linter, "~> 0.3.0", [hex: :nerves_system_linter, repo: "hexpm", optional: false]}, {:nerves_toolchain_arm_unknown_linux_gnueabihf, "1.2.0", [hex: :nerves_toolchain_arm_unknown_linux_gnueabihf, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"nerves_system_br": {:hex, :nerves_system_br, "1.8.2", "bd1639c9aadbeb104c1d1080554048eeed28352841bf5621a7cbeaca53e99404", [:mix], [], "hexpm"},
|
||||||
|
"nerves_system_linter": {:hex, :nerves_system_linter, "0.3.0", "84e0f63c8ac196b16b77608bbe7df66dcf352845c4e4fb394bffd2b572025413", [:mix], [], "hexpm"},
|
||||||
|
"nerves_system_rpi": {:hex, :nerves_system_rpi, "1.8.0", "c8e0df198a2e9923a94583a1f7cfaeeb0d6057aca0a2e02ecd34dd24b51e6ebc", [:mix], [{:nerves, "~> 1.4", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_system_br, "1.8.2", [hex: :nerves_system_br, repo: "hexpm", optional: false]}, {:nerves_system_linter, "~> 0.3.0", [hex: :nerves_system_linter, repo: "hexpm", optional: false]}, {:nerves_toolchain_armv6_rpi_linux_gnueabi, "1.2.0", [hex: :nerves_toolchain_armv6_rpi_linux_gnueabi, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"nerves_system_rpi0": {:hex, :nerves_system_rpi0, "1.8.0", "332f1bc0a19243690bb88c62d8dd40bf0e97af6e6ac8c6e0e90a90d0c52d3990", [:mix], [{:nerves, "~> 1.4", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_system_br, "1.8.2", [hex: :nerves_system_br, repo: "hexpm", optional: false]}, {:nerves_system_linter, "~> 0.3.0", [hex: :nerves_system_linter, repo: "hexpm", optional: false]}, {:nerves_toolchain_armv6_rpi_linux_gnueabi, "1.2.0", [hex: :nerves_toolchain_armv6_rpi_linux_gnueabi, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"nerves_system_rpi2": {:hex, :nerves_system_rpi2, "1.8.0", "a81a2720fc5d4f28f586272833ae71ab200101cf41d0fe7ed1c560703993dd31", [:mix], [{:nerves, "~> 1.4", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_system_br, "1.8.2", [hex: :nerves_system_br, repo: "hexpm", optional: false]}, {:nerves_system_linter, "~> 0.3.0", [hex: :nerves_system_linter, repo: "hexpm", optional: false]}, {:nerves_toolchain_arm_unknown_linux_gnueabihf, "1.2.0", [hex: :nerves_toolchain_arm_unknown_linux_gnueabihf, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"nerves_system_rpi3": {:hex, :nerves_system_rpi3, "1.8.0", "64cc1e669613a64b29fba8c8ad7ee2686600956a056aeb5833753ed6cf939812", [:mix], [{:nerves, "~> 1.4", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_system_br, "1.8.2", [hex: :nerves_system_br, repo: "hexpm", optional: false]}, {:nerves_system_linter, "~> 0.3.0", [hex: :nerves_system_linter, repo: "hexpm", optional: false]}, {:nerves_toolchain_arm_unknown_linux_gnueabihf, "1.2.0", [hex: :nerves_toolchain_arm_unknown_linux_gnueabihf, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"nerves_system_rpi3a": {:hex, :nerves_system_rpi3a, "1.8.0", "466af465517a46bc3f96a58a2f8abfd1298f60ff659b1cf636dc21eee5bd8d9b", [:mix], [{:nerves, "~> 1.4", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_system_br, "1.8.2", [hex: :nerves_system_br, repo: "hexpm", optional: false]}, {:nerves_system_linter, "~> 0.3.0", [hex: :nerves_system_linter, repo: "hexpm", optional: false]}, {:nerves_toolchain_arm_unknown_linux_gnueabihf, "1.2.0", [hex: :nerves_toolchain_arm_unknown_linux_gnueabihf, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"nerves_system_x86_64": {:hex, :nerves_system_x86_64, "1.8.0", "f58a9043dede98666249d2ab53ba81fbae51ecfefec21eb9e034772eb327b77d", [:mix], [{:nerves, "~> 1.4", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_system_br, "1.8.2", [hex: :nerves_system_br, repo: "hexpm", optional: false]}, {:nerves_system_linter, "~> 0.3.0", [hex: :nerves_system_linter, repo: "hexpm", optional: false]}, {:nerves_toolchain_x86_64_unknown_linux_musl, "1.2.0", [hex: :nerves_toolchain_x86_64_unknown_linux_musl, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"nerves_toolchain_arm_unknown_linux_gnueabihf": {:hex, :nerves_toolchain_arm_unknown_linux_gnueabihf, "1.2.0", "ba48ce7c846ee12dfca8148dc7240988d96a3f2eb9c234bf08bffe4f0f7a3c62", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 1.6.0", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"nerves_toolchain_armv6_rpi_linux_gnueabi": {:hex, :nerves_toolchain_armv6_rpi_linux_gnueabi, "1.2.0", "007668c7ad1f73bad8fd54ad1a27a3b0fb91bca51b4af6bb3bbdac968ccae0ba", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 1.6.0", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"nerves_toolchain_ctng": {:hex, :nerves_toolchain_ctng, "1.6.0", "452f8589c1a58ac787477caab20a8cfc6671e345837ccc19beefe49ae35ba983", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"nerves_toolchain_x86_64_unknown_linux_musl": {:hex, :nerves_toolchain_x86_64_unknown_linux_musl, "1.2.0", "fbe688fa561b03190765e269d4336333c4961a48d2acd3f6cb283443a058e138", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 1.6.0", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"nerves_wpa_supplicant": {:hex, :nerves_wpa_supplicant, "0.5.2", "4ec392fc08faf35f50d1070446c2e5019f6b85bd53f5716f904e3f75716d9596", [:make, :mix], [{:elixir_make, "~> 0.5", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"one_dhcpd": {:hex, :one_dhcpd, "0.2.2", "2dfdcad61ed7c6d9f652bc1a06f14924a497de2c2aa2d4f48ee3b1aa13be0f33", [:make, :mix], [{:elixir_make, "~> 0.5", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"phoenix": {:hex, :phoenix, "1.4.9", "746d098e10741c334d88143d3c94cab1756435f94387a63441792e66ec0ee974", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"phoenix_ecto": {:hex, :phoenix_ecto, "4.0.0", "c43117a136e7399ea04ecaac73f8f23ee0ffe3e07acfcb8062fe5f4c9f0f6531", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"phoenix_html": {:hex, :phoenix_html, "2.13.3", "850e292ff6e204257f5f9c4c54a8cb1f6fbc16ed53d360c2b780a3d0ba333867", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"},
|
||||||
|
"plug": {:hex, :plug, "1.8.2", "0bcce1daa420f189a6491f3940cc77ea7fb1919761175c9c3b59800d897440fc", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
|
"plug_cowboy": {:hex, :plug_cowboy, "2.1.0", "b75768153c3a8a9e8039d4b25bb9b14efbc58e9c4a6e6a270abff1cd30cbe320", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
|
||||||
|
"postgrex": {:hex, :postgrex, "0.14.3", "5754dee2fdf6e9e508cbf49ab138df964278700b764177e8f3871e658b345a1e", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
|
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
|
||||||
|
"ring_logger": {:hex, :ring_logger, "0.8.0", "b1baddc269099b2afe2ea3a87b8e2b71e57331c0000038ae55090068aac679db", [:mix], [], "hexpm"},
|
||||||
|
"shoehorn": {:hex, :shoehorn, "0.6.0", "f9a1b7d6212cf18ba91c4f71c26076059df33cea4db2eb3c098bfa6673349412", [:mix], [{:distillery, "~> 2.1", [hex: :distillery, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
|
"socket": {:hex, :socket, "0.3.13", "98a2ab20ce17f95fb512c5cadddba32b57273e0d2dba2d2e5f976c5969d0c632", [:mix], [], "hexpm"},
|
||||||
|
"system_registry": {:hex, :system_registry, "0.8.2", "df791dc276652fcfb53be4dab823e05f8269b96ac57c26f86a67838dbc0eefe7", [:mix], [], "hexpm"},
|
||||||
|
"telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"},
|
||||||
|
"toolshed": {:hex, :toolshed, "0.2.10", "31e33f3bfbd88085a5f34844930a75e579940417873eb8a5c25b9525ad1a1372", [:mix], [{:nerves_runtime, "~> 0.8", [hex: :nerves_runtime, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
|
"uboot_env": {:hex, :uboot_env, "0.1.1", "b01e3ec0973e99473234f27839e29e63b5b81eba6a136a18a78d049d4813d6c5", [:mix], [], "hexpm"},
|
||||||
|
}
|
43
firmware/rel/vm.args.eex
Normal file
43
firmware/rel/vm.args.eex
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
## Add custom options here
|
||||||
|
|
||||||
|
## Distributed Erlang Options
|
||||||
|
## The cookie needs to be configured prior to vm boot for
|
||||||
|
## for read only filesystem.
|
||||||
|
|
||||||
|
-setcookie <%= @release.options[:cookie] %>
|
||||||
|
|
||||||
|
## Use Ctrl-C to interrupt the current shell rather than invoking the emulator's
|
||||||
|
## break handler and possibly exiting the VM.
|
||||||
|
+Bc
|
||||||
|
|
||||||
|
# Allow time warps so that the Erlang system time can more closely match the
|
||||||
|
# OS system time.
|
||||||
|
+C multi_time_warp
|
||||||
|
|
||||||
|
## Load code at system startup
|
||||||
|
## See http://erlang.org/doc/system_principles/system_principles.html#code-loading-strategy
|
||||||
|
-mode embedded
|
||||||
|
|
||||||
|
## Save the shell history between reboots
|
||||||
|
## See http://erlang.org/doc/man/kernel_app.html for additional options
|
||||||
|
-kernel shell_history enabled
|
||||||
|
|
||||||
|
## Enable heartbeat monitoring of the Erlang runtime system
|
||||||
|
-heart -env HEART_BEAT_TIMEOUT 30
|
||||||
|
|
||||||
|
## Start the Elixir shell
|
||||||
|
|
||||||
|
-noshell
|
||||||
|
-user Elixir.IEx.CLI
|
||||||
|
|
||||||
|
## Enable colors in the shell
|
||||||
|
-elixir ansi_enabled true
|
||||||
|
|
||||||
|
## Options added after -extra are interpreted as plain arguments and can be
|
||||||
|
## retrieved using :init.get_plain_arguments(). Options before the "--" are
|
||||||
|
## interpreted by Elixir and anything afterwards is left around for other IEx
|
||||||
|
## and user applications.
|
||||||
|
-extra --no-halt
|
||||||
|
--
|
||||||
|
--dot-iex /etc/iex.exs
|
||||||
|
|
18
firmware/rootfs_overlay/etc/iex.exs
Normal file
18
firmware/rootfs_overlay/etc/iex.exs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Add Toolshed helpers to the IEx session
|
||||||
|
use Toolshed
|
||||||
|
|
||||||
|
if RingLogger in Application.get_env(:logger, :backends, []) do
|
||||||
|
IO.puts """
|
||||||
|
RingLogger is collecting log messages from Elixir and Linux. To see the
|
||||||
|
messages, either attach the current IEx session to the logger:
|
||||||
|
|
||||||
|
RingLogger.attach
|
||||||
|
|
||||||
|
or print the next messages in the log:
|
||||||
|
|
||||||
|
RingLogger.next
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
# Be careful when adding to this file. Nearly any error can crash the VM and
|
||||||
|
# cause a reboot.
|
8
firmware/test/firmware_test.exs
Normal file
8
firmware/test/firmware_test.exs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
defmodule FirmwareTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
doctest Firmware
|
||||||
|
|
||||||
|
test "greets the world" do
|
||||||
|
assert Firmware.hello() == :world
|
||||||
|
end
|
||||||
|
end
|
1
firmware/test/test_helper.exs
Normal file
1
firmware/test/test_helper.exs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
ExUnit.start()
|
4
ui/.formatter.exs
Normal file
4
ui/.formatter.exs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[
|
||||||
|
import_deps: [:phoenix],
|
||||||
|
inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"]
|
||||||
|
]
|
34
ui/.gitignore
vendored
Normal file
34
ui/.gitignore
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# The directory Mix will write compiled artifacts to.
|
||||||
|
/_build/
|
||||||
|
|
||||||
|
# If you run "mix test --cover", coverage assets end up here.
|
||||||
|
/cover/
|
||||||
|
|
||||||
|
# The directory Mix downloads your dependencies sources to.
|
||||||
|
/deps/
|
||||||
|
|
||||||
|
# Where 3rd-party dependencies like ExDoc output generated docs.
|
||||||
|
/doc/
|
||||||
|
|
||||||
|
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||||
|
/.fetch
|
||||||
|
|
||||||
|
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||||
|
erl_crash.dump
|
||||||
|
|
||||||
|
# Also ignore archive artifacts (built via "mix archive.build").
|
||||||
|
*.ez
|
||||||
|
|
||||||
|
# Ignore package tarball (built via "mix hex.build").
|
||||||
|
ui-*.tar
|
||||||
|
|
||||||
|
# If NPM crashes, it generates a log, let's ignore it too.
|
||||||
|
npm-debug.log
|
||||||
|
|
||||||
|
# The directory NPM downloads your dependencies sources to.
|
||||||
|
/assets/node_modules/
|
||||||
|
|
||||||
|
# Since we are building assets from assets/,
|
||||||
|
# we ignore priv/static. You may want to comment
|
||||||
|
# this depending on your deployment strategy.
|
||||||
|
/priv/static/
|
19
ui/README.md
Normal file
19
ui/README.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Ui
|
||||||
|
|
||||||
|
To start your Phoenix server:
|
||||||
|
|
||||||
|
* Install dependencies with `mix deps.get`
|
||||||
|
* Install Node.js dependencies with `cd assets && npm install`
|
||||||
|
* Start Phoenix endpoint with `mix phx.server`
|
||||||
|
|
||||||
|
Now you can visit [`localhost:4000`](http://localhost:4000) from your browser.
|
||||||
|
|
||||||
|
Ready to run in production? Please [check our deployment guides](https://hexdocs.pm/phoenix/deployment.html).
|
||||||
|
|
||||||
|
## Learn more
|
||||||
|
|
||||||
|
* Official website: http://www.phoenixframework.org/
|
||||||
|
* Guides: https://hexdocs.pm/phoenix/overview.html
|
||||||
|
* Docs: https://hexdocs.pm/phoenix
|
||||||
|
* Mailing list: http://groups.google.com/group/phoenix-talk
|
||||||
|
* Source: https://github.com/phoenixframework/phoenix
|
5
ui/assets/.babelrc
Normal file
5
ui/assets/.babelrc
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
"@babel/preset-env"
|
||||||
|
]
|
||||||
|
}
|
3
ui/assets/css/app.css
Normal file
3
ui/assets/css/app.css
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
/* This file is for your main application css. */
|
||||||
|
|
||||||
|
@import "./phoenix.css";
|
134
ui/assets/css/phoenix.css
Normal file
134
ui/assets/css/phoenix.css
Normal file
File diff suppressed because one or more lines are too long
17
ui/assets/js/app.js
Normal file
17
ui/assets/js/app.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// We need to import the CSS so that webpack will load it.
|
||||||
|
// The MiniCssExtractPlugin is used to separate it out into
|
||||||
|
// its own CSS file.
|
||||||
|
import css from "../css/app.css"
|
||||||
|
|
||||||
|
// webpack automatically bundles all modules in your
|
||||||
|
// entry points. Those entry points can be configured
|
||||||
|
// in "webpack.config.js".
|
||||||
|
//
|
||||||
|
// Import dependencies
|
||||||
|
//
|
||||||
|
import "phoenix_html"
|
||||||
|
|
||||||
|
// Import local files
|
||||||
|
//
|
||||||
|
// Local files can be imported directly using relative paths, for example:
|
||||||
|
// import socket from "./socket"
|
63
ui/assets/js/socket.js
Normal file
63
ui/assets/js/socket.js
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// NOTE: The contents of this file will only be executed if
|
||||||
|
// you uncomment its entry in "assets/js/app.js".
|
||||||
|
|
||||||
|
// To use Phoenix channels, the first step is to import Socket,
|
||||||
|
// and connect at the socket path in "lib/web/endpoint.ex".
|
||||||
|
//
|
||||||
|
// Pass the token on params as below. Or remove it
|
||||||
|
// from the params if you are not using authentication.
|
||||||
|
import {Socket} from "phoenix"
|
||||||
|
|
||||||
|
let socket = new Socket("/socket", {params: {token: window.userToken}})
|
||||||
|
|
||||||
|
// When you connect, you'll often need to authenticate the client.
|
||||||
|
// For example, imagine you have an authentication plug, `MyAuth`,
|
||||||
|
// which authenticates the session and assigns a `:current_user`.
|
||||||
|
// If the current user exists you can assign the user's token in
|
||||||
|
// the connection for use in the layout.
|
||||||
|
//
|
||||||
|
// In your "lib/web/router.ex":
|
||||||
|
//
|
||||||
|
// pipeline :browser do
|
||||||
|
// ...
|
||||||
|
// plug MyAuth
|
||||||
|
// plug :put_user_token
|
||||||
|
// end
|
||||||
|
//
|
||||||
|
// defp put_user_token(conn, _) do
|
||||||
|
// if current_user = conn.assigns[:current_user] do
|
||||||
|
// token = Phoenix.Token.sign(conn, "user socket", current_user.id)
|
||||||
|
// assign(conn, :user_token, token)
|
||||||
|
// else
|
||||||
|
// conn
|
||||||
|
// end
|
||||||
|
// end
|
||||||
|
//
|
||||||
|
// Now you need to pass this token to JavaScript. You can do so
|
||||||
|
// inside a script tag in "lib/web/templates/layout/app.html.eex":
|
||||||
|
//
|
||||||
|
// <script>window.userToken = "<%= assigns[:user_token] %>";</script>
|
||||||
|
//
|
||||||
|
// You will need to verify the user token in the "connect/3" function
|
||||||
|
// in "lib/web/channels/user_socket.ex":
|
||||||
|
//
|
||||||
|
// def connect(%{"token" => token}, socket, _connect_info) do
|
||||||
|
// # max_age: 1209600 is equivalent to two weeks in seconds
|
||||||
|
// case Phoenix.Token.verify(socket, "user socket", token, max_age: 1209600) do
|
||||||
|
// {:ok, user_id} ->
|
||||||
|
// {:ok, assign(socket, :user, user_id)}
|
||||||
|
// {:error, reason} ->
|
||||||
|
// :error
|
||||||
|
// end
|
||||||
|
// end
|
||||||
|
//
|
||||||
|
// Finally, connect to the socket:
|
||||||
|
socket.connect()
|
||||||
|
|
||||||
|
// Now that you are connected, you can join channels with a topic:
|
||||||
|
let channel = socket.channel("topic:subtopic", {})
|
||||||
|
channel.join()
|
||||||
|
.receive("ok", resp => { console.log("Joined successfully", resp) })
|
||||||
|
.receive("error", resp => { console.log("Unable to join", resp) })
|
||||||
|
|
||||||
|
export default socket
|
11146
ui/assets/package-lock.json
generated
Normal file
11146
ui/assets/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
24
ui/assets/package.json
Normal file
24
ui/assets/package.json
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"repository": {},
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"deploy": "webpack --mode production",
|
||||||
|
"watch": "webpack --mode development --watch"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"phoenix": "file:../deps/phoenix",
|
||||||
|
"phoenix_html": "file:../deps/phoenix_html"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.0.0",
|
||||||
|
"@babel/preset-env": "^7.0.0",
|
||||||
|
"babel-loader": "^8.0.0",
|
||||||
|
"copy-webpack-plugin": "^4.5.0",
|
||||||
|
"css-loader": "^2.1.1",
|
||||||
|
"mini-css-extract-plugin": "^0.4.0",
|
||||||
|
"optimize-css-assets-webpack-plugin": "^4.0.0",
|
||||||
|
"uglifyjs-webpack-plugin": "^1.2.4",
|
||||||
|
"webpack": "4.4.0",
|
||||||
|
"webpack-cli": "^2.0.10"
|
||||||
|
}
|
||||||
|
}
|
BIN
ui/assets/static/favicon.ico
Normal file
BIN
ui/assets/static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
ui/assets/static/images/phoenix.png
Normal file
BIN
ui/assets/static/images/phoenix.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
5
ui/assets/static/robots.txt
Normal file
5
ui/assets/static/robots.txt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
|
||||||
|
#
|
||||||
|
# To ban all spiders from the entire site uncomment the next two lines:
|
||||||
|
# User-agent: *
|
||||||
|
# Disallow: /
|
41
ui/assets/webpack.config.js
Normal file
41
ui/assets/webpack.config.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
const path = require('path');
|
||||||
|
const glob = require('glob');
|
||||||
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
|
||||||
|
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||||
|
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||||
|
|
||||||
|
module.exports = (env, options) => ({
|
||||||
|
optimization: {
|
||||||
|
minimizer: [
|
||||||
|
new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: false }),
|
||||||
|
new OptimizeCSSAssetsPlugin({})
|
||||||
|
]
|
||||||
|
},
|
||||||
|
entry: {
|
||||||
|
'./js/app.js': glob.sync('./vendor/**/*.js').concat(['./js/app.js'])
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: 'app.js',
|
||||||
|
path: path.resolve(__dirname, '../priv/static/js')
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: {
|
||||||
|
loader: 'babel-loader'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: [MiniCssExtractPlugin.loader, 'css-loader']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new MiniCssExtractPlugin({ filename: '../css/app.css' }),
|
||||||
|
new CopyWebpackPlugin([{ from: 'static/', to: '../' }])
|
||||||
|
]
|
||||||
|
});
|
27
ui/config/config.exs
Normal file
27
ui/config/config.exs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# This file is responsible for configuring your application
|
||||||
|
# and its dependencies with the aid of the Mix.Config module.
|
||||||
|
#
|
||||||
|
# This configuration file is loaded before any dependency and
|
||||||
|
# is restricted to this project.
|
||||||
|
|
||||||
|
# General application configuration
|
||||||
|
use Mix.Config
|
||||||
|
|
||||||
|
# Configures the endpoint
|
||||||
|
config :ui, UiWeb.Endpoint,
|
||||||
|
url: [host: "localhost"],
|
||||||
|
secret_key_base: "FkfuB09FEncz4aAi6hS6w5bsNast+D1P12MckXr5dlRdhtFJrKqgEhvhpTU3qzgh",
|
||||||
|
render_errors: [view: UiWeb.ErrorView, accepts: ~w(html json)],
|
||||||
|
pubsub: [name: Ui.PubSub, adapter: Phoenix.PubSub.PG2]
|
||||||
|
|
||||||
|
# Configures Elixir's Logger
|
||||||
|
config :logger, :console,
|
||||||
|
format: "$time $metadata[$level] $message\n",
|
||||||
|
metadata: [:request_id]
|
||||||
|
|
||||||
|
# Use Jason for JSON parsing in Phoenix
|
||||||
|
config :phoenix, :json_library, Jason
|
||||||
|
|
||||||
|
# Import environment specific config. This must remain at the bottom
|
||||||
|
# of this file so it overrides the configuration defined above.
|
||||||
|
import_config "#{Mix.env()}.exs"
|
67
ui/config/dev.exs
Normal file
67
ui/config/dev.exs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
use Mix.Config
|
||||||
|
|
||||||
|
# For development, we disable any cache and enable
|
||||||
|
# debugging and code reloading.
|
||||||
|
#
|
||||||
|
# The watchers configuration can be used to run external
|
||||||
|
# watchers to your application. For example, we use it
|
||||||
|
# with webpack to recompile .js and .css sources.
|
||||||
|
config :ui, UiWeb.Endpoint,
|
||||||
|
http: [port: 4000],
|
||||||
|
debug_errors: true,
|
||||||
|
code_reloader: true,
|
||||||
|
check_origin: false,
|
||||||
|
watchers: [
|
||||||
|
node: [
|
||||||
|
"node_modules/webpack/bin/webpack.js",
|
||||||
|
"--mode",
|
||||||
|
"development",
|
||||||
|
"--watch-stdin",
|
||||||
|
cd: Path.expand("../assets", __DIR__)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
# ## SSL Support
|
||||||
|
#
|
||||||
|
# In order to use HTTPS in development, a self-signed
|
||||||
|
# certificate can be generated by running the following
|
||||||
|
# Mix task:
|
||||||
|
#
|
||||||
|
# mix phx.gen.cert
|
||||||
|
#
|
||||||
|
# Note that this task requires Erlang/OTP 20 or later.
|
||||||
|
# Run `mix help phx.gen.cert` for more information.
|
||||||
|
#
|
||||||
|
# The `http:` config above can be replaced with:
|
||||||
|
#
|
||||||
|
# https: [
|
||||||
|
# port: 4001,
|
||||||
|
# cipher_suite: :strong,
|
||||||
|
# keyfile: "priv/cert/selfsigned_key.pem",
|
||||||
|
# certfile: "priv/cert/selfsigned.pem"
|
||||||
|
# ],
|
||||||
|
#
|
||||||
|
# If desired, both `http:` and `https:` keys can be
|
||||||
|
# configured to run both http and https servers on
|
||||||
|
# different ports.
|
||||||
|
|
||||||
|
# Watch static and templates for browser reloading.
|
||||||
|
config :ui, UiWeb.Endpoint,
|
||||||
|
live_reload: [
|
||||||
|
patterns: [
|
||||||
|
~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
|
||||||
|
~r"priv/gettext/.*(po)$",
|
||||||
|
~r"lib/ui_web/{live,views}/.*(ex)$",
|
||||||
|
~r"lib/ui_web/templates/.*(eex)$"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
# Do not include metadata nor timestamps in development logs
|
||||||
|
config :logger, :console, format: "[$level] $message\n"
|
||||||
|
|
||||||
|
# Set a higher stacktrace during development. Avoid configuring such
|
||||||
|
# in production as building large stacktraces may be expensive.
|
||||||
|
config :phoenix, :stacktrace_depth, 20
|
||||||
|
|
||||||
|
# Initialize plugs at runtime for faster development compilation
|
||||||
|
config :phoenix, :plug_init_mode, :runtime
|
55
ui/config/prod.exs
Normal file
55
ui/config/prod.exs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
use Mix.Config
|
||||||
|
|
||||||
|
# For production, don't forget to configure the url host
|
||||||
|
# to something meaningful, Phoenix uses this information
|
||||||
|
# when generating URLs.
|
||||||
|
#
|
||||||
|
# Note we also include the path to a cache manifest
|
||||||
|
# containing the digested version of static files. This
|
||||||
|
# manifest is generated by the `mix phx.digest` task,
|
||||||
|
# which you should run after static files are built and
|
||||||
|
# before starting your production server.
|
||||||
|
config :ui, UiWeb.Endpoint,
|
||||||
|
url: [host: "example.com", port: 80],
|
||||||
|
cache_static_manifest: "priv/static/cache_manifest.json"
|
||||||
|
|
||||||
|
# Do not print debug messages in production
|
||||||
|
config :logger, level: :info
|
||||||
|
|
||||||
|
# ## SSL Support
|
||||||
|
#
|
||||||
|
# To get SSL working, you will need to add the `https` key
|
||||||
|
# to the previous section and set your `:url` port to 443:
|
||||||
|
#
|
||||||
|
# config :ui, UiWeb.Endpoint,
|
||||||
|
# ...
|
||||||
|
# url: [host: "example.com", port: 443],
|
||||||
|
# https: [
|
||||||
|
# :inet6,
|
||||||
|
# port: 443,
|
||||||
|
# cipher_suite: :strong,
|
||||||
|
# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
|
||||||
|
# certfile: System.get_env("SOME_APP_SSL_CERT_PATH")
|
||||||
|
# ]
|
||||||
|
#
|
||||||
|
# The `cipher_suite` is set to `:strong` to support only the
|
||||||
|
# latest and more secure SSL ciphers. This means old browsers
|
||||||
|
# and clients may not be supported. You can set it to
|
||||||
|
# `:compatible` for wider support.
|
||||||
|
#
|
||||||
|
# `:keyfile` and `:certfile` expect an absolute path to the key
|
||||||
|
# and cert in disk or a relative path inside priv, for example
|
||||||
|
# "priv/ssl/server.key". For all supported SSL configuration
|
||||||
|
# options, see https://hexdocs.pm/plug/Plug.SSL.html#configure/1
|
||||||
|
#
|
||||||
|
# We also recommend setting `force_ssl` in your endpoint, ensuring
|
||||||
|
# no data is ever sent via http, always redirecting to https:
|
||||||
|
#
|
||||||
|
# config :ui, UiWeb.Endpoint,
|
||||||
|
# force_ssl: [hsts: true]
|
||||||
|
#
|
||||||
|
# Check `Plug.SSL` for all available options in `force_ssl`.
|
||||||
|
|
||||||
|
# Finally import the config/prod.secret.exs which loads secrets
|
||||||
|
# and configuration from environment variables.
|
||||||
|
import_config "prod.secret.exs"
|
26
ui/config/prod.secret.exs
Normal file
26
ui/config/prod.secret.exs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# In this file, we load production configuration and secrets
|
||||||
|
# from environment variables. You can also hardcode secrets,
|
||||||
|
# although such is generally not recommended and you have to
|
||||||
|
# remember to add this file to your .gitignore.
|
||||||
|
use Mix.Config
|
||||||
|
|
||||||
|
secret_key_base =
|
||||||
|
System.get_env("SECRET_KEY_BASE") ||
|
||||||
|
raise """
|
||||||
|
environment variable SECRET_KEY_BASE is missing.
|
||||||
|
You can generate one by calling: mix phx.gen.secret
|
||||||
|
"""
|
||||||
|
|
||||||
|
config :ui, UiWeb.Endpoint,
|
||||||
|
http: [:inet6, port: String.to_integer(System.get_env("PORT") || "4000")],
|
||||||
|
secret_key_base: secret_key_base
|
||||||
|
|
||||||
|
# ## Using releases (Elixir v1.9+)
|
||||||
|
#
|
||||||
|
# If you are doing OTP releases, you need to instruct Phoenix
|
||||||
|
# to start each relevant endpoint:
|
||||||
|
#
|
||||||
|
# config :ui, UiWeb.Endpoint, server: true
|
||||||
|
#
|
||||||
|
# Then you can assemble a release by calling `mix release`.
|
||||||
|
# See `mix help release` for more information.
|
10
ui/config/test.exs
Normal file
10
ui/config/test.exs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
use Mix.Config
|
||||||
|
|
||||||
|
# We don't run a server during test. If one is required,
|
||||||
|
# you can enable the server option below.
|
||||||
|
config :ui, UiWeb.Endpoint,
|
||||||
|
http: [port: 4002],
|
||||||
|
server: false
|
||||||
|
|
||||||
|
# Print only warnings and errors during test
|
||||||
|
config :logger, level: :warn
|
9
ui/lib/ui.ex
Normal file
9
ui/lib/ui.ex
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Ui do
|
||||||
|
@moduledoc """
|
||||||
|
Ui keeps the contexts that define your domain
|
||||||
|
and business logic.
|
||||||
|
|
||||||
|
Contexts are also responsible for managing your data, regardless
|
||||||
|
if it comes from the database, an external API or others.
|
||||||
|
"""
|
||||||
|
end
|
29
ui/lib/ui/application.ex
Normal file
29
ui/lib/ui/application.ex
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
defmodule Ui.Application do
|
||||||
|
# See https://hexdocs.pm/elixir/Application.html
|
||||||
|
# for more information on OTP Applications
|
||||||
|
@moduledoc false
|
||||||
|
|
||||||
|
use Application
|
||||||
|
|
||||||
|
def start(_type, _args) do
|
||||||
|
# List all child processes to be supervised
|
||||||
|
children = [
|
||||||
|
# Start the endpoint when the application starts
|
||||||
|
UiWeb.Endpoint
|
||||||
|
# Starts a worker by calling: Ui.Worker.start_link(arg)
|
||||||
|
# {Ui.Worker, arg},
|
||||||
|
]
|
||||||
|
|
||||||
|
# See https://hexdocs.pm/elixir/Supervisor.html
|
||||||
|
# for other strategies and supported options
|
||||||
|
opts = [strategy: :one_for_one, name: Ui.Supervisor]
|
||||||
|
Supervisor.start_link(children, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Tell Phoenix to update the endpoint configuration
|
||||||
|
# whenever the application is updated.
|
||||||
|
def config_change(changed, _new, removed) do
|
||||||
|
UiWeb.Endpoint.config_change(changed, removed)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
69
ui/lib/ui_web.ex
Normal file
69
ui/lib/ui_web.ex
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
defmodule UiWeb do
|
||||||
|
@moduledoc """
|
||||||
|
The entrypoint for defining your web interface, such
|
||||||
|
as controllers, views, channels and so on.
|
||||||
|
|
||||||
|
This can be used in your application as:
|
||||||
|
|
||||||
|
use UiWeb, :controller
|
||||||
|
use UiWeb, :view
|
||||||
|
|
||||||
|
The definitions below will be executed for every view,
|
||||||
|
controller, etc, so keep them short and clean, focused
|
||||||
|
on imports, uses and aliases.
|
||||||
|
|
||||||
|
Do NOT define functions inside the quoted expressions
|
||||||
|
below. Instead, define any helper function in modules
|
||||||
|
and import those modules here.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def controller do
|
||||||
|
quote do
|
||||||
|
use Phoenix.Controller, namespace: UiWeb
|
||||||
|
|
||||||
|
import Plug.Conn
|
||||||
|
import UiWeb.Gettext
|
||||||
|
alias UiWeb.Router.Helpers, as: Routes
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def view do
|
||||||
|
quote do
|
||||||
|
use Phoenix.View,
|
||||||
|
root: "lib/ui_web/templates",
|
||||||
|
namespace: UiWeb
|
||||||
|
|
||||||
|
# Import convenience functions from controllers
|
||||||
|
import Phoenix.Controller, only: [get_flash: 1, get_flash: 2, view_module: 1]
|
||||||
|
|
||||||
|
# Use all HTML functionality (forms, tags, etc)
|
||||||
|
use Phoenix.HTML
|
||||||
|
|
||||||
|
import UiWeb.ErrorHelpers
|
||||||
|
import UiWeb.Gettext
|
||||||
|
alias UiWeb.Router.Helpers, as: Routes
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def router do
|
||||||
|
quote do
|
||||||
|
use Phoenix.Router
|
||||||
|
import Plug.Conn
|
||||||
|
import Phoenix.Controller
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def channel do
|
||||||
|
quote do
|
||||||
|
use Phoenix.Channel
|
||||||
|
import UiWeb.Gettext
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
When used, dispatch to the appropriate controller/view/etc.
|
||||||
|
"""
|
||||||
|
defmacro __using__(which) when is_atom(which) do
|
||||||
|
apply(__MODULE__, which, [])
|
||||||
|
end
|
||||||
|
end
|
33
ui/lib/ui_web/channels/user_socket.ex
Normal file
33
ui/lib/ui_web/channels/user_socket.ex
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
defmodule UiWeb.UserSocket do
|
||||||
|
use Phoenix.Socket
|
||||||
|
|
||||||
|
## Channels
|
||||||
|
# channel "room:*", UiWeb.RoomChannel
|
||||||
|
|
||||||
|
# Socket params are passed from the client and can
|
||||||
|
# be used to verify and authenticate a user. After
|
||||||
|
# verification, you can put default assigns into
|
||||||
|
# the socket that will be set for all channels, ie
|
||||||
|
#
|
||||||
|
# {:ok, assign(socket, :user_id, verified_user_id)}
|
||||||
|
#
|
||||||
|
# To deny connection, return `:error`.
|
||||||
|
#
|
||||||
|
# See `Phoenix.Token` documentation for examples in
|
||||||
|
# performing token verification on connect.
|
||||||
|
def connect(_params, socket, _connect_info) do
|
||||||
|
{:ok, socket}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Socket id's are topics that allow you to identify all sockets for a given user:
|
||||||
|
#
|
||||||
|
# def id(socket), do: "user_socket:#{socket.assigns.user_id}"
|
||||||
|
#
|
||||||
|
# Would allow you to broadcast a "disconnect" event and terminate
|
||||||
|
# all active sockets and channels for a given user:
|
||||||
|
#
|
||||||
|
# UiWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
|
||||||
|
#
|
||||||
|
# Returning `nil` makes this socket anonymous.
|
||||||
|
def id(_socket), do: nil
|
||||||
|
end
|
7
ui/lib/ui_web/controllers/page_controller.ex
Normal file
7
ui/lib/ui_web/controllers/page_controller.ex
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
defmodule UiWeb.PageController do
|
||||||
|
use UiWeb, :controller
|
||||||
|
|
||||||
|
def index(conn, _params) do
|
||||||
|
render(conn, "index.html")
|
||||||
|
end
|
||||||
|
end
|
46
ui/lib/ui_web/endpoint.ex
Normal file
46
ui/lib/ui_web/endpoint.ex
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
defmodule UiWeb.Endpoint do
|
||||||
|
use Phoenix.Endpoint, otp_app: :ui
|
||||||
|
|
||||||
|
socket "/socket", UiWeb.UserSocket,
|
||||||
|
websocket: true,
|
||||||
|
longpoll: false
|
||||||
|
|
||||||
|
# Serve at "/" the static files from "priv/static" directory.
|
||||||
|
#
|
||||||
|
# You should set gzip to true if you are running phx.digest
|
||||||
|
# when deploying your static files in production.
|
||||||
|
plug Plug.Static,
|
||||||
|
at: "/",
|
||||||
|
from: :ui,
|
||||||
|
gzip: false,
|
||||||
|
only: ~w(css fonts images js favicon.ico robots.txt)
|
||||||
|
|
||||||
|
# Code reloading can be explicitly enabled under the
|
||||||
|
# :code_reloader configuration of your endpoint.
|
||||||
|
if code_reloading? do
|
||||||
|
socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
|
||||||
|
plug Phoenix.LiveReloader
|
||||||
|
plug Phoenix.CodeReloader
|
||||||
|
end
|
||||||
|
|
||||||
|
plug Plug.RequestId
|
||||||
|
plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]
|
||||||
|
|
||||||
|
plug Plug.Parsers,
|
||||||
|
parsers: [:urlencoded, :multipart, :json],
|
||||||
|
pass: ["*/*"],
|
||||||
|
json_decoder: Phoenix.json_library()
|
||||||
|
|
||||||
|
plug Plug.MethodOverride
|
||||||
|
plug Plug.Head
|
||||||
|
|
||||||
|
# The session will be stored in the cookie and signed,
|
||||||
|
# this means its contents can be read but not tampered with.
|
||||||
|
# Set :encryption_salt if you would also like to encrypt it.
|
||||||
|
plug Plug.Session,
|
||||||
|
store: :cookie,
|
||||||
|
key: "_ui_key",
|
||||||
|
signing_salt: "N1da3BnS"
|
||||||
|
|
||||||
|
plug UiWeb.Router
|
||||||
|
end
|
24
ui/lib/ui_web/gettext.ex
Normal file
24
ui/lib/ui_web/gettext.ex
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
defmodule UiWeb.Gettext do
|
||||||
|
@moduledoc """
|
||||||
|
A module providing Internationalization with a gettext-based API.
|
||||||
|
|
||||||
|
By using [Gettext](https://hexdocs.pm/gettext),
|
||||||
|
your module gains a set of macros for translations, for example:
|
||||||
|
|
||||||
|
import UiWeb.Gettext
|
||||||
|
|
||||||
|
# Simple translation
|
||||||
|
gettext("Here is the string to translate")
|
||||||
|
|
||||||
|
# Plural translation
|
||||||
|
ngettext("Here is the string to translate",
|
||||||
|
"Here are the strings to translate",
|
||||||
|
3)
|
||||||
|
|
||||||
|
# Domain-based translation
|
||||||
|
dgettext("errors", "Here is the error message to translate")
|
||||||
|
|
||||||
|
See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage.
|
||||||
|
"""
|
||||||
|
use Gettext, otp_app: :ui
|
||||||
|
end
|
26
ui/lib/ui_web/router.ex
Normal file
26
ui/lib/ui_web/router.ex
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
defmodule UiWeb.Router do
|
||||||
|
use UiWeb, :router
|
||||||
|
|
||||||
|
pipeline :browser do
|
||||||
|
plug :accepts, ["html"]
|
||||||
|
plug :fetch_session
|
||||||
|
plug :fetch_flash
|
||||||
|
plug :protect_from_forgery
|
||||||
|
plug :put_secure_browser_headers
|
||||||
|
end
|
||||||
|
|
||||||
|
pipeline :api do
|
||||||
|
plug :accepts, ["json"]
|
||||||
|
end
|
||||||
|
|
||||||
|
scope "/", UiWeb do
|
||||||
|
pipe_through :browser
|
||||||
|
|
||||||
|
get "/", PageController, :index
|
||||||
|
end
|
||||||
|
|
||||||
|
# Other scopes may use custom stacks.
|
||||||
|
# scope "/api", UiWeb do
|
||||||
|
# pipe_through :api
|
||||||
|
# end
|
||||||
|
end
|
30
ui/lib/ui_web/templates/layout/app.html.eex
Normal file
30
ui/lib/ui_web/templates/layout/app.html.eex
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
|
<title>Ui · Phoenix Framework</title>
|
||||||
|
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<section class="container">
|
||||||
|
<nav role="navigation">
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://hexdocs.pm/phoenix/overview.html">Get Started</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<a href="http://phoenixframework.org/" class="phx-logo">
|
||||||
|
<img src="<%= Routes.static_path(@conn, "/images/phoenix.png") %>" alt="Phoenix Framework Logo"/>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
</header>
|
||||||
|
<main role="main" class="container">
|
||||||
|
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
|
||||||
|
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
|
||||||
|
<%= render @view_module, @view_template, assigns %>
|
||||||
|
</main>
|
||||||
|
<script type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
35
ui/lib/ui_web/templates/page/index.html.eex
Normal file
35
ui/lib/ui_web/templates/page/index.html.eex
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<section class="phx-hero">
|
||||||
|
<h1><%= gettext "Welcome to %{name}!", name: "Phoenix" %></h1>
|
||||||
|
<p>A productive web framework that<br/>does not compromise speed or maintainability.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="row">
|
||||||
|
<article class="column">
|
||||||
|
<h2>Resources</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="https://hexdocs.pm/phoenix/overview.html">Guides & Docs</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://github.com/phoenixframework/phoenix">Source</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://github.com/phoenixframework/phoenix/blob/v1.4/CHANGELOG.md">v1.4 Changelog</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
<article class="column">
|
||||||
|
<h2>Help</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="https://elixirforum.com/c/phoenix-forum">Forum</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://webchat.freenode.net/?channels=elixir-lang">#elixir-lang on Freenode IRC</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://twitter.com/elixirphoenix">Twitter @elixirphoenix</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
</section>
|
44
ui/lib/ui_web/views/error_helpers.ex
Normal file
44
ui/lib/ui_web/views/error_helpers.ex
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
defmodule UiWeb.ErrorHelpers do
|
||||||
|
@moduledoc """
|
||||||
|
Conveniences for translating and building error messages.
|
||||||
|
"""
|
||||||
|
|
||||||
|
use Phoenix.HTML
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Generates tag for inlined form input errors.
|
||||||
|
"""
|
||||||
|
def error_tag(form, field) do
|
||||||
|
Enum.map(Keyword.get_values(form.errors, field), fn error ->
|
||||||
|
content_tag(:span, translate_error(error), class: "help-block")
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Translates an error message using gettext.
|
||||||
|
"""
|
||||||
|
def translate_error({msg, opts}) do
|
||||||
|
# When using gettext, we typically pass the strings we want
|
||||||
|
# to translate as a static argument:
|
||||||
|
#
|
||||||
|
# # Translate "is invalid" in the "errors" domain
|
||||||
|
# dgettext("errors", "is invalid")
|
||||||
|
#
|
||||||
|
# # Translate the number of files with plural rules
|
||||||
|
# dngettext("errors", "1 file", "%{count} files", count)
|
||||||
|
#
|
||||||
|
# Because the error messages we show in our forms and APIs
|
||||||
|
# are defined inside Ecto, we need to translate them dynamically.
|
||||||
|
# This requires us to call the Gettext module passing our gettext
|
||||||
|
# backend as first argument.
|
||||||
|
#
|
||||||
|
# Note we use the "errors" domain, which means translations
|
||||||
|
# should be written to the errors.po file. The :count option is
|
||||||
|
# set by Ecto and indicates we should also apply plural rules.
|
||||||
|
if count = opts[:count] do
|
||||||
|
Gettext.dngettext(UiWeb.Gettext, "errors", msg, msg, count, opts)
|
||||||
|
else
|
||||||
|
Gettext.dgettext(UiWeb.Gettext, "errors", msg, opts)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
16
ui/lib/ui_web/views/error_view.ex
Normal file
16
ui/lib/ui_web/views/error_view.ex
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
defmodule UiWeb.ErrorView do
|
||||||
|
use UiWeb, :view
|
||||||
|
|
||||||
|
# If you want to customize a particular status code
|
||||||
|
# for a certain format, you may uncomment below.
|
||||||
|
# def render("500.html", _assigns) do
|
||||||
|
# "Internal Server Error"
|
||||||
|
# end
|
||||||
|
|
||||||
|
# By default, Phoenix returns the status message from
|
||||||
|
# the template name. For example, "404.html" becomes
|
||||||
|
# "Not Found".
|
||||||
|
def template_not_found(template, _assigns) do
|
||||||
|
Phoenix.Controller.status_message_from_template(template)
|
||||||
|
end
|
||||||
|
end
|
3
ui/lib/ui_web/views/layout_view.ex
Normal file
3
ui/lib/ui_web/views/layout_view.ex
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
defmodule UiWeb.LayoutView do
|
||||||
|
use UiWeb, :view
|
||||||
|
end
|
3
ui/lib/ui_web/views/page_view.ex
Normal file
3
ui/lib/ui_web/views/page_view.ex
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
defmodule UiWeb.PageView do
|
||||||
|
use UiWeb, :view
|
||||||
|
end
|
44
ui/mix.exs
Normal file
44
ui/mix.exs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
defmodule Ui.MixProject do
|
||||||
|
use Mix.Project
|
||||||
|
|
||||||
|
def project do
|
||||||
|
[
|
||||||
|
app: :ui,
|
||||||
|
version: "0.1.0",
|
||||||
|
elixir: "~> 1.5",
|
||||||
|
elixirc_paths: elixirc_paths(Mix.env()),
|
||||||
|
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
||||||
|
start_permanent: Mix.env() == :prod,
|
||||||
|
deps: deps()
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Configuration for the OTP application.
|
||||||
|
#
|
||||||
|
# Type `mix help compile.app` for more information.
|
||||||
|
def application do
|
||||||
|
[
|
||||||
|
mod: {Ui.Application, []},
|
||||||
|
extra_applications: [:logger, :runtime_tools]
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Specifies which paths to compile per environment.
|
||||||
|
defp elixirc_paths(:test), do: ["lib", "test/support"]
|
||||||
|
defp elixirc_paths(_), do: ["lib"]
|
||||||
|
|
||||||
|
# Specifies your project dependencies.
|
||||||
|
#
|
||||||
|
# Type `mix help deps` for examples and options.
|
||||||
|
defp deps do
|
||||||
|
[
|
||||||
|
{:phoenix, "~> 1.4.9"},
|
||||||
|
{:phoenix_pubsub, "~> 1.1"},
|
||||||
|
{:phoenix_html, "~> 2.11"},
|
||||||
|
{:phoenix_live_reload, "~> 1.2", only: :dev},
|
||||||
|
{:gettext, "~> 0.11"},
|
||||||
|
{:jason, "~> 1.0"},
|
||||||
|
{:plug_cowboy, "~> 2.0"}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
17
ui/mix.lock
Normal file
17
ui/mix.lock
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
%{
|
||||||
|
"cowboy": {:hex, :cowboy, "2.6.3", "99aa50e94e685557cad82e704457336a453d4abcb77839ad22dbe71f311fcc06", [:rebar3], [{:cowlib, "~> 2.7.3", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"cowlib": {:hex, :cowlib, "2.7.3", "a7ffcd0917e6d50b4d5fb28e9e2085a0ceb3c97dea310505f7460ff5ed764ce9", [:rebar3], [], "hexpm"},
|
||||||
|
"file_system": {:hex, :file_system, "0.2.7", "e6f7f155970975789f26e77b8b8d8ab084c59844d8ecfaf58cbda31c494d14aa", [:mix], [], "hexpm"},
|
||||||
|
"gettext": {:hex, :gettext, "0.17.0", "abe21542c831887a2b16f4c94556db9c421ab301aee417b7c4fbde7fbdbe01ec", [:mix], [], "hexpm"},
|
||||||
|
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
|
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
|
||||||
|
"phoenix": {:hex, :phoenix, "1.4.9", "746d098e10741c334d88143d3c94cab1756435f94387a63441792e66ec0ee974", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"phoenix_html": {:hex, :phoenix_html, "2.13.3", "850e292ff6e204257f5f9c4c54a8cb1f6fbc16ed53d360c2b780a3d0ba333867", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.1", "274a4b07c4adbdd7785d45a8b0bb57634d0b4f45b18d2c508b26c0344bd59b8f", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"},
|
||||||
|
"plug": {:hex, :plug, "1.8.2", "0bcce1daa420f189a6491f3940cc77ea7fb1919761175c9c3b59800d897440fc", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
|
"plug_cowboy": {:hex, :plug_cowboy, "2.1.0", "b75768153c3a8a9e8039d4b25bb9b14efbc58e9c4a6e6a270abff1cd30cbe320", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
|
||||||
|
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
|
||||||
|
"telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"},
|
||||||
|
}
|
11
ui/priv/gettext/en/LC_MESSAGES/errors.po
Normal file
11
ui/priv/gettext/en/LC_MESSAGES/errors.po
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
## `msgid`s in this file come from POT (.pot) files.
|
||||||
|
##
|
||||||
|
## Do not add, change, or remove `msgid`s manually here as
|
||||||
|
## they're tied to the ones in the corresponding POT file
|
||||||
|
## (with the same domain).
|
||||||
|
##
|
||||||
|
## Use `mix gettext.extract --merge` or `mix gettext.merge`
|
||||||
|
## to merge POT files into PO files.
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Language: en\n"
|
10
ui/priv/gettext/errors.pot
Normal file
10
ui/priv/gettext/errors.pot
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
## This is a PO Template file.
|
||||||
|
##
|
||||||
|
## `msgid`s here are often extracted from source code.
|
||||||
|
## Add new translations manually only if they're dynamic
|
||||||
|
## translations that can't be statically extracted.
|
||||||
|
##
|
||||||
|
## Run `mix gettext.extract` to bring this file up to
|
||||||
|
## date. Leave `msgstr`s empty as changing them here has no
|
||||||
|
## effect: edit them in PO (`.po`) files instead.
|
||||||
|
|
31
ui/test/support/channel_case.ex
Normal file
31
ui/test/support/channel_case.ex
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
defmodule UiWeb.ChannelCase do
|
||||||
|
@moduledoc """
|
||||||
|
This module defines the test case to be used by
|
||||||
|
channel tests.
|
||||||
|
|
||||||
|
Such tests rely on `Phoenix.ChannelTest` and also
|
||||||
|
import other functionality to make it easier
|
||||||
|
to build common data structures and query the data layer.
|
||||||
|
|
||||||
|
Finally, if the test case interacts with the database,
|
||||||
|
it cannot be async. For this reason, every test runs
|
||||||
|
inside a transaction which is reset at the beginning
|
||||||
|
of the test unless the test case is marked as async.
|
||||||
|
"""
|
||||||
|
|
||||||
|
use ExUnit.CaseTemplate
|
||||||
|
|
||||||
|
using do
|
||||||
|
quote do
|
||||||
|
# Import conveniences for testing with channels
|
||||||
|
use Phoenix.ChannelTest
|
||||||
|
|
||||||
|
# The default endpoint for testing
|
||||||
|
@endpoint UiWeb.Endpoint
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
setup _tags do
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
32
ui/test/support/conn_case.ex
Normal file
32
ui/test/support/conn_case.ex
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
defmodule UiWeb.ConnCase do
|
||||||
|
@moduledoc """
|
||||||
|
This module defines the test case to be used by
|
||||||
|
tests that require setting up a connection.
|
||||||
|
|
||||||
|
Such tests rely on `Phoenix.ConnTest` and also
|
||||||
|
import other functionality to make it easier
|
||||||
|
to build common data structures and query the data layer.
|
||||||
|
|
||||||
|
Finally, if the test case interacts with the database,
|
||||||
|
it cannot be async. For this reason, every test runs
|
||||||
|
inside a transaction which is reset at the beginning
|
||||||
|
of the test unless the test case is marked as async.
|
||||||
|
"""
|
||||||
|
|
||||||
|
use ExUnit.CaseTemplate
|
||||||
|
|
||||||
|
using do
|
||||||
|
quote do
|
||||||
|
# Import conveniences for testing with connections
|
||||||
|
use Phoenix.ConnTest
|
||||||
|
alias UiWeb.Router.Helpers, as: Routes
|
||||||
|
|
||||||
|
# The default endpoint for testing
|
||||||
|
@endpoint UiWeb.Endpoint
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
setup _tags do
|
||||||
|
{:ok, conn: Phoenix.ConnTest.build_conn()}
|
||||||
|
end
|
||||||
|
end
|
1
ui/test/test_helper.exs
Normal file
1
ui/test/test_helper.exs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
ExUnit.start()
|
8
ui/test/ui_web/controllers/page_controller_test.exs
Normal file
8
ui/test/ui_web/controllers/page_controller_test.exs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
defmodule UiWeb.PageControllerTest do
|
||||||
|
use UiWeb.ConnCase
|
||||||
|
|
||||||
|
test "GET /", %{conn: conn} do
|
||||||
|
conn = get(conn, "/")
|
||||||
|
assert html_response(conn, 200) =~ "Welcome to Phoenix!"
|
||||||
|
end
|
||||||
|
end
|
14
ui/test/ui_web/views/error_view_test.exs
Normal file
14
ui/test/ui_web/views/error_view_test.exs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
defmodule UiWeb.ErrorViewTest do
|
||||||
|
use UiWeb.ConnCase, async: true
|
||||||
|
|
||||||
|
# Bring render/3 and render_to_string/3 for testing custom views
|
||||||
|
import Phoenix.View
|
||||||
|
|
||||||
|
test "renders 404.html" do
|
||||||
|
assert render_to_string(UiWeb.ErrorView, "404.html", []) == "Not Found"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "renders 500.html" do
|
||||||
|
assert render_to_string(UiWeb.ErrorView, "500.html", []) == "Internal Server Error"
|
||||||
|
end
|
||||||
|
end
|
3
ui/test/ui_web/views/layout_view_test.exs
Normal file
3
ui/test/ui_web/views/layout_view_test.exs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
defmodule UiWeb.LayoutViewTest do
|
||||||
|
use UiWeb.ConnCase, async: true
|
||||||
|
end
|
3
ui/test/ui_web/views/page_view_test.exs
Normal file
3
ui/test/ui_web/views/page_view_test.exs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
defmodule UiWeb.PageViewTest do
|
||||||
|
use UiWeb.ConnCase, async: true
|
||||||
|
end
|
Loading…
Reference in a new issue