402 lines
14 KiB
JavaScript
402 lines
14 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
const Database = require('better-sqlite3');
|
|
const mysql = require('mysql2/promise');
|
|
const path = require('path');
|
|
const dotenv = require('dotenv');
|
|
|
|
dotenv.config({ path: path.join(__dirname, '.env') });
|
|
dotenv.config({ path: path.join(__dirname, '..', '.env') });
|
|
|
|
if (!process.env.DB_PASSWORD) {
|
|
throw new Error('DB_PASSWORD must be set in database/.env or .env');
|
|
}
|
|
|
|
// SQLite database path
|
|
const sqliteDbPath = path.join(__dirname, 'sqlite', 'deathwatch.db');
|
|
|
|
// MariaDB configuration
|
|
const mariadbConfig = {
|
|
host: process.env.DB_HOST || '192.168.1.113',
|
|
user: process.env.DB_USER || 'deathwatch',
|
|
password: process.env.DB_PASSWORD,
|
|
database: process.env.DB_NAME || 'deathwatch',
|
|
port: Number(process.env.DB_PORT || 3307)
|
|
};
|
|
|
|
// Helper function to convert SQLite datetime to MySQL format
|
|
function convertDateTime(dateString) {
|
|
if (!dateString) return null;
|
|
try {
|
|
return new Date(dateString).toISOString().slice(0, 19).replace('T', ' ');
|
|
} catch (error) {
|
|
console.warn(`Warning: Could not convert datetime: ${dateString}`);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function migrateData() {
|
|
let sqliteDb = null;
|
|
let mariadbConnection = null;
|
|
|
|
try {
|
|
console.log('Starting data migration from SQLite to MariaDB...');
|
|
|
|
// Connect to SQLite
|
|
console.log('Connecting to SQLite database...');
|
|
sqliteDb = new Database(sqliteDbPath);
|
|
|
|
// Connect to MariaDB
|
|
console.log('Connecting to MariaDB database...');
|
|
mariadbConnection = await mysql.createConnection(mariadbConfig);
|
|
|
|
// Migrate players table (includes requisition_points and renown_level)
|
|
console.log('Migrating players table...');
|
|
const players = sqliteDb.prepare('SELECT * FROM players').all();
|
|
console.log(`Found ${players.length} players to migrate`);
|
|
|
|
for (const player of players) {
|
|
console.log(` Migrating player: ${player.name}`);
|
|
|
|
// Convert text fields to proper JSON
|
|
const rollerInfo = player.roller_info || '{}';
|
|
const shopInfo = player.shop_info || '{}';
|
|
const tabInfo = player.tab_info || '{}';
|
|
|
|
await mariadbConnection.execute(
|
|
`INSERT INTO players (id, name, roller_info, shop_info, tab_info, pw, pw_hash, created_at, updated_at, requisition_points, renown_level)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
roller_info = VALUES(roller_info),
|
|
shop_info = VALUES(shop_info),
|
|
tab_info = VALUES(tab_info),
|
|
pw = VALUES(pw),
|
|
pw_hash = VALUES(pw_hash),
|
|
updated_at = VALUES(updated_at),
|
|
requisition_points = VALUES(requisition_points),
|
|
renown_level = VALUES(renown_level)`,
|
|
[
|
|
player.id,
|
|
player.name,
|
|
rollerInfo,
|
|
shopInfo,
|
|
tabInfo,
|
|
player.pw || '',
|
|
player.pw_hash || '',
|
|
convertDateTime(player.created_at),
|
|
convertDateTime(player.updated_at),
|
|
player.requisition_points || 0,
|
|
player.renown_level || 'None'
|
|
]
|
|
);
|
|
}
|
|
|
|
// Migrate sessions table
|
|
console.log('Migrating sessions table...');
|
|
const sessions = sqliteDb.prepare('SELECT * FROM sessions').all();
|
|
console.log(`Found ${sessions.length} sessions to migrate`);
|
|
|
|
for (const session of sessions) {
|
|
console.log(` Migrating session: ${session.session_id}`);
|
|
|
|
await mariadbConnection.execute(
|
|
`INSERT INTO sessions (id, session_id, data, expires_at, created_at, updated_at)
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
data = VALUES(data),
|
|
expires_at = VALUES(expires_at),
|
|
updated_at = VALUES(updated_at)`,
|
|
[
|
|
session.id,
|
|
session.session_id,
|
|
session.data || '{}',
|
|
convertDateTime(session.expires_at),
|
|
convertDateTime(session.created_at),
|
|
convertDateTime(session.updated_at)
|
|
]
|
|
);
|
|
}
|
|
|
|
// Migrate shop_items table
|
|
console.log('Migrating shop_items table...');
|
|
const shopItems = sqliteDb.prepare('SELECT * FROM shop_items').all();
|
|
console.log(`Found ${shopItems.length} shop items to migrate`);
|
|
|
|
for (const item of shopItems) {
|
|
console.log(` Migrating shop item: ${item.name}`);
|
|
|
|
await mariadbConnection.execute(
|
|
`INSERT INTO shop_items (id, name, category, requisition_cost, renown_requirement, item_type, stats, source, created_at, updated_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
name = VALUES(name),
|
|
category = VALUES(category),
|
|
requisition_cost = VALUES(requisition_cost),
|
|
renown_requirement = VALUES(renown_requirement),
|
|
item_type = VALUES(item_type),
|
|
stats = VALUES(stats),
|
|
source = VALUES(source),
|
|
updated_at = VALUES(updated_at)`,
|
|
[
|
|
item.id,
|
|
item.name,
|
|
item.category,
|
|
item.requisition_cost,
|
|
item.renown_requirement,
|
|
item.item_type,
|
|
item.stats,
|
|
item.source,
|
|
convertDateTime(item.created_at),
|
|
convertDateTime(item.updated_at)
|
|
]
|
|
);
|
|
}
|
|
|
|
// Migrate player_inventory table
|
|
console.log('Migrating player_inventory table...');
|
|
const playerInventory = sqliteDb.prepare('SELECT * FROM player_inventory').all();
|
|
console.log(`Found ${playerInventory.length} inventory items to migrate`);
|
|
|
|
for (const invItem of playerInventory) {
|
|
console.log(` Migrating inventory item for player ${invItem.player_id}`);
|
|
|
|
await mariadbConnection.execute(
|
|
`INSERT INTO player_inventory (id, player_id, item_id, quantity, acquired_at, transaction_details)
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
quantity = VALUES(quantity),
|
|
transaction_details = VALUES(transaction_details)`,
|
|
[
|
|
invItem.id,
|
|
invItem.player_id,
|
|
invItem.item_id,
|
|
invItem.quantity,
|
|
convertDateTime(invItem.acquired_at),
|
|
invItem.transaction_details
|
|
]
|
|
);
|
|
}
|
|
|
|
// Migrate transactions table
|
|
console.log('Migrating transactions table...');
|
|
const transactions = sqliteDb.prepare('SELECT * FROM transactions').all();
|
|
console.log(`Found ${transactions.length} transactions to migrate`);
|
|
|
|
for (const transaction of transactions) {
|
|
console.log(` Migrating transaction ${transaction.id}`);
|
|
|
|
await mariadbConnection.execute(
|
|
`INSERT INTO transactions (id, player_id, item_id, requisition_cost, previous_rp, new_rp, transaction_date)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
[
|
|
transaction.id,
|
|
transaction.player_id,
|
|
transaction.item_id,
|
|
transaction.requisition_cost,
|
|
transaction.previous_rp,
|
|
transaction.new_rp,
|
|
convertDateTime(transaction.transaction_date)
|
|
]
|
|
);
|
|
}
|
|
|
|
// Migrate armour table
|
|
console.log('Migrating armour table...');
|
|
const armour = sqliteDb.prepare('SELECT * FROM armour').all();
|
|
console.log(`Found ${armour.length} armour items to migrate`);
|
|
|
|
for (const armourItem of armour) {
|
|
console.log(` Migrating armour: ${armourItem.name}`);
|
|
|
|
await mariadbConnection.execute(
|
|
`INSERT INTO armour (id, name, req, renown, category, stats, source, created_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
req = VALUES(req),
|
|
renown = VALUES(renown),
|
|
category = VALUES(category),
|
|
stats = VALUES(stats),
|
|
source = VALUES(source)`,
|
|
[
|
|
armourItem.id,
|
|
armourItem.name,
|
|
armourItem.req,
|
|
armourItem.renown,
|
|
armourItem.category,
|
|
armourItem.stats,
|
|
armourItem.source,
|
|
convertDateTime(armourItem.created_at)
|
|
]
|
|
);
|
|
}
|
|
|
|
// Migrate weapons table
|
|
console.log('Migrating weapons table...');
|
|
const weapons = sqliteDb.prepare('SELECT * FROM weapons').all();
|
|
console.log(`Found ${weapons.length} weapons to migrate`);
|
|
|
|
for (const weapon of weapons) {
|
|
console.log(` Migrating weapon: ${weapon.name}`);
|
|
|
|
await mariadbConnection.execute(
|
|
`INSERT INTO weapons (id, name, req, renown, category, stats, source, created_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
req = VALUES(req),
|
|
renown = VALUES(renown),
|
|
category = VALUES(category),
|
|
stats = VALUES(stats),
|
|
source = VALUES(source)`,
|
|
[
|
|
weapon.id,
|
|
weapon.name,
|
|
weapon.req,
|
|
weapon.renown,
|
|
weapon.category,
|
|
weapon.stats,
|
|
weapon.source,
|
|
convertDateTime(weapon.created_at)
|
|
]
|
|
);
|
|
}
|
|
|
|
// Migrate bestiary table
|
|
console.log('Migrating bestiary table...');
|
|
const bestiary = sqliteDb.prepare('SELECT * FROM bestiary').all();
|
|
console.log(`Found ${bestiary.length} bestiary entries to migrate`);
|
|
|
|
for (const beast of bestiary) {
|
|
console.log(` Migrating bestiary: ${beast.name}`);
|
|
|
|
await mariadbConnection.execute(
|
|
`INSERT INTO bestiary (id, name, book, page, pdf, stats, profile, snippet, created_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
book = VALUES(book),
|
|
page = VALUES(page),
|
|
pdf = VALUES(pdf),
|
|
stats = VALUES(stats),
|
|
profile = VALUES(profile),
|
|
snippet = VALUES(snippet)`,
|
|
[
|
|
beast.id,
|
|
beast.name,
|
|
beast.book,
|
|
beast.page,
|
|
beast.pdf,
|
|
beast.stats,
|
|
beast.profile,
|
|
beast.snippet,
|
|
convertDateTime(beast.created_at)
|
|
]
|
|
);
|
|
}
|
|
|
|
// Migrate rules table
|
|
console.log('Migrating rules table...');
|
|
const rules = sqliteDb.prepare('SELECT * FROM rules').all();
|
|
console.log(`Found ${rules.length} rules to migrate`);
|
|
|
|
for (const rule of rules) {
|
|
console.log(` Migrating rule: ${rule.title}`);
|
|
|
|
// Truncate rule_id if it's too long (max 255 chars)
|
|
const ruleId = rule.rule_id ? rule.rule_id.substring(0, 255) : null;
|
|
// Truncate title if it's too long (max 500 chars)
|
|
const title = rule.title ? rule.title.substring(0, 500) : null;
|
|
|
|
await mariadbConnection.execute(
|
|
`INSERT INTO rules (id, rule_id, title, content, page, source, source_abbr, category, created_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
title = VALUES(title),
|
|
content = VALUES(content),
|
|
page = VALUES(page),
|
|
source = VALUES(source),
|
|
source_abbr = VALUES(source_abbr),
|
|
category = VALUES(category)`,
|
|
[
|
|
rule.id,
|
|
ruleId,
|
|
title,
|
|
rule.content,
|
|
rule.page,
|
|
rule.source,
|
|
rule.source_abbr,
|
|
rule.category,
|
|
convertDateTime(rule.created_at)
|
|
]
|
|
);
|
|
}
|
|
|
|
// Migrate rules_staging table
|
|
console.log('Migrating rules_staging table...');
|
|
const rulesStaging = sqliteDb.prepare('SELECT * FROM rules_staging').all();
|
|
console.log(`Found ${rulesStaging.length} rules_staging entries to migrate`);
|
|
|
|
for (const rule of rulesStaging) {
|
|
console.log(` Migrating rules_staging: ${rule.title}`);
|
|
|
|
await mariadbConnection.execute(
|
|
`INSERT INTO rules_staging (id, title, content, category, page, original_json, created_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
title = VALUES(title),
|
|
content = VALUES(content),
|
|
category = VALUES(category),
|
|
page = VALUES(page),
|
|
original_json = VALUES(original_json)`,
|
|
[
|
|
rule.id,
|
|
rule.title,
|
|
rule.content,
|
|
rule.category,
|
|
rule.page,
|
|
rule.original_json,
|
|
convertDateTime(rule.created_at)
|
|
]
|
|
);
|
|
}
|
|
|
|
console.log('Migration completed successfully!');
|
|
|
|
// Verify migration
|
|
console.log('\nVerifying migration...');
|
|
const [mariadbPlayers] = await mariadbConnection.execute('SELECT COUNT(*) as count FROM players');
|
|
const [mariadbSessions] = await mariadbConnection.execute('SELECT COUNT(*) as count FROM sessions');
|
|
const [mariadbShopItems] = await mariadbConnection.execute('SELECT COUNT(*) as count FROM shop_items');
|
|
const [mariadbInventory] = await mariadbConnection.execute('SELECT COUNT(*) as count FROM player_inventory');
|
|
const [mariadbTransactions] = await mariadbConnection.execute('SELECT COUNT(*) as count FROM transactions');
|
|
const [mariadbArmour] = await mariadbConnection.execute('SELECT COUNT(*) as count FROM armour');
|
|
const [mariadbWeapons] = await mariadbConnection.execute('SELECT COUNT(*) as count FROM weapons');
|
|
const [mariadbBestiary] = await mariadbConnection.execute('SELECT COUNT(*) as count FROM bestiary');
|
|
const [mariadbRules] = await mariadbConnection.execute('SELECT COUNT(*) as count FROM rules');
|
|
const [mariadbRulesStaging] = await mariadbConnection.execute('SELECT COUNT(*) as count FROM rules_staging');
|
|
|
|
console.log(`MariaDB players: ${mariadbPlayers[0].count}`);
|
|
console.log(`MariaDB sessions: ${mariadbSessions[0].count}`);
|
|
console.log(`MariaDB shop_items: ${mariadbShopItems[0].count}`);
|
|
console.log(`MariaDB player_inventory: ${mariadbInventory[0].count}`);
|
|
console.log(`MariaDB transactions: ${mariadbTransactions[0].count}`);
|
|
console.log(`MariaDB armour: ${mariadbArmour[0].count}`);
|
|
console.log(`MariaDB weapons: ${mariadbWeapons[0].count}`);
|
|
console.log(`MariaDB bestiary: ${mariadbBestiary[0].count}`);
|
|
console.log(`MariaDB rules: ${mariadbRules[0].count}`);
|
|
console.log(`MariaDB rules_staging: ${mariadbRulesStaging[0].count}`);
|
|
|
|
} catch (error) {
|
|
console.error('Migration failed:', error);
|
|
process.exit(1);
|
|
} finally {
|
|
if (sqliteDb) {
|
|
sqliteDb.close();
|
|
}
|
|
if (mariadbConnection) {
|
|
await mariadbConnection.end();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Run migration
|
|
migrateData().catch(console.error);
|