From a80adc7bdfaa1f2140121c7550f0f46769d255ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?PontualTech=20/=20Karl=C3=A3o?= Date: Mon, 27 Apr 2026 15:44:05 -0300 Subject: [PATCH] =?UTF-8?q?fix(saas):=20migration=20robusta=20=E2=80=94=20?= =?UTF-8?q?recria=20state=20e=20anchor=5Fsession=20se=20schema=20legado,?= =?UTF-8?q?=20=C3=ADndices=20user=5Fid=20ap=C3=B3s=20ALTER?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/db.js | 64 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/server/src/db.js b/server/src/db.js index 808b086..6eaf850 100644 --- a/server/src/db.js +++ b/server/src/db.js @@ -59,7 +59,6 @@ db.exec(` FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_media_parent ON media(parent_id); - CREATE INDEX IF NOT EXISTS idx_media_user ON media(user_id); CREATE TABLE IF NOT EXISTS anchor_session ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -101,7 +100,6 @@ db.exec(` FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_shares_expires ON shares(expires_at); - CREATE INDEX IF NOT EXISTS idx_shares_user ON shares(user_id); CREATE TABLE IF NOT EXISTS share_positions ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -133,18 +131,74 @@ try { } } catch (e) { /* ignore */ } -// ===== Migração multi-tenant: adicionar user_id em tabelas existentes ===== -// Idempotente: roda toda startup, só ALTER se coluna não existir +// ===== Migração multi-tenant ===== +// 1) Tabelas que mudaram PRIMARY KEY (state, anchor_session): se schema antigo detectado, +// recriar limpo. Sem perda de dados crítica neste momento (deploy inicial). +function recreateIfLegacy(table, newSchema) { + try { + const cols = db.prepare(`PRAGMA table_info(${table})`).all(); + if (cols.length === 0) return; // tabela ainda não existe (CREATE TABLE pegou o schema novo) + const hasUserId = cols.some(c => c.name === 'user_id'); + const idCol = cols.find(c => c.name === 'id'); + // Schema antigo: id é PRIMARY KEY mas NÃO é AUTOINCREMENT (rowid alias com CHECK constraint) + const isLegacyPK = idCol && idCol.pk === 1 && !cols.some(c => c.type === 'INTEGER' && c.pk && c.name === 'id' && (c.dflt_value || '').toString().includes('AUTO')); + if (!hasUserId || isLegacyPK) { + console.log(`[migration] recreating ${table} (legacy schema detected)`); + db.exec(`DROP TABLE IF EXISTS ${table}; ${newSchema}`); + } + } catch (e) { console.warn(`[migration] recreate ${table}:`, e.message); } +} + +recreateIfLegacy('state', ` + CREATE TABLE state ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL DEFAULT 1, + data TEXT NOT NULL, + updated_at INTEGER NOT NULL, + UNIQUE(user_id), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE + ); +`); +recreateIfLegacy('anchor_session', ` + CREATE TABLE anchor_session ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL DEFAULT 1, + active INTEGER NOT NULL DEFAULT 0, + boat_name TEXT, + anchor_lat REAL, + anchor_lng REAL, + radius INTEGER, + started_at INTEGER, + last_heartbeat INTEGER, + last_lat REAL, + last_lng REAL, + last_distance REAL, + alarm_fired INTEGER DEFAULT 0, + UNIQUE(user_id), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE + ); +`); + +// 2) Tabelas que só ganharam coluna user_id: ALTER TABLE ADD COLUMN function ensureUserIdColumn(table) { try { const cols = db.prepare(`PRAGMA table_info(${table})`).all(); + if (cols.length === 0) return; if (!cols.some(c => c.name === 'user_id')) { db.exec(`ALTER TABLE ${table} ADD COLUMN user_id INTEGER NOT NULL DEFAULT 1`); console.log(`[migration] added user_id to ${table}`); } } catch (e) { console.warn(`[migration] ${table}:`, e.message); } } -['state', 'media', 'anchor_session', 'alarm_log', 'shares', 'audit_log'].forEach(ensureUserIdColumn); +['media', 'alarm_log', 'shares', 'audit_log'].forEach(ensureUserIdColumn); + +// 3) Índices em user_id rodam DEPOIS do ALTER TABLE (senão "no such column") +try { + db.exec(` + CREATE INDEX IF NOT EXISTS idx_media_user ON media(user_id); + CREATE INDEX IF NOT EXISTS idx_shares_user ON shares(user_id); + `); +} catch (e) { console.warn('[migration] user_id indexes:', e.message); } // Garante user default (id=1, Karlão) — donos de dados pré-multi-tenant function ensureDefaultUser() {