diff --git a/lib/philomena/filters.ex b/lib/philomena/filters.ex index 363926f9..a5da7964 100644 --- a/lib/philomena/filters.ex +++ b/lib/philomena/filters.ex @@ -141,37 +141,32 @@ defmodule Philomena.Filters do end def recent_and_user_filters(user) do - recent_filter_ids = - [user.current_filter_id | user.recent_filter_ids] - |> Enum.take(10) + sorter = fn f -> + Enum.find_index(user.recent_filter_ids, fn id -> f.id == id end) + end + + mapper = fn %{id: id, name: name} -> + [key: name, value: id] + end + + add_if = fn + list, _label, [] -> list + list, label, entries -> [{label, entries} | list] + end user_filters = - Filter - |> select([f], %{id: f.id, name: f.name, recent: ^"f"}) - |> where(user_id: ^user.id) - |> limit(10) + user.layout.my_filters + |> Enum.sort_by(sorter) + |> Enum.map(mapper) recent_filters = - Filter - |> select([f], %{id: f.id, name: f.name, recent: ^"t"}) - |> where([f], f.id in ^recent_filter_ids) + user.layout.recent_filters + |> Enum.sort_by(sorter) + |> Enum.map(mapper) - union_all(recent_filters, ^user_filters) - |> Repo.all() - |> Enum.sort_by(fn f -> - Enum.find_index(user.recent_filter_ids, fn id -> f.id == id end) - end) - |> Enum.group_by( - fn - %{recent: "t"} -> "Recent Filters" - _user -> "Your Filters" - end, - fn %{id: id, name: name} -> - [key: name, value: id] - end - ) - |> Enum.to_list() - |> Enum.reverse() + [] + |> add_if.("Your Filters", user_filters) + |> add_if.("Recent Filters", recent_filters) end def hide_tag(filter, tag) do diff --git a/lib/philomena/layouts.ex b/lib/philomena/layouts.ex index ccf02ced..349748fe 100644 --- a/lib/philomena/layouts.ex +++ b/lib/philomena/layouts.ex @@ -21,4 +21,24 @@ defmodule Philomena.Layouts do def get_layout! do Repo.one!(Layout) end + + alias Philomena.Layouts.UserLayout + alias Philomena.Users.User + + + @doc """ + Gets a single user_layout. + + ## Examples + + iex> get_user_layout!(%User{}) + %UserLayout{} + + """ + @spec get_user_layout!(User.t()) :: UserLayout.t() + def get_user_layout!(user) do + UserLayout + |> where(user_id: ^user.id) + |> Repo.one!() + end end diff --git a/lib/philomena/layouts/user_layout.ex b/lib/philomena/layouts/user_layout.ex new file mode 100644 index 00000000..37a602d9 --- /dev/null +++ b/lib/philomena/layouts/user_layout.ex @@ -0,0 +1,18 @@ +defmodule Philomena.Layouts.UserLayout do + use Ecto.Schema + + alias Philomena.Users.User + alias Philomena.Users.Role + alias Philomena.Filters.Filter + + @primary_key false + schema "user_layouts" do + field :unread_notification_count, :integer + field :conversation_count, :integer + belongs_to :user, User + + embeds_many :my_filters, Filter + embeds_many :recent_filters, Filter + embeds_many :roles, Role + end +end diff --git a/lib/philomena/users.ex b/lib/philomena/users.ex index a940ce70..148408a5 100644 --- a/lib/philomena/users.ex +++ b/lib/philomena/users.ex @@ -661,16 +661,33 @@ defmodule Philomena.Users do end defp load_with_roles(query) do + query = exclude(query, :select) + + query = + from [user_token, user] in query, + left_join: current_filter in assoc(user, :current_filter), + left_join: forced_filter in assoc(user, :forced_filter), + inner_join: layout in assoc(user, :layout), + preload: [ + user: { + user, + current_filter: current_filter, + forced_filter: forced_filter, + layout: layout + } + ], + select: user_token + query |> Repo.one() - |> Repo.preload([:roles, :current_filter]) + |> Map.fetch!(:user) |> setup_roles() end defp setup_roles(nil), do: nil defp setup_roles(user) do - role_map = Map.new(user.roles, &{&1.resource_type || &1.name, &1.name}) + role_map = Map.new(user.layout.roles, &{&1.resource_type || &1.name, &1.name}) %{user | role_map: role_map} end diff --git a/lib/philomena/users/user.ex b/lib/philomena/users/user.ex index 3a4ee253..8d884749 100644 --- a/lib/philomena/users/user.ex +++ b/lib/philomena/users/user.ex @@ -20,6 +20,7 @@ defmodule Philomena.Users.User do alias Philomena.UserIps.UserIp alias Philomena.Bans.User, as: UserBan alias Philomena.Donations.Donation + alias Philomena.Layouts.UserLayout @derive {Phoenix.Param, key: :slug} @derive {Inspect, except: [:password]} @@ -37,6 +38,7 @@ defmodule Philomena.Users.User do has_many :bans, UserBan has_many :donations, Donation has_one :commission, Commission + has_one :layout, UserLayout many_to_many :roles, Role, join_through: "users_roles", on_replace: :delete belongs_to :current_filter, Filter diff --git a/lib/philomena_web/plugs/current_filter_plug.ex b/lib/philomena_web/plugs/current_filter_plug.ex index 81ea7184..baa4023d 100644 --- a/lib/philomena_web/plugs/current_filter_plug.ex +++ b/lib/philomena_web/plugs/current_filter_plug.ex @@ -14,10 +14,7 @@ defmodule PhilomenaWeb.CurrentFilterPlug do {filter, forced_filter} = if user do - user = - user - |> Repo.preload([:current_filter, :forced_filter]) - |> maybe_set_default_filter() + user = maybe_set_default_filter(user) {user.current_filter, user.forced_filter} else diff --git a/lib/philomena_web/plugs/layout_plug.ex b/lib/philomena_web/plugs/layout_plug.ex index b70c0cd9..003ecd5b 100644 --- a/lib/philomena_web/plugs/layout_plug.ex +++ b/lib/philomena_web/plugs/layout_plug.ex @@ -9,6 +9,7 @@ defmodule PhilomenaWeb.LayoutPlug do alias Canada.Can alias Philomena.Layouts + alias Philomena.Users.User import Plug.Conn @doc false @@ -29,6 +30,7 @@ defmodule PhilomenaWeb.LayoutPlug do |> assign(:report_count, layout.report_count) |> assign(:forums, visible_forums(user, layout.forums)) |> assign(:site_notices, site_notices(layout.site_notices)) + |> user_assignments(user) end defp visible_forums(user, forum_list) do @@ -40,4 +42,15 @@ defmodule PhilomenaWeb.LayoutPlug do defp site_notices(notice_list) do Enum.sort_by(notice_list, & &1.start_date, Date) end + + @spec user_assignments(Plug.Conn.t(), User.t()) :: Plug.Conn.t() + defp user_assignments(conn, %{layout: layout}) when is_map(layout) do + conn + |> assign(:notification_count, layout.unread_notification_count) + |> assign(:conversation_count, layout.conversation_count) + end + + defp user_assignments(conn, _user) do + conn + end end diff --git a/lib/philomena_web/router.ex b/lib/philomena_web/router.ex index 82675a8c..0de89abb 100644 --- a/lib/philomena_web/router.ex +++ b/lib/philomena_web/router.ex @@ -16,7 +16,6 @@ defmodule PhilomenaWeb.Router do plug PhilomenaWeb.PaginationPlug plug PhilomenaWeb.EnsureUserEnabledPlug plug PhilomenaWeb.CurrentBanPlug - plug PhilomenaWeb.NotificationCountPlug plug PhilomenaWeb.LayoutPlug plug PhilomenaWeb.FilterSelectPlug end diff --git a/priv/repo/migrations/20211108003620_add_layouts.exs b/priv/repo/migrations/20211108003620_add_layouts.exs index 4ec92ad2..dc9b4222 100644 --- a/priv/repo/migrations/20211108003620_add_layouts.exs +++ b/priv/repo/migrations/20211108003620_add_layouts.exs @@ -30,7 +30,28 @@ defmodule Philomena.Repo.Migrations.AddLayouts do forums, site_notices """, - "DROP VIEW layouts" + "DROP VIEW IF EXISTS layouts" + ) + + execute( + """ + CREATE VIEW user_layouts AS + SELECT + u.id AS user_id, + roles.array AS roles, + my_filters.array AS my_filters, + recent_filters.array AS recent_filters, + unread_notification_count.count AS unread_notification_count, + conversation_from_count.count + conversation_to_count.count AS conversation_count + FROM users u + INNER JOIN LATERAL (SELECT array_agg(row_to_json(r.*)) AS array FROM roles r JOIN users_roles ur ON r.id=ur.role_id WHERE ur.user_id=u.id) roles ON 't' + INNER JOIN LATERAL (SELECT array_agg(row_to_json(f)) AS array FROM filters f WHERE f.user_id=u.id LIMIT 10) my_filters ON 't' + INNER JOIN LATERAL (SELECT array_agg(row_to_json(f)) AS array FROM filters f WHERE f.id = ANY(u.recent_filter_ids) LIMIT 10) recent_filters ON 't' + INNER JOIN LATERAL (SELECT COUNT(*) FROM unread_notifications WHERE user_id=u.id) unread_notification_count ON 't' + INNER JOIN LATERAL (SELECT COUNT(*) FROM conversations WHERE from_read='f' AND from_hidden='f' AND from_id=u.id) conversation_from_count ON 't' + INNER JOIN LATERAL (SELECT COUNT(*) FROM conversations WHERE to_read='f' AND to_hidden='f' AND to_id=u.id) conversation_to_count ON 't' + """, + "DROP VIEW IF EXISTS user_layouts" ) end end diff --git a/priv/repo/structure.sql b/priv/repo/structure.sql index 6c0efb90..c706b71c 100644 --- a/priv/repo/structure.sql +++ b/priv/repo/structure.sql @@ -1145,6 +1145,39 @@ CREATE SEQUENCE public.mod_notes_id_seq ALTER SEQUENCE public.mod_notes_id_seq OWNED BY public.mod_notes.id; +-- +-- Name: moderation_logs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.moderation_logs ( + id bigint NOT NULL, + user_id bigint NOT NULL, + body character varying NOT NULL, + subject_path character varying NOT NULL, + type character varying NOT NULL, + created_at timestamp(0) without time zone NOT NULL +); + + +-- +-- Name: moderation_logs_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.moderation_logs_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: moderation_logs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.moderation_logs_id_seq OWNED BY public.moderation_logs.id; + + -- -- Name: notifications; Type: TABLE; Schema: public; Owner: - -- @@ -1840,6 +1873,136 @@ CREATE SEQUENCE public.user_ips_id_seq ALTER SEQUENCE public.user_ips_id_seq OWNED BY public.user_ips.id; +-- +-- Name: users; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.users ( + id integer NOT NULL, + email public.citext DEFAULT ''::character varying NOT NULL, + encrypted_password character varying DEFAULT ''::character varying NOT NULL, + reset_password_token character varying, + reset_password_sent_at timestamp without time zone, + remember_created_at timestamp without time zone, + sign_in_count integer DEFAULT 0 NOT NULL, + current_sign_in_at timestamp without time zone, + last_sign_in_at timestamp without time zone, + current_sign_in_ip inet, + last_sign_in_ip inet, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL, + deleted_at timestamp without time zone, + authentication_token character varying NOT NULL, + name character varying NOT NULL, + slug character varying NOT NULL, + role character varying DEFAULT 'user'::character varying NOT NULL, + description_textile character varying, + avatar character varying, + spoiler_type character varying DEFAULT 'static'::character varying NOT NULL, + theme character varying DEFAULT 'default'::character varying NOT NULL, + images_per_page integer DEFAULT 15 NOT NULL, + show_large_thumbnails boolean DEFAULT true NOT NULL, + show_sidebar_and_watched_images boolean DEFAULT true NOT NULL, + fancy_tag_field_on_upload boolean DEFAULT true NOT NULL, + fancy_tag_field_on_edit boolean DEFAULT true NOT NULL, + fancy_tag_field_in_settings boolean DEFAULT true NOT NULL, + autorefresh_by_default boolean DEFAULT false NOT NULL, + anonymous_by_default boolean DEFAULT false NOT NULL, + comments_newest_first boolean DEFAULT true NOT NULL, + comments_always_jump_to_last boolean DEFAULT false NOT NULL, + comments_per_page integer DEFAULT 20 NOT NULL, + watch_on_reply boolean DEFAULT true NOT NULL, + watch_on_new_topic boolean DEFAULT true NOT NULL, + watch_on_upload boolean DEFAULT true NOT NULL, + messages_newest_first boolean DEFAULT false NOT NULL, + serve_webm boolean DEFAULT false NOT NULL, + no_spoilered_in_watched boolean DEFAULT false NOT NULL, + watched_images_query_str character varying DEFAULT ''::character varying NOT NULL, + watched_images_exclude_str character varying DEFAULT ''::character varying NOT NULL, + forum_posts_count integer DEFAULT 0 NOT NULL, + topic_count integer DEFAULT 0 NOT NULL, + recent_filter_ids integer[] DEFAULT '{}'::integer[] NOT NULL, + unread_notification_ids integer[] DEFAULT '{}'::integer[] NOT NULL, + watched_tag_ids integer[] DEFAULT '{}'::integer[] NOT NULL, + deleted_by_user_id integer, + current_filter_id integer, + failed_attempts integer, + unlock_token character varying, + locked_at timestamp without time zone, + uploads_count integer DEFAULT 0 NOT NULL, + votes_cast_count integer DEFAULT 0 NOT NULL, + comments_posted_count integer DEFAULT 0 NOT NULL, + metadata_updates_count integer DEFAULT 0 NOT NULL, + images_favourited_count integer DEFAULT 0 NOT NULL, + last_donation_at timestamp without time zone, + scratchpad_textile text, + use_centered_layout boolean DEFAULT true NOT NULL, + secondary_role character varying, + hide_default_role boolean DEFAULT false NOT NULL, + personal_title character varying, + show_hidden_items boolean DEFAULT false NOT NULL, + hide_vote_counts boolean DEFAULT false NOT NULL, + hide_advertisements boolean DEFAULT false NOT NULL, + encrypted_otp_secret character varying, + encrypted_otp_secret_iv character varying, + encrypted_otp_secret_salt character varying, + consumed_timestep integer, + otp_required_for_login boolean, + otp_backup_codes character varying[], + last_renamed_at timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, + forced_filter_id bigint, + confirmed_at timestamp(0) without time zone, + senior_staff boolean DEFAULT false, + description character varying, + scratchpad character varying, + bypass_rate_limits boolean DEFAULT false, + scale_large_images character varying(255) DEFAULT 'true'::character varying NOT NULL +); + + +-- +-- Name: users_roles; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.users_roles ( + user_id integer NOT NULL, + role_id integer NOT NULL +); + + +-- +-- Name: user_layouts; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.user_layouts AS + SELECT u.id AS user_id, + roles."array" AS roles, + my_filters."array" AS my_filters, + recent_filters."array" AS recent_filters, + unread_notification_count.count AS unread_notification_count, + (conversation_from_count.count + conversation_to_count.count) AS conversation_count + FROM ((((((public.users u + JOIN LATERAL ( SELECT array_agg(row_to_json(r.*)) AS "array" + FROM (public.roles r + JOIN public.users_roles ur ON ((r.id = ur.role_id))) + WHERE (ur.user_id = u.id)) roles ON (true)) + JOIN LATERAL ( SELECT array_agg(row_to_json(f.*)) AS "array" + FROM public.filters f + WHERE (f.user_id = u.id)) my_filters ON (true)) + JOIN LATERAL ( SELECT array_agg(row_to_json(f.*)) AS "array" + FROM public.filters f + WHERE (f.id = ANY (u.recent_filter_ids))) recent_filters ON (true)) + JOIN LATERAL ( SELECT count(*) AS count + FROM public.unread_notifications + WHERE (unread_notifications.user_id = u.id)) unread_notification_count ON (true)) + JOIN LATERAL ( SELECT count(*) AS count + FROM public.conversations + WHERE ((conversations.from_read = false) AND (conversations.from_hidden = false) AND (conversations.from_id = u.id))) conversation_from_count ON (true)) + JOIN LATERAL ( SELECT count(*) AS count + FROM public.conversations + WHERE ((conversations.to_read = false) AND (conversations.to_hidden = false) AND (conversations.to_id = u.id))) conversation_to_count ON (true)); + + -- -- Name: user_name_changes; Type: TABLE; Schema: public; Owner: - -- @@ -1973,93 +2136,6 @@ CREATE SEQUENCE public.user_whitelists_id_seq ALTER SEQUENCE public.user_whitelists_id_seq OWNED BY public.user_whitelists.id; --- --- Name: users; Type: TABLE; Schema: public; Owner: - --- - -CREATE TABLE public.users ( - id integer NOT NULL, - email public.citext DEFAULT ''::character varying NOT NULL, - encrypted_password character varying DEFAULT ''::character varying NOT NULL, - reset_password_token character varying, - reset_password_sent_at timestamp without time zone, - remember_created_at timestamp without time zone, - sign_in_count integer DEFAULT 0 NOT NULL, - current_sign_in_at timestamp without time zone, - last_sign_in_at timestamp without time zone, - current_sign_in_ip inet, - last_sign_in_ip inet, - created_at timestamp without time zone NOT NULL, - updated_at timestamp without time zone NOT NULL, - deleted_at timestamp without time zone, - authentication_token character varying NOT NULL, - name character varying NOT NULL, - slug character varying NOT NULL, - role character varying DEFAULT 'user'::character varying NOT NULL, - description_textile character varying, - avatar character varying, - spoiler_type character varying DEFAULT 'static'::character varying NOT NULL, - theme character varying DEFAULT 'default'::character varying NOT NULL, - images_per_page integer DEFAULT 15 NOT NULL, - show_large_thumbnails boolean DEFAULT true NOT NULL, - show_sidebar_and_watched_images boolean DEFAULT true NOT NULL, - fancy_tag_field_on_upload boolean DEFAULT true NOT NULL, - fancy_tag_field_on_edit boolean DEFAULT true NOT NULL, - fancy_tag_field_in_settings boolean DEFAULT true NOT NULL, - autorefresh_by_default boolean DEFAULT false NOT NULL, - anonymous_by_default boolean DEFAULT false NOT NULL, - comments_newest_first boolean DEFAULT true NOT NULL, - comments_always_jump_to_last boolean DEFAULT false NOT NULL, - comments_per_page integer DEFAULT 20 NOT NULL, - watch_on_reply boolean DEFAULT true NOT NULL, - watch_on_new_topic boolean DEFAULT true NOT NULL, - watch_on_upload boolean DEFAULT true NOT NULL, - messages_newest_first boolean DEFAULT false NOT NULL, - serve_webm boolean DEFAULT false NOT NULL, - no_spoilered_in_watched boolean DEFAULT false NOT NULL, - watched_images_query_str character varying DEFAULT ''::character varying NOT NULL, - watched_images_exclude_str character varying DEFAULT ''::character varying NOT NULL, - forum_posts_count integer DEFAULT 0 NOT NULL, - topic_count integer DEFAULT 0 NOT NULL, - recent_filter_ids integer[] DEFAULT '{}'::integer[] NOT NULL, - unread_notification_ids integer[] DEFAULT '{}'::integer[] NOT NULL, - watched_tag_ids integer[] DEFAULT '{}'::integer[] NOT NULL, - deleted_by_user_id integer, - current_filter_id integer, - failed_attempts integer, - unlock_token character varying, - locked_at timestamp without time zone, - uploads_count integer DEFAULT 0 NOT NULL, - votes_cast_count integer DEFAULT 0 NOT NULL, - comments_posted_count integer DEFAULT 0 NOT NULL, - metadata_updates_count integer DEFAULT 0 NOT NULL, - images_favourited_count integer DEFAULT 0 NOT NULL, - last_donation_at timestamp without time zone, - scratchpad_textile text, - use_centered_layout boolean DEFAULT true NOT NULL, - secondary_role character varying, - hide_default_role boolean DEFAULT false NOT NULL, - personal_title character varying, - show_hidden_items boolean DEFAULT false NOT NULL, - hide_vote_counts boolean DEFAULT false NOT NULL, - hide_advertisements boolean DEFAULT false NOT NULL, - encrypted_otp_secret character varying, - encrypted_otp_secret_iv character varying, - encrypted_otp_secret_salt character varying, - consumed_timestep integer, - otp_required_for_login boolean, - otp_backup_codes character varying[], - last_renamed_at timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, - forced_filter_id bigint, - confirmed_at timestamp(0) without time zone, - senior_staff boolean DEFAULT false, - description character varying, - scratchpad character varying, - bypass_rate_limits boolean DEFAULT false, - scale_large_images character varying(255) DEFAULT 'true'::character varying NOT NULL -); - - -- -- Name: users_id_seq; Type: SEQUENCE; Schema: public; Owner: - -- @@ -2079,16 +2155,6 @@ CREATE SEQUENCE public.users_id_seq ALTER SEQUENCE public.users_id_seq OWNED BY public.users.id; --- --- Name: users_roles; Type: TABLE; Schema: public; Owner: - --- - -CREATE TABLE public.users_roles ( - user_id integer NOT NULL, - role_id integer NOT NULL -); - - -- -- Name: versions; Type: TABLE; Schema: public; Owner: - -- @@ -2293,6 +2359,13 @@ ALTER TABLE ONLY public.messages ALTER COLUMN id SET DEFAULT nextval('public.mes ALTER TABLE ONLY public.mod_notes ALTER COLUMN id SET DEFAULT nextval('public.mod_notes_id_seq'::regclass); +-- +-- Name: moderation_logs id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.moderation_logs ALTER COLUMN id SET DEFAULT nextval('public.moderation_logs_id_seq'::regclass); + + -- -- Name: notifications id; Type: DEFAULT; Schema: public; Owner: - -- @@ -2652,6 +2725,14 @@ ALTER TABLE ONLY public.mod_notes ADD CONSTRAINT mod_notes_pkey PRIMARY KEY (id); +-- +-- Name: moderation_logs moderation_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.moderation_logs + ADD CONSTRAINT moderation_logs_pkey PRIMARY KEY (id); + + -- -- Name: notifications notifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -4050,6 +4131,41 @@ CREATE INDEX index_vpns_on_ip ON public.vpns USING gist (ip inet_ops); CREATE INDEX intensities_index ON public.images USING btree (se_intensity, sw_intensity, ne_intensity, nw_intensity, average_intensity); +-- +-- Name: moderation_logs_created_at_index; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX moderation_logs_created_at_index ON public.moderation_logs USING btree (created_at); + + +-- +-- Name: moderation_logs_type_created_at_index; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX moderation_logs_type_created_at_index ON public.moderation_logs USING btree (type, created_at); + + +-- +-- Name: moderation_logs_type_index; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX moderation_logs_type_index ON public.moderation_logs USING btree (type); + + +-- +-- Name: moderation_logs_user_id_created_at_index; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX moderation_logs_user_id_created_at_index ON public.moderation_logs USING btree (user_id, created_at); + + +-- +-- Name: moderation_logs_user_id_index; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX moderation_logs_user_id_index ON public.moderation_logs USING btree (user_id); + + -- -- Name: user_tokens_context_token_index; Type: INDEX; Schema: public; Owner: - -- @@ -4880,6 +4996,14 @@ ALTER TABLE ONLY public.image_tag_locks ADD CONSTRAINT image_tag_locks_tag_id_fkey FOREIGN KEY (tag_id) REFERENCES public.tags(id) ON DELETE CASCADE; +-- +-- Name: moderation_logs moderation_logs_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.moderation_logs + ADD CONSTRAINT moderation_logs_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + -- -- Name: user_tokens user_tokens_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -4916,4 +5040,5 @@ INSERT INTO public."schema_migrations" (version) VALUES (20210912171343); INSERT INTO public."schema_migrations" (version) VALUES (20210917190346); INSERT INTO public."schema_migrations" (version) VALUES (20210921025336); INSERT INTO public."schema_migrations" (version) VALUES (20210929181319); +INSERT INTO public."schema_migrations" (version) VALUES (20211107130226); INSERT INTO public."schema_migrations" (version) VALUES (20211108003620);