6301f521fd
Campus, Stress Index e Longevity escono dal sito e diventano apps.insanitylab.it, con utenti, sessioni, login e database propri. Deciso da Adriano il 03/09/2026: una repo sola, due applicazioni autosufficienti, cosi' che ognuna resti estraibile. Misura che ha guidato il taglio: le tre aree condividevano col sito SOLO l'identita' - ~170 righe fra auth.ts, middleware.ts e safe-next.ts. Layout, librerie e dati erano gia' disgiunti. Lato piattaforme: - ruoli piattaforme/cliente/trainer/admin, senza quelli redazionali - canAccessPath con default CHIUSO (nel sito era aperto: li' le rotte non elencate erano di chiunque fosse loggato, qui ogni sezione e' di qualcuno) - tutta l'app protetta, si elencano le eccezioni invece dei prefissi protetti - /api risponde 401/403, non un redirect verso una pagina di login - tre database distinti: utenti, dati clinici senza nomi, mappatura coi nomi Lato sito: - ROLES tornano i tre redazionali; via le regole e il login pubblico - il login del pannello RESPINGE un account emigrato invece di aprirgli una sessione: senza, landingFor lo mandava su /admin, il middleware glielo negava, e si formava un ciclo di redirect che sembra un guasto del sito - l'header non mostra piu' un menu delle piattaforme: un link che esce Verificato: sito 20 file / 146 test, piattaforme 26 file / 197 test, entrambi i build passano, e l'app girata davvero in locale - il ruolo piattaforme entra in Campus e viene rimbalzato da Longevity, il cliente il contrario. Non ancora in produzione: manca il record DNS di apps.insanitylab.it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EndnceRA5WnA6rvA5iV9WL
81 lines
3.8 KiB
JavaScript
Executable File
81 lines
3.8 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;
|
|
|
|
for (const u of candidati) {
|
|
if (gia.has(u.username)) {
|
|
console.log(` = ${u.username.padEnd(16)} ${u.role.padEnd(12)} già presente a destinazione, lasciato com'è`);
|
|
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}.`);
|
|
if (!applica) console.log('Niente è stato scritto.');
|