smoonb 1.0.11 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,79 +1,35 @@
1
- const chalk = require('chalk');
2
1
  const path = require('path');
3
- const { execSync } = require('child_process');
4
- const { extractPasswordFromDbUrl, ensureCleanLink } = require('../../../utils/supabaseLink');
5
- const { cleanDir, countFiles, copyDirSafe } = require('../../../utils/fsExtra');
6
- const { t } = require('../../../i18n');
7
- const ui = require('../../../utils/cliUi');
8
-
9
- /**
10
- * Etapa 10: Migrations Backup (NOVA ETAPA INDEPENDENTE)
11
- */
12
- module.exports = async (context) => {
13
- const { projectId, accessToken, databaseUrl, backupDir } = context;
2
+ const fs = require('fs').promises;
3
+ const os = require('os');
4
+ const { runSupabase } = require('../../../utils/supabaseCli');
5
+ const { runSql } = require('../../restore/pg');
6
+ const { copyDirectoryRecursive } = require('../../restore/utils');
7
+ module.exports = async ({ databaseUrl, accessToken, backupDir, postgresMajor = 17 }, { query = runSql, run = runSupabase } = {}) => {
8
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'smoonb-migrations-'));
9
+ let phase = 'migration_history_check_failed';
14
10
  try {
15
- const getT = global.smoonbI18n?.t || t;
16
- // Reset de link ao projeto de ORIGEM
17
- const dbPassword = extractPasswordFromDbUrl(databaseUrl);
18
- await ensureCleanLink(projectId, accessToken, dbPassword);
19
-
20
- // Limpar migrations local (opcional, mas recomendado para garantir servidor como fonte da verdade)
21
- const migrationsDir = path.join(process.cwd(), 'supabase', 'migrations');
22
- await cleanDir(migrationsDir);
23
- console.log(chalk.white(` - ${getT('backup.steps.migrations.cleaning')}`));
24
-
25
- // Baixar todas as migrations do servidor usando migration fetch
26
- console.log(chalk.white(` - ${getT('backup.steps.migrations.downloading')}`));
27
-
28
- const env = {
29
- ...process.env,
30
- SUPABASE_ACCESS_TOKEN: accessToken
31
- };
32
-
33
- try {
34
- execSync('supabase migration fetch', {
35
- cwd: process.cwd(),
36
- stdio: 'pipe',
37
- encoding: 'utf8',
38
- timeout: 120000,
39
- env
40
- });
41
- } catch (error) {
42
- console.log(chalk.yellow(` ⚠️ ${getT('backup.steps.migrations.fetchError', { message: error.message })}`));
43
- console.log(chalk.yellow(` 💡 ${getT('backup.steps.migrations.fetchTip')}`));
44
- return { success: false };
11
+ const history = await query(databaseUrl, "BEGIN READ ONLY; SET LOCAL statement_timeout = '20s'; SELECT to_regclass('supabase_migrations.schema_migrations') IS NOT NULL; COMMIT;", { postgresMajor });
12
+ if (!['t', 'f'].includes(history.trim())) throw new Error('Unexpected history response');
13
+ let count = 0;
14
+ if (history.trim() === 't') {
15
+ const value = await query(databaseUrl, "BEGIN READ ONLY; SET LOCAL statement_timeout = '20s'; SELECT count(*) FROM supabase_migrations.schema_migrations; COMMIT;", { postgresMajor });
16
+ if (!/^\d+$/.test(value.trim())) throw new Error('Unexpected migration count');
17
+ count = Number(value.trim());
45
18
  }
46
-
47
- // Contar arquivos baixados
48
- const fileCount = await countFiles(migrationsDir);
49
- console.log(chalk.white(` - ${getT('backup.steps.migrations.downloaded', { count: fileCount })}`));
50
-
51
- // Copiar migrations para o backup
52
- const backupMigrationsDir = path.join(backupDir, 'migrations');
53
- const copiedCount = await copyDirSafe(migrationsDir, backupMigrationsDir);
54
- const relativePath = path.relative(process.cwd(), backupMigrationsDir);
55
- console.log(chalk.white(` - ${getT('backup.steps.migrations.copying', { path: relativePath, count: copiedCount })}`));
56
-
57
- if (copiedCount > 0) {
58
- console.log(chalk.green(` ✅ ${getT('backup.steps.migrations.copied', { count: copiedCount })}`));
19
+ if (count === 0) {
20
+ await fs.mkdir(path.join(backupDir, 'migrations'), { recursive: true });
21
+ console.log('Migrations: nenhum histórico registrado no banco; nada a exportar.');
22
+ return { success: true, file_count: 0, reason: history.trim() === 'f' ? 'history_absent' : 'history_empty' };
59
23
  }
60
-
61
- // Usar flag de limpeza do contexto (já foi perguntado no início)
62
- const shouldClean = context?.cleanupFlags?.cleanMigrations || false;
63
-
64
- if (shouldClean) {
65
- await cleanDir(migrationsDir);
66
- ui.hint(` - ${getT('backup.steps.migrations.cleaned')}`);
67
- }
68
-
69
- return {
70
- success: true,
71
- file_count: copiedCount
72
- };
73
- } catch (error) {
74
- const getT = global.smoonbI18n?.t || t;
75
- console.log(chalk.yellow(` ⚠️ ${getT('backup.steps.migrations.error', { message: error.message })}`));
76
- return { success: false };
77
- }
24
+ phase = 'migration_export_failed';
25
+ await fs.mkdir(path.join(root, 'supabase', 'migrations'), { recursive: true });
26
+ await fs.writeFile(path.join(root, 'supabase', 'config.toml'), 'project_id = "smoonb-backup"\n');
27
+ run('supabase', ['migration', 'fetch', '--db-url', databaseUrl, '--workdir', root, '--yes'], { cwd: root, env: { ...process.env, SUPABASE_ACCESS_TOKEN: accessToken }, stdio: 'pipe' });
28
+ const source = path.join(root, 'supabase', 'migrations');
29
+ await copyDirectoryRecursive(source, path.join(backupDir, 'migrations'));
30
+ const fileCount = (await fs.readdir(source)).filter(name => name.endsWith('.sql')).length;
31
+ if (fileCount !== count) throw new Error('Migration export count mismatch');
32
+ return { success: true, file_count: fileCount };
33
+ } catch { return { success: false, reason: phase, action: 'Check database connectivity and access to supabase_migrations.schema_migrations; retry the backup.' }; }
34
+ finally { await fs.rm(root, { recursive: true, force: true }); }
78
35
  };
79
-