import { isIP } from 'node:net' import { DatabaseSync } from 'node:sqlite' const [command, rawIp, rawLimit, ...noteParts] = process.argv.slice(2) const database = new DatabaseSync('data/game.db') function normalizeIp(value) { const address = String(value ?? '').trim() if (address.startsWith('::ffff:') && isIP(address.slice(7)) === 4) { return address.slice(7) } return address } function requireIp(value) { const address = normalizeIp(value) if (!isIP(address)) { throw new Error('Provide a valid IPv4 or IPv6 address.') } return address } try { if (command === 'set') { const ip = requireIp(rawIp) const maxAccounts = Number(rawLimit) if (!Number.isInteger(maxAccounts) || maxAccounts < 2 || maxAccounts > 100) { throw new Error('The allowed account count must be an integer from 2 to 100.') } const note = noteParts.join(' ').slice(0, 200) database.prepare(` INSERT INTO account_ip_allowances (ip_address, max_accounts, note, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(ip_address) DO UPDATE SET max_accounts = excluded.max_accounts, note = excluded.note, updated_at = CURRENT_TIMESTAMP `).run(ip, maxAccounts, note) console.log(`${ip} may create up to ${maxAccounts} accounts${note ? ` (${note})` : ''}.`) } else if (command === 'remove') { const ip = requireIp(rawIp) const result = database.prepare(` DELETE FROM account_ip_allowances WHERE ip_address = ? `).run(ip) console.log( result.changes ? `${ip} returned to the default one-account limit.` : `${ip} had no custom allowance.`, ) } else if (command === 'list') { const rows = database.prepare(` SELECT account_ip_allowances.ip_address AS ip, account_ip_allowances.max_accounts AS maxAccounts, account_ip_allowances.note, account_ip_allowances.updated_at AS updatedAt, COUNT(accounts.id) AS existingAccounts FROM account_ip_allowances LEFT JOIN accounts ON accounts.created_ip = account_ip_allowances.ip_address GROUP BY account_ip_allowances.ip_address ORDER BY account_ip_allowances.updated_at DESC `).all() console.table(rows) } else { console.log('Usage:') console.log(' npm run accounts:ip -- set [note]') console.log(' npm run accounts:ip -- remove ') console.log(' npm run accounts:ip -- list') process.exitCode = command ? 1 : 0 } } catch (error) { console.error(error instanceof Error ? error.message : error) process.exitCode = 1 } finally { database.close() }