Files
Adriano Dal Pastro 13d8cdd137 Il sito lascia le piattaforme: separazione fusa in main
Fuso `feat/split-apps` (fermo dal 03/09 e mai portato in main, mentre il container
delle piattaforme girava gia' da li'): il sito e le piattaforme erano online in due
copie, con database diversi e ruoli divergenti — `nicola` era `admin` di la' e
`piattaforme` di qua, e la promozione di ieri valeva solo su una delle due.

Adesso `insanitylab.it/campus`, `/piattaforme` e `/login` fanno 404: le aree
riservate stanno solo su `piattaforme.tielogic.xyz`. Il pannello `/admin` resta.
Nel database del sito restano `admin` ed `editor`; `nicola` e `adriano` sono usciti.

Il lavoro di oggi e' passato intatto: edizione `settembre-2026` coi cinque
pacchetti, i 73 tag, il countdown al 19/09 e la correzione del sabato. Verificato
sull'immagine nuova prima di metterla, chiedendo la pagina a quattro date.

Tre cose che lo split del 03/09 aveva lasciato indietro, trovate eseguendo:

- **`Dockerfile` del sito copiava ancora `campus-content`**, spostato sotto
  `apps/platforms`: il build falliva. Nel sito non c'era piu' un solo riferimento a
  quella cartella - solo la riga del Dockerfile. Rimossa.
- **`migra-utenti.mjs --togli-dal-sito` era un no-op silenzioso**: il `continue`
  sugli utenti gia' presenti a destinazione saltava anche la cancellazione
  dall'origine. Siccome copiare e ripulire sono due passi separati apposta, l'opzione
  non funzionava proprio nel caso per cui esiste. Ora chi e' gia' di la' esce di qua
  lo stesso, e il riepilogo dice quanti ne ha tolti.
- **`.env` non e' in git**, quindi il merge non lo ha spostato sotto `apps/sito/`.
  L'`up` e' fallito dopo che il `down` aveva gia' tolto il container: **9,7 secondi
  di sito giu'**, misurati. La prova con `docker run` non lo aveva visto perche' non
  legge `env_file`. Il rimedio - `docker compose config` prima dello swap - e' scritto
  nel README del sito, che prima non diceva come si fa un deploy.

Sito 149 test verdi, piattaforme 202.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPKFABRT3y26dFNMquF3CS
2026-09-04 10:18:00 +00:00

89 lines
4.5 KiB
JavaScript
Executable File

#!/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 <file.db> --a <file.db> --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.');