about summary refs log tree commit diff
path: root/lib
diff options
context:
space:
mode:
authorMelonai <einebeere@gmail.com>2021-05-07 17:11:50 +0200
committerMelonai <einebeere@gmail.com>2021-05-07 17:11:50 +0200
commit88c0fbf10145ddd4ccd10ee432b4ca1aadd96a91 (patch)
tree33691a532f460dbdf8a0e7909b898c0b324eaa2f /lib
downloadrook-88c0fbf10145ddd4ccd10ee432b4ca1aadd96a91.tar.zst
rook-88c0fbf10145ddd4ccd10ee432b4ca1aadd96a91.zip
Initial structure
Diffstat (limited to 'lib')
-rw-r--r--lib/rook.ex9
-rw-r--r--lib/rook/application.ex32
-rw-r--r--lib/rook/utils/token.ex7
-rw-r--r--lib/rook_web.ex78
-rw-r--r--lib/rook_web/channels/user_socket.ex35
-rw-r--r--lib/rook_web/controllers/app_controller.ex21
-rw-r--r--lib/rook_web/controllers/home_controller.ex7
-rw-r--r--lib/rook_web/endpoint.ex47
-rw-r--r--lib/rook_web/router.ex28
-rw-r--r--lib/rook_web/telemetry.ex48
-rw-r--r--lib/rook_web/templates/app/entrypoint.html.eex5
-rw-r--r--lib/rook_web/templates/app/request.html.eex1
-rw-r--r--lib/rook_web/templates/app/share.html.eex1
-rw-r--r--lib/rook_web/templates/home/index.html.eex1
-rw-r--r--lib/rook_web/templates/layout/app.html.eex21
-rw-r--r--lib/rook_web/views/app_view.ex7
-rw-r--r--lib/rook_web/views/error_helpers.ex30
-rw-r--r--lib/rook_web/views/error_view.ex16
-rw-r--r--lib/rook_web/views/home_view.ex3
-rw-r--r--lib/rook_web/views/layout_view.ex3
20 files changed, 400 insertions, 0 deletions
diff --git a/lib/rook.ex b/lib/rook.ex
new file mode 100644
index 0000000..9bc30ab
--- /dev/null
+++ b/lib/rook.ex
@@ -0,0 +1,9 @@
+defmodule Rook do
+  @moduledoc """
+  Rook keeps the contexts that define your domain
+  and business logic.
+
+  Contexts are also responsible for managing your data, regardless
+  if it comes from the database, an external API or others.
+  """
+end
diff --git a/lib/rook/application.ex b/lib/rook/application.ex
new file mode 100644
index 0000000..71fdf24
--- /dev/null
+++ b/lib/rook/application.ex
@@ -0,0 +1,32 @@
+defmodule Rook.Application do
+  # See https://hexdocs.pm/elixir/Application.html
+  # for more information on OTP Applications
+  @moduledoc false
+
+  use Application
+
+  def start(_type, _args) do
+    children = [
+      # Start the Telemetry supervisor
+      RookWeb.Telemetry,
+      # Start the PubSub system
+      {Phoenix.PubSub, name: Rook.PubSub},
+      # Start the Endpoint (http/https)
+      RookWeb.Endpoint
+      # Start a worker by calling: Rook.Worker.start_link(arg)
+      # {Rook.Worker, arg}
+    ]
+
+    # See https://hexdocs.pm/elixir/Supervisor.html
+    # for other strategies and supported options
+    opts = [strategy: :one_for_one, name: Rook.Supervisor]
+    Supervisor.start_link(children, opts)
+  end
+
+  # Tell Phoenix to update the endpoint configuration
+  # whenever the application is updated.
+  def config_change(changed, _new, removed) do
+    RookWeb.Endpoint.config_change(changed, removed)
+    :ok
+  end
+end
diff --git a/lib/rook/utils/token.ex b/lib/rook/utils/token.ex
new file mode 100644
index 0000000..344789b
--- /dev/null
+++ b/lib/rook/utils/token.ex
@@ -0,0 +1,7 @@
+defmodule Rook.Utils.Token do
+  @alphabet "abcdefghijklmnopqrstuvw0123456789"
+
+  def token() do
+    Nanoid.generate(21, @alphabet)
+  end
+end
diff --git a/lib/rook_web.ex b/lib/rook_web.ex
new file mode 100644
index 0000000..5b96dc8
--- /dev/null
+++ b/lib/rook_web.ex
@@ -0,0 +1,78 @@
+defmodule RookWeb do
+  @moduledoc """
+  The entrypoint for defining your web interface, such
+  as controllers, views, channels and so on.
+
+  This can be used in your application as:
+
+      use RookWeb, :controller
+      use RookWeb, :view
+
+  The definitions below will be executed for every view,
+  controller, etc, so keep them short and clean, focused
+  on imports, uses and aliases.
+
+  Do NOT define functions inside the quoted expressions
+  below. Instead, define any helper function in modules
+  and import those modules here.
+  """
+
+  def controller do
+    quote do
+      use Phoenix.Controller, namespace: RookWeb
+
+      import Plug.Conn
+      alias RookWeb.Router.Helpers, as: Routes
+    end
+  end
+
+  def view do
+    quote do
+      use Phoenix.View,
+        root: "lib/rook_web/templates",
+        namespace: RookWeb
+
+      # Import convenience functions from controllers
+      import Phoenix.Controller,
+        only: [get_flash: 1, get_flash: 2, view_module: 1, view_template: 1]
+
+      # Include shared imports and aliases for views
+      unquote(view_helpers())
+    end
+  end
+
+  def router do
+    quote do
+      use Phoenix.Router
+
+      import Plug.Conn
+      import Phoenix.Controller
+    end
+  end
+
+  def channel do
+    quote do
+      use Phoenix.Channel
+    end
+  end
+
+  defp view_helpers do
+    quote do
+      # Use all HTML functionality (forms, tags, etc)
+      use Phoenix.HTML
+
+      # Import basic rendering functionality (render, render_layout, etc)
+      import Phoenix.View
+
+      import RookWeb.ErrorHelpers
+      alias RookWeb.Router.Helpers, as: Routes
+    end
+  end
+
+  @doc """
+  When used, dispatch to the appropriate controller/view/etc.
+  """
+  defmacro __using__(which) when is_atom(which) do
+    apply(__MODULE__, which, [])
+  end
+end
diff --git a/lib/rook_web/channels/user_socket.ex b/lib/rook_web/channels/user_socket.ex
new file mode 100644
index 0000000..66637af
--- /dev/null
+++ b/lib/rook_web/channels/user_socket.ex
@@ -0,0 +1,35 @@
+defmodule RookWeb.UserSocket do
+  use Phoenix.Socket
+
+  ## Channels
+  # channel "room:*", RookWeb.RoomChannel
+
+  # Socket params are passed from the client and can
+  # be used to verify and authenticate a user. After
+  # verification, you can put default assigns into
+  # the socket that will be set for all channels, ie
+  #
+  #     {:ok, assign(socket, :user_id, verified_user_id)}
+  #
+  # To deny connection, return `:error`.
+  #
+  # See `Phoenix.Token` documentation for examples in
+  # performing token verification on connect.
+  @impl true
+  def connect(_params, socket, _connect_info) do
+    {:ok, socket}
+  end
+
+  # Socket id's are topics that allow you to identify all sockets for a given user:
+  #
+  #     def id(socket), do: "user_socket:#{socket.assigns.user_id}"
+  #
+  # Would allow you to broadcast a "disconnect" event and terminate
+  # all active sockets and channels for a given user:
+  #
+  #     RookWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
+  #
+  # Returning `nil` makes this socket anonymous.
+  @impl true
+  def id(_socket), do: nil
+end
diff --git a/lib/rook_web/controllers/app_controller.ex b/lib/rook_web/controllers/app_controller.ex
new file mode 100644
index 0000000..4c36fda
--- /dev/null
+++ b/lib/rook_web/controllers/app_controller.ex
@@ -0,0 +1,21 @@
+defmodule RookWeb.AppController do
+  use RookWeb, :controller
+
+  plug :add_token
+
+  def share(conn, _params) do
+    render(conn, "share.html")
+  end
+
+  def request(conn, _params) do
+    render(conn, "request.html")
+  end
+
+  defp add_token(conn, _params) do
+    if conn.assigns[:token] do
+      conn
+    else
+      assign(conn, :token, Rook.Utils.Token.token())
+    end
+  end
+end
diff --git a/lib/rook_web/controllers/home_controller.ex b/lib/rook_web/controllers/home_controller.ex
new file mode 100644
index 0000000..c87814d
--- /dev/null
+++ b/lib/rook_web/controllers/home_controller.ex
@@ -0,0 +1,7 @@
+defmodule RookWeb.HomeController do
+  use RookWeb, :controller
+
+  def index(conn, _params) do
+    render(conn, "index.html")
+  end
+end
diff --git a/lib/rook_web/endpoint.ex b/lib/rook_web/endpoint.ex
new file mode 100644
index 0000000..dd847f7
--- /dev/null
+++ b/lib/rook_web/endpoint.ex
@@ -0,0 +1,47 @@
+defmodule RookWeb.Endpoint do
+  use Phoenix.Endpoint, otp_app: :rook
+
+  # The session will be stored in the cookie and signed,
+  # this means its contents can be read but not tampered with.
+  # Set :encryption_salt if you would also like to encrypt it.
+  @session_options [
+    store: :cookie,
+    key: "_rook_key",
+    signing_salt: "IVge2MOv"
+  ]
+
+  socket "/socket", RookWeb.UserSocket,
+    websocket: true,
+    longpoll: false
+
+  # Serve at "/" the static files from "priv/static" directory.
+  #
+  # You should set gzip to true if you are running phx.digest
+  # when deploying your static files in production.
+  plug Plug.Static,
+    at: "/",
+    from: :rook,
+    gzip: false,
+    only: ~w(css fonts images js favicon.ico robots.txt)
+
+  # Code reloading can be explicitly enabled under the
+  # :code_reloader configuration of your endpoint.
+  if code_reloading? do
+    socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
+    plug Phoenix.LiveReloader
+    plug Phoenix.CodeReloader
+  end
+
+  plug Plug.RequestId
+  plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]
+
+  plug Plug.Parsers,
+    parsers: [:urlencoded, :multipart, :json],
+    pass: ["*/*"],
+    json_decoder: Phoenix.json_library()
+
+  plug Plug.MethodOverride
+  plug Plug.Head
+  plug Plug.Session, @session_options
+  plug RookWeb.Router
+end
diff --git a/lib/rook_web/router.ex b/lib/rook_web/router.ex
new file mode 100644
index 0000000..298d722
--- /dev/null
+++ b/lib/rook_web/router.ex
@@ -0,0 +1,28 @@
+defmodule RookWeb.Router do
+  use RookWeb, :router
+
+  pipeline :browser do
+    plug :accepts, ["html"]
+    plug :fetch_session
+    plug :fetch_flash
+    plug :protect_from_forgery
+    plug :put_secure_browser_headers
+  end
+
+  pipeline :api do
+    plug :accepts, ["json"]
+  end
+
+  scope "/", RookWeb do
+    pipe_through :browser
+
+    get "/", HomeController, :index
+    get "/share", AppController, :share
+    get "/:token", AppController, :request
+  end
+
+  # Other scopes may use custom stacks.
+  # scope "/api", RookWeb do
+  #   pipe_through :api
+  # end
+end
diff --git a/lib/rook_web/telemetry.ex b/lib/rook_web/telemetry.ex
new file mode 100644
index 0000000..24b502b
--- /dev/null
+++ b/lib/rook_web/telemetry.ex
@@ -0,0 +1,48 @@
+defmodule RookWeb.Telemetry do
+  use Supervisor
+  import Telemetry.Metrics
+
+  def start_link(arg) do
+    Supervisor.start_link(__MODULE__, arg, name: __MODULE__)
+  end
+
+  @impl true
+  def init(_arg) do
+    children = [
+      # Telemetry poller will execute the given period measurements
+      # every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics
+      {:telemetry_poller, measurements: periodic_measurements(), period: 10_000}
+      # Add reporters as children of your supervision tree.
+      # {Telemetry.Metrics.ConsoleReporter, metrics: metrics()}
+    ]
+
+    Supervisor.init(children, strategy: :one_for_one)
+  end
+
+  def metrics do
+    [
+      # Phoenix Metrics
+      summary("phoenix.endpoint.stop.duration",
+        unit: {:native, :millisecond}
+      ),
+      summary("phoenix.router_dispatch.stop.duration",
+        tags: [:route],
+        unit: {:native, :millisecond}
+      ),
+
+      # VM Metrics
+      summary("vm.memory.total", unit: {:byte, :kilobyte}),
+      summary("vm.total_run_queue_lengths.total"),
+      summary("vm.total_run_queue_lengths.cpu"),
+      summary("vm.total_run_queue_lengths.io")
+    ]
+  end
+
+  defp periodic_measurements do
+    [
+      # A module, function and arguments to be invoked periodically.
+      # This function must call :telemetry.execute/3 and a metric must be added above.
+      # {RookWeb, :count_users, []}
+    ]
+  end
+end
diff --git a/lib/rook_web/templates/app/entrypoint.html.eex b/lib/rook_web/templates/app/entrypoint.html.eex
new file mode 100644
index 0000000..bc7e84a
--- /dev/null
+++ b/lib/rook_web/templates/app/entrypoint.html.eex
@@ -0,0 +1,5 @@
+<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/#{@entrypoint}.css") %>"/>
+<script defer type="text/javascript" src="<%= Routes.static_path(@conn, "/js/#{@entrypoint}.js") %>"></script>
+<script>window.token = "<%= @token %>";</script>
+
+<div id="app"></div>
diff --git a/lib/rook_web/templates/app/request.html.eex b/lib/rook_web/templates/app/request.html.eex
new file mode 100644
index 0000000..c09a843
--- /dev/null
+++ b/lib/rook_web/templates/app/request.html.eex
@@ -0,0 +1 @@
+<%= render_app(@conn, @token, "request") %>
diff --git a/lib/rook_web/templates/app/share.html.eex b/lib/rook_web/templates/app/share.html.eex
new file mode 100644
index 0000000..d72b2c4
--- /dev/null
+++ b/lib/rook_web/templates/app/share.html.eex
@@ -0,0 +1 @@
+<%= render_app(@conn, @token, "share") %>
diff --git a/lib/rook_web/templates/home/index.html.eex b/lib/rook_web/templates/home/index.html.eex
new file mode 100644
index 0000000..9152474
--- /dev/null
+++ b/lib/rook_web/templates/home/index.html.eex
@@ -0,0 +1 @@
+<p>Hi! Welcome to Walmart.</p>
diff --git a/lib/rook_web/templates/layout/app.html.eex b/lib/rook_web/templates/layout/app.html.eex
new file mode 100644
index 0000000..d651e23
--- /dev/null
+++ b/lib/rook_web/templates/layout/app.html.eex
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8"/>
+    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+    <title>Rook</title>
+    <link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
+  </head>
+  <body>
+    <header>
+      <section class="container">
+        Rook
+    </header>
+    <main role="main" class="container">
+      <p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
+      <p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
+      <%= @inner_content %>
+    </main>
+  </body>
+</html>
diff --git a/lib/rook_web/views/app_view.ex b/lib/rook_web/views/app_view.ex
new file mode 100644
index 0000000..66c7f6a
--- /dev/null
+++ b/lib/rook_web/views/app_view.ex
@@ -0,0 +1,7 @@
+defmodule RookWeb.AppView do
+  use RookWeb, :view
+
+  def render_app(conn, token, entrypoint) do
+    render("entrypoint.html", conn: conn, token: token, entrypoint: entrypoint)
+  end
+end
diff --git a/lib/rook_web/views/error_helpers.ex b/lib/rook_web/views/error_helpers.ex
new file mode 100644
index 0000000..a905b5e
--- /dev/null
+++ b/lib/rook_web/views/error_helpers.ex
@@ -0,0 +1,30 @@
+defmodule RookWeb.ErrorHelpers do
+  @moduledoc """
+  Conveniences for translating and building error messages.
+  """
+
+  use Phoenix.HTML
+
+  @doc """
+  Generates tag for inlined form input errors.
+  """
+  def error_tag(form, field) do
+    Enum.map(Keyword.get_values(form.errors, field), fn error ->
+      content_tag(:span, translate_error(error),
+        class: "invalid-feedback",
+        phx_feedback_for: input_name(form, field)
+      )
+    end)
+  end
+
+  @doc """
+  Translates an error message.
+  """
+  def translate_error({msg, opts}) do
+    # Because the error messages we show in our forms and APIs
+    # are defined inside Ecto, we need to translate them dynamically.
+    Enum.reduce(opts, msg, fn {key, value}, acc ->
+      String.replace(acc, "%{#{key}}", to_string(value))
+    end)
+  end
+end
diff --git a/lib/rook_web/views/error_view.ex b/lib/rook_web/views/error_view.ex
new file mode 100644
index 0000000..6ffc93d
--- /dev/null
+++ b/lib/rook_web/views/error_view.ex
@@ -0,0 +1,16 @@
+defmodule RookWeb.ErrorView do
+  use RookWeb, :view
+
+  # If you want to customize a particular status code
+  # for a certain format, you may uncomment below.
+  # def render("500.html", _assigns) do
+  #   "Internal Server Error"
+  # end
+
+  # By default, Phoenix returns the status message from
+  # the template name. For example, "404.html" becomes
+  # "Not Found".
+  def template_not_found(template, _assigns) do
+    Phoenix.Controller.status_message_from_template(template)
+  end
+end
diff --git a/lib/rook_web/views/home_view.ex b/lib/rook_web/views/home_view.ex
new file mode 100644
index 0000000..700ea71
--- /dev/null
+++ b/lib/rook_web/views/home_view.ex
@@ -0,0 +1,3 @@
+defmodule RookWeb.HomeView do
+  use RookWeb, :view
+end
diff --git a/lib/rook_web/views/layout_view.ex b/lib/rook_web/views/layout_view.ex
new file mode 100644
index 0000000..19d121f
--- /dev/null
+++ b/lib/rook_web/views/layout_view.ex
@@ -0,0 +1,3 @@
+defmodule RookWeb.LayoutView do
+  use RookWeb, :view
+end