mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
@@ -17,8 +17,10 @@ defmodule FgHttp.Devices do
|
||||
[%Device{}, ...]
|
||||
|
||||
"""
|
||||
def list_devices do
|
||||
Repo.all(Device)
|
||||
def list_devices, do: Repo.all(Device)
|
||||
|
||||
def list_devices(user_id) do
|
||||
Repo.all(from d in Device, where: d.user_id == ^user_id)
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -52,6 +54,14 @@ defmodule FgHttp.Devices do
|
||||
"""
|
||||
def get_device!(id), do: Repo.get!(Device, id)
|
||||
|
||||
def get_device!(id, with_rules: true) do
|
||||
Repo.one(
|
||||
from d in Device,
|
||||
where: d.id == ^id,
|
||||
preload: :rules
|
||||
)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a device.
|
||||
|
||||
|
||||
@@ -6,12 +6,15 @@ defmodule FgHttp.Devices.Device do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
alias FgHttp.{Rules.Rule, Users.User}
|
||||
|
||||
schema "devices" do
|
||||
field :name, :string
|
||||
field :public_key, :string
|
||||
field :user_id, :id
|
||||
field :last_ip, EctoNetwork.INET
|
||||
|
||||
has_many :rules, FgHttp.Rules.Rule
|
||||
has_many :rules, Rule
|
||||
belongs_to :user, User
|
||||
|
||||
timestamps()
|
||||
end
|
||||
@@ -19,7 +22,7 @@ defmodule FgHttp.Devices.Device do
|
||||
@doc false
|
||||
def changeset(device, attrs) do
|
||||
device
|
||||
|> cast(attrs, [:user_id, :name, :public_key])
|
||||
|> cast(attrs, [:last_ip, :user_id, :name, :public_key])
|
||||
|> validate_required([:user_id])
|
||||
end
|
||||
end
|
||||
|
||||
18
apps/fg_http/lib/fg_http/ecto_enums.ex
Normal file
18
apps/fg_http/lib/fg_http/ecto_enums.ex
Normal file
@@ -0,0 +1,18 @@
|
||||
import EctoEnum
|
||||
|
||||
# We only allow dropping or accepting packets for now
|
||||
defenum(RuleActionEnum, :action, [:drop, :accept])
|
||||
|
||||
# See http://ipset.netfilter.org/iptables.man.html
|
||||
defenum(RuleProtocolEnum, :protocol, [
|
||||
:all,
|
||||
:tcp,
|
||||
:udp,
|
||||
:udplite,
|
||||
:icmp,
|
||||
:icmpv6,
|
||||
:esp,
|
||||
:ah,
|
||||
:sctp,
|
||||
:mh
|
||||
])
|
||||
@@ -17,6 +17,10 @@ defmodule FgHttp.Rules do
|
||||
[%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
|
||||
|
||||
@@ -6,13 +6,17 @@ defmodule FgHttp.Rules.Rule do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
schema "rules" do
|
||||
field :destination, :string
|
||||
field :enabled, :boolean, default: false
|
||||
field :port, :string
|
||||
field :protocol, :string
|
||||
alias FgHttp.{Devices.Device}
|
||||
|
||||
belongs_to :device, FgHttp.Devices.Device
|
||||
schema "rules" do
|
||||
field :destination, EctoNetwork.INET
|
||||
field :action, RuleActionEnum, default: "drop"
|
||||
field :priority, :integer, default: 0
|
||||
field :enabled, :boolean, default: true
|
||||
field :port, :string
|
||||
field :protocol, RuleProtocolEnum, default: "all"
|
||||
|
||||
belongs_to :device, Device
|
||||
|
||||
timestamps()
|
||||
end
|
||||
@@ -20,7 +24,7 @@ defmodule FgHttp.Rules.Rule do
|
||||
@doc false
|
||||
def changeset(rule, attrs) do
|
||||
rule
|
||||
|> cast(attrs, [:destination, :port, :protocol, :enabled])
|
||||
|> validate_required([:destination, :port, :protocol, :enabled])
|
||||
|> cast(attrs, [:device_id, :priority, :action, :destination, :port, :protocol, :enabled])
|
||||
|> validate_required([:device_id, :priority, :action, :destination, :protocol, :enabled])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,8 +6,10 @@ defmodule FgHttp.Sessions.Session do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
alias FgHttp.{Users.User}
|
||||
|
||||
schema "sessions" do
|
||||
field :user_id, :id
|
||||
belongs_to :user, User
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
@@ -6,12 +6,17 @@ defmodule FgHttp.Users.User do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
alias FgHttp.{Devices.Device, Sessions.Session}
|
||||
|
||||
schema "users" do
|
||||
field :email, :string
|
||||
field :confirmed_at, :utc_datetime
|
||||
field :last_signed_in_at, :utc_datetime
|
||||
field :password_digest, :string
|
||||
|
||||
has_many :devices, Device
|
||||
has_many :sessions, Session
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ defmodule FgHttpWeb.DeviceController do
|
||||
plug FgHttpWeb.Plugs.Authenticator
|
||||
|
||||
def index(conn, _params) do
|
||||
devices = Devices.list_devices()
|
||||
devices = Devices.list_devices(conn.assigns.current_user.id)
|
||||
render(conn, "index.html", devices: devices)
|
||||
end
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@ defmodule FgHttpWeb.RuleController do
|
||||
plug FgHttpWeb.Plugs.Authenticator
|
||||
|
||||
def index(conn, %{"device_id" => device_id}) do
|
||||
device = Devices.get_device!(device_id)
|
||||
|
||||
device = Devices.get_device!(device_id, with_rules: true)
|
||||
render(conn, "index.html", device: device, rules: device.rules)
|
||||
end
|
||||
|
||||
@@ -21,15 +20,19 @@ defmodule FgHttpWeb.RuleController do
|
||||
render(conn, "new.html", changeset: changeset, device: device)
|
||||
end
|
||||
|
||||
def create(conn, %{"rule" => rule_params}) do
|
||||
case Rules.create_rule(rule_params) do
|
||||
def create(conn, %{"device_id" => device_id, "rule" => rule_params}) do
|
||||
# XXX RBAC
|
||||
all_params = Map.merge(rule_params, %{"device_id" => device_id})
|
||||
|
||||
case Rules.create_rule(all_params) do
|
||||
{:ok, rule} ->
|
||||
conn
|
||||
|> put_flash(:info, "Rule created successfully.")
|
||||
|> redirect(to: Routes.rule_path(conn, :show, rule))
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
render(conn, "new.html", changeset: changeset)
|
||||
device = Devices.get_device!(device_id)
|
||||
render(conn, "new.html", device: device, changeset: changeset)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -12,5 +12,9 @@
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<span><%= link "Edit", to: Routes.device_path(@conn, :edit, @device) %></span>
|
||||
<span><%= link "Back", to: Routes.device_path(@conn, :index) %></span>
|
||||
<p>
|
||||
<%= link "Show Rules for This Device", to: Routes.device_rule_path(@conn, :index, @device) %>
|
||||
</p>
|
||||
|
||||
<p><%= link "Edit", to: Routes.device_path(@conn, :edit, @device) %></p>
|
||||
<p><%= link "Back", to: Routes.device_path(@conn, :index) %></p>
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
<%= render "form.html", Map.put(assigns, :action, Routes.rule_path(@conn, :update, @rule)) %>
|
||||
|
||||
<span><%= link "Back", to: Routes.rule_path(@conn, :index, @rule.device) %></span>
|
||||
<span><%= link "Back", to: Routes.device_rule_path(@conn, :index, @rule.device_id) %></span>
|
||||
|
||||
@@ -1,27 +1,41 @@
|
||||
<%= form_for @changeset, @action, fn f -> %>
|
||||
<%= form_for @changeset, @action, [class: "black-80"], fn f -> %>
|
||||
<%= if @changeset.action do %>
|
||||
<div>
|
||||
<div class="mt4 measure">
|
||||
<p>Oops, something went wrong! Please check the errors below.</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= label f, :destination %>
|
||||
<%= text_input f, :destination %>
|
||||
<%= error_tag f, :destination %>
|
||||
<div class="mt4 measure">
|
||||
<%= label f, :destination, class: "f6 b db mb2" %>
|
||||
<%= text_input f, :destination, class: "input-reset ba b--black-20 pa2 mb2 db w-100" %>
|
||||
<%= error_tag f, :destination %>
|
||||
</div>
|
||||
|
||||
<%= label f, :port %>
|
||||
<%= text_input f, :port %>
|
||||
<%= error_tag f, :port %>
|
||||
<div class="mt4 measure">
|
||||
<%= label f, :port, class: "f6 b db mb2" %>
|
||||
<%= text_input f, :port, class: "input-reset ba b--black-20 pa2 mb2 db w-100" %>
|
||||
<%= error_tag f, :port %>
|
||||
</div>
|
||||
|
||||
<%= label f, :protocol %>
|
||||
<%= text_input f, :protocol %>
|
||||
<%= error_tag f, :protocol %>
|
||||
<div class="mt4 measure">
|
||||
<%= label f, :protocol, class: "f6 b db mb2" %>
|
||||
<%= text_input f, :protocol, class: "input-reset ba b--black-20 pa2 mb2 db w-100" %>
|
||||
<%= error_tag f, :protocol %>
|
||||
</div>
|
||||
|
||||
<%= label f, :enabled %>
|
||||
<%= checkbox f, :enabled %>
|
||||
<%= error_tag f, :enabled %>
|
||||
<div class="mt4 measure">
|
||||
<%= label f, :priority, class: "f6 b db mb2" %>
|
||||
<%= text_input f, :priority, class: "input-reset ba b--black-20 pa2 mb2 db w-100" %>
|
||||
<%= error_tag f, :priority %>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="mt4 measure">
|
||||
<%= label f, :enabled, class: "f6 b db mb2" %>
|
||||
<%= checkbox f, :enabled %>
|
||||
<%= error_tag f, :enabled %>
|
||||
</div>
|
||||
|
||||
<div class="mt4 measure">
|
||||
<%= submit "Save" %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
<h1>Listing Rules</h1>
|
||||
<h1>Listing Rules for Device <%= @device.name %></h1>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Destination</th>
|
||||
<th>Port</th>
|
||||
<th>Protocol</th>
|
||||
<th>Enabled</th>
|
||||
<table class="collapse ba br2 b--black-10 pv2 ph3 mt4">
|
||||
<tbody>
|
||||
<tr class="striped--near-white">
|
||||
<th class="pv2 ph3 tl f6 fw6 ttu">Action</th>
|
||||
<th class="pv2 ph3 tl f6 fw6 ttu">Destination</th>
|
||||
<th class="pv2 ph3 tl f6 fw6 ttu">Port</th>
|
||||
<th class="pv2 ph3 tl f6 fw6 ttu">Protocol</th>
|
||||
<th class="pv2 ph3 tl f6 fw6 ttu">Priority</th>
|
||||
<th class="pv2 ph3 tl f6 fw6 ttu">Enabled</th>
|
||||
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<%= for rule <- @rules do %>
|
||||
<tr>
|
||||
<td><%= rule.destination %></td>
|
||||
<td><%= rule.port %></td>
|
||||
<td><%= rule.protocol %></td>
|
||||
<td><%= rule.enabled %></td>
|
||||
<tr class="striped--near-white">
|
||||
<td class="pv2 ph3 tl f6 fw6 ttu"><%= rule.action %></td>
|
||||
<td class="pv2 ph3 tl f6 fw6 ttu"><%= rule.destination %></td>
|
||||
<td class="pv2 ph3 tl f6 fw6 ttu"><%= rule.port %></td>
|
||||
<td class="pv2 ph3 tl f6 fw6 ttu"><%= rule.protocol %></td>
|
||||
<td class="pv2 ph3 tl f6 fw6 ttu"><%= rule.priority %></td>
|
||||
<td class="pv2 ph3 tl f6 fw6 ttu"><%= rule.enabled %></td>
|
||||
|
||||
<td>
|
||||
<span><%= link "Show", to: Routes.rule_path(@conn, :show, rule) %></span>
|
||||
@@ -30,5 +32,5 @@
|
||||
</table>
|
||||
|
||||
<p>
|
||||
<%= link "New Rule", to: Routes.rule_path(@conn, :new, @device) %>
|
||||
<%= link "New Rule", to: Routes.device_rule_path(@conn, :new, @device) %>
|
||||
</p>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<h1>New Rule</h1>
|
||||
<h1>New Rule for Device <%= @device.name %></h1>
|
||||
|
||||
<%= render "form.html", Map.put(assigns, :action, Routes.rule_path(@conn, :create, @device)) %>
|
||||
<%= render "form.html", Map.put(assigns, :action, Routes.device_rule_path(@conn, :create, @device)) %>
|
||||
|
||||
<span><%= link "Back", to: Routes.rule_path(@conn, :index, @device) %></span>
|
||||
<span><%= link "Back", to: Routes.device_rule_path(@conn, :index, @device) %></span>
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
<ul>
|
||||
|
||||
<li>
|
||||
<strong>Action:</strong>
|
||||
<%= @rule.action %>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<strong>Destination:</strong>
|
||||
<%= @rule.destination %>
|
||||
@@ -17,6 +22,11 @@
|
||||
<%= @rule.protocol %>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<strong>Priority</strong>
|
||||
<%= @rule.priority %>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<strong>Enabled:</strong>
|
||||
<%= @rule.enabled %>
|
||||
@@ -25,4 +35,4 @@
|
||||
</ul>
|
||||
|
||||
<span><%= link "Edit", to: Routes.rule_path(@conn, :edit, @rule) %></span>
|
||||
<span><%= link "Back", to: Routes.rule_path(@conn, :index, @rule.device) %></span>
|
||||
<span><%= link "Back", to: Routes.device_rule_path(@conn, :index, @rule.device_id) %></span>
|
||||
|
||||
@@ -41,7 +41,7 @@ defmodule FgHttp.MixProject do
|
||||
{:phoenix_pubsub, "~> 2.0"},
|
||||
{:phoenix_ecto, "~> 4.0"},
|
||||
{:ecto_sql, "~> 3.1"},
|
||||
# Exposes Postgres inet, cidr, macaddr types
|
||||
{:ecto_enum, "~> 1.4.0"},
|
||||
{:ecto_network, "~> 1.3.0"},
|
||||
{:postgrex, ">= 0.0.0"},
|
||||
{:phoenix_html, "~> 2.11"},
|
||||
|
||||
@@ -5,7 +5,8 @@ defmodule FgHttp.Repo.Migrations.CreateDevices do
|
||||
create table(:devices) do
|
||||
add :name, :string
|
||||
add :public_key, :string
|
||||
add :user_id, references(:users, on_delete: :delete_all)
|
||||
add :last_ip, :inet
|
||||
add :user_id, references(:users, on_delete: :delete_all), null: false
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
@@ -2,12 +2,17 @@ defmodule FgHttp.Repo.Migrations.CreateRules do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
RuleActionEnum.create_type()
|
||||
RuleProtocolEnum.create_type()
|
||||
|
||||
create table(:rules) do
|
||||
add :destination, :inet
|
||||
add :port, :string
|
||||
add :protocol, :string
|
||||
add :protocol, RuleProtocolEnum.type(), default: "all", null: false
|
||||
add :action, RuleActionEnum.type(), default: "drop", null: false
|
||||
add :priority, :integer, default: 0, null: false
|
||||
add :enabled, :boolean, default: false, null: false
|
||||
add :device_id, references(:devices, on_delete: :delete_all)
|
||||
add :port, :string
|
||||
add :device_id, references(:devices, on_delete: :delete_all), null: false
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
@@ -9,3 +9,23 @@
|
||||
#
|
||||
# We recommend using the bang functions (`insert!`, `update!`
|
||||
# and so on) as they will fail if something goes wrong.
|
||||
|
||||
alias FgHttp.Repo
|
||||
|
||||
Repo.transaction(fn ->
|
||||
{:ok, user} = FgHttp.Users.create_user(%{email: "testuser@fireguard.network"})
|
||||
|
||||
{:ok, device} =
|
||||
FgHttp.Devices.create_device(%{
|
||||
name: "Seed",
|
||||
public_key: "Seed",
|
||||
last_ip: %Postgrex.INET{address: {127, 0, 0, 1}},
|
||||
user_id: user.id
|
||||
})
|
||||
|
||||
{:ok, _rule} =
|
||||
FgHttp.Rules.create_rule(%{
|
||||
device_id: device.id,
|
||||
destination: %Postgrex.INET{address: {0, 0, 0, 0}, netmask: 0}
|
||||
})
|
||||
end)
|
||||
|
||||
@@ -20,6 +20,9 @@ defmodule FgHttpWeb.DeviceControllerTest do
|
||||
|
||||
describe "index" do
|
||||
test "lists all devices", %{conn: conn} do
|
||||
# Mock authentication
|
||||
conn = Plug.Conn.assign(conn, :current_user, fixture(:user))
|
||||
|
||||
conn = get(conn, Routes.device_path(conn, :index))
|
||||
assert html_response(conn, 200) =~ "Listing Devices"
|
||||
end
|
||||
|
||||
9
mix.lock
9
mix.lock
@@ -6,23 +6,24 @@
|
||||
"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.3", "3a14c2500c3964165245a4f24a463e080762f7ccd0c632c763ea589f75ca205f", [: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", "9b6f18dea95f2004d0369f6a8346513ca3f706614f4ede219a5f3fe5db5dd962"},
|
||||
"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_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.3", "c552aa8a7ccff2b64024f835503b3155d8e73452c180298527fbdbcd6e79710b", [: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", "ec9e59d6fa3f8cfda9963ada371e9e6659167c2338a997bd7ea23b10b245842b"},
|
||||
"file_system": {:hex, :file_system, "0.2.8", "f632bd287927a1eed2b718f22af727c5aeaccc9a98d8c2bd7bff709e851dc986", [:mix], [], "hexpm", "97a3b6f8d63ef53bd0113070102db2ce05352ecf0d25390eb8d747c2bde98bca"},
|
||||
"gettext": {:hex, :gettext, "0.17.4", "f13088e1ec10ce01665cf25f5ff779e7df3f2dc71b37084976cf89d1aa124d5c", [:mix], [], "hexpm", "3c75b5ea8288e2ee7ea503ff9e30dfe4d07ad3c054576a6e60040e79a801e14d"},
|
||||
"gettext": {:hex, :gettext, "0.18.0", "406d6b9e0e3278162c2ae1de0a60270452c553536772167e2d701f028116f870", [:mix], [], "hexpm", "c3f850be6367ebe1a08616c2158affe4a23231c70391050bf359d5f92f66a571"},
|
||||
"jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"},
|
||||
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"},
|
||||
"phoenix": {:hex, :phoenix, "1.5.1", "95156589879dc69201d5fc0ebdbfdfc7901a09a3616ea611ec297f81340275a2", [: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", "fc272b38e79d2881790fccae6f67a9fbe9b790103d6878175ea03d23003152eb"},
|
||||
"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.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", "41b4103a2fa282cfd747d377233baf213c648fdcc7928f432937676532490eee"},
|
||||
"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_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.0", "6508295cbeb4c654860845fb95260737e4a8838d34d115ad76cd487584e2fc4d", [: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: true]}], "hexpm", "422a9727e667be1bf5ab1de03be6fa0ad67b775b2d84ed908f3264415ef29d4a"},
|
||||
"plug_cowboy": {:hex, :plug_cowboy, "2.2.1", "fcf58aa33227a4322a050e4783ee99c63c031a2e7f9a2eb7340d55505e17f30f", [: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", "3b43de24460d87c0971887286e7a20d40462e48eb7235954681a20cee25ddeb6"},
|
||||
"plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"},
|
||||
"postgrex": {:hex, :postgrex, "0.15.3", "5806baa8a19a68c4d07c7a624ccdb9b57e89cbc573f1b98099e3741214746ae4", [: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", "4737ce62a31747b4c63c12b20c62307e51bb4fcd730ca0c32c280991e0606c90"},
|
||||
"postgrex": {:hex, :postgrex, "0.15.4", "5d691c25fc79070705a2ff0e35ce0822b86a0ee3c6fdb7a4fb354623955e1aed", [: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", "306515b9d975fcb2478dc337a1d27dc3bf8af7cd71017c333fe9db3a3d211b0a"},
|
||||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
|
||||
"telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm", "4738382e36a0a9a2b6e25d67c960e40e1a2c95560b9f936d8e29de8cd858480f"},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user