smoonb 1.0.7 → 1.0.9
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 +43 -0
- package/README.md +27 -7
- package/README.pt-BR.md +27 -7
- package/package.json +4 -3
- package/src/commands/backup/steps/05-auth-settings.js +6 -2
- 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/auth.js +65 -0
- package/src/commands/restore/dump.js +284 -0
- package/src/commands/restore/index.js +8 -127
- package/src/commands/restore/management.js +96 -0
- package/src/commands/restore/pg.js +83 -0
- package/src/commands/restore/runner.js +140 -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 +15 -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 +803 -797
- package/src/i18n/locales/pt-BR.json +802 -796
- package/src/utils/auth-secrets.js +23 -0
- package/src/utils/supabaseCli.js +32 -0
|
@@ -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,15 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
const {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (!fs.existsSync(authSettingsPath)) {
|
|
17
|
-
console.log(chalk.yellow(` ⚠️ ${getT('restore.steps.auth.notFound')}`));
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const authSettings = JSON.parse(fs.readFileSync(authSettingsPath, 'utf8'));
|
|
22
|
-
const dashboardUrl = `https://supabase.com/dashboard/project/${targetProject.targetProjectId}/auth/url-config`;
|
|
23
|
-
|
|
24
|
-
console.log(chalk.green(`\n ✅ ${getT('restore.steps.auth.urlTitle')}`));
|
|
25
|
-
console.log(chalk.cyan(` ${dashboardUrl}`));
|
|
26
|
-
console.log(chalk.yellow(`\n 📋 ${getT('restore.steps.auth.configureTitle')}`));
|
|
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 { applyConfig } = require('../management');
|
|
2
|
+
const { prepareAuth } = require('../auth');
|
|
3
|
+
const allowed = require('../auth-fields.json');
|
|
4
|
+
module.exports = async (context, { request } = {}) => {
|
|
5
|
+
const { desired, writeOnly } =
|
|
6
|
+
context.authConfig || (await prepareAuth(context));
|
|
7
|
+
return applyConfig(
|
|
8
|
+
context.targetProject,
|
|
9
|
+
'config/auth',
|
|
10
|
+
desired,
|
|
11
|
+
allowed,
|
|
12
|
+
request,
|
|
13
|
+
{ writeOnly },
|
|
14
|
+
);
|
|
15
|
+
};
|