···1+# StatusphereElixir
2+3+To start your Phoenix server:
4+5+* Run `mix setup` to install and setup dependencies
6+* Start Phoenix endpoint with `mix phx.server` or inside IEx with `iex -S mix phx.server`
7+8+Now you can visit [`localhost:4000`](http://localhost:4000) from your browser.
9+10+Ready to run in production? Please [check our deployment guides](https://hexdocs.pm/phoenix/deployment.html).
11+12+## Learn more
13+14+* Official website: https://www.phoenixframework.org/
15+* Guides: https://hexdocs.pm/phoenix/overview.html
16+* Docs: https://hexdocs.pm/phoenix
17+* Forum: https://elixirforum.com/c/phoenix-forum
18+* Source: https://github.com/phoenixframework/phoenix
···1+// If you want to use Phoenix channels, run `mix help phx.gen.channel`
2+// to get started and then uncomment the line below.
3+// import "./user_socket.js"
4+5+// You can include dependencies in two ways.
6+//
7+// The simplest option is to put them in assets/vendor and
8+// import them using relative paths:
9+//
10+// import "../vendor/some-package.js"
11+//
12+// Alternatively, you can `npm install some-package --prefix assets` and import
13+// them using a path starting with the package name:
14+//
15+// import "some-package"
16+//
17+// If you have dependencies that try to import CSS, esbuild will generate a separate `app.css` file.
18+// To load it, simply add a second `<link>` to your `root.html.heex` file.
19+20+// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
21+import "phoenix_html"
22+// Establish Phoenix Socket and LiveView configuration.
23+import {Socket} from "phoenix"
24+import {LiveSocket} from "phoenix_live_view"
25+import {hooks as colocatedHooks} from "phoenix-colocated/statusphere_elixir"
26+import topbar from "../vendor/topbar"
27+28+const csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
29+const liveSocket = new LiveSocket("/live", Socket, {
30+ longPollFallbackMs: 2500,
31+ params: {_csrf_token: csrfToken},
32+ hooks: {...colocatedHooks},
33+})
34+35+// Show progress bar on live navigation and form submits
36+topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
37+window.addEventListener("phx:page-loading-start", _info => topbar.show(300))
38+window.addEventListener("phx:page-loading-stop", _info => topbar.hide())
39+40+// connect if there are any LiveViews on the page
41+liveSocket.connect()
42+43+// expose liveSocket on window for web console debug logs and latency simulation:
44+// >> liveSocket.enableDebug()
45+// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
46+// >> liveSocket.disableLatencySim()
47+window.liveSocket = liveSocket
48+49+// The lines below enable quality of life phoenix_live_reload
50+// development features:
51+//
52+// 1. stream server logs to the browser console
53+// 2. click on elements to jump to their definitions in your code editor
54+//
55+if (process.env.NODE_ENV === "development") {
56+ window.addEventListener("phx:live_reload:attached", ({detail: reloader}) => {
57+ // Enable server log streaming to client.
58+ // Disable with reloader.disableServerLogs()
59+ reloader.enableServerLogs()
60+61+ // Open configured PLUG_EDITOR at file:line of the clicked element's HEEx component
62+ //
63+ // * click with "c" key pressed to open at caller location
64+ // * click with "d" key pressed to open at function component definition location
65+ let keyDown
66+ window.addEventListener("keydown", e => keyDown = e.key)
67+ window.addEventListener("keyup", _e => keyDown = null)
68+ window.addEventListener("click", e => {
69+ if(keyDown === "c"){
70+ e.preventDefault()
71+ e.stopImmediatePropagation()
72+ reloader.openEditorAtCaller(e.target)
73+ } else if(keyDown === "d"){
74+ e.preventDefault()
75+ e.stopImmediatePropagation()
76+ reloader.openEditorAtDef(e.target)
77+ }
78+ }, true)
79+80+ window.liveReloader = reloader
81+ })
82+}
83+
+32
assets/tsconfig.json
···00000000000000000000000000000000
···1+// This file is needed on most editors to enable the intelligent autocompletion
2+// of LiveView's JavaScript API methods. You can safely delete it if you don't need it.
3+//
4+// Note: This file assumes a basic esbuild setup without node_modules.
5+// We include a generic paths alias to deps to mimic how esbuild resolves
6+// the Phoenix and LiveView JavaScript assets.
7+// If you have a package.json in your project, you should remove the
8+// paths configuration and instead add the phoenix dependencies to the
9+// dependencies section of your package.json:
10+//
11+// {
12+// ...
13+// "dependencies": {
14+// ...,
15+// "phoenix": "../deps/phoenix",
16+// "phoenix_html": "../deps/phoenix_html",
17+// "phoenix_live_view": "../deps/phoenix_live_view"
18+// }
19+// }
20+//
21+// Feel free to adjust this configuration however you need.
22+{
23+ "compilerOptions": {
24+ "baseUrl": ".",
25+ "paths": {
26+ "*": ["../deps/*"]
27+ },
28+ "allowJs": true,
29+ "noEmit": true
30+ },
31+ "include": ["js/**/*"]
32+}
···1+import Config
2+3+# Configure your database
4+config :statusphere_elixir, StatusphereElixir.Repo,
5+ database: Path.expand("../statusphere_elixir_dev.db", __DIR__),
6+ pool_size: 5,
7+ stacktrace: true,
8+ show_sensitive_data_on_connection_error: true
9+10+# For development, we disable any cache and enable
11+# debugging and code reloading.
12+#
13+# The watchers configuration can be used to run external
14+# watchers to your application. For example, we can use it
15+# to bundle .js and .css sources.
16+config :statusphere_elixir, StatusphereElixirWeb.Endpoint,
17+ # Binding to loopback ipv4 address prevents access from other machines.
18+ # Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
19+ http: [ip: {127, 0, 0, 1}],
20+ check_origin: false,
21+ code_reloader: true,
22+ debug_errors: true,
23+ secret_key_base: "YjHndGQyYIEkOsJtZzle8dUtbznbvM2CwGhF7NSoWxgSTIGpouNbVIND7cAmnWfB",
24+ watchers: [
25+ esbuild: {Esbuild, :install_and_run, [:statusphere_elixir, ~w(--sourcemap=inline --watch)]},
26+ tailwind: {Tailwind, :run, [:statusphere_elixir, ~w(--watch)]}
27+ ]
28+29+# ## SSL Support
30+#
31+# In order to use HTTPS in development, a self-signed
32+# certificate can be generated by running the following
33+# Mix task:
34+#
35+# mix phx.gen.cert
36+#
37+# Run `mix help phx.gen.cert` for more information.
38+#
39+# The `http:` config above can be replaced with:
40+#
41+# https: [
42+# port: 4001,
43+# cipher_suite: :strong,
44+# keyfile: "priv/cert/selfsigned_key.pem",
45+# certfile: "priv/cert/selfsigned.pem"
46+# ],
47+#
48+# If desired, both `http:` and `https:` keys can be
49+# configured to run both http and https servers on
50+# different ports.
51+52+# Reload browser tabs when matching files change.
53+config :statusphere_elixir, StatusphereElixirWeb.Endpoint,
54+ live_reload: [
55+ web_console_logger: true,
56+ patterns: [
57+ # Static assets, except user uploads
58+ ~r"priv/static/(?!uploads/).*\.(js|css|png|jpeg|jpg|gif|svg)$",
59+ # Router, Controllers, LiveViews and LiveComponents
60+ ~r"lib/statusphere_elixir_web/router\.ex$",
61+ ~r"lib/statusphere_elixir_web/(controllers|live|components)/.*\.(ex|heex)$"
62+ ]
63+ ]
64+65+# Enable dev routes for dashboard and mailbox
66+config :statusphere_elixir, dev_routes: true
67+68+# Do not include metadata nor timestamps in development logs
69+config :logger, :default_formatter, format: "[$level] $message\n"
70+71+# Set a higher stacktrace during development. Avoid configuring such
72+# in production as building large stacktraces may be expensive.
73+config :phoenix, :stacktrace_depth, 20
74+75+# Initialize plugs at runtime for faster development compilation
76+config :phoenix, :plug_init_mode, :runtime
77+78+config :phoenix_live_view,
79+ # Include debug annotations and locations in rendered markup.
80+ # Changing this configuration will require mix clean and a full recompile.
81+ debug_heex_annotations: true,
82+ debug_attributes: true,
83+ # Enable helpful, but potentially expensive runtime checks
84+ enable_expensive_runtime_checks: true
+25
config/prod.exs
···0000000000000000000000000
···1+import Config
2+3+# Note we also include the path to a cache manifest
4+# containing the digested version of static files. This
5+# manifest is generated by the `mix assets.deploy` task,
6+# which you should run after static files are built and
7+# before starting your production server.
8+config :statusphere_elixir, StatusphereElixirWeb.Endpoint,
9+ cache_static_manifest: "priv/static/cache_manifest.json"
10+11+# Force using SSL in production. This also sets the "strict-security-transport" header,
12+# known as HSTS. If you have a health check endpoint, you may want to exclude it below.
13+# Note `:force_ssl` is required to be set at compile-time.
14+config :statusphere_elixir, StatusphereElixirWeb.Endpoint,
15+ force_ssl: [rewrite_on: [:x_forwarded_proto]],
16+ exclude: [
17+ # paths: ["/health"],
18+ hosts: ["localhost", "127.0.0.1"]
19+ ]
20+21+# Do not print debug messages in production
22+config :logger, level: :info
23+24+# Runtime production configuration, including reading
25+# of environment variables, is done on config/runtime.exs.
···1+import Config
2+3+# config/runtime.exs is executed for all environments, including
4+# during releases. It is executed after compilation and before the
5+# system starts, so it is typically used to load production configuration
6+# and secrets from environment variables or elsewhere. Do not define
7+# any compile-time configuration in here, as it won't be applied.
8+# The block below contains prod specific runtime configuration.
9+10+# ## Using releases
11+#
12+# If you use `mix release`, you need to explicitly enable the server
13+# by passing the PHX_SERVER=true when you start it:
14+#
15+# PHX_SERVER=true bin/statusphere_elixir start
16+#
17+# Alternatively, you can use `mix phx.gen.release` to generate a `bin/server`
18+# script that automatically sets the env var above.
19+if System.get_env("PHX_SERVER") do
20+ config :statusphere_elixir, StatusphereElixirWeb.Endpoint, server: true
21+end
22+23+config :statusphere_elixir, StatusphereElixirWeb.Endpoint,
24+ http: [port: String.to_integer(System.get_env("PORT", "4000"))]
25+26+if config_env() == :prod do
27+ database_path =
28+ System.get_env("DATABASE_PATH") ||
29+ raise """
30+ environment variable DATABASE_PATH is missing.
31+ For example: /etc/statusphere_elixir/statusphere_elixir.db
32+ """
33+34+ config :statusphere_elixir, StatusphereElixir.Repo,
35+ database: database_path,
36+ pool_size: String.to_integer(System.get_env("POOL_SIZE") || "5")
37+38+ # The secret key base is used to sign/encrypt cookies and other secrets.
39+ # A default value is used in config/dev.exs and config/test.exs but you
40+ # want to use a different value for prod and you most likely don't want
41+ # to check this value into version control, so we use an environment
42+ # variable instead.
43+ secret_key_base =
44+ System.get_env("SECRET_KEY_BASE") ||
45+ raise """
46+ environment variable SECRET_KEY_BASE is missing.
47+ You can generate one by calling: mix phx.gen.secret
48+ """
49+50+ host = System.get_env("PHX_HOST") || "example.com"
51+52+ config :statusphere_elixir, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")
53+54+ config :statusphere_elixir, StatusphereElixirWeb.Endpoint,
55+ url: [host: host, port: 443, scheme: "https"],
56+ http: [
57+ # Enable IPv6 and bind on all interfaces.
58+ # Set it to {0, 0, 0, 0, 0, 0, 0, 1} for local network only access.
59+ # See the documentation on https://hexdocs.pm/bandit/Bandit.html#t:options/0
60+ # for details about using IPv6 vs IPv4 and loopback vs public addresses.
61+ ip: {0, 0, 0, 0, 0, 0, 0, 0}
62+ ],
63+ secret_key_base: secret_key_base
64+65+ # ## SSL Support
66+ #
67+ # To get SSL working, you will need to add the `https` key
68+ # to your endpoint configuration:
69+ #
70+ # config :statusphere_elixir, StatusphereElixirWeb.Endpoint,
71+ # https: [
72+ # ...,
73+ # port: 443,
74+ # cipher_suite: :strong,
75+ # keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
76+ # certfile: System.get_env("SOME_APP_SSL_CERT_PATH")
77+ # ]
78+ #
79+ # The `cipher_suite` is set to `:strong` to support only the
80+ # latest and more secure SSL ciphers. This means old browsers
81+ # and clients may not be supported. You can set it to
82+ # `:compatible` for wider support.
83+ #
84+ # `:keyfile` and `:certfile` expect an absolute path to the key
85+ # and cert in disk or a relative path inside priv, for example
86+ # "priv/ssl/server.key". For all supported SSL configuration
87+ # options, see https://hexdocs.pm/plug/Plug.SSL.html#configure/1
88+ #
89+ # We also recommend setting `force_ssl` in your config/prod.exs,
90+ # ensuring no data is ever sent via http, always redirecting to https:
91+ #
92+ # config :statusphere_elixir, StatusphereElixirWeb.Endpoint,
93+ # force_ssl: [hsts: true]
94+ #
95+ # Check `Plug.SSL` for all available options in `force_ssl`.
96+end
+32
config/test.exs
···00000000000000000000000000000000
···1+import Config
2+3+# Configure your database
4+#
5+# The MIX_TEST_PARTITION environment variable can be used
6+# to provide built-in test partitioning in CI environment.
7+# Run `mix help test` for more information.
8+config :statusphere_elixir, StatusphereElixir.Repo,
9+ database: Path.expand("../statusphere_elixir_test.db", __DIR__),
10+ pool_size: 5,
11+ pool: Ecto.Adapters.SQL.Sandbox
12+13+# We don't run a server during test. If one is required,
14+# you can enable the server option below.
15+config :statusphere_elixir, StatusphereElixirWeb.Endpoint,
16+ http: [ip: {127, 0, 0, 1}, port: 4002],
17+ secret_key_base: "AjDaXXlcrR04FEL+g7EV1+pKX7Fnvuhp63uWWK4I8zwKYmgReKy4WxqvdSNDdoT7",
18+ server: false
19+20+# Print only warnings and errors during test
21+config :logger, level: :warning
22+23+# Initialize plugs at runtime for faster test compilation
24+config :phoenix, :plug_init_mode, :runtime
25+26+# Enable helpful, but potentially expensive runtime checks
27+config :phoenix_live_view,
28+ enable_expensive_runtime_checks: true
29+30+# Sort query params output of verified routes for robust url comparisons
31+config :phoenix,
32+ sort_verified_routes_query_params: true
···1+defmodule StatusphereElixir do
2+ @moduledoc """
3+ StatusphereElixir keeps the contexts that define your domain
4+ and business logic.
5+6+ Contexts are also responsible for managing your data, regardless
7+ if it comes from the database, an external API or others.
8+ """
9+end
+41
lib/statusphere_elixir/application.ex
···00000000000000000000000000000000000000000
···1+defmodule StatusphereElixir.Application do
2+ # See https://hexdocs.pm/elixir/Application.html
3+ # for more information on OTP Applications
4+ @moduledoc false
5+6+ use Application
7+8+ @impl true
9+ def start(_type, _args) do
10+ children = [
11+ StatusphereElixirWeb.Telemetry,
12+ StatusphereElixir.Repo,
13+ {Ecto.Migrator,
14+ repos: Application.fetch_env!(:statusphere_elixir, :ecto_repos), skip: skip_migrations?()},
15+ {DNSCluster, query: Application.get_env(:statusphere_elixir, :dns_cluster_query) || :ignore},
16+ {Phoenix.PubSub, name: StatusphereElixir.PubSub},
17+ # Start a worker by calling: StatusphereElixir.Worker.start_link(arg)
18+ # {StatusphereElixir.Worker, arg},
19+ # Start to serve requests, typically the last entry
20+ StatusphereElixirWeb.Endpoint
21+ ]
22+23+ # See https://hexdocs.pm/elixir/Supervisor.html
24+ # for other strategies and supported options
25+ opts = [strategy: :one_for_one, name: StatusphereElixir.Supervisor]
26+ Supervisor.start_link(children, opts)
27+ end
28+29+ # Tell Phoenix to update the endpoint configuration
30+ # whenever the application is updated.
31+ @impl true
32+ def config_change(changed, _new, removed) do
33+ StatusphereElixirWeb.Endpoint.config_change(changed, removed)
34+ :ok
35+ end
36+37+ defp skip_migrations?() do
38+ # By default, sqlite migrations are run when using a release
39+ System.get_env("RELEASE_NAME") == nil
40+ end
41+end
+5
lib/statusphere_elixir/repo.ex
···00000
···1+defmodule StatusphereElixir.Repo do
2+ use Ecto.Repo,
3+ otp_app: :statusphere_elixir,
4+ adapter: Ecto.Adapters.SQLite3
5+end
···1+defmodule StatusphereElixirWeb do
2+ @moduledoc """
3+ The entrypoint for defining your web interface, such
4+ as controllers, components, channels, and so on.
5+6+ This can be used in your application as:
7+8+ use StatusphereElixirWeb, :controller
9+ use StatusphereElixirWeb, :html
10+11+ The definitions below will be executed for every controller,
12+ component, etc, so keep them short and clean, focused
13+ on imports, uses and aliases.
14+15+ Do NOT define functions inside the quoted expressions
16+ below. Instead, define additional modules and import
17+ those modules here.
18+ """
19+20+ def static_paths, do: ~w(assets fonts images favicon.ico robots.txt)
21+22+ def router do
23+ quote do
24+ use Phoenix.Router, helpers: false
25+26+ # Import common connection and controller functions to use in pipelines
27+ import Plug.Conn
28+ import Phoenix.Controller
29+ import Phoenix.LiveView.Router
30+ end
31+ end
32+33+ def channel do
34+ quote do
35+ use Phoenix.Channel
36+ end
37+ end
38+39+ def controller do
40+ quote do
41+ use Phoenix.Controller, formats: [:html, :json]
42+43+ import Plug.Conn
44+45+ unquote(verified_routes())
46+ end
47+ end
48+49+ def live_view do
50+ quote do
51+ use Phoenix.LiveView
52+53+ unquote(html_helpers())
54+ end
55+ end
56+57+ def live_component do
58+ quote do
59+ use Phoenix.LiveComponent
60+61+ unquote(html_helpers())
62+ end
63+ end
64+65+ def html do
66+ quote do
67+ use Phoenix.Component
68+69+ # Import convenience functions from controllers
70+ import Phoenix.Controller,
71+ only: [get_csrf_token: 0, view_module: 1, view_template: 1]
72+73+ # Include general helpers for rendering HTML
74+ unquote(html_helpers())
75+ end
76+ end
77+78+ defp html_helpers do
79+ quote do
80+ # HTML escaping functionality
81+ import Phoenix.HTML
82+ # Core UI components
83+ import StatusphereElixirWeb.CoreComponents
84+85+ # Common modules used in templates
86+ alias Phoenix.LiveView.JS
87+ alias StatusphereElixirWeb.Layouts
88+89+ # Routes generation with the ~p sigil
90+ unquote(verified_routes())
91+ end
92+ end
93+94+ def verified_routes do
95+ quote do
96+ use Phoenix.VerifiedRoutes,
97+ endpoint: StatusphereElixirWeb.Endpoint,
98+ router: StatusphereElixirWeb.Router,
99+ statics: StatusphereElixirWeb.static_paths()
100+ end
101+ end
102+103+ @doc """
104+ When used, dispatch to the appropriate controller/live_view/etc.
105+ """
106+ defmacro __using__(which) when is_atom(which) do
107+ apply(__MODULE__, which, [])
108+ end
109+end
···1+defmodule StatusphereElixirWeb.ErrorHTML do
2+ @moduledoc """
3+ This module is invoked by your endpoint in case of errors on HTML requests.
4+5+ See config/config.exs.
6+ """
7+ use StatusphereElixirWeb, :html
8+9+ # If you want to customize your error pages,
10+ # uncomment the embed_templates/1 call below
11+ # and add pages to the error directory:
12+ #
13+ # * lib/statusphere_elixir_web/controllers/error_html/404.html.heex
14+ # * lib/statusphere_elixir_web/controllers/error_html/500.html.heex
15+ #
16+ # embed_templates "error_html/*"
17+18+ # The default is to render a plain text page based on
19+ # the template name. For example, "404.html" becomes
20+ # "Not Found".
21+ def render(template, _assigns) do
22+ Phoenix.Controller.status_message_from_template(template)
23+ end
24+end
···1+defmodule StatusphereElixirWeb.ErrorJSON do
2+ @moduledoc """
3+ This module is invoked by your endpoint in case of errors on JSON requests.
4+5+ See config/config.exs.
6+ """
7+8+ # If you want to customize a particular status code,
9+ # you may add your own clauses, such as:
10+ #
11+ # def render("500.json", _assigns) do
12+ # %{errors: %{detail: "Internal Server Error"}}
13+ # end
14+15+ # By default, Phoenix returns the status message from
16+ # the template name. For example, "404.json" becomes
17+ # "Not Found".
18+ def render(template, _assigns) do
19+ %{errors: %{detail: Phoenix.Controller.status_message_from_template(template)}}
20+ end
21+end
···1+defmodule StatusphereElixirWeb.PageController do
2+ use StatusphereElixirWeb, :controller
3+4+ def home(conn, _params) do
5+ render(conn, :home)
6+ end
7+end
···1+defmodule StatusphereElixirWeb.PageHTML do
2+ @moduledoc """
3+ This module contains pages rendered by PageController.
4+5+ See the `page_html` directory for all templates available.
6+ """
7+ use StatusphereElixirWeb, :html
8+9+ embed_templates "page_html/*"
10+end
···1+defmodule StatusphereElixirWeb.Endpoint do
2+ use Phoenix.Endpoint, otp_app: :statusphere_elixir
3+4+ # The session will be stored in the cookie and signed,
5+ # this means its contents can be read but not tampered with.
6+ # Set :encryption_salt if you would also like to encrypt it.
7+ @session_options [
8+ store: :cookie,
9+ key: "_statusphere_elixir_key",
10+ signing_salt: "zbKtWcZI",
11+ same_site: "Lax"
12+ ]
13+14+ socket "/live", Phoenix.LiveView.Socket,
15+ websocket: [connect_info: [session: @session_options]],
16+ longpoll: [connect_info: [session: @session_options]]
17+18+ # Serve at "/" the static files from "priv/static" directory.
19+ #
20+ # When code reloading is disabled (e.g., in production),
21+ # the `gzip` option is enabled to serve compressed
22+ # static files generated by running `phx.digest`.
23+ plug Plug.Static,
24+ at: "/",
25+ from: :statusphere_elixir,
26+ gzip: not code_reloading?,
27+ only: StatusphereElixirWeb.static_paths(),
28+ raise_on_missing_only: code_reloading?
29+30+ # Code reloading can be explicitly enabled under the
31+ # :code_reloader configuration of your endpoint.
32+ if code_reloading? do
33+ socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
34+ plug Phoenix.LiveReloader
35+ plug Phoenix.CodeReloader
36+ plug Phoenix.Ecto.CheckRepoStatus, otp_app: :statusphere_elixir
37+ end
38+39+ plug Phoenix.LiveDashboard.RequestLogger,
40+ param_key: "request_logger",
41+ cookie_key: "request_logger"
42+43+ plug Plug.RequestId
44+ plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]
45+46+ plug Plug.Parsers,
47+ parsers: [:urlencoded, :multipart, :json],
48+ pass: ["*/*"],
49+ json_decoder: Phoenix.json_library()
50+51+ plug Plug.MethodOverride
52+ plug Plug.Head
53+ plug Plug.Session, @session_options
54+ plug StatusphereElixirWeb.Router
55+end
+43
lib/statusphere_elixir_web/router.ex
···0000000000000000000000000000000000000000000
···1+defmodule StatusphereElixirWeb.Router do
2+ use StatusphereElixirWeb, :router
3+4+ pipeline :browser do
5+ plug :accepts, ["html"]
6+ plug :fetch_session
7+ plug :fetch_live_flash
8+ plug :put_root_layout, html: {StatusphereElixirWeb.Layouts, :root}
9+ plug :protect_from_forgery
10+ plug :put_secure_browser_headers
11+ end
12+13+ pipeline :api do
14+ plug :accepts, ["json"]
15+ end
16+17+ scope "/", StatusphereElixirWeb do
18+ pipe_through :browser
19+20+ get "/", PageController, :home
21+ end
22+23+ # Other scopes may use custom stacks.
24+ # scope "/api", StatusphereElixirWeb do
25+ # pipe_through :api
26+ # end
27+28+ # Enable LiveDashboard in development
29+ if Application.compile_env(:statusphere_elixir, :dev_routes) do
30+ # If you want to use the LiveDashboard in production, you should put
31+ # it behind authentication and allow only admins to access it.
32+ # If your application does not have an admins-only section yet,
33+ # you can use Plug.BasicAuth to set up some basic authentication
34+ # as long as you are also using SSL (which you should anyway).
35+ import Phoenix.LiveDashboard.Router
36+37+ scope "/dev" do
38+ pipe_through :browser
39+40+ live_dashboard "/dashboard", metrics: StatusphereElixirWeb.Telemetry
41+ end
42+ end
43+end
···1+# Script for populating the database. You can run it as:
2+#
3+# mix run priv/repo/seeds.exs
4+#
5+# Inside the script, you can read and write to any of your
6+# repositories directly:
7+#
8+# StatusphereElixir.Repo.insert!(%StatusphereElixir.SomeSchema{})
9+#
10+# We recommend using the bang functions (`insert!`, `update!`
11+# and so on) as they will fail if something goes wrong.
···1+# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2+#
3+# To ban all spiders from the entire site uncomment the next two lines:
4+# User-agent: *
5+# Disallow: /
···1+defmodule StatusphereElixirWeb.ErrorJSONTest do
2+ use StatusphereElixirWeb.ConnCase, async: true
3+4+ test "renders 404" do
5+ assert StatusphereElixirWeb.ErrorJSON.render("404.json", %{}) == %{errors: %{detail: "Not Found"}}
6+ end
7+8+ test "renders 500" do
9+ assert StatusphereElixirWeb.ErrorJSON.render("500.json", %{}) ==
10+ %{errors: %{detail: "Internal Server Error"}}
11+ end
12+end
···1+defmodule StatusphereElixirWeb.PageControllerTest do
2+ use StatusphereElixirWeb.ConnCase
3+4+ test "GET /", %{conn: conn} do
5+ conn = get(conn, ~p"/")
6+ assert html_response(conn, 200) =~ "Peace of mind from prototype to production"
7+ end
8+end
+38
test/support/conn_case.ex
···00000000000000000000000000000000000000
···1+defmodule StatusphereElixirWeb.ConnCase do
2+ @moduledoc """
3+ This module defines the test case to be used by
4+ tests that require setting up a connection.
5+6+ Such tests rely on `Phoenix.ConnTest` and also
7+ import other functionality to make it easier
8+ to build common data structures and query the data layer.
9+10+ Finally, if the test case interacts with the database,
11+ we enable the SQL sandbox, so changes done to the database
12+ are reverted at the end of every test. If you are using
13+ PostgreSQL, you can even run database tests asynchronously
14+ by setting `use StatusphereElixirWeb.ConnCase, async: true`, although
15+ this option is not recommended for other databases.
16+ """
17+18+ use ExUnit.CaseTemplate
19+20+ using do
21+ quote do
22+ # The default endpoint for testing
23+ @endpoint StatusphereElixirWeb.Endpoint
24+25+ use StatusphereElixirWeb, :verified_routes
26+27+ # Import conveniences for testing with connections
28+ import Plug.Conn
29+ import Phoenix.ConnTest
30+ import StatusphereElixirWeb.ConnCase
31+ end
32+ end
33+34+ setup tags do
35+ StatusphereElixir.DataCase.setup_sandbox(tags)
36+ {:ok, conn: Phoenix.ConnTest.build_conn()}
37+ end
38+end
···1+defmodule StatusphereElixir.DataCase do
2+ @moduledoc """
3+ This module defines the setup for tests requiring
4+ access to the application's data layer.
5+6+ You may define functions here to be used as helpers in
7+ your tests.
8+9+ Finally, if the test case interacts with the database,
10+ we enable the SQL sandbox, so changes done to the database
11+ are reverted at the end of every test. If you are using
12+ PostgreSQL, you can even run database tests asynchronously
13+ by setting `use StatusphereElixir.DataCase, async: true`, although
14+ this option is not recommended for other databases.
15+ """
16+17+ use ExUnit.CaseTemplate
18+19+ using do
20+ quote do
21+ alias StatusphereElixir.Repo
22+23+ import Ecto
24+ import Ecto.Changeset
25+ import Ecto.Query
26+ import StatusphereElixir.DataCase
27+ end
28+ end
29+30+ setup tags do
31+ StatusphereElixir.DataCase.setup_sandbox(tags)
32+ :ok
33+ end
34+35+ @doc """
36+ Sets up the sandbox based on the test tags.
37+ """
38+ def setup_sandbox(tags) do
39+ pid = Ecto.Adapters.SQL.Sandbox.start_owner!(StatusphereElixir.Repo, shared: not tags[:async])
40+ on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end)
41+ end
42+43+ @doc """
44+ A helper that transforms changeset errors into a map of messages.
45+46+ assert {:error, changeset} = Accounts.create_user(%{password: "short"})
47+ assert "password is too short" in errors_on(changeset).password
48+ assert %{password: ["password is too short"]} = errors_on(changeset)
49+50+ """
51+ def errors_on(changeset) do
52+ Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
53+ Regex.replace(~r"%{(\w+)}", message, fn _, key ->
54+ opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
55+ end)
56+ end)
57+ end
58+end