UNPKG

@fiftyten/db-connect

Version:

CLI tool for database connections and DynamoDB operations via AWS Session Manager

250 lines • 11.2 kB
"use strict"; 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