Compare commits

..

No commits in common. "339d075edda35110f5aa24beb334542383e68af7" and "118922be50fb8895ccfb3a4a390589e14dc33411" have entirely different histories.

19 changed files with 100 additions and 288 deletions

1
.envrc
View file

@ -1 +0,0 @@
export PASSWORD_HASH='$argon2id$v=19$m=65536,t=3,p=4$ctxMPSfgu5i28J0bjRl2yg$D7+qs+R7caAe5lw5m7s+k9M0t75R4XBhkwG1dv6MGOQ'

View file

@ -1,6 +1,6 @@
[ [
import_deps: [:ecto, :ecto_sql, :phoenix], import_deps: [:ecto, :ecto_sql, :phoenix],
subdirectories: ["priv/*/migrations"], subdirectories: ["priv/*/migrations"],
plugins: [Styler, Phoenix.LiveView.HTMLFormatter], plugins: [Phoenix.LiveView.HTMLFormatter],
inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}", "priv/*/seeds.exs"] inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}", "priv/*/seeds.exs"]
] ]

View file

@ -7,6 +7,11 @@
# General application configuration # General application configuration
import Config import Config
config :cms,
namespace: CMS,
ecto_repos: [CMS.Repo],
generators: [timestamp_type: :utc_datetime, binary_id: true]
# Configures the endpoint # Configures the endpoint
config :cms, CMSWeb.Endpoint, config :cms, CMSWeb.Endpoint,
url: [host: "localhost"], url: [host: "localhost"],
@ -18,28 +23,16 @@ config :cms, CMSWeb.Endpoint,
pubsub_server: CMS.PubSub, pubsub_server: CMS.PubSub,
live_view: [signing_salt: "afQxdsCJ"] live_view: [signing_salt: "afQxdsCJ"]
config :cms,
namespace: CMS,
ecto_repos: [CMS.Repo],
generators: [timestamp_type: :utc_datetime, binary_id: true]
# Configure esbuild (the version is required) # Configure esbuild (the version is required)
config :esbuild, config :esbuild,
version: "0.17.11", version: "0.17.11",
cms: [ cms: [
args: ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*), args:
~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*),
cd: Path.expand("../assets", __DIR__), cd: Path.expand("../assets", __DIR__),
env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)} env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
] ]
# Configures Elixir's Logger
config :logger, :console,
format: "$time $metadata[$level] $message\n",
metadata: [:request_id]
# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason
# Configure tailwind (the version is required) # Configure tailwind (the version is required)
config :tailwind, config :tailwind,
version: "3.4.3", version: "3.4.3",
@ -49,10 +42,17 @@ config :tailwind,
--input=css/app.css --input=css/app.css
--output=../priv/static/assets/app.css --output=../priv/static/assets/app.css
), ),
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
cd: Path.expand("../assets", __DIR__) cd: Path.expand("../assets", __DIR__)
] ]
# Configures Elixir's Logger
config :logger, :console,
format: "$time $metadata[$level] $message\n",
metadata: [:request_id]
# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{config_env()}.exs" import_config "#{config_env()}.exs"

View file

