diff --git a/assets/tailwind.config.js b/assets/tailwind.config.js index 96c548a..a6be861 100644 --- a/assets/tailwind.config.js +++ b/assets/tailwind.config.js @@ -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 }) }) ] } diff --git a/config/config.exs b/config/config.exs index 260235d..ee5b709 100644 --- a/config/config.exs +++ b/config/config.exs @@ -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__) ] diff --git a/config/dev.exs b/config/dev.exs index 52e40ff..cccdef2 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -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 diff --git a/config/prod.exs b/config/prod.exs index eb88e2a..6041dfa 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -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" diff --git a/config/runtime.exs b/config/runtime.exs index 827c59b..59dae60 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -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 diff --git a/config/test.exs b/config/test.exs index 86322de..e447f24 100644 --- a/config/test.exs +++ b/config/test.exs @@ -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 diff --git a/lib/cms.ex b/lib/cms.ex deleted file mode 100644 index 5bfe4d8..0000000 --- a/lib/cms.ex +++ /dev/null @@ -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 diff --git a/lib/cms/application.ex b/lib/cms/application.ex deleted file mode 100644 index 6b9b5cc..0000000 --- a/lib/cms/application.ex +++ /dev/null @@ -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 diff --git a/lib/cms/posts.ex b/lib/cms/posts.ex deleted file mode 100644 index 5ebaa01..0000000 --- a/lib/cms/posts.ex +++ /dev/null @@ -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 diff --git a/lib/cms/posts/post.ex b/lib/cms/posts/post.ex deleted file mode 100644 index aaaea4f..0000000 --- a/lib/cms/posts/post.ex +++ /dev/null @@ -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 diff --git a/lib/cms/repo.ex b/lib/cms/repo.ex deleted file mode 100644 index edf96ef..0000000 --- a/lib/cms/repo.ex +++ /dev/null @@ -1,5 +0,0 @@ -defmodule CMS.Repo do - use Ecto.Repo, - otp_app: :cms, - adapter: Ecto.Adapters.Postgres -end diff --git a/lib/cms/statuses.ex b/lib/cms/statuses.ex deleted file mode 100644 index 7f817f9..0000000 --- a/lib/cms/statuses.ex +++ /dev/null @@ -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 diff --git a/lib/cms/statuses/status.ex b/lib/cms/statuses/status.ex deleted file mode 100644 index 00cd5f0..0000000 --- a/lib/cms/statuses/status.ex +++ /dev/null @@ -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 diff --git a/lib/cms_web/controllers/page_controller.ex b/lib/cms_web/controllers/page_controller.ex deleted file mode 100644 index ca7d635..0000000 --- a/lib/cms_web/controllers/page_controller.ex +++ /dev/null @@ -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 diff --git a/lib/cms_web/telemetry.ex b/lib/cms_web/telemetry.ex deleted file mode 100644 index ffc1ce9..0000000 --- a/lib/cms_web/telemetry.ex +++ /dev/null @@ -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 diff --git a/lib/core.ex b/lib/core.ex new file mode 100644 index 0000000..0ba92d0 --- /dev/null +++ b/lib/core.ex @@ -0,0 +1,4 @@ +defmodule Core do + @moduledoc false + use Boundary, deps: [Schema], exports: [Author, Posts, Statuses] +end diff --git a/lib/cms/author.ex b/lib/core/author.ex similarity index 93% rename from lib/cms/author.ex rename to lib/core/author.ex index 65e38a2..6ec9eef 100644 --- a/lib/cms/author.ex +++ b/lib/core/author.ex @@ -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" } diff --git a/lib/core/posts.ex b/lib/core/posts.ex new file mode 100644 index 0000000..e674b08 --- /dev/null +++ b/lib/core/posts.ex @@ -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 diff --git a/lib/core/repo.ex b/lib/core/repo.ex new file mode 100644 index 0000000..ae02811 --- /dev/null +++ b/lib/core/repo.ex @@ -0,0 +1,5 @@ +defmodule Core.Repo do + use Ecto.Repo, + otp_app: :sloanely_but_surely, + adapter: Ecto.Adapters.Postgres +end diff --git a/lib/core/statuses.ex b/lib/core/statuses.ex new file mode 100644 index 0000000..bf9827c --- /dev/null +++ b/lib/core/statuses.ex @@ -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 diff --git a/lib/mix/tasks/cms.gen.password_hash.ex b/lib/mix/tasks/sloanely_but_surely.gen.password_hash.ex similarity index 84% rename from lib/mix/tasks/cms.gen.password_hash.ex rename to lib/mix/tasks/sloanely_but_surely.gen.password_hash.ex index 8dfab3c..699ab8c 100644 --- a/lib/mix/tasks/cms.gen.password_hash.ex +++ b/lib/mix/tasks/sloanely_but_surely.gen.password_hash.ex @@ -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 diff --git a/lib/schema.ex b/lib/schema.ex new file mode 100644 index 0000000..a23bc07 --- /dev/null +++ b/lib/schema.ex @@ -0,0 +1,4 @@ +defmodule Schema do + @moduledoc false + use Boundary, deps: [], exports: [Post, Status] +end diff --git a/lib/schema/post.ex b/lib/schema/post.ex new file mode 100644 index 0000000..f61ca39 --- /dev/null +++ b/lib/schema/post.ex @@ -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 diff --git a/lib/schema/status.ex b/lib/schema/status.ex new file mode 100644 index 0000000..b7d2feb --- /dev/null +++ b/lib/schema/status.ex @@ -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 diff --git a/lib/sloanely_but_surely.ex b/lib/sloanely_but_surely.ex new file mode 100644 index 0000000..833a630 --- /dev/null +++ b/lib/sloanely_but_surely.ex @@ -0,0 +1,4 @@ +defmodule SloanelyButSurely do + @moduledoc false + use Boundary, top_level?: true, deps: [Core, Web] +end diff --git a/lib/sloanely_but_surely/application.ex b/lib/sloanely_but_surely/application.ex new file mode 100644 index 0000000..c21a035 --- /dev/null +++ b/lib/sloanely_but_surely/application.ex @@ -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 diff --git a/lib/sloanely_but_surely/mix.ex b/lib/sloanely_but_surely/mix.ex new file mode 100644 index 0000000..f34549e --- /dev/null +++ b/lib/sloanely_but_surely/mix.ex @@ -0,0 +1,4 @@ +defmodule SloanelyButSurely.Mix do + @moduledoc false + use Boundary, deps: [], exports: [] +end diff --git a/lib/cms_web.ex b/lib/web.ex similarity index 68% rename from lib/cms_web.ex rename to lib/web.ex index d00f5ad..9d2fdee 100644 --- a/lib/cms_web.ex +++ b/lib/web.ex @@ -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 diff --git a/lib/cms_web/components/core_components.ex b/lib/web/components/core_components.ex similarity index 99% rename from lib/cms_web/components/core_components.ex rename to lib/web/components/core_components.ex index 70f31e3..cfdd2a9 100644 --- a/lib/cms_web/components/core_components.ex +++ b/lib/web/components/core_components.ex @@ -1,4 +1,4 @@ -defmodule CMSWeb.CoreComponents do +defmodule Web.CoreComponents do @moduledoc """ Provides core UI components. """ diff --git a/lib/cms_web/components/layouts.ex b/lib/web/components/layouts.ex similarity index 78% rename from lib/cms_web/components/layouts.ex rename to lib/web/components/layouts.ex index 4ceac28..7ed0d51 100644 --- a/lib/cms_web/components/layouts.ex +++ b/lib/web/components/layouts.ex @@ -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/*" diff --git a/lib/cms_web/components/layouts/app.html.heex b/lib/web/components/layouts/app.html.heex similarity index 100% rename from lib/cms_web/components/layouts/app.html.heex rename to lib/web/components/layouts/app.html.heex diff --git a/lib/cms_web/components/layouts/root.html.heex b/lib/web/components/layouts/root.html.heex similarity index 100% rename from lib/cms_web/components/layouts/root.html.heex rename to lib/web/components/layouts/root.html.heex diff --git a/lib/cms_web/controllers/admin_auth.ex b/lib/web/controllers/admin_auth.ex similarity index 86% rename from lib/cms_web/controllers/admin_auth.ex rename to lib/web/controllers/admin_auth.ex index e671dc6..40da948 100644 --- a/lib/cms_web/controllers/admin_auth.ex +++ b/lib/web/controllers/admin_auth.ex @@ -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 diff --git a/lib/cms_web/controllers/admin_session_controller.ex b/lib/web/controllers/admin_session_controller.ex similarity index 79% rename from lib/cms_web/controllers/admin_session_controller.ex rename to lib/web/controllers/admin_session_controller.ex index a205f39..1b5e913 100644 --- a/lib/cms_web/controllers/admin_session_controller.ex +++ b/lib/web/controllers/admin_session_controller.ex @@ -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 diff --git a/lib/cms_web/controllers/error_html.ex b/lib/web/controllers/error_html.ex similarity index 92% rename from lib/cms_web/controllers/error_html.ex rename to lib/web/controllers/error_html.ex index d855081..2c93991 100644 --- a/lib/cms_web/controllers/error_html.ex +++ b/lib/web/controllers/error_html.ex @@ -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 diff --git a/lib/cms_web/controllers/error_json.ex b/lib/web/controllers/error_json.ex similarity index 95% rename from lib/cms_web/controllers/error_json.ex rename to lib/web/controllers/error_json.ex index 2aad2f8..d27986a 100644 --- a/lib/cms_web/controllers/error_json.ex +++ b/lib/web/controllers/error_json.ex @@ -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. diff --git a/lib/cms_web/controllers/globals.ex b/lib/web/controllers/globals.ex similarity index 91% rename from lib/cms_web/controllers/globals.ex rename to lib/web/controllers/globals.ex index b79931d..f45728a 100644 --- a/lib/cms_web/controllers/globals.ex +++ b/lib/web/controllers/globals.ex @@ -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 diff --git a/lib/web/controllers/page_controller.ex b/lib/web/controllers/page_controller.ex new file mode 100644 index 0000000..d35f827 --- /dev/null +++ b/lib/web/controllers/page_controller.ex @@ -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 diff --git a/lib/cms_web/controllers/page_html.ex b/lib/web/controllers/page_html.ex similarity index 78% rename from lib/cms_web/controllers/page_html.ex rename to lib/web/controllers/page_html.ex index f682d22..70589f4 100644 --- a/lib/cms_web/controllers/page_html.ex +++ b/lib/web/controllers/page_html.ex @@ -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 diff --git a/lib/cms_web/controllers/page_html/home.html.heex b/lib/web/controllers/page_html/home.html.heex similarity index 100% rename from lib/cms_web/controllers/page_html/home.html.heex rename to lib/web/controllers/page_html/home.html.heex diff --git a/lib/cms_web/controllers/post_controller.ex b/lib/web/controllers/post_controller.ex similarity index 59% rename from lib/cms_web/controllers/post_controller.ex rename to lib/web/controllers/post_controller.ex index 22b3b62..181261b 100644 --- a/lib/cms_web/controllers/post_controller.ex +++ b/lib/web/controllers/post_controller.ex @@ -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) diff --git a/lib/cms_web/controllers/post_html.ex b/lib/web/controllers/post_html.ex similarity index 53% rename from lib/cms_web/controllers/post_html.ex rename to lib/web/controllers/post_html.ex index f3f2082..c7cf197 100644 --- a/lib/cms_web/controllers/post_html.ex +++ b/lib/web/controllers/post_html.ex @@ -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 diff --git a/lib/cms_web/controllers/post_html/index.html.heex b/lib/web/controllers/post_html/index.html.heex similarity index 100% rename from lib/cms_web/controllers/post_html/index.html.heex rename to lib/web/controllers/post_html/index.html.heex diff --git a/lib/cms_web/controllers/post_html/show.html.heex b/lib/web/controllers/post_html/show.html.heex similarity index 100% rename from lib/cms_web/controllers/post_html/show.html.heex rename to lib/web/controllers/post_html/show.html.heex diff --git a/lib/cms_web/controllers/status_controller.ex b/lib/web/controllers/status_controller.ex similarity index 57% rename from lib/cms_web/controllers/status_controller.ex rename to lib/web/controllers/status_controller.ex index 6bf211e..7c8798e 100644 --- a/lib/cms_web/controllers/status_controller.ex +++ b/lib/web/controllers/status_controller.ex @@ -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) diff --git a/lib/cms_web/controllers/status_html.ex b/lib/web/controllers/status_html.ex similarity index 53% rename from lib/cms_web/controllers/status_html.ex rename to lib/web/controllers/status_html.ex index e92f149..e99f601 100644 --- a/lib/cms_web/controllers/status_html.ex +++ b/lib/web/controllers/status_html.ex @@ -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 diff --git a/lib/cms_web/controllers/status_html/index.html.heex b/lib/web/controllers/status_html/index.html.heex similarity index 100% rename from lib/cms_web/controllers/status_html/index.html.heex rename to lib/web/controllers/status_html/index.html.heex diff --git a/lib/cms_web/controllers/status_html/show.html.heex b/lib/web/controllers/status_html/show.html.heex similarity index 100% rename from lib/cms_web/controllers/status_html/show.html.heex rename to lib/web/controllers/status_html/show.html.heex diff --git a/lib/cms_web/endpoint.ex b/lib/web/endpoint.ex similarity index 85% rename from lib/cms_web/endpoint.ex rename to lib/web/endpoint.ex index ea98ace..80608e7 100644 --- a/lib/cms_web/endpoint.ex +++ b/lib/web/endpoint.ex @@ -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 diff --git a/lib/cms_web/live/admin_live.ex b/lib/web/live/admin_live.ex similarity index 77% rename from lib/cms_web/live/admin_live.ex rename to lib/web/live/admin_live.ex index 8ae7075..3f6bc13 100644 --- a/lib/cms_web/live/admin_live.ex +++ b/lib/web/live/admin_live.ex @@ -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 diff --git a/lib/cms_web/live/admin_login_live.ex b/lib/web/live/admin_login_live.ex similarity index 94% rename from lib/cms_web/live/admin_login_live.ex rename to lib/web/live/admin_login_live.ex index 9af7136..1f94232 100644 --- a/lib/cms_web/live/admin_login_live.ex +++ b/lib/web/live/admin_login_live.ex @@ -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 diff --git a/lib/cms_web/live/post_live.ex b/lib/web/live/post_live.ex similarity index 77% rename from lib/cms_web/live/post_live.ex rename to lib/web/live/post_live.ex index d47e53a..dcb7bef 100644 --- a/lib/cms_web/live/post_live.ex +++ b/lib/web/live/post_live.ex @@ -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)) diff --git a/lib/cms_web/live/status_live.ex b/lib/web/live/status_live.ex similarity index 66% rename from lib/cms_web/live/status_live.ex rename to lib/web/live/status_live.ex index 563021e..bbfc028 100644 --- a/lib/cms_web/live/status_live.ex +++ b/lib/web/live/status_live.ex @@ -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)) diff --git a/lib/cms_web/router.ex b/lib/web/router.ex similarity index 58% rename from lib/cms_web/router.ex rename to lib/web/router.ex index 4d87d96..319c7db 100644 --- a/lib/cms_web/router.ex +++ b/lib/web/router.ex @@ -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 diff --git a/mix.exs b/mix.exs index 1a94067..d068189 100644 --- a/mix.exs +++ b/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 diff --git a/mix.lock b/mix.lock index c80adeb..fce0b88 100644 --- a/mix.lock +++ b/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"}, diff --git a/priv/repo/migrations/20250222164951_add_posts_table.exs b/priv/repo/migrations/20250222164951_add_posts_table.exs index e424163..2ab8d7b 100644 --- a/priv/repo/migrations/20250222164951_add_posts_table.exs +++ b/priv/repo/migrations/20250222164951_add_posts_table.exs @@ -1,4 +1,4 @@ -defmodule CMS.Repo.Migrations.AddPostsTable do +defmodule Core.Repo.Migrations.AddPostsTable do use Ecto.Migration def change do diff --git a/priv/repo/migrations/20250222201807_add_statuses_table.exs b/priv/repo/migrations/20250222201807_add_statuses_table.exs index e57fab2..6b6d69a 100644 --- a/priv/repo/migrations/20250222201807_add_statuses_table.exs +++ b/priv/repo/migrations/20250222201807_add_statuses_table.exs @@ -1,4 +1,4 @@ -defmodule CMS.Repo.Migrations.AddStatusesTable do +defmodule Core.Repo.Migrations.AddStatusesTable do use Ecto.Migration def change do diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 58b0e6b..a1b3017 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -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. diff --git a/test/cms_web/controllers/error_html_test.exs b/test/cms_web/controllers/error_html_test.exs deleted file mode 100644 index 6789eb3..0000000 --- a/test/cms_web/controllers/error_html_test.exs +++ /dev/null @@ -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 diff --git a/test/cms_web/controllers/error_json_test.exs b/test/cms_web/controllers/error_json_test.exs deleted file mode 100644 index a289796..0000000 --- a/test/cms_web/controllers/error_json_test.exs +++ /dev/null @@ -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 diff --git a/test/cms_web/controllers/page_controller_test.exs b/test/cms_web/controllers/page_controller_test.exs deleted file mode 100644 index 6eea161..0000000 --- a/test/cms_web/controllers/page_controller_test.exs +++ /dev/null @@ -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 diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index 82631ca..1e6b619 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -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 diff --git a/test/support/data_case.ex b/test/support/data_case.ex index 0432616..ff9c165 100644 --- a/test/support/data_case.ex +++ b/test/support/data_case.ex @@ -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 diff --git a/test/support/test.ex b/test/support/test.ex new file mode 100644 index 0000000..0dde93c --- /dev/null +++ b/test/support/test.ex @@ -0,0 +1,3 @@ +defmodule Test do + use Boundary, top_level?: true, check: [in: false, out: false] +end diff --git a/test/test_helper.exs b/test/test_helper.exs index dbebc90..e7e3f45 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,2 +1,2 @@ ExUnit.start() -Ecto.Adapters.SQL.Sandbox.mode(CMS.Repo, :manual) +Ecto.Adapters.SQL.Sandbox.mode(Core.Repo, :manual) diff --git a/test/web/controllers/error_html_test.exs b/test/web/controllers/error_html_test.exs new file mode 100644 index 0000000..7c750cc --- /dev/null +++ b/test/web/controllers/error_html_test.exs @@ -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 diff --git a/test/web/controllers/error_json_test.exs b/test/web/controllers/error_json_test.exs new file mode 100644 index 0000000..b80c961 --- /dev/null +++ b/test/web/controllers/error_json_test.exs @@ -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 diff --git a/test/web/controllers/page_controller_test.exs b/test/web/controllers/page_controller_test.exs new file mode 100644 index 0000000..6747117 --- /dev/null +++ b/test/web/controllers/page_controller_test.exs @@ -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