@fiftyten/db-connect
Version:
CLI tool for database connections and DynamoDB operations via AWS Session Manager
250 lines ⢠11.2 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DynamoDBConnector = void 0;
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
const chalk_1 = __importDefault(require("chalk"));
const mfa_auth_1 = require("./mfa-auth");
class DynamoDBConnector {
constructor(region = 'us-west-1') {
this.mfaAuthenticated = false;
this.dynamoClient = new client_dynamodb_1.DynamoDBClient({ region });
this.docClient = lib_dynamodb_1.DynamoDBDocumentClient.from(this.dynamoClient);
this.mfaAuth = new mfa_auth_1.MfaAuthenticator(region);
}
/**
* Ensure MFA authentication if required
*/
async ensureMfaAuthentication() {
if (!this.mfaAuthenticated) {
console.log(chalk_1.default.yellow('š MFA authentication may be required for DynamoDB access...'));
try {
await this.mfaAuth.authenticateWithMfa();
this.mfaAuthenticated = true;
console.log(chalk_1.default.green('ā
MFA authentication successful'));
}
catch (error) {
console.log(chalk_1.default.blue('ā¹ļø Proceeding without MFA - may work with existing credentials'));
}
}
}
/**
* List all DynamoDB tables
*/
async listTables() {
await this.ensureMfaAuthentication();
try {
console.log(chalk_1.default.blue('š Listing DynamoDB tables...'));
const command = new client_dynamodb_1.ListTablesCommand({});
const response = await this.dynamoClient.send(command);
if (!response.TableNames || response.TableNames.length === 0) {
console.log(chalk_1.default.yellow('No DynamoDB tables found'));
return;
}
console.log(chalk_1.default.green(`\nā
Found ${response.TableNames.length} tables:`));
for (const tableName of response.TableNames) {
try {
const describeCommand = new client_dynamodb_1.DescribeTableCommand({ TableName: tableName });
const tableInfo = await this.dynamoClient.send(describeCommand);
const table = tableInfo.Table;
console.log(chalk_1.default.cyan(`\nš ${tableName}`));
console.log(` Status: ${table?.TableStatus}`);
console.log(` Items: ${table?.ItemCount || 'Unknown'}`);
console.log(` Size: ${table?.TableSizeBytes ? `${(table.TableSizeBytes / 1024).toFixed(2)} KB` : 'Unknown'}`);
console.log(` Created: ${table?.CreationDateTime?.toISOString() || 'Unknown'}`);
}
catch (error) {
console.log(chalk_1.default.cyan(`\nš ${tableName}`));
console.log(chalk_1.default.red(` Error getting details: ${error instanceof Error ? error.message : 'Unknown error'}`));
}
}
}
catch (error) {
throw new Error(`Failed to list tables: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Filter out sensitive fields from data
*/
filterSensitiveFields(items) {
const sensitiveFields = [
'encrypted_api_key',
'encrypted_secret_key',
'dek_encrypted',
'api_key',
'secret_key',
'passphrase',
'password',
'token',
'access_token',
'refresh_token'
];
return items.map(item => {
const filteredItem = { ...item };
sensitiveFields.forEach(field => {
if (filteredItem[field]) {
filteredItem[field] = '[HIDDEN]';
}
});
return filteredItem;
});
}
/**
* Scan a DynamoDB table
*/
async scanTable(tableName, limit) {
await this.ensureMfaAuthentication();
try {
console.log(chalk_1.default.blue(`š Scanning table: ${tableName}${limit ? ` (limit: ${limit})` : ''}...`));
const command = new lib_dynamodb_1.ScanCommand({
TableName: tableName,
...(limit && { Limit: limit })
});
const response = await this.docClient.send(command);
if (!response.Items || response.Items.length === 0) {
console.log(chalk_1.default.yellow('No items found'));
return;
}
// Always filter sensitive fields for security
const items = this.filterSensitiveFields(response.Items);
console.log(chalk_1.default.green(`\nā
Found ${items.length} items (sensitive fields hidden):`));
console.log(JSON.stringify(items, null, 2));
if (response.LastEvaluatedKey) {
console.log(chalk_1.default.yellow('\nā ļø More items available (pagination truncated)'));
}
}
catch (error) {
throw new Error(`Failed to scan table: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Query a DynamoDB table
*/
async queryTable(tableName, keyCondition, limit) {
await this.ensureMfaAuthentication();
try {
console.log(chalk_1.default.blue(`š Querying table: ${tableName}`));
console.log(chalk_1.default.gray(`Key condition: ${keyCondition}${limit ? ` (limit: ${limit})` : ''}`));
// Parse simple key condition (e.g., "pk = 'value'")
const match = keyCondition.match(/(\w+)\s*=\s*['"]?([^'"]+)['"]?/);
if (!match) {
throw new Error('Invalid key condition format. Use: "keyName = value"');
}
const [, keyName, keyValue] = match;
const command = new lib_dynamodb_1.QueryCommand({
TableName: tableName,
KeyConditionExpression: `#pk = :pk`,
ExpressionAttributeNames: {
'#pk': keyName
},
ExpressionAttributeValues: {
':pk': keyValue
},
...(limit && { Limit: limit })
});
const response = await this.docClient.send(command);
if (!response.Items || response.Items.length === 0) {
console.log(chalk_1.default.yellow('No items found'));
return;
}
// Always filter sensitive fields for security
const items = this.filterSensitiveFields(response.Items);
console.log(chalk_1.default.green(`\nā
Found ${items.length} items (sensitive fields hidden):`));
console.log(JSON.stringify(items, null, 2));
if (response.LastEvaluatedKey) {
console.log(chalk_1.default.yellow('\nā ļø More items available (pagination truncated)'));
}
}
catch (error) {
throw new Error(`Failed to query table: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Get a specific item from DynamoDB table
*/
async getItem(tableName, key) {
await this.ensureMfaAuthentication();
try {
console.log(chalk_1.default.blue(`šÆ Getting item from table: ${tableName}`));
console.log(chalk_1.default.gray(`Key: ${key}`));
// Parse simple key (e.g., "pk:value" or JSON)
let keyObj;
if (key.startsWith('{')) {
// JSON format
keyObj = JSON.parse(key);
}
else {
// Simple format "keyName:value"
const [keyName, keyValue] = key.split(':');
if (!keyName || !keyValue) {
throw new Error('Invalid key format. Use: "keyName:value" or JSON format');
}
keyObj = { [keyName]: keyValue };
}
const command = new lib_dynamodb_1.GetCommand({
TableName: tableName,
Key: keyObj
});
const response = await this.docClient.send(command);
if (!response.Item) {
console.log(chalk_1.default.yellow('Item not found'));
return;
}
// Always filter sensitive fields for security
const [filteredItem] = this.filterSensitiveFields([response.Item]);
console.log(chalk_1.default.green('\nā
Item found (sensitive fields hidden):'));
console.log(JSON.stringify(filteredItem, null, 2));
}
catch (error) {
throw new Error(`Failed to get item: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Describe a specific table
*/
async describeTable(tableName) {
await this.ensureMfaAuthentication();
try {
console.log(chalk_1.default.blue(`š Describing table: ${tableName}...`));
const command = new client_dynamodb_1.DescribeTableCommand({ TableName: tableName });
const response = await this.dynamoClient.send(command);
const table = response.Table;
if (!table) {
console.log(chalk_1.default.yellow('Table not found'));
return;
}
console.log(chalk_1.default.green('\nā
Table details:'));
console.log(`Name: ${table.TableName}`);
console.log(`Status: ${table.TableStatus}`);
console.log(`Items: ${table.ItemCount || 'Unknown'}`);
console.log(`Size: ${table.TableSizeBytes ? `${(table.TableSizeBytes / 1024).toFixed(2)} KB` : 'Unknown'}`);
console.log(`Created: ${table.CreationDateTime?.toISOString() || 'Unknown'}`);
if (table.KeySchema) {
console.log('\nKey Schema:');
table.KeySchema.forEach(key => {
console.log(` ${key.AttributeName}: ${key.KeyType}`);
});
}
if (table.AttributeDefinitions) {
console.log('\nAttribute Definitions:');
table.AttributeDefinitions.forEach(attr => {
console.log(` ${attr.AttributeName}: ${attr.AttributeType}`);
});
}
if (table.GlobalSecondaryIndexes) {
console.log('\nGlobal Secondary Indexes:');
table.GlobalSecondaryIndexes.forEach(gsi => {
console.log(` ${gsi.IndexName}: ${gsi.IndexStatus}`);
});
}
}
catch (error) {
throw new Error(`Failed to describe table: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
}
exports.DynamoDBConnector = DynamoDBConnector;
//# sourceMappingURL=dynamodb-connector.js.map