@ -1,13 +1,6 @@
import Config import Config
# Configure your database # Configure your database
# For development, we disable any cache and enable
# debugging and code reloading.
#
# The watchers configuration can be used to run external
# watchers to your application. For example, we can use it
# to bundle .js and .css sources.
# Binding to loopback ipv4 address prevents access from other machines.
config :cms, CMS.Repo, config :cms, CMS.Repo,
username: "postgres", username: "postgres",
password: "postgres", password: "postgres",
@ -17,6 +10,13 @@ config :cms, CMS.Repo,
show_sensitive_data_on_connection_error: true, show_sensitive_data_on_connection_error: true,
pool_size: 10 pool_size: 10
# For development, we disable any cache and enable
# debugging and code reloading.
#
# The watchers configuration can be used to run external
# watchers to your application. For example, we can use it
# to bundle .js and .css sources.
# Binding to loopback ipv4 address prevents access from other machines.
config :cms, CMSWeb.Endpoint, config :cms, CMSWeb.Endpoint,
# Change to `ip: {0, 0, 0, 0}` to allow 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: [ip: {127, 0, 0, 1}, port: 4000],
@ -29,6 +29,29 @@ config :cms, CMSWeb.Endpoint,
tailwind: {Tailwind, :install_and_run, [:cms, ~w(--watch)]} tailwind: {Tailwind, :install_and_run, [:cms, ~w(--watch)]}
] ]
# ## SSL Support
#
# In order to use HTTPS in development, a self-signed
# certificate can be generated by running the following
# Mix task:
#
# mix phx.gen.cert
#
# Run `mix help phx.gen.cert` for more information.
#
# The `http:` config above can be replaced with:
#
# https: [
# port: 4001,
# cipher_suite: :strong,
# keyfile: "priv/cert/selfsigned_key.pem",
# certfile: "priv/cert/selfsigned.pem"
# ],
#
# If desired, both `http:` and `https:` keys can be
# configured to run both http and https servers on
# different ports.
# Watch static and templates for browser reloading. # Watch static and templates for browser reloading.
config :cms, CMSWeb.Endpoint, config :cms, CMSWeb.Endpoint,
live_reload: [ live_reload: [
@ -38,45 +61,21 @@ config :cms, CMSWeb.Endpoint,
] ]
] ]
# ## SSL Support
#
# Enable dev routes for dashboard and mailbox # Enable dev routes for dashboard and mailbox
# In order to use HTTPS in development, a self-signed
# certificate can be generated by running the following
# Mix task:
#
config :cms, dev_routes: true config :cms, dev_routes: true
# Do not include metadata nor timestamps in development logs # Do not include metadata nor timestamps in development logs
# mix phx.gen.cert
#
# Run `mix help phx.gen.cert` for more information.
#
config :logger, :console, format: "[$level] $message\n" config :logger, :console, format: "[$level] $message\n"
# Initialize plugs at runtime for faster development compilation
# The `http:` config above can be replaced with:
#
# https: [
config :phoenix, :plug_init_mode, :runtime
# Set a higher stacktrace during development. Avoid configuring such # Set a higher stacktrace during development. Avoid configuring such
# port: 4001,
# in production as building large stacktraces may be expensive. # in production as building large stacktraces may be expensive.
# cipher_suite: :strong,
# keyfile: "priv/cert/selfsigned_key.pem",
# certfile: "priv/cert/selfsigned.pem"
config :phoenix, :stacktrace_depth, 20 config :phoenix, :stacktrace_depth, 20
# Initialize plugs at runtime for faster development compilation
config :phoenix, :plug_init_mode, :runtime
config :phoenix_live_view, config :phoenix_live_view,
# Include HEEx debug annotations as HTML comments in rendered markup # Include HEEx debug annotations as HTML comments in rendered markup
# ],
#
debug_heex_annotations: true, debug_heex_annotations: true,
# Enable helpful, but potentially expensive runtime checks # Enable helpful, but potentially expensive runtime checks
# If desired, both `http:` and `https:` keys can be
# configured to run both http and https servers on
# different ports.
enable_expensive_runtime_checks: true enable_expensive_runtime_checks: true

View file

