Spaces:
Sleeping
Sleeping
Scott Hiett
commited on
Commit
•
2e92879
0
Parent(s):
Initial commit
Browse files- .formatter.exs +4 -0
- .gitignore +30 -0
- README.md +11 -0
- lib/srh.ex +21 -0
- lib/srh/auth/token_resolver.ex +17 -0
- lib/srh/http/base_router.ex +43 -0
- lib/srh/http/command_handler.ex +35 -0
- lib/srh/http/request_validator.ex +24 -0
- lib/srh/redis/client.ex +80 -0
- lib/srh/redis/client_registry.ex +87 -0
- lib/srh/redis/client_worker.ex +68 -0
- mix.exs +34 -0
- mix.lock +15 -0
.formatter.exs
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Used by "mix format"
|
2 |
+
[
|
3 |
+
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
4 |
+
]
|
.gitignore
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# The directory Mix will write compiled artifacts to.
|
2 |
+
/_build/
|
3 |
+
|
4 |
+
# If you run "mix test --cover", coverage assets end up here.
|
5 |
+
/cover/
|
6 |
+
|
7 |
+
# The directory Mix downloads your dependencies sources to.
|
8 |
+
/deps/
|
9 |
+
|
10 |
+
# Where third-party dependencies like ExDoc output generated docs.
|
11 |
+
/doc/
|
12 |
+
|
13 |
+
# Ignore .fetch files in case you like to edit your project deps locally.
|
14 |
+
/.fetch
|
15 |
+
|
16 |
+
# If the VM crashes, it generates a dump, let's ignore it too.
|
17 |
+
erl_crash.dump
|
18 |
+
|
19 |
+
# Also ignore archive artifacts (built via "mix archive.build").
|
20 |
+
*.ez
|
21 |
+
|
22 |
+
# Ignore package tarball (built via "mix hex.build").
|
23 |
+
srh-*.tar
|
24 |
+
|
25 |
+
# Temporary files, for example, from tests.
|
26 |
+
/tmp/
|
27 |
+
|
28 |
+
.idea/
|
29 |
+
|
30 |
+
*.iml
|
README.md
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Serverless Redis HTTP
|
2 |
+
A redis connection pooler for serverless applications. This allows your serverless functions to talk to Redis via HTTP,
|
3 |
+
while also not having to worry about the Redis max connection limits.
|
4 |
+
|
5 |
+
Totally open source so you still own the data - run it with your own Redis server.
|
6 |
+
|
7 |
+
I will also offer some kinda SaaS version when it's a bit more done ;) - but still BYOR (bring your own Redis)!
|
8 |
+
|
9 |
+
Don't use this yet - it's still in development.
|
10 |
+
|
11 |
+
Also, it's totally compatible with the Upstash Redis Client SDKs.
|
lib/srh.ex
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
defmodule Srh do
|
2 |
+
use Application
|
3 |
+
|
4 |
+
def start(_type, _args) do
|
5 |
+
children = [
|
6 |
+
{GenRegistry, worker_module: Srh.Redis.Client},
|
7 |
+
{
|
8 |
+
Plug.Cowboy,
|
9 |
+
scheme: :http,
|
10 |
+
plug: Srh.Http.BaseRouter,
|
11 |
+
options: [
|
12 |
+
port: 8080
|
13 |
+
]
|
14 |
+
}
|
15 |
+
]
|
16 |
+
|
17 |
+
opts = [strategy: :one_for_one, name: Srh.Supervisor]
|
18 |
+
|
19 |
+
Supervisor.start_link(children, opts)
|
20 |
+
end
|
21 |
+
end
|
lib/srh/auth/token_resolver.ex
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
defmodule Srh.Auth.TokenResolver do
|
2 |
+
def resolve(token) do
|
3 |
+
{
|
4 |
+
:ok,
|
5 |
+
Jason.decode!(
|
6 |
+
# This is done to replicate what will eventually be API endpoints, so they keys are not atoms
|
7 |
+
Jason.encode!(
|
8 |
+
%{
|
9 |
+
srh_id: "1000",
|
10 |
+
connection_string: "redis://localhost:6379",
|
11 |
+
max_connections: 10
|
12 |
+
}
|
13 |
+
)
|
14 |
+
)
|
15 |
+
}
|
16 |
+
end
|
17 |
+
end
|
lib/srh/http/base_router.ex
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
defmodule Srh.Http.BaseRouter do
|
2 |
+
use Plug.Router
|
3 |
+
alias Srh.Http.RequestValidator
|
4 |
+
alias Srh.Http.CommandHandler
|
5 |
+
|
6 |
+
plug :match
|
7 |
+
plug Plug.Parsers, parsers: [:json], pass: ["application/json"], json_decoder: Jason
|
8 |
+
plug :dispatch
|
9 |
+
|
10 |
+
get "/" do
|
11 |
+
handle_response({:ok, "Welcome to Serverless Redis HTTP!"}, conn)
|
12 |
+
end
|
13 |
+
|
14 |
+
post "/" do
|
15 |
+
case conn
|
16 |
+
|> get_req_header("authorization")
|
17 |
+
|> RequestValidator.validate_bearer_header()
|
18 |
+
do
|
19 |
+
{:ok, token} ->
|
20 |
+
CommandHandler.handle_command(conn, token)
|
21 |
+
{:error, _} ->
|
22 |
+
{:malformed_data, "Missing/Invalid authorization header"}
|
23 |
+
end
|
24 |
+
|> handle_response(conn)
|
25 |
+
end
|
26 |
+
|
27 |
+
match _ do
|
28 |
+
send_resp(conn, 404, "Endpoint not found")
|
29 |
+
end
|
30 |
+
|
31 |
+
defp handle_response(response, conn) do
|
32 |
+
%{code: code, message: message} =
|
33 |
+
case response do
|
34 |
+
{:ok, data} -> %{code: 200, message: Jason.encode!(data)}
|
35 |
+
{:not_found, message} -> %{code: 404, message: message}
|
36 |
+
{:malformed_data, message} -> %{code: 400, message: message}
|
37 |
+
{:server_error, _} -> %{code: 500, message: "An error occurred internally"}
|
38 |
+
end
|
39 |
+
|
40 |
+
conn
|
41 |
+
|> send_resp(code, message)
|
42 |
+
end
|
43 |
+
end
|
lib/srh/http/command_handler.ex
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
defmodule Srh.Http.CommandHandler do
|
2 |
+
alias Srh.Http.RequestValidator
|
3 |
+
alias Srh.Auth.TokenResolver
|
4 |
+
alias Srh.Redis.Client
|
5 |
+
|
6 |
+
def handle_command(conn, token) do
|
7 |
+
case RequestValidator.validate_redis_body(conn.body_params) do
|
8 |
+
{:ok, command_array} ->
|
9 |
+
IO.inspect(command_array)
|
10 |
+
do_handle_command(command_array, token)
|
11 |
+
{:error, error_message} ->
|
12 |
+
{:malformed_data, error_message}
|
13 |
+
end
|
14 |
+
end
|
15 |
+
|
16 |
+
defp do_handle_command(command_array, token) do
|
17 |
+
case TokenResolver.resolve(token) do
|
18 |
+
{:ok, connection_info} ->
|
19 |
+
dispatch_command(command_array, connection_info)
|
20 |
+
{:error, _} -> {:error, "Invalid token"}
|
21 |
+
end
|
22 |
+
end
|
23 |
+
|
24 |
+
defp dispatch_command(command_array, %{"srh_id" => srh_id, "max_connections" => max_connections} = connection_info)
|
25 |
+
when is_number(max_connections) do
|
26 |
+
case GenRegistry.lookup_or_start(Client, srh_id, [max_connections, connection_info]) do
|
27 |
+
{:ok, pid} ->
|
28 |
+
# Run the command
|
29 |
+
{:ok, res} = Client.redis_command(pid, command_array)
|
30 |
+
{:ok, %{result: res}}
|
31 |
+
{:error, msg} ->
|
32 |
+
{:server_error, msg}
|
33 |
+
end
|
34 |
+
end
|
35 |
+
end
|
lib/srh/http/request_validator.ex
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
defmodule Srh.Http.RequestValidator do
|
2 |
+
def validate_redis_body(%{"_json" => command_array}) when is_list(command_array), do: {:ok, command_array}
|
3 |
+
|
4 |
+
def validate_redis_body(payload),
|
5 |
+
do: {:error, "Invalid command array. Expected a string array at root of the command and its arguments."}
|
6 |
+
|
7 |
+
def validate_bearer_header(header_value_array) when is_list(header_value_array) do
|
8 |
+
do_validate_bearer_header(header_value_array)
|
9 |
+
end
|
10 |
+
|
11 |
+
# any amount of items left
|
12 |
+
defp do_validate_bearer_header([first_item | rest]) do
|
13 |
+
case first_item
|
14 |
+
|> String.split(" ") do
|
15 |
+
["Bearer", token] ->
|
16 |
+
{:ok, token}
|
17 |
+
_ ->
|
18 |
+
do_validate_bearer_header(rest)
|
19 |
+
end
|
20 |
+
end
|
21 |
+
|
22 |
+
# no items left
|
23 |
+
defp do_validate_bearer_header([]), do: {:error, :not_found}
|
24 |
+
end
|
lib/srh/redis/client.ex
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
defmodule Srh.Redis.Client do
|
2 |
+
use GenServer
|
3 |
+
alias Srh.Redis.ClientRegistry
|
4 |
+
alias Srh.Redis.ClientWorker
|
5 |
+
|
6 |
+
@idle_death_time 1000 * 3
|
7 |
+
|
8 |
+
def start_link(max_connections, connection_info) do
|
9 |
+
GenServer.start_link(__MODULE__, {max_connections, connection_info}, [])
|
10 |
+
end
|
11 |
+
|
12 |
+
def init({max_connections, connection_info}) do
|
13 |
+
IO.puts("Client starting alive! Srh_id=#{Map.get(connection_info, "srh_id", "not found")}")
|
14 |
+
|
15 |
+
Process.send(self(), :create_registry, [])
|
16 |
+
|
17 |
+
{
|
18 |
+
:ok,
|
19 |
+
%{
|
20 |
+
registry_pid: nil,
|
21 |
+
idle_death_ref: nil,
|
22 |
+
max_connections: max_connections,
|
23 |
+
connection_info: connection_info
|
24 |
+
}
|
25 |
+
}
|
26 |
+
end
|
27 |
+
|
28 |
+
def redis_command(client, command_array) do
|
29 |
+
GenServer.call(client, {:redis_command, command_array})
|
30 |
+
end
|
31 |
+
|
32 |
+
def handle_call({:redis_command, command_array}, _from, %{registry_pid: registry_pid} = state)
|
33 |
+
when is_pid(registry_pid) do
|
34 |
+
{:ok, worker} = ClientRegistry.find_worker(registry_pid)
|
35 |
+
Process.send(self(), :reset_idle_death, [])
|
36 |
+
{:reply, ClientWorker.redis_command(worker, command_array), state}
|
37 |
+
end
|
38 |
+
|
39 |
+
def handle_call(_msg, _from, state) do
|
40 |
+
{:reply, :ok, state}
|
41 |
+
end
|
42 |
+
|
43 |
+
def handle_cast(_msg, state) do
|
44 |
+
{:noreply, state}
|
45 |
+
end
|
46 |
+
|
47 |
+
def handle_info(:idle_death, state) do
|
48 |
+
IO.puts("Client dying! No requests for period. Srh_id=#{Map.get(state.connection_info, "srh_id", "not found")}")
|
49 |
+
ClientRegistry.destroy_workers(state.registry_pid)
|
50 |
+
|
51 |
+
{:stop, :normal, state}
|
52 |
+
end
|
53 |
+
|
54 |
+
def handle_info(:reset_idle_death, state) do
|
55 |
+
if state.idle_death_ref != nil do
|
56 |
+
Process.cancel_timer(state.idle_death_ref)
|
57 |
+
end
|
58 |
+
|
59 |
+
{
|
60 |
+
:noreply,
|
61 |
+
%{state | idle_death_ref: Process.send_after(self(), :idle_death, @idle_death_time)}
|
62 |
+
}
|
63 |
+
end
|
64 |
+
|
65 |
+
def handle_info(:create_registry, state) do
|
66 |
+
{:ok, pid} = ClientRegistry.start_link()
|
67 |
+
|
68 |
+
# Spin up three workers
|
69 |
+
for _ <- 0..2 do
|
70 |
+
{:ok, worker} = ClientWorker.start_link(state.connection_info)
|
71 |
+
ClientRegistry.add_worker(pid, worker)
|
72 |
+
end
|
73 |
+
|
74 |
+
{:noreply, %{state | registry_pid: pid}}
|
75 |
+
end
|
76 |
+
|
77 |
+
def handle_info(_msg, state) do
|
78 |
+
{:noreply, state}
|
79 |
+
end
|
80 |
+
end
|
lib/srh/redis/client_registry.ex
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
defmodule Srh.Redis.ClientRegistry do
|
2 |
+
use GenServer
|
3 |
+
|
4 |
+
def start_link() do
|
5 |
+
GenServer.start_link(__MODULE__, %{}, [])
|
6 |
+
end
|
7 |
+
|
8 |
+
def init(_opts) do
|
9 |
+
{
|
10 |
+
:ok,
|
11 |
+
%{
|
12 |
+
worker_pids: [],
|
13 |
+
last_worker_index: 0
|
14 |
+
}
|
15 |
+
}
|
16 |
+
end
|
17 |
+
|
18 |
+
def find_worker(registry) do
|
19 |
+
GenServer.call(registry, {:find_worker})
|
20 |
+
end
|
21 |
+
|
22 |
+
def add_worker(registry, pid) do
|
23 |
+
GenServer.cast(registry, {:add_worker, pid})
|
24 |
+
end
|
25 |
+
|
26 |
+
def destroy_workers(registry) do
|
27 |
+
GenServer.cast(registry, {:destroy_workers})
|
28 |
+
end
|
29 |
+
|
30 |
+
def handle_call({:find_worker}, _from, state) do
|
31 |
+
case length(state.worker_pids) do
|
32 |
+
0 ->
|
33 |
+
{:reply, {:error, :none_available}, state}
|
34 |
+
|
35 |
+
len ->
|
36 |
+
target = state.last_worker_index + 1
|
37 |
+
|
38 |
+
corrected_target = case target >= len do
|
39 |
+
true -> 0
|
40 |
+
false -> target
|
41 |
+
end
|
42 |
+
|
43 |
+
IO.puts("Worker target: #{corrected_target}, pool size: #{len}")
|
44 |
+
|
45 |
+
{:reply, {:ok, Enum.at(state.worker_pids, corrected_target)}, %{state | last_worker_index: corrected_target}}
|
46 |
+
end
|
47 |
+
end
|
48 |
+
|
49 |
+
def handle_call(_msg, _from, state) do
|
50 |
+
{:reply, :ok, state}
|
51 |
+
end
|
52 |
+
|
53 |
+
def handle_cast({:add_worker, pid}, state) do
|
54 |
+
Process.monitor(pid)
|
55 |
+
|
56 |
+
{
|
57 |
+
:noreply,
|
58 |
+
%{
|
59 |
+
state
|
60 |
+
|
|
61 |
+
worker_pids:
|
62 |
+
[pid | state.worker_pids]
|
63 |
+
|> Enum.uniq()
|
64 |
+
}
|
65 |
+
}
|
66 |
+
end
|
67 |
+
|
68 |
+
def handle_cast({:destroy_workers}, state) do
|
69 |
+
for worker_pid <- state.worker_pids do
|
70 |
+
Process.exit(worker_pid, :normal)
|
71 |
+
end
|
72 |
+
|
73 |
+
{:noreply, %{state | worker_pids: [], last_worker_index: 0}}
|
74 |
+
end
|
75 |
+
|
76 |
+
def handle_cast(_msg, state) do
|
77 |
+
{:noreply, state}
|
78 |
+
end
|
79 |
+
|
80 |
+
def handle_info({:DOWN, pid, :normal, _ref}, state) do
|
81 |
+
{:noreply, %{state | worker_pids: List.delete(state.worker_pids, pid)}}
|
82 |
+
end
|
83 |
+
|
84 |
+
def handle_info(_msg, state) do
|
85 |
+
{:noreply, state}
|
86 |
+
end
|
87 |
+
end
|
lib/srh/redis/client_worker.ex
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
defmodule Srh.Redis.ClientWorker do
|
2 |
+
use GenServer
|
3 |
+
|
4 |
+
def start_link(connection_info) do
|
5 |
+
GenServer.start_link(__MODULE__, connection_info, [])
|
6 |
+
end
|
7 |
+
|
8 |
+
def init(connection_info) do
|
9 |
+
IO.puts("Client worker reporting for duty! Srh_id=#{Map.get(connection_info, "srh_id", "not found")}")
|
10 |
+
|
11 |
+
Process.send(self(), :create_connection, [])
|
12 |
+
|
13 |
+
{
|
14 |
+
:ok,
|
15 |
+
%{
|
16 |
+
connection_info: connection_info,
|
17 |
+
redix_pid: nil
|
18 |
+
}
|
19 |
+
}
|
20 |
+
end
|
21 |
+
|
22 |
+
def redis_command(worker, command_array) do
|
23 |
+
GenServer.call(worker, {:redis_command, command_array})
|
24 |
+
end
|
25 |
+
|
26 |
+
def handle_call({:redis_command, command_array}, _from, %{redix_pid: redix_pid} = state)
|
27 |
+
when is_pid(redix_pid) do
|
28 |
+
case Redix.command(redix_pid, command_array) do
|
29 |
+
{:ok, res} ->
|
30 |
+
IO.puts("Worker PID:")
|
31 |
+
IO.inspect(self())
|
32 |
+
IO.puts("Worker Redis response:")
|
33 |
+
IO.inspect(res)
|
34 |
+
{:reply, {:ok, res}, state}
|
35 |
+
|
36 |
+
{:error, res} ->
|
37 |
+
{:reply, {:error, res}, state}
|
38 |
+
end
|
39 |
+
end
|
40 |
+
|
41 |
+
def handle_call(msg, _from, state) do
|
42 |
+
IO.inspect(msg)
|
43 |
+
{:reply, :ok, state}
|
44 |
+
end
|
45 |
+
|
46 |
+
def handle_cast(_msg, state) do
|
47 |
+
{:noreply, state}
|
48 |
+
end
|
49 |
+
|
50 |
+
# TODO: Handle host / port connections
|
51 |
+
def handle_info(
|
52 |
+
:create_connection,
|
53 |
+
%{
|
54 |
+
connection_info: %{
|
55 |
+
"connection_string" => connection_string
|
56 |
+
}
|
57 |
+
} = state
|
58 |
+
)
|
59 |
+
when is_binary(connection_string) do
|
60 |
+
{:ok, pid} = Redix.start_link(connection_string)
|
61 |
+
IO.puts("Redis up and running for worker Srh_id=#{Map.get(state.connection_info, "srh_id", "not found")}")
|
62 |
+
{:noreply, %{state | redix_pid: pid}}
|
63 |
+
end
|
64 |
+
|
65 |
+
def handle_info(_msg, state) do
|
66 |
+
{:noreply, state}
|
67 |
+
end
|
68 |
+
end
|
mix.exs
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
defmodule Srh.MixProject do
|
2 |
+
use Mix.Project
|
3 |
+
|
4 |
+
def project do
|
5 |
+
[
|
6 |
+
app: :srh,
|
7 |
+
version: "0.1.0",
|
8 |
+
elixir: "~> 1.13",
|
9 |
+
start_permanent: Mix.env() == :prod,
|
10 |
+
deps: deps()
|
11 |
+
]
|
12 |
+
end
|
13 |
+
|
14 |
+
# Run "mix help compile.app" to learn about applications.
|
15 |
+
def application do
|
16 |
+
[
|
17 |
+
extra_applications: [:logger],
|
18 |
+
mod: {Srh, []}
|
19 |
+
]
|
20 |
+
end
|
21 |
+
|
22 |
+
# Run "mix help deps" to learn about dependencies.
|
23 |
+
defp deps do
|
24 |
+
[
|
25 |
+
{:redix, "~> 1.1"},
|
26 |
+
{:castore, ">= 0.0.0"},
|
27 |
+
{:plug, "~> 1.13"},
|
28 |
+
{:cowboy, "~> 2.9"},
|
29 |
+
{:plug_cowboy, "~> 2.5"},
|
30 |
+
{:jason, "~> 1.3"},
|
31 |
+
{:gen_registry, "~> 1.1"}
|
32 |
+
]
|
33 |
+
end
|
34 |
+
end
|
mix.lock
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
%{
|
2 |
+
"castore": {:hex, :castore, "0.1.17", "ba672681de4e51ed8ec1f74ed624d104c0db72742ea1a5e74edbc770c815182f", [:mix], [], "hexpm", "d9844227ed52d26e7519224525cb6868650c272d4a3d327ce3ca5570c12163f9"},
|
3 |
+
"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"},
|
4 |
+
"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"},
|
5 |
+
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
|
6 |
+
"gen_registry": {:hex, :gen_registry, "1.1.0", "30d483ae11dee615e500783fcec3f493de77fc80d346ba785947731207f790b7", [:mix], [], "hexpm", "c3db77e15c62599d01dc6b65aec6a3412a99b00d7785ef4a065c0c043b5a4478"},
|
7 |
+
"jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"},
|
8 |
+
"mime": {:hex, :mime, "2.0.2", "0b9e1a4c840eafb68d820b0e2158ef5c49385d17fb36855ac6e7e087d4b1dcc5", [:mix], [], "hexpm", "e6a3f76b4c277739e36c2e21a2c640778ba4c3846189d5ab19f97f126df5f9b7"},
|
9 |
+
"plug": {:hex, :plug, "1.13.6", "187beb6b67c6cec50503e940f0434ea4692b19384d47e5fdfd701e93cadb4cc2", [: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", "02b9c6b9955bce92c829f31d6284bf53c591ca63c4fb9ff81dfd0418667a34ff"},
|
10 |
+
"plug_cowboy": {:hex, :plug_cowboy, "2.5.2", "62894ccd601cf9597e2c23911ff12798a8a18d237e9739f58a6b04e4988899fe", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ea6e87f774c8608d60c8d34022a7d073bd7680a0a013f049fc62bf35efea1044"},
|
11 |
+
"plug_crypto": {:hex, :plug_crypto, "1.2.2", "05654514ac717ff3a1843204b424477d9e60c143406aa94daf2274fdd280794d", [:mix], [], "hexpm", "87631c7ad914a5a445f0a3809f99b079113ae4ed4b867348dd9eec288cecb6db"},
|
12 |
+
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
|
13 |
+
"redix": {:hex, :redix, "1.1.5", "6fc460d66a5c2287e83e6d73dddc8d527ff59cb4d4f298b41e03a4db8c3b2bd5", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "679afdd4c14502fe9c11387ff1cdcb33065a1cf511097da1eee407f17c7a418b"},
|
14 |
+
"telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
|
15 |
+
}
|