Packages
phoenix_kit
1.7.208
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
Current section
Files
lib/phoenix_kit/migrations/postgres/v01.ex
defmodule PhoenixKit.Migrations.Postgres.V01 do
@moduledoc false
use Ecto.Migration
alias PhoenixKit.Migrations.Postgres.Helpers
def up(%{create_schema: create?, prefix: prefix} = opts) do
%{quoted_prefix: quoted} = opts
# Only attempt schema creation when the schema is actually missing.
# Postgres checks the CREATE-on-database privilege BEFORE the
# IF-NOT-EXISTS short-circuit, so executing the statement against a
# pre-created schema fails for low-privilege roles that own the
# schema but not the database.
if prefix != "public" && !schema_exists?(prefix) do
if create? do
execute("CREATE SCHEMA IF NOT EXISTS #{quoted}")
else
raise """
PhoenixKit: schema #{inspect(prefix)} does not exist and the migration
was called with create_schema: false.
Create it as a privileged role first:
CREATE SCHEMA #{prefix} AUTHORIZATION your_app_role;
or drop the create_schema: false option to let the migration create it.
"""
end
end
# Create citext extension if not exists (skips the statement — and its
# privilege check — when the extension is already installed)
Helpers.ensure_extension!("citext")
# Create version tracking table (phoenix_kit)
create_if_not_exists table(:phoenix_kit, primary_key: false, prefix: prefix) do
add :id, :serial, primary_key: true
add :version, :integer, null: false
add :migrated_at, :naive_datetime, null: false, default: fragment("NOW()")
end
create_if_not_exists unique_index(:phoenix_kit, [:version], prefix: prefix)
# Create users table (phoenix_kit_users)
create_if_not_exists table(:phoenix_kit_users, primary_key: false, prefix: prefix) do
add :id, :bigserial, primary_key: true
add :email, :citext, null: false
add :hashed_password, :string, null: false
add :first_name, :string, size: 100
add :last_name, :string, size: 100
add :is_active, :boolean, default: true, null: false
add :confirmed_at, :naive_datetime
timestamps(type: :naive_datetime)
end
create_if_not_exists unique_index(:phoenix_kit_users, [:email], prefix: prefix)
# Create tokens table (phoenix_kit_users_tokens)
create_if_not_exists table(:phoenix_kit_users_tokens, primary_key: false, prefix: prefix) do
add :id, :bigserial, primary_key: true
add :user_id, references(:phoenix_kit_users, on_delete: :delete_all, prefix: prefix),
null: false
add :token, :binary, null: false
add :context, :string, null: false
add :sent_to, :string
timestamps(updated_at: false, type: :naive_datetime)
end
create_if_not_exists index(:phoenix_kit_users_tokens, [:user_id], prefix: prefix)
create_if_not_exists unique_index(:phoenix_kit_users_tokens, [:context, :token],
prefix: prefix
)
# Create user roles table (phoenix_kit_user_roles)
create_if_not_exists table(:phoenix_kit_user_roles, primary_key: false, prefix: prefix) do
add :id, :bigserial, primary_key: true
add :name, :string, size: 50, null: false
add :description, :text
add :is_system_role, :boolean, default: false, null: false
timestamps(type: :naive_datetime)
end
create_if_not_exists unique_index(:phoenix_kit_user_roles, [:name], prefix: prefix)
# Create user role assignments table (phoenix_kit_user_role_assignments)
create_if_not_exists table(:phoenix_kit_user_role_assignments,
primary_key: false,
prefix: prefix
) do
add :id, :bigserial, primary_key: true
add :user_id, references(:phoenix_kit_users, on_delete: :delete_all, prefix: prefix),
null: false
add :role_id, references(:phoenix_kit_user_roles, on_delete: :delete_all, prefix: prefix),
null: false
add :assigned_by, references(:phoenix_kit_users, on_delete: :nilify_all, prefix: prefix)
add :assigned_at, :naive_datetime, null: false, default: fragment("NOW()")
add :is_active, :boolean, default: true, null: false
timestamps(updated_at: false, type: :naive_datetime)
end
create_if_not_exists index(:phoenix_kit_user_role_assignments, [:user_id], prefix: prefix)
create_if_not_exists index(:phoenix_kit_user_role_assignments, [:role_id], prefix: prefix)
create_if_not_exists index(:phoenix_kit_user_role_assignments, [:assigned_by], prefix: prefix)
create_if_not_exists unique_index(:phoenix_kit_user_role_assignments, [:user_id, :role_id],
prefix: prefix
)
# Performance optimization indexes for active role queries
create_if_not_exists index(:phoenix_kit_user_role_assignments, [:user_id, :is_active],
prefix: prefix,
name: :idx_user_role_assignments_user_active
)
create_if_not_exists index(:phoenix_kit_user_role_assignments, [:role_id, :is_active],
prefix: prefix,
name: :idx_user_role_assignments_role_active
)
create_if_not_exists index(:phoenix_kit_users, [:is_active],
prefix: prefix,
name: :idx_users_active
)
# Insert system roles
execute """
INSERT INTO #{inspect(prefix)}.phoenix_kit_user_roles (name, description, is_system_role, inserted_at, updated_at)
VALUES
('Owner', 'System owner with full access', true, NOW(), NOW()),
('Admin', 'Administrator with elevated privileges', true, NOW(), NOW()),
('User', 'Standard user with basic access', true, NOW(), NOW())
ON CONFLICT (name) DO NOTHING
"""
# Role assignment is handled by Elixir application logic
# The ensure_first_user_is_owner/1 function in PhoenixKit.Users.Roles
# manages Owner/User role assignment with proper race condition protection
# Set version comment on phoenix_kit table for version tracking
execute "COMMENT ON TABLE #{prefix_table_name("phoenix_kit", prefix)} IS '1'"
end
# Helper function to build table name with prefix
defp prefix_table_name(table_name, nil), do: table_name
defp prefix_table_name(table_name, prefix), do: "#{prefix}.#{table_name}"
# Immediate check (repo().query/3 executes outside the migration
# command queue) — safe here because nothing queued before V01 could
# create the schema.
defp schema_exists?(prefix) do
query = "SELECT EXISTS (SELECT FROM information_schema.schemata WHERE schema_name = $1)"
case repo().query(query, [prefix], log: false) do
{:ok, %{rows: [[true]]}} -> true
_ -> false
end
end
def down(%{prefix: prefix}) do
# Drop tables in correct order (foreign key dependencies)
drop_if_exists table(:phoenix_kit_user_role_assignments, prefix: prefix)
drop_if_exists table(:phoenix_kit_user_roles, prefix: prefix)
drop_if_exists table(:phoenix_kit_users_tokens, prefix: prefix)
drop_if_exists table(:phoenix_kit_users, prefix: prefix)
drop_if_exists table(:phoenix_kit, prefix: prefix)
end
end