@ -20,15 +20,6 @@ if System.get_env("PHX_SERVER") do
config :cms, CMSWeb.Endpoint, server: true config :cms, CMSWeb.Endpoint, server: true
end end
config :cms,
password_hash:
System.get_env("PASSWORD_HASH") ||
raise("""
environment variable PASSWORD_HASH is missing.
Generate a hashed password using `mix cms.gen.password_hash`
""")
if config_env() == :prod do if config_env() == :prod do
database_url = database_url =
System.get_env("DATABASE_URL") || System.get_env("DATABASE_URL") ||
@ -39,6 +30,12 @@ if config_env() == :prod do
maybe_ipv6 = if System.get_env("ECTO_IPV6") in ~w(true 1), do: [:inet6], else: [] maybe_ipv6 = if System.get_env("ECTO_IPV6") in ~w(true 1), do: [:inet6], else: []
config :cms, CMS.Repo,
# ssl: true,
url: database_url,
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
socket_options: maybe_ipv6
# The secret key base is used to sign/encrypt cookies and other secrets. # 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 # 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 # want to use a different value for prod and you most likely don't want
@ -54,11 +51,7 @@ if config_env() == :prod do
host = System.get_env("PHX_HOST") || "example.com" host = System.get_env("PHX_HOST") || "example.com"
port = String.to_integer(System.get_env("PORT") || "4000") port = String.to_integer(System.get_env("PORT") || "4000")
config :cms, CMS.Repo, config :cms, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")
# ssl: true,
url: database_url,
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
socket_options: maybe_ipv6
config :cms, CMSWeb.Endpoint, config :cms, CMSWeb.Endpoint,
url: [host: host, port: 443, scheme: "https"], url: [host: host, port: 443, scheme: "https"],
@ -72,8 +65,6 @@ if config_env() == :prod do
], ],
secret_key_base: secret_key_base secret_key_base: secret_key_base
config :cms, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")
# ## SSL Support # ## SSL Support
# #
# To get SSL working, you will need to add the `https` key # To get SSL working, you will need to add the `https` key

View file

@ -23,11 +23,10 @@ defmodule CMSWeb do
quote do quote do
use Phoenix.Router, helpers: false use Phoenix.Router, helpers: false
import Phoenix.Controller
import Phoenix.LiveView.Router
# Import common connection and controller functions to use in pipelines # Import common connection and controller functions to use in pipelines
import Plug.Conn import Plug.Conn
import Phoenix.Controller
import Phoenix.LiveView.Router
end end
end end
@ -81,10 +80,10 @@ defmodule CMSWeb do
defp html_helpers do defp html_helpers do
quote do quote do
import CMSWeb.CoreComponents
# HTML escaping functionality # HTML escaping functionality
import Phoenix.HTML import Phoenix.HTML
# Core UI components # Core UI components
import CMSWeb.CoreComponents
# Shortcut for generating JS commands # Shortcut for generating JS commands
alias Phoenix.LiveView.JS alias Phoenix.LiveView.JS

View file

@ -1,30 +1,14 @@
<div :if={@admin?} class="flex flex-row justify-between py-1 px-3 mb-2 border-b border-slate-100"> <div class="flex flex-col md:flex-row mx-auto max-w-3xl">
<section class="flex flex-row gap-x-2">
<div class="pr-2 border-r border-slate-100">
<.link navigate={~p"/admin"} class="font-bold">admin mode</.link>
</div>
<nav>
<ul class="flex flex-row">
<.link href="#" class="hover:underline">new post</.link>
</ul>
</nav>
</section>
<section class="flex flex-row">
<.link href={~p"/admin/session"} method="delete" class="hover:underline">sign out</.link>
</section>
</div>
<div class="flex flex-col md:flex-row mx-auto max-w-4xl">
<section class="flex flex-col p-2 gap-y-1 border-slate-100 border-b md:border-b-0"> <section class="flex flex-col p-2 gap-y-1 border-slate-100 border-b md:border-b-0">
<.link href={~p"/"} class="font-bold hover:underline">sloanelybutsurely.com</.link> <.link navigate={~p"/"} class="font-bold hover:underline">sloanelybutsurely.com</.link>
<nav> <nav>
<ul> <ul>
<li><.link href={~p"/writing"} class="hover:underline">writing</.link></li> <li><.link navigate={~p"/writing"} class="hover:underline">writing</.link></li>
<li><.link href={~p"/microblog"} class="hover:underline">microblog</.link></li> <li><.link navigate={~p"/microblog"} class="hover:underline">microblog</.link></li>
</ul> </ul>
</nav> </nav>
</section> </section>
<main class="p-2"> <main>
{@inner_content} {@inner_content}
</main> </main>
</div> </div>

View file

