x
This commit is contained in:
21
backup.js
21
backup.js
@@ -1,7 +1,8 @@
|
||||
require('dotenv').config();
|
||||
const {
|
||||
createDatabaseBackup,
|
||||
downloadBackupFile,
|
||||
const {
|
||||
createDatabaseBackup,
|
||||
downloadBackupFile,
|
||||
downloadBackupFileSMB,
|
||||
sendTelegramBroadcast,
|
||||
formatBytes,
|
||||
config: sqlConfig
|
||||
@@ -26,8 +27,18 @@ async function createAndDownloadBackup() {
|
||||
|
||||
// Step 2: Download the backup file to local folder
|
||||
console.log('Step 2: Downloading backup file to local folder...');
|
||||
const localBackupPath = await downloadBackupFile();
|
||||
console.log(`Backup downloaded to: ${localBackupPath}`);
|
||||
|
||||
// Try SCP first, fall back to SMB if it fails
|
||||
let localBackupPath;
|
||||
try {
|
||||
localBackupPath = await downloadBackupFile();
|
||||
console.log(`Backup downloaded via SCP to: ${localBackupPath}`);
|
||||
} catch (scpError) {
|
||||
console.log('SCP download failed, falling back to SMB...');
|
||||
console.log(`SCP error: ${scpError.message}`);
|
||||
localBackupPath = await downloadBackupFileSMB();
|
||||
console.log(`Backup downloaded via SMB to: ${localBackupPath}`);
|
||||
}
|
||||
console.log('');
|
||||
|
||||
// Step 3: Check local file and show details
|
||||
|
||||
2
do.sh
Executable file
2
do.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
node upload.js --port 1433 --sql-path /sharemnt/ez.bak
|
||||
|
||||
97
index.js
97
index.js
@@ -7,6 +7,7 @@ const path = require('path');
|
||||
const zlib = require('zlib');
|
||||
const { pipeline } = require('stream/promises');
|
||||
const SambaClient = require('samba-client');
|
||||
const { Client } = require('ssh2');
|
||||
|
||||
// AWS S3 Configuration (v3 client)
|
||||
const s3 = new S3Client({
|
||||
@@ -36,7 +37,15 @@ const config = {
|
||||
// Backup file path
|
||||
const backupFilePath = process.env.BACKUP_FILE_PATH;
|
||||
|
||||
// SMB Configuration
|
||||
// SCP Configuration (SSH key authentication)
|
||||
const scpConfig = {
|
||||
host: process.env.SCP_HOST,
|
||||
port: parseInt(process.env.SCP_PORT) || 22,
|
||||
username: process.env.SCP_USERNAME,
|
||||
privateKey: fs.readFileSync(require('path').resolve(process.env.SCP_KEY_PATH))
|
||||
};
|
||||
|
||||
// Legacy SMB Configuration (deprecated)
|
||||
const smbConfig = {
|
||||
address: process.env.SMB_ADDRESS,
|
||||
username: process.env.SMB_USERNAME,
|
||||
@@ -45,8 +54,8 @@ const smbConfig = {
|
||||
};
|
||||
|
||||
// Ensure download directory exists
|
||||
const downloadFile = process.env.SMB_DOWNLOAD_FILE;
|
||||
const localDownloadFile = process.env.SMB_LOCAL_DOWNLOAD_FILE;
|
||||
const downloadFile = process.env.SCP_REMOTE_PATH || process.env.SMB_DOWNLOAD_FILE;
|
||||
const localDownloadFile = process.env.SCP_LOCAL_PATH || process.env.SMB_LOCAL_DOWNLOAD_FILE;
|
||||
|
||||
|
||||
// Admin Telegram Broadcast (env-configured)
|
||||
@@ -153,19 +162,83 @@ async function createDatabaseBackup() {
|
||||
}
|
||||
}
|
||||
|
||||
// Function to download backup file from SMB share
|
||||
// Function to download backup file via SCP
|
||||
async function downloadBackupFile() {
|
||||
try {
|
||||
console.log('Downloading backup file via SCP...');
|
||||
|
||||
const remotePath = process.env.SCP_REMOTE_PATH || '/sharemnt/ez.bak';
|
||||
const localPath = process.env.SCP_LOCAL_PATH || 'ez.bak';
|
||||
|
||||
console.log(`From: ${scpConfig.username}@${scpConfig.host}:${remotePath}`);
|
||||
console.log(`To: ${localPath}`);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const conn = new Client();
|
||||
|
||||
conn.on('ready', () => {
|
||||
console.log('SSH connection established');
|
||||
|
||||
conn.sftp((err, sftp) => {
|
||||
if (err) {
|
||||
console.error('SFTP error:', err);
|
||||
conn.end();
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
const readStream = sftp.createReadStream(remotePath);
|
||||
const writeStream = fs.createWriteStream(localPath);
|
||||
|
||||
readStream.on('error', (err) => {
|
||||
console.error('Read stream error:', err);
|
||||
conn.end();
|
||||
reject(err);
|
||||
});
|
||||
|
||||
writeStream.on('error', (err) => {
|
||||
console.error('Write stream error:', err);
|
||||
conn.end();
|
||||
reject(err);
|
||||
});
|
||||
|
||||
writeStream.on('finish', () => {
|
||||
console.log('Backup file downloaded successfully to:', localPath);
|
||||
conn.end();
|
||||
resolve(localPath);
|
||||
});
|
||||
|
||||
readStream.pipe(writeStream);
|
||||
});
|
||||
});
|
||||
|
||||
conn.on('error', (err) => {
|
||||
console.error('SSH connection error:', err);
|
||||
reject(err);
|
||||
});
|
||||
|
||||
conn.connect(scpConfig);
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error('Error downloading backup file via SCP:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy function to download backup file from SMB share (deprecated)
|
||||
async function downloadBackupFileSMB() {
|
||||
try {
|
||||
console.log('Downloading backup file from SMB share...');
|
||||
|
||||
|
||||
// Create SMB client
|
||||
const client = new SambaClient(smbConfig);
|
||||
|
||||
|
||||
// Download file from SMB share
|
||||
await client.getFile(downloadFile, localDownloadFile);
|
||||
|
||||
console.log('Backup file downloaded successfully to:', localDownloadFile);
|
||||
return localDownloadFile;
|
||||
await client.getFile(process.env.SMB_DOWNLOAD_FILE, process.env.SMB_LOCAL_DOWNLOAD_FILE);
|
||||
|
||||
console.log('Backup file downloaded successfully to:', process.env.SMB_LOCAL_DOWNLOAD_FILE);
|
||||
return process.env.SMB_LOCAL_DOWNLOAD_FILE;
|
||||
} catch (err) {
|
||||
console.error('Error downloading backup file from SMB share:', err);
|
||||
throw err;
|
||||
@@ -358,12 +431,14 @@ if (require.main === module) {
|
||||
module.exports = {
|
||||
createDatabaseBackup,
|
||||
downloadBackupFile,
|
||||
downloadBackupFileSMB, // Legacy SMB function
|
||||
compressBackupFile,
|
||||
formatBytes,
|
||||
sendTelegramBroadcast,
|
||||
getDbSizeBytes,
|
||||
// Export configurations that might be needed
|
||||
smbConfig,
|
||||
scpConfig,
|
||||
smbConfig, // Legacy SMB config
|
||||
config: config, // MSSQL config
|
||||
s3 // S3 client
|
||||
};
|
||||
|
||||
74
package-lock.json
generated
74
package-lock.json
generated
@@ -12,7 +12,8 @@
|
||||
"@aws-sdk/client-s3": "^3.859.0",
|
||||
"dotenv": "^16.0.3",
|
||||
"mssql": "^9.1.1",
|
||||
"samba-client": "^7.2.0"
|
||||
"samba-client": "^7.2.0",
|
||||
"ssh2": "^1.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.0.1"
|
||||
@@ -2104,6 +2105,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/asn1": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/async-function": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
|
||||
@@ -2155,6 +2165,15 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
@@ -2259,6 +2278,15 @@
|
||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/buildcheck": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz",
|
||||
"integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
|
||||
@@ -2347,6 +2375,20 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cpu-features": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz",
|
||||
"integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"buildcheck": "~0.0.6",
|
||||
"nan": "^2.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@@ -3643,6 +3685,13 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/nan": {
|
||||
"version": "2.23.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz",
|
||||
"integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/native-duplexpair": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz",
|
||||
@@ -4196,6 +4245,23 @@
|
||||
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/ssh2": {
|
||||
"version": "1.17.0",
|
||||
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.17.0.tgz",
|
||||
"integrity": "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"asn1": "^0.2.6",
|
||||
"bcrypt-pbkdf": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.16.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"cpu-features": "~0.0.10",
|
||||
"nan": "^2.23.0"
|
||||
}
|
||||
},
|
||||
"node_modules/stop-iteration-iterator": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
|
||||
@@ -4379,6 +4445,12 @@
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
|
||||
"license": "Unlicense"
|
||||
},
|
||||
"node_modules/typed-array-buffer": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
"@aws-sdk/client-s3": "^3.859.0",
|
||||
"dotenv": "^16.0.3",
|
||||
"mssql": "^9.1.1",
|
||||
"samba-client": "^7.2.0"
|
||||
"samba-client": "^7.2.0",
|
||||
"ssh2": "^1.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.0.1"
|
||||
|
||||
72
test-scp.js
Normal file
72
test-scp.js
Normal file
@@ -0,0 +1,72 @@
|
||||
require('dotenv').config();
|
||||
const { Client } = require('ssh2');
|
||||
|
||||
// Test SCP connection
|
||||
async function testSCPConnection() {
|
||||
const config = {
|
||||
host: process.env.SCP_HOST,
|
||||
port: parseInt(process.env.SCP_PORT) || 22,
|
||||
username: process.env.SCP_USERNAME,
|
||||
privateKey: require('fs').readFileSync(require('path').resolve(process.env.SCP_KEY_PATH))
|
||||
};
|
||||
|
||||
console.log('Testing SCP connection...');
|
||||
console.log(`Host: ${config.host}`);
|
||||
console.log(`Port: ${config.port}`);
|
||||
console.log(`Username: ${config.username}`);
|
||||
|
||||
const conn = new Client();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
conn.on('ready', () => {
|
||||
console.log('✅ SSH connection successful!');
|
||||
console.log('Now testing SCP download...');
|
||||
|
||||
conn.sftp((err, sftp) => {
|
||||
if (err) {
|
||||
console.error('❌ SFTP error:', err.message);
|
||||
conn.end();
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
const remotePath = process.env.SCP_REMOTE_PATH || '/sharemnt/ez.bak';
|
||||
console.log(`Checking remote file: ${remotePath}`);
|
||||
|
||||
sftp.stat(remotePath, (err, stats) => {
|
||||
if (err) {
|
||||
console.error('❌ Cannot access remote file:', err.message);
|
||||
conn.end();
|
||||
reject(err);
|
||||
} else {
|
||||
console.log('✅ Remote file found!');
|
||||
console.log(`File size: ${(stats.size / 1024 / 1024).toFixed(2)} MB`);
|
||||
console.log(`Modified: ${stats.mtime}`);
|
||||
conn.end();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
conn.on('error', (err) => {
|
||||
console.error('❌ SSH connection failed:', err.message);
|
||||
console.error('Error details:', err);
|
||||
reject(err);
|
||||
});
|
||||
|
||||
conn.connect(config);
|
||||
});
|
||||
}
|
||||
|
||||
// Run test
|
||||
testSCPConnection()
|
||||
.then(() => {
|
||||
console.log('\n🎉 SCP test completed successfully!');
|
||||
console.log('You can now use: node backup.js');
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('\n💥 SCP test failed!');
|
||||
console.error('Please check your SSH configuration.');
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user