mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-19 16:36:00 +00:00
SYNC: Merge pull request #3 from dbgate/feature/zip
This commit is contained in:
@@ -4,11 +4,20 @@ const fsp = require('fs/promises');
|
||||
const semver = require('semver');
|
||||
const currentVersion = require('../currentVersion');
|
||||
const { getLogger, extractErrorLogData } = require('dbgate-tools');
|
||||
const { storageReadConfig } = require('../controllers/storageDb');
|
||||
|
||||
const logger = getLogger('cloudUpgrade');
|
||||
|
||||
async function checkCloudUpgrade() {
|
||||
try {
|
||||
if (process.env.STORAGE_DATABASE) {
|
||||
const settings = await storageReadConfig('settings');
|
||||
if (settings['cloud.useAutoUpgrade'] != 1) {
|
||||
// auto-upgrade not allowed
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const resp = await axios.default.get('https://api.github.com/repos/dbgate/dbgate/releases/latest');
|
||||
const json = resp.data;
|
||||
const version = json.name.substring(1);
|
||||
@@ -43,7 +52,11 @@ async function checkCloudUpgrade() {
|
||||
|
||||
logger.info(`Downloaded new version from ${zipUrl}`);
|
||||
} else {
|
||||
logger.info(`Checked version ${version} is not newer than ${cloudDownloadedVersion ?? currentVersion.version}, upgrade skippped`);
|
||||
logger.info(
|
||||
`Checked version ${version} is not newer than ${
|
||||
cloudDownloadedVersion ?? currentVersion.version
|
||||
}, upgrade skippped`
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(extractErrorLogData(err), 'Error checking cloud upgrade');
|
||||
|
||||
@@ -59,7 +59,7 @@ async function loadEncryptionKeyFromExternal(storedValue, setStoredValue) {
|
||||
|
||||
let _encryptor = null;
|
||||
|
||||
function getEncryptor() {
|
||||
function getInternalEncryptor() {
|
||||
if (_encryptor) {
|
||||
return _encryptor;
|
||||
}
|
||||
@@ -69,14 +69,14 @@ function getEncryptor() {
|
||||
|
||||
function encryptPasswordString(password) {
|
||||
if (password && !password.startsWith('crypt:')) {
|
||||
return 'crypt:' + getEncryptor().encrypt(password);
|
||||
return 'crypt:' + getInternalEncryptor().encrypt(password);
|
||||
}
|
||||
return password;
|
||||
}
|
||||
|
||||
function decryptPasswordString(password) {
|
||||
if (password && password.startsWith('crypt:')) {
|
||||
return getEncryptor().decrypt(password.substring('crypt:'.length));
|
||||
return getInternalEncryptor().decrypt(password.substring('crypt:'.length));
|
||||
}
|
||||
return password;
|
||||
}
|
||||
@@ -85,7 +85,7 @@ function encryptObjectPasswordField(obj, field) {
|
||||
if (obj && obj[field] && !obj[field].startsWith('crypt:')) {
|
||||
return {
|
||||
...obj,
|
||||
[field]: 'crypt:' + getEncryptor().encrypt(obj[field]),
|
||||
[field]: 'crypt:' + getInternalEncryptor().encrypt(obj[field]),
|
||||
};
|
||||
}
|
||||
return obj;
|
||||
@@ -95,7 +95,7 @@ function decryptObjectPasswordField(obj, field) {
|
||||
if (obj && obj[field] && obj[field].startsWith('crypt:')) {
|
||||
return {
|
||||
...obj,
|
||||
[field]: getEncryptor().decrypt(obj[field].substring('crypt:'.length)),
|
||||
[field]: getInternalEncryptor().decrypt(obj[field].substring('crypt:'.length)),
|
||||
};
|
||||
}
|
||||
return obj;
|
||||
@@ -156,6 +156,49 @@ function getEncryptionKey() {
|
||||
return _encryptionKey;
|
||||
}
|
||||
|
||||
function generateTransportEncryptionKey() {
|
||||
const encryptor = simpleEncryptor.createEncryptor(defaultEncryptionKey);
|
||||
const result = {
|
||||
encryptionKey: crypto.randomBytes(32).toString('hex'),
|
||||
};
|
||||
return encryptor.encrypt(result);
|
||||
}
|
||||
|
||||
function createTransportEncryptor(encryptionData) {
|
||||
const encryptor = simpleEncryptor.createEncryptor(defaultEncryptionKey);
|
||||
const data = encryptor.decrypt(encryptionData);
|
||||
const res = simpleEncryptor.createEncryptor(data['encryptionKey']);
|
||||
return res;
|
||||
}
|
||||
|
||||
function recryptObjectPasswordField(obj, field, decryptEncryptor, encryptEncryptor) {
|
||||
if (obj && obj[field] && obj[field].startsWith('crypt:')) {
|
||||
return {
|
||||
...obj,
|
||||
[field]: 'crypt:' + encryptEncryptor.encrypt(decryptEncryptor.decrypt(obj[field].substring('crypt:'.length))),
|
||||
};
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
function recryptObjectPasswordFieldInPlace(obj, field, decryptEncryptor, encryptEncryptor) {
|
||||
if (obj && obj[field] && obj[field].startsWith('crypt:')) {
|
||||
obj[field] = 'crypt:' + encryptEncryptor.encrypt(decryptEncryptor.decrypt(obj[field].substring('crypt:'.length)));
|
||||
}
|
||||
}
|
||||
|
||||
function recryptConnection(connection, decryptEncryptor, encryptEncryptor) {
|
||||
connection = recryptObjectPasswordField(connection, 'password', decryptEncryptor, encryptEncryptor);
|
||||
connection = recryptObjectPasswordField(connection, 'sshPassword', decryptEncryptor, encryptEncryptor);
|
||||
connection = recryptObjectPasswordField(connection, 'sshKeyfilePassword', decryptEncryptor, encryptEncryptor);
|
||||
return connection;
|
||||
}
|
||||
|
||||
function recryptUser(user, decryptEncryptor, encryptEncryptor) {
|
||||
user = recryptObjectPasswordField(user, 'password', decryptEncryptor, encryptEncryptor);
|
||||
return user;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
loadEncryptionKey,
|
||||
encryptConnection,
|
||||
@@ -169,4 +212,12 @@ module.exports = {
|
||||
setEncryptionKey,
|
||||
encryptPasswordString,
|
||||
decryptPasswordString,
|
||||
|
||||
getInternalEncryptor,
|
||||
recryptConnection,
|
||||
recryptUser,
|
||||
generateTransportEncryptionKey,
|
||||
createTransportEncryptor,
|
||||
recryptObjectPasswordField,
|
||||
recryptObjectPasswordFieldInPlace,
|
||||
};
|
||||
|
||||
77
packages/api/src/utility/extractSingleFileFromZip.js
Normal file
77
packages/api/src/utility/extractSingleFileFromZip.js
Normal file
@@ -0,0 +1,77 @@
|
||||
const yauzl = require('yauzl');
|
||||
const fs = require('fs');
|
||||
const { getLogger, extractErrorLogData } = require('dbgate-tools');
|
||||
const logger = getLogger('extractSingleFileFromZip');
|
||||
/**
|
||||
* Extracts a single file from a ZIP using yauzl.
|
||||
* Stops reading the rest of the archive once the file is found.
|
||||
*
|
||||
* @param {string} zipPath - Path to the ZIP file on disk.
|
||||
* @param {string} fileInZip - The file path *inside* the ZIP to extract.
|
||||
* @param {string} outputPath - Where to write the extracted file on disk.
|
||||
* @returns {Promise<boolean>} - Resolves with a success message or a "not found" message.
|
||||
*/
|
||||
function extractSingleFileFromZip(zipPath, fileInZip, outputPath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
yauzl.open(zipPath, { lazyEntries: true }, (err, zipFile) => {
|
||||
if (err) return reject(err);
|
||||
|
||||
let fileFound = false;
|
||||
|
||||
// Start reading the first entry
|
||||
zipFile.readEntry();
|
||||
|
||||
zipFile.on('entry', entry => {
|
||||
// Compare the entry name to the file we want
|
||||
if (entry.fileName === fileInZip) {
|
||||
fileFound = true;
|
||||
|
||||
// Open a read stream for this entry
|
||||
zipFile.openReadStream(entry, (err, readStream) => {
|
||||
if (err) return reject(err);
|
||||
|
||||
// Create a write stream to outputPath
|
||||
const writeStream = fs.createWriteStream(outputPath);
|
||||
readStream.pipe(writeStream);
|
||||
|
||||
// When the read stream ends, we can close the zipFile
|
||||
readStream.on('end', () => {
|
||||
// We won't read further entries
|
||||
zipFile.close();
|
||||
});
|
||||
|
||||
// When the file is finished writing, resolve
|
||||
writeStream.on('finish', () => {
|
||||
logger.info(`File "${fileInZip}" extracted to "${outputPath}".`);
|
||||
resolve(true);
|
||||
});
|
||||
|
||||
// Handle write errors
|
||||
writeStream.on('error', writeErr => {
|
||||
logger.error(extractErrorLogData(writeErr), `Error extracting "${fileInZip}" from "${zipPath}".`);
|
||||
reject(writeErr);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Not the file we want; skip to the next entry
|
||||
zipFile.readEntry();
|
||||
}
|
||||
});
|
||||
|
||||
// If we reach the end without finding the file
|
||||
zipFile.on('end', () => {
|
||||
if (!fileFound) {
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle general errors
|
||||
zipFile.on('error', err => {
|
||||
logger.error(extractErrorLogData(err), `ZIP file error in ${zipPath}.`);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = extractSingleFileFromZip;
|
||||
41
packages/api/src/utility/listZipEntries.js
Normal file
41
packages/api/src/utility/listZipEntries.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const yauzl = require('yauzl');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* Lists the files in a ZIP archive using yauzl,
|
||||
* returning an array of { fileName, uncompressedSize } objects.
|
||||
*
|
||||
* @param {string} zipPath - The path to the ZIP file.
|
||||
* @returns {Promise<Array<{fileName: string, uncompressedSize: number}>>}
|
||||
*/
|
||||
function listZipEntries(zipPath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
|
||||
if (err) return reject(err);
|
||||
|
||||
const entries = [];
|
||||
|
||||
// Start reading entries
|
||||
zipfile.readEntry();
|
||||
|
||||
// Handle each entry
|
||||
zipfile.on('entry', entry => {
|
||||
entries.push({
|
||||
fileName: entry.fileName,
|
||||
uncompressedSize: entry.uncompressedSize,
|
||||
});
|
||||
|
||||
// Move on to the next entry (we’re only listing, not reading file data)
|
||||
zipfile.readEntry();
|
||||
});
|
||||
|
||||
// Finished reading all entries
|
||||
zipfile.on('end', () => resolve(entries));
|
||||
|
||||
// Handle errors
|
||||
zipfile.on('error', err => reject(err));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = listZipEntries;
|
||||
Reference in New Issue
Block a user