@ -1,40 +0,0 @@
defmodule CMSWeb.AdminAuth do
@moduledoc false
use CMSWeb, :verified_routes
import Phoenix.Controller
import Plug.Conn
def log_in_admin(conn) do
conn
|> renew_session()
|> put_session(:admin?, true)
|> redirect(to: ~p"/")
end
def log_out_admin(conn) do
if live_socket_id = get_session(conn, :live_socket_id) do
CMSWeb.Endpoint.broadcast(live_socket_id, "disconnect", %{})
end
conn
|> renew_session()
|> redirect(to: ~p"/")
end
def correct_password?(password) do
password_hash = Application.fetch_env!(:cms, :password_hash)
Argon2.verify_pass(password, password_hash)
end
## private
defp renew_session(conn) do
delete_csrf_token()
conn
|> configure_session(renew: true)
|> clear_session()
end
end

View file

@ -1,18 +0,0 @@
defmodule CMSWeb.AdminMode do
@moduledoc false
use CMSWeb, :live_view
def admin_mode(%Plug.Conn{} = conn, _opts) do
Plug.Conn.assign(conn, :admin?, admin?(conn))
end
def on_mount(:default, _params, session, socket) do
{:cont, assign(socket, :admin?, admin?(session))}
end
defp admin?(%Plug.Conn{} = conn) do
Plug.Conn.get_session(conn, :admin?, false) == true
end
defp admin?(%{} = session), do: Map.get(session, :admin?, false) == true
end

View file

@ -1,23 +0,0 @@
defmodule CMSWeb.AdminSessionController do
use CMSWeb, :controller
alias CMSWeb.AdminAuth
def create(conn, %{"password" => password}) do
if AdminAuth.correct_password?(password) do
AdminAuth.log_in_admin(conn)
else
redirect(conn, to: ~p"/admin/sign-in")
end
end
def create(conn, _params) do
redirect(conn, to: ~p"/admin/sign-in")
end
def destroy(conn, _params) do
conn
|> AdminAuth.log_out_admin()
|> redirect(to: ~p"/")
end
end

View file

@ -1,16 +0,0 @@
defmodule CMSWeb.AdminLive do
@moduledoc false
use CMSWeb, :live_view
@impl true
def mount(_params, _session, socket) do
{:ok, socket}
end
@impl true
def render(assigns) do
~H"""
<h1>AdminLive</h1>
"""
end
end

View file

@ -1,29 +0,0 @@
defmodule CMSWeb.AdminLoginLive do
@moduledoc false
use CMSWeb, :live_view
@impl true
def mount(_params, _session, socket) do
socket = assign(socket, :form, to_form(%{"password" => ""}))
{:ok, socket}
end
@impl true
def render(assigns) do
~H"""
<h1 class="font-bold text-lg mb-4">Sign in</h1>
<.form for={@form} action={~p"/admin/session"}>
<input
type="password"
placeholder="password"
id={@form[:password].id}
name={@form[:password].name}
value={@form[:password].value}
required
/>
</.form>
"""
end
end

View file

@ -1,10 +1,6 @@
defmodule CMSWeb.Router do defmodule CMSWeb.Router do
use CMSWeb, :router use CMSWeb, :router
import CMSWeb.AdminMode
alias CMSWeb.AdminMode
pipeline :browser do pipeline :browser do
plug :accepts, ["html"] plug :accepts, ["html"]
plug :fetch_session plug :fetch_session
@ -12,26 +8,25 @@ defmodule CMSWeb.Router do
plug :put_root_layout, html: {CMSWeb.Layouts, :root} plug :put_root_layout, html: {CMSWeb.Layouts, :root}
plug :protect_from_forgery plug :protect_from_forgery
plug :put_secure_browser_headers plug :put_secure_browser_headers
plug :admin_mode
end end
live_session :default, on_mount: AdminMode do pipeline :api do
plug :accepts, ["json"]
end
scope "/", CMSWeb do scope "/", CMSWeb do
pipe_through :browser pipe_through :browser
get "/", PageController, :home get "/", PageController, :home
get "/writing", PageController, :writing get "/writing", PageController, :writing
get "/microblog", PageController, :microblog get "/microblog", PageController, :microblog
live "/admin", AdminLive
live "/admin/sign-in", AdminLoginLive
post "/admin/session", AdminSessionController, :create
delete "/admin/session", AdminSessionController, :destroy
end
end end
# Other scopes may use custom stacks.
# scope "/api", CMSWeb do
# pipe_through :api
# end
# Enable LiveDashboard in development # Enable LiveDashboard in development
if Application.compile_env(:cms, :dev_routes) do if Application.compile_env(:cms, :dev_routes) do
# If you want to use the LiveDashboard in production, you should put # If you want to use the LiveDashboard in production, you should put

