mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
Merge pull request #31 from CloudFire-LLC/higher-coverage
Higher coverage
This commit is contained in:
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
uses: actions/setup-elixir@v1
|
||||
with:
|
||||
elixir-version: "1.10.3"
|
||||
otp-version: "23.0.1"
|
||||
otp-version: "23.0.2"
|
||||
- name: Install Dependencies
|
||||
run: mix deps.get --only test
|
||||
- name: Setup Database
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
elixir 1.10.3-otp-23
|
||||
erlang 23.0.1
|
||||
erlang 23.0.2
|
||||
nodejs 10.20.1
|
||||
|
||||
@@ -8,14 +8,6 @@ defmodule FgHttp.Rules do
|
||||
|
||||
alias FgHttp.Rules.Rule
|
||||
|
||||
def list_rules(device_id) do
|
||||
Repo.all(from r in Rule, where: r.device_id == ^device_id)
|
||||
end
|
||||
|
||||
def list_rules do
|
||||
Repo.all(Rule)
|
||||
end
|
||||
|
||||
def get_rule!(id), do: Repo.get!(Rule, id)
|
||||
|
||||
def create_rule(attrs \\ %{}) do
|
||||
|
||||
@@ -8,10 +8,6 @@ defmodule FgHttp.Users do
|
||||
|
||||
alias FgHttp.Users.User
|
||||
|
||||
def list_users do
|
||||
Repo.all(User)
|
||||
end
|
||||
|
||||
def get_user!(email: email) do
|
||||
Repo.get_by!(User, email: email)
|
||||
end
|
||||
|
||||
@@ -34,6 +34,7 @@ defmodule FgHttp.Users.PasswordReset do
|
||||
password_reset
|
||||
|> cast(attrs, [:email, :reset_sent_at, :reset_token])
|
||||
|> validate_required([:email])
|
||||
|> validate_format(:email, ~r/@/)
|
||||
|> generate_reset_token()
|
||||
|> validate_required([:reset_token])
|
||||
|> unique_constraint(:reset_token)
|
||||
|
||||
@@ -52,8 +52,4 @@ defmodule FgHttp.Users.Session do
|
||||
add_error(changeset, :password, "invalid: #{error_msg}")
|
||||
end
|
||||
end
|
||||
|
||||
defp authenticate_user(changeset) do
|
||||
changeset
|
||||
end
|
||||
end
|
||||
|
||||
@@ -85,6 +85,11 @@ defmodule FgHttp.Users.User do
|
||||
user
|
||||
|> cast(attrs, [:email])
|
||||
|> validate_required([:email])
|
||||
|> validate_format(:email, ~r/@/)
|
||||
end
|
||||
|
||||
def update_changeset(user, %{} = attrs) do
|
||||
changeset(user, attrs)
|
||||
end
|
||||
|
||||
def changeset(user, attrs) do
|
||||
|
||||
11
apps/fg_http/lib/fg_http/util/fg_crypto.ex
Normal file
11
apps/fg_http/lib/fg_http/util/fg_crypto.ex
Normal file
@@ -0,0 +1,11 @@
|
||||
defmodule FgHttp.Util.FgCrypto do
|
||||
@moduledoc """
|
||||
Utilities for working with crypto functions
|
||||
"""
|
||||
|
||||
def rand_string(length \\ 16) do
|
||||
:crypto.strong_rand_bytes(length)
|
||||
|> Base.url_encode64()
|
||||
|> binary_part(0, length)
|
||||
end
|
||||
end
|
||||
@@ -4,6 +4,19 @@ defmodule FgHttpWeb.ErrorHelpers do
|
||||
"""
|
||||
|
||||
use Phoenix.HTML
|
||||
import Ecto.Changeset, only: [traverse_errors: 2]
|
||||
|
||||
def aggregated_errors(changeset) do
|
||||
traverse_errors(changeset, fn {msg, opts} ->
|
||||
Enum.reduce(opts, msg, fn {key, value}, acc ->
|
||||
String.replace(acc, "%{#{key}}", to_string(value))
|
||||
end)
|
||||
end)
|
||||
|> Enum.reduce("", fn {key, value}, acc ->
|
||||
joined_errors = Enum.join(value, "; ")
|
||||
"#{acc}#{key}: #{joined_errors}\n"
|
||||
end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Generates tag for inlined form input errors.
|
||||
@@ -8,20 +8,22 @@ defmodule FgHttpWeb.NewDeviceLive do
|
||||
alias FgHttp.Devices.Device
|
||||
alias FgHttpWeb.Router.Helpers, as: Routes
|
||||
|
||||
def mount(_params, %{"current_user_id" => user_id}, socket) do
|
||||
if connected?(socket), do: wait_for_device_connect(socket)
|
||||
# Number of seconds before simulating a device connect
|
||||
@mocked_timer 3000
|
||||
|
||||
device = %Device{id: "1", user_id: user_id}
|
||||
def mount(_params, %{"user_id" => user_id}, socket) do
|
||||
if connected?(socket) do
|
||||
# Send a mock device connect
|
||||
:timer.send_after(@mocked_timer, self(), {:pubkey, "foobar"})
|
||||
end
|
||||
|
||||
device = %Device{user_id: user_id}
|
||||
{:ok, assign(socket, :device, device)}
|
||||
end
|
||||
|
||||
# XXX: Receive other device details to create an intelligent name
|
||||
def handle_info({:pubkey, pubkey}, socket) do
|
||||
device = %Device{public_key: pubkey}
|
||||
device = %{socket.assigns.device | public_key: pubkey}
|
||||
{:noreply, assign(socket, :device, device)}
|
||||
end
|
||||
|
||||
defp wait_for_device_connect(_socket) do
|
||||
:timer.send_after(3000, self(), {:pubkey, "foobar"})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<%= aggregated_errors(@changeset) %>
|
||||
<% end %>
|
||||
|
||||
<%= live_render(@conn, FgHttpWeb.NewDeviceLive, session: %{"current_user_id" => @session.id}) %>
|
||||
<%= live_render(@conn, FgHttpWeb.NewDeviceLive, session: %{"user_id" => @session.id}) %>
|
||||
|
||||
<p>
|
||||
<%= link "Back", to: Routes.device_path(@conn, :index) %>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<fieldset id="edit_account" class="ba b--transparent ph0 mh0">
|
||||
<fieldset id="edit_user" class="ba b--transparent ph0 mh0">
|
||||
<legend class="f4 fw6 ph0 mh0">Edit Account</legend>
|
||||
<div class="mt3">
|
||||
<%= label(f, :email, class: "db fw6 lh-copy f6") %>
|
||||
|
||||
@@ -1,27 +1,36 @@
|
||||
<h3>Sign Up</h3>
|
||||
|
||||
<%= form_for @changeset, Routes.user_path(@conn, :new), fn f -> %>
|
||||
<%= form_for @changeset, Routes.user_path(@conn, :create), fn f -> %>
|
||||
<%= if @changeset.action do %>
|
||||
<div>
|
||||
<p>Oops, something went wrong! Please check the errors below.</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<label>
|
||||
Email: <%= text_input f, :email %>
|
||||
</label>
|
||||
|
||||
<br>
|
||||
|
||||
<label>
|
||||
Password: <%= text_input f, :password %>
|
||||
</label>
|
||||
|
||||
<br>
|
||||
|
||||
<label>
|
||||
Password Confirmation: <%= text_input f, :password_confirmation %>
|
||||
</label>
|
||||
|
||||
<%= submit "Submit" %>
|
||||
<fieldset id="create_user" class="ba b--transparent ph0 mh0">
|
||||
<legend class="f4 fw6 ph0 mh0">Sign Up</legend>
|
||||
<div class="mt3">
|
||||
<%= label(f, :email, class: "db fw6 lh-copy f6") %>
|
||||
<%= text_input(f, :email, class: "pa2 input-reset ba bg-transparent hover-bg-black hover-white w-100") %>
|
||||
<%= error_tag f, :email %>
|
||||
</div>
|
||||
<div class="mv3">
|
||||
<%= label(f, :password, class: "db fw6 lh-copy f6") %>
|
||||
<%= password_input(f, :password, class: "pa2 input-reset ba bg-transparent hover-bg-black hover-white w-100") %>
|
||||
<%= error_tag f, :password %>
|
||||
</div>
|
||||
<div class="mv3">
|
||||
<%= label(f, :password_confirmation, class: "db fw6 lh-copy f6") %>
|
||||
<%= password_input(f, :password_confirmation, class: "pa2 input-reset ba bg-transparent hover-bg-black hover-white w-100") %>
|
||||
<%= error_tag f, :password_confirmation %>
|
||||
</div>
|
||||
<div class="mv3">
|
||||
<%= label(f, :current_password, class: "db fw6 lh-copy f6") %>
|
||||
<%= password_input(f, :current_password, class: "pa2 input-reset ba bg-transparent hover-bg-black hover-white w-100") %>
|
||||
<%= error_tag f, :current_password %>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div>
|
||||
<%= submit "Sign Up", class: "b ph3 pv2 input-reset ba b--black bg-transparent grow pointer f6 dib" %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -1,16 +1,3 @@
|
||||
defmodule FgHttpWeb.DeviceView do
|
||||
use FgHttpWeb, :view
|
||||
import Ecto.Changeset, only: [traverse_errors: 2]
|
||||
|
||||
def aggregated_errors(changeset) do
|
||||
traverse_errors(changeset, fn {msg, opts} ->
|
||||
Enum.reduce(opts, msg, fn {key, value}, acc ->
|
||||
String.replace(acc, "%{#{key}}", to_string(value))
|
||||
end)
|
||||
end)
|
||||
|> Enum.reduce("", fn {key, value}, acc ->
|
||||
joined_errors = Enum.join(value, "; ")
|
||||
"#{acc}#{key}: #{joined_errors}\n"
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -46,6 +46,7 @@ defmodule FgHttp.MixProject do
|
||||
[
|
||||
{:phoenix, "~> 1.5.1"},
|
||||
{:excoveralls, "~> 0.13", only: :test},
|
||||
{:floki, ">= 0.0.0", only: :test},
|
||||
{:argon2_elixir, "~> 2.0"},
|
||||
{:phoenix_pubsub, "~> 2.0"},
|
||||
{:phoenix_ecto, "~> 4.0"},
|
||||
|
||||
@@ -6,8 +6,8 @@ defmodule FgHttp.PasswordResetsTest do
|
||||
describe "password_resets" do
|
||||
alias FgHttp.Users.PasswordReset
|
||||
|
||||
@valid_attrs %{email: "test"}
|
||||
@invalid_attrs %{email: ""}
|
||||
@valid_attrs %{email: "test@test"}
|
||||
@invalid_attrs %{email: "invalid"}
|
||||
|
||||
test "get_password_reset!/1 returns the password_reset with given token" do
|
||||
token = Fixtures.password_reset(%{reset_sent_at: DateTime.utc_now()}).reset_token
|
||||
|
||||
17
apps/fg_http/test/fg_http/util/fg_crypto_test.exs
Normal file
17
apps/fg_http/test/fg_http/util/fg_crypto_test.exs
Normal file
@@ -0,0 +1,17 @@
|
||||
defmodule FgHttp.Util.FgCryptoTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
alias FgHttp.Util.FgCrypto
|
||||
|
||||
describe "rand_string" do
|
||||
test "it returns a string of default length" do
|
||||
assert 16 == String.length(FgCrypto.rand_string())
|
||||
end
|
||||
|
||||
test "it returns a string of proper length" do
|
||||
for length <- [1, 32, 32_768] do
|
||||
assert length == String.length(FgCrypto.rand_string(length))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,8 +1,72 @@
|
||||
defmodule FgHttpWeb.DeviceControllerTest do
|
||||
use FgHttpWeb.ConnCase, async: true
|
||||
|
||||
defmodule FgHttpWeb.DeviceControllerTestHelpers do
|
||||
alias FgHttp.Fixtures
|
||||
|
||||
def create_device(_) do
|
||||
device = Fixtures.device()
|
||||
{:ok, device: device}
|
||||
end
|
||||
end
|
||||
|
||||
defmodule FgHttpWeb.DeviceControllerUnauthedTest do
|
||||
use FgHttpWeb.ConnCase, async: true
|
||||
import FgHttpWeb.DeviceControllerTestHelpers
|
||||
|
||||
@create_attrs %{public_key: "foobar"}
|
||||
@update_attrs %{name: "some updated name"}
|
||||
|
||||
describe "index" do
|
||||
test "redirects to new session", %{unauthed_conn: conn} do
|
||||
test_conn = get(conn, Routes.device_path(conn, :index))
|
||||
assert redirected_to(test_conn) == Routes.session_path(test_conn, :new)
|
||||
end
|
||||
end
|
||||
|
||||
describe "new device" do
|
||||
test "redirects to new session", %{unauthed_conn: conn} do
|
||||
test_conn = get(conn, Routes.device_path(conn, :new))
|
||||
assert redirected_to(test_conn) == Routes.session_path(test_conn, :new)
|
||||
end
|
||||
end
|
||||
|
||||
describe "create device" do
|
||||
test "redirects to new session", %{unauthed_conn: conn} do
|
||||
test_conn = post(conn, Routes.device_path(conn, :create), device: @create_attrs)
|
||||
assert redirected_to(test_conn) == Routes.session_path(test_conn, :new)
|
||||
end
|
||||
end
|
||||
|
||||
describe "edit device" do
|
||||
setup [:create_device]
|
||||
|
||||
test "redirects to new session", %{unauthed_conn: conn, device: device} do
|
||||
test_conn = get(conn, Routes.device_path(conn, :edit, device))
|
||||
assert redirected_to(test_conn) == Routes.session_path(test_conn, :new)
|
||||
end
|
||||
end
|
||||
|
||||
describe "update device" do
|
||||
setup [:create_device]
|
||||
|
||||
test "redirects to new session", %{unauthed_conn: conn, device: device} do
|
||||
test_conn = put(conn, Routes.device_path(conn, :update, device), device: @update_attrs)
|
||||
assert redirected_to(test_conn) == Routes.session_path(test_conn, :new)
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete device" do
|
||||
setup [:create_device]
|
||||
|
||||
test "redirects to new session", %{unauthed_conn: conn, device: device} do
|
||||
test_conn = delete(conn, Routes.device_path(conn, :delete, device))
|
||||
assert redirected_to(test_conn) == Routes.session_path(test_conn, :new)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmodule FgHttpWeb.DeviceControllerAuthedTest do
|
||||
use FgHttpWeb.ConnCase, async: true
|
||||
import FgHttpWeb.DeviceControllerTestHelpers
|
||||
|
||||
@create_attrs %{public_key: "foobar"}
|
||||
@update_attrs %{name: "some updated name"}
|
||||
@invalid_attrs %{public_key: nil}
|
||||
@@ -28,8 +92,8 @@ defmodule FgHttpWeb.DeviceControllerTest do
|
||||
end
|
||||
|
||||
test "renders errors when data is invalid", %{authed_conn: conn} do
|
||||
conn = post(conn, Routes.device_path(conn, :create), device: @invalid_attrs)
|
||||
assert html_response(conn, 200) =~ "public_key: can't be blank"
|
||||
test_conn = post(conn, Routes.device_path(conn, :create), device: @invalid_attrs)
|
||||
assert html_response(test_conn, 200) =~ "public_key: can't be blank"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -37,8 +101,8 @@ defmodule FgHttpWeb.DeviceControllerTest do
|
||||
setup [:create_device]
|
||||
|
||||
test "renders form for editing chosen device", %{authed_conn: conn, device: device} do
|
||||
conn = get(conn, Routes.device_path(conn, :edit, device))
|
||||
assert html_response(conn, 200) =~ "Edit Device"
|
||||
test_conn = get(conn, Routes.device_path(conn, :edit, device))
|
||||
assert html_response(test_conn, 200) =~ "Edit Device"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -71,9 +135,4 @@ defmodule FgHttpWeb.DeviceControllerTest do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp create_device(_) do
|
||||
device = Fixtures.device()
|
||||
{:ok, device: device}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,7 +3,7 @@ defmodule FgHttpWeb.PasswordResetControllerTest do
|
||||
|
||||
alias FgHttp.Fixtures
|
||||
|
||||
@valid_create_attrs %{email: "test"}
|
||||
@valid_create_attrs %{email: "test@test"}
|
||||
@invalid_create_attrs %{email: "doesnt-exist"}
|
||||
|
||||
describe "new password_reset" do
|
||||
|
||||
@@ -3,8 +3,8 @@ defmodule FgHttpWeb.SessionControllerTest do
|
||||
|
||||
alias FgHttp.{Fixtures, Repo, Users.User}
|
||||
|
||||
@valid_attrs %{email: "test", password: "test"}
|
||||
@invalid_attrs %{email: "test", password: "wrong"}
|
||||
@valid_attrs %{email: "test@test", password: "test"}
|
||||
@invalid_attrs %{email: "test@test", password: "wrong"}
|
||||
|
||||
describe "new when a user is already signed in" do
|
||||
test "redirects to authenticated root", %{authed_conn: conn} do
|
||||
@@ -15,11 +15,17 @@ defmodule FgHttpWeb.SessionControllerTest do
|
||||
end
|
||||
|
||||
describe "new when a user is not signed in" do
|
||||
test "renders sign in form", %{unauthed_conn: conn} do
|
||||
test "renders sign in form for new session path", %{unauthed_conn: conn} do
|
||||
test_conn = get(conn, Routes.session_path(conn, :new))
|
||||
|
||||
assert html_response(test_conn, 200) =~ "Sign In"
|
||||
end
|
||||
|
||||
test "renders sign in form for root path", %{unauthed_conn: conn} do
|
||||
test_conn = get(conn, "/")
|
||||
|
||||
assert html_response(test_conn, 200) =~ "Sign In"
|
||||
end
|
||||
end
|
||||
|
||||
describe "create when user exists" do
|
||||
|
||||
@@ -4,34 +4,42 @@ defmodule FgHttpWeb.UserControllerTest do
|
||||
alias FgHttp.Users.Session
|
||||
|
||||
@valid_create_attrs %{
|
||||
email: "fixure",
|
||||
email: "valid@test",
|
||||
password: "password",
|
||||
password_confirmation: "password"
|
||||
}
|
||||
@invalid_create_attrs %{
|
||||
email: "fixture",
|
||||
email: "test@test",
|
||||
password: "password",
|
||||
password_confirmation: "wrong_password"
|
||||
}
|
||||
@valid_update_attrs %{
|
||||
email: "new-email",
|
||||
email: "test@test",
|
||||
password: "new_password",
|
||||
password_confirmation: "new_password"
|
||||
}
|
||||
@valid_email_attrs %{email: "test@test"}
|
||||
@invalid_email_attrs %{email: "invalid"}
|
||||
@empty_update_password_attrs %{
|
||||
email: "",
|
||||
password: "",
|
||||
password_confirmation: "",
|
||||
current_password: ""
|
||||
}
|
||||
@valid_update_password_attrs %{
|
||||
email: "fixture",
|
||||
email: "test@test",
|
||||
password: "new_password",
|
||||
password_confirmation: "new_password",
|
||||
current_password: "test"
|
||||
}
|
||||
@invalid_update_password_attrs %{
|
||||
email: "fixture",
|
||||
email: "test@test",
|
||||
password: "new_password",
|
||||
password_confirmation: "new_password",
|
||||
current_password: "wrong current password"
|
||||
}
|
||||
@invalid_update_attrs %{
|
||||
email: "new-email",
|
||||
email: "test@test",
|
||||
password: "new_password",
|
||||
password_confirmation: "wrong_password"
|
||||
}
|
||||
@@ -87,9 +95,25 @@ defmodule FgHttpWeb.UserControllerTest do
|
||||
|
||||
assert html_response(test_conn, 200) =~ "is invalid: invalid password"
|
||||
end
|
||||
|
||||
test "does nothing when password params are empty", %{authed_conn: conn} do
|
||||
test_conn = put(conn, Routes.user_path(conn, :update), user: @empty_update_password_attrs)
|
||||
end
|
||||
end
|
||||
|
||||
describe "update" do
|
||||
test "updates email", %{authed_conn: conn} do
|
||||
test_conn = put(conn, Routes.user_path(conn, :update), user: @valid_email_attrs)
|
||||
|
||||
assert redirected_to(test_conn) == Routes.user_path(test_conn, :show)
|
||||
end
|
||||
|
||||
test "renders error when email is invalid", %{authed_conn: conn} do
|
||||
test_conn = put(conn, Routes.user_path(conn, :update), user: @invalid_email_attrs)
|
||||
|
||||
assert html_response(test_conn, 200) =~ "has invalid format"
|
||||
end
|
||||
|
||||
test "updates user when params are valid", %{authed_conn: conn} do
|
||||
test_conn = put(conn, Routes.user_path(conn, :update), user: @valid_update_attrs)
|
||||
|
||||
|
||||
19
apps/fg_http/test/fg_http_web/live/new_device_live_test.exs
Normal file
19
apps/fg_http/test/fg_http_web/live/new_device_live_test.exs
Normal file
@@ -0,0 +1,19 @@
|
||||
defmodule FgHttpWeb.NewDeviceLiveTest do
|
||||
use FgHttpWeb.ConnCase, async: true
|
||||
import Phoenix.LiveViewTest
|
||||
@endpoint FgHttpWeb.Endpoint
|
||||
|
||||
test "disconnected", %{authed_conn: conn} do
|
||||
conn = get(conn, Routes.device_path(conn, :new))
|
||||
|
||||
assert html_response(conn, 200) =~ "New Device"
|
||||
end
|
||||
|
||||
test "mount and handle_info/2", %{authed_conn: conn} do
|
||||
{:ok, view, html} = live_isolated(conn, FgHttpWeb.NewDeviceLive)
|
||||
assert html =~ "When we receive a connection from your device, we'll prompt"
|
||||
assert render(view) =~ "When we receive a connection from your device, we'll prompt"
|
||||
send(view.pid, {:pubkey, "test pubkey"})
|
||||
assert render(view) =~ "test pubkey"
|
||||
end
|
||||
end
|
||||
@@ -5,13 +5,13 @@ defmodule FgHttp.Fixtures do
|
||||
alias FgHttp.{Devices, PasswordResets, Repo, Rules, Sessions, Users, Users.User}
|
||||
|
||||
def user(attrs \\ %{}) do
|
||||
case Repo.get_by(User, email: "test") do
|
||||
case Repo.get_by(User, email: "test@test") do
|
||||
nil ->
|
||||
attrs =
|
||||
attrs
|
||||
|> Enum.into(%{email: "test", password: "test", password_confirmation: "test"})
|
||||
{:ok, user} =
|
||||
%{email: "test@test", password: "test", password_confirmation: "test"}
|
||||
|> Map.merge(attrs)
|
||||
|> Users.create_user()
|
||||
|
||||
{:ok, user} = Users.create_user(attrs)
|
||||
user
|
||||
|
||||
%User{} = user ->
|
||||
|
||||
@@ -1,5 +1,80 @@
|
||||
/*
|
||||
* fg-green: 6ece00;
|
||||
* fg-blue: 006ece;
|
||||
* fg-purple: 6000ce;
|
||||
* fg-orange: ce6000;
|
||||
* fg-yellow: cec700;
|
||||
* fg-red: ce0006;
|
||||
*/
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
img.logo {
|
||||
position: fixed;
|
||||
left: 2ch;
|
||||
width: 5ch;
|
||||
height: 5ch;
|
||||
}
|
||||
|
||||
nav {
|
||||
margin: auto;
|
||||
padding-top: 0;
|
||||
height: 6ch;
|
||||
}
|
||||
|
||||
ul.nav-links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
max-width: 70ch;
|
||||
list-style-type: none;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
ul.nav-links li a {
|
||||
display: block;
|
||||
padding: 2ch 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #006ece;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
main section {
|
||||
background-color: white;
|
||||
}
|
||||
div.content {
|
||||
max-width: 70ch;
|
||||
padding: 2ch;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
footer {
|
||||
color: white;
|
||||
margin: auto;
|
||||
padding-top: 0;
|
||||
height: 6ch;
|
||||
height:
|
||||
}
|
||||
|
||||
footer div {
|
||||
margin: 0 auto;
|
||||
padding: 2ch 0;
|
||||
max-width: 70ch;
|
||||
}
|
||||
|
||||
68
docs/img/logo.svg
Normal file
68
docs/img/logo.svg
Normal file
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
|
||||
sodipodi:docname="logo.svg"
|
||||
id="svg14"
|
||||
version="1.1"
|
||||
clip-rule="evenodd"
|
||||
fill-rule="evenodd"
|
||||
y="0px"
|
||||
x="0px"
|
||||
viewBox="0 0 23.6446 19.1606"
|
||||
style="shape-rendering:geometricPrecision;text-rendering:geometricPrecision;image-rendering:optimizeQuality;"
|
||||
xml:space="preserve"
|
||||
fill="#000000"
|
||||
width="100px"
|
||||
height="100px"><metadata
|
||||
id="metadata18"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><sodipodi:namedview
|
||||
inkscape:current-layer="g12"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-x="0"
|
||||
inkscape:cy="38.095238"
|
||||
inkscape:cx="30.902778"
|
||||
inkscape:zoom="10.08"
|
||||
showgrid="false"
|
||||
id="namedview16"
|
||||
inkscape:window-height="1440"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
guidetolerance="10"
|
||||
gridtolerance="10"
|
||||
objecttolerance="10"
|
||||
borderopacity="1"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#ffffff" /><defs
|
||||
id="defs4"><style
|
||||
id="style2"
|
||||
type="text/css">
|
||||
|
||||
.fil0 {fill:#000000}
|
||||
|
||||
</style></defs><g
|
||||
id="g12"><path
|
||||
style="fill:#cf5c00;fill-opacity:1"
|
||||
id="path6"
|
||||
d="M15.1887 0c4.69642,3.91162 -0.165501,12.5809 1.55021,16.0813 -3.52571,-5.05912 1.27791,-9.40684 -1.55021,-16.0813z"
|
||||
class="fil0" /><path
|
||||
style="fill:#8d1e00;fill-opacity:1"
|
||||
id="path8"
|
||||
d="M17.7931 7.52913c3.06981,1.99461 -0.615103,6.66813 1.34661,7.86443 1.98661,1.21161 1.76181,-3.82372 4.50492,-2.53201 -2.85611,-0.971704 -1.67131,5.02322 -5.21022,4.22542 -4.04992,-0.913004 0.986704,-7.35483 -0.641303,-9.55784z"
|
||||
class="fil0" /><path
|
||||
style="fill:#2b1200;fill-opacity:1"
|
||||
id="path10"
|
||||
d="M-0 14.3588c5.84662,-4.65822 13.8104,2.81691 17.5685,3.28401 4.83302,0.600603 3.54561,-4.98742 5.96463,-4.77922 -2.22901,0.268801 -0.891004,6.11293 -5.87452,6.29353 -5.42722,0.196701 -10.8419,-8.07783 -17.6586,-4.79832z"
|
||||
class="fil0" /></g><g
|
||||
inkscape:label="Layer 1"
|
||||
id="layer1"
|
||||
inkscape:groupmode="layer" /></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -7,36 +7,46 @@
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<nav>
|
||||
<a href="#introduction">Introduction</a>
|
||||
|
|
||||
<a href="#installation">Installation</a>
|
||||
|
|
||||
<a href="#usage">Usage</a>
|
||||
|
|
||||
<a href="#contributing">Contributing</a>
|
||||
</nav>
|
||||
<hr>
|
||||
<header>
|
||||
<img class="logo" src="img/logo.svg" alt="FireGuard Logo">
|
||||
<nav>
|
||||
<ul class="nav-links">
|
||||
<li>
|
||||
<a href="#introduction">Introduction</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#installation">Installation</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#usage">Usage</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#contributing">Contributing</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
<section>
|
||||
<h1>FireGuard</h1>
|
||||
<div class="content">
|
||||
<h1>FireGuard</h1>
|
||||
|
||||
<a name="introduction"></a>
|
||||
<h2>Introduction</h2>
|
||||
<a name="introduction"></a>
|
||||
<h2>Introduction</h2>
|
||||
|
||||
<a name="installation"></a>
|
||||
<h2>Installation</h2>
|
||||
<a name="installation"></a>
|
||||
<h2>Installation</h2>
|
||||
|
||||
<a name="usage"></a>
|
||||
<h2>Usage</h2>
|
||||
<a name="usage"></a>
|
||||
<h2>Usage</h2>
|
||||
|
||||
<a name="contributing"></a>
|
||||
<h2>Contributing</h2>
|
||||
<a name="contributing"></a>
|
||||
<h2>Contributing</h2>
|
||||
</div>
|
||||
</section>
|
||||
<footer>
|
||||
<hr>
|
||||
<span style="float: left">
|
||||
<div>
|
||||
© 2020 CloudFire LLC
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
10
mix.lock
10
mix.lock
@@ -10,7 +10,7 @@
|
||||
"credo": {:hex, :credo, "1.4.0", "92339d4cbadd1e88b5ee43d427b639b68a11071b6f73854e33638e30a0ea11f5", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1fd3b70dce216574ce3c18bdf510b57e7c4c85c2ec9cad4bff854abaf7e58658"},
|
||||
"db_connection": {:hex, :db_connection, "2.2.2", "3bbca41b199e1598245b716248964926303b5d4609ff065125ce98bcd368939e", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "642af240d8a8affb93b4ba5a6fcd2bbcbdc327e1a524b825d383711536f8070c"},
|
||||
"decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"},
|
||||
"ecto": {:hex, :ecto, "3.4.4", "a2c881e80dc756d648197ae0d936216c0308370332c5e77a2325a10293eef845", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4bd3ad62abc3b21fb629f0f7a3dab23a192fca837d257dd08449fba7373561"},
|
||||
"ecto": {:hex, :ecto, "3.4.5", "2bcd262f57b2c888b0bd7f7a28c8a48aa11dc1a2c6a858e45dd8f8426d504265", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "8c6d1d4d524559e9b7a062f0498e2c206122552d63eacff0a6567ffe7a8e8691"},
|
||||
"ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"},
|
||||
"ecto_network": {:hex, :ecto_network, "1.3.0", "1e77fa37c20e0f6a426d3862732f3317b0fa4c18f123d325f81752a491d7304e", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 0.0.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.14.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "053a5e46ef2837e8ea5ea97c82fa0f5494699209eddd764e663c85f11b2865bd"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.4.4", "d28bac2d420f708993baed522054870086fd45016a9d09bb2cd521b9c48d32ea", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "edb49af715dd72f213b66adfd0f668a43c17ed510b5d9ac7528569b23af57fe8"},
|
||||
@@ -18,8 +18,10 @@
|
||||
"ex_machina": {:hex, :ex_machina, "2.4.0", "09a34c5d371bfb5f78399029194a8ff67aff340ebe8ba19040181af35315eabb", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "a20bc9ddc721b33ea913b93666c5d0bdca5cbad7a67540784ae277228832d72c"},
|
||||
"excoveralls": {:hex, :excoveralls, "0.13.0", "4e1b7cc4e0351d8d16e9be21b0345a7e165798ee5319c7800b9138ce17e0b38e", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "fe2a56c8909564e2e6764765878d7d5e141f2af3bc8ff3b018a68ee2a218fced"},
|
||||
"file_system": {:hex, :file_system, "0.2.8", "f632bd287927a1eed2b718f22af727c5aeaccc9a98d8c2bd7bff709e851dc986", [:mix], [], "hexpm", "97a3b6f8d63ef53bd0113070102db2ce05352ecf0d25390eb8d747c2bde98bca"},
|
||||
"floki": {:hex, :floki, "0.26.0", "4df88977e2e357c6720e1b650f613444bfb48c5acfc6a0c646ab007d08ad13bf", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "e7b66ce7feef5518a9cd9fc7b52dd62a64028bd9cb6d6ad282a0f0fc90a4ae52"},
|
||||
"gettext": {:hex, :gettext, "0.18.0", "406d6b9e0e3278162c2ae1de0a60270452c553536772167e2d701f028116f870", [:mix], [], "hexpm", "c3f850be6367ebe1a08616c2158affe4a23231c70391050bf359d5f92f66a571"},
|
||||
"hackney": {:hex, :hackney, "1.16.0", "5096ac8e823e3a441477b2d187e30dd3fff1a82991a806b2003845ce72ce2d84", [:rebar3], [{:certifi, "2.5.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.1", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "3bf0bebbd5d3092a3543b783bf065165fa5d3ad4b899b836810e513064134e18"},
|
||||
"html_entities": {:hex, :html_entities, "0.5.1", "1c9715058b42c35a2ab65edc5b36d0ea66dd083767bef6e3edb57870ef556549", [:mix], [], "hexpm", "30efab070904eb897ff05cd52fa61c1025d7f8ef3a9ca250bc4e6513d16c32de"},
|
||||
"idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a02c8a1c4fd601215bb0b0324c8a6986749f807ce35f25449ec9e69758708122"},
|
||||
"jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"},
|
||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
||||
@@ -29,11 +31,11 @@
|
||||
"phoenix": {:hex, :phoenix, "1.5.3", "bfe0404e48ea03dfe17f141eff34e1e058a23f15f109885bbdcf62be303b49ff", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "8e16febeb9640d8b33895a691a56481464b82836d338bb3a23125cd7b6157c25"},
|
||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.1.0", "a044d0756d0464c5a541b4a0bf4bcaf89bffcaf92468862408290682c73ae50d", [: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", "c5e666a341ff104d0399d8f0e4ff094559b2fde13a5985d4cb5023b2c2ac558b"},
|
||||
"phoenix_html": {:hex, :phoenix_html, "2.14.2", "b8a3899a72050f3f48a36430da507dd99caf0ac2d06c77529b1646964f3d563e", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "58061c8dfd25da5df1ea0ca47c972f161beb6c875cd293917045b92ffe1bf617"},
|
||||
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.2", "38d94c30df5e2ef11000697a4fbe2b38d0fbf79239d492ff1be87bbc33bc3a84", [: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", "a3dec3d28ddb5476c96a7c8a38ea8437923408bc88da43e5c45d97037b396280"},
|
||||
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.4", "940c0344b1d66a2e46eef02af3a70e0c5bb45a4db0bf47917add271b76cd3914", [: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", "38f9308357dea4cc77f247e216da99fcb0224e05ada1469167520bed4cb8cccd"},
|
||||
"phoenix_live_view": {:hex, :phoenix_live_view, "0.12.1", "42f591c781edbf9fab921319076b7ac635d43aa23e6748d2644563326236d7e4", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.4.16 or ~> 1.5.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "585321e98df1cd5943e370b9784e950a37ca073744eb534660c9048967c52ab6"},
|
||||
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"},
|
||||
"plug": {:hex, :plug, "1.10.2", "0079345cfdf9e17da3858b83eb46bc54beb91554c587b96438f55c1477af5a86", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7898d0eb4767efb3b925fd7f9d1870d15e66e9c33b89c58d8d2ad89aa75ab3c1"},
|
||||
"plug_cowboy": {:hex, :plug_cowboy, "2.2.2", "7a09aa5d10e79b92d332a288f21cc49406b1b994cbda0fde76160e7f4cc890ea", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e82364b29311dbad3753d588febd7e5ef05062cd6697d8c231e0e007adab3727"},
|
||||
"plug": {:hex, :plug, "1.10.3", "c9cebe917637d8db0e759039cc106adca069874e1a9034fd6e3fdd427fd3c283", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "01f9037a2a1de1d633b5a881101e6a444bcabb1d386ca1e00bb273a1f1d9d939"},
|
||||
"plug_cowboy": {:hex, :plug_cowboy, "2.3.0", "149a50e05cb73c12aad6506a371cd75750c0b19a32f81866e1a323dda9e0e99d", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bc595a1870cef13f9c1e03df56d96804db7f702175e4ccacdb8fc75c02a7b97e"},
|
||||
"plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"},
|
||||
"postgrex": {:hex, :postgrex, "0.15.5", "aec40306a622d459b01bff890fa42f1430dac61593b122754144ad9033a2152f", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [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", "ed90c81e1525f65a2ba2279dbcebf030d6d13328daa2f8088b9661eb9143af7f"},
|
||||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
|
||||
|
||||
Reference in New Issue
Block a user