Skip to content

Commit

Permalink
Fix migration tool params called from install.sh (#190)
Browse files Browse the repository at this point in the history
  • Loading branch information
lanedirt committed Dec 24, 2024
1 parent 4d43acb commit a7502d4
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 9 deletions.
16 changes: 12 additions & 4 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1659,7 +1659,7 @@ handle_migrate_db() {
SQLITE_DB_NAME=$(basename "$SQLITE_DB_ABS")

# Get PostgreSQL password from .env file
POSTGRES_PASSWORD=$(grep "^POSTGRES_PASSWORD=" "$ENV_FILE" | cut -d '=' -f2)
POSTGRES_PASSWORD=$(grep "^POSTGRES_PASSWORD=" "$ENV_FILE" | cut -d= -f2-)
if [ -z "$POSTGRES_PASSWORD" ]; then
printf "${RED}Error: POSTGRES_PASSWORD not found in .env file${NC}\n"
exit 1
Expand All @@ -1670,9 +1670,17 @@ handle_migrate_db() {
NETWORK_NAME=$(echo "$NETWORK_NAME" | tr '[:upper:]' '[:lower:]')

printf "\n${YELLOW}Warning: This will migrate data from your SQLite database to PostgreSQL.${NC}\n"
printf "\n"
printf "This is a one-time operation necessary when upgrading from <= 0.9.x to 0.10.0+ and only needs to be run once.\n"
printf "\n"
printf "Source database: ${CYAN}${SQLITE_DB_ABS}${NC}\n"
printf "Target: PostgreSQL database (using connection string from docker-compose.yml)\n"
printf "Make sure you have backed up your data before proceeding.\n"

printf "\n${RED}WARNING: This operation will DELETE ALL EXISTING DATA in the PostgreSQL database.${NC}\n"
printf "${RED}Only proceed if you understand that any current PostgreSQL data will be permanently lost.${NC}\n"
printf "\n"

read -p "Continue with migration? [y/N]: " confirm
if [[ ! $confirm =~ ^[Yy]$ ]]; then
printf "${YELLOW}Migration cancelled.${NC}\n"
Expand Down Expand Up @@ -1705,14 +1713,14 @@ handle_migrate_db() {
docker run --rm \
--network="${NETWORK_NAME}" \
-v "${SQLITE_DB_DIR}:/sqlite" \
${GITHUB_CONTAINER_REGISTRY}-installcli migrate-sqlite "/sqlite/${SQLITE_DB_NAME}" "Host=postgres;Database=aliasvault;Username=aliasvault;Password=${POSTGRES_PASSWORD}"
installcli migrate-sqlite "/sqlite/${SQLITE_DB_NAME}" "Host=postgres;Database=aliasvault;Username=aliasvault;Password=${POSTGRES_PASSWORD}"
else
# Run migration with volume mount using pre-built image
docker run --rm \
--network="${NETWORK_NAME}" \
-v "${SQLITE_DB_DIR}:/sqlite" \
installcli migrate-sqlite "/sqlite/${SQLITE_DB_NAME}" "Host=postgres;Database=aliasvault;Username=aliasvault;Password=${POSTGRES_PASSWORD}"
fi
${GITHUB_CONTAINER_REGISTRY}-installcli:0.10.0 migrate-sqlite "/sqlite/${SQLITE_DB_NAME}" "Host=postgres;Database=aliasvault;Username=aliasvault;Password=${POSTGRES_PASSWORD}"
fi

printf "${GREEN}> Check migration output above for details.${NC}\n"
}
Expand Down
57 changes: 52 additions & 5 deletions src/Utilities/AliasVault.InstallCli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,21 +104,40 @@ private static async Task<int> MigrateSqliteToPostgres(string sqliteDbPath, stri
var optionsBuilderSqlite = new DbContextOptionsBuilder<AliasServerDbContext>()
.UseSqlite(sqliteConnString);

var optionsBuilderPg = new DbContextOptionsBuilder<AliasServerDbContext>()
.UseNpgsql(pgConnString);

// Make sure sqlite is on latest version migration
Console.WriteLine("Update sqlite database to latest version...");
await using var sqliteContext = new AliasServerDbContextSqlite(optionsBuilderSqlite.Options);
await sqliteContext.Database.MigrateAsync();
Console.WriteLine("Updating finished.");

var optionsBuilderPg = new DbContextOptionsBuilder<AliasServerDbContext>()
.UseNpgsql(pgConnString);

// Make sure postgres is on latest version migration
Console.WriteLine("Update postgres database to latest version...");
await using var pgContext = new AliasServerDbContextPostgresql(optionsBuilderPg.Options);
await pgContext.Database.MigrateAsync();
Console.WriteLine("Updating finished.");

Console.WriteLine("Truncating existing tables in reverse dependency order...");

// Truncate tables in reverse order of dependencies
await TruncateTable(pgContext.EmailAttachments, "EmailAttachments");
await TruncateTable(pgContext.Emails, "Emails");
await TruncateTable(pgContext.UserTokens, "UserTokens");
await TruncateTable(pgContext.UserRoles, "UserRoles");
await TruncateTable(pgContext.UserLogin, "UserLogins");
await TruncateTable(pgContext.UserEmailClaims, "UserEmailClaims");
await TruncateTable(pgContext.Vaults, "Vaults");
await TruncateTable(pgContext.UserEncryptionKeys, "UserEncryptionKeys");
await TruncateTable(pgContext.AliasVaultUserRefreshTokens, "AliasVaultUserRefreshTokens");
await TruncateTable(pgContext.AuthLogs, "AuthLogs");
await TruncateTable(pgContext.DataProtectionKeys, "DataProtectionKeys");
await TruncateTable(pgContext.ServerSettings, "ServerSettings");
await TruncateTable(pgContext.AliasVaultUsers, "AliasVaultUsers");
await TruncateTable(pgContext.AliasVaultRoles, "AliasVaultRoles");
await TruncateTable(pgContext.AdminUsers, "AdminUsers");

Console.WriteLine("Starting content migration...");

// First, migrate tables without foreign key dependencies
Expand Down Expand Up @@ -155,6 +174,25 @@ private static async Task<int> MigrateSqliteToPostgres(string sqliteDbPath, stri
}
}

/// <summary>
/// Truncates a table in the PostgreSQL database.
/// </summary>
/// <typeparam name="T">The entity type of the table being truncated.</typeparam>
/// <param name="table">The database table to truncate.</param>
/// <param name="tableName">The name of the table being truncated (for logging purposes).</param>
/// <returns>A task representing the asynchronous truncation operation.</returns>
private static async Task TruncateTable<T>(DbSet<T> table, string tableName)
where T : class
{
Console.WriteLine($"Truncating table {tableName}...");
var count = await table.CountAsync();
if (count > 0)
{
await table.ExecuteDeleteAsync();
Console.WriteLine($"Removed {count} records from {tableName}");
}
}

/// <summary>
/// Migrates data from one database table to another, handling the transfer in batches.
/// </summary>
Expand Down Expand Up @@ -184,6 +222,15 @@ private static async Task MigrateTable<T>(

if (items.Count > 0)
{
// Remove any existing entries in the destination table
var existingEntries = await destination.ToListAsync();
if (existingEntries.Any())
{
Console.WriteLine($"Removing {existingEntries.Count} existing entries from {tableName}...");
destination.RemoveRange(existingEntries);
await destinationContext.SaveChangesAsync();
}

const int batchSize = 30;
foreach (var batch in items.Chunk(batchSize))
{
Expand Down Expand Up @@ -220,9 +267,9 @@ private static async Task MigrateTable<T>(
}

// Ensure that the amount of records in the source and destination tables match
if (await source.CountAsync() != await destination.CountAsync())
if (await source.CountAsync() > await destination.CountAsync())
{
throw new ArgumentException($"The amount of records in the source and destination tables do not match. Check if the migration is working correctly.");
throw new ArgumentException($"The amount of records in the source is greater than the destination. Check if the migration is working correctly.");
}
}
}

0 comments on commit a7502d4

Please sign in to comment.