View file

@ -1,7 +1,5 @@
defmodule CMSWeb.Telemetry do defmodule CMSWeb.Telemetry do
@moduledoc false
use Supervisor use Supervisor
import Telemetry.Metrics import Telemetry.Metrics
def start_link(arg) do def start_link(arg) do
@ -72,7 +70,8 @@ defmodule CMSWeb.Telemetry do
), ),
summary("cms.repo.query.idle_time", summary("cms.repo.query.idle_time",
unit: {:native, :millisecond}, unit: {:native, :millisecond},
description: "The time the connection spent waiting before being checked out for the query" description:
"The time the connection spent waiting before being checked out for the query"
), ),
# VM Metrics # VM Metrics

View file

@ -1,23 +0,0 @@
defmodule Mix.Tasks.Cms.Gen.PasswordHash do
@shortdoc @moduledoc
@moduledoc """
Hashes a password for the admin account
"""
use Mix.Task
@impl Mix.Task
def run(_args) do
password = Mix.shell().prompt("Password: ")
password = String.trim_trailing(password)
password_confirmation = Mix.shell().prompt("Confirm password: ")
password_confirmation = String.trim_trailing(password_confirmation)
if password == password_confirmation do
hashed = Argon2.hash_pwd_salt(password)
Mix.shell().info(hashed)
else
Mix.shell().error("Passwords do not match")
end
end
end

13
mix.exs
View file

@ -44,16 +44,17 @@ defmodule CMS.MixProject do
{:esbuild, "~> 0.8", runtime: Mix.env() == :dev}, {:esbuild, "~> 0.8", runtime: Mix.env() == :dev},
{:tailwind, "~> 0.2", runtime: Mix.env() == :dev}, {:tailwind, "~> 0.2", runtime: Mix.env() == :dev},
{:heroicons, {:heroicons,
github: "tailwindlabs/heroicons", tag: "v2.1.1", sparse: "optimized", app: false, compile: false, depth: 1}, github: "tailwindlabs/heroicons",
tag: "v2.1.1",
sparse: "optimized",
app: false,
compile: false,
depth: 1},
{:telemetry_metrics, "~> 1.0"}, {:telemetry_metrics, "~> 1.0"},
{:telemetry_poller, "~> 1.0"}, {:telemetry_poller, "~> 1.0"},
{:jason, "~> 1.2"}, {:jason, "~> 1.2"},
{:dns_cluster, "~> 0.1.1"}, {:dns_cluster, "~> 0.1.1"},
{:bandit, "~> 1.5"}, {:bandit, "~> 1.5"}
{:argon2_elixir, "~> 4.1"},
# dev/test only
{:styler, "~> 1.4", only: [:dev, :test], runtime: false}
] ]
end end

View file

