Skip to content

Commit

Permalink
feat: added localstack and s3 upload with presigned url
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolkill committed Feb 3, 2023
1 parent 58e6bbf commit 8415884
Show file tree
Hide file tree
Showing 38 changed files with 855 additions and 144 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1 +1,2 @@
/.idea
/.localstack
6 changes: 6 additions & 0 deletions README.md
Expand Up @@ -30,3 +30,9 @@ by an BroadwayWorker and notify with PhoenixChannels to all the connected fronte

- Maketool (Installed on Linux and MacOSX)
- Docker

To ensure Localstack can be reached from the frontend, add this to your `/etc/hosts` file

```
127.0.0.1 localstack
```
17 changes: 12 additions & 5 deletions backend/config/dev.exs
Expand Up @@ -2,10 +2,10 @@ import Config

# Configure your database
config :events_arq_backend, EventsArqBackend.Repo,
username: "postgres",
password: "postgres",
hostname: "localhost",
database: "events_arq_backend_dev",
username: System.get_env("POSTGRES_USERNAME"),
password: System.get_env("POSTGRES_PASSWORD"),
hostname: System.get_env("POSTGRES_HOSTNAME"),
database: System.get_env("POSTGRES_DATABASE"),
stacktrace: true,
show_sensitive_data_on_connection_error: true,
pool_size: 10
Expand All @@ -19,7 +19,7 @@ config :events_arq_backend, EventsArqBackend.Repo,
config :events_arq_backend, EventsArqBackendWeb.Endpoint,
# Binding to loopback ipv4 address prevents access from other machines.
# Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
http: [ip: {127, 0, 0, 1}, port: 4000],
http: [port: 4000],
check_origin: false,
code_reloader: true,
debug_errors: true,
Expand Down Expand Up @@ -57,5 +57,12 @@ config :logger, :console, format: "[$level] $message\n"
# in production as building large stacktraces may be expensive.
config :phoenix, :stacktrace_depth, 20

config :ex_aws, :s3,
scheme: "http://",
region: System.get_env("AWS_S3_REGION"),
host: System.get_env("AWS_S3_URL"),
port: System.get_env("AWS_S3_PORT"),
bucket: System.get_env("AWS_S3_BUCKET")

# Initialize plugs at runtime for faster development compilation
config :phoenix, :plug_init_mode, :runtime
20 changes: 10 additions & 10 deletions backend/config/runtime.exs
Expand Up @@ -21,21 +21,22 @@ if System.get_env("PHX_SERVER") do
end

if config_env() == :prod do
database_url =
System.get_env("DATABASE_URL") ||
raise """
environment variable DATABASE_URL is missing.
For example: ecto://USER:PASS@HOST/DATABASE
"""

maybe_ipv6 = if System.get_env("ECTO_IPV6"), do: [:inet6], else: []

config :events_arq_backend, EventsArqBackend.Repo,
# ssl: true,
url: database_url,
username: System.get_env("POSTGRES_USERNAME"),
password: System.get_env("POSTGRES_PASSWORD"),
hostname: System.get_env("POSTGRES_HOSTNAME"),
database: System.get_env("POSTGRES_DATABASE"),
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
socket_options: maybe_ipv6


config :ex_aws, :s3,
region: RuntimeUtil.get_env_or_raise_error("AWS_REGION"),
bucket: RuntimeUtil.get_env_or_raise_error("AWS_S3_BUCKET")

