13d8cdd137
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
89 lines
4.5 KiB
JavaScript
Executable File
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.');
|