Files
shopify-ai-backup/chat/scripts/migrate-to-database.js
2026-02-09 19:33:00 +00:00

311 lines
9.4 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Migration script - Migrate data from JSON files to database
*/
const fs = require('fs');
const path = require('path');
const { initDatabase, getDatabase, closeDatabase } = require('../src/database/connection');
const { initEncryption } = require('../src/utils/encryption');
const userRepo = require('../src/repositories/userRepository');
const DATA_ROOT = process.env.CHAT_DATA_ROOT || path.join(__dirname, '..', '.data');
const DATABASE_PATH = process.env.DATABASE_PATH || path.join(DATA_ROOT, 'shopify_ai.db');
const DATABASE_ENCRYPTION_KEY = process.env.DATABASE_ENCRYPTION_KEY;
const USERS_FILE = path.join(DATA_ROOT, 'users.json');
const SESSIONS_FILE = path.join(DATA_ROOT, 'user-sessions.json');
const AFFILIATES_FILE = path.join(DATA_ROOT, 'affiliates.json');
async function loadJsonFile(filePath, defaultValue = []) {
try {
const data = fs.readFileSync(filePath, 'utf8');
return JSON.parse(data);
} catch (error) {
if (error.code === 'ENOENT') {
console.log(` File not found: ${filePath}, using default`);
return defaultValue;
}
throw error;
}
}
async function migrateUsers() {
console.log('\n📦 Migrating users...');
const users = await loadJsonFile(USERS_FILE, []);
console.log(` Found ${users.length} users in JSON`);
if (users.length === 0) {
console.log(' No users to migrate');
return { success: 0, failed: 0 };
}
let success = 0;
let failed = 0;
for (const user of users) {
try {
// Check if user already exists
const existing = userRepo.getUserById(user.id);
if (existing) {
console.log(` Skipping existing user: ${user.email}`);
success++;
continue;
}
// Create user in database
userRepo.createUser({
id: user.id,
email: user.email,
name: user.name || null,
passwordHash: user.passwordHash || user.password_hash,
providers: user.providers || [],
emailVerified: user.emailVerified,
verificationToken: user.verificationToken || null,
verificationExpiresAt: user.verificationExpiresAt || null,
plan: user.plan || 'hobby',
billingStatus: user.billingStatus || 'active',
billingEmail: user.billingEmail || user.email
});
console.log(` ✓ Migrated user: ${user.email}`);
success++;
} catch (error) {
console.error(` ✗ Failed to migrate user ${user.email}:`, error.message);
failed++;
}
}
console.log(` Completed: ${success} success, ${failed} failed`);
return { success, failed };
}
async function migrateSessions() {
console.log('\n📦 Migrating sessions...');
const sessions = await loadJsonFile(SESSIONS_FILE, {});
const sessionCount = Object.keys(sessions).length;
console.log(` Found ${sessionCount} sessions in JSON`);
if (sessionCount === 0) {
console.log(' No sessions to migrate');
return { success: 0, failed: 0, expired: 0 };
}
const now = Date.now();
let success = 0;
let failed = 0;
let expired = 0;
const db = getDatabase();
const sessionRepo = require('../src/repositories/sessionRepository');
for (const [token, session] of Object.entries(sessions)) {
try {
// Skip expired sessions
if (session.expiresAt && session.expiresAt <= now) {
expired++;
continue;
}
// Check if user exists
const user = userRepo.getUserById(session.userId);
if (!user) {
console.log(` Skipping session for non-existent user: ${session.userId}`);
failed++;
continue;
}
// Create session in database
sessionRepo.createSession({
id: session.id || require('crypto').randomUUID(),
userId: session.userId,
token: token,
deviceFingerprint: session.deviceFingerprint || null,
ipAddress: session.ipAddress || null,
userAgent: session.userAgent || null,
expiresAt: session.expiresAt,
createdAt: session.createdAt || now,
lastAccessedAt: session.lastAccessedAt || now
});
success++;
} catch (error) {
console.error(` ✗ Failed to migrate session:`, error.message);
failed++;
}
}
console.log(` Completed: ${success} success, ${failed} failed, ${expired} expired`);
return { success, failed, expired };
}
async function migrateAffiliates() {
console.log('\n📦 Migrating affiliates...');
const affiliates = await loadJsonFile(AFFILIATES_FILE, []);
console.log(` Found ${affiliates.length} affiliates in JSON`);
if (affiliates.length === 0) {
console.log(' No affiliates to migrate');
return { success: 0, failed: 0 };
}
let success = 0;
let failed = 0;
const db = getDatabase();
for (const affiliate of affiliates) {
try {
// Check if user exists
const user = userRepo.getUserById(affiliate.userId);
if (!user) {
console.log(` Skipping affiliate for non-existent user: ${affiliate.userId}`);
failed++;
continue;
}
// Insert affiliate
const stmt = db.prepare(`
INSERT INTO affiliates (
id, user_id, codes, earnings, commission_rate,
total_referrals, total_earnings_cents, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
stmt.run(
affiliate.id || require('crypto').randomUUID(),
affiliate.userId,
JSON.stringify(affiliate.codes || []),
JSON.stringify(affiliate.earnings || []),
affiliate.commissionRate || 0.15,
affiliate.totalReferrals || 0,
affiliate.totalEarningsCents || 0,
affiliate.createdAt || Date.now(),
affiliate.updatedAt || Date.now()
);
console.log(` ✓ Migrated affiliate for user: ${user.email}`);
success++;
} catch (error) {
console.error(` ✗ Failed to migrate affiliate:`, error.message);
failed++;
}
}
console.log(` Completed: ${success} success, ${failed} failed`);
return { success, failed };
}
async function createBackup() {
console.log('\n💾 Creating backup of JSON files...');
const backupDir = path.join(DATA_ROOT, `migration_backup_${Date.now()}`);
if (!fs.existsSync(backupDir)) {
fs.mkdirSync(backupDir, { recursive: true });
}
const files = [USERS_FILE, SESSIONS_FILE, AFFILIATES_FILE];
let backedUp = 0;
for (const file of files) {
if (fs.existsSync(file)) {
const fileName = path.basename(file);
const backupPath = path.join(backupDir, fileName);
fs.copyFileSync(file, backupPath);
console.log(` ✓ Backed up: ${fileName}`);
backedUp++;
}
}
console.log(` Created backup in: ${backupDir}`);
console.log(` Backed up ${backedUp} files`);
return backupDir;
}
async function runMigration() {
console.log('🔄 Starting database migration...');
console.log(' Source: JSON files in', DATA_ROOT);
console.log(' Target: Database at', DATABASE_PATH);
// Check if database exists
if (!fs.existsSync(DATABASE_PATH)) {
console.error('❌ Database not found. Please run setup-database.js first.');
process.exit(1);
}
// Initialize encryption
if (!DATABASE_ENCRYPTION_KEY) {
console.error('❌ DATABASE_ENCRYPTION_KEY not set');
process.exit(1);
}
try {
initEncryption(DATABASE_ENCRYPTION_KEY);
console.log('✅ Encryption initialized');
} catch (error) {
console.error('❌ Failed to initialize encryption:', error.message);
process.exit(1);
}
// Initialize database
try {
initDatabase(DATABASE_PATH, { verbose: false });
console.log('✅ Database connected');
} catch (error) {
console.error('❌ Failed to connect to database:', error.message);
process.exit(1);
}
// Create backup
const backupDir = await createBackup();
// Run migrations
const results = {
users: await migrateUsers(),
sessions: await migrateSessions(),
affiliates: await migrateAffiliates()
};
// Close database
closeDatabase();
// Print summary
console.log('\n📊 Migration Summary:');
console.log(' Users:');
console.log(` ✓ Success: ${results.users.success}`);
console.log(` ✗ Failed: ${results.users.failed}`);
console.log(' Sessions:');
console.log(` ✓ Success: ${results.sessions.success}`);
console.log(` ✗ Failed: ${results.sessions.failed}`);
console.log(` ⏰ Expired: ${results.sessions.expired}`);
console.log(' Affiliates:');
console.log(` ✓ Success: ${results.affiliates.success}`);
console.log(` ✗ Failed: ${results.affiliates.failed}`);
const totalSuccess = results.users.success + results.sessions.success + results.affiliates.success;
const totalFailed = results.users.failed + results.sessions.failed + results.affiliates.failed;
console.log('\n Total:');
console.log(` ✓ Success: ${totalSuccess}`);
console.log(` ✗ Failed: ${totalFailed}`);
console.log('\n✅ Migration complete!');
console.log(` Backup created in: ${backupDir}`);
console.log('\nNext steps:');
console.log(' 1. Verify migration: node scripts/verify-migration.js');
console.log(' 2. Test the application with: USE_JSON_DATABASE=1 npm start');
console.log(' 3. Switch to database mode: unset USE_JSON_DATABASE && npm start');
}
// Run migration
runMigration().catch(error => {
console.error('❌ Migration failed:', error);
closeDatabase();
process.exit(1);
});