@ -1,14 +1,11 @@
%{ %{
"argon2_elixir": {:hex, :argon2_elixir, "4.1.2", "1160a3ccd59b951175525882240651f5ed3303b75c616204713f8b31c76b37bd", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "9222341e1b0d9aa5ca7e26a1c77bd1bd92d2314c92b57ca3e2c7ed847223b51d"},
"bandit": {:hex, :bandit, "1.6.7", "42f30e37a1c89a2a12943c5dca76f731a2313e8a2e21c1a95dc8241893e922d1", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "551ba8ff5e4fc908cbeb8c9f0697775fb6813a96d9de5f7fe02e34e76fd7d184"}, "bandit": {:hex, :bandit, "1.6.7", "42f30e37a1c89a2a12943c5dca76f731a2313e8a2e21c1a95dc8241893e922d1", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "551ba8ff5e4fc908cbeb8c9f0697775fb6813a96d9de5f7fe02e34e76fd7d184"},
"castore": {:hex, :castore, "1.0.11", "4bbd584741601eb658007339ea730b082cc61f3554cf2e8f39bf693a11b49073", [:mix], [], "hexpm", "e03990b4db988df56262852f20de0f659871c35154691427a5047f4967a16a62"}, "castore": {:hex, :castore, "1.0.11", "4bbd584741601eb658007339ea730b082cc61f3554cf2e8f39bf693a11b49073", [:mix], [], "hexpm", "e03990b4db988df56262852f20de0f659871c35154691427a5047f4967a16a62"},
"comeonin": {:hex, :comeonin, "5.5.1", "5113e5f3800799787de08a6e0db307133850e635d34e9fab23c70b6501669510", [:mix], [], "hexpm", "65aac8f19938145377cee73973f192c5645873dcf550a8a6b18187d17c13ccdb"},
"db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"},
"decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"},
"dns_cluster": {:hex, :dns_cluster, "0.1.3", "0bc20a2c88ed6cc494f2964075c359f8c2d00e1bf25518a6a6c7fd277c9b0c66", [:mix], [], "hexpm", "46cb7c4a1b3e52c7ad4cbe33ca5079fbde4840dedeafca2baf77996c2da1bc33"}, "dns_cluster": {:hex, :dns_cluster, "0.1.3", "0bc20a2c88ed6cc494f2964075c359f8c2d00e1bf25518a6a6c7fd277c9b0c66", [:mix], [], "hexpm", "46cb7c4a1b3e52c7ad4cbe33ca5079fbde4840dedeafca2baf77996c2da1bc33"},
"ecto": {:hex, :ecto, "3.12.5", "4a312960ce612e17337e7cefcf9be45b95a3be6b36b6f94dfb3d8c361d631866", [:mix], [{:decimal, "~> 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", "6eb18e80bef8bb57e17f5a7f068a1719fbda384d40fc37acb8eb8aeca493b6ea"}, "ecto": {:hex, :ecto, "3.12.5", "4a312960ce612e17337e7cefcf9be45b95a3be6b36b6f94dfb3d8c361d631866", [:mix], [{:decimal, "~> 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", "6eb18e80bef8bb57e17f5a7f068a1719fbda384d40fc37acb8eb8aeca493b6ea"},
"ecto_sql": {:hex, :ecto_sql, "3.12.1", "c0d0d60e85d9ff4631f12bafa454bc392ce8b9ec83531a412c12a0d415a3a4d0", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 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", "aff5b958a899762c5f09028c847569f7dfb9cc9d63bdb8133bff8a5546de6bf5"}, "ecto_sql": {:hex, :ecto_sql, "3.12.1", "c0d0d60e85d9ff4631f12bafa454bc392ce8b9ec83531a412c12a0d415a3a4d0", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 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", "aff5b958a899762c5f09028c847569f7dfb9cc9d63bdb8133bff8a5546de6bf5"},
"elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"},
"esbuild": {:hex, :esbuild, "0.9.0", "f043eeaca4932ca8e16e5429aebd90f7766f31ac160a25cbd9befe84f2bc068f", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "b415027f71d5ab57ef2be844b2a10d0c1b5a492d431727f43937adce22ba45ae"}, "esbuild": {:hex, :esbuild, "0.9.0", "f043eeaca4932ca8e16e5429aebd90f7766f31ac160a25cbd9befe84f2bc068f", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "b415027f71d5ab57ef2be844b2a10d0c1b5a492d431727f43937adce22ba45ae"},
"file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"},
"floki": {:hex, :floki, "0.37.0", "b83e0280bbc6372f2a403b2848013650b16640cd2470aea6701f0632223d719e", [:mix], [], "hexpm", "516a0c15a69f78c47dc8e0b9b3724b29608aa6619379f91b1ffa47109b5d0dd3"}, "floki": {:hex, :floki, "0.37.0", "b83e0280bbc6372f2a403b2848013650b16640cd2470aea6701f0632223d719e", [:mix], [], "hexpm", "516a0c15a69f78c47dc8e0b9b3724b29608aa6619379f91b1ffa47109b5d0dd3"},
@ -27,7 +24,6 @@
"plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"}, "plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"},
"plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"},
"postgrex": {:hex, :postgrex, "0.20.0", "363ed03ab4757f6bc47942eff7720640795eb557e1935951c1626f0d303a3aed", [:mix], [{: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", "d36ef8b36f323d29505314f704e21a1a038e2dc387c6409ee0cd24144e187c0f"}, "postgrex": {:hex, :postgrex, "0.20.0", "363ed03ab4757f6bc47942eff7720640795eb557e1935951c1626f0d303a3aed", [:mix], [{: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", "d36ef8b36f323d29505314f704e21a1a038e2dc387c6409ee0cd24144e187c0f"},
"styler": {:hex, :styler, "1.4.0", "5944723d08afe4d38210b674d7e97dd1137a75968a85a633983cc308e86dc5f2", [:mix], [], "hexpm", "07de0e89c27490c8e469bb814d77ddaaa3283d7d8038501021d80a7705cf13e9"},
"tailwind": {:hex, :tailwind, "0.2.4", "5706ec47182d4e7045901302bf3a333e80f3d1af65c442ba9a9eed152fb26c2e", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "c6e4a82b8727bab593700c998a4d98cf3d8025678bfde059aed71d0000c3e463"}, "tailwind": {:hex, :tailwind, "0.2.4", "5706ec47182d4e7045901302bf3a333e80f3d1af65c442ba9a9eed152fb26c2e", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "c6e4a82b8727bab593700c998a4d98cf3d8025678bfde059aed71d0000c3e463"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
"telemetry_metrics": {:hex, :telemetry_metrics, "1.1.0", "5bd5f3b5637e0abea0426b947e3ce5dd304f8b3bc6617039e2b5a008adc02f8f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7b79e8ddfde70adb6db8a6623d1778ec66401f366e9a8f5dd0955c56bc8ce67"}, "telemetry_metrics": {:hex, :telemetry_metrics, "1.1.0", "5bd5f3b5637e0abea0426b947e3ce5dd304f8b3bc6617039e2b5a008adc02f8f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7b79e8ddfde70adb6db8a6623d1778ec66401f366e9a8f5dd0955c56bc8ce67"},

View file

@ -19,15 +19,15 @@ defmodule CMSWeb.ConnCase do
using do using do
quote do quote do
use CMSWeb, :verified_routes
import CMSWeb.ConnCase
import Phoenix.ConnTest
import Plug.Conn
# The default endpoint for testing # The default endpoint for testing
@endpoint CMSWeb.Endpoint @endpoint CMSWeb.Endpoint
use CMSWeb, :verified_routes
# Import conveniences for testing with connections # Import conveniences for testing with connections
import Plug.Conn
import Phoenix.ConnTest
import CMSWeb.ConnCase
end end
end end

View file

@ -16,16 +16,14 @@ defmodule CMS.DataCase do
use ExUnit.CaseTemplate use ExUnit.CaseTemplate
alias Ecto.Adapters.SQL.Sandbox
using do using do
quote do quote do
import CMS.DataCase alias CMS.Repo
import Ecto import Ecto
import Ecto.Changeset import Ecto.Changeset
import Ecto.Query import Ecto.Query
import CMS.DataCase
alias CMS.Repo
end end
end end
@ -38,8 +36,8 @@ defmodule CMS.DataCase do
Sets up the sandbox based on the test tags. Sets up the sandbox based on the test tags.
""" """
def setup_sandbox(tags) do def setup_sandbox(tags) do
pid = Sandbox.start_owner!(CMS.Repo, shared: not tags[:async]) pid = Ecto.Adapters.SQL.Sandbox.start_owner!(CMS.Repo, shared: not tags[:async])
on_exit(fn -> Sandbox.stop_owner(pid) end) on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end)
end end
@doc """ @doc """