chomens-database-migrator/migrator.js

106 lines
3.5 KiB
JavaScript
Raw Permalink Normal View History

2025-08-16 15:17:55 +07:00
// fun fact: this migration script is written entirely in the github actions vps using micro editor
// so don't expect good code here
const fs = require('fs/promises')
const { Parser } = require('node-sql-parser/build/mariadb');
(async () => {
const file = (await fs.readFile('./dump_file.sql', 'utf8')).toString();
console.log('parsing dump file')
const parser = new Parser()
const { ast } = parser.parse(file) // this takes the longest (for a production database that has ~650,000 rows)
console.log('parsed dump file')
// too lazy to explain this part, i just used the REPL to get what i wanted
// (an array containing the username and the JSON data)
const parsed = ast
.filter(each => each.type === 'insert')
.map(each => each.values)
.map(each => each.values)
.flat()
.map(each => each.value)
.map(line => ({
username: line[0].value
.replaceAll('\n', '\\n')
.replaceAll('\b', '\\b')
.replaceAll('\f', '\\f')
.replaceAll('\r', '\\r')
.replaceAll('\t', '\\t'),
data: JSON.parse(line[1].value.replaceAll('\\"', '"'))
}))
console.log('parsed the parsed dump file (into actually readable things you know)')
let migrated = 'SET GLOBAL max_allowed_packet=107374182400;\n\n'
migrated += 'DROP TABLE IF EXISTS `players`;\n\n'
migrated += "CREATE TABLE IF NOT EXISTS players (" +
"uuid CHAR(36) PRIMARY KEY, " +
"username VARCHAR(32) NOT NULL, " +
"lastSeenTime TIMESTAMP," +
"lastSeenServer VARCHAR(100)" +
");\n"
migrated += "CREATE TABLE IF NOT EXISTS playerIPs (" +
"uuid CHAR(36) NOT NULL, " +
"server VARCHAR(100) NOT NULL, " +
"ip VARCHAR(45) NOT NULL, " +
"PRIMARY KEY (uuid, server), " +
"INDEX idx_ip_server (ip, server), " +
"FOREIGN KEY (uuid) REFERENCES players(uuid) ON DELETE CASCADE" +
");\n\n"
migrated += 'INSERT IGNORE INTO `players` (uuid, username, lastSeenTime, lastSeenServer) VALUES \n'
const now = Date.now()
for (const { username, data } of parsed) {
// it was very spammy to log players that don't have last seen entries so i commented them out
let lastSeenTime = data.lastSeen?.time
if (!lastSeenTime) {
lastSeenTime = now // literally just use the time at migration
// console.log(`${username} doesn't have lastSeen.time`)
}
let lastSeenServer = data.lastSeen?.server
if (!lastSeenServer) {
lastSeenServer = 'play.kaboom.pw:25565'
// console.log(`${username} doesn't have lastSeen.server, i defaulted to ${lastSeenServer}`)
}
migrated += `('${data.uuid}', '${username}', FROM_UNIXTIME(FLOOR(${lastSeenTime} / 1000)), '${lastSeenServer}'),\n`
}
migrated = migrated.substring(0, migrated.length - 2) + ';\n' // removes trailing comma
console.log('done processing players table')
migrated += '\n'
migrated += 'INSERT IGNORE INTO `playerIPs` (uuid, server, ip) VALUES \n'
for (const { username, data } of parsed) {
if (data.ips === undefined) {
console.log(`${username} doesn't have data.ips`)
continue
}
for (const server of Object.keys(data.ips)) {
migrated += `('${data.uuid}', '${server}', '${data.ips[server]}'),\n`
}
}
migrated = migrated.substring(0, migrated.length - 2) + ';\n' // removes trailing comma
console.log('done processing playerIPs table')
migrated += `\n-- migrated on ${new Date()} using migrator made by chayapak\n`
await fs.writeFile('migrated.sql', migrated, 'utf8')
console.log('result SAVED ! thank you.')
})()