#!/usr/bin/env node /** * migra-utenti.mjs — porta gli utenti delle aree riservate dal database del sito a quello * di quest'app, dopo la separazione del 03/09/2026. * * COSA FA: copia le righe di `users` con un ruolo di quest'app (piattaforme, cliente, * trainer) da insanitylab.db a piattaforme.db, **conservando l'hash della password** — * chi entrava prima entra dopo, con le stesse credenziali. Le sessioni non si copiano: * vivono in un altro dominio con un altro cookie, quindi chi era dentro rifà il login. * * COME SI USA: * node scripts/migra-utenti.mjs # prova a vuoto (default) * node scripts/migra-utenti.mjs --applica # scrive davvero * node scripts/migra-utenti.mjs --applica --togli-dal-sito # e li toglie dall'origine * opzioni: --da --a --ruoli piattaforme,cliente,trainer * * ⚠️ Un utente già presente a destinazione NON viene toccato: si salta e lo si dice. Una * migrazione che sovrascrive è una migrazione che non si può rilanciare. * ⚠️ `--togli-dal-sito` è l'unica parte distruttiva ed è separata apposta: finché le righe * restano di là non fanno danno (il login del pannello le respinge con un messaggio), e * averle in due posti per qualche giorno è meglio che non averle in nessuno. * * DIPENDE DA: better-sqlite3 (già dipendenza dell'app). */ import Database from 'better-sqlite3'; const argv = process.argv.slice(2); const opt = (nome, def) => { const i = argv.indexOf(nome); return i >= 0 && argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[i + 1] : def; }; const applica = argv.includes('--applica'); const togli = argv.includes('--togli-dal-sito'); const origine = opt('--da', '/opt/docker/insanitylab/data/insanitylab.db'); const destinazione = opt('--a', process.env.DB_PATH ?? '/opt/docker/insanitylab-platforms/data/piattaforme.db'); const ruoli = opt('--ruoli', 'piattaforme,cliente,trainer').split(',').map((r) => r.trim()).filter(Boolean); const src = new Database(origine, { readonly: !togli }); const dst = new Database(destinazione); dst.exec(`CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'cliente' );`); const segnaposto = ruoli.map(() => '?').join(','); const candidati = src.prepare(`SELECT username, password_hash, role FROM users WHERE role IN (${segnaposto}) ORDER BY username`).all(...ruoli); const gia = new Set(dst.prepare('SELECT username FROM users').all().map((r) => r.username)); console.log(`origine ${origine}`); console.log(`destinazione ${destinazione}`); console.log(`ruoli ${ruoli.join(', ')}`); console.log(`modo ${applica ? 'SCRITTURA' : 'prova a vuoto (aggiungi --applica per scrivere)'}\n`); if (candidati.length === 0) { console.log('Nessun utente da migrare: nel database del sito non c\'è nessuna riga con questi ruoli.'); process.exit(0); } const ins = dst.prepare('INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)'); const del = togli ? src.prepare('DELETE FROM users WHERE username = ?') : null; let copiati = 0, saltati = 0, tolti = 0; for (const u of candidati) { if (gia.has(u.username)) { // Già a destinazione: non lo si tocca di là (una migrazione che sovrascrive non si può // rilanciare). Ma `--togli-dal-sito` riguarda l'ORIGINE, e va fatto lo stesso: chi è // arrivato in un giro precedente non ha meno diritto di uscire dal sito di chi arriva // adesso. Fino al 04/09/2026 questo `continue` saltava anche la cancellazione, e // l'opzione diventava un no-op silenzioso appena la copia era già avvenuta — che è il // caso normale, visto che copiare e ripulire sono due passi separati apposta. const esce = togli && applica; console.log(` = ${u.username.padEnd(16)} ${u.role.padEnd(12)} già a destinazione, lasciato com'è${esce ? ' — tolto dal sito' : ''}`); if (esce) { del.run(u.username); tolti++; } saltati++; continue; } console.log(` ${applica ? '+' : '·'} ${u.username.padEnd(16)} ${u.role.padEnd(12)} ${applica ? 'copiato' : 'da copiare'}${togli && applica ? ' e tolto dal sito' : ''}`); if (applica) { ins.run(u.username, u.password_hash, u.role); if (del) del.run(u.username); copiati++; } } console.log(`\n${applica ? `copiati ${copiati}` : `da copiare ${candidati.length - saltati}`}, saltati ${saltati}${togli && applica ? `, tolti dal sito ${tolti + copiati}` : ''}.`); if (!applica) console.log('Niente è stato scritto.');