working to get bones73k up to par with work done on shift73k
This commit is contained in:
parent
19f14f7046
commit
2e61ee0031
14 changed files with 73 additions and 134 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -47,3 +47,6 @@ npm-debug.log
|
||||||
|
|
||||||
# for vscode elixir_ls extension files
|
# for vscode elixir_ls extension files
|
||||||
/.elixir_ls
|
/.elixir_ls
|
||||||
|
|
||||||
|
# dev
|
||||||
|
TODO.md
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
# General application configuration
|
# General application configuration
|
||||||
use Mix.Config
|
use Mix.Config
|
||||||
|
|
||||||
|
config :elixir, :time_zone_database, Tzdata.TimeZoneDatabase
|
||||||
|
|
||||||
config :bones73k,
|
config :bones73k,
|
||||||
ecto_repos: [Bones73k.Repo]
|
ecto_repos: [Bones73k.Repo]
|
||||||
|
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
# In this file, we load production configuration and secrets
|
|
||||||
# from environment variables. You can also hardcode secrets,
|
|
||||||
# although such is generally not recommended and you have to
|
|
||||||
# remember to add this file to your .gitignore.
|
|
||||||
use Mix.Config
|
|
||||||
|
|
||||||
database_url =
|
|
||||||
System.get_env("DATABASE_URL") ||
|
|
||||||
raise """
|
|
||||||
environment variable DATABASE_URL is missing.
|
|
||||||
For example: ecto://USER:PASS@HOST/DATABASE
|
|
||||||
"""
|
|
||||||
|
|
||||||
config :bones73k, Bones73k.Repo,
|
|
||||||
# ssl: true,
|
|
||||||
url: database_url,
|
|
||||||
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
|
|
||||||
|
|
||||||
secret_key_base =
|
|
||||||
System.get_env("SECRET_KEY_BASE") ||
|
|
||||||
raise """
|
|
||||||
environment variable SECRET_KEY_BASE is missing.
|
|
||||||
You can generate one by calling: mix phx.gen.secret
|
|
||||||
"""
|
|
||||||
|
|
||||||
config :bones73k, Bones73kWeb.Endpoint,
|
|
||||||
http: [
|
|
||||||
port: String.to_integer(System.get_env("PORT") || "4000"),
|
|
||||||
transport_options: [socket_opts: [:inet6]]
|
|
||||||
],
|
|
||||||
secret_key_base: secret_key_base
|
|
||||||
|
|
||||||
# ## Using releases (Elixir v1.9+)
|
|
||||||
#
|
|
||||||
# If you are doing OTP releases, you need to instruct Phoenix
|
|
||||||
# to start each relevant endpoint:
|
|
||||||
#
|
|
||||||
# config :bones73k, Bones73kWeb.Endpoint, server: true
|
|
||||||
#
|
|
||||||
# Then you can assemble a release by calling `mix release`.
|
|
||||||
# See `mix help release` for more information.
|
|
||||||
|
|
||||||
# Import extra secret stuff not to be included in git repo
|
|
||||||
import_config "really.secret.exs"
|
|
|
@ -6,4 +6,9 @@ defmodule Bones73k do
|
||||||
Contexts are also responsible for managing your data, regardless
|
Contexts are also responsible for managing your data, regardless
|
||||||
if it comes from the database, an external API or others.
|
if it comes from the database, an external API or others.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@app_vars Application.get_env(:bones73k, :app_global_vars, time_zone: "America/New_York")
|
||||||
|
@app_time_zone @app_vars[:time_zone]
|
||||||
|
|
||||||
|
def app_time_zone, do: @app_time_zone
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
defmodule Bones73k.Accounts.User do
|
defmodule Bones73k.Accounts.User do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
import EctoEnum
|
|
||||||
|
|
||||||
@roles [
|
@roles [
|
||||||
user: "Basic user level",
|
user: "Basic user level",
|
||||||
|
@ -9,8 +8,6 @@ defmodule Bones73k.Accounts.User do
|
||||||
admin: "Can delete users and change user roles"
|
admin: "Can delete users and change user roles"
|
||||||
]
|
]
|
||||||
|
|
||||||
defenum(RolesEnum, :role, Keyword.keys(@roles))
|
|
||||||
|
|
||||||
@max_email 254
|
@max_email 254
|
||||||
@min_password 6
|
@min_password 6
|
||||||
@max_password 80
|
@max_password 80
|
||||||
|
@ -23,8 +20,8 @@ defmodule Bones73k.Accounts.User do
|
||||||
field :password, :string, virtual: true
|
field :password, :string, virtual: true
|
||||||
field :hashed_password, :string
|
field :hashed_password, :string
|
||||||
field :confirmed_at, :naive_datetime
|
field :confirmed_at, :naive_datetime
|
||||||
|
field :role, Ecto.Enum, values: Keyword.keys(@roles), default: :user
|
||||||
|
|
||||||
field :role, RolesEnum, default: :user
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -73,21 +70,10 @@ defmodule Bones73k.Accounts.User do
|
||||||
|> validate_password_not_required(opts)
|
|> validate_password_not_required(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
# def update_changeset_no_pw(user, attrs) do
|
|
||||||
# user
|
|
||||||
# |> cast(attrs, [:email, :role])
|
|
||||||
# |> validate_role()
|
|
||||||
# |> validate_email()
|
|
||||||
# end
|
|
||||||
|
|
||||||
defp role_validator(:role, role) do
|
|
||||||
(RolesEnum.valid_value?(role) && []) || [role: "invalid user role"]
|
|
||||||
end
|
|
||||||
|
|
||||||
defp validate_role(changeset) do
|
defp validate_role(changeset) do
|
||||||
changeset
|
changeset
|
||||||
|> validate_required([:role])
|
|> validate_required([:role])
|
||||||
|> validate_change(:role, &role_validator/2)
|
|> validate_inclusion(:role, Keyword.keys(@roles), message: "invalid user role")
|
||||||
end
|
end
|
||||||
|
|
||||||
defp validate_email_format(changeset) do
|
defp validate_email_format(changeset) do
|
||||||
|
@ -115,9 +101,11 @@ defmodule Bones73k.Accounts.User do
|
||||||
defp validate_password_not_required(changeset, opts) do
|
defp validate_password_not_required(changeset, opts) do
|
||||||
changeset
|
changeset
|
||||||
|> validate_length(:password, min: @min_password, max: @max_password)
|
|> validate_length(:password, min: @min_password, max: @max_password)
|
||||||
# |> validate_format(:password, ~r/[a-z]/, message: "at least one lower case character")
|
|> validate_format(:password, ~r/[a-z]/, message: "at least one lower case character")
|
||||||
# |> validate_format(:password, ~r/[A-Z]/, message: "at least one upper case character")
|
|> validate_format(:password, ~r/[A-Z]/, message: "at least one upper case character")
|
||||||
# |> validate_format(:password, ~r/[!?@#$%^&*_0-9]/, message: "at least one digit or punctuation character")
|
|> validate_format(:password, ~r/[!?@#$%^&*_0-9]/,
|
||||||
|
message: "at least one digit or punctuation character"
|
||||||
|
)
|
||||||
|> maybe_hash_password(opts)
|
|> maybe_hash_password(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,9 @@ defmodule Bones73k.Accounts.UserToken do
|
||||||
@primary_key {:id, :binary_id, autogenerate: true}
|
@primary_key {:id, :binary_id, autogenerate: true}
|
||||||
@foreign_key_type :binary_id
|
@foreign_key_type :binary_id
|
||||||
schema "users_tokens" do
|
schema "users_tokens" do
|
||||||
field :token, :binary
|
field(:token, :binary)
|
||||||
field :context, :string
|
field(:context, :string)
|
||||||
field :sent_to, :string
|
field(:sent_to, :string)
|
||||||
belongs_to :user, Bones73k.Accounts.User
|
belongs_to :user, Bones73k.Accounts.User
|
||||||
|
|
||||||
timestamps(updated_at: false)
|
timestamps(updated_at: false)
|
||||||
|
@ -40,10 +40,11 @@ defmodule Bones73k.Accounts.UserToken do
|
||||||
"""
|
"""
|
||||||
def verify_session_token_query(token) do
|
def verify_session_token_query(token) do
|
||||||
query =
|
query =
|
||||||
from token in token_and_context_query(token, "session"),
|
from(token in token_and_context_query(token, "session"),
|
||||||
join: user in assoc(token, :user),
|
join: user in assoc(token, :user),
|
||||||
where: token.inserted_at > ago(@session_validity_in_days, "day"),
|
where: token.inserted_at > ago(@session_validity_in_days, "day"),
|
||||||
select: user
|
select: user
|
||||||
|
)
|
||||||
|
|
||||||
{:ok, query}
|
{:ok, query}
|
||||||
end
|
end
|
||||||
|
@ -85,10 +86,11 @@ defmodule Bones73k.Accounts.UserToken do
|
||||||
days = days_for_context(context)
|
days = days_for_context(context)
|
||||||
|
|
||||||
query =
|
query =
|
||||||
from token in token_and_context_query(hashed_token, context),
|
from(token in token_and_context_query(hashed_token, context),
|
||||||
join: user in assoc(token, :user),
|
join: user in assoc(token, :user),
|
||||||
where: token.inserted_at > ago(^days, "day") and token.sent_to == user.email,
|
where: token.inserted_at > ago(^days, "day") and token.sent_to == user.email,
|
||||||
select: user
|
select: user
|
||||||
|
)
|
||||||
|
|
||||||
{:ok, query}
|
{:ok, query}
|
||||||
|
|
||||||
|
@ -111,8 +113,9 @@ defmodule Bones73k.Accounts.UserToken do
|
||||||
hashed_token = :crypto.hash(@hash_algorithm, decoded_token)
|
hashed_token = :crypto.hash(@hash_algorithm, decoded_token)
|
||||||
|
|
||||||
query =
|
query =
|
||||||
from token in token_and_context_query(hashed_token, context),
|
from(token in token_and_context_query(hashed_token, context),
|
||||||
where: token.inserted_at > ago(@change_email_validity_in_days, "day")
|
where: token.inserted_at > ago(@change_email_validity_in_days, "day")
|
||||||
|
)
|
||||||
|
|
||||||
{:ok, query}
|
{:ok, query}
|
||||||
|
|
||||||
|
@ -125,17 +128,17 @@ defmodule Bones73k.Accounts.UserToken do
|
||||||
Returns the given token with the given context.
|
Returns the given token with the given context.
|
||||||
"""
|
"""
|
||||||
def token_and_context_query(token, context) do
|
def token_and_context_query(token, context) do
|
||||||
from Bones73k.Accounts.UserToken, where: [token: ^token, context: ^context]
|
from(Bones73k.Accounts.UserToken, where: [token: ^token, context: ^context])
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Gets all tokens for the given user for the given contexts.
|
Gets all tokens for the given user for the given contexts.
|
||||||
"""
|
"""
|
||||||
def user_and_contexts_query(user, :all) do
|
def user_and_contexts_query(user, :all) do
|
||||||
from t in Bones73k.Accounts.UserToken, where: t.user_id == ^user.id
|
from(t in Bones73k.Accounts.UserToken, where: t.user_id == ^user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_and_contexts_query(user, [_ | _] = contexts) do
|
def user_and_contexts_query(user, [_ | _] = contexts) do
|
||||||
from t in Bones73k.Accounts.UserToken, where: t.user_id == ^user.id and t.context in ^contexts
|
from(t in Bones73k.Accounts.UserToken, where: t.user_id == ^user.id and t.context in ^contexts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
defmodule Bones73k.Util.Dt do
|
|
||||||
@app_vars Application.get_env(:bones73k, :app_global_vars, time_zone: "America/New_York")
|
|
||||||
@time_zone @app_vars[:time_zone]
|
|
||||||
|
|
||||||
def ndt_to_local(%NaiveDateTime{} = ndt), do: Timex.to_datetime(ndt, @time_zone)
|
|
||||||
|
|
||||||
def format_dt_local(dt_local, fstr), do: Timex.format!(dt_local, fstr)
|
|
||||||
|
|
||||||
def format_ndt(%NaiveDateTime{} = ndt, fstr), do: ndt |> ndt_to_local() |> format_dt_local(fstr)
|
|
||||||
end
|
|
|
@ -3,7 +3,6 @@ defmodule Bones73kWeb.UserManagementLive.Index do
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
import Bones73kWeb.Pagination
|
import Bones73kWeb.Pagination
|
||||||
import Bones73k.Util.Dt
|
|
||||||
|
|
||||||
alias Bones73k.Repo
|
alias Bones73k.Repo
|
||||||
alias Bones73k.Accounts
|
alias Bones73k.Accounts
|
||||||
|
@ -27,9 +26,8 @@ defmodule Bones73kWeb.UserManagementLive.Index do
|
||||||
if Roles.can?(current_user, user, live_action) do
|
if Roles.can?(current_user, user, live_action) do
|
||||||
socket
|
socket
|
||||||
|> assign(:query, query_map(params))
|
|> assign(:query, query_map(params))
|
||||||
|> assign_modal_return_to()
|
|> assign_modal_close_handlers()
|
||||||
|> assign(:delete_user, nil)
|
|> assign(:delete_user, nil)
|
||||||
|> assign(:page, nil)
|
|
||||||
|> request_page_query()
|
|> request_page_query()
|
||||||
|> apply_action(socket.assigns.live_action, params)
|
|> apply_action(socket.assigns.live_action, params)
|
||||||
|> live_noreply()
|
|> live_noreply()
|
||||||
|
@ -64,9 +62,9 @@ defmodule Bones73kWeb.UserManagementLive.Index do
|
||||||
|> assign(:user, Accounts.get_user!(id))
|
|> assign(:user, Accounts.get_user!(id))
|
||||||
end
|
end
|
||||||
|
|
||||||
def assign_modal_return_to(%{assigns: %{query: query}} = socket) do
|
defp assign_modal_close_handlers(%{assigns: %{query: query}} = socket) do
|
||||||
to = Routes.user_management_index_path(socket, :index, Enum.into(query, []))
|
to = Routes.user_management_index_path(socket, :index, Enum.into(query, []))
|
||||||
assign(socket, :modal_return_to, to)
|
assign(socket, modal_return_to: to, modal_close_action: :return)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp user_from_params(params)
|
defp user_from_params(params)
|
||||||
|
@ -97,7 +95,10 @@ defmodule Bones73kWeb.UserManagementLive.Index do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("delete-modal", %{"id" => id}, socket) do
|
def handle_event("delete-modal", %{"id" => id}, socket) do
|
||||||
{:noreply, assign(socket, :delete_user, Accounts.get_user(id))}
|
socket
|
||||||
|
|> assign(:modal_close_action, :delete_user)
|
||||||
|
|> assign(:delete_user, Accounts.get_user!(id))
|
||||||
|
|> live_noreply()
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
@ -113,17 +114,13 @@ defmodule Bones73kWeb.UserManagementLive.Index do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event(
|
def handle_event("sort-change", %{"sort_by" => column} = params, socket) do
|
||||||
"sort-change",
|
if column == socket.assigns.query.sort_by do
|
||||||
%{"sort_by" => column} = params,
|
order = (socket.assigns.query.sort_order == "asc" && "desc") || "asc"
|
||||||
%{assigns: %{query: query}} = socket
|
send(self(), {:query_update, %{"sort_order" => order}})
|
||||||
) do
|
else
|
||||||
(column == query.sort_by &&
|
|
||||||
send(
|
|
||||||
self(),
|
|
||||||
{:query_update, %{"sort_order" => (query.sort_order == "asc" && "desc") || "asc"}}
|
|
||||||
)) ||
|
|
||||||
send(self(), {:query_update, Map.put(params, "sort_order", "asc")})
|
send(self(), {:query_update, Map.put(params, "sort_order", "asc")})
|
||||||
|
end
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
@ -168,8 +165,20 @@ defmodule Bones73kWeb.UserManagementLive.Index do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_info({:close_modal, _}, %{assigns: %{modal_return_to: to}} = socket) do
|
def handle_info({:close_modal, _}, %{assigns: %{modal_close_action: :return}} = socket) do
|
||||||
socket |> copy_flash() |> push_patch(to: to) |> live_noreply()
|
socket
|
||||||
|
|> copy_flash()
|
||||||
|
|> push_patch(to: socket.assigns.modal_return_to)
|
||||||
|
|> live_noreply()
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_info({:close_modal, _}, %{assigns: %{modal_close_action: assign_key}} = socket) do
|
||||||
|
socket
|
||||||
|
|> assign(assign_key, nil)
|
||||||
|
|> assign_modal_close_handlers()
|
||||||
|
|> request_page_query()
|
||||||
|
|> live_noreply()
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
@ -187,5 +196,9 @@ defmodule Bones73kWeb.UserManagementLive.Index do
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
def dt_out(ndt), do: format_ndt(ndt, "{YYYY} {Mshort} {0D}, {h12}:{0m} {am}")
|
def dt_out(ndt) do
|
||||||
|
ndt
|
||||||
|
|> DateTime.from_naive!(Bones73k.app_time_zone())
|
||||||
|
|> Calendar.strftime("%Y %b %-d, %-I:%M %p")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
<%= live_modal @socket, Bones73kWeb.UserManagement.DeleteComponent,
|
<%= live_modal @socket, Bones73kWeb.UserManagement.DeleteComponent,
|
||||||
id: @delete_user.id,
|
id: @delete_user.id,
|
||||||
title: "Delete User",
|
title: "Delete User",
|
||||||
delete_user: @delete_user,
|
delete_user: @delete_user
|
||||||
current_user: @current_user %>
|
%>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
|
||||||
|
@ -92,20 +92,20 @@
|
||||||
</dt>
|
</dt>
|
||||||
<dd class="d-inline d-sm-block col-auto">
|
<dd class="d-inline d-sm-block col-auto">
|
||||||
<span class="visually-hidden"><%= user.confirmed_at && "Yes" || "No" %></span>
|
<span class="visually-hidden"><%= user.confirmed_at && "Yes" || "No" %></span>
|
||||||
<input type="checkbox" class="form-check-input" aria-hidden="true" <%= user.confirmed_at && "checked" || "" %>>
|
<input type="checkbox" class="form-check-input" aria-hidden="true" <%= user.confirmed_at && "checked" || "" %> disabled>
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<%= if Roles.can?(@current_user, user, :edit) do %>
|
<%= if Roles.can?(@current_user, user, :edit) do %>
|
||||||
<%= live_patch to: Routes.user_management_index_path(@socket, :edit, user.id, Enum.into(@query, [])), class: "btn btn-primary btn-sm text-nowrap" do %>
|
<%= live_patch to: Routes.user_management_index_path(@socket, :edit, user.id, Enum.into(@query, [])), class: "btn btn-primary btn-sm text-nowrap" do %>
|
||||||
<%= icon_div @socket, "bi-pencil", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
<%= icon_div @socket, "bi-pencil", [class: "icon baseline"] %>
|
||||||
Edit
|
Edit
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= if Roles.can?(@current_user, user, :delete) do %>
|
<%= if Roles.can?(@current_user, user, :delete) do %>
|
||||||
<button class="btn btn-outline-danger btn-sm text-nowrap" phx-click="delete-modal" phx-value-id="<%= user.id %>">
|
<button class="btn btn-outline-danger btn-sm text-nowrap" phx-click="delete-modal" phx-value-id="<%= user.id %>">
|
||||||
<%= icon_div @socket, "bi-trash", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
<%= icon_div @socket, "bi-trash", [class: "icon baseline"] %>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</button>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -174,20 +174,20 @@
|
||||||
<td class="align-middle" style="white-space: nowrap;"><%= dt_out(user.inserted_at) %></td>
|
<td class="align-middle" style="white-space: nowrap;"><%= dt_out(user.inserted_at) %></td>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
<span class="visually-hidden"><%= user.confirmed_at && "Confirmed" || "Not confirmed" %></span>
|
<span class="visually-hidden"><%= user.confirmed_at && "Confirmed" || "Not confirmed" %></span>
|
||||||
<input type="checkbox" class="form-check-input" aria-hidden="true" <%= user.confirmed_at && "checked" || "" %>>
|
<input type="checkbox" class="form-check-input" aria-hidden="true" <%= user.confirmed_at && "checked" || "" %> disabled>
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle text-end text-nowrap">
|
<td class="align-middle text-end text-nowrap">
|
||||||
|
|
||||||
<%= if Roles.can?(@current_user, user, :edit) do %>
|
<%= if Roles.can?(@current_user, user, :edit) do %>
|
||||||
<%= live_patch to: Routes.user_management_index_path(@socket, :edit, user.id, Enum.into(@query, [])), class: "btn btn-outline-primary btn-sm text-nowrap" do %>
|
<%= live_patch to: Routes.user_management_index_path(@socket, :edit, user.id, Enum.into(@query, [])), class: "btn btn-outline-primary btn-sm text-nowrap" do %>
|
||||||
<%= icon_div @socket, "bi-pencil", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
<%= icon_div @socket, "bi-pencil", [class: "icon baseline"] %>
|
||||||
Edit
|
Edit
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= if Roles.can?(@current_user, user, :delete) do %>
|
<%= if Roles.can?(@current_user, user, :delete) do %>
|
||||||
<button class="btn btn-outline-danger btn-sm text-nowrap" phx-click="delete-modal" phx-value-id="<%= user.id %>">
|
<button class="btn btn-outline-danger btn-sm text-nowrap" phx-click="delete-modal" phx-value-id="<%= user.id %>">
|
||||||
<%= icon_div @socket, "bi-trash", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
<%= icon_div @socket, "bi-trash", [class: "icon baseline"] %>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</button>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<%= if !@current_user do %>
|
<%= if !@current_user do %>
|
||||||
|
|
||||||
<%= link nav_link_opts(@conn, to: Routes.user_session_path(@conn, :new), class: "btn btn-outline-light") do %>
|
<%= link nav_link_opts(@conn, to: Routes.user_session_path(@conn, :new), class: "btn btn-outline-light") do %>
|
||||||
<%= icon_div @conn, "bi-door-open", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
<%= icon_div @conn, "bi-door-open", [class: "icon baseline"] %>
|
||||||
Log in
|
Log in
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<%= csrf_meta_tag() %>
|
<%= csrf_meta_tag() %>
|
||||||
<%= live_title_tag assigns[:page_title] || "Bones73k", suffix: " · Phoenix Framework" %>
|
<%= live_title_tag assigns[:page_title] || "", suffix: assigns[:page_title] && " · Bones73k" || "Bones73k" %>
|
||||||
<link phx-track-static rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
|
<link phx-track-static rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
|
||||||
<script defer phx-track-static type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
|
<script defer phx-track-static type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
3
mix.exs
3
mix.exs
|
@ -48,11 +48,10 @@ defmodule Bones73k.MixProject do
|
||||||
{:gettext, "~> 0.11"},
|
{:gettext, "~> 0.11"},
|
||||||
{:jason, "~> 1.0"},
|
{:jason, "~> 1.0"},
|
||||||
{:plug_cowboy, "~> 2.0"},
|
{:plug_cowboy, "~> 2.0"},
|
||||||
{:ecto_enum, "~> 1.4"},
|
|
||||||
{:bamboo, "~> 2.0"},
|
{:bamboo, "~> 2.0"},
|
||||||
{:bamboo_smtp, "~> 4.0"},
|
{:bamboo_smtp, "~> 4.0"},
|
||||||
{:scrivener_ecto, "~> 2.0"},
|
{:scrivener_ecto, "~> 2.0"},
|
||||||
{:timex, "~> 3.5"}
|
{:tzdata, "~> 1.1"}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule Bones73k.Repo.Migrations.CreateUsersAuthTables do
|
||||||
add(:id, :binary_id, primary_key: true)
|
add(:id, :binary_id, primary_key: true)
|
||||||
add(:email, :citext, null: false)
|
add(:email, :citext, null: false)
|
||||||
add(:hashed_password, :string, null: false)
|
add(:hashed_password, :string, null: false)
|
||||||
|
add(:role, :string, null: false)
|
||||||
add(:confirmed_at, :naive_datetime)
|
add(:confirmed_at, :naive_datetime)
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
defmodule Bones73k.Repo.Migrations.AddRoleToUsers do
|
|
||||||
use Ecto.Migration
|
|
||||||
|
|
||||||
alias Bones73k.Accounts.User.RolesEnum
|
|
||||||
|
|
||||||
def up do
|
|
||||||
RolesEnum.create_type()
|
|
||||||
|
|
||||||
alter table(:users) do
|
|
||||||
add(:role, RolesEnum.type(), null: false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def down do
|
|
||||||
alter table(:users) do
|
|
||||||
remove(:role)
|
|
||||||
end
|
|
||||||
|
|
||||||
RolesEnum.drop_type()
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in a new issue