chore: reorganize project, enforce boundaries
This commit is contained in:
parent
41ad5aa924
commit
fae90f44da
69 changed files with 369 additions and 638 deletions
assets
config
lib
cms.ex
mix.exsmix.lockcms
cms_web
core.excore
mix/tasks
schema.exschema
sloanely_but_surely.exsloanely_but_surely
web.exweb
components
controllers
admin_auth.exadmin_session_controller.exerror_html.exerror_json.exglobals.expage_controller.expage_html.ex
endpoint.expage_html
post_controller.expost_html.expost_html
status_controller.exstatus_html.exstatus_html
live
router.expriv/repo
test
|
@ -8,13 +8,13 @@ const path = require("path")
|
|||
module.exports = {
|
||||
content: [
|
||||
"./js/**/*.js",
|
||||
"../lib/cms_web.ex",
|
||||
"../lib/cms_web/**/*.*ex"
|
||||
"../lib/web.ex",
|
||||
"../lib/web/**/*.*ex"
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
typography: ({theme}) => ({
|
||||
cms: {
|
||||
typography: ({ theme }) => ({
|
||||
sloanely_but_surely: {
|
||||
css: {
|
||||
'--tw-prose-body': theme('colors.black'),
|
||||
'--tw-prose-headings': theme('colors.black'),
|
||||
|
@ -61,14 +61,14 @@ module.exports = {
|
|||
//
|
||||
// <div class="phx-click-loading:animate-ping">
|
||||
//
|
||||
plugin(({addVariant}) => addVariant("phx-click-loading", [".phx-click-loading&", ".phx-click-loading &"])),
|
||||
plugin(({addVariant}) => addVariant("phx-submit-loading", [".phx-submit-loading&", ".phx-submit-loading &"])),
|
||||
plugin(({addVariant}) => addVariant("phx-change-loading", [".phx-change-loading&", ".phx-change-loading &"])),
|
||||
plugin(({ addVariant }) => addVariant("phx-click-loading", [".phx-click-loading&", ".phx-click-loading &"])),
|
||||
plugin(({ addVariant }) => addVariant("phx-submit-loading", [".phx-submit-loading&", ".phx-submit-loading &"])),
|
||||
plugin(({ addVariant }) => addVariant("phx-change-loading", [".phx-change-loading&", ".phx-change-loading &"])),
|
||||
|
||||
// Embeds Heroicons (https://heroicons.com) into your app.css bundle
|
||||
// See your `CoreComponents.icon/1` for more information.
|
||||
//
|
||||
plugin(function({matchComponents, theme}) {
|
||||
plugin(function({ matchComponents, theme }) {
|
||||
let iconsDir = path.join(__dirname, "../deps/heroicons/optimized")
|
||||
let values = {}
|
||||
let icons = [
|
||||
|
@ -80,11 +80,11 @@ module.exports = {
|
|||
icons.forEach(([suffix, dir]) => {
|
||||
fs.readdirSync(path.join(iconsDir, dir)).forEach(file => {
|
||||
let name = path.basename(file, ".svg") + suffix
|
||||
values[name] = {name, fullPath: path.join(iconsDir, dir, file)}
|
||||
values[name] = { name, fullPath: path.join(iconsDir, dir, file) }
|
||||
})
|
||||
})
|
||||
matchComponents({
|
||||
"hero": ({name, fullPath}) => {
|
||||
"hero": ({ name, fullPath }) => {
|
||||
let content = fs.readFileSync(fullPath).toString().replace(/\r?\n|\r/g, "")
|
||||
let size = theme("spacing.6")
|
||||
if (name.endsWith("-mini")) {
|
||||
|
@ -104,7 +104,7 @@ module.exports = {
|
|||
"height": size
|
||||
}
|
||||
}
|
||||
}, {values})
|
||||
}, { values })
|
||||
})
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,57 +1,42 @@
|
|||
# This file is responsible for configuring your application
|
||||
# and its dependencies with the aid of the Config module.
|
||||
#
|
||||
# This configuration file is loaded before any dependency and
|
||||
# is restricted to this project.
|
||||
|
||||
# General application configuration
|
||||
import Config
|
||||
|
||||
# Configures the endpoint
|
||||
config :cms, CMSWeb.Endpoint,
|
||||
url: [host: "localhost"],
|
||||
adapter: Bandit.PhoenixAdapter,
|
||||
render_errors: [
|
||||
formats: [html: CMSWeb.ErrorHTML, json: CMSWeb.ErrorJSON],
|
||||
layout: false
|
||||
],
|
||||
pubsub_server: CMS.PubSub,
|
||||
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)
|
||||
config :esbuild,
|
||||
version: "0.17.11",
|
||||
cms: [
|
||||
sloanely_but_surely: [
|
||||
args: ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*),
|
||||
cd: Path.expand("../assets", __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)
|
||||
config :sloanely_but_surely, Web.Endpoint,
|
||||
url: [host: "localhost"],
|
||||
adapter: Bandit.PhoenixAdapter,
|
||||
render_errors: [
|
||||
formats: [html: Web.ErrorHTML, json: Web.ErrorJSON],
|
||||
layout: false
|
||||
],
|
||||
pubsub_server: Core.PubSub,
|
||||
live_view: [signing_salt: "afQxdsCJ"]
|
||||
|
||||
config :sloanely_but_surely,
|
||||
namespace: Core,
|
||||
ecto_repos: [Core.Repo],
|
||||
generators: [timestamp_type: :utc_datetime, binary_id: true]
|
||||
|
||||
config :tailwind,
|
||||
version: "3.4.3",
|
||||
cms: [
|
||||
sloanely_but_surely: [
|
||||
args: ~w(
|
||||
--config=tailwind.config.js
|
||||
--input=css/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__)
|
||||
]
|
||||
|
||||
|
|
|
@ -1,82 +1,39 @@
|
|||
import Config
|
||||
|
||||
# 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 :logger, :console, format: "[$level] $message\n"
|
||||
|
||||
config :phoenix,
|
||||
plug_init_mode: :runtime,
|
||||
stacktrace_depth: 20
|
||||
|
||||
config :phoenix_live_view,
|
||||
debug_heex_annotations: true,
|
||||
enable_expensive_runtime_checks: true
|
||||
|
||||
config :sloanely_but_surely, Core.Repo,
|
||||
username: "postgres",
|
||||
password: "postgres",
|
||||
hostname: "localhost",
|
||||
database: "cms_dev",
|
||||
database: "sloanely_but_surely_dev",
|
||||
stacktrace: true,
|
||||
show_sensitive_data_on_connection_error: true,
|
||||
pool_size: 10
|
||||
|
||||
config :cms, CMSWeb.Endpoint,
|
||||
# Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
|
||||
config :sloanely_but_surely, Web.Endpoint,
|
||||
http: [ip: {127, 0, 0, 1}, port: 4000],
|
||||
check_origin: false,
|
||||
code_reloader: true,
|
||||
debug_errors: true,
|
||||
secret_key_base: "OiPAYORPwZPHThILCLsbcM9fqJNWoTphDydEEXsrKaILQm1lz8lt0DMiu9cCoVqC",
|
||||
watchers: [
|
||||
esbuild: {Esbuild, :install_and_run, [:cms, ~w(--sourcemap=inline --watch)]},
|
||||
tailwind: {Tailwind, :install_and_run, [:cms, ~w(--watch)]}
|
||||
]
|
||||
|
||||
# Watch static and templates for browser reloading.
|
||||
config :cms, CMSWeb.Endpoint,
|
||||
esbuild: {Esbuild, :install_and_run, [:sloanely_but_surely, ~w(--sourcemap=inline --watch)]},
|
||||
tailwind: {Tailwind, :install_and_run, [:sloanely_but_surely, ~w(--watch)]}
|
||||
],
|
||||
live_reload: [
|
||||
patterns: [
|
||||
~r"priv/static/(?!uploads/).*(js|css|png|jpeg|jpg|gif|svg)$",
|
||||
~r"lib/cms_web/(controllers|live|components)/.*(ex|heex)$"
|
||||
~r"lib/web/(controllers|live|components)/.*(ex|heex)$"
|
||||
]
|
||||
]
|
||||
|
||||
# ## SSL Support
|
||||
#
|
||||
|
||||
# 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
|
||||
|
||||
# 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"
|
||||
|
||||
# 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
|
||||
# port: 4001,
|
||||
# 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_live_view,
|
||||
# Include HEEx debug annotations as HTML comments in rendered markup
|
||||
# ],
|
||||
#
|
||||
debug_heex_annotations: true,
|
||||
# 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
|
||||
config :sloanely_but_surely, dev_routes: true
|
||||
|
|
|
@ -1,14 +1,5 @@
|
|||
import Config
|
||||
|
||||
# Note we also include the path to a cache manifest
|
||||
# containing the digested version of static files. This
|
||||
# manifest is generated by the `mix assets.deploy` task,
|
||||
# which you should run after static files are built and
|
||||
# before starting your production server.
|
||||
config :cms, CMSWeb.Endpoint, cache_static_manifest: "priv/static/cache_manifest.json"
|
||||
|
||||
# Do not print debug messages in production
|
||||
config :logger, level: :info
|
||||
|
||||
# Runtime production configuration, including reading
|
||||
# of environment variables, is done on config/runtime.exs.
|
||||
config :sloanely_but_surely, Web.Endpoint, cache_static_manifest: "priv/static/cache_manifest.json"
|
||||
|
|
|
@ -1,26 +1,10 @@
|
|||
import Config
|
||||
|
||||
# config/runtime.exs is executed for all environments, including
|
||||
# during releases. It is executed after compilation and before the
|
||||
# system starts, so it is typically used to load production configuration
|
||||
# and secrets from environment variables or elsewhere. Do not define
|
||||
# any compile-time configuration in here, as it won't be applied.
|
||||
# The block below contains prod specific runtime configuration.
|
||||
|
||||
# ## Using releases
|
||||
#
|
||||
# If you use `mix release`, you need to explicitly enable the server
|
||||
# by passing the PHX_SERVER=true when you start it:
|
||||
#
|
||||
# PHX_SERVER=true bin/cms start
|
||||
#
|
||||
# Alternatively, you can use `mix phx.gen.release` to generate a `bin/server`
|
||||
# script that automatically sets the env var above.
|
||||
if System.get_env("PHX_SERVER") do
|
||||
config :cms, CMSWeb.Endpoint, server: true
|
||||
config :sloanely_but_surely, Web.Endpoint, server: true
|
||||
end
|
||||
|
||||
config :cms,
|
||||
config :sloanely_but_surely,
|
||||
password_hash:
|
||||
System.get_env("PASSWORD_HASH") ||
|
||||
raise("""
|
||||
|
@ -39,11 +23,6 @@ if config_env() == :prod do
|
|||
|
||||
maybe_ipv6 = if System.get_env("ECTO_IPV6") in ~w(true 1), do: [:inet6], else: []
|
||||
|
||||
# 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
|
||||
# want to use a different value for prod and you most likely don't want
|
||||
# to check this value into version control, so we use an environment
|
||||
# variable instead.
|
||||
secret_key_base =
|
||||
System.get_env("SECRET_KEY_BASE") ||
|
||||
raise """
|
||||
|
@ -54,55 +33,17 @@ if config_env() == :prod do
|
|||
host = System.get_env("PHX_HOST") || "example.com"
|
||||
port = String.to_integer(System.get_env("PORT") || "4000")
|
||||
|
||||
config :cms, CMS.Repo,
|
||||
config :sloanely_but_surely, Core.Repo,
|
||||
# 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 :sloanely_but_surely, Web.Endpoint,
|
||||
url: [host: host, port: 443, scheme: "https"],
|
||||
http: [
|
||||
# Enable IPv6 and bind on all interfaces.
|
||||
# Set it to {0, 0, 0, 0, 0, 0, 0, 1} for local network only access.
|
||||
# See the documentation on https://hexdocs.pm/bandit/Bandit.html#t:options/0
|
||||
# for details about using IPv6 vs IPv4 and loopback vs public addresses.
|
||||
ip: {0, 0, 0, 0, 0, 0, 0, 0},
|
||||
port: port
|
||||
],
|
||||
secret_key_base: secret_key_base
|
||||
|
||||
config :cms, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")
|
||||
|
||||
# ## SSL Support
|
||||
#
|
||||
# To get SSL working, you will need to add the `https` key
|
||||
# to your endpoint configuration:
|
||||
#
|
||||
# config :cms, CMSWeb.Endpoint,
|
||||
# https: [
|
||||
# ...,
|
||||
# port: 443,
|
||||
# cipher_suite: :strong,
|
||||
# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
|
||||
# certfile: System.get_env("SOME_APP_SSL_CERT_PATH")
|
||||
# ]
|
||||
#
|
||||
# The `cipher_suite` is set to `:strong` to support only the
|
||||
# latest and more secure SSL ciphers. This means old browsers
|
||||
# and clients may not be supported. You can set it to
|
||||
# `:compatible` for wider support.
|
||||
#
|
||||
# `:keyfile` and `:certfile` expect an absolute path to the key
|
||||
# and cert in disk or a relative path inside priv, for example
|
||||
# "priv/ssl/server.key". For all supported SSL configuration
|
||||
# options, see https://hexdocs.pm/plug/Plug.SSL.html#configure/1
|
||||
#
|
||||
# We also recommend setting `force_ssl` in your config/prod.exs,
|
||||
# ensuring no data is ever sent via http, always redirecting to https:
|
||||
#
|
||||
# config :cms, CMSWeb.Endpoint,
|
||||
# force_ssl: [hsts: true]
|
||||
#
|
||||
# Check `Plug.SSL` for all available options in `force_ssl`.
|
||||
end
|
||||
|
|
|
@ -1,31 +1,21 @@
|
|||
import Config
|
||||
|
||||
# Configure your database
|
||||
#
|
||||
# The MIX_TEST_PARTITION environment variable can be used
|
||||
# to provide built-in test partitioning in CI environment.
|
||||
# Run `mix help test` for more information.
|
||||
config :cms, CMS.Repo,
|
||||
config :logger, level: :warning
|
||||
|
||||
config :phoenix, :plug_init_mode, :runtime
|
||||
|
||||
config :phoenix_live_view,
|
||||
enable_expensive_runtime_checks: true
|
||||
|
||||
config :sloanely_but_surely, Core.Repo,
|
||||
username: "postgres",
|
||||
password: "postgres",
|
||||
hostname: "localhost",
|
||||
database: "cms_test#{System.get_env("MIX_TEST_PARTITION")}",
|
||||
database: "sloanely_but_surely_test#{System.get_env("MIX_TEST_PARTITION")}",
|
||||
pool: Ecto.Adapters.SQL.Sandbox,
|
||||
pool_size: System.schedulers_online() * 2
|
||||
|
||||
# We don't run a server during test. If one is required,
|
||||
# you can enable the server option below.
|
||||
config :cms, CMSWeb.Endpoint,
|
||||
config :sloanely_but_surely, Web.Endpoint,
|
||||
http: [ip: {127, 0, 0, 1}, port: 4002],
|
||||
secret_key_base: "IAv/74HChZvRYUunjUjCoj/b8NA6mZtVbcxv6ECoOJ+Xr+CeNBVGZ7zkDhUlXSq4",
|
||||
server: false
|
||||
|
||||
# Print only warnings and errors during test
|
||||
config :logger, level: :warning
|
||||
|
||||
# Initialize plugs at runtime for faster test compilation
|
||||
config :phoenix, :plug_init_mode, :runtime
|
||||
|
||||
# Enable helpful, but potentially expensive runtime checks
|
||||
config :phoenix_live_view,
|
||||
enable_expensive_runtime_checks: true
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
defmodule CMS do
|
||||
@moduledoc """
|
||||
CMS 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
|
|
@ -1,34 +0,0 @@
|
|||
defmodule CMS.Application do
|
||||
# See https://hexdocs.pm/elixir/Application.html
|
||||
# for more information on OTP Applications
|
||||
@moduledoc false
|
||||
|
||||
use Application
|
||||
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
children = [
|
||||
CMSWeb.Telemetry,
|
||||
CMS.Repo,
|
||||
{DNSCluster, query: Application.get_env(:cms, :dns_cluster_query) || :ignore},
|
||||
{Phoenix.PubSub, name: CMS.PubSub},
|
||||
# Start a worker by calling: CMS.Worker.start_link(arg)
|
||||
# {CMS.Worker, arg},
|
||||
# Start to serve requests, typically the last entry
|
||||
CMSWeb.Endpoint
|
||||
]
|
||||
|
||||
# See https://hexdocs.pm/elixir/Supervisor.html
|
||||
# for other strategies and supported options
|
||||
opts = [strategy: :one_for_one, name: CMS.Supervisor]
|
||||
Supervisor.start_link(children, opts)
|
||||
end
|
||||
|
||||
# Tell Phoenix to update the endpoint configuration
|
||||
# whenever the application is updated.
|
||||
@impl true
|
||||
def config_change(changed, _new, removed) do
|
||||
CMSWeb.Endpoint.config_change(changed, removed)
|
||||
:ok
|
||||
end
|
||||
end
|
|
@ -1,31 +0,0 @@
|
|||
defmodule CMS.Posts do
|
||||
@moduledoc false
|
||||
import Ecto.Query
|
||||
|
||||
alias CMS.Posts.Post
|
||||
alias CMS.Repo
|
||||
|
||||
def create_post(attrs) do
|
||||
%Post{}
|
||||
|> Post.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
def update_post(post, attrs) do
|
||||
post
|
||||
|> Post.changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def get_post!(id) do
|
||||
Repo.get!(Post, id)
|
||||
end
|
||||
|
||||
def list_posts do
|
||||
query =
|
||||
from post in Post,
|
||||
order_by: [desc: post.inserted_at]
|
||||
|
||||
Repo.all(query)
|
||||
end
|
||||
end
|
|
@ -1,20 +0,0 @@
|
|||
defmodule CMS.Posts.Post do
|
||||
@moduledoc false
|
||||
use Ecto.Schema
|
||||
|
||||
import Ecto.Changeset, warn: false
|
||||
|
||||
@primary_key {:id, :binary_id, autogenerate: true}
|
||||
schema "posts" do
|
||||
field :title, :string
|
||||
field :body, :string
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
def changeset(%__MODULE__{} = post, attrs \\ %{}) do
|
||||
post
|
||||
|> cast(attrs, [:title, :body])
|
||||
|> validate_required([:body])
|
||||
end
|
||||
end
|
|
@ -1,5 +0,0 @@
|
|||
defmodule CMS.Repo do
|
||||
use Ecto.Repo,
|
||||
otp_app: :cms,
|
||||
adapter: Ecto.Adapters.Postgres
|
||||
end
|
|
@ -1,31 +0,0 @@
|
|||
defmodule CMS.Statuses do
|
||||
@moduledoc false
|
||||
import Ecto.Query
|
||||
|
||||
alias CMS.Repo
|
||||
alias CMS.Statuses.Status
|
||||
|
||||
def create_status(attrs) do
|
||||
%Status{}
|
||||
|> Status.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
def update_status(status, attrs) do
|
||||
status
|
||||
|> Status.changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def get_status!(id) do
|
||||
Repo.get!(Status, id)
|
||||
end
|
||||
|
||||
def list_statuses do
|
||||
query =
|
||||
from status in Status,
|
||||
order_by: [desc: status.inserted_at]
|
||||
|
||||
Repo.all(query)
|
||||
end
|
||||
end
|
|
@ -1,19 +0,0 @@
|
|||
defmodule CMS.Statuses.Status do
|
||||
@moduledoc false
|
||||
use Ecto.Schema
|
||||
|
||||
import Ecto.Changeset
|
||||
|
||||
@primary_key {:id, :binary_id, autogenerate: true}
|
||||
schema "statuses" do
|
||||
field :body
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
def changeset(%__MODULE__{} = status, attrs \\ %{}) do
|
||||
status
|
||||
|> cast(attrs, [:body])
|
||||
|> validate_required([:body])
|
||||
end
|
||||
end
|
|
@ -1,16 +0,0 @@
|
|||
defmodule CMSWeb.PageController do
|
||||
use CMSWeb, :controller
|
||||
|
||||
alias CMS.Posts
|
||||
alias CMS.Statuses
|
||||
|
||||
def home(conn, _params) do
|
||||
posts = Enum.take(Posts.list_posts(), 5)
|
||||
statuses = Enum.take(Statuses.list_statuses(), 10)
|
||||
|
||||
conn
|
||||
|> assign(:posts, posts)
|
||||
|> assign(:statuses, statuses)
|
||||
|> render(:home)
|
||||
end
|
||||
end
|
|
@ -1,93 +0,0 @@
|
|||
defmodule CMSWeb.Telemetry do
|
||||
@moduledoc false
|
||||
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.start.system_time",
|
||||
unit: {:native, :millisecond}
|
||||
),
|
||||
summary("phoenix.endpoint.stop.duration",
|
||||
unit: {:native, :millisecond}
|
||||
),
|
||||
summary("phoenix.router_dispatch.start.system_time",
|
||||
tags: [:route],
|
||||
unit: {:native, :millisecond}
|
||||
),
|
||||
summary("phoenix.router_dispatch.exception.duration",
|
||||
tags: [:route],
|
||||
unit: {:native, :millisecond}
|
||||
),
|
||||
summary("phoenix.router_dispatch.stop.duration",
|
||||
tags: [:route],
|
||||
unit: {:native, :millisecond}
|
||||
),
|
||||
summary("phoenix.socket_connected.duration",
|
||||
unit: {:native, :millisecond}
|
||||
),
|
||||
summary("phoenix.channel_joined.duration",
|
||||
unit: {:native, :millisecond}
|
||||
),
|
||||
summary("phoenix.channel_handled_in.duration",
|
||||
tags: [:event],
|
||||
unit: {:native, :millisecond}
|
||||
),
|
||||
|
||||
# Database Metrics
|
||||
summary("cms.repo.query.total_time",
|
||||
unit: {:native, :millisecond},
|
||||
description: "The sum of the other measurements"
|
||||
),
|
||||
summary("cms.repo.query.decode_time",
|
||||
unit: {:native, :millisecond},
|
||||
description: "The time spent decoding the data received from the database"
|
||||
),
|
||||
summary("cms.repo.query.query_time",
|
||||
unit: {:native, :millisecond},
|
||||
description: "The time spent executing the query"
|
||||
),
|
||||
summary("cms.repo.query.queue_time",
|
||||
unit: {:native, :millisecond},
|
||||
description: "The time spent waiting for a database connection"
|
||||
),
|
||||
summary("cms.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.
|
||||
# {CMSWeb, :count_users, []}
|
||||
]
|
||||
end
|
||||
end
|
4
lib/core.ex
Normal file
4
lib/core.ex
Normal file
|
@ -0,0 +1,4 @@
|
|||
defmodule Core do
|
||||
@moduledoc false
|
||||
use Boundary, deps: [Schema], exports: [Author, Posts, Statuses]
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
defmodule CMS.Author do
|
||||
defmodule Core.Author do
|
||||
@moduledoc """
|
||||
Properties of the author, Sloane
|
||||
|
||||
|
@ -26,7 +26,7 @@ defmodule CMS.Author do
|
|||
given_name: "Sloane",
|
||||
additional_name: "Loretta",
|
||||
family_name: "Perrault",
|
||||
nickname: "sloanelybutsurely",
|
||||
nickname: "sloanely_but_surely",
|
||||
email: "sloane@fastmail.com",
|
||||
url: "https://sloanelybutsurely.com"
|
||||
}
|
37
lib/core/posts.ex
Normal file
37
lib/core/posts.ex
Normal file
|
@ -0,0 +1,37 @@
|
|||
defmodule Core.Posts do
|
||||
@moduledoc false
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
|
||||
alias Core.Repo
|
||||
|
||||
def changeset(%Schema.Post{} = post, attrs) do
|
||||
post
|
||||
|> cast(attrs, [:title, :body])
|
||||
|> validate_required([:body])
|
||||
end
|
||||
|
||||
def create_post(attrs) do
|
||||
%Schema.Post{}
|
||||
|> changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
def update_post(post, attrs) do
|
||||
post
|
||||
|> changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def get_post!(id) do
|
||||
Repo.get!(Schema.Post, id)
|
||||
end
|
||||
|
||||
def list_posts do
|
||||
query =
|
||||
from post in Schema.Post,
|
||||
order_by: [desc: post.inserted_at]
|
||||
|
||||
Repo.all(query)
|
||||
end
|
||||
end
|
5
lib/core/repo.ex
Normal file
5
lib/core/repo.ex
Normal file
|
@ -0,0 +1,5 @@
|
|||
defmodule Core.Repo do
|
||||
use Ecto.Repo,
|
||||
otp_app: :sloanely_but_surely,
|
||||
adapter: Ecto.Adapters.Postgres
|
||||
end
|
37
lib/core/statuses.ex
Normal file
37
lib/core/statuses.ex
Normal file
|
@ -0,0 +1,37 @@
|
|||
defmodule Core.Statuses do
|
||||
@moduledoc false
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
|
||||
alias Core.Repo
|
||||
|
||||
def changeset(%Schema.Status{} = status, attrs \\ %{}) do
|
||||
status
|
||||
|> cast(attrs, [:body])
|
||||
|> validate_required([:body])
|
||||
end
|
||||
|
||||
def create_status(attrs) do
|
||||
%Schema.Status{}
|
||||
|> changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
def update_status(status, attrs) do
|
||||
status
|
||||
|> changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def get_status!(id) do
|
||||
Repo.get!(Schema.Status, id)
|
||||
end
|
||||
|
||||
def list_statuses do
|
||||
query =
|
||||
from status in Schema.Status,
|
||||
order_by: [desc: status.inserted_at]
|
||||
|
||||
Repo.all(query)
|
||||
end
|
||||
end
|
|
@ -1,8 +1,9 @@
|
|||
defmodule Mix.Tasks.Cms.Gen.PasswordHash do
|
||||
defmodule Mix.Tasks.SloanelyButSurely.Gen.PasswordHash do
|
||||
@shortdoc @moduledoc
|
||||
@moduledoc """
|
||||
Hashes a password for the admin account
|
||||
"""
|
||||
use Boundary, classify_to: SloanelyButSurely.Mix
|
||||
use Mix.Task
|
||||
|
||||
@impl Mix.Task
|
4
lib/schema.ex
Normal file
4
lib/schema.ex
Normal file
|
@ -0,0 +1,4 @@
|
|||
defmodule Schema do
|
||||
@moduledoc false
|
||||
use Boundary, deps: [], exports: [Post, Status]
|
||||
end
|
12
lib/schema/post.ex
Normal file
12
lib/schema/post.ex
Normal file
|
@ -0,0 +1,12 @@
|
|||
defmodule Schema.Post do
|
||||
@moduledoc false
|
||||
use Ecto.Schema
|
||||
|
||||
@primary_key {:id, :binary_id, autogenerate: true}
|
||||
schema "posts" do
|
||||
field :title, :string
|
||||
field :body, :string
|
||||
|
||||
timestamps()
|
||||
end
|
||||
end
|
11
lib/schema/status.ex
Normal file
11
lib/schema/status.ex
Normal file
|
@ -0,0 +1,11 @@
|
|||
defmodule Schema.Status do
|
||||
@moduledoc false
|
||||
use Ecto.Schema
|
||||
|
||||
@primary_key {:id, :binary_id, autogenerate: true}
|
||||
schema "statuses" do
|
||||
field :body
|
||||
|
||||
timestamps()
|
||||
end
|
||||
end
|
4
lib/sloanely_but_surely.ex
Normal file
4
lib/sloanely_but_surely.ex
Normal file
|
@ -0,0 +1,4 @@
|
|||
defmodule SloanelyButSurely do
|
||||
@moduledoc false
|
||||
use Boundary, top_level?: true, deps: [Core, Web]
|
||||
end
|
21
lib/sloanely_but_surely/application.ex
Normal file
21
lib/sloanely_but_surely/application.ex
Normal file
|
@ -0,0 +1,21 @@
|
|||
defmodule SloanelyButSurely.Application do
|
||||
@moduledoc false
|
||||
use Application
|
||||
|
||||
@impl Application
|
||||
def start(_type, _args) do
|
||||
children = [
|
||||
Core.Repo,
|
||||
{Phoenix.PubSub, name: Core.PubSub},
|
||||
Web.Endpoint
|
||||
]
|
||||
|
||||
Supervisor.start_link(children, strategy: :one_for_one, name: Core.Supervisor)
|
||||
end
|
||||
|
||||
@impl Application
|
||||
def config_change(changed, _new, removed) do
|
||||
Web.Endpoint.config_change(changed, removed)
|
||||
:ok
|
||||
end
|
||||
end
|
4
lib/sloanely_but_surely/mix.ex
Normal file
4
lib/sloanely_but_surely/mix.ex
Normal file
|
@ -0,0 +1,4 @@
|
|||
defmodule SloanelyButSurely.Mix do
|
||||
@moduledoc false
|
||||
use Boundary, deps: [], exports: []
|
||||
end
|
|
@ -1,21 +1,6 @@
|
|||
defmodule CMSWeb do
|
||||
@moduledoc """
|
||||
The entrypoint for defining your web interface, such
|
||||
as controllers, components, channels, and so on.
|
||||
|
||||
This can be used in your application as:
|
||||
|
||||
use CMSWeb, :controller
|
||||
use CMSWeb, :html
|
||||
|
||||
The definitions below will be executed for every controller,
|
||||
component, etc, so keep them short and clean, focused
|
||||
on imports, uses and aliases.
|
||||
|
||||
Do NOT define functions inside the quoted expressions
|
||||
below. Instead, define additional modules and import
|
||||
those modules here.
|
||||
"""
|
||||
defmodule Web do
|
||||
@moduledoc false
|
||||
use Boundary, deps: [Core, Schema], exports: [Endpoint]
|
||||
|
||||
def static_paths, do: ~w(assets fonts images favicon.ico robots.txt)
|
||||
|
||||
|
@ -41,7 +26,7 @@ defmodule CMSWeb do
|
|||
quote do
|
||||
use Phoenix.Controller,
|
||||
formats: [:html, :json],
|
||||
layouts: [html: CMSWeb.Layouts]
|
||||
layouts: [html: Web.Layouts]
|
||||
|
||||
import Plug.Conn
|
||||
|
||||
|
@ -52,7 +37,7 @@ defmodule CMSWeb do
|
|||
def live_view do
|
||||
quote do
|
||||
use Phoenix.LiveView,
|
||||
layout: {CMSWeb.Layouts, :app}
|
||||
layout: {Web.Layouts, :app}
|
||||
|
||||
unquote(html_helpers())
|
||||
end
|
||||
|
@ -81,9 +66,9 @@ defmodule CMSWeb do
|
|||
|
||||
defp html_helpers do
|
||||
quote do
|
||||
import CMSWeb.CoreComponents
|
||||
# HTML escaping functionality
|
||||
import Phoenix.HTML
|
||||
import Web.CoreComponents
|
||||
# HTML escaping functionality
|
||||
# Core UI components
|
||||
|
||||
# Shortcut for generating JS commands
|
||||
|
@ -97,9 +82,9 @@ defmodule CMSWeb do
|
|||
def verified_routes do
|
||||
quote do
|
||||
use Phoenix.VerifiedRoutes,
|
||||
endpoint: CMSWeb.Endpoint,
|
||||
router: CMSWeb.Router,
|
||||
statics: CMSWeb.static_paths()
|
||||
endpoint: Web.Endpoint,
|
||||
router: Web.Router,
|
||||
statics: Web.static_paths()
|
||||
end
|
||||
end
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
defmodule CMSWeb.CoreComponents do
|
||||
defmodule Web.CoreComponents do
|
||||
@moduledoc """
|
||||
Provides core UI components.
|
||||
"""
|
|
@ -1,14 +1,13 @@
|
|||
defmodule CMSWeb.Layouts do
|
||||
defmodule Web.Layouts do
|
||||
@moduledoc """
|
||||
This module holds different layouts used by your application.
|
||||
|
||||
See the `layouts` directory for all templates available.
|
||||
The "root" layout is a skeleton rendered as part of the
|
||||
application router. The "app" layout is set as the default
|
||||
layout on both `use CMSWeb, :controller` and
|
||||
`use CMSWeb, :live_view`.
|
||||
layout on both `use Web, :controller` and `use Web, :live_view`.
|
||||
"""
|
||||
use CMSWeb, :html
|
||||
use Web, :html
|
||||
|
||||
embed_templates "layouts/*"
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
defmodule CMSWeb.AdminAuth do
|
||||
defmodule Web.AdminAuth do
|
||||
@moduledoc false
|
||||
use CMSWeb, :verified_routes
|
||||
use Web, :verified_routes
|
||||
|
||||
import Phoenix.Controller
|
||||
import Plug.Conn
|
||||
|
@ -14,7 +14,7 @@ defmodule CMSWeb.AdminAuth do
|
|||
|
||||
def log_out_admin(conn, params) do
|
||||
if live_socket_id = get_session(conn, :live_socket_id) do
|
||||
CMSWeb.Endpoint.broadcast(live_socket_id, "disconnect", %{})
|
||||
Web.Endpoint.broadcast(live_socket_id, "disconnect", %{})
|
||||
end
|
||||
|
||||
conn
|
||||
|
@ -35,7 +35,7 @@ defmodule CMSWeb.AdminAuth do
|
|||
end
|
||||
|
||||
def correct_password?(password) do
|
||||
password_hash = Application.fetch_env!(:cms, :password_hash)
|
||||
password_hash = Application.fetch_env!(:sloanely_but_surely, :password_hash)
|
||||
|
||||
Argon2.verify_pass(password, password_hash)
|
||||
end
|
|
@ -1,7 +1,7 @@
|
|||
defmodule CMSWeb.AdminSessionController do
|
||||
use CMSWeb, :controller
|
||||
defmodule Web.AdminSessionController do
|
||||
use Web, :controller
|
||||
|
||||
alias CMSWeb.AdminAuth
|
||||
alias Web.AdminAuth
|
||||
|
||||
def create(conn, %{"password" => password} = params) do
|
||||
if AdminAuth.correct_password?(password) do
|
|
@ -1,10 +1,10 @@
|
|||
defmodule CMSWeb.ErrorHTML do
|
||||
defmodule Web.ErrorHTML do
|
||||
@moduledoc """
|
||||
This module is invoked by your endpoint in case of errors on HTML requests.
|
||||
|
||||
See config/config.exs.
|
||||
"""
|
||||
use CMSWeb, :html
|
||||
use Web, :html
|
||||
|
||||
# If you want to customize your error pages,
|
||||
# uncomment the embed_templates/1 call below
|
|
@ -1,4 +1,4 @@
|
|||
defmodule CMSWeb.ErrorJSON do
|
||||
defmodule Web.ErrorJSON do
|
||||
@moduledoc """
|
||||
This module is invoked by your endpoint in case of errors on JSON requests.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
defmodule CMSWeb.Globals do
|
||||
defmodule Web.Globals do
|
||||
@moduledoc false
|
||||
use CMSWeb, :live_view
|
||||
use Web, :live_view
|
||||
|
||||
def assign_globals(%Plug.Conn{} = conn, _opts) do
|
||||
conn
|
13
lib/web/controllers/page_controller.ex
Normal file
13
lib/web/controllers/page_controller.ex
Normal file
|
@ -0,0 +1,13 @@
|
|||
defmodule Web.PageController do
|
||||
use Web, :controller
|
||||
|
||||
def home(conn, _params) do
|
||||
posts = Enum.take(Core.Posts.list_posts(), 5)
|
||||
statuses = Enum.take(Core.Statuses.list_statuses(), 10)
|
||||
|
||||
conn
|
||||
|> assign(:posts, posts)
|
||||
|> assign(:statuses, statuses)
|
||||
|> render(:home)
|
||||
end
|
||||
end
|
|
@ -1,10 +1,10 @@
|
|||
defmodule CMSWeb.PageHTML do
|
||||
defmodule Web.PageHTML do
|
||||
@moduledoc """
|
||||
This module contains pages rendered by PageController.
|
||||
|
||||
See the `page_html` directory for all templates available.
|
||||
"""
|
||||
use CMSWeb, :html
|
||||
use Web, :html
|
||||
|
||||
embed_templates "page_html/*"
|
||||
end
|
|
@ -1,10 +1,8 @@
|
|||
defmodule CMSWeb.PostController do
|
||||
use CMSWeb, :controller
|
||||
|
||||
alias CMS.Posts
|
||||
defmodule Web.PostController do
|
||||
use Web, :controller
|
||||
|
||||
def index(conn, _params) do
|
||||
posts = Posts.list_posts()
|
||||
posts = Core.Posts.list_posts()
|
||||
|
||||
conn
|
||||
|> assign(:posts, posts)
|
||||
|
@ -12,7 +10,7 @@ defmodule CMSWeb.PostController do
|
|||
end
|
||||
|
||||
def show(conn, %{"post_id" => post_id}) do
|
||||
post = Posts.get_post!(post_id)
|
||||
post = Core.Posts.get_post!(post_id)
|
||||
|
||||
conn
|
||||
|> assign(:post, post)
|
|
@ -1,6 +1,6 @@
|
|||
defmodule CMSWeb.PostHTML do
|
||||
defmodule Web.PostHTML do
|
||||
@moduledoc false
|
||||
use CMSWeb, :html
|
||||
use Web, :html
|
||||
|
||||
embed_templates "post_html/*"
|
||||
end
|
|
@ -1,10 +1,8 @@
|
|||
defmodule CMSWeb.StatusController do
|
||||
use CMSWeb, :controller
|
||||
|
||||
alias CMS.Statuses
|
||||
defmodule Web.StatusController do
|
||||
use Web, :controller
|
||||
|
||||
def index(conn, _params) do
|
||||
statuses = Statuses.list_statuses()
|
||||
statuses = Core.Statuses.list_statuses()
|
||||
|
||||
conn
|
||||
|> assign(:statuses, statuses)
|
||||
|
@ -12,7 +10,7 @@ defmodule CMSWeb.StatusController do
|
|||
end
|
||||
|
||||
def show(conn, %{"status_id" => status_id}) do
|
||||
status = Statuses.get_status!(status_id)
|
||||
status = Core.Statuses.get_status!(status_id)
|
||||
|
||||
conn
|
||||
|> assign(:status, status)
|
|
@ -1,6 +1,6 @@
|
|||
defmodule CMSWeb.StatusHTML do
|
||||
defmodule Web.StatusHTML do
|
||||
@moduledoc false
|
||||
use CMSWeb, :html
|
||||
use Web, :html
|
||||
|
||||
embed_templates "status_html/*"
|
||||
end
|
|
@ -1,5 +1,5 @@
|
|||
defmodule CMSWeb.Endpoint do
|
||||
use Phoenix.Endpoint, otp_app: :cms
|
||||
defmodule Web.Endpoint do
|
||||
use Phoenix.Endpoint, otp_app: :sloanely_but_surely
|
||||
|
||||
# The session will be stored in the cookie and signed,
|
||||
# this means its contents can be read but not tampered with.
|
||||
|
@ -21,9 +21,9 @@ defmodule CMSWeb.Endpoint do
|
|||
# when deploying your static files in production.
|
||||
plug Plug.Static,
|
||||
at: "/",
|
||||
from: :cms,
|
||||
from: :sloanely_but_surely,
|
||||
gzip: false,
|
||||
only: CMSWeb.static_paths()
|
||||
only: Web.static_paths()
|
||||
|
||||
# Code reloading can be explicitly enabled under the
|
||||
# :code_reloader configuration of your endpoint.
|
||||
|
@ -31,7 +31,7 @@ defmodule CMSWeb.Endpoint do
|
|||
socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
|
||||
plug Phoenix.LiveReloader
|
||||
plug Phoenix.CodeReloader
|
||||
plug Phoenix.Ecto.CheckRepoStatus, otp_app: :cms
|
||||
plug Phoenix.Ecto.CheckRepoStatus, otp_app: :sloanely_but_surely
|
||||
end
|
||||
|
||||
plug Phoenix.LiveDashboard.RequestLogger,
|
||||
|
@ -49,5 +49,5 @@ defmodule CMSWeb.Endpoint do
|
|||
plug Plug.MethodOverride
|
||||
plug Plug.Head
|
||||
plug Plug.Session, @session_options
|
||||
plug CMSWeb.Router
|
||||
plug Web.Router
|
||||
end
|
|
@ -1,6 +1,6 @@
|
|||
defmodule CMSWeb.AdminLive do
|
||||
defmodule Web.AdminLive do
|
||||
@moduledoc false
|
||||
use CMSWeb, :live_view
|
||||
use Web, :live_view
|
||||
|
||||
@impl true
|
||||
def mount(_params, _session, socket) do
|
|
@ -1,6 +1,6 @@
|
|||
defmodule CMSWeb.AdminLoginLive do
|
||||
defmodule Web.AdminLoginLive do
|
||||
@moduledoc false
|
||||
use CMSWeb, :live_view
|
||||
use Web, :live_view
|
||||
|
||||
@impl true
|
||||
def mount(params, _session, socket) do
|
|
@ -1,9 +1,6 @@
|
|||
defmodule CMSWeb.PostLive do
|
||||
defmodule Web.PostLive do
|
||||
@moduledoc false
|
||||
use CMSWeb, :live_view
|
||||
|
||||
alias CMS.Posts
|
||||
alias CMS.Posts.Post
|
||||
use Web, :live_view
|
||||
|
||||
@impl true
|
||||
def mount(_params, _session, socket) do
|
||||
|
@ -14,8 +11,8 @@ defmodule CMSWeb.PostLive do
|
|||
|
||||
@impl true
|
||||
def handle_params(_params, _uri, %{assigns: %{live_action: :new}} = socket) do
|
||||
post = %Post{}
|
||||
changeset = Post.changeset(post)
|
||||
post = %Schema.Post{}
|
||||
changeset = Core.Posts.changeset(post, %{})
|
||||
|
||||
socket = assign(socket, post: post, form: to_form(changeset))
|
||||
|
||||
|
@ -23,9 +20,9 @@ defmodule CMSWeb.PostLive do
|
|||
end
|
||||
|
||||
def handle_params(%{"post_id" => post_id}, _uri, %{assigns: %{live_action: :edit}} = socket) do
|
||||
post = Posts.get_post!(post_id)
|
||||
post = Core.Posts.get_post!(post_id)
|
||||
|
||||
changeset = Post.changeset(post)
|
||||
changeset = Core.Posts.changeset(post, %{})
|
||||
|
||||
socket = assign(socket, post: post, form: to_form(changeset))
|
||||
|
||||
|
@ -35,7 +32,7 @@ defmodule CMSWeb.PostLive do
|
|||
@impl true
|
||||
def handle_event("save_post", %{"post" => attrs}, %{assigns: %{live_action: :new}} = socket) do
|
||||
socket =
|
||||
case Posts.create_post(attrs) do
|
||||
case Core.Posts.create_post(attrs) do
|
||||
{:ok, post} -> push_navigate(socket, to: ~p"/admin/posts/#{post}")
|
||||
{:error, changeset} -> assign(socket, form: to_form(changeset))
|
||||
end
|
||||
|
@ -45,9 +42,15 @@ defmodule CMSWeb.PostLive do
|
|||
|
||||
def handle_event("save_post", %{"post" => attrs}, %{assigns: %{post: post, live_action: :edit}} = socket) do
|
||||
socket =
|
||||
case Posts.update_post(post, attrs) do
|
||||
case Core.Posts.update_post(post, attrs) do
|
||||
{:ok, post} ->
|
||||
assign(socket, post: post, form: post |> Post.changeset() |> to_form())
|
||||
assign(socket,
|
||||
post: post,
|
||||
form:
|
||||
post
|
||||
|> Core.Posts.changeset(%{})
|
||||
|> to_form()
|
||||
)
|
||||
|
||||
{:error, changeset} ->
|
||||
assign(socket, form: to_form(changeset))
|
|
@ -1,9 +1,6 @@
|
|||
defmodule CMSWeb.StatusLive do
|
||||
defmodule Web.StatusLive do
|
||||
@moduledoc false
|
||||
use CMSWeb, :live_view
|
||||
|
||||
alias CMS.Statuses
|
||||
alias CMS.Statuses.Status
|
||||
use Web, :live_view
|
||||
|
||||
@impl true
|
||||
def mount(_params, _session, socket) do
|
||||
|
@ -12,9 +9,9 @@ defmodule CMSWeb.StatusLive do
|
|||
|
||||
@impl true
|
||||
def handle_params(_params, _uri, %{assigns: %{live_action: :new}} = socket) do
|
||||
status = %Status{}
|
||||
status = %Schema.Status{}
|
||||
|
||||
changeset = Status.changeset(status)
|
||||
changeset = Core.Statuses.changeset(status, %{})
|
||||
|
||||
socket = assign(socket, status: status, form: to_form(changeset))
|
||||
|
||||
|
@ -22,9 +19,9 @@ defmodule CMSWeb.StatusLive do
|
|||
end
|
||||
|
||||
def handle_params(%{"status_id" => status_id}, _uri, %{assigns: %{live_action: :edit}} = socket) do
|
||||
status = Statuses.get_status!(status_id)
|
||||
status = Core.Statuses.get_status!(status_id)
|
||||
|
||||
changeset = Status.changeset(status)
|
||||
changeset = Core.Statuses.changeset(status, %{})
|
||||
|
||||
socket = assign(socket, status: status, form: to_form(changeset))
|
||||
|
||||
|
@ -34,7 +31,7 @@ defmodule CMSWeb.StatusLive do
|
|||
@impl true
|
||||
def handle_event("save_status", %{"status" => attrs}, %{assigns: %{live_action: :new}} = socket) do
|
||||
socket =
|
||||
case Statuses.create_status(attrs) do
|
||||
case Core.Statuses.create_status(attrs) do
|
||||
{:ok, status} -> push_navigate(socket, to: ~p"/admin/statuses/#{status}")
|
||||
{:error, changeset} -> assign(socket, form: to_form(changeset))
|
||||
end
|
||||
|
@ -42,15 +39,17 @@ defmodule CMSWeb.StatusLive do
|
|||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
"save_status",
|
||||
%{"status" => attrs},
|
||||
%{assigns: %{status: status, live_action: :edit}} = socket
|
||||
) do
|
||||
def handle_event("save_status", %{"status" => attrs}, %{assigns: %{status: status, live_action: :edit}} = socket) do
|
||||
socket =
|
||||
case Statuses.update_status(status, attrs) do
|
||||
case Core.Statuses.update_status(status, attrs) do
|
||||
{:ok, status} ->
|
||||
assign(socket, status: status, form: to_form(Status.changeset(status)))
|
||||
assign(socket,
|
||||
status: status,
|
||||
form:
|
||||
status
|
||||
|> Core.Statuses.changeset(%{})
|
||||
|> to_form()
|
||||
)
|
||||
|
||||
{:error, changeset} ->
|
||||
assign(socket, form: to_form(changeset))
|
|
@ -1,17 +1,17 @@
|
|||
defmodule CMSWeb.Router do
|
||||
use CMSWeb, :router
|
||||
defmodule Web.Router do
|
||||
use Web, :router
|
||||
|
||||
import CMSWeb.AdminAuth
|
||||
import CMSWeb.Globals
|
||||
import Web.AdminAuth
|
||||
import Web.Globals
|
||||
|
||||
alias CMSWeb.AdminAuth
|
||||
alias CMSWeb.Globals
|
||||
alias Web.AdminAuth
|
||||
alias Web.Globals
|
||||
|
||||
pipeline :browser do
|
||||
plug :accepts, ["html"]
|
||||
plug :fetch_session
|
||||
plug :fetch_live_flash
|
||||
plug :put_root_layout, html: {CMSWeb.Layouts, :root}
|
||||
plug :put_root_layout, html: {Web.Layouts, :root}
|
||||
plug :protect_from_forgery
|
||||
plug :put_secure_browser_headers
|
||||
plug :assign_globals
|
||||
|
@ -27,7 +27,7 @@ defmodule CMSWeb.Router do
|
|||
end
|
||||
|
||||
live_session :default, on_mount: [AdminAuth, Globals] do
|
||||
scope "/", CMSWeb do
|
||||
scope "/", Web do
|
||||
pipe_through :browser
|
||||
pipe_through :supports_admin_action
|
||||
|
||||
|
@ -44,7 +44,7 @@ defmodule CMSWeb.Router do
|
|||
get "/admin/session/destroy", AdminSessionController, :destroy
|
||||
end
|
||||
|
||||
scope "/admin", CMSWeb do
|
||||
scope "/admin", Web do
|
||||
pipe_through :browser
|
||||
pipe_through :requires_admin
|
||||
|
||||
|
@ -57,20 +57,4 @@ defmodule CMSWeb.Router do
|
|||
live "/statuses/:status_id", StatusLive, :edit
|
||||
end
|
||||
end
|
||||
|
||||
# Enable LiveDashboard in development
|
||||
if Application.compile_env(:cms, :dev_routes) do
|
||||
# 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).
|
||||
import Phoenix.LiveDashboard.Router
|
||||
|
||||
scope "/dev" do
|
||||
pipe_through :browser
|
||||
|
||||
live_dashboard "/dashboard", metrics: CMSWeb.Telemetry
|
||||
end
|
||||
end
|
||||
end
|
13
mix.exs
13
mix.exs
|
@ -1,12 +1,13 @@
|
|||
defmodule CMS.MixProject do
|
||||
defmodule SlaonelyButSurely.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :cms,
|
||||
version: "0.1.0",
|
||||
app: :sloanely_but_surely,
|
||||
version: "1.0.0",
|
||||
elixir: "~> 1.14",
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
compilers: [:boundary] ++ Mix.compilers(),
|
||||
start_permanent: Mix.env() == :prod,
|
||||
aliases: aliases(),
|
||||
deps: deps()
|
||||
|
@ -18,7 +19,7 @@ defmodule CMS.MixProject do
|
|||
# Type `mix help compile.app` for more information.
|
||||
def application do
|
||||
[
|
||||
mod: {CMS.Application, []},
|
||||
mod: {SloanelyButSurely.Application, []},
|
||||
extra_applications: [:logger, :runtime_tools]
|
||||
]
|
||||
end
|
||||
|
@ -48,14 +49,14 @@ defmodule CMS.MixProject do
|
|||
{:telemetry_metrics, "~> 1.0"},
|
||||
{:telemetry_poller, "~> 1.0"},
|
||||
{:jason, "~> 1.2"},
|
||||
{:dns_cluster, "~> 0.1.1"},
|
||||
{:bandit, "~> 1.5"},
|
||||
{:argon2_elixir, "~> 4.1"},
|
||||
{:timex, "~> 3.7"},
|
||||
{:typed_struct, "~> 0.3.0"},
|
||||
|
||||
# dev/test only
|
||||
{:styler, "~> 1.4", only: [:dev, :test], runtime: false}
|
||||
{:styler, "~> 1.4", only: [:dev, :test], runtime: false},
|
||||
{:boundary, "~> 0.10.4"}
|
||||
]
|
||||
end
|
||||
|
||||
|
|
1
mix.lock
1
mix.lock
|
@ -1,6 +1,7 @@
|
|||
%{
|
||||
"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"},
|
||||
"boundary": {:hex, :boundary, "0.10.4", "5fec5d2736c12f9bfe1720c3a2bd8c48c3547c24d6002ebf8e087570afd5bd2f", [:mix], [], "hexpm", "8baf6f23987afdb1483033ed0bde75c9c703613c22ed58d5f23bf948f203247c"},
|
||||
"castore": {:hex, :castore, "1.0.11", "4bbd584741601eb658007339ea730b082cc61f3554cf2e8f39bf693a11b49073", [:mix], [], "hexpm", "e03990b4db988df56262852f20de0f659871c35154691427a5047f4967a16a62"},
|
||||
"certifi": {:hex, :certifi, "2.14.0", "ed3bef654e69cde5e6c022df8070a579a79e8ba2368a00acf3d75b82d9aceeed", [:rebar3], [], "hexpm", "ea59d87ef89da429b8e905264fdec3419f84f2215bb3d81e07a18aac919026c3"},
|
||||
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
defmodule CMS.Repo.Migrations.AddPostsTable do
|
||||
defmodule Core.Repo.Migrations.AddPostsTable do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
defmodule CMS.Repo.Migrations.AddStatusesTable do
|
||||
defmodule Core.Repo.Migrations.AddStatusesTable do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# Inside the script, you can read and write to any of your
|
||||
# repositories directly:
|
||||
#
|
||||
# CMS.Repo.insert!(%CMS.SomeSchema{})
|
||||
# Core.Repo.insert!(%CMS.SomeSchema{})
|
||||
#
|
||||
# We recommend using the bang functions (`insert!`, `update!`
|
||||
# and so on) as they will fail if something goes wrong.
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
defmodule CMSWeb.ErrorHTMLTest do
|
||||
use CMSWeb.ConnCase, async: true
|
||||
|
||||
# Bring render_to_string/4 for testing custom views
|
||||
import Phoenix.Template
|
||||
|
||||
test "renders 404.html" do
|
||||
assert render_to_string(CMSWeb.ErrorHTML, "404", "html", []) == "Not Found"
|
||||
end
|
||||
|
||||
test "renders 500.html" do
|
||||
assert render_to_string(CMSWeb.ErrorHTML, "500", "html", []) == "Internal Server Error"
|
||||
end
|
||||
end
|
|
@ -1,12 +0,0 @@
|
|||
defmodule CMSWeb.ErrorJSONTest do
|
||||
use CMSWeb.ConnCase, async: true
|
||||
|
||||
test "renders 404" do
|
||||
assert CMSWeb.ErrorJSON.render("404.json", %{}) == %{errors: %{detail: "Not Found"}}
|
||||
end
|
||||
|
||||
test "renders 500" do
|
||||
assert CMSWeb.ErrorJSON.render("500.json", %{}) ==
|
||||
%{errors: %{detail: "Internal Server Error"}}
|
||||
end
|
||||
end
|
|
@ -1,8 +0,0 @@
|
|||
defmodule CMSWeb.PageControllerTest do
|
||||
use CMSWeb.ConnCase
|
||||
|
||||
test "GET /", %{conn: conn} do
|
||||
conn = get(conn, ~p"/")
|
||||
assert html_response(conn, 200) =~ "Peace of mind from prototype to production"
|
||||
end
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
defmodule CMSWeb.ConnCase do
|
||||
defmodule Test.ConnCase do
|
||||
@moduledoc """
|
||||
This module defines the test case to be used by
|
||||
tests that require setting up a connection.
|
||||
|
@ -11,7 +11,7 @@ defmodule CMSWeb.ConnCase do
|
|||
we enable the SQL sandbox, so changes done to the database
|
||||
are reverted at the end of every test. If you are using
|
||||
PostgreSQL, you can even run database tests asynchronously
|
||||
by setting `use CMSWeb.ConnCase, async: true`, although
|
||||
by setting `use Test.ConnCase, async: true`, although
|
||||
this option is not recommended for other databases.
|
||||
"""
|
||||
|
||||
|
@ -19,20 +19,21 @@ defmodule CMSWeb.ConnCase do
|
|||
|
||||
using do
|
||||
quote do
|
||||
use CMSWeb, :verified_routes
|
||||
use Web, :verified_routes
|
||||
|
||||
import CMSWeb.ConnCase
|
||||
import Phoenix.ConnTest
|
||||
import Plug.Conn
|
||||
import Test.ConnCase
|
||||
|
||||
# The default endpoint for testing
|
||||
@endpoint CMSWeb.Endpoint
|
||||
@endpoint Web.Endpoint
|
||||
|
||||
# Import conveniences for testing with connections
|
||||
end
|
||||
end
|
||||
|
||||
setup tags do
|
||||
CMS.DataCase.setup_sandbox(tags)
|
||||
Test.DataCase.setup_sandbox(tags)
|
||||
{:ok, conn: Phoenix.ConnTest.build_conn()}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
defmodule CMS.DataCase do
|
||||
defmodule Test.DataCase do
|
||||
@moduledoc """
|
||||
This module defines the setup for tests requiring
|
||||
access to the application's data layer.
|
||||
|
@ -10,7 +10,7 @@ defmodule CMS.DataCase do
|
|||
we enable the SQL sandbox, so changes done to the database
|
||||
are reverted at the end of every test. If you are using
|
||||
PostgreSQL, you can even run database tests asynchronously
|
||||
by setting `use CMS.DataCase, async: true`, although
|
||||
by setting `use Test.DataCase, async: true`, although
|
||||
this option is not recommended for other databases.
|
||||
"""
|
||||
|
||||
|
@ -20,17 +20,17 @@ defmodule CMS.DataCase do
|
|||
|
||||
using do
|
||||
quote do
|
||||
import CMS.DataCase
|
||||
import Ecto
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
import Test.DataCase
|
||||
|
||||
alias CMS.Repo
|
||||
alias Core.Repo
|
||||
end
|
||||
end
|
||||
|
||||
setup tags do
|
||||
CMS.DataCase.setup_sandbox(tags)
|
||||
Test.DataCase.setup_sandbox(tags)
|
||||
:ok
|
||||
end
|
||||
|
||||
|
@ -38,7 +38,7 @@ defmodule CMS.DataCase do
|
|||
Sets up the sandbox based on the test tags.
|
||||
"""
|
||||
def setup_sandbox(tags) do
|
||||
pid = Sandbox.start_owner!(CMS.Repo, shared: not tags[:async])
|
||||
pid = Sandbox.start_owner!(Core.Repo, shared: not tags[:async])
|
||||
on_exit(fn -> Sandbox.stop_owner(pid) end)
|
||||
end
|
||||
|
||||
|
|
3
test/support/test.ex
Normal file
3
test/support/test.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule Test do
|
||||
use Boundary, top_level?: true, check: [in: false, out: false]
|
||||
end
|
|
@ -1,2 +1,2 @@
|
|||
ExUnit.start()
|
||||
Ecto.Adapters.SQL.Sandbox.mode(CMS.Repo, :manual)
|
||||
Ecto.Adapters.SQL.Sandbox.mode(Core.Repo, :manual)
|
||||
|
|
14
test/web/controllers/error_html_test.exs
Normal file
14
test/web/controllers/error_html_test.exs
Normal file
|
@ -0,0 +1,14 @@
|
|||
defmodule Test.Web.ErrorHTMLTest do
|
||||
use Test.ConnCase, async: true
|
||||
|
||||
# Bring render_to_string/4 for testing custom views
|
||||
import Phoenix.Template
|
||||
|
||||
test "renders 404.html" do
|
||||
assert render_to_string(Web.ErrorHTML, "404", "html", []) == "Not Found"
|
||||
end
|
||||
|
||||
test "renders 500.html" do
|
||||
assert render_to_string(Web.ErrorHTML, "500", "html", []) == "Internal Server Error"
|
||||
end
|
||||
end
|
12
test/web/controllers/error_json_test.exs
Normal file
12
test/web/controllers/error_json_test.exs
Normal file
|
@ -0,0 +1,12 @@
|
|||
defmodule Test.Web.ErrorJSONTest do
|
||||
use Test.ConnCase, async: true
|
||||
|
||||
test "renders 404" do
|
||||
assert Web.ErrorJSON.render("404.json", %{}) == %{errors: %{detail: "Not Found"}}
|
||||
end
|
||||
|
||||
test "renders 500" do
|
||||
assert Web.ErrorJSON.render("500.json", %{}) ==
|
||||
%{errors: %{detail: "Internal Server Error"}}
|
||||
end
|
||||
end
|
8
test/web/controllers/page_controller_test.exs
Normal file
8
test/web/controllers/page_controller_test.exs
Normal file
|
@ -0,0 +1,8 @@
|
|||
defmodule Test.Web.PageControllerTest do
|
||||
use Test.ConnCase
|
||||
|
||||
test "GET /", %{conn: conn} do
|
||||
conn = get(conn, ~p"/")
|
||||
assert html_response(conn, 200) =~ "sloanelybutsurely.com"
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue