smoonb 1.0.6 → 1.0.8
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.
- package/CHANGELOG.md +44 -0
- package/README.md +679 -644
- package/README.pt-BR.md +679 -644
- package/package.json +68 -66
- package/src/commands/backup/index.js +8 -2
- package/src/commands/backup/steps/03-database-separated.js +161 -114
- package/src/commands/backup/steps/06-realtime-settings.js +19 -33
- package/src/commands/backup/steps/07-storage.js +7 -12
- package/src/commands/backup/steps/09-edge-functions.js +76 -202
- package/src/commands/restore/auth-fields.json +237 -0
- package/src/commands/restore/dump.js +284 -0
- package/src/commands/restore/index.js +4 -126
- package/src/commands/restore/management.js +74 -0
- package/src/commands/restore/pg.js +83 -0
- package/src/commands/restore/runner.js +117 -0
- package/src/commands/restore/steps/03-database.js +18 -84
- package/src/commands/restore/steps/04-edge-functions.js +95 -122
- package/src/commands/restore/steps/05-auth-settings.js +26 -54
- package/src/commands/restore/steps/06-storage.js +203 -584
- package/src/commands/restore/steps/07-database-settings.js +87 -69
- package/src/commands/restore/steps/08-realtime-settings.js +61 -53
- package/src/i18n/locales/en.json +800 -796
- package/src/i18n/locales/pt-BR.json +799 -795
- package/src/utils/pg-dump-diagnostics.js +64 -0
- package/src/utils/supabaseCli.js +32 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { t } = require('../../i18n');
|
|
4
|
+
const { buildRestore, readDump } = require('./dump');
|
|
5
|
+
const { storagePlan } = require('./steps/06-storage');
|
|
6
|
+
const steps = {
|
|
7
|
+
database: require('./steps/03-database'),
|
|
8
|
+
edgeFunctions: require('./steps/04-edge-functions'),
|
|
9
|
+
authSettings: require('./steps/05-auth-settings'),
|
|
10
|
+
storage: require('./steps/06-storage'),
|
|
11
|
+
databaseSettings: require('./steps/07-database-settings'),
|
|
12
|
+
realtimeSettings: require('./steps/08-realtime-settings'),
|
|
13
|
+
};
|
|
14
|
+
async function runRestore(
|
|
15
|
+
{
|
|
16
|
+
selectedBackup,
|
|
17
|
+
components,
|
|
18
|
+
targetProject,
|
|
19
|
+
processDir,
|
|
20
|
+
postgresMajor,
|
|
21
|
+
remapUrls = true,
|
|
22
|
+
},
|
|
23
|
+
implementations = steps,
|
|
24
|
+
) {
|
|
25
|
+
if (selectedBackup.projectId === targetProject.targetProjectId)
|
|
26
|
+
throw new Error('Restore requires a different, empty destination project');
|
|
27
|
+
const context = {
|
|
28
|
+
backupPath: selectedBackup.path,
|
|
29
|
+
backupFilePath: path.join(selectedBackup.path, selectedBackup.backupFile),
|
|
30
|
+
targetProject,
|
|
31
|
+
targetDatabaseUrl: targetProject.targetDatabaseUrl,
|
|
32
|
+
sourceProjectId: selectedBackup.projectId,
|
|
33
|
+
targetProjectId: targetProject.targetProjectId,
|
|
34
|
+
postgresMajor,
|
|
35
|
+
remapUrls,
|
|
36
|
+
};
|
|
37
|
+
const report = {
|
|
38
|
+
process: 'restore',
|
|
39
|
+
smoonb_version: require('../../../package.json').version,
|
|
40
|
+
created_at: new Date().toISOString(),
|
|
41
|
+
source_project_id: selectedBackup.projectId,
|
|
42
|
+
target_project_id: targetProject.targetProjectId,
|
|
43
|
+
status: 'running',
|
|
44
|
+
components,
|
|
45
|
+
results: {},
|
|
46
|
+
remap_service_urls: remapUrls,
|
|
47
|
+
notes: [
|
|
48
|
+
'Local source files and backup archives are preserved.',
|
|
49
|
+
'Verification covers selected components; application login and workflows require acceptance testing.',
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
const save = () =>
|
|
53
|
+
fs.writeFileSync(
|
|
54
|
+
path.join(processDir, 'report.json'),
|
|
55
|
+
JSON.stringify(report, null, 2),
|
|
56
|
+
);
|
|
57
|
+
save();
|
|
58
|
+
let current = 'preflight';
|
|
59
|
+
try {
|
|
60
|
+
if (targetProject.targetDatabaseUrl) {
|
|
61
|
+
const url = new URL(targetProject.targetDatabaseUrl);
|
|
62
|
+
const hosted =
|
|
63
|
+
url.hostname.endsWith('.supabase.co') ||
|
|
64
|
+
url.hostname.endsWith('.supabase.com');
|
|
65
|
+
if (
|
|
66
|
+
hosted &&
|
|
67
|
+
url.hostname !== `db.${targetProject.targetProjectId}.supabase.co` &&
|
|
68
|
+
!decodeURIComponent(url.username).endsWith(
|
|
69
|
+
`.${targetProject.targetProjectId}`,
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
throw new Error(
|
|
73
|
+
'Database connection does not match the destination project',
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
if (
|
|
77
|
+
targetProject.targetUrl &&
|
|
78
|
+
new URL(targetProject.targetUrl).hostname !==
|
|
79
|
+
`${targetProject.targetProjectId}.supabase.co`
|
|
80
|
+
)
|
|
81
|
+
throw new Error('API URL does not match the destination project');
|
|
82
|
+
// Parse and validate inventories before the first mutation.
|
|
83
|
+
if (components.database)
|
|
84
|
+
buildRestore(readDump(context.backupFilePath), context);
|
|
85
|
+
if (components.storage)
|
|
86
|
+
storagePlan(context.backupPath, context.backupFilePath);
|
|
87
|
+
for (const [name, step] of Object.entries(implementations)) {
|
|
88
|
+
if (!components[name]) continue;
|
|
89
|
+
current = name;
|
|
90
|
+
report.results[name] = { success: false, status: 'running' };
|
|
91
|
+
save();
|
|
92
|
+
const labels = {
|
|
93
|
+
database: 'restoringDatabase', edgeFunctions: 'restoringEdgeFunctions',
|
|
94
|
+
authSettings: 'restoringAuthSettings', storage: 'restoringStorageBuckets',
|
|
95
|
+
databaseSettings: 'restoringDatabaseSettings', realtimeSettings: 'restoringRealtimeSettings',
|
|
96
|
+
};
|
|
97
|
+
console.log((global.smoonbI18n?.t || t)(`restore.index.${labels[name]}`));
|
|
98
|
+
const result = await step(context);
|
|
99
|
+
if (!result?.success || !result?.verified)
|
|
100
|
+
throw new Error(`Component ${name} did not pass verification`);
|
|
101
|
+
report.results[name] = { ...result, status: 'verified' };
|
|
102
|
+
save();
|
|
103
|
+
}
|
|
104
|
+
report.status = 'verified';
|
|
105
|
+
report.completed_at = new Date().toISOString();
|
|
106
|
+
save();
|
|
107
|
+
return report.results;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
report.status = 'failed';
|
|
110
|
+
report.failed_component = current;
|
|
111
|
+
report.results[current] = { success: false, status: 'failed' };
|
|
112
|
+
report.completed_at = new Date().toISOString();
|
|
113
|
+
save();
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
module.exports = { runRestore };
|
|
@@ -1,84 +1,18 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
console.log(chalk.white(` - ${getT('restore.steps.database.detectedGz')}`));
|
|
20
|
-
console.log(chalk.white(` - ${getT('restore.steps.database.extractingGz')}`));
|
|
21
|
-
|
|
22
|
-
const unzipCmd = [
|
|
23
|
-
'docker run --rm',
|
|
24
|
-
`-v "${backupDirAbs}:/host"`,
|
|
25
|
-
'postgres:17 gunzip /host/' + fileName
|
|
26
|
-
].join(' ');
|
|
27
|
-
|
|
28
|
-
execSync(unzipCmd, { stdio: 'pipe' });
|
|
29
|
-
uncompressedFile = fileName.replace('.gz', '');
|
|
30
|
-
console.log(chalk.white(` - ${getT('restore.steps.database.uncompressed', { file: uncompressedFile })}`));
|
|
31
|
-
} else if (fileName.endsWith('.backup')) {
|
|
32
|
-
console.log(chalk.white(` - ${getT('restore.steps.database.detectedBackup')}`));
|
|
33
|
-
console.log(chalk.white(` - ${getT('restore.steps.database.proceeding')}`));
|
|
34
|
-
} else {
|
|
35
|
-
throw new Error(`Formato de arquivo inválido. Esperado .backup.gz ou .backup, recebido: ${fileName}`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Extrair credenciais da URL de conexão
|
|
39
|
-
const urlMatch = targetDatabaseUrl.match(/postgresql:\/\/([^@:]+):([^@]+)@(.+)$/);
|
|
40
|
-
|
|
41
|
-
if (!urlMatch) {
|
|
42
|
-
const getT = global.smoonbI18n?.t || t;
|
|
43
|
-
throw new Error(getT('error.databaseUrlInvalid'));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Comando psql conforme documentação oficial Supabase
|
|
47
|
-
const restoreCmd = [
|
|
48
|
-
'docker run --rm --network host',
|
|
49
|
-
`-v "${backupDirAbs}:/host"`,
|
|
50
|
-
`-e PGPASSWORD="${encodeURIComponent(urlMatch[2])}"`,
|
|
51
|
-
'postgres:17 psql',
|
|
52
|
-
`-d "${targetDatabaseUrl}"`,
|
|
53
|
-
`-f /host/${uncompressedFile}`
|
|
54
|
-
].join(' ');
|
|
55
|
-
|
|
56
|
-
console.log(chalk.cyan(` - ${getT('restore.steps.database.executing')}`));
|
|
57
|
-
console.log(chalk.cyan(` ℹ️ ${getT('restore.steps.database.followingDocs')}`));
|
|
58
|
-
console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.database.warning')}`));
|
|
59
|
-
console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.database.warningReason1')}`));
|
|
60
|
-
console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.database.warningReason2')}`));
|
|
61
|
-
|
|
62
|
-
// Executar comando de restauração
|
|
63
|
-
execSync(restoreCmd, { stdio: 'inherit', encoding: 'utf8' });
|
|
64
|
-
|
|
65
|
-
console.log(chalk.green(` ✅ ${getT('restore.steps.database.success')}`));
|
|
66
|
-
ui.hint(` ℹ️ ${getT('restore.steps.database.normalErrors')}`);
|
|
67
|
-
|
|
68
|
-
} catch (error) {
|
|
69
|
-
// Erros esperados conforme documentação oficial Supabase
|
|
70
|
-
const getT = global.smoonbI18n?.t || t;
|
|
71
|
-
if (error.message.includes('already exists') ||
|
|
72
|
-
error.message.includes('constraint') ||
|
|
73
|
-
error.message.includes('duplicate') ||
|
|
74
|
-
error.stdout?.includes('already exists')) {
|
|
75
|
-
console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.database.expectedErrors')}`));
|
|
76
|
-
console.log(chalk.green(` ✅ ${getT('restore.steps.database.success')}`));
|
|
77
|
-
ui.hint(` ℹ️ ${getT('restore.steps.database.errorsIgnored')}`);
|
|
78
|
-
} else {
|
|
79
|
-
console.error(chalk.red(` ❌ ${getT('restore.steps.database.error', { message: error.message })}`));
|
|
80
|
-
throw error;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
1
|
+
const { readDump, buildRestore } = require('../dump');
|
|
2
|
+
const { runSql } = require('../pg');
|
|
3
|
+
module.exports = async ({
|
|
4
|
+
backupFilePath,
|
|
5
|
+
targetDatabaseUrl,
|
|
6
|
+
sourceProjectId,
|
|
7
|
+
targetProjectId,
|
|
8
|
+
remapUrls = true,
|
|
9
|
+
postgresMajor = 17,
|
|
10
|
+
}) => {
|
|
11
|
+
const plan = buildRestore(readDump(backupFilePath), {
|
|
12
|
+
sourceProjectId,
|
|
13
|
+
targetProjectId,
|
|
14
|
+
remapUrls,
|
|
15
|
+
});
|
|
16
|
+
await runSql(targetDatabaseUrl, plan.sql, { postgresMajor });
|
|
17
|
+
return plan.summary;
|
|
18
|
+
};
|
|
@@ -1,122 +1,95 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
5
|
-
const {
|
|
6
|
-
const {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
try {
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.edgeFunctions.deployFailed', { funcName, message: deployError.message })}`));
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Limpar supabase/functions após deploy (arquivos temporários não são mais necessários)
|
|
101
|
-
console.log(chalk.cyan(` - ${getT('restore.steps.edgeFunctions.cleaningAfter')}`));
|
|
102
|
-
try {
|
|
103
|
-
await fs.rm(supabaseFunctionsDir, { recursive: true, force: true });
|
|
104
|
-
} catch {
|
|
105
|
-
// Ignorar erro de limpeza
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
console.log(chalk.green(` ✅ ${getT('restore.steps.edgeFunctions.success')}`));
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
success: true,
|
|
112
|
-
functions_count: functions.length,
|
|
113
|
-
success_count: successCount
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
} catch (error) {
|
|
117
|
-
const getT = global.smoonbI18n?.t || t;
|
|
118
|
-
console.error(chalk.red(` ❌ ${getT('restore.steps.edgeFunctions.error', { message: error.message })}`));
|
|
119
|
-
throw error;
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
|
|
1
|
+
const fs = require('fs').promises;
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { runSupabase } = require('../../../utils/supabaseCli');
|
|
5
|
+
const { management } = require('../management');
|
|
6
|
+
const { copyDirectoryRecursive } = require('../utils');
|
|
7
|
+
module.exports = async (
|
|
8
|
+
{ backupPath, targetProject },
|
|
9
|
+
{ deploy = runSupabase, request = management } = {},
|
|
10
|
+
) => {
|
|
11
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'smoonb-functions-'));
|
|
12
|
+
try {
|
|
13
|
+
const source = path.join(backupPath, 'edge-functions');
|
|
14
|
+
const names = (await fs.readdir(source, { withFileTypes: true }))
|
|
15
|
+
.filter((e) => e.isDirectory() && e.name !== '_shared')
|
|
16
|
+
.map((e) => e.name);
|
|
17
|
+
if (!names.length)
|
|
18
|
+
return {
|
|
19
|
+
success: true,
|
|
20
|
+
verified: true,
|
|
21
|
+
functions_count: 0,
|
|
22
|
+
success_count: 0,
|
|
23
|
+
};
|
|
24
|
+
if (names.some((n) => !/^[-a-zA-Z0-9_]+$/.test(n)))
|
|
25
|
+
throw new Error('Invalid function slug in backup');
|
|
26
|
+
await copyDirectoryRecursive(
|
|
27
|
+
source,
|
|
28
|
+
path.join(root, 'supabase', 'functions'),
|
|
29
|
+
);
|
|
30
|
+
const manifest = JSON.parse(
|
|
31
|
+
await fs.readFile(path.join(backupPath, 'backup-manifest.json'), 'utf8'),
|
|
32
|
+
);
|
|
33
|
+
const metadata = manifest.components?.edge_functions?.functions || [];
|
|
34
|
+
const settings = names.map((name) => ({
|
|
35
|
+
name,
|
|
36
|
+
verify_jwt:
|
|
37
|
+
metadata.find((f) => (f.slug || f.name) === name)?.verify_jwt ?? true,
|
|
38
|
+
}));
|
|
39
|
+
await fs.writeFile(
|
|
40
|
+
path.join(root, 'supabase', 'config.toml'),
|
|
41
|
+
`project_id = "smoonb-restore"\n` +
|
|
42
|
+
settings
|
|
43
|
+
.map((f) => `[functions.${f.name}]\nverify_jwt = ${f.verify_jwt}\n`)
|
|
44
|
+
.join('\n'),
|
|
45
|
+
);
|
|
46
|
+
for (const f of settings) {
|
|
47
|
+
const args = [
|
|
48
|
+
'functions',
|
|
49
|
+
'deploy',
|
|
50
|
+
f.name,
|
|
51
|
+
'--project-ref',
|
|
52
|
+
targetProject.targetProjectId,
|
|
53
|
+
'--workdir',
|
|
54
|
+
root,
|
|
55
|
+
'--use-api',
|
|
56
|
+
];
|
|
57
|
+
if (!f.verify_jwt) args.push('--no-verify-jwt');
|
|
58
|
+
try {
|
|
59
|
+
deploy('supabase', args, {
|
|
60
|
+
cwd: root,
|
|
61
|
+
env: {
|
|
62
|
+
...process.env,
|
|
63
|
+
SUPABASE_ACCESS_TOKEN: targetProject.targetAccessToken,
|
|
64
|
+
},
|
|
65
|
+
stdio: 'pipe',
|
|
66
|
+
timeout: 180000,
|
|
67
|
+
windowsHide: true,
|
|
68
|
+
});
|
|
69
|
+
} catch {
|
|
70
|
+
throw new Error(`Edge Function deployment failed: ${f.name}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const remote = await request(targetProject, 'functions');
|
|
74
|
+
for (const f of settings)
|
|
75
|
+
if (
|
|
76
|
+
!remote.some(
|
|
77
|
+
(r) =>
|
|
78
|
+
r.slug === f.name &&
|
|
79
|
+
r.status === 'ACTIVE' &&
|
|
80
|
+
r.verify_jwt === f.verify_jwt,
|
|
81
|
+
)
|
|
82
|
+
)
|
|
83
|
+
throw new Error(`Edge Function verification failed: ${f.name}`);
|
|
84
|
+
return {
|
|
85
|
+
success: true,
|
|
86
|
+
verified: true,
|
|
87
|
+
functions_count: names.length,
|
|
88
|
+
success_count: names.length,
|
|
89
|
+
local_sources_preserved: true,
|
|
90
|
+
};
|
|
91
|
+
} finally {
|
|
92
|
+
// Only the directory freshly allocated by mkdtemp is removed.
|
|
93
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
94
|
+
}
|
|
95
|
+
};
|
|
@@ -1,54 +1,26 @@
|
|
|
1
|
-
const
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (authSettings.settings?.auth_url_config) {
|
|
29
|
-
Object.entries(authSettings.settings.auth_url_config).forEach(([key, value]) => {
|
|
30
|
-
console.log(chalk.white(` - ${key}: ${value}`));
|
|
31
|
-
});
|
|
32
|
-
} else if (authSettings.auth_url_config) {
|
|
33
|
-
Object.entries(authSettings.auth_url_config).forEach(([key, value]) => {
|
|
34
|
-
console.log(chalk.white(` - ${key}: ${value}`));
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
console.log(chalk.yellow(`\n ⚠️ ${getT('restore.steps.auth.pressEnter')}`));
|
|
39
|
-
|
|
40
|
-
await inquirer.prompt([{
|
|
41
|
-
type: 'input',
|
|
42
|
-
name: 'continue',
|
|
43
|
-
message: getT('restore.steps.auth.pressEnter'),
|
|
44
|
-
prefix: ''
|
|
45
|
-
}]);
|
|
46
|
-
|
|
47
|
-
console.log(chalk.green(` ✅ ${getT('restore.steps.auth.success')}`));
|
|
48
|
-
|
|
49
|
-
} catch (error) {
|
|
50
|
-
const getT = global.smoonbI18n?.t || t;
|
|
51
|
-
console.error(chalk.red(` ❌ ${getT('restore.steps.auth.error', { message: error.message })}`));
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { applyConfig } = require('../management');
|
|
4
|
+
const { remapServiceUrls } = require('../dump');
|
|
5
|
+
const allowed = require('../auth-fields.json');
|
|
6
|
+
module.exports = async ({ backupPath, targetProject, remapUrls = true }) => {
|
|
7
|
+
const backup = JSON.parse(
|
|
8
|
+
fs.readFileSync(path.join(backupPath, 'auth-settings.json'), 'utf8'),
|
|
9
|
+
);
|
|
10
|
+
const settings =
|
|
11
|
+
backup.settings?.auth_url_config ||
|
|
12
|
+
backup.settings ||
|
|
13
|
+
backup.auth_url_config;
|
|
14
|
+
if (!settings || !Object.keys(settings).length)
|
|
15
|
+
throw new Error('Auth configuration missing from backup');
|
|
16
|
+
const desired = remapUrls
|
|
17
|
+
? JSON.parse(
|
|
18
|
+
remapServiceUrls(
|
|
19
|
+
JSON.stringify(settings),
|
|
20
|
+
backup.project_id,
|
|
21
|
+
targetProject.targetProjectId,
|
|
22
|
+
),
|
|
23
|
+
)
|
|
24
|
+
: settings;
|
|
25
|
+
return applyConfig(targetProject, 'config/auth', desired, allowed);
|
|
26
|
+
};
|