Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 29 additions & 10 deletions lib/phoenix_kit/migrations/postgres.ex
Original file line number Diff line number Diff line change
Expand Up @@ -158,24 +158,43 @@ defmodule PhoenixKit.Migrations.Postgres do
- Configurable strictness: Can log warnings or force re-authentication
- Performance indexes for efficient fingerprint verification

### V24 - File Checksum Unique Index ⚡ LATEST
### V24 - File Checksum Unique Index
- Unique index on phoenix_kit_files.checksum for O(1) duplicate detection
- Enables automatic deduplication of uploaded files
- Prevents redundant storage of identical files
- Improves performance of duplicate file lookups

### V25 - Aspect Ratio Control for Dimensions
- Adds maintain_aspect_ratio boolean column to phoenix_kit_storage_dimensions
- Allows choosing between aspect ratio preservation (width-only) or fixed dimensions
- Per-dimension control for responsive sizing vs exact crops
- Defaults to maintaining aspect ratio for all dimensions

### V26 - Rename Checksum Fields & Per-User Deduplication ⚡ LATEST
- Renames `checksum` to `file_checksum` (clearer naming)
- Removes unique index on file_checksum (allows same file from different users)
- Adds `user_file_checksum` column (SHA256 of user_id + file_checksum)
- Creates unique index on user_file_checksum for per-user duplicate detection
- Same user cannot upload same file twice (enforced by user_file_checksum)
- Different users CAN upload same file (different user_file_checksum values)
- Preserves file_checksum field for popularity analytics across all users
- Clearer naming convention: file_checksum vs user_file_checksum

## Migration Paths

### Fresh Installation (0 → Current)
Runs all migrations V01 through V24 in sequence.
Runs all migrations V01 through V26 in sequence.

### Incremental Updates
- V01 → V24: Runs V02 through V24 in sequence
- V23 → V24: Runs V24 only (adds file checksum unique index)
- V22 → V24: Runs V23 and V24 in sequence
- V20 → V24: Runs V21, V22, V23, and V24 in sequence
- V01 → V26: Runs V02 through V26 in sequence
- V25 → V26: Runs V26 only (adds per-user file hash)
- V24 → V26: Runs V25 and V26 in sequence
- V23 → V26: Runs V24, V25, and V26 in sequence
- V20 → V26: Runs V21 through V26 in sequence

### Rollback Support
- V26 → V25: Removes user_file_checksum, renames file_checksum back to checksum, restores checksum unique index
- V25 → V24: Removes aspect ratio control from dimensions
- V24 → V23: Removes unique index on checksum
- V23 → V22: Removes session fingerprinting columns and indexes
- V22 → V21: Removes audit logging system, email orphaned events, and email metrics
Expand All @@ -193,14 +212,14 @@ defmodule PhoenixKit.Migrations.Postgres do

## Usage Examples

# Update to latest version (V24)
# Update to latest version (V26)
PhoenixKit.Migrations.Postgres.up(prefix: "myapp")

# Update to specific version
PhoenixKit.Migrations.Postgres.up(prefix: "myapp", version: 24)
PhoenixKit.Migrations.Postgres.up(prefix: "myapp", version: 26)

# Rollback to specific version
PhoenixKit.Migrations.Postgres.down(prefix: "myapp", version: 23)
PhoenixKit.Migrations.Postgres.down(prefix: "myapp", version: 25)

# Complete rollback
PhoenixKit.Migrations.Postgres.down(prefix: "myapp", version: 0)
Expand All @@ -218,7 +237,7 @@ defmodule PhoenixKit.Migrations.Postgres do
use Ecto.Migration

@initial_version 1
@current_version 25
@current_version 26
@default_prefix "public"

@doc false
Expand Down
91 changes: 91 additions & 0 deletions lib/phoenix_kit/migrations/postgres/v26.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
defmodule PhoenixKit.Migrations.Postgres.V26 do
@moduledoc """
Migration V26: Rename checksum fields and add per-user deduplication.

This migration renames checksum fields for clarity and adds per-user file deduplication
while preserving the ability to query for popular files across all users.

## Changes
- Enables `pgcrypto` extension for cryptographic functions
- Renames `checksum` column to `file_checksum` (for clarity)
- Drops unique index on `file_checksum` (allows same file from different users)
- Adds `user_file_checksum` column (SHA256 of user_id + file_checksum)
- Creates unique index on `user_file_checksum` to enforce per-user uniqueness
- Backfills existing records with calculated user_file_checksum values

## Requirements
- PostgreSQL with `pgcrypto` extension support (enabled automatically)

## Purpose
- Same user cannot upload the same file twice (duplicate prevention via user_file_checksum)
- Different users CAN upload the same file (no unique constraint on file_checksum)
- Original `file_checksum` field preserved for finding most popular images across all users
- Clearer naming: file_checksum vs user_file_checksum
"""

use Ecto.Migration

def up(%{prefix: prefix} = _opts) do
# Enable pgcrypto extension for digest function
execute "CREATE EXTENSION IF NOT EXISTS pgcrypto"

# Drop the unique index on checksum (from V24)
drop_if_exists unique_index(:phoenix_kit_files, [:checksum], prefix: prefix)

# Rename checksum to file_checksum for clarity
rename table(:phoenix_kit_files, prefix: prefix), :checksum, to: :file_checksum

# Add user_file_checksum column
alter table(:phoenix_kit_files, prefix: prefix) do
add :user_file_checksum, :string
end

# Backfill existing records with user_file_checksum
execute """
UPDATE #{prefix}.phoenix_kit_files
SET user_file_checksum = encode(digest(CAST(user_id AS text) || file_checksum, 'sha256'), 'hex')
WHERE user_file_checksum IS NULL
"""

# Make the column NOT NULL after backfill
alter table(:phoenix_kit_files, prefix: prefix) do
modify :user_file_checksum, :string, null: false
end

# Create unique index on user_file_checksum for fast per-user duplicate detection
create unique_index(:phoenix_kit_files, [:user_file_checksum],
prefix: prefix,
name: "#{prefix}_phoenix_kit_files_user_file_checksum_index"
)

# Set version comment on phoenix_kit table for version tracking
execute "COMMENT ON TABLE #{prefix_table_name("phoenix_kit", prefix)} IS '26'"
end

def down(%{prefix: prefix} = _opts) do
# Drop the user_file_checksum unique index
drop_if_exists unique_index(:phoenix_kit_files, [:user_file_checksum],
prefix: prefix,
name: "#{prefix}_phoenix_kit_files_user_file_checksum_index"
)

# Remove user_file_checksum column
alter table(:phoenix_kit_files, prefix: prefix) do
remove :user_file_checksum
end

# Rename file_checksum back to checksum
rename table(:phoenix_kit_files, prefix: prefix), :file_checksum, to: :checksum

# Restore the unique index on checksum (from V24)
create_if_not_exists unique_index(:phoenix_kit_files, [:checksum], prefix: prefix)

# Update version comment on phoenix_kit table to previous version
execute "COMMENT ON TABLE #{prefix_table_name("phoenix_kit", prefix)} IS '25'"
end

# Helper functions

defp prefix_table_name(table_name, nil), do: table_name
defp prefix_table_name(table_name, prefix), do: "#{prefix}.#{table_name}"
end
Loading
Loading