This commit is contained in:
sebseb7
2025-09-16 09:02:58 +02:00
parent 6fb6b0da78
commit 48f1fae32e
6 changed files with 251 additions and 18 deletions

View File

@@ -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
View File

@@ -0,0 +1,2 @@
node upload.js --port 1433 --sql-path /sharemnt/ez.bak

View File

@@ -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
View File

@@ -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",

View File

@@ -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
View 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);
});