-
Notifications
You must be signed in to change notification settings - Fork 3.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Introduce backup and restore for postgres #37326
base: release
Are you sure you want to change the base?
Changes from all commits
0906372
d52d62f
f304f6c
5368a68
be3f228
74f96f0
a31fffe
7933a05
a6d9be8
032356b
ecd642f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -125,7 +125,15 @@ function getEncryptionPasswordFromUser(){ | |||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
async function exportDatabase(destFolder) { | ||||||||||||||||||||||||||||||
console.log('Exporting database'); | ||||||||||||||||||||||||||||||
await executeMongoDumpCMD(destFolder, utils.getDburl()) | ||||||||||||||||||||||||||||||
const dbUrl = utils.getDburl(); | ||||||||||||||||||||||||||||||
// Check the DB url | ||||||||||||||||||||||||||||||
if (dbUrl.startsWith('mongodb')) { | ||||||||||||||||||||||||||||||
await executeMongoDumpCMD(destFolder, dbUrl); | ||||||||||||||||||||||||||||||
} else if (dbUrl.startsWith('postgresql')) { | ||||||||||||||||||||||||||||||
await executePostgresDumpCMD(destFolder, dbUrl); | ||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||
throw new Error(`Unsupported database type in URL: ${dbUrl}`); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
console.log('Exporting database done.'); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
|
@@ -141,7 +149,7 @@ async function createGitStorageArchive(destFolder) { | |||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
async function createManifestFile(path) { | ||||||||||||||||||||||||||||||
const version = await utils.getCurrentAppsmithVersion() | ||||||||||||||||||||||||||||||
const manifest_data = { "appsmithVersion": version, "dbName": utils.getDatabaseNameFromMongoURI(utils.getDburl()) } | ||||||||||||||||||||||||||||||
const manifest_data = { "appsmithVersion": version, "dbName": utils.getDatabaseNameFromDBURI(utils.getDburl()) } | ||||||||||||||||||||||||||||||
await fsPromises.writeFile(path + '/manifest.json', JSON.stringify(manifest_data)); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
|
@@ -157,8 +165,12 @@ async function exportDockerEnvFile(destFolder, encryptArchive) { | |||||||||||||||||||||||||||||
console.log('Exporting docker environment file done.'); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
async function executeMongoDumpCMD(destFolder, appsmithMongoURI) { | ||||||||||||||||||||||||||||||
return await utils.execCommand(['mongodump', `--uri=${appsmithMongoURI}`, `--archive=${destFolder}/mongodb-data.gz`, '--gzip']);// generate cmd | ||||||||||||||||||||||||||||||
async function executeMongoDumpCMD(destFolder, dbUrl) { | ||||||||||||||||||||||||||||||
return await utils.execCommand(['mongodump', `--uri=${dbUrl}`, `--archive=${destFolder}/mongodb-data.gz`, '--gzip']);// generate cmd | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
Comment on lines
+168
to
+170
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add error handling for MongoDB dump Add try-catch block to handle potential MongoDB dump failures. async function executeMongoDumpCMD(destFolder, dbUrl) {
- return await utils.execCommand(['mongodump', `--uri=${dbUrl}`, `--archive=${destFolder}/mongodb-data.gz`, '--gzip']);
+ try {
+ return await utils.execCommand(['mongodump', `--uri=${dbUrl}`, `--archive=${destFolder}/mongodb-data.gz`, '--gzip']);
+ } catch (error) {
+ throw new Error(`MongoDB dump failed: ${error.message}`);
+ }
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
async function executePostgresDumpCMD(destFolder, dbUrl) { | ||||||||||||||||||||||||||||||
return await utils.execCommand(['pg_dump', dbUrl, '-n', 'appsmith','-Fc', '-f', destFolder + '/pg-data.archive']); | ||||||||||||||||||||||||||||||
Comment on lines
+172
to
+173
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance PostgreSQL dump reliability Add error handling and schema existence check. async function executePostgresDumpCMD(destFolder, dbUrl) {
- return await utils.execCommand(['pg_dump', dbUrl, '-n', 'appsmith','-Fc', '-f', destFolder + '/pg-data.archive']);
+ try {
+ // Check if schema exists
+ await utils.execCommand(['psql', dbUrl, '-c', "SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'appsmith'"]);
+ return await utils.execCommand(['pg_dump', dbUrl, '-n', 'appsmith', '-Fc', '-f', destFolder + '/pg-data.archive']);
+ } catch (error) {
+ if (error.message.includes('schema_name')) {
+ throw new Error('Schema "appsmith" not found in database');
+ }
+ throw new Error(`PostgreSQL dump failed: ${error.message}`);
+ }
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
async function createFinalArchive(destFolder, timestamp) { | ||||||||||||||||||||||||||||||
|
@@ -253,6 +265,7 @@ module.exports = { | |||||||||||||||||||||||||||||
generateBackupRootPath, | ||||||||||||||||||||||||||||||
getBackupContentsPath, | ||||||||||||||||||||||||||||||
executeMongoDumpCMD, | ||||||||||||||||||||||||||||||
executePostgresDumpCMD, | ||||||||||||||||||||||||||||||
getGitRoot, | ||||||||||||||||||||||||||||||
executeCopyCMD, | ||||||||||||||||||||||||||||||
removeSensitiveEnvData, | ||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,6 +55,16 @@ test('Test mongodump CMD generaton', async () => { | |
console.log(res) | ||
}) | ||
|
||
test('Test postgresdump CMD generaton', async () => { | ||
var dest = '/dest' | ||
var appsmithMongoURI = 'postgresql://username:password@host/appsmith' | ||
var cmd = 'pg_dump postgresql://username:password@host/appsmith -Fc -f /dest/pg-data.archive' | ||
utils.execCommand = jest.fn().mockImplementation(async (a) => a.join(' ')); | ||
const res = await backup.executePostgresDumpCMD(dest, appsmithMongoURI) | ||
expect(res).toBe(cmd) | ||
console.log(res) | ||
}) | ||
|
||
test('Test get gitRoot path when APPSMITH_GIT_ROOT is \'\' ', () => { | ||
expect(backup.getGitRoot('')).toBe('/appsmith-stacks/git-storage') | ||
}); | ||
|
@@ -228,28 +238,42 @@ test('Test backup encryption function', async () => { | |
test('Get DB name from Mongo URI 1', async () => { | ||
var mongodb_uri = "mongodb+srv://admin:[email protected]/my_db_name?retryWrites=true&minPoolSize=1&maxPoolSize=10&maxIdleTimeMS=900000&authSource=admin" | ||
var expectedDBName = 'my_db_name' | ||
const dbName = utils.getDatabaseNameFromMongoURI(mongodb_uri) | ||
const dbName = utils.getDatabaseNameFromDBURI(mongodb_uri) | ||
expect(dbName).toEqual(expectedDBName) | ||
}) | ||
|
||
test('Get DB name from Mongo URI 2', async () => { | ||
var mongodb_uri = "mongodb+srv://admin:[email protected]/test123?retryWrites=true&minPoolSize=1&maxPoolSize=10&maxIdleTimeMS=900000&authSource=admin" | ||
var expectedDBName = 'test123' | ||
const dbName = utils.getDatabaseNameFromMongoURI(mongodb_uri) | ||
const dbName = utils.getDatabaseNameFromDBURI(mongodb_uri) | ||
expect(dbName).toEqual(expectedDBName) | ||
}) | ||
|
||
test('Get DB name from Mongo URI 3', async () => { | ||
var mongodb_uri = "mongodb+srv://admin:[email protected]/test123" | ||
var expectedDBName = 'test123' | ||
const dbName = utils.getDatabaseNameFromMongoURI(mongodb_uri) | ||
const dbName = utils.getDatabaseNameFromDBURI(mongodb_uri) | ||
expect(dbName).toEqual(expectedDBName) | ||
}) | ||
|
||
test('Get DB name from Mongo URI 4', async () => { | ||
var mongodb_uri = "mongodb://appsmith:pAssW0rd!@localhost:27017/appsmith" | ||
var expectedDBName = 'appsmith' | ||
const dbName = utils.getDatabaseNameFromMongoURI(mongodb_uri) | ||
const dbName = utils.getDatabaseNameFromDBURI(mongodb_uri) | ||
expect(dbName).toEqual(expectedDBName) | ||
}) | ||
|
||
test('Get DB name from PostgreSQL URI', async () => { | ||
var pg_uri = "postgresql://user:password@host:5432/postgres_db" | ||
var expectedDBName = 'postgres_db' | ||
const dbName = utils.getDatabaseNameFromDBURI(pg_uri) | ||
expect(dbName).toEqual(expectedDBName) | ||
}) | ||
|
||
test('Get DB name from PostgreSQL URI with query params', async () => { | ||
var pg_uri = "postgresql://user:password@host:5432/postgres_db?sslmode=disable" | ||
var expectedDBName = 'postgres_db' | ||
const dbName = utils.getDatabaseNameFromDBURI(pg_uri) | ||
expect(dbName).toEqual(expectedDBName) | ||
}) | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,4 +1,4 @@ | ||||||||||||
// Init function export mongodb | ||||||||||||
// Init function export db | ||||||||||||
const shell = require('shelljs'); | ||||||||||||
const Constants = require('./constants'); | ||||||||||||
const utils = require('./utils'); | ||||||||||||
|
@@ -7,7 +7,15 @@ function export_database() { | |||||||||||
console.log('export_database ....'); | ||||||||||||
dbUrl = utils.getDburl(); | ||||||||||||
shell.mkdir('-p', [Constants.BACKUP_PATH]); | ||||||||||||
const cmd = `mongodump --uri='${dbUrl}' --archive='${Constants.BACKUP_PATH}/${Constants.DUMP_FILE_NAME}' --gzip`; | ||||||||||||
let cmd; | ||||||||||||
if (dbUrl.startsWith('mongodb')) { | ||||||||||||
cmd = `mongodump --uri='${dbUrl}' --archive='${Constants.BACKUP_PATH}/${Constants.DUMP_FILE_NAME}' --gzip`; | ||||||||||||
} else if (dbUrl.startsWith('postgresql')) { | ||||||||||||
abhvsn marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
// Dump only the appsmith schema with custom format | ||||||||||||
cmd = `pg_dump ${dbUrl} -n appsmith -Fc -f '${Constants.BACKUP_PATH}/${Constants.DUMP_FILE_NAME}'`; | ||||||||||||
} else { | ||||||||||||
throw new Error('Unsupported database type, only MongoDB and PostgreSQL are supported'); | ||||||||||||
} | ||||||||||||
shell.exec(cmd); | ||||||||||||
console.log('export_database done'); | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add error handling for database export commands The command execution should handle potential failures and provide meaningful error messages. -shell.exec(cmd);
+const result = shell.exec(cmd);
+if (result.code !== 0) {
+ throw new Error(`Database export failed: ${result.stderr}`);
+} 📝 Committable suggestion
Suggested change
|
||||||||||||
} | ||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -59,17 +59,49 @@ async function extractArchive(backupFilePath, restoreRootPath) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
async function restoreDatabase(restoreContentsPath, dbUrl) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
console.log('Restoring database...'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (dbUrl.startsWith('mongodb')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
await restore_mongo_db(restoreContentsPath, dbUrl); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else if (dbUrl.includes('postgresql')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
await restore_postgres_db(restoreContentsPath, dbUrl); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
throw new Error('Unsupported database type, only MongoDB and PostgreSQL are supported'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
console.log('Restoring database completed'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+62
to
+70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Use URL parsing for database type detection The current string-based database type detection is fragile. Consider using URL parsing for more robust type detection. async function restoreDatabase(restoreContentsPath, dbUrl) {
console.log('Restoring database...');
- if (dbUrl.startsWith('mongodb')) {
- await restore_mongo_db(restoreContentsPath, dbUrl);
- } else if (dbUrl.includes('postgresql')) {
- await restore_postgres_db(restoreContentsPath, dbUrl);
+ const url = new URL(dbUrl);
+ const protocol = url.protocol.replace(':', '');
+ switch(protocol) {
+ case 'mongodb':
+ await restore_mongo_db(restoreContentsPath, dbUrl);
+ break;
+ case 'postgresql':
+ await restore_postgres_db(restoreContentsPath, dbUrl);
+ break;
+ default:
+ throw new Error(`Unsupported database type: ${protocol}. Only MongoDB and PostgreSQL are supported`);
}
console.log('Restoring database completed');
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
async function restore_mongo_db(restoreContentsPath, dbUrl) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const cmd = ['mongorestore', `--uri=${dbUrl}`, '--drop', `--archive=${restoreContentsPath}/mongodb-data.gz`, '--gzip'] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const fromDbName = await getBackupDatabaseName(restoreContentsPath); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const toDbName = utils.getDatabaseNameFromMongoURI(dbUrl); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const toDbName = utils.getDatabaseNameFromDBURI(dbUrl); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
console.log("Restoring database from " + fromDbName + " to " + toDbName) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cmd.push('--nsInclude=*', `--nsFrom=${fromDbName}.*`, `--nsTo=${toDbName}.*`) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
console.warn('Error reading manifest file. Assuming same database name.', error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
await utils.execCommand(cmd); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
console.log('Restoring database completed'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
async function restore_postgres_db(restoreContentsPath, dbUrl) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const cmd = ['pg_restore', '--verbose', '--clean', `${restoreContentsPath}/pg-data.archive`]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const url = new URL(dbUrl); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const isLocalhost = ['localhost', '127.0.0.1'].includes(url.hostname); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (isLocalhost) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let dbName; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
dbName = utils.getDatabaseNameFromDBURI(dbUrl); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
console.log("Restoring database to " + dbName); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
console.warn('Error reading manifest file. Assuming same database name as appsmith.', error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
dbName = 'appsmith'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cmd.push('-d' , 'postgresql://localhost:5432/' + dbName); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Use default user for local postgres | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cmd.push('-U', 'postgres'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cmd.push('-d', dbUrl); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
await utils.execCommand(cmd); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+85
to
+104
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Improve PostgreSQL restore implementation The implementation needs improvements in credential handling and error management:
async function restore_postgres_db(restoreContentsPath, dbUrl) {
const cmd = ['pg_restore', '--verbose', '--clean', `${restoreContentsPath}/pg-data.archive`];
const url = new URL(dbUrl);
const isLocalhost = ['localhost', '127.0.0.1'].includes(url.hostname);
if (isLocalhost) {
let dbName;
try {
dbName = utils.getDatabaseNameFromDBURI(dbUrl);
console.log("Restoring database to " + dbName);
} catch (error) {
console.warn('Error reading manifest file. Assuming same database name as appsmith.', error);
dbName = 'appsmith';
}
cmd.push('-d' , 'postgresql://localhost:5432/' + dbName);
- cmd.push('-U', 'postgres');
+ const pgUser = process.env.POSTGRES_USER || 'postgres';
+ cmd.push('-U', pgUser);
} else {
cmd.push('-d', dbUrl);
}
- await utils.execCommand(cmd);
+ try {
+ await utils.execCommand(cmd);
+ } catch (error) {
+ throw new Error(`PostgreSQL restore failed: ${error.message}`);
+ }
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
async function restoreDockerEnvFile(restoreContentsPath, backupName, overwriteEncryptionKeys) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add validation for database URL
The database URL should be validated before checking its type.
📝 Committable suggestion