Compare commits
2 Commits
a926733f1b
...
bd0f5d52c2
| Author | SHA1 | Date | |
|---|---|---|---|
| bd0f5d52c2 | |||
| 874fec835d |
@@ -1186,12 +1186,12 @@ function App() {
|
||||
{email ? (
|
||||
<>
|
||||
<span className="mx-version" style={{ color: "var(--mx-fg2)" }}>{email}</span>
|
||||
<a className="mx-auth-link" href="/auth/sign-out">Sign out</a>
|
||||
<a className="mx-auth-link" href="/sign-out">Sign out</a>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<a className="mx-auth-link" href="/register">Create account</a>
|
||||
<a className="mx-auth-link" href="/auth/sign-in">Sign in</a>
|
||||
<a className="mx-auth-link" href="/sign-in">Sign in</a>
|
||||
</>
|
||||
)}
|
||||
<span className="mx-version">v0.1.0</span>
|
||||
|
||||
@@ -126,7 +126,17 @@ config :esbuild,
|
||||
args:
|
||||
~w(js/index.tsx js/app.js --bundle --target=es2022 --outdir=../priv/static/assets --external:/fonts/* --external:/images/* --alias:@=. --splitting --format=esm),
|
||||
cd: Path.expand("../assets", __DIR__),
|
||||
env: %{"NODE_PATH" => Enum.join([Path.expand("../deps", __DIR__), Path.expand(Mix.Project.build_path()), Path.expand("../_build/dev", __DIR__)], ":")}
|
||||
env: %{
|
||||
"NODE_PATH" =>
|
||||
Enum.join(
|
||||
[
|
||||
Path.expand("../deps", __DIR__),
|
||||
Path.expand(Mix.Project.build_path()),
|
||||
Path.expand("../_build/dev", __DIR__)
|
||||
],
|
||||
":"
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
# Configure tailwind (the version is required)
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
defmodule Mixer.Accounts do
|
||||
use Ash.Domain, otp_app: :mixer, extensions: [AshTypescript.Rpc, AshAdmin.Domain]
|
||||
|
||||
typescript_rpc do
|
||||
resource Mixer.Accounts.User do
|
||||
rpc_action :read_user, :read
|
||||
end
|
||||
|
||||
resource Mixer.Accounts.Follow do
|
||||
rpc_action :read_follow, :read
|
||||
rpc_action :follow_user, :follow
|
||||
rpc_action :unfollow_user, :unfollow
|
||||
end
|
||||
end
|
||||
|
||||
admin do
|
||||
show? true
|
||||
end
|
||||
@@ -12,15 +24,4 @@ defmodule Mixer.Accounts do
|
||||
|
||||
resource Mixer.Accounts.Follow
|
||||
end
|
||||
|
||||
typescript_rpc do
|
||||
resource Mixer.Accounts.User do
|
||||
rpc_action :read_user, :read
|
||||
end
|
||||
resource Mixer.Accounts.Follow do
|
||||
rpc_action :read_follow, :read
|
||||
rpc_action :follow_user, :follow
|
||||
rpc_action :unfollow_user, :unfollow
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
defmodule Mixer.Accounts.Follow do
|
||||
require Ash.Query
|
||||
|
||||
use Ash.Resource,
|
||||
domain: Mixer.Accounts,
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
@@ -20,25 +21,6 @@ defmodule Mixer.Accounts.Follow do
|
||||
type_name "follows"
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key :id
|
||||
create_timestamp :created_at
|
||||
end
|
||||
|
||||
relationships do
|
||||
belongs_to :follower, Mixer.Accounts.User do
|
||||
primary_key? true
|
||||
allow_nil? false
|
||||
attribute_writable? true
|
||||
end
|
||||
|
||||
belongs_to :following, Mixer.Accounts.User do
|
||||
primary_key? true
|
||||
allow_nil? false
|
||||
attribute_writable? true
|
||||
end
|
||||
end
|
||||
|
||||
actions do
|
||||
defaults [:read, :destroy]
|
||||
|
||||
@@ -48,6 +30,7 @@ defmodule Mixer.Accounts.Follow do
|
||||
upsert_identity :unique_follow
|
||||
accept [:following_id]
|
||||
change relate_actor(:follower)
|
||||
|
||||
validate fn changeset, _context ->
|
||||
follower_id = Ash.Changeset.get_attribute(changeset, :follower_id)
|
||||
following_id = Ash.Changeset.get_attribute(changeset, :following_id)
|
||||
@@ -82,10 +65,6 @@ defmodule Mixer.Accounts.Follow do
|
||||
end
|
||||
end
|
||||
|
||||
identities do
|
||||
identity :unique_follow, [:follower_id, :following_id]
|
||||
end
|
||||
|
||||
policies do
|
||||
policy action_type(:read) do
|
||||
authorize_if always()
|
||||
@@ -99,4 +78,27 @@ defmodule Mixer.Accounts.Follow do
|
||||
authorize_if actor_present()
|
||||
end
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key :id
|
||||
create_timestamp :created_at
|
||||
end
|
||||
|
||||
relationships do
|
||||
belongs_to :follower, Mixer.Accounts.User do
|
||||
primary_key? true
|
||||
allow_nil? false
|
||||
attribute_writable? true
|
||||
end
|
||||
|
||||
belongs_to :following, Mixer.Accounts.User do
|
||||
primary_key? true
|
||||
allow_nil? false
|
||||
attribute_writable? true
|
||||
end
|
||||
end
|
||||
|
||||
identities do
|
||||
identity :unique_follow, [:follower_id, :following_id]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -31,14 +31,21 @@ defmodule Mixer.Accounts.User.Senders.SendMagicLinkEmail do
|
||||
defp body(params) do
|
||||
# NOTE: You may have to change this to match your magic link acceptance URL.
|
||||
link = url(~p"/magic_link/#{params[:token]}")
|
||||
email_template("Your magic link", "Hello, #{params[:email]}!", """
|
||||
<p style="margin:0 0 20px 0;color:#4B5563;font-size:16px;line-height:1.6;">
|
||||
Use the button below to sign in to Mixer. This link is valid for a short time and can only be used once.
|
||||
</p>
|
||||
<p style="margin:0 0 32px 0;color:#4B5563;font-size:16px;line-height:1.6;">
|
||||
If you didn't request this, you can safely ignore this email.
|
||||
</p>
|
||||
""", link, "Sign In to Mixer")
|
||||
|
||||
email_template(
|
||||
"Your magic link",
|
||||
"Hello, #{params[:email]}!",
|
||||
"""
|
||||
<p style="margin:0 0 20px 0;color:#4B5563;font-size:16px;line-height:1.6;">
|
||||
Use the button below to sign in to Mixer. This link is valid for a short time and can only be used once.
|
||||
</p>
|
||||
<p style="margin:0 0 32px 0;color:#4B5563;font-size:16px;line-height:1.6;">
|
||||
If you didn't request this, you can safely ignore this email.
|
||||
</p>
|
||||
""",
|
||||
link,
|
||||
"Sign In to Mixer"
|
||||
)
|
||||
end
|
||||
|
||||
defp email_template(title, greeting, content, button_url, button_label) do
|
||||
|
||||
@@ -22,14 +22,21 @@ defmodule Mixer.Accounts.User.Senders.SendNewUserConfirmationEmail do
|
||||
|
||||
defp body(params) do
|
||||
link = url(~p"/confirm_new_user/#{params[:token]}")
|
||||
email_template("Confirm your email", "Welcome to Mixer!", """
|
||||
<p style="margin:0 0 20px 0;color:#4B5563;font-size:16px;line-height:1.6;">
|
||||
Thanks for signing up. Just one more step — confirm your email address to activate your account.
|
||||
</p>
|
||||
<p style="margin:0 0 32px 0;color:#4B5563;font-size:16px;line-height:1.6;">
|
||||
If you didn't create an account on Mixer, you can safely ignore this email.
|
||||
</p>
|
||||
""", link, "Confirm Email Address")
|
||||
|
||||
email_template(
|
||||
"Confirm your email",
|
||||
"Welcome to Mixer!",
|
||||
"""
|
||||
<p style="margin:0 0 20px 0;color:#4B5563;font-size:16px;line-height:1.6;">
|
||||
Thanks for signing up. Just one more step — confirm your email address to activate your account.
|
||||
</p>
|
||||
<p style="margin:0 0 32px 0;color:#4B5563;font-size:16px;line-height:1.6;">
|
||||
If you didn't create an account on Mixer, you can safely ignore this email.
|
||||
</p>
|
||||
""",
|
||||
link,
|
||||
"Confirm Email Address"
|
||||
)
|
||||
end
|
||||
|
||||
defp email_template(title, greeting, content, button_url, button_label) do
|
||||
|
||||
@@ -22,14 +22,21 @@ defmodule Mixer.Accounts.User.Senders.SendPasswordResetEmail do
|
||||
|
||||
defp body(params) do
|
||||
link = url(~p"/password-reset/#{params[:token]}")
|
||||
email_template("Reset your password", "Password reset request", """
|
||||
<p style="margin:0 0 20px 0;color:#4B5563;font-size:16px;line-height:1.6;">
|
||||
We received a request to reset the password for your Mixer account. Click the button below to choose a new one.
|
||||
</p>
|
||||
<p style="margin:0 0 32px 0;color:#4B5563;font-size:16px;line-height:1.6;">
|
||||
If you didn't request a password reset, you can safely ignore this email — your password will not change.
|
||||
</p>
|
||||
""", link, "Reset My Password")
|
||||
|
||||
email_template(
|
||||
"Reset your password",
|
||||
"Password reset request",
|
||||
"""
|
||||
<p style="margin:0 0 20px 0;color:#4B5563;font-size:16px;line-height:1.6;">
|
||||
We received a request to reset the password for your Mixer account. Click the button below to choose a new one.
|
||||
</p>
|
||||
<p style="margin:0 0 32px 0;color:#4B5563;font-size:16px;line-height:1.6;">
|
||||
If you didn't request a password reset, you can safely ignore this email — your password will not change.
|
||||
</p>
|
||||
""",
|
||||
link,
|
||||
"Reset My Password"
|
||||
)
|
||||
end
|
||||
|
||||
defp email_template(title, greeting, content, button_url, button_label) do
|
||||
|
||||
@@ -3,16 +3,6 @@ defmodule Mixer.Posts do
|
||||
otp_app: :mixer,
|
||||
extensions: [AshTypescript.Rpc, AshAdmin.Domain]
|
||||
|
||||
admin do
|
||||
show? true
|
||||
end
|
||||
|
||||
resources do
|
||||
resource Mixer.Posts.Tweet
|
||||
resource Mixer.Posts.TweetLike
|
||||
resource Mixer.Posts.Media
|
||||
end
|
||||
|
||||
typescript_rpc do
|
||||
resource Mixer.Posts.Tweet do
|
||||
rpc_action :create_tweet, :create
|
||||
@@ -27,4 +17,14 @@ defmodule Mixer.Posts do
|
||||
rpc_action :read_media, :read
|
||||
end
|
||||
end
|
||||
|
||||
admin do
|
||||
show? true
|
||||
end
|
||||
|
||||
resources do
|
||||
resource Mixer.Posts.Tweet
|
||||
resource Mixer.Posts.TweetLike
|
||||
resource Mixer.Posts.Media
|
||||
end
|
||||
end
|
||||
|
||||
@@ -38,6 +38,24 @@ defmodule Mixer.Posts.Media do
|
||||
end
|
||||
end
|
||||
|
||||
policies do
|
||||
policy action_type(:read) do
|
||||
authorize_if always()
|
||||
end
|
||||
|
||||
policy action(:upload) do
|
||||
authorize_if actor_present()
|
||||
end
|
||||
|
||||
policy action(:link_to_tweet) do
|
||||
authorize_if relates_to_actor_via(:user)
|
||||
end
|
||||
|
||||
policy action_type(:destroy) do
|
||||
authorize_if relates_to_actor_via(:user)
|
||||
end
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key :id
|
||||
|
||||
@@ -64,22 +82,4 @@ defmodule Mixer.Posts.Media do
|
||||
public? true
|
||||
end
|
||||
end
|
||||
|
||||
policies do
|
||||
policy action_type(:read) do
|
||||
authorize_if always()
|
||||
end
|
||||
|
||||
policy action(:upload) do
|
||||
authorize_if actor_present()
|
||||
end
|
||||
|
||||
policy action(:link_to_tweet) do
|
||||
authorize_if relates_to_actor_via(:user)
|
||||
end
|
||||
|
||||
policy action_type(:destroy) do
|
||||
authorize_if relates_to_actor_via(:user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,7 +10,8 @@ defmodule Mixer.Posts.MediaUploader do
|
||||
if ext in @extensions, do: :ok, else: {:error, "unsupported file type #{ext}"}
|
||||
end
|
||||
|
||||
def storage_dir(_version, {_file, scope}), do: "uploads/media/#{scope.user_id}/#{scope.media_id}"
|
||||
def storage_dir(_version, {_file, scope}),
|
||||
do: "uploads/media/#{scope.user_id}/#{scope.media_id}"
|
||||
|
||||
def filename(_version, {file, _scope}) do
|
||||
Path.basename(file.file_name, Path.extname(file.file_name))
|
||||
|
||||
@@ -14,10 +14,6 @@ defmodule Mixer.Posts.Tweet do
|
||||
repo Mixer.Repo
|
||||
end
|
||||
|
||||
typescript do
|
||||
type_name "tweets"
|
||||
end
|
||||
|
||||
state_machine do
|
||||
initial_states [:drafted, :posted]
|
||||
default_initial_state :drafted
|
||||
@@ -27,6 +23,10 @@ defmodule Mixer.Posts.Tweet do
|
||||
end
|
||||
end
|
||||
|
||||
typescript do
|
||||
type_name "tweets"
|
||||
end
|
||||
|
||||
actions do
|
||||
defaults [:read, :destroy]
|
||||
|
||||
@@ -36,6 +36,7 @@ defmodule Mixer.Posts.Tweet do
|
||||
argument :media_id, :uuid, allow_nil?: true
|
||||
change relate_actor(:user)
|
||||
change transition_state(:posted)
|
||||
|
||||
change fn changeset, context ->
|
||||
case Ash.Changeset.get_argument(changeset, :media_id) do
|
||||
nil ->
|
||||
@@ -45,7 +46,9 @@ defmodule Mixer.Posts.Tweet do
|
||||
Ash.Changeset.after_action(changeset, fn _changeset, tweet ->
|
||||
Mixer.Posts.Media
|
||||
|> Ash.get!(media_id, authorize?: false)
|
||||
|> Ash.Changeset.for_update(:link_to_tweet, %{tweet_id: tweet.id}, actor: context.actor)
|
||||
|> Ash.Changeset.for_update(:link_to_tweet, %{tweet_id: tweet.id},
|
||||
actor: context.actor
|
||||
)
|
||||
|> Ash.update!()
|
||||
|
||||
{:ok, tweet}
|
||||
@@ -111,6 +114,32 @@ defmodule Mixer.Posts.Tweet do
|
||||
end
|
||||
end
|
||||
|
||||
policies do
|
||||
policy action_type(:read) do
|
||||
authorize_if always()
|
||||
end
|
||||
|
||||
policy action_type(:create) do
|
||||
authorize_if actor_present()
|
||||
end
|
||||
|
||||
policy action(:update) do
|
||||
authorize_if relates_to_actor_via(:user)
|
||||
end
|
||||
|
||||
policy action(:destroy) do
|
||||
authorize_if relates_to_actor_via(:user)
|
||||
end
|
||||
|
||||
policy action(:like) do
|
||||
authorize_if actor_present()
|
||||
end
|
||||
|
||||
policy action(:unlike) do
|
||||
authorize_if actor_present()
|
||||
end
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key :id
|
||||
|
||||
@@ -165,32 +194,6 @@ defmodule Mixer.Posts.Tweet do
|
||||
end
|
||||
end
|
||||
|
||||
policies do
|
||||
policy action_type(:read) do
|
||||
authorize_if always()
|
||||
end
|
||||
|
||||
policy action_type(:create) do
|
||||
authorize_if actor_present()
|
||||
end
|
||||
|
||||
policy action(:update) do
|
||||
authorize_if relates_to_actor_via(:user)
|
||||
end
|
||||
|
||||
policy action(:destroy) do
|
||||
authorize_if relates_to_actor_via(:user)
|
||||
end
|
||||
|
||||
policy action(:like) do
|
||||
authorize_if actor_present()
|
||||
end
|
||||
|
||||
policy action(:unlike) do
|
||||
authorize_if actor_present()
|
||||
end
|
||||
end
|
||||
|
||||
defp ensure_like(_tweet, nil), do: {:error, Ash.Error.Forbidden.exception([])}
|
||||
|
||||
defp ensure_like(tweet, actor) do
|
||||
|
||||
@@ -23,6 +23,20 @@ defmodule Mixer.Posts.TweetLike do
|
||||
end
|
||||
end
|
||||
|
||||
policies do
|
||||
policy action_type(:read) do
|
||||
authorize_if always()
|
||||
end
|
||||
|
||||
policy action(:create) do
|
||||
authorize_if actor_present()
|
||||
end
|
||||
|
||||
policy action_type(:destroy) do
|
||||
authorize_if relates_to_actor_via(:user)
|
||||
end
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key :id
|
||||
|
||||
@@ -52,18 +66,4 @@ defmodule Mixer.Posts.TweetLike do
|
||||
identities do
|
||||
identity :unique_user_tweet, [:tweet_id, :user_id]
|
||||
end
|
||||
|
||||
policies do
|
||||
policy action_type(:read) do
|
||||
authorize_if always()
|
||||
end
|
||||
|
||||
policy action(:create) do
|
||||
authorize_if actor_present()
|
||||
end
|
||||
|
||||
policy action_type(:destroy) do
|
||||
authorize_if relates_to_actor_via(:user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,12 +9,10 @@ defmodule MixerWeb.AuthOverrides do
|
||||
|
||||
# For a complete reference, see https://hexdocs.pm/ash_authentication_phoenix/ui-overrides.html
|
||||
|
||||
# override AshAuthentication.Phoenix.Components.Banner do
|
||||
# set :image_url, "https://media.giphy.com/media/g7GKcSzwQfugw/giphy.gif"
|
||||
# set :text_class, "bg-red-500"
|
||||
# end
|
||||
|
||||
# override AshAuthentication.Phoenix.Components.SignIn do
|
||||
# set :show_banner, false
|
||||
# end
|
||||
override AshAuthentication.Phoenix.Components.Banner do
|
||||
set :image_url, nil
|
||||
set :dark_image_url, nil
|
||||
set :text, "⬡ Mixer"
|
||||
set :text_class, "text-3xl font-bold tracking-tight"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,7 +2,11 @@ defmodule MixerWeb.PageController do
|
||||
use MixerWeb, :controller
|
||||
|
||||
def home(conn, _params) do
|
||||
render(conn, :home)
|
||||
if conn.assigns[:current_user] do
|
||||
redirect(conn, to: ~p"/feed")
|
||||
else
|
||||
render(conn, :home)
|
||||
end
|
||||
end
|
||||
|
||||
def index(conn, _params) do
|
||||
@@ -28,11 +32,11 @@ defmodule MixerWeb.PageController do
|
||||
conn
|
||||
|> put_root_layout(html: {MixerWeb.Layouts, :spa_root})
|
||||
|> render(:index,
|
||||
current_user: conn.assigns[:current_user],
|
||||
media_host: "#{asset_host}/#{bucket}",
|
||||
page: page,
|
||||
tweet_id: tweet_id,
|
||||
user_id: user_id
|
||||
)
|
||||
current_user: conn.assigns[:current_user],
|
||||
media_host: "#{asset_host}/#{bucket}",
|
||||
page: page,
|
||||
tweet_id: tweet_id,
|
||||
user_id: user_id
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<p class="text-base-content/60 text-lg mb-10">A social feed built with Ash & Phoenix.</p>
|
||||
<div class="flex flex-col sm:flex-row gap-3 justify-center">
|
||||
<a href="/register" class="btn btn-primary btn-lg rounded-full px-8">Create account</a>
|
||||
<a href="/auth/sign-in" class="btn btn-ghost btn-lg rounded-full px-8">Sign in</a>
|
||||
<a href="/sign-in" class="btn btn-ghost btn-lg rounded-full px-8">Sign in</a>
|
||||
</div>
|
||||
<p class="mt-8 text-sm text-base-content/40">
|
||||
Already have an account?
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<div id="app"
|
||||
data-current-user-id={if @current_user, do: @current_user.id, else: ""}
|
||||
data-current-user-email={if @current_user, do: @current_user.email, else: ""}
|
||||
data-asset-host={@media_host}
|
||||
data-page={@page}
|
||||
data-tweet-id={@tweet_id || ""}
|
||||
data-user-id={@user_id || ""}>
|
||||
<div
|
||||
id="app"
|
||||
data-current-user-id={if @current_user, do: @current_user.id, else: ""}
|
||||
data-current-user-email={if @current_user, do: @current_user.email, else: ""}
|
||||
data-asset-host={@media_host}
|
||||
data-page={@page}
|
||||
data-tweet-id={@tweet_id || ""}
|
||||
data-user-id={@user_id || ""}
|
||||
>
|
||||
</div>
|
||||
|
||||
13
mix.exs
13
mix.exs
@@ -133,18 +133,21 @@ defmodule Mixer.MixProject do
|
||||
build: [
|
||||
"ash-framework": [
|
||||
# The description tells people how to use this skill.
|
||||
description: "Use this skill working with Ash Framework or any of its extensions. Always consult this when making any domain changes, features or fixes.",
|
||||
description:
|
||||
"Use this skill working with Ash Framework or any of its extensions. Always consult this when making any domain changes, features or fixes.",
|
||||
# Include all Ash dependencies
|
||||
usage_rules: [:ash, ~r/^ash_/]
|
||||
],
|
||||
"phoenix-framework": [
|
||||
description: "Use this skill working with Phoenix Framework. Consult this when working with the web layer, controllers, views, liveviews etc.",
|
||||
description:
|
||||
"Use this skill working with Phoenix Framework. Consult this when working with the web layer, controllers, views, liveviews etc.",
|
||||
# Include all Phoenix dependencies
|
||||
usage_rules: [:phoenix, ~r/^phoenix_/]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
[
|
||||
file: "AGENTS.md",
|
||||
usage_rules: ["usage_rules:all"],
|
||||
@@ -152,11 +155,13 @@ defmodule Mixer.MixProject do
|
||||
location: ".agents/skills",
|
||||
build: [
|
||||
"ash-framework": [
|
||||
description: "Use this skill working with Ash Framework or any of its extensions. Always consult this when making any domain changes, features or fixes.",
|
||||
description:
|
||||
"Use this skill working with Ash Framework or any of its extensions. Always consult this when making any domain changes, features or fixes.",
|
||||
usage_rules: [:ash, ~r/^ash_/]
|
||||
],
|
||||
"phoenix-framework": [
|
||||
description: "Use this skill working with Phoenix Framework. Consult this when working with the web layer, controllers, views, liveviews etc.",
|
||||
description:
|
||||
"Use this skill working with Phoenix Framework. Consult this when working with the web layer, controllers, views, liveviews etc.",
|
||||
usage_rules: [:phoenix, ~r/^phoenix_/]
|
||||
]
|
||||
]
|
||||
|
||||
@@ -18,7 +18,8 @@ defmodule Mixer.Repo.Migrations.SetupPostsAndTweets do
|
||||
name: "tweets_user_id_fkey",
|
||||
type: :uuid,
|
||||
prefix: "public"
|
||||
), null: false
|
||||
),
|
||||
null: false
|
||||
|
||||
add :state, :text, null: false, default: "drafted"
|
||||
end
|
||||
|
||||
@@ -22,7 +22,8 @@ defmodule Mixer.Repo.Migrations.AddPostsMediaS3 do
|
||||
name: "media_tweet_id_fkey",
|
||||
type: :uuid,
|
||||
prefix: "public"
|
||||
), null: false
|
||||
),
|
||||
null: false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@ defmodule Mixer.Repo.Migrations.AddUserIdToMediaAndAllowNullTweetId do
|
||||
name: "media_user_id_fkey",
|
||||
type: :uuid,
|
||||
prefix: "public"
|
||||
), null: false
|
||||
),
|
||||
null: false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@ defmodule Mixer.Repo.Migrations.AddTweetLikes do
|
||||
name: "tweet_likes_tweet_id_fkey",
|
||||
type: :uuid,
|
||||
prefix: "public"
|
||||
), null: false
|
||||
),
|
||||
null: false
|
||||
|
||||
add :user_id,
|
||||
references(:users,
|
||||
@@ -25,7 +26,8 @@ defmodule Mixer.Repo.Migrations.AddTweetLikes do
|
||||
name: "tweet_likes_user_id_fkey",
|
||||
type: :uuid,
|
||||
prefix: "public"
|
||||
), null: false
|
||||
),
|
||||
null: false
|
||||
end
|
||||
|
||||
create unique_index(:tweet_likes, [:tweet_id, :user_id],
|
||||
|
||||
@@ -1,8 +1,29 @@
|
||||
defmodule MixerWeb.PageControllerTest do
|
||||
use MixerWeb.ConnCase
|
||||
|
||||
test "GET /", %{conn: conn} do
|
||||
test "GET / redirects to /feed when logged in", %{conn: conn} do
|
||||
user =
|
||||
Mixer.Accounts.User
|
||||
|> Ash.Changeset.for_create(
|
||||
:register_with_password,
|
||||
%{
|
||||
email: "test@example.com",
|
||||
password: "Password1!",
|
||||
password_confirmation: "Password1!"
|
||||
}, authorize?: false)
|
||||
|> Ash.create!()
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> Plug.Test.init_test_session(%{})
|
||||
|> AshAuthentication.Plug.Helpers.store_in_session(user)
|
||||
|> get(~p"/")
|
||||
|
||||
assert redirected_to(conn) == ~p"/feed"
|
||||
end
|
||||
|
||||
test "GET / renders the home page for unauthenticated users", %{conn: conn} do
|
||||
conn = get(conn, ~p"/")
|
||||
assert html_response(conn, 200) =~ "Peace of mind from prototype to production"
|
||||
assert html_response(conn, 200) =~ "Mixer"
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user