# The secret key base is used to sign/encrypt cookies and other secrets.
# A default value is used in config/dev.exs and config/test.exs but you
# want to use a different value for prod and you most likely don't want
Expand All @@ -48,11 +49,10 @@ if config_env() == :prod do
You can generate one by calling: mix phx.gen.secret
"""

host = System.get_env("PHX_HOST") || "example.com"
port = String.to_integer(System.get_env("PORT") || "4000")

config :events_arq_backend, EventsArqBackendWeb.Endpoint,
url: [host: host, port: 443, scheme: "https"],
http: [port: String.to_integer(System.get_env("PORT") || "4000")],
http: [
# Enable IPv6 and bind on all interfaces.
# Set it to {0, 0, 0, 0, 0, 0, 0, 1} for local network only access.
Expand Down
8 changes: 4 additions & 4 deletions backend/config/test.exs
Expand Up @@ -6,10 +6,10 @@ import Config
# to provide built-in test partitioning in CI environment.
# Run `mix help test` for more information.
config :events_arq_backend, EventsArqBackend.Repo,
username: "postgres",
password: "postgres",
hostname: "localhost",
database: "events_arq_backend_test#{System.get_env("MIX_TEST_PARTITION")}",
username: System.get_env("POSTGRES_USERNAME"),
password: System.get_env("POSTGRES_PASSWORD"),
hostname: System.get_env("POSTGRES_HOSTNAME"),
database: "#{System.get_env("POSTGRES_DATABASE")}_test",
pool: Ecto.Adapters.SQL.Sandbox,
pool_size: 10

Expand Down
1 change: 0 additions & 1 deletion backend/docker-entrypoint.sh
@@ -1,4 +1,3 @@

#!/bin/sh

set -e
Expand Down
14 changes: 14 additions & 0 deletions backend/lib/events_arq_backend/generator.ex
@@ -0,0 +1,14 @@
defmodule EventsArqBackend.Generator do
def generate_unique_ref,
do:
make_ref()
|> :erlang.ref_to_list()
|> List.to_string()
|> String.replace("#Ref<", "")
|> String.replace(">", "")
|> String.replace(".", "")
|> String.codepoints()
|> Enum.chunk_every(8)
|> Enum.join("-")

end
31 changes: 31 additions & 0 deletions backend/lib/events_arq_backend/s3_client.ex
@@ -0,0 +1,31 @@
defmodule EventsArqBackend.S3Client do
@moduledoc false

defmacrop is_prod, do: Mix.env() == :prod
defp s3_bucket, do: Application.get_env(:ex_aws, :s3)[:bucket]
defp config, do: ExAws.Config.new(:s3, [])

def presigned_url_upload(object_key, opts \\ []) do
get_presigned_url(:put, object_key, opts)
end

defp get_presigned_url(http_method, object_key, opts) do
# this its for a configuration issue with localhost
opts =
if is_prod() do
opts
else
opts
|> Keyword.put(:virtual_host, is_prod())
|> Keyword.put(:bucket_as_host, true)
end

ExAws.S3.presigned_url(
config(),
http_method,
s3_bucket(),
object_key,
opts
)
end
end
28 changes: 28 additions & 0 deletions backend/lib/events_arq_backend_web/controllers/file_controller.ex
@@ -0,0 +1,28 @@
defmodule EventsArqBackendWeb.FileController do
@moduledoc false
use EventsArqBackendWeb, :controller

alias EventsArqBackend.Generator
alias EventsArqBackend.S3Client

# 30 mins
@presigned_upload_url_max_age 60 * 30
# 10 MB
@presigned_upload_url_max_file_size 10 * 1_000_000

@presigned_url_opts [
["starts-with", "$Content-Type", ""],
{:expires_in, @presigned_upload_url_max_age},
{:content_length_range, [1, @presigned_upload_url_max_file_size]}
]

def get_presigned_url(conn, _) do
{:ok, url} =
Generator.generate_unique_ref()
|> S3Client.presigned_url_upload(@presigned_url_opts)

conn
|> put_status(200)
|> render("upload_url.json", url: url)
end
end
2 changes: 2 additions & 0 deletions backend/lib/events_arq_backend_web/endpoint.ex
Expand Up @@ -10,6 +10,8 @@ defmodule EventsArqBackendWeb.Endpoint do
signing_salt: "6Gy9K6yk"
]

plug CORSPlug

# socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]]

# Serve at "/" the static files from "priv/static" directory.
Expand Down
6 changes: 4 additions & 2 deletions backend/lib/events_arq_backend_web/router.ex
Expand Up @@ -5,7 +5,9 @@ defmodule EventsArqBackendWeb.Router do
plug :accepts, ["json"]
end

scope "/api", EventsArqBackendWeb do
pipe_through :api
scope "/api/v1", EventsArqBackendWeb do
pipe_through [:api]

post "/get_presigned_url", FileController, :get_presigned_url
end
end
12 changes: 12 additions & 0 deletions backend/lib/events_arq_backend_web/views/file_view.ex
@@ -0,0 +1,12 @@
defmodule EventsArqBackendWeb.FileView do
use EventsArqBackendWeb, :view

alias EventsArqBackendWeb.FileView

def render("upload_url.json", %{url: url}) do
%{
url: url
}
end

end
6 changes: 5 additions & 1 deletion backend/mix.exs
Expand Up @@ -40,7 +40,11 @@ defmodule EventsArqBackend.MixProject do
{:telemetry_metrics, "~> 0.6"},
{:telemetry_poller, "~> 1.0"},
{:jason, "~> 1.2"},
{:plug_cowboy, "~> 2.5"}
{:plug_cowboy, "~> 2.5"},
{:cors_plug, "~> 3.0"},
{:hackney, "~> 1.9"},
{:ex_aws, "~> 2.1"},
{:ex_aws_s3, "~> 2.0"}
]
end

Expand Down
37 changes: 37 additions & 0 deletions backend/mix.lock
@@ -0,0 +1,37 @@
%{
"castore": {:hex, :castore, "1.0.0", "c25cd0794c054ebe6908a86820c8b92b5695814479ec95eeff35192720b71eec", [:mix], [], "hexpm", "577d0e855983a97ca1dfa33cbb8a3b6ece6767397ffb4861514343b078fc284b"},
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
"cors_plug": {:hex, :cors_plug, "3.0.3", "7c3ac52b39624bc616db2e937c282f3f623f25f8d550068b6710e58d04a0e330", [:mix], [{:plug, "~> 1.13", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3f2d759e8c272ed3835fab2ef11b46bddab8c1ab9528167bd463b6452edf830d"},
"cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
"db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"},
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
"ecto": {:hex, :ecto, "3.9.4", "3ee68e25dbe0c36f980f1ba5dd41ee0d3eb0873bccae8aeaf1a2647242bffa35", [: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 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "de5f988c142a3aa4ec18b85a4ec34a2390b65b24f02385c1144252ff6ff8ee75"},
"ecto_sql": {:hex, :ecto_sql, "3.9.2", "34227501abe92dba10d9c3495ab6770e75e79b836d114c41108a4bf2ce200ad5", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1eb5eeb4358fdbcd42eac11c1fbd87e3affd7904e639d77903c1358b2abd3f70"},
"ex_aws": {:hex, :ex_aws, "2.4.1", "d1dc8965d1dc1c939dd4570e37f9f1d21e047e4ecd4f9373dc89cd4e45dce5ef", [:mix], [{:configparser_ex, "~> 4.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:jsx, "~> 2.8 or ~> 3.0", [hex: :jsx, repo: "hexpm", optional: true]}, {:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "803387db51b4e91be4bf0110ba999003ec6103de7028b808ee9b01f28dbb9eee"},
"ex_aws_s3": {:hex, :ex_aws_s3, "2.4.0", "ce8decb6b523381812798396bc0e3aaa62282e1b40520125d1f4eff4abdff0f4", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "85dda6e27754d94582869d39cba3241d9ea60b6aa4167f9c88e309dc687e56bb"},
"hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"},
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"phoenix": {:hex, :phoenix, "1.6.15", "0a1d96bbc10747fd83525370d691953cdb6f3ccbac61aa01b4acb012474b047d", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d70ab9fbf6b394755ea88b644d34d79d8b146e490973151f248cacd122d20672"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"},
"phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"},
"phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"},
"plug": {:hex, :plug, "1.14.0", "ba4f558468f69cbd9f6b356d25443d0b796fbdc887e03fa89001384a9cac638f", [:mix], [{:mime, "~> 1.0 or ~> 2.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.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bf020432c7d4feb7b3af16a0c2701455cbbbb95e5b6866132cb09eb0c29adc14"},
"plug_cowboy": {:hex, :plug_cowboy, "2.6.0", "d1cf12ff96a1ca4f52207c5271a6c351a4733f413803488d75b70ccf44aebec2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "073cf20b753ce6682ed72905cd62a2d4bd9bad1bf9f7feb02a1b8e525bd94fa6"},
"plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"},
"postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"},
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
}

0 comments on commit 8415884

Please sign in to comment.