diff --git a/config/config.exs b/config/config.exs index d1e28b42..e2677ea9 100644 --- a/config/config.exs +++ b/config/config.exs @@ -10,6 +10,12 @@ use Mix.Config config :philomena, ecto_repos: [Philomena.Repo] +config :philomena, + password_pepper: "dn2e0EpZrvBLoxUM3gfQveBhjf0bG/6/bYhrOyq3L3hV9hdo/bimJ+irbDWsuXLP" + +config :bcrypt_elixir, + log_rounds: 12 + # Configures the endpoint config :philomena, PhilomenaWeb.Endpoint, url: [host: "localhost"], @@ -17,6 +23,12 @@ config :philomena, PhilomenaWeb.Endpoint, render_errors: [view: PhilomenaWeb.ErrorView, accepts: ~w(html json)], pubsub: [name: Philomena.PubSub, adapter: Phoenix.PubSub.PG2] +config :phoenix, :template_engines, + slim: PhoenixSlime.Engine, + slime: PhoenixSlime.Engine, + # If you want to use LiveView + slimleex: PhoenixSlime.LiveViewEngine + # Configures Elixir's Logger config :logger, :console, format: "$time $metadata[$level] $message\n", diff --git a/config/dev.exs b/config/dev.exs index 12987625..33bfff42 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -61,7 +61,7 @@ config :philomena, PhilomenaWeb.Endpoint, ~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$", ~r"priv/gettext/.*(po)$", ~r"lib/philomena_web/{live,views}/.*(ex)$", - ~r"lib/philomena_web/templates/.*(eex)$" + ~r"lib/philomena_web/templates/.*(eex|slim|slime)$" ] ] diff --git a/config/prod.secret.exs b/config/prod.secret.exs index 9f69adcf..76b24d67 100644 --- a/config/prod.secret.exs +++ b/config/prod.secret.exs @@ -11,6 +11,17 @@ database_url = For example: ecto://USER:PASS@HOST/DATABASE """ +config :bcrypt_elixir, + log_rounds: String.to_integer(System.get_env("BCRYPT_ROUNDS") || "12") + +config :philomena, + password_pepper: + System.get_env("PASSWORD_PEPPER") || + raise(""" + environment variable PASSWORD_PEPPER is missing. + You can generate one by calling: mix phx.gen.secret + """) + config :philomena, Philomena.Repo, # ssl: true, url: database_url, @@ -25,7 +36,8 @@ secret_key_base = config :philomena, PhilomenaWeb.Endpoint, http: [:inet6, port: String.to_integer(System.get_env("PORT") || "4000")], - secret_key_base: secret_key_base + secret_key_base: secret_key_base, + server: true # ## Using releases (Elixir v1.9+) # diff --git a/lib/philomena/users.ex b/lib/philomena/users.ex new file mode 100644 index 00000000..923b73bf --- /dev/null +++ b/lib/philomena/users.ex @@ -0,0 +1,104 @@ +defmodule Philomena.Users do + @moduledoc """ + The Users context. + """ + + import Ecto.Query, warn: false + alias Philomena.Repo + + alias Philomena.Users.User + + @doc """ + Returns the list of users. + + ## Examples + + iex> list_users() + [%User{}, ...] + + """ + def list_users do + Repo.all(User) + end + + @doc """ + Gets a single user. + + Raises `Ecto.NoResultsError` if the User does not exist. + + ## Examples + + iex> get_user!(123) + %User{} + + iex> get_user!(456) + ** (Ecto.NoResultsError) + + """ + def get_user!(id), do: Repo.get!(User, id) + + @doc """ + Creates a user. + + ## Examples + + iex> create_user(%{field: value}) + {:ok, %User{}} + + iex> create_user(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_user(attrs \\ %{}) do + %User{} + |> User.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a user. + + ## Examples + + iex> update_user(user, %{field: new_value}) + {:ok, %User{}} + + iex> update_user(user, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_user(%User{} = user, attrs) do + user + |> User.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a User. + + ## Examples + + iex> delete_user(user) + {:ok, %User{}} + + iex> delete_user(user) + {:error, %Ecto.Changeset{}} + + """ + def delete_user(%User{} = user) do + Repo.delete(user) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking user changes. + + ## Examples + + iex> change_user(user) + %Ecto.Changeset{source: %User{}} + + """ + def change_user(%User{} = user) do + User.changeset(user, %{}) + end +end diff --git a/lib/philomena/users/user.ex b/lib/philomena/users/user.ex new file mode 100644 index 00000000..506e8212 --- /dev/null +++ b/lib/philomena/users/user.ex @@ -0,0 +1,25 @@ +defmodule Philomena.Users.User do + use Ecto.Schema + import Ecto.Changeset + + schema "users" do + field :email, :string + field :encrypted_password, :string, default: "" + field :reset_password_token, :string + field :reset_password_sent_at, :naive_datetime + field :remember_created_at, :naive_datetime + field :sign_in_count, :integer, default: 0 + field :current_sign_in_at, :naive_datetime + field :last_sign_in_at, :naive_datetime + field :current_sign_in_ip, EctoNetwork.INET + field :last_sign_in_ip, EctoNetwork.INET + timestamps(inserted_at: :created_at) + end + + @doc false + def changeset(user, attrs) do + user + |> cast(attrs, []) + |> validate_required([]) + end +end diff --git a/lib/philomena_web/endpoint.ex b/lib/philomena_web/endpoint.ex index 3a44d128..4f7265e4 100644 --- a/lib/philomena_web/endpoint.ex +++ b/lib/philomena_web/endpoint.ex @@ -40,7 +40,9 @@ defmodule PhilomenaWeb.Endpoint do plug Plug.Session, store: :cookie, key: "_philomena_key", - signing_salt: "YoFrfZr1" + signing_salt: "signed cookie", + encryption_salt: "authenticated encrypted cookie" + plug PhilomenaWeb.Plugs.RenderTime plug PhilomenaWeb.Router end diff --git a/lib/philomena_web/plugs/render_time.ex b/lib/philomena_web/plugs/render_time.ex new file mode 100644 index 00000000..2705d73f --- /dev/null +++ b/lib/philomena_web/plugs/render_time.ex @@ -0,0 +1,11 @@ +defmodule PhilomenaWeb.Plugs.RenderTime do + import Plug.Conn + + # No options + def init([]), do: false + + # Assign current time + def call(conn, _opts) do + conn |> assign(:start_time, Time.utc_now()) + end +end diff --git a/lib/philomena_web/templates/layout/app.html.eex b/lib/philomena_web/templates/layout/app.html.eex index 5bf288f2..a580fb35 100644 --- a/lib/philomena_web/templates/layout/app.html.eex +++ b/lib/philomena_web/templates/layout/app.html.eex @@ -24,6 +24,7 @@ <%= render @view_module, @view_template, assigns %> +

Rendered in <%= render_time(@conn) %> ms

diff --git a/lib/philomena_web/templates/page/index.html.eex b/lib/philomena_web/templates/page/index.html.eex deleted file mode 100644 index 8cbd9d83..00000000 --- a/lib/philomena_web/templates/page/index.html.eex +++ /dev/null @@ -1,35 +0,0 @@ -
-

<%= gettext "Welcome to %{name}!", name: "Phoenix" %>

-

A productive web framework that
does not compromise speed or maintainability.

-
- -
-
-

Resources

- -
-
-

Help

- -
-
diff --git a/lib/philomena_web/templates/page/index.html.slime b/lib/philomena_web/templates/page/index.html.slime new file mode 100644 index 00000000..cce047ba --- /dev/null +++ b/lib/philomena_web/templates/page/index.html.slime @@ -0,0 +1,26 @@ +section.phx-hero + h1 = gettext "Welcome %{name}!", name: "Philomena" + p + ' A productive web framework that + br + ' does not compromise speed or maintainability. + +section.row + article.column + h2 Resources + ul + li + a href="https://hexdocs.pm/phoenix/overview.html" Guides & Docs + li + a href="https://github.com/phoenixframework/phoenix" Source + li + a href="https://github.com/phoenixframework/phoenix/blob/v1.4/CHANGELOG.md" Changelog + article.column + h2 Help + ul + li + a href="https://elixirforum.com/c/phoenix-forum" Forums + li + a href="https://webchat.freenode.net/?channels=elixir-lang" #elixir-lang on Freenode IRC + li + a href="https://twitter.com/elixirphoenix" Twitter @elixirphoenix diff --git a/lib/philomena_web/views/layout_view.ex b/lib/philomena_web/views/layout_view.ex index c6649c5f..2a106979 100644 --- a/lib/philomena_web/views/layout_view.ex +++ b/lib/philomena_web/views/layout_view.ex @@ -1,3 +1,9 @@ defmodule PhilomenaWeb.LayoutView do use PhilomenaWeb, :view + + def render_time(conn) do + (Time.diff(Time.utc_now(), conn.assigns[:start_time], :microsecond) / 1000.0) + |> Float.round(3) + |> Float.to_string() + end end diff --git a/mix.exs b/mix.exs index 06e5f4c8..58076258 100644 --- a/mix.exs +++ b/mix.exs @@ -42,7 +42,10 @@ defmodule Philomena.MixProject do {:phoenix_live_reload, "~> 1.2", only: :dev}, {:gettext, "~> 0.11"}, {:jason, "~> 1.0"}, - {:plug_cowboy, "~> 2.0"} + {:plug_cowboy, "~> 2.0"}, + {:phoenix_slime, "~> 0.12.0"}, + {:ecto_network, "~> 1.1"}, + {:pbkdf2, "~> 2.0"} ] end diff --git a/mix.lock b/mix.lock index 8789eac4..4e7f372d 100644 --- a/mix.lock +++ b/mix.lock @@ -5,20 +5,25 @@ "db_connection": {:hex, :db_connection, "2.1.1", "a51e8a2ee54ef2ae6ec41a668c85787ed40cb8944928c191280fe34c15b76ae5", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"}, "decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"}, "ecto": {:hex, :ecto, "3.1.7", "fa21d06ef56cdc2fdaa62574e8c3ba34a2751d44ea34c30bc65f0728421043e5", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, + "ecto_network": {:hex, :ecto_network, "1.1.0", "7062004b9324ff13e50c02dab84877f8a55e06db9eabbf2d04bda21da6fc6e8a", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 0.0.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.14.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm"}, "ecto_sql": {:hex, :ecto_sql, "3.1.6", "1e80e30d16138a729c717f73dcb938590bcdb3a4502f3012414d0cbb261045d8", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.1.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.9.1", [hex: :mariaex, repo: "hexpm", optional: true]}, {:myxql, "~> 0.2.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14.0 or ~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"}, "file_system": {:hex, :file_system, "0.2.7", "e6f7f155970975789f26e77b8b8d8ab084c59844d8ecfaf58cbda31c494d14aa", [:mix], [], "hexpm"}, "gettext": {:hex, :gettext, "0.17.0", "abe21542c831887a2b16f4c94556db9c421ab301aee417b7c4fbde7fbdbe01ec", [:mix], [], "hexpm"}, "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"}, + "neotoma": {:hex, :neotoma, "1.7.3", "d8bd5404b73273989946e4f4f6d529e5c2088f5fa1ca790b4dbe81f4be408e61", [:rebar], [], "hexpm"}, + "pbkdf2": {:hex, :pbkdf2, "2.0.0", "11c23279fded5c0027ab3996cfae77805521d7ef4babde2bd7ec04a9086cf499", [:rebar3], [], "hexpm"}, "phoenix": {:hex, :phoenix, "1.4.9", "746d098e10741c334d88143d3c94cab1756435f94387a63441792e66ec0ee974", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.0.0", "c43117a136e7399ea04ecaac73f8f23ee0ffe3e07acfcb8062fe5f4c9f0f6531", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, "phoenix_html": {:hex, :phoenix_html, "2.13.3", "850e292ff6e204257f5f9c4c54a8cb1f6fbc16ed53d360c2b780a3d0ba333867", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.1", "274a4b07c4adbdd7785d45a8b0bb57634d0b4f45b18d2c508b26c0344bd59b8f", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"}, + "phoenix_slime": {:hex, :phoenix_slime, "0.12.0", "0d5f2b600b8a69dad361a987fdfc836bc71cdce373ce11abade4f6548880f0bf", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.10", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:slime, "~> 1.0", [hex: :slime, repo: "hexpm", optional: false]}], "hexpm"}, "plug": {:hex, :plug, "1.8.3", "12d5f9796dc72e8ac9614e94bda5e51c4c028d0d428e9297650d09e15a684478", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"}, "plug_cowboy": {:hex, :plug_cowboy, "2.1.0", "b75768153c3a8a9e8039d4b25bb9b14efbc58e9c4a6e6a270abff1cd30cbe320", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"}, "postgrex": {:hex, :postgrex, "0.15.0", "dd5349161019caeea93efa42f9b22f9d79995c3a86bdffb796427b4c9863b0f0", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"}, + "slime": {:hex, :slime, "1.2.0", "d46ede53c96b743dfdd23821268dc9b01f04ffea65d9d57c4e3d9200b162df02", [:mix], [{:neotoma, "~> 1.7", [hex: :neotoma, repo: "hexpm", optional: false]}], "hexpm"}, "telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"}, }