Files
MotoresArgentinosV2/Backend/MotoresArgentinosV2.MigrationTool/Program.cs
2026-01-29 13:43:44 -03:00

211 lines
7.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Data.SqlClient;
using Dapper;
namespace MotoresArgentinosV2.MigrationTool;
class Program
{
// --- CONFIGURACIÓN ---
public const string ConnectionStringAutos = "Server=localhost;Database=autos;Trusted_Connection=True;TrustServerCertificate=True;";
public const string ConnectionStringV2 = "Server=localhost;Database=MotoresV2;Trusted_Connection=True;TrustServerCertificate=True;";
// Fecha de corte para usuarios inactivos
static readonly DateTime FechaCorte = new DateTime(2021, 01, 01);
static async Task Main(string[] args)
{
Console.OutputEncoding = Encoding.UTF8;
while (true)
{
Console.Clear();
Console.WriteLine("==================================================");
Console.WriteLine("🚀 HERRAMIENTA DE MIGRACIÓN MOTORES V2");
Console.WriteLine("==================================================");
Console.WriteLine("1. Migrar USUARIOS (Desde 2021)");
Console.WriteLine("2. Migrar AVISOS DE AUTOS (Activos)");
Console.WriteLine("3. Migrar AVISOS DE MOTOS (Activos)");
Console.WriteLine("4. Salir");
Console.Write("\nSeleccione una opción: ");
var key = Console.ReadLine();
if (key == "1")
{
await RunUserMigration();
Console.WriteLine("\nPresione Enter para volver...");
Console.ReadLine();
}
else if (key == "2")
{
var adMigrator = new AdMigrator(ConnectionStringAutos, ConnectionStringV2);
await adMigrator.ExecuteAsync();
Console.WriteLine("\nPresione Enter para volver...");
Console.ReadLine();
}
else if (key == "3")
{
var adMigrator = new AdMigrator(ConnectionStringAutos, ConnectionStringV2);
await adMigrator.MigrateMotosAsync();
Console.WriteLine("\nPresione Enter para volver...");
Console.ReadLine();
}
else if (key == "4")
{
break;
}
}
}
// --- LÓGICA DE MIGRACIÓN DE USUARIOS (Encapsulada) ---
static async Task RunUserMigration()
{
Console.WriteLine("\n🔍 Iniciando migración de usuarios...");
try
{
using var dbAutos = new SqlConnection(ConnectionStringAutos);
using var dbV2 = new SqlConnection(ConnectionStringV2);
var queryLegacy = @"
SELECT
u.UserName,
m.Email,
m.Password as PasswordHash,
m.PasswordSalt,
u.LastActivityDate,
1 as UserType
FROM aspnet_Users u
INNER JOIN aspnet_Membership m ON u.UserId = m.UserId
WHERE u.LastActivityDate >= @FechaCorte";
var usersLegacy = (await dbAutos.QueryAsync<UserLegacy>(queryLegacy, new { FechaCorte })).ToList();
Console.WriteLine($"✅ Encontrados {usersLegacy.Count} usuarios activos para procesar.");
var processedUsernames = new HashSet<string>();
int migrados = 0;
int saltados = 0;
int passReset = 0;
foreach (var user in usersLegacy)
{
// A. NORMALIZACIÓN
string cleanUsername = NormalizeUsername(user.UserName);
if (cleanUsername.Contains("@"))
{
cleanUsername = cleanUsername.Split('@')[0];
cleanUsername = NormalizeUsername(cleanUsername);
}
string finalUsername = cleanUsername;
int counter = 1;
while (processedUsernames.Contains(finalUsername) || await UserExistsInDb(dbV2, finalUsername))
{
finalUsername = $"{cleanUsername}{counter}";
counter++;
}
processedUsernames.Add(finalUsername);
// B. LÓGICA DE CONTRASEÑAS
bool isPlainText = user.PasswordHash.Length < 20;
string finalHash = user.PasswordHash;
string? finalSalt = user.PasswordSalt;
byte migrationStatus = 0;
if (isPlainText)
{
finalHash = "INVALID_PASSWORD_NEEDS_RESET";
finalSalt = null;
passReset++;
}
// C. INSERCIÓN
var existeEmail = await dbV2.ExecuteScalarAsync<int>(
"SELECT COUNT(1) FROM Users WHERE Email = @Email", new { user.Email });
if (existeEmail == 0)
{
var insertQuery = @"
INSERT INTO Users (
UserName, Email, PasswordHash, PasswordSalt, MigrationStatus,
UserType, CreatedAt, IsMFAEnabled, FirstName, LastName
)
VALUES (
@UserName, @Email, @PasswordHash, @PasswordSalt, @MigrationStatus,
@UserType, @CreatedAt, 0, NULL, NULL
)";
await dbV2.ExecuteAsync(insertQuery, new
{
UserName = finalUsername,
user.Email,
PasswordHash = finalHash,
PasswordSalt = finalSalt,
MigrationStatus = migrationStatus,
user.UserType,
CreatedAt = user.LastActivityDate
});
migrados++;
}
else
{
saltados++;
}
}
Console.WriteLine($"🏁 FIN: {migrados} migrados, {saltados} saltados (email duplicado).");
}
catch (Exception ex)
{
Console.WriteLine($"❌ ERROR: {ex.Message}");
}
}
static async Task<bool> UserExistsInDb(SqlConnection db, string username)
{
var count = await db.ExecuteScalarAsync<int>(
"SELECT COUNT(1) FROM Users WHERE UserName = @username", new { username });
return count > 0;
}
static string NormalizeUsername(string text)
{
if (string.IsNullOrWhiteSpace(text)) return "usuario_desconocido";
text = text.ToLowerInvariant();
var normalizedString = text.Normalize(NormalizationForm.FormD);
var stringBuilder = new StringBuilder();
foreach (var c in normalizedString)
{
if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
stringBuilder.Append(c);
}
text = stringBuilder.ToString().Normalize(NormalizationForm.FormC);
text = Regex.Replace(text, "[^a-z0-9]", "");
if (string.IsNullOrEmpty(text)) return "usuario";
return text;
}
}
public class UserLegacy
{
public string UserName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string PasswordHash { get; set; } = string.Empty;
public string PasswordSalt { get; set; } = string.Empty;
public DateTime LastActivityDate { get; set; }
public int UserType { get; set; }
}