From 53467cd611cbadaa11fc3ca5b72431feb1639a52 Mon Sep 17 00:00:00 2001 From: qdust41 Date: Tue, 31 Mar 2026 17:12:40 -0400 Subject: [PATCH] slightly changed how files are stored in the bucket and allows post deletion --- lib/mixer/posts/media.ex | 4 + lib/mixer/posts/media_uploader.ex | 2 +- lib/mixer/posts/tweet_like.ex | 4 + .../controllers/upload_controller.ex | 6 +- ...1210904_cascade_delete_tweet_relations.exs | 63 ++++++++++ .../repo/media/20260331210905.json | 106 ++++++++++++++++ .../repo/tweet_likes/20260331210906.json | 113 ++++++++++++++++++ 7 files changed, 295 insertions(+), 3 deletions(-) create mode 100644 priv/repo/migrations/20260331210904_cascade_delete_tweet_relations.exs create mode 100644 priv/resource_snapshots/repo/media/20260331210905.json create mode 100644 priv/resource_snapshots/repo/tweet_likes/20260331210906.json diff --git a/lib/mixer/posts/media.ex b/lib/mixer/posts/media.ex index 439be42..0886fd5 100644 --- a/lib/mixer/posts/media.ex +++ b/lib/mixer/posts/media.ex @@ -11,6 +11,10 @@ defmodule Mixer.Posts.Media do postgres do table "media" repo Mixer.Repo + + references do + reference :tweet, on_delete: :delete + end end typescript do diff --git a/lib/mixer/posts/media_uploader.ex b/lib/mixer/posts/media_uploader.ex index 2318c39..64efb94 100644 --- a/lib/mixer/posts/media_uploader.ex +++ b/lib/mixer/posts/media_uploader.ex @@ -10,7 +10,7 @@ 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.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)) diff --git a/lib/mixer/posts/tweet_like.ex b/lib/mixer/posts/tweet_like.ex index ddcabf7..33bde07 100644 --- a/lib/mixer/posts/tweet_like.ex +++ b/lib/mixer/posts/tweet_like.ex @@ -8,6 +8,10 @@ defmodule Mixer.Posts.TweetLike do postgres do table "tweet_likes" repo Mixer.Repo + + references do + reference :tweet, on_delete: :delete + end end actions do diff --git a/lib/mixer_web/controllers/upload_controller.ex b/lib/mixer_web/controllers/upload_controller.ex index 28fa410..adbae75 100644 --- a/lib/mixer_web/controllers/upload_controller.ex +++ b/lib/mixer_web/controllers/upload_controller.ex @@ -11,15 +11,17 @@ defmodule MixerWeb.UploadController do |> put_status(:unauthorized) |> json(%{error: "authentication required"}) else - scope = %{id: Ash.UUID.generate()} + media_id = Ash.UUID.generate() + scope = %{user_id: actor.id, media_id: media_id} case MediaUploader.store({upload, scope}) do {:ok, file_name} -> - s3_key = "uploads/media/#{scope.id}/#{file_name}" + s3_key = "uploads/media/#{scope.user_id}/#{scope.media_id}/#{file_name}" url = MediaUploader.url({file_name, scope}) Mixer.Posts.Media |> Ash.Changeset.for_create(:upload, %{s3_key: s3_key}, actor: actor) + |> Ash.Changeset.force_change_attribute(:id, media_id) |> Ash.create() |> case do {:ok, media} -> diff --git a/priv/repo/migrations/20260331210904_cascade_delete_tweet_relations.exs b/priv/repo/migrations/20260331210904_cascade_delete_tweet_relations.exs new file mode 100644 index 0000000..1700e58 --- /dev/null +++ b/priv/repo/migrations/20260331210904_cascade_delete_tweet_relations.exs @@ -0,0 +1,63 @@ +defmodule Mixer.Repo.Migrations.CascadeDeleteTweetRelations do + @moduledoc """ + Updates resources based on their most recent snapshots. + + This file was autogenerated with `mix ash_postgres.generate_migrations` + """ + + use Ecto.Migration + + def up do + drop constraint(:tweet_likes, "tweet_likes_tweet_id_fkey") + + alter table(:tweet_likes) do + modify :tweet_id, + references(:tweets, + column: :id, + name: "tweet_likes_tweet_id_fkey", + type: :uuid, + prefix: "public", + on_delete: :delete_all + ) + end + + drop constraint(:media, "media_tweet_id_fkey") + + alter table(:media) do + modify :tweet_id, + references(:tweets, + column: :id, + name: "media_tweet_id_fkey", + type: :uuid, + prefix: "public", + on_delete: :delete_all + ) + end + end + + def down do + drop constraint(:media, "media_tweet_id_fkey") + + alter table(:media) do + modify :tweet_id, + references(:tweets, + column: :id, + name: "media_tweet_id_fkey", + type: :uuid, + prefix: "public" + ) + end + + drop constraint(:tweet_likes, "tweet_likes_tweet_id_fkey") + + alter table(:tweet_likes) do + modify :tweet_id, + references(:tweets, + column: :id, + name: "tweet_likes_tweet_id_fkey", + type: :uuid, + prefix: "public" + ) + end + end +end diff --git a/priv/resource_snapshots/repo/media/20260331210905.json b/priv/resource_snapshots/repo/media/20260331210905.json new file mode 100644 index 0000000..337610d --- /dev/null +++ b/priv/resource_snapshots/repo/media/20260331210905.json @@ -0,0 +1,106 @@ +{ + "attributes": [ + { + "allow_nil?": false, + "default": "fragment(\"gen_random_uuid()\")", + "generated?": false, + "precision": null, + "primary_key?": true, + "references": null, + "scale": null, + "size": null, + "source": "id", + "type": "uuid" + }, + { + "allow_nil?": false, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": null, + "scale": null, + "size": null, + "source": "s3_key", + "type": "text" + }, + { + "allow_nil?": false, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": { + "deferrable": false, + "destination_attribute": "id", + "destination_attribute_default": null, + "destination_attribute_generated": null, + "index?": false, + "match_type": null, + "match_with": null, + "multitenancy": { + "attribute": null, + "global": null, + "strategy": null + }, + "name": "media_user_id_fkey", + "on_delete": null, + "on_update": null, + "primary_key?": true, + "schema": "public", + "table": "users" + }, + "scale": null, + "size": null, + "source": "user_id", + "type": "uuid" + }, + { + "allow_nil?": true, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": { + "deferrable": false, + "destination_attribute": "id", + "destination_attribute_default": null, + "destination_attribute_generated": null, + "index?": false, + "match_type": null, + "match_with": null, + "multitenancy": { + "attribute": null, + "global": null, + "strategy": null + }, + "name": "media_tweet_id_fkey", + "on_delete": "delete", + "on_update": null, + "primary_key?": true, + "schema": "public", + "table": "tweets" + }, + "scale": null, + "size": null, + "source": "tweet_id", + "type": "uuid" + } + ], + "base_filter": null, + "check_constraints": [], + "create_table_options": null, + "custom_indexes": [], + "custom_statements": [], + "has_create_action": true, + "hash": "B9772141860212686745F81D509EBD97BACB9A5A4E7C26A0EB924D6926D1827E", + "identities": [], + "multitenancy": { + "attribute": null, + "global": null, + "strategy": null + }, + "repo": "Elixir.Mixer.Repo", + "schema": null, + "table": "media" +} \ No newline at end of file diff --git a/priv/resource_snapshots/repo/tweet_likes/20260331210906.json b/priv/resource_snapshots/repo/tweet_likes/20260331210906.json new file mode 100644 index 0000000..968cff2 --- /dev/null +++ b/priv/resource_snapshots/repo/tweet_likes/20260331210906.json @@ -0,0 +1,113 @@ +{ + "attributes": [ + { + "allow_nil?": false, + "default": "fragment(\"gen_random_uuid()\")", + "generated?": false, + "precision": null, + "primary_key?": true, + "references": null, + "scale": null, + "size": null, + "source": "id", + "type": "uuid" + }, + { + "allow_nil?": false, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": { + "deferrable": false, + "destination_attribute": "id", + "destination_attribute_default": null, + "destination_attribute_generated": null, + "index?": false, + "match_type": null, + "match_with": null, + "multitenancy": { + "attribute": null, + "global": null, + "strategy": null + }, + "name": "tweet_likes_tweet_id_fkey", + "on_delete": "delete", + "on_update": null, + "primary_key?": true, + "schema": "public", + "table": "tweets" + }, + "scale": null, + "size": null, + "source": "tweet_id", + "type": "uuid" + }, + { + "allow_nil?": false, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": { + "deferrable": false, + "destination_attribute": "id", + "destination_attribute_default": null, + "destination_attribute_generated": null, + "index?": false, + "match_type": null, + "match_with": null, + "multitenancy": { + "attribute": null, + "global": null, + "strategy": null + }, + "name": "tweet_likes_user_id_fkey", + "on_delete": null, + "on_update": null, + "primary_key?": true, + "schema": "public", + "table": "users" + }, + "scale": null, + "size": null, + "source": "user_id", + "type": "uuid" + } + ], + "base_filter": null, + "check_constraints": [], + "create_table_options": null, + "custom_indexes": [], + "custom_statements": [], + "has_create_action": true, + "hash": "A5479A2259477E7040C393810B5805794903152376377FA38E92C119C4947108", + "identities": [ + { + "all_tenants?": false, + "base_filter": null, + "index_name": "tweet_likes_unique_user_tweet_index", + "keys": [ + { + "type": "atom", + "value": "tweet_id" + }, + { + "type": "atom", + "value": "user_id" + } + ], + "name": "unique_user_tweet", + "nils_distinct?": true, + "where": null + } + ], + "multitenancy": { + "attribute": null, + "global": null, + "strategy": null + }, + "repo": "Elixir.Mixer.Repo", + "schema": null, + "table": "tweet_likes" +} \ No newline at end of file