Packages

phoenix_kit

1.7.89
1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib phoenix_kit migrations postgres v36.ex
Raw

lib/phoenix_kit/migrations/postgres/v36.ex

defmodule PhoenixKit.Migrations.Postgres.V36 do
@moduledoc """
PhoenixKit V36 Migration: Connections Module - Social Relationships System
Adds complete social relationships system with follows, connections, and blocking.
## Architecture
Each relationship type has two tables:
- **Main table**: Current state only (one row per user pair)
- **History table**: Activity log of all changes over time
## Changes
### User Follows Table (phoenix_kit_user_follows)
- One-way follow relationships (no consent required)
- Stores only CURRENT follows (row deleted when unfollowed)
- Unique constraint ensures one row per user pair
### User Follows History Table (phoenix_kit_user_follows_history)
- Logs all follow/unfollow events
- Actions: "follow", "unfollow"
- Preserves full audit trail
### User Connections Table (phoenix_kit_user_connections)
- Two-way mutual relationships (requires acceptance)
- Stores only CURRENT status per user pair
- Status: "pending", "accepted" (rejected rows are deleted)
### User Connections History Table (phoenix_kit_user_connections_history)
- Logs all connection events
- Actions: "requested", "accepted", "rejected", "removed"
- Preserves full audit trail
### User Blocks Table (phoenix_kit_user_blocks)
- Blocking prevents all interaction
- Stores only CURRENT blocks (row deleted when unblocked)
- Unique constraint ensures one row per user pair
### User Blocks History Table (phoenix_kit_user_blocks_history)
- Logs all block/unblock events
- Actions: "block", "unblock"
- Preserves full audit trail
## Settings
- connections_enabled: Enable/disable entire module
## Features
- UUIDv7 primary keys for time-sortable IDs
- Comprehensive indexes for efficient queries
- Foreign key constraints for data integrity
- Complete activity history for auditing
"""
use Ecto.Migration
@doc """
Run the V36 migration to add connections system.
"""
def up(%{prefix: prefix} = _opts) do
# Main tables (current state)
create_user_follows_table(prefix)
create_user_connections_table(prefix)
create_user_blocks_table(prefix)
# History tables (activity log)
create_user_follows_history_table(prefix)
create_user_connections_history_table(prefix)
create_user_blocks_history_table(prefix)
seed_settings(prefix)
execute("COMMENT ON TABLE #{prefix_table_name("phoenix_kit", prefix)} IS '36'")
end
@doc """
Rollback the V36 migration.
"""
def down(%{prefix: prefix} = _opts) do
# Drop history tables first (no foreign keys to main tables)
drop_if_exists(table(:phoenix_kit_user_blocks_history, prefix: prefix))
drop_if_exists(table(:phoenix_kit_user_connections_history, prefix: prefix))
drop_if_exists(table(:phoenix_kit_user_follows_history, prefix: prefix))
# Drop main tables
drop_if_exists(table(:phoenix_kit_user_blocks, prefix: prefix))
drop_if_exists(table(:phoenix_kit_user_connections, prefix: prefix))
drop_if_exists(table(:phoenix_kit_user_follows, prefix: prefix))
delete_setting(prefix, "connections_enabled")
execute("COMMENT ON TABLE #{prefix_table_name("phoenix_kit", prefix)} IS '35'")
end
defp create_user_follows_table(prefix) do
create_if_not_exists table(:phoenix_kit_user_follows, primary_key: false, prefix: prefix) do
add(:id, :uuid, primary_key: true)
add(
:follower_id,
references(:phoenix_kit_users, on_delete: :delete_all, prefix: prefix, type: :bigint),
null: false
)
add(
:followed_id,
references(:phoenix_kit_users, on_delete: :delete_all, prefix: prefix, type: :bigint),
null: false
)
add(:inserted_at, :naive_datetime, null: false)
end
create_if_not_exists(index(:phoenix_kit_user_follows, [:follower_id], prefix: prefix))
create_if_not_exists(index(:phoenix_kit_user_follows, [:followed_id], prefix: prefix))
create_if_not_exists(
unique_index(:phoenix_kit_user_follows, [:follower_id, :followed_id],
name: :phoenix_kit_user_follows_unique_idx,
prefix: prefix
)
)
execute("""
COMMENT ON TABLE #{prefix_table_name("phoenix_kit_user_follows", prefix)} IS
'One-way follow relationships (follower follows followed, no consent required)'
""")
end
defp create_user_connections_table(prefix) do
create_if_not_exists table(:phoenix_kit_user_connections, primary_key: false, prefix: prefix) do
add(:id, :uuid, primary_key: true)
add(
:requester_id,
references(:phoenix_kit_users, on_delete: :delete_all, prefix: prefix, type: :bigint),
null: false
)
add(
:recipient_id,
references(:phoenix_kit_users, on_delete: :delete_all, prefix: prefix, type: :bigint),
null: false
)
add(:status, :string, null: false, default: "pending")
add(:requested_at, :naive_datetime, null: false)
add(:responded_at, :naive_datetime)
timestamps(type: :naive_datetime)
end
create_if_not_exists(index(:phoenix_kit_user_connections, [:requester_id], prefix: prefix))
create_if_not_exists(index(:phoenix_kit_user_connections, [:recipient_id], prefix: prefix))
create_if_not_exists(index(:phoenix_kit_user_connections, [:status], prefix: prefix))
create_if_not_exists(
index(:phoenix_kit_user_connections, [:recipient_id, :status],
name: :phoenix_kit_user_connections_recipient_status_idx,
prefix: prefix
)
)
create_if_not_exists(
index(:phoenix_kit_user_connections, [:requester_id, :status],
name: :phoenix_kit_user_connections_requester_status_idx,
prefix: prefix
)
)
execute("""
COMMENT ON TABLE #{prefix_table_name("phoenix_kit_user_connections", prefix)} IS
'Two-way mutual connections (requires acceptance from both parties)'
""")
end
defp create_user_blocks_table(prefix) do
create_if_not_exists table(:phoenix_kit_user_blocks, primary_key: false, prefix: prefix) do
add(:id, :uuid, primary_key: true)
add(
:blocker_id,
references(:phoenix_kit_users, on_delete: :delete_all, prefix: prefix, type: :bigint),
null: false
)
add(
:blocked_id,
references(:phoenix_kit_users, on_delete: :delete_all, prefix: prefix, type: :bigint),
null: false
)
add(:reason, :string)
add(:inserted_at, :naive_datetime, null: false)
end
create_if_not_exists(index(:phoenix_kit_user_blocks, [:blocker_id], prefix: prefix))
create_if_not_exists(index(:phoenix_kit_user_blocks, [:blocked_id], prefix: prefix))
create_if_not_exists(
unique_index(:phoenix_kit_user_blocks, [:blocker_id, :blocked_id],
name: :phoenix_kit_user_blocks_unique_idx,
prefix: prefix
)
)
execute("""
COMMENT ON TABLE #{prefix_table_name("phoenix_kit_user_blocks", prefix)} IS
'User blocking (prevents all interaction between users)'
""")
end
defp seed_settings(prefix) do
insert_setting(prefix, "connections_enabled", "false")
end
defp insert_setting(prefix, key, value) do
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
execute("""
INSERT INTO #{prefix_table_name("phoenix_kit_settings", prefix)}
(key, value, date_added, date_updated)
VALUES ('#{key}', '#{value}', '#{now}', '#{now}')
ON CONFLICT (key) DO NOTHING
""")
end
defp delete_setting(prefix, key) do
execute("""
DELETE FROM #{prefix_table_name("phoenix_kit_settings", prefix)}
WHERE key = '#{key}'
""")
end
# ===== HISTORY TABLES =====
defp create_user_follows_history_table(prefix) do
create_if_not_exists table(:phoenix_kit_user_follows_history,
primary_key: false,
prefix: prefix
) do
add(:id, :uuid, primary_key: true)
add(
:follower_id,
references(:phoenix_kit_users, on_delete: :delete_all, prefix: prefix, type: :bigint),
null: false
)
add(
:followed_id,
references(:phoenix_kit_users, on_delete: :delete_all, prefix: prefix, type: :bigint),
null: false
)
# Action: "follow" or "unfollow"
add(:action, :string, null: false)
add(:inserted_at, :naive_datetime, null: false)
end
create_if_not_exists(index(:phoenix_kit_user_follows_history, [:follower_id], prefix: prefix))
create_if_not_exists(index(:phoenix_kit_user_follows_history, [:followed_id], prefix: prefix))
create_if_not_exists(index(:phoenix_kit_user_follows_history, [:inserted_at], prefix: prefix))
execute("""
COMMENT ON TABLE #{prefix_table_name("phoenix_kit_user_follows_history", prefix)} IS
'Activity log of follow/unfollow events'
""")
end
defp create_user_connections_history_table(prefix) do
create_if_not_exists table(:phoenix_kit_user_connections_history,
primary_key: false,
prefix: prefix
) do
add(:id, :uuid, primary_key: true)
add(
:user_a_id,
references(:phoenix_kit_users, on_delete: :delete_all, prefix: prefix, type: :bigint),
null: false
)
add(
:user_b_id,
references(:phoenix_kit_users, on_delete: :delete_all, prefix: prefix, type: :bigint),
null: false
)
# Who initiated this action
add(
:actor_id,
references(:phoenix_kit_users, on_delete: :delete_all, prefix: prefix, type: :bigint),
null: false
)
# Action: "requested", "accepted", "rejected", "removed"
add(:action, :string, null: false)
add(:inserted_at, :naive_datetime, null: false)
end
create_if_not_exists(
index(:phoenix_kit_user_connections_history, [:user_a_id], prefix: prefix)
)
create_if_not_exists(
index(:phoenix_kit_user_connections_history, [:user_b_id], prefix: prefix)
)
create_if_not_exists(
index(:phoenix_kit_user_connections_history, [:actor_id], prefix: prefix)
)
create_if_not_exists(
index(:phoenix_kit_user_connections_history, [:inserted_at], prefix: prefix)
)
execute("""
COMMENT ON TABLE #{prefix_table_name("phoenix_kit_user_connections_history", prefix)} IS
'Activity log of connection request/accept/reject/remove events'
""")
end
defp create_user_blocks_history_table(prefix) do
create_if_not_exists table(:phoenix_kit_user_blocks_history,
primary_key: false,
prefix: prefix
) do
add(:id, :uuid, primary_key: true)
add(
:blocker_id,
references(:phoenix_kit_users, on_delete: :delete_all, prefix: prefix, type: :bigint),
null: false
)
add(
:blocked_id,
references(:phoenix_kit_users, on_delete: :delete_all, prefix: prefix, type: :bigint),
null: false
)
# Action: "block" or "unblock"
add(:action, :string, null: false)
add(:reason, :string)
add(:inserted_at, :naive_datetime, null: false)
end
create_if_not_exists(index(:phoenix_kit_user_blocks_history, [:blocker_id], prefix: prefix))
create_if_not_exists(index(:phoenix_kit_user_blocks_history, [:blocked_id], prefix: prefix))
create_if_not_exists(index(:phoenix_kit_user_blocks_history, [:inserted_at], prefix: prefix))
execute("""
COMMENT ON TABLE #{prefix_table_name("phoenix_kit_user_blocks_history", prefix)} IS
'Activity log of block/unblock events'
""")
end
defp prefix_table_name(table_name, nil), do: table_name
defp prefix_table_name(table_name, prefix), do: "#{prefix}.#{table_name}"
end