initial commit
This commit is contained in:
parent
d91e8e7dcd
commit
836e828371
53 changed files with 2142 additions and 0 deletions
62
lib/demo_web/channels/room_channel.ex
Normal file
62
lib/demo_web/channels/room_channel.ex
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
defmodule DemoWeb.RoomChannel do
|
||||
use DemoWeb, :channel
|
||||
require Logger
|
||||
|
||||
@impl true
|
||||
def join("room:lobby", payload, socket) do
|
||||
if authorized?(payload) do
|
||||
{:ok, socket}
|
||||
else
|
||||
{:error, %{reason: "unauthorized"}}
|
||||
end
|
||||
end
|
||||
|
||||
# Channels can be used in a request/response fashion
|
||||
# by sending replies to requests from the client
|
||||
@impl true
|
||||
def handle_in("ping", payload, socket) do
|
||||
{:reply, {:ok, payload}, socket}
|
||||
end
|
||||
|
||||
#
|
||||
# Here is recieved the requests from the client
|
||||
# we spawn a new process to compute the result, which
|
||||
# is sent back in a new message when the task is finished
|
||||
# The new process PID is used as a key for the client to
|
||||
# associate the result to its request.
|
||||
# the key(id) is immediatly returned to the client.
|
||||
#
|
||||
@impl true
|
||||
def handle_in("fib", payload, socket) do
|
||||
me = self()
|
||||
id = spawn(fn ->
|
||||
result = Funcs.fib(String.to_integer(payload["query"]))
|
||||
send(me, {:response, %{id: Kernel.inspect(self()),
|
||||
query: payload["query"],
|
||||
result: result}})
|
||||
end)
|
||||
{:reply, {:ok, %{id: Kernel.inspect(id)}}, socket}
|
||||
end
|
||||
|
||||
# It is also common to receive messages from the client and
|
||||
# broadcast to everyone in the current topic (room:lobby).
|
||||
@impl true
|
||||
def handle_in("shout", payload, socket) do
|
||||
broadcast(socket, "shout", payload)
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
# Add authorization logic here as required.
|
||||
defp authorized?(_payload) do
|
||||
true
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info({:response, map}, socket) do
|
||||
Logger.debug("send response #{Kernel.inspect map}")
|
||||
push(socket, "response", map)
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
41
lib/demo_web/channels/user_socket.ex
Normal file
41
lib/demo_web/channels/user_socket.ex
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
defmodule DemoWeb.UserSocket do
|
||||
use Phoenix.Socket
|
||||
|
||||
# A Socket handler
|
||||
#
|
||||
# It's possible to control the websocket connection and
|
||||
# assign values that can be accessed by your channel topics.
|
||||
|
||||
## Channels
|
||||
|
||||
channel "room:*", DemoWeb.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:
|
||||
#
|
||||
# Elixir.DemoWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
|
||||
#
|
||||
# Returning `nil` makes this socket anonymous.
|
||||
@impl true
|
||||
def id(_socket), do: nil
|
||||
end
|
||||
7
lib/demo_web/controllers/page_controller.ex
Normal file
7
lib/demo_web/controllers/page_controller.ex
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
defmodule DemoWeb.PageController do
|
||||
use DemoWeb, :controller
|
||||
|
||||
def index(conn, _params) do
|
||||
render(conn, "index.html")
|
||||
end
|
||||
end
|
||||
55
lib/demo_web/endpoint.ex
Normal file
55
lib/demo_web/endpoint.ex
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
defmodule DemoWeb.Endpoint do
|
||||
use Phoenix.Endpoint, otp_app: :demo
|
||||
|
||||
# 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: "_demo_key",
|
||||
signing_salt: "AZgmTvLs"
|
||||
]
|
||||
|
||||
socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]]
|
||||
|
||||
# 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: :demo,
|
||||
gzip: false,
|
||||
only: ~w(assets fonts images 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
|
||||
plug Phoenix.Ecto.CheckRepoStatus, otp_app: :demo
|
||||
end
|
||||
|
||||
plug Phoenix.LiveDashboard.RequestLogger,
|
||||
param_key: "request_logger",
|
||||
cookie_key: "request_logger"
|
||||
|
||||
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 DemoWeb.Router
|
||||
|
||||
socket "/socket", DemoWeb.UserSocket,
|
||||
websocket: true,
|
||||
longpoll: false
|
||||
|
||||
end
|
||||
24
lib/demo_web/gettext.ex
Normal file
24
lib/demo_web/gettext.ex
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
defmodule DemoWeb.Gettext do
|
||||
@moduledoc """
|
||||
A module providing Internationalization with a gettext-based API.
|
||||
|
||||
By using [Gettext](https://hexdocs.pm/gettext),
|
||||
your module gains a set of macros for translations, for example:
|
||||
|
||||
import DemoWeb.Gettext
|
||||
|
||||
# Simple translation
|
||||
gettext("Here is the string to translate")
|
||||
|
||||
# Plural translation
|
||||
ngettext("Here is the string to translate",
|
||||
"Here are the strings to translate",
|
||||
3)
|
||||
|
||||
# Domain-based translation
|
||||
dgettext("errors", "Here is the error message to translate")
|
||||
|
||||
See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage.
|
||||
"""
|
||||
use Gettext, otp_app: :demo
|
||||
end
|
||||
55
lib/demo_web/router.ex
Normal file
55
lib/demo_web/router.ex
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
defmodule DemoWeb.Router do
|
||||
use DemoWeb, :router
|
||||
|
||||
pipeline :browser do
|
||||
plug :accepts, ["html"]
|
||||
plug :fetch_session
|
||||
plug :fetch_live_flash
|
||||
plug :put_root_layout, {DemoWeb.LayoutView, :root}
|
||||
plug :protect_from_forgery
|
||||
plug :put_secure_browser_headers
|
||||
end
|
||||
|
||||
pipeline :api do
|
||||
plug :accepts, ["json"]
|
||||
end
|
||||
|
||||
scope "/", DemoWeb do
|
||||
pipe_through :browser
|
||||
|
||||
get "/", PageController, :index
|
||||
end
|
||||
|
||||
# Other scopes may use custom stacks.
|
||||
# scope "/api", DemoWeb do
|
||||
# pipe_through :api
|
||||
# end
|
||||
|
||||
# Enables LiveDashboard only for development
|
||||
#
|
||||
# If you want to use the LiveDashboard in production, you should put
|
||||
# it behind authentication and allow only admins to access it.
|
||||
# If your application does not have an admins-only section yet,
|
||||
# you can use Plug.BasicAuth to set up some basic authentication
|
||||
# as long as you are also using SSL (which you should anyway).
|
||||
if Mix.env() in [:dev, :test] do
|
||||
import Phoenix.LiveDashboard.Router
|
||||
|
||||
scope "/" do
|
||||
pipe_through :browser
|
||||
live_dashboard "/dashboard", metrics: DemoWeb.Telemetry
|
||||
end
|
||||
end
|
||||
|
||||
# Enables the Swoosh mailbox preview in development.
|
||||
#
|
||||
# Note that preview only shows emails that were sent by the same
|
||||
# node running the Phoenix server.
|
||||
if Mix.env() == :dev do
|
||||
scope "/dev" do
|
||||
pipe_through :browser
|
||||
|
||||
forward "/mailbox", Plug.Swoosh.MailboxPreview
|
||||
end
|
||||
end
|
||||
end
|
||||
71
lib/demo_web/telemetry.ex
Normal file
71
lib/demo_web/telemetry.ex
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
defmodule DemoWeb.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}
|
||||
),
|
||||
|
||||
# Database Metrics
|
||||
summary("demo.repo.query.total_time",
|
||||
unit: {:native, :millisecond},
|
||||
description: "The sum of the other measurements"
|
||||
),
|
||||
summary("demo.repo.query.decode_time",
|
||||
unit: {:native, :millisecond},
|
||||
description: "The time spent decoding the data received from the database"
|
||||
),
|
||||
summary("demo.repo.query.query_time",
|
||||
unit: {:native, :millisecond},
|
||||
description: "The time spent executing the query"
|
||||
),
|
||||
summary("demo.repo.query.queue_time",
|
||||
unit: {:native, :millisecond},
|
||||
description: "The time spent waiting for a database connection"
|
||||
),
|
||||
summary("demo.repo.query.idle_time",
|
||||
unit: {:native, :millisecond},
|
||||
description:
|
||||
"The time the connection spent waiting before being checked out for the query"
|
||||
),
|
||||
|
||||
# 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.
|
||||
# {DemoWeb, :count_users, []}
|
||||
]
|
||||
end
|
||||
end
|
||||
5
lib/demo_web/templates/layout/app.html.heex
Normal file
5
lib/demo_web/templates/layout/app.html.heex
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<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>
|
||||
11
lib/demo_web/templates/layout/live.html.heex
Normal file
11
lib/demo_web/templates/layout/live.html.heex
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<main class="container">
|
||||
<p class="alert alert-info" role="alert"
|
||||
phx-click="lv:clear-flash"
|
||||
phx-value-key="info"><%= live_flash(@flash, :info) %></p>
|
||||
|
||||
<p class="alert alert-danger" role="alert"
|
||||
phx-click="lv:clear-flash"
|
||||
phx-value-key="error"><%= live_flash(@flash, :error) %></p>
|
||||
|
||||
<%= @inner_content %>
|
||||
</main>
|
||||
30
lib/demo_web/templates/layout/root.html.heex
Normal file
30
lib/demo_web/templates/layout/root.html.heex
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<!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"/>
|
||||
<%= csrf_meta_tag() %>
|
||||
<%= live_title_tag assigns[:page_title] || "Demo", suffix: " · Phoenix Framework" %>
|
||||
<link phx-track-static rel="stylesheet" href={Routes.static_path(@conn, "/assets/app.css")}/>
|
||||
<script defer phx-track-static type="text/javascript" src={Routes.static_path(@conn, "/assets/app.js")}></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<section class="container">
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="https://hexdocs.pm/phoenix/overview.html">Get Started</a></li>
|
||||
<%= if function_exported?(Routes, :live_dashboard_path, 2) do %>
|
||||
<li><%= link "LiveDashboard", to: Routes.live_dashboard_path(@conn, :home) %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</nav>
|
||||
<a href="https://phoenixframework.org/" class="phx-logo">
|
||||
<img src={Routes.static_path(@conn, "/images/phoenix.png")} alt="Phoenix Framework Logo"/>
|
||||
</a>
|
||||
</section>
|
||||
</header>
|
||||
<%= @inner_content %>
|
||||
</body>
|
||||
</html>
|
||||
13
lib/demo_web/templates/page/index.html.heex
Normal file
13
lib/demo_web/templates/page/index.html.heex
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<section class="phx-hero">
|
||||
<h1>Stats</h1>
|
||||
<canvas style="width=800px" id="ping_canvas" width="400" height="200"></canvas>
|
||||
</section>
|
||||
|
||||
<section class="row">
|
||||
<article class="column">
|
||||
<h2>Fib</h2>
|
||||
<input id="in_fib" /><input id="btn_fib" type="button" value="Send"/>
|
||||
<div id="results">
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
41
lib/demo_web/templates/page/index.html.heex~
Normal file
41
lib/demo_web/templates/page/index.html.heex~
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<section class="phx-hero">
|
||||
<h1><%= gettext "Welcome to %{name}!", name: "Phoenix" %></h1>
|
||||
<p>Peace of mind from prototype to production</p>
|
||||
</section>
|
||||
|
||||
<section class="row">
|
||||
<article class="column">
|
||||
<h2>Resources</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://hexdocs.pm/phoenix/overview.html">Guides & Docs</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/phoenixframework/phoenix">Source</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/phoenixframework/phoenix/blob/v1.6/CHANGELOG.md">v1.6 Changelog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
<article class="column">
|
||||
<h2>Help</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://elixirforum.com/c/phoenix-forum">Forum</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://web.libera.chat/#elixir">#elixir on Libera Chat (IRC)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://twitter.com/elixirphoenix">Twitter @elixirphoenix</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://elixir-slackin.herokuapp.com/">Elixir on Slack</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://discord.gg/elixir">Elixir on Discord</a>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
</section>
|
||||
47
lib/demo_web/views/error_helpers.ex
Normal file
47
lib/demo_web/views/error_helpers.ex
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
defmodule DemoWeb.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 using gettext.
|
||||
"""
|
||||
def translate_error({msg, opts}) do
|
||||
# When using gettext, we typically pass the strings we want
|
||||
# to translate as a static argument:
|
||||
#
|
||||
# # Translate "is invalid" in the "errors" domain
|
||||
# dgettext("errors", "is invalid")
|
||||
#
|
||||
# # Translate the number of files with plural rules
|
||||
# dngettext("errors", "1 file", "%{count} files", count)
|
||||
#
|
||||
# Because the error messages we show in our forms and APIs
|
||||
# are defined inside Ecto, we need to translate them dynamically.
|
||||
# This requires us to call the Gettext module passing our gettext
|
||||
# backend as first argument.
|
||||
#
|
||||
# Note we use the "errors" domain, which means translations
|
||||
# should be written to the errors.po file. The :count option is
|
||||
# set by Ecto and indicates we should also apply plural rules.
|
||||
if count = opts[:count] do
|
||||
Gettext.dngettext(DemoWeb.Gettext, "errors", msg, msg, count, opts)
|
||||
else
|
||||
Gettext.dgettext(DemoWeb.Gettext, "errors", msg, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
16
lib/demo_web/views/error_view.ex
Normal file
16
lib/demo_web/views/error_view.ex
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
defmodule DemoWeb.ErrorView do
|
||||
use DemoWeb, :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
|
||||
7
lib/demo_web/views/layout_view.ex
Normal file
7
lib/demo_web/views/layout_view.ex
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
defmodule DemoWeb.LayoutView do
|
||||
use DemoWeb, :view
|
||||
|
||||
# Phoenix LiveDashboard is available only in development by default,
|
||||
# so we instruct Elixir to not warn if the dashboard route is missing.
|
||||
@compile {:no_warn_undefined, {Routes, :live_dashboard_path, 2}}
|
||||
end
|
||||
3
lib/demo_web/views/page_view.ex
Normal file
3
lib/demo_web/views/page_view.ex
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
defmodule DemoWeb.PageView do
|
||||
use DemoWeb, :view
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue