Skip to content

Commit 1575931

Browse files
committed
Switch to generated auth
1 parent 8020d84 commit 1575931

35 files changed

+2037
-328
lines changed

assets/js/app.js

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
2+
import '../../deps/phoenix_html';
3+
14
import { Socket } from '../../deps/phoenix';
25
import { LiveSocket } from '../../deps/phoenix_live_view';
36

config/test.exs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ config :ex_rss, ExRssWeb.Endpoint,
1111
secret_key_base: String.duplicate("a", 64)
1212

1313
# In test we don't send emails.
14-
config :lightweight_todo, ExRss.Mailer, adapter: Swoosh.Adapters.Test
14+
config :ex_rss, ExRss.Mailer, adapter: Swoosh.Adapters.Test
1515

1616
# Disable swoosh api client as it is only required for production adapters.
1717
config :swoosh, :api_client, false

lib/ex_rss_web/components/core_components.ex

+92-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,72 @@
11
defmodule ExRssWeb.CoreComponents do
22
use Phoenix.Component
33

4+
@doc """
5+
Renders a simple form.
6+
7+
## Examples
8+
9+
<.simple_form for={@form} phx-change="validate" phx-submit="save">
10+
<.input field={@form[:email]} label="Email"/>
11+
<.input field={@form[:username]} label="Username" />
12+
<:actions>
13+
<.button>Save</.button>
14+
</:actions>
15+
</.simple_form>
16+
"""
17+
attr :for, :any, required: true, doc: "the data structure for the form"
18+
attr :as, :any, default: nil, doc: "the server side parameter to collect all input under"
19+
20+
attr :rest, :global,
21+
include: ~w(autocomplete name rel action enctype method novalidate target multipart),
22+
doc: "the arbitrary HTML attributes to apply to the form tag"
23+
24+
slot :inner_block, required: true
25+
slot :actions, doc: "the slot for form actions, such as a submit button"
26+
27+
def simple_form(assigns) do
28+
~H"""
29+
<.form :let={f} for={@for} as={@as} {@rest}>
30+
<div class="mt-10 space-y-8">
31+
{render_slot(@inner_block, f)}
32+
<div :for={action <- @actions} class="mt-2 flex items-center justify-between gap-6">
33+
{render_slot(action, f)}
34+
</div>
35+
</div>
36+
</.form>
37+
"""
38+
end
39+
40+
@doc """
41+
Renders a button.
42+
43+
## Examples
44+
45+
<.button>Send!</.button>
46+
<.button phx-click="go" class="ml-2">Send!</.button>
47+
"""
48+
attr :type, :string, default: nil
49+
attr :class, :string, default: nil
50+
attr :rest, :global, include: ~w(disabled form name value)
51+
52+
slot :inner_block, required: true
53+
54+
def button(assigns) do
55+
~H"""
56+
<button
57+
type={@type}
58+
class={[
59+
"phx-submit-loading:opacity-75 rounded-lg bg-zinc-900 hover:bg-zinc-700 py-2 px-3",
60+
"text-sm font-semibold leading-6 text-white active:text-white/80",
61+
@class
62+
]}
63+
{@rest}
64+
>
65+
{render_slot(@inner_block)}
66+
</button>
67+
"""
68+
end
69+
470
@doc """
571
Renders an input with label and error messages.
672
@@ -94,7 +160,7 @@ defmodule ExRssWeb.CoreComponents do
94160
<select
95161
id={@id}
96162
name={@name}
97-
class="mt-2 block w-full rounded-md border border-gray-300 bg-white shadow-sm focus:border-zinc-400 focus:ring-0 sm:text-sm"
163+
class="mt-2 block w-full rounded-md border border-gray-300 shadow-sm focus:border-zinc-400 focus:ring-0 sm:text-sm"
98164
multiple={@multiple}
99165
{@rest}
100166
>
@@ -175,6 +241,31 @@ defmodule ExRssWeb.CoreComponents do
175241
"""
176242
end
177243

244+
@doc """
245+
Renders a header with title.
246+
"""
247+
attr :class, :string, default: nil
248+
249+
slot :inner_block, required: true
250+
slot :subtitle
251+
slot :actions
252+
253+
def header(assigns) do
254+
~H"""
255+
<header class={[@actions != [] && "flex items-center justify-between gap-6", @class]}>
256+
<div>
257+
<h1 class="text-lg font-semibold leading-8 text-zinc-800">
258+
{render_slot(@inner_block)}
259+
</h1>
260+
<p :if={@subtitle != []} class="mt-2 text-sm leading-6 text-zinc-600">
261+
{render_slot(@subtitle)}
262+
</p>
263+
</div>
264+
<div class="flex-none">{render_slot(@actions)}</div>
265+
</header>
266+
"""
267+
end
268+
178269
@doc """
179270
Renders a [Heroicon](https://heroicons.com).
180271

lib/ex_rss_web/components/layouts/root.html.heex

+46-5
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,53 @@
1818
</head>
1919

2020
<body class="m-6 grid grid-cols-12">
21-
<div class="col-span-12 md:col-start-3 md:col-span-8">
22-
<p role="alert">{Phoenix.Flash.get(@flash, :info)}</p>
23-
<p role="alert">{Phoenix.Flash.get(@flash, :error)}</p>
21+
<header class="col-span-12 md:col-start-3 md:col-span-8">
22+
<ul class="relative z-10 flex items-center gap-4 justify-end">
23+
<%= if @current_user do %>
24+
<li class="text-[0.8125rem] leading-6 text-zinc-900">
25+
{@current_user.email}
26+
</li>
27+
<li>
28+
<.link
29+
href={~p"/users/settings"}
30+
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
31+
>
32+
Settings
33+
</.link>
34+
</li>
35+
<li>
36+
<.link
37+
href={~p"/users/log_out"}
38+
method="delete"
39+
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
40+
>
41+
Log out
42+
</.link>
43+
</li>
44+
<% else %>
45+
<li>
46+
<.link
47+
href={~p"/users/register"}
48+
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
49+
>
50+
Register
51+
</.link>
52+
</li>
53+
<li>
54+
<.link
55+
href={~p"/users/log_in"}
56+
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
57+
>
58+
Log in
59+
</.link>
60+
</li>
61+
<% end %>
62+
</ul>
2463

64+
</header>
65+
66+
<main class="col-span-12 md:col-start-3 md:col-span-8">
2567
{@inner_content}
26-
</div>
27-
<!-- /container -->
68+
</main>
2869
</body>
2970
</html>

lib/ex_rss_web/controllers/feed_controller.ex

-23
This file was deleted.

lib/ex_rss_web/controllers/session_controller.ex

-33
This file was deleted.

lib/ex_rss_web/controllers/user_controller.ex

-25
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
defmodule ExRssWeb.UserSessionController do
2+
use ExRssWeb, :controller
3+
4+
alias ExRss.Accounts
5+
alias ExRssWeb.UserAuth
6+
7+
def create(conn, %{"_action" => "registered"} = params) do
8+
create(conn, params, "Account created successfully!")
9+
end
10+
11+
def create(conn, %{"_action" => "password_updated"} = params) do
12+
conn
13+
|> put_session(:user_return_to, ~p"/users/settings")
14+
|> create(params, "Password updated successfully!")
15+
end
16+
17+
def create(conn, params) do
18+
create(conn, params, "Welcome back!")
19+
end
20+
21+
defp create(conn, %{"user" => user_params}, info) do
22+
%{"email" => email, "password" => password} = user_params
23+
24+
if user = Accounts.get_user_by_email_and_password(email, password) do
25+
conn
26+
|> put_flash(:info, info)
27+
|> UserAuth.log_in_user(user, user_params)
28+
else
29+
# In order to prevent user enumeration attacks, don't disclose whether the email is registered.
30+
conn
31+
|> put_flash(:error, "Invalid email or password")
32+
|> put_flash(:email, String.slice(email, 0, 160))
33+
|> redirect(to: ~p"/users/log_in")
34+
end
35+
end
36+
37+
def delete(conn, _params) do
38+
conn
39+
|> put_flash(:info, "Logged out successfully.")
40+
|> UserAuth.log_out_user()
41+
end
42+
end

lib/ex_rss_web/feed_live/index.ex

+3-5
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@ defmodule ExRssWeb.FeedLive.Index do
66
alias ExRss.{Entry, Feed, Repo, User}
77
alias ExRss.{FeedAdder, FeedRemover}
88

9-
def mount(
10-
_params,
11-
%{"current_user" => current_user} = _session,
12-
socket
13-
) do
9+
def mount(_params, _session, socket) do
10+
current_user = socket.assigns.current_user
11+
1412
ExRssWeb.Endpoint.subscribe("user:#{current_user.id}")
1513

1614
socket =

lib/ex_rss_web/feed_live/new.ex

+3-5
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@ defmodule ExRssWeb.FeedLive.New do
44
alias ExRss.{Repo, User}
55
alias ExRss.FeedAdder
66

7-
def mount(
8-
%{"url" => url},
9-
%{"current_user" => current_user},
10-
socket
11-
) do
7+
def mount( %{"url" => url}, _session, socket) do
8+
current_user = socket.assigns.current_user
9+
1210
candidate =
1311
case FeedAdder.discover_feed(url) do
1412
{:ok, candidate} -> candidate
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
defmodule ExRssWeb.UserConfirmationInstructionsLive do
2+
use ExRssWeb, :live_view
3+
4+
alias ExRss.Accounts
5+
6+
def render(assigns) do
7+
~H"""
8+
<div class="mx-auto max-w-sm">
9+
<.header class="text-center">
10+
No confirmation instructions received?
11+
<:subtitle>We'll send a new confirmation link to your inbox</:subtitle>
12+
</.header>
13+
14+
<.simple_form for={@form} id="resend_confirmation_form" phx-submit="send_instructions">
15+
<.input field={@form[:email]} type="email" placeholder="Email" required />
16+
<:actions>
17+
<.button phx-disable-with="Sending..." class="w-full">
18+
Resend confirmation instructions
19+
</.button>
20+
</:actions>
21+
</.simple_form>
22+
23+
<p class="text-center mt-4">
24+
<.link href={~p"/users/register"}>Register</.link>
25+
| <.link href={~p"/users/log_in"}>Log in</.link>
26+
</p>
27+
</div>
28+
"""
29+
end
30+
31+
def mount(_params, _session, socket) do
32+
{:ok, assign(socket, form: to_form(%{}, as: "user"))}
33+
end
34+
35+
def handle_event("send_instructions", %{"user" => %{"email" => email}}, socket) do
36+
if user = Accounts.get_user_by_email(email) do
37+
Accounts.deliver_user_confirmation_instructions(
38+
user,
39+
&url(~p"/users/confirm/#{&1}")
40+
)
41+
end
42+
43+
info =
44+
"If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly."
45+
46+
{:noreply,
47+
socket
48+
|> put_flash(:info, info)
49+
|> redirect(to: ~p"/")}
50+
end
51+
end

0 commit comments

Comments
 (0)