From 6d1abf30ab7b1a2d6efb7bd6271274415e68da90 Mon Sep 17 00:00:00 2001 From: Andreas Knuth Date: Thu, 1 Jan 2026 08:37:57 -0600 Subject: [PATCH] rm unnec. stuff --- backend/server.js | 336 ++-------- sync/.env | 13 - sync/QUICKSTART.md | 171 ----- sync/README.md | 231 ------- sync/install-cron.sh | 39 -- sync/package-lock.json | 1420 ---------------------------------------- sync/package.json | 16 - sync/setup-sudo.sh | 46 -- sync/sync-wrapper.sh | 23 - sync/sync.js | 260 -------- sync/view-db.js | 53 -- 11 files changed, 40 insertions(+), 2568 deletions(-) delete mode 100644 sync/.env delete mode 100644 sync/QUICKSTART.md delete mode 100644 sync/README.md delete mode 100755 sync/install-cron.sh delete mode 100644 sync/package-lock.json delete mode 100644 sync/package.json delete mode 100755 sync/setup-sudo.sh delete mode 100755 sync/sync-wrapper.sh delete mode 100755 sync/sync.js delete mode 100755 sync/view-db.js diff --git a/backend/server.js b/backend/server.js index 6c6ffe2..665d328 100644 --- a/backend/server.js +++ b/backend/server.js @@ -6,9 +6,7 @@ const morgan = require('morgan'); const crypto = require('crypto'); const { DynamoDBClient } = require('@aws-sdk/client-dynamodb'); const { DynamoDBDocumentClient, GetCommand, PutCommand, DeleteCommand, ScanCommand } = require('@aws-sdk/lib-dynamodb'); -const { body, param, validationResult, query } = require('express-validator'); -const { spawn } = require('child_process'); -const path = require('path'); +const { body, param, validationResult } = require('express-validator'); const app = express(); const PORT = process.env.PORT || 3001; @@ -16,22 +14,13 @@ const TOKEN_SECRET = process.env.TOKEN_SECRET_KEY; // Middleware app.use(helmet()); -//app.use(cors()); const corsOptions = { - origin: [ - 'https://config.email-bayarea.com', - ], + origin: ['https://config.email-bayarea.com'], methods: ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE'], - allowedHeaders: [ - 'Content-Type', - 'Accept', - 'Authorization', - 'x-hide-loading', - ], - credentials: false, // true nur wenn Cookies / Auth-Headers mit credentials genutzt werden + allowedHeaders: ['Content-Type', 'Accept', 'Authorization', 'x-hide-loading'], + credentials: false, }; app.use(cors(corsOptions)); -app.options('*', cors(corsOptions)); app.use(express.json()); app.use(morgan('dev')); @@ -43,318 +32,73 @@ const client = new DynamoDBClient({ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, }, }); - const docClient = DynamoDBDocumentClient.from(client); const TABLE_NAME = process.env.DYNAMODB_TABLE || 'email-rules'; -// Validation Middleware -const validateEmailRule = [ - body('email_address').isEmail().withMessage('Valid email address is required'), - body('ooo_active').optional().isBoolean().withMessage('ooo_active must be a boolean'), - body('ooo_message').optional().isString().withMessage('ooo_message must be a string'), - body('ooo_content_type').optional().isIn(['text', 'html']).withMessage('ooo_content_type must be "text" or "html"'), - body('forwards').optional().isArray().withMessage('forwards must be an array'), - body('forwards.*').optional().isEmail().withMessage('All forward addresses must be valid emails'), -]; - -const validateEmail = [ - param('email').isEmail().withMessage('Valid email address is required'), -]; - -// Error Handler Middleware +// Validation const handleValidationErrors = (req, res, next) => { const errors = validationResult(req); - if (!errors.isEmpty()) { - return res.status(400).json({ - error: 'Validation failed', - details: errors.array(), - }); - } + if (!errors.isEmpty()) return res.status(400).json({ error: 'Validation failed', details: errors.array() }); next(); }; -// Token Validation Function const validateToken = (email, expires, signature) => { - // Check expiry const now = Math.floor(Date.now() / 1000); - if (now > parseInt(expires)) { - return { valid: false, error: 'Token expired' }; - } - - // Verify signature + if (now > parseInt(expires)) return { valid: false, error: 'Token expired' }; const data = `${email}|${expires}`; - const expected = crypto - .createHmac('sha256', TOKEN_SECRET) - .update(data) - .digest('hex'); - - if (signature !== expected) { - return { valid: false, error: 'Invalid signature' }; - } - - return { valid: true, email }; + const expected = crypto.createHmac('sha256', TOKEN_SECRET).update(data).digest('hex'); + return signature === expected ? { valid: true, email } : { valid: false, error: 'Invalid signature' }; }; -// Trigger Email Rules Synchronization -const triggerSync = () => { - const syncScriptPath = path.join(__dirname, '../sync/sync.js'); +// --- API Routes --- - console.log('๐Ÿ”„ Triggering email rules synchronization...'); - - const syncProcess = spawn('sudo', ['node', syncScriptPath], { - detached: true, - stdio: 'ignore' - }); - - syncProcess.unref(); // Allow the parent to exit independently - - console.log('โœ… Sync triggered (running in background)'); -}; - -// POST /api/auth/validate-token - Validate authentication token from Roundcube +// 1. Auth check app.post('/api/auth/validate-token', [ - body('email').isEmail().withMessage('Valid email is required'), - body('expires').isNumeric().withMessage('Expires must be a number'), - body('signature').notEmpty().withMessage('Signature is required'), + body('email').isEmail(), + body('expires').isNumeric(), + body('signature').notEmpty(), ], handleValidationErrors, (req, res) => { - try { - const { email, expires, signature } = req.body; - const result = validateToken(email, expires, signature); - - if (!result.valid) { - return res.status(401).json({ - error: 'Invalid token', - message: result.error, - }); - } - - res.json({ - success: true, - email: result.email, - }); - } catch (error) { - console.error('Token validation error:', error); - res.status(500).json({ - error: 'Token validation failed', - message: error.message, - }); - } + const { email, expires, signature } = req.body; + const result = validateToken(email, expires, signature); + if (!result.valid) return res.status(401).json({ error: result.error }); + res.json({ success: true, email: result.email }); }); -// Health Check -app.get('/health', (req, res) => { - res.json({ status: 'OK', timestamp: new Date().toISOString() }); -}); +// 2. Health check +app.get('/health', (req, res) => res.json({ status: 'OK' })); -// GET /api/rules - Get all rules -app.get('/api/rules', async (req, res) => { +// 3. Rules: Get One +app.get('/api/rules/:email', [ + param('email').isEmail() +], handleValidationErrors, async (req, res) => { try { - const command = new ScanCommand({ + const response = await docClient.send(new GetCommand({ TableName: TABLE_NAME, - }); - - const response = await docClient.send(command); - - res.json({ - rules: response.Items || [], - count: response.Count || 0, - }); - } catch (error) { - console.error('Error fetching rules:', error); - res.status(500).json({ - error: 'Failed to fetch rules', - message: error.message, - }); - } -}); - -// GET /api/rules/:email - Get rule for specific email -app.get('/api/rules/:email', validateEmail, handleValidationErrors, async (req, res) => { - try { - const email = decodeURIComponent(req.params.email); - - const command = new GetCommand({ - TableName: TABLE_NAME, - Key: { - email_address: email, - }, - }); - - const response = await docClient.send(command); - - if (!response.Item) { - return res.status(404).json({ - error: 'Rule not found', - message: `No rule exists for email: ${email}`, - }); - } - + Key: { email_address: decodeURIComponent(req.params.email) } + })); + if (!response.Item) return res.status(404).json({ error: 'No rule found' }); res.json(response.Item); } catch (error) { - console.error('Error fetching rule:', error); - res.status(500).json({ - error: 'Failed to fetch rule', - message: error.message, - }); + res.status(500).json({ error: error.message }); } }); -// POST /api/rules - Create or update rule -app.post('/api/rules', validateEmailRule, handleValidationErrors, async (req, res) => { +// 4. Rules: Save/Update (OOO & Forwards) +app.post('/api/rules', [ + body('email_address').isEmail(), + body('ooo_active').isBoolean(), + body('forwards').isArray() +], handleValidationErrors, async (req, res) => { try { - const { email_address, ooo_active, ooo_message, ooo_content_type, forwards } = req.body; - const item = { - email_address, - ooo_active: ooo_active || false, - ooo_message: ooo_message || '', - ooo_content_type: ooo_content_type || 'text', - forwards: forwards || [], + ...req.body, last_updated: new Date().toISOString(), }; - - const command = new PutCommand({ - TableName: TABLE_NAME, - Item: item, - }); - - await docClient.send(command); - - // Trigger immediate synchronization - triggerSync(); - - res.status(201).json({ - message: 'Rule created/updated successfully', - rule: item, - }); + await docClient.send(new PutCommand({ TableName: TABLE_NAME, Item: item })); + res.json({ success: true, rule: item }); } catch (error) { - console.error('Error creating/updating rule:', error); - res.status(500).json({ - error: 'Failed to create/update rule', - message: error.message, - }); + res.status(500).json({ error: error.message }); } }); -// PUT /api/rules/:email - Update existing rule -app.put('/api/rules/:email', validateEmail, validateEmailRule, handleValidationErrors, async (req, res) => { - try { - const email = decodeURIComponent(req.params.email); - const { ooo_active, ooo_message, ooo_content_type, forwards } = req.body; - - // First check if rule exists - const getCommand = new GetCommand({ - TableName: TABLE_NAME, - Key: { email_address: email }, - }); - - const existingRule = await docClient.send(getCommand); - - if (!existingRule.Item) { - return res.status(404).json({ - error: 'Rule not found', - message: `No rule exists for email: ${email}`, - }); - } - - // Merge with existing data - const item = { - ...existingRule.Item, - ooo_active: ooo_active !== undefined ? ooo_active : existingRule.Item.ooo_active, - ooo_message: ooo_message !== undefined ? ooo_message : existingRule.Item.ooo_message, - ooo_content_type: ooo_content_type !== undefined ? ooo_content_type : existingRule.Item.ooo_content_type, - forwards: forwards !== undefined ? forwards : existingRule.Item.forwards, - last_updated: new Date().toISOString(), - }; - - const putCommand = new PutCommand({ - TableName: TABLE_NAME, - Item: item, - }); - - await docClient.send(putCommand); - - // Trigger immediate synchronization - triggerSync(); - - res.json({ - message: 'Rule updated successfully', - rule: item, - }); - } catch (error) { - console.error('Error updating rule:', error); - res.status(500).json({ - error: 'Failed to update rule', - message: error.message, - }); - } -}); - -// DELETE /api/rules/:email - Delete rule -app.delete('/api/rules/:email', validateEmail, handleValidationErrors, async (req, res) => { - try { - const email = decodeURIComponent(req.params.email); - - // First check if rule exists - const getCommand = new GetCommand({ - TableName: TABLE_NAME, - Key: { email_address: email }, - }); - - const existingRule = await docClient.send(getCommand); - - if (!existingRule.Item) { - return res.status(404).json({ - error: 'Rule not found', - message: `No rule exists for email: ${email}`, - }); - } - - const deleteCommand = new DeleteCommand({ - TableName: TABLE_NAME, - Key: { - email_address: email, - }, - }); - - await docClient.send(deleteCommand); - - // Trigger immediate synchronization - triggerSync(); - - res.json({ - message: 'Rule deleted successfully', - email_address: email, - }); - } catch (error) { - console.error('Error deleting rule:', error); - res.status(500).json({ - error: 'Failed to delete rule', - message: error.message, - }); - } -}); - -// 404 Handler -app.use((req, res) => { - res.status(404).json({ - error: 'Not Found', - message: `Cannot ${req.method} ${req.path}`, - }); -}); - -// Global Error Handler -app.use((err, req, res, next) => { - console.error('Unhandled error:', err); - res.status(500).json({ - error: 'Internal Server Error', - message: err.message, - }); -}); - -// Start Server -app.listen(PORT, () => { - console.log(`๐Ÿš€ Email Config API running on port ${PORT}`); - console.log(`๐Ÿ“Š DynamoDB Table: ${TABLE_NAME}`); - console.log(`๐ŸŒ Region: ${process.env.AWS_REGION || 'us-east-2'}`); -}); +app.listen(PORT, () => console.log(`๐Ÿš€ API active on port ${PORT}`)); \ No newline at end of file diff --git a/sync/.env b/sync/.env deleted file mode 100644 index 1eb33f3..0000000 --- a/sync/.env +++ /dev/null @@ -1,13 +0,0 @@ -AWS_ACCESS_KEY_ID=AKIAU6GDWVAQXE5HQCWG -AWS_SECRET_ACCESS_KEY=S7cCm+zIPVXeOdKJmuvTKVh/Ul40jXKhHAv7OfIX -AWS_REGION=us-east-2 -DYNAMODB_TABLE=email-rules - -# Mail server paths (adjust based on your setup) -MAIL_DATA_PATH=/home/timo/docker-mailserver/docker-data/dms/mail-data -MAIL_STATE_PATH=/home/timo/docker-mailserver/docker-data/dms/mail-state -VIRTUAL_ALIASES_PATH=/home/timo/docker-mailserver/docker-data/dms/config/postfix-virtual.cf -SIEVE_BASE_PATH=/home/timo/docker-mailserver/docker-data/dms/mail-data - -# Docker container name -MAILSERVER_CONTAINER=mailserver-new diff --git a/sync/QUICKSTART.md b/sync/QUICKSTART.md deleted file mode 100644 index fa809df..0000000 --- a/sync/QUICKSTART.md +++ /dev/null @@ -1,171 +0,0 @@ -# Quick Start Guide - Email Rules Sync - -## What This Does - -โœ… **Reads email rules from AWS DynamoDB** -โœ… **Generates Sieve scripts for Out-of-Office auto-replies** -โœ… **Generates Postfix virtual aliases for email forwarding** -โœ… **Syncs every 5 minutes automatically** - -## Setup (5 Minutes) - -### Step 1: Setup Sudo Permissions - -```bash -cd /home/timo/config-email/sync -./setup-sudo.sh -``` - -This allows the sync script to change file ownership without password prompts. - -### Step 2: Test Manual Sync - -```bash -sudo node sync.js -``` - -You should see: -``` -โœ… Found X email rules -โœ… Created Sieve script for user@domain.com -โœ… Updated virtual aliases -โœ… Postfix reloaded -โœ… Dovecot reloaded -``` - -### Step 3: Install Cron Job - -```bash -./install-cron.sh -``` - -This sets up automatic syncing every 5 minutes. - -## Verify It's Working - -### Check Logs - -```bash -tail -f /tmp/email-rules-sync.log -``` - -### Check Sieve Scripts (Auto-Reply) - -```bash -# For support@qrmaster.net -cat /home/timo/docker-mailserver/docker-data/dms/mail-data/qrmaster.net/support/home/.dovecot.sieve -``` - -### Check Virtual Aliases (Forwarding) - -```bash -cat /home/timo/docker-mailserver/docker-data/dms/config/postfix-virtual.cf -``` - -## Test Auto-Reply - -1. Go to Roundcube โ†’ Settings โ†’ Email Configuration -2. Click "Open Email Configuration" -3. Enable "Out of Office" -4. Set message: "I'm out until Monday" -5. Click "Update Rule" -6. Wait 5 minutes (or run `sudo node sync.js` manually) -7. Send an email to that address -8. You should receive an auto-reply! ๐ŸŽ‰ - -## Test Forwarding - -1. Go to Roundcube โ†’ Settings โ†’ Email Configuration -2. Click "Open Email Configuration" -3. Go to "Email Forwarding" tab -4. Add forward address: `your@email.com` -5. Click "Update Rule" -6. Wait 5 minutes (or run `sudo node sync.js` manually) -7. Send an email to that address -8. You should receive it at your forward address! ๐ŸŽ‰ - -## Architecture - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ React UI โ”‚ โ† User configures rules -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ†“ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Express API โ”‚ โ† Saves to DynamoDB -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ†“ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ DynamoDB โ”‚ โ† Rules stored here -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ†“ (Every 5 min) -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Sync Script โ”‚ โ† YOU ARE HERE -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ†“ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Mail Server โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Sieve โ”‚ โ”‚ Postfix โ”‚ โ”‚ -โ”‚ โ”‚ OOO โ”‚ โ”‚ Forwarding โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -## Troubleshooting - -### Sync fails with permission error - -Run: `./setup-sudo.sh` - -### Auto-reply not working - -1. Check Sieve script was created: - ```bash - sudo node sync.js - # Look for "โœ… Created Sieve script for..." - ``` - -2. Check Dovecot logs: - ```bash - docker logs mailserver-new 2>&1 | grep -i sieve - ``` - -### Forwarding not working - -1. Check virtual aliases: - ```bash - cat /home/timo/docker-mailserver/docker-data/dms/config/postfix-virtual.cf - ``` - -2. Check Postfix logs: - ```bash - docker logs mailserver-new 2>&1 | grep -i virtual - ``` - -3. Manually reload: - ```bash - docker exec mailserver-new postfix reload - ``` - -## Next Steps - -1. โœ… Setup sudo permissions -2. โœ… Test manual sync -3. โœ… Install cron job -4. โœ… Test auto-reply -5. โœ… Test forwarding -6. ๐ŸŽ‰ Enjoy automated email rules! - -## Files - -- `sync.js` - Main sync script -- `setup-sudo.sh` - Setup sudo permissions -- `install-cron.sh` - Install cron job -- `.env` - Configuration (AWS credentials) -- `QUICKSTART.md` - This file -- `README.md` - Detailed documentation diff --git a/sync/README.md b/sync/README.md deleted file mode 100644 index af85f22..0000000 --- a/sync/README.md +++ /dev/null @@ -1,231 +0,0 @@ -# Email Rules Sync System - -This script synchronizes email rules from AWS DynamoDB to the mail server, enabling: -- **Out-of-Office Auto-Replies** (Sieve scripts) -- **Email Forwarding** (Postfix virtual aliases) - -## How It Works - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ DynamoDB โ”‚ โ† Rules stored here (via Web UI) -โ”‚ email-rules โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ”‚ Sync Script (every 5 minutes) - โ†“ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Mail Server (Docker) โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Sieve โ”‚ โ”‚ Postfix โ”‚ โ”‚ -โ”‚ โ”‚ Scripts โ”‚ โ”‚Virtual Aliasesโ”‚ โ”‚ -โ”‚ โ”‚ (OOO) โ”‚ โ”‚ (Forwarding) โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -## Setup - -### 1. Install Dependencies - -```bash -cd /home/timo/config-email/sync -npm install -``` - -### 2. Configure Environment - -The `.env` file is already configured with: -- AWS credentials -- DynamoDB table name -- Mail server paths - -### 3. Test Manual Sync - -```bash -npm start -``` - -You should see output like: -``` -๐Ÿš€ Starting email rules sync... -๐Ÿ“Š DynamoDB Table: email-rules -๐ŸŒ Region: us-east-2 - -๐Ÿ“ฅ Fetching rules from DynamoDB... -โœ… Found 2 email rules - -๐Ÿ“ Processing Sieve scripts (Out-of-Office)... -โœ… Created Sieve script for support@qrmaster.net -โœ… Processed 1 Sieve scripts - -๐Ÿ“ฎ Updating virtual aliases (Forwarding)... -โœ… Found 0 forwarding rules -โœ… Updated virtual aliases - -๐Ÿ”„ Applying changes to mail server... -โœ… Postfix reloaded -โœ… Dovecot reloaded - -โœจ Sync completed successfully! -โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” -Total Rules: 2 -OOO Active: 1 -Forwarding Active: 0 -โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” -``` - -### 4. Install as Cron Job (Automatic Sync) - -```bash -./install-cron.sh -``` - -This will: -- Run sync every 5 minutes -- Log to `/tmp/email-rules-sync.log` - -## Usage - -### View Sync Logs - -```bash -tail -f /tmp/email-rules-sync.log -``` - -### Manual Sync - -```bash -cd /home/timo/config-email/sync -npm start -``` - -### Check Current Cron Jobs - -```bash -crontab -l -``` - -### Remove Cron Job - -```bash -crontab -l | grep -v "email-rules-sync" | crontab - -``` - -## What Gets Generated - -### 1. Sieve Scripts (Out-of-Office) - -Location: `/home/timo/docker-mailserver/docker-data/dms/mail-data/{domain}/{user}/home/.dovecot.sieve` - -Example for `support@qrmaster.net`: -```sieve -require ["vacation", "variables"]; - -# Auto-Reply / Out-of-Office -# Generated by Email Rules Sync System -# Last updated: 2025-12-27T12:00:00.000Z - -if true { - vacation - :days 1 - :subject "Out of Office" - "I am currently out of office and will respond when I return."; -} -``` - -### 2. Virtual Aliases (Forwarding) - -Location: `/home/timo/docker-mailserver/docker-data/dms/config/postfix-virtual.cf` - -Example: -``` -# Virtual Aliases - Email Forwarding -# Generated by Email Rules Sync System -# Last updated: 2025-12-27T12:00:00.000Z - -# Forwarding for support@qrmaster.net -support@qrmaster.net forward1@example.com,forward2@example.com -``` - -## DynamoDB Schema - -The sync script expects this schema: - -```javascript -{ - email_address: "support@qrmaster.net", // Primary Key - ooo_active: true, // Enable/disable auto-reply - ooo_message: "I am out of office...", // Auto-reply message - ooo_content_type: "text", // "text" or "html" - forwards: ["user@example.com"], // Array of forward addresses - last_updated: "2025-12-27T12:00:00.000Z" // Timestamp -} -``` - -## Troubleshooting - -### Script fails to connect to DynamoDB - -Check AWS credentials in `.env`: -```bash -cat .env | grep AWS -``` - -### Sieve scripts not working - -1. Check script was created: - ```bash - ls -la /home/timo/docker-mailserver/docker-data/dms/mail-data/qrmaster.net/support/home/ - ``` - -2. Check Dovecot logs: - ```bash - docker logs mailserver-new 2>&1 | grep -i sieve - ``` - -### Forwarding not working - -1. Check virtual aliases file: - ```bash - cat /home/timo/docker-mailserver/docker-data/dms/config/postfix-virtual.cf - ``` - -2. Check Postfix logs: - ```bash - docker logs mailserver-new 2>&1 | grep -i virtual - ``` - -3. Reload Postfix manually: - ```bash - docker exec mailserver-new postfix reload - ``` - -## Architecture - -``` -Web UI (React) - โ†“ -Backend API (Express) - โ†“ -DynamoDB (email-rules) - โ†“ -Sync Script (Node.js) โ† You are here - โ†“ -Mail Server (Dovecot + Postfix) -``` - -## Files - -- `sync.js` - Main sync script -- `package.json` - Dependencies -- `.env` - Configuration -- `install-cron.sh` - Cron job installer -- `README.md` - This file - -## Support - -For issues, check: -1. Sync logs: `/tmp/email-rules-sync.log` -2. Mail server logs: `docker logs mailserver-new` -3. DynamoDB table: AWS Console โ†’ DynamoDB โ†’ email-rules diff --git a/sync/install-cron.sh b/sync/install-cron.sh deleted file mode 100755 index 61b8b7a..0000000 --- a/sync/install-cron.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -# Install Email Rules Sync as a cron job - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -SYNC_SCRIPT="$SCRIPT_DIR/sync.js" - -echo "๐Ÿ“ฆ Installing Email Rules Sync Cron Job..." -echo "" - -# Make sync script executable -chmod +x "$SYNC_SCRIPT" - -# Create cron job to run every 30 minutes (as fallback - main sync is event-driven) -CRON_JOB="*/30 * * * * cd $SCRIPT_DIR && sudo /usr/bin/node sync.js >> /tmp/email-rules-sync.log 2>&1" - -# Check if cron job already exists -if crontab -l 2>/dev/null | grep -q "email-rules-sync"; then - echo "โš ๏ธ Cron job already exists. Updating..." - (crontab -l 2>/dev/null | grep -v "email-rules-sync"; echo "$CRON_JOB") | crontab - -else - echo "โž• Adding new cron job..." - (crontab -l 2>/dev/null; echo "$CRON_JOB") | crontab - -fi - -echo "" -echo "โœ… Cron job installed successfully!" -echo "" -echo "The sync script will run every 30 minutes as a fallback." -echo "Main synchronization is event-driven (triggered by API changes)." -echo "Logs are written to: /tmp/email-rules-sync.log" -echo "" -echo "To view current cron jobs:" -echo " crontab -l" -echo "" -echo "To view logs:" -echo " tail -f /tmp/email-rules-sync.log" -echo "" -echo "To run sync manually:" -echo " cd $SCRIPT_DIR && npm start" diff --git a/sync/package-lock.json b/sync/package-lock.json deleted file mode 100644 index 1c68ca6..0000000 --- a/sync/package-lock.json +++ /dev/null @@ -1,1420 +0,0 @@ -{ - "name": "email-rules-sync", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "email-rules-sync", - "version": "1.0.0", - "dependencies": { - "@aws-sdk/client-dynamodb": "^3.679.0", - "@aws-sdk/lib-dynamodb": "^3.679.0", - "dotenv": "^16.4.5" - } - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-dynamodb": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.958.0.tgz", - "integrity": "sha512-R3G5cxf3fsL0CEcTbY1VkSwU1FJtImrhA5I9Eepd8nEO6isZ6C99qVKZtDG9eG7qVNK6zTzUigXac/GFrn6hYA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.957.0", - "@aws-sdk/credential-provider-node": "3.958.0", - "@aws-sdk/dynamodb-codec": "3.957.0", - "@aws-sdk/middleware-endpoint-discovery": "3.957.0", - "@aws-sdk/middleware-host-header": "3.957.0", - "@aws-sdk/middleware-logger": "3.957.0", - "@aws-sdk/middleware-recursion-detection": "3.957.0", - "@aws-sdk/middleware-user-agent": "3.957.0", - "@aws-sdk/region-config-resolver": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@aws-sdk/util-endpoints": "3.957.0", - "@aws-sdk/util-user-agent-browser": "3.957.0", - "@aws-sdk/util-user-agent-node": "3.957.0", - "@smithy/config-resolver": "^4.4.5", - "@smithy/core": "^3.20.0", - "@smithy/fetch-http-handler": "^5.3.8", - "@smithy/hash-node": "^4.2.7", - "@smithy/invalid-dependency": "^4.2.7", - "@smithy/middleware-content-length": "^4.2.7", - "@smithy/middleware-endpoint": "^4.4.1", - "@smithy/middleware-retry": "^4.4.17", - "@smithy/middleware-serde": "^4.2.8", - "@smithy/middleware-stack": "^4.2.7", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/node-http-handler": "^4.4.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", - "@smithy/url-parser": "^4.2.7", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.16", - "@smithy/util-defaults-mode-node": "^4.2.19", - "@smithy/util-endpoints": "^3.2.7", - "@smithy/util-middleware": "^4.2.7", - "@smithy/util-retry": "^4.2.7", - "@smithy/util-utf8": "^4.2.0", - "@smithy/util-waiter": "^4.2.7", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.958.0.tgz", - "integrity": "sha512-6qNCIeaMzKzfqasy2nNRuYnMuaMebCcCPP4J2CVGkA8QYMbIVKPlkn9bpB20Vxe6H/r3jtCCLQaOJjVTx/6dXg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.957.0", - "@aws-sdk/middleware-host-header": "3.957.0", - "@aws-sdk/middleware-logger": "3.957.0", - "@aws-sdk/middleware-recursion-detection": "3.957.0", - "@aws-sdk/middleware-user-agent": "3.957.0", - "@aws-sdk/region-config-resolver": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@aws-sdk/util-endpoints": "3.957.0", - "@aws-sdk/util-user-agent-browser": "3.957.0", - "@aws-sdk/util-user-agent-node": "3.957.0", - "@smithy/config-resolver": "^4.4.5", - "@smithy/core": "^3.20.0", - "@smithy/fetch-http-handler": "^5.3.8", - "@smithy/hash-node": "^4.2.7", - "@smithy/invalid-dependency": "^4.2.7", - "@smithy/middleware-content-length": "^4.2.7", - "@smithy/middleware-endpoint": "^4.4.1", - "@smithy/middleware-retry": "^4.4.17", - "@smithy/middleware-serde": "^4.2.8", - "@smithy/middleware-stack": "^4.2.7", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/node-http-handler": "^4.4.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", - "@smithy/url-parser": "^4.2.7", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.16", - "@smithy/util-defaults-mode-node": "^4.2.19", - "@smithy/util-endpoints": "^3.2.7", - "@smithy/util-middleware": "^4.2.7", - "@smithy/util-retry": "^4.2.7", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/core": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.957.0.tgz", - "integrity": "sha512-DrZgDnF1lQZv75a52nFWs6MExihJF2GZB6ETZRqr6jMwhrk2kbJPUtvgbifwcL7AYmVqHQDJBrR/MqkwwFCpiw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.957.0", - "@aws-sdk/xml-builder": "3.957.0", - "@smithy/core": "^3.20.0", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/property-provider": "^4.2.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/signature-v4": "^5.3.7", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-middleware": "^4.2.7", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.957.0.tgz", - "integrity": "sha512-475mkhGaWCr+Z52fOOVb/q2VHuNvqEDixlYIkeaO6xJ6t9qR0wpLt4hOQaR6zR1wfZV0SlE7d8RErdYq/PByog==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@smithy/property-provider": "^4.2.7", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.957.0.tgz", - "integrity": "sha512-8dS55QHRxXgJlHkEYaCGZIhieCs9NU1HU1BcqQ4RfUdSsfRdxxktqUKgCnBnOOn0oD3PPA8cQOCAVgIyRb3Rfw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@smithy/fetch-http-handler": "^5.3.8", - "@smithy/node-http-handler": "^4.4.7", - "@smithy/property-provider": "^4.2.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", - "@smithy/util-stream": "^4.5.8", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.958.0.tgz", - "integrity": "sha512-u7twvZa1/6GWmPBZs6DbjlegCoNzNjBsMS/6fvh5quByYrcJr/uLd8YEr7S3UIq4kR/gSnHqcae7y2nL2bqZdg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/credential-provider-env": "3.957.0", - "@aws-sdk/credential-provider-http": "3.957.0", - "@aws-sdk/credential-provider-login": "3.958.0", - "@aws-sdk/credential-provider-process": "3.957.0", - "@aws-sdk/credential-provider-sso": "3.958.0", - "@aws-sdk/credential-provider-web-identity": "3.958.0", - "@aws-sdk/nested-clients": "3.958.0", - "@aws-sdk/types": "3.957.0", - "@smithy/credential-provider-imds": "^4.2.7", - "@smithy/property-provider": "^4.2.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.958.0.tgz", - "integrity": "sha512-sDwtDnBSszUIbzbOORGh5gmXGl9aK25+BHb4gb1aVlqB+nNL2+IUEJA62+CE55lXSH8qXF90paivjK8tOHTwPA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/nested-clients": "3.958.0", - "@aws-sdk/types": "3.957.0", - "@smithy/property-provider": "^4.2.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.958.0.tgz", - "integrity": "sha512-vdoZbNG2dt66I7EpN3fKCzi6fp9xjIiwEA/vVVgqO4wXCGw8rKPIdDUus4e13VvTr330uQs2W0UNg/7AgtquEQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.957.0", - "@aws-sdk/credential-provider-http": "3.957.0", - "@aws-sdk/credential-provider-ini": "3.958.0", - "@aws-sdk/credential-provider-process": "3.957.0", - "@aws-sdk/credential-provider-sso": "3.958.0", - "@aws-sdk/credential-provider-web-identity": "3.958.0", - "@aws-sdk/types": "3.957.0", - "@smithy/credential-provider-imds": "^4.2.7", - "@smithy/property-provider": "^4.2.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.957.0.tgz", - "integrity": "sha512-/KIz9kadwbeLy6SKvT79W81Y+hb/8LMDyeloA2zhouE28hmne+hLn0wNCQXAAupFFlYOAtZR2NTBs7HBAReJlg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@smithy/property-provider": "^4.2.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.958.0.tgz", - "integrity": "sha512-CBYHJ5ufp8HC4q+o7IJejCUctJXWaksgpmoFpXerbjAso7/Fg7LLUu9inXVOxlHKLlvYekDXjIUBXDJS2WYdgg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.958.0", - "@aws-sdk/core": "3.957.0", - "@aws-sdk/token-providers": "3.958.0", - "@aws-sdk/types": "3.957.0", - "@smithy/property-provider": "^4.2.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.958.0.tgz", - "integrity": "sha512-dgnvwjMq5Y66WozzUzxNkCFap+umHUtqMMKlr8z/vl9NYMLem/WUbWNpFFOVFWquXikc+ewtpBMR4KEDXfZ+KA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/nested-clients": "3.958.0", - "@aws-sdk/types": "3.957.0", - "@smithy/property-provider": "^4.2.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/dynamodb-codec": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/dynamodb-codec/-/dynamodb-codec-3.957.0.tgz", - "integrity": "sha512-xds1mkwEGzXrNy/gT6/ehaJ+cbYn/QM7AkdwNrO1NBlwJVLo3imO6hOnOQ/0KWG2ck1dbKv9H9f2hka67bAzEA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.957.0", - "@smithy/core": "^3.20.0", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", - "@smithy/util-base64": "^4.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-dynamodb": "^3.957.0" - } - }, - "node_modules/@aws-sdk/endpoint-cache": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.957.0.tgz", - "integrity": "sha512-QxvFejXYYBZp/GBfT7B15gvmvuq+0f2U8RPHqArf5IqBi51ZyBqUD805tQ8TlsVrlLoi+Z4fEFw4HEM5pGvPUg==", - "license": "Apache-2.0", - "dependencies": { - "mnemonist": "0.38.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/lib-dynamodb": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/lib-dynamodb/-/lib-dynamodb-3.958.0.tgz", - "integrity": "sha512-ojqEe4ojhk/MINaaEzqFLcZ9abBGP+zwUxTJh9x2mM2Y7Y4Gvqmwvs5aT790pI8yiPKPDkeF/E+DNEmSCUPtlA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/util-dynamodb": "3.958.0", - "@smithy/core": "^3.20.0", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-dynamodb": "^3.958.0" - } - }, - "node_modules/@aws-sdk/middleware-endpoint-discovery": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.957.0.tgz", - "integrity": "sha512-MJjlw4mVJNTyR5dW6wpzKLRzFPIYAMA8qUWqgG4hGscmm4GFHvWVJ9mhhdpDu7Ie4Uaikmzfy0C4xzZ+lkf1+w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/endpoint-cache": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.957.0.tgz", - "integrity": "sha512-BBgKawVyfQZglEkNTuBBdC3azlyqNXsvvN4jPkWAiNYcY0x1BasaJFl+7u/HisfULstryweJq/dAvIZIxzlZaA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.957.0", - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.957.0.tgz", - "integrity": "sha512-w1qfKrSKHf9b5a8O76yQ1t69u6NWuBjr5kBX+jRWFx/5mu6RLpqERXRpVJxfosbep7k3B+DSB5tZMZ82GKcJtQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.957.0", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.957.0.tgz", - "integrity": "sha512-D2H/WoxhAZNYX+IjkKTdOhOkWQaK0jjJrDBj56hKjU5c9ltQiaX/1PqJ4dfjHntEshJfu0w+E6XJ+/6A6ILBBA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.957.0", - "@aws/lambda-invoke-store": "^0.2.2", - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.957.0.tgz", - "integrity": "sha512-50vcHu96XakQnIvlKJ1UoltrFODjsq2KvtTgHiPFteUS884lQnK5VC/8xd1Msz/1ONpLMzdCVproCQqhDTtMPQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@aws-sdk/util-endpoints": "3.957.0", - "@smithy/core": "^3.20.0", - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/nested-clients": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.958.0.tgz", - "integrity": "sha512-/KuCcS8b5TpQXkYOrPLYytrgxBhv81+5pChkOlhegbeHttjM69pyUpQVJqyfDM/A7wPLnDrzCAnk4zaAOkY0Nw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.957.0", - "@aws-sdk/middleware-host-header": "3.957.0", - "@aws-sdk/middleware-logger": "3.957.0", - "@aws-sdk/middleware-recursion-detection": "3.957.0", - "@aws-sdk/middleware-user-agent": "3.957.0", - "@aws-sdk/region-config-resolver": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@aws-sdk/util-endpoints": "3.957.0", - "@aws-sdk/util-user-agent-browser": "3.957.0", - "@aws-sdk/util-user-agent-node": "3.957.0", - "@smithy/config-resolver": "^4.4.5", - "@smithy/core": "^3.20.0", - "@smithy/fetch-http-handler": "^5.3.8", - "@smithy/hash-node": "^4.2.7", - "@smithy/invalid-dependency": "^4.2.7", - "@smithy/middleware-content-length": "^4.2.7", - "@smithy/middleware-endpoint": "^4.4.1", - "@smithy/middleware-retry": "^4.4.17", - "@smithy/middleware-serde": "^4.2.8", - "@smithy/middleware-stack": "^4.2.7", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/node-http-handler": "^4.4.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", - "@smithy/url-parser": "^4.2.7", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.16", - "@smithy/util-defaults-mode-node": "^4.2.19", - "@smithy/util-endpoints": "^3.2.7", - "@smithy/util-middleware": "^4.2.7", - "@smithy/util-retry": "^4.2.7", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.957.0.tgz", - "integrity": "sha512-V8iY3blh8l2iaOqXWW88HbkY5jDoWjH56jonprG/cpyqqCnprvpMUZWPWYJoI8rHRf2bqzZeql1slxG6EnKI7A==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.957.0", - "@smithy/config-resolver": "^4.4.5", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.958.0.tgz", - "integrity": "sha512-UCj7lQXODduD1myNJQkV+LYcGYJ9iiMggR8ow8Hva1g3A/Na5imNXzz6O67k7DAee0TYpy+gkNw+SizC6min8Q==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.957.0", - "@aws-sdk/nested-clients": "3.958.0", - "@aws-sdk/types": "3.957.0", - "@smithy/property-provider": "^4.2.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/types": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.957.0.tgz", - "integrity": "sha512-wzWC2Nrt859ABk6UCAVY/WYEbAd7FjkdrQL6m24+tfmWYDNRByTJ9uOgU/kw9zqLCAwb//CPvrJdhqjTznWXAg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-dynamodb": { - "version": "3.958.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-dynamodb/-/util-dynamodb-3.958.0.tgz", - "integrity": "sha512-wNGCmCBaj+Om4e93+zkiWv7L+sPGlJuDKHWpndCLdHp3EyHt46KxQpAC5QOMTzbRS0obxl2LqXilX8fbQZxU6A==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-dynamodb": "^3.958.0" - } - }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.957.0.tgz", - "integrity": "sha512-xwF9K24mZSxcxKS3UKQFeX/dPYkEps9wF1b+MGON7EvnbcucrJGyQyK1v1xFPn1aqXkBTFi+SZaMRx5E5YCVFw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.957.0", - "@smithy/types": "^4.11.0", - "@smithy/url-parser": "^4.2.7", - "@smithy/util-endpoints": "^3.2.7", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.957.0.tgz", - "integrity": "sha512-nhmgKHnNV9K+i9daumaIz8JTLsIIML9PE/HUks5liyrjUzenjW/aHoc7WJ9/Td/gPZtayxFnXQSJRb/fDlBuJw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.957.0.tgz", - "integrity": "sha512-exueuwxef0lUJRnGaVkNSC674eAiWU07ORhxBnevFFZEKisln+09Qrtw823iyv5I1N8T+wKfh95xvtWQrNKNQw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.957.0", - "@smithy/types": "^4.11.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.957.0.tgz", - "integrity": "sha512-ycbYCwqXk4gJGp0Oxkzf2KBeeGBdTxz559D41NJP8FlzSej1Gh7Rk40Zo6AyTfsNWkrl/kVi1t937OIzC5t+9Q==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.957.0", - "@aws-sdk/types": "3.957.0", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/xml-builder": { - "version": "3.957.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.957.0.tgz", - "integrity": "sha512-Ai5iiQqS8kJ5PjzMhWcLKN0G2yasAkvpnPlq2EnqlIMdB48HsizElt62qcktdxp4neRMyGkFq4NzgmDbXnhRiA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.11.0", - "fast-xml-parser": "5.2.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws/lambda-invoke-store": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.2.tgz", - "integrity": "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==", - "license": "Apache-2.0", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/abort-controller": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.7.tgz", - "integrity": "sha512-rzMY6CaKx2qxrbYbqjXWS0plqEy7LOdKHS0bg4ixJ6aoGDPNUcLWk/FRNuCILh7GKLG9TFUXYYeQQldMBBwuyw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/config-resolver": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.5.tgz", - "integrity": "sha512-HAGoUAFYsUkoSckuKbCPayECeMim8pOu+yLy1zOxt1sifzEbrsRpYa+mKcMdiHKMeiqOibyPG0sFJnmaV/OGEg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.7", - "@smithy/types": "^4.11.0", - "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-endpoints": "^3.2.7", - "@smithy/util-middleware": "^4.2.7", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/core": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.20.0.tgz", - "integrity": "sha512-WsSHCPq/neD5G/MkK4csLI5Y5Pkd9c1NMfpYEKeghSGaD4Ja1qLIohRQf2D5c1Uy5aXp76DeKHkzWZ9KAlHroQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-serde": "^4.2.8", - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-middleware": "^4.2.7", - "@smithy/util-stream": "^4.5.8", - "@smithy/util-utf8": "^4.2.0", - "@smithy/uuid": "^1.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.7.tgz", - "integrity": "sha512-CmduWdCiILCRNbQWFR0OcZlUPVtyE49Sr8yYL0rZQ4D/wKxiNzBNS/YHemvnbkIWj623fplgkexUd/c9CAKdoA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.7", - "@smithy/property-provider": "^4.2.7", - "@smithy/types": "^4.11.0", - "@smithy/url-parser": "^4.2.7", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "5.3.8", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.8.tgz", - "integrity": "sha512-h/Fi+o7mti4n8wx1SR6UHWLaakwHRx29sizvp8OOm7iqwKGFneT06GCSFhml6Bha5BT6ot5pj3CYZnCHhGC2Rg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.3.7", - "@smithy/querystring-builder": "^4.2.7", - "@smithy/types": "^4.11.0", - "@smithy/util-base64": "^4.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-node": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.7.tgz", - "integrity": "sha512-PU/JWLTBCV1c8FtB8tEFnY4eV1tSfBc7bDBADHfn1K+uRbPgSJ9jnJp0hyjiFN2PMdPzxsf1Fdu0eo9fJ760Xw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.11.0", - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/invalid-dependency": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.7.tgz", - "integrity": "sha512-ncvgCr9a15nPlkhIUx3CU4d7E7WEuVJOV7fS7nnK2hLtPK9tYRBkMHQbhXU1VvvKeBm/O0x26OEoBq+ngFpOEQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/is-array-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", - "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-content-length": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.7.tgz", - "integrity": "sha512-GszfBfCcvt7kIbJ41LuNa5f0wvQCHhnGx/aDaZJCCT05Ld6x6U2s0xsc/0mBFONBZjQJp2U/0uSJ178OXOwbhg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-endpoint": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.1.tgz", - "integrity": "sha512-gpLspUAoe6f1M6H0u4cVuFzxZBrsGZmjx2O9SigurTx4PbntYa4AJ+o0G0oGm1L2oSX6oBhcGHwrfJHup2JnJg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.20.0", - "@smithy/middleware-serde": "^4.2.8", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", - "@smithy/url-parser": "^4.2.7", - "@smithy/util-middleware": "^4.2.7", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-retry": { - "version": "4.4.17", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.17.tgz", - "integrity": "sha512-MqbXK6Y9uq17h+4r0ogu/sBT6V/rdV+5NvYL7ZV444BKfQygYe8wAhDrVXagVebN6w2RE0Fm245l69mOsPGZzg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/service-error-classification": "^4.2.7", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", - "@smithy/util-middleware": "^4.2.7", - "@smithy/util-retry": "^4.2.7", - "@smithy/uuid": "^1.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-serde": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.8.tgz", - "integrity": "sha512-8rDGYen5m5+NV9eHv9ry0sqm2gI6W7mc1VSFMtn6Igo25S507/HaOX9LTHAS2/J32VXD0xSzrY0H5FJtOMS4/w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-stack": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.7.tgz", - "integrity": "sha512-bsOT0rJ+HHlZd9crHoS37mt8qRRN/h9jRve1SXUhVbkRzu0QaNYZp1i1jha4n098tsvROjcwfLlfvcFuJSXEsw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-config-provider": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.7.tgz", - "integrity": "sha512-7r58wq8sdOcrwWe+klL9y3bc4GW1gnlfnFOuL7CXa7UzfhzhxKuzNdtqgzmTV+53lEp9NXh5hY/S4UgjLOzPfw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^4.2.7", - "@smithy/shared-ini-file-loader": "^4.4.2", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-http-handler": { - "version": "4.4.7", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.7.tgz", - "integrity": "sha512-NELpdmBOO6EpZtWgQiHjoShs1kmweaiNuETUpuup+cmm/xJYjT4eUjfhrXRP4jCOaAsS3c3yPsP3B+K+/fyPCQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.2.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/querystring-builder": "^4.2.7", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/property-provider": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.7.tgz", - "integrity": "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/protocol-http": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.7.tgz", - "integrity": "sha512-1r07pb994I20dD/c2seaZhoCuNYm0rWrvBxhCQ70brNh11M5Ml2ew6qJVo0lclB3jMIXirD4s2XRXRe7QEi0xA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-builder": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.7.tgz", - "integrity": "sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.11.0", - "@smithy/util-uri-escape": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-parser": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.7.tgz", - "integrity": "sha512-3X5ZvzUHmlSTHAXFlswrS6EGt8fMSIxX/c3Rm1Pni3+wYWB6cjGocmRIoqcQF9nU5OgGmL0u7l9m44tSUpfj9w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/service-error-classification": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.7.tgz", - "integrity": "sha512-YB7oCbukqEb2Dlh3340/8g8vNGbs/QsNNRms+gv3N2AtZz9/1vSBx6/6tpwQpZMEJFs7Uq8h4mmOn48ZZ72MkA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.11.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.2.tgz", - "integrity": "sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/signature-v4": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.7.tgz", - "integrity": "sha512-9oNUlqBlFZFOSdxgImA6X5GFuzE7V2H7VG/7E70cdLhidFbdtvxxt81EHgykGK5vq5D3FafH//X+Oy31j3CKOg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^4.2.0", - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", - "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-middleware": "^4.2.7", - "@smithy/util-uri-escape": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/smithy-client": { - "version": "4.10.2", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.10.2.tgz", - "integrity": "sha512-D5z79xQWpgrGpAHb054Fn2CCTQZpog7JELbVQ6XAvXs5MNKWf28U9gzSBlJkOyMl9LA1TZEjRtwvGXfP0Sl90g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.20.0", - "@smithy/middleware-endpoint": "^4.4.1", - "@smithy/middleware-stack": "^4.2.7", - "@smithy/protocol-http": "^5.3.7", - "@smithy/types": "^4.11.0", - "@smithy/util-stream": "^4.5.8", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/types": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.11.0.tgz", - "integrity": "sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/url-parser": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.7.tgz", - "integrity": "sha512-/RLtVsRV4uY3qPWhBDsjwahAtt3x2IsMGnP5W1b2VZIe+qgCqkLxI1UOHDZp1Q1QSOrdOR32MF3Ph2JfWT1VHg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/querystring-parser": "^4.2.7", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-base64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", - "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-body-length-browser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", - "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-body-length-node": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", - "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-buffer-from": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", - "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-config-provider": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", - "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.16", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.16.tgz", - "integrity": "sha512-/eiSP3mzY3TsvUOYMeL4EqUX6fgUOj2eUOU4rMMgVbq67TiRLyxT7Xsjxq0bW3OwuzK009qOwF0L2OgJqperAQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^4.2.7", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.19", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.19.tgz", - "integrity": "sha512-3a4+4mhf6VycEJyHIQLypRbiwG6aJvbQAeRAVXydMmfweEPnLLabRbdyo/Pjw8Rew9vjsh5WCdhmDaHkQnhhhA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/config-resolver": "^4.4.5", - "@smithy/credential-provider-imds": "^4.2.7", - "@smithy/node-config-provider": "^4.3.7", - "@smithy/property-provider": "^4.2.7", - "@smithy/smithy-client": "^4.10.2", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-endpoints": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.7.tgz", - "integrity": "sha512-s4ILhyAvVqhMDYREeTS68R43B1V5aenV5q/V1QpRQJkCXib5BPRo4s7uNdzGtIKxaPHCfU/8YkvPAEvTpxgspg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.7", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-hex-encoding": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", - "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-middleware": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.7.tgz", - "integrity": "sha512-i1IkpbOae6NvIKsEeLLM9/2q4X+M90KV3oCFgWQI4q0Qz+yUZvsr+gZPdAEAtFhWQhAHpTsJO8DRJPuwVyln+w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-retry": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.7.tgz", - "integrity": "sha512-SvDdsQyF5CIASa4EYVT02LukPHVzAgUA4kMAuZ97QJc2BpAqZfA4PINB8/KOoCXEw9tsuv/jQjMeaHFvxdLNGg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^4.2.7", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-stream": { - "version": "4.5.8", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.8.tgz", - "integrity": "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/fetch-http-handler": "^5.3.8", - "@smithy/node-http-handler": "^4.4.7", - "@smithy/types": "^4.11.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-uri-escape": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", - "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-utf8": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", - "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-waiter": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.7.tgz", - "integrity": "sha512-vHJFXi9b7kUEpHWUCY3Twl+9NPOZvQ0SAi+Ewtn48mbiJk4JY9MZmKQjGB4SCvVb9WPiSphZJYY6RIbs+grrzw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.2.7", - "@smithy/types": "^4.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/uuid": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", - "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/bowser": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz", - "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==", - "license": "MIT" - }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/fast-xml-parser": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", - "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^2.1.0" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/mnemonist": { - "version": "0.38.3", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz", - "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==", - "license": "MIT", - "dependencies": { - "obliterator": "^1.6.1" - } - }, - "node_modules/obliterator": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-1.6.1.tgz", - "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==", - "license": "MIT" - }, - "node_modules/strnum": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", - "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - } - } -} diff --git a/sync/package.json b/sync/package.json deleted file mode 100644 index 86fae00..0000000 --- a/sync/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "email-rules-sync", - "version": "1.0.0", - "description": "Sync email rules from DynamoDB to mail server", - "main": "sync.js", - "type": "module", - "scripts": { - "start": "node sync.js", - "sync": "node sync.js" - }, - "dependencies": { - "@aws-sdk/client-dynamodb": "^3.679.0", - "@aws-sdk/lib-dynamodb": "^3.679.0", - "dotenv": "^16.4.5" - } -} diff --git a/sync/setup-sudo.sh b/sync/setup-sudo.sh deleted file mode 100755 index 5e793aa..0000000 --- a/sync/setup-sudo.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash -# Setup sudo permissions for email sync script - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -SYNC_SCRIPT="$SCRIPT_DIR/sync.js" -USERNAME=$(whoami) - -echo "๐Ÿ” Setting up sudo permissions for email rules sync..." -echo "" - -# Create sudoers file -SUDOERS_FILE="/etc/sudoers.d/email-rules-sync" - -# Check if already configured -if [ -f "$SUDOERS_FILE" ]; then - echo "โš ๏ธ Sudoers file already exists at $SUDOERS_FILE" - echo "Remove it first if you want to recreate it" - exit 1 -fi - -# Create temp file -TEMP_SUDOERS=$(mktemp) - -cat > "$TEMP_SUDOERS" << EOF -# Allow $USERNAME to run email-rules-sync without password -# This is needed to change file ownership to mail server user (UID 5000) -$USERNAME ALL=(ALL) NOPASSWD: /usr/bin/node $SYNC_SCRIPT -EOF - -# Validate sudoers syntax -if visudo -c -f "$TEMP_SUDOERS" 2>/dev/null; then - echo "โœ… Sudoers file syntax is valid" - echo "Moving to $SUDOERS_FILE..." - sudo mv "$TEMP_SUDOERS" "$SUDOERS_FILE" - sudo chmod 0440 "$SUDOERS_FILE" - echo "โœ… Sudo permissions configured successfully!" - echo "" - echo "You can now run:" - echo " sudo node $SYNC_SCRIPT" - echo "" - echo "Without entering a password." -else - echo "โŒ Sudoers file syntax error!" - rm -f "$TEMP_SUDOERS" - exit 1 -fi diff --git a/sync/sync-wrapper.sh b/sync/sync-wrapper.sh deleted file mode 100755 index 15ed035..0000000 --- a/sync/sync-wrapper.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# Wrapper script to run sync with proper permissions - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -CONTAINER="mailserver-new" - -# Run sync script and capture output -echo "๐Ÿš€ Running email rules sync..." - -# Copy sync script to container -docker cp "$SCRIPT_DIR/sync.js" $CONTAINER:/tmp/sync.js -docker cp "$SCRIPT_DIR/.env" $CONTAINER:/tmp/.env -docker cp "$SCRIPT_DIR/package.json" $CONTAINER:/tmp/package.json - -# Install dependencies in container if needed -docker exec $CONTAINER bash -c "cd /tmp && npm install --quiet 2>/dev/null || true" - -# Run sync inside container with proper environment -docker exec -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_REGION \ - $CONTAINER bash -c "cd /tmp && node sync.js" - -echo "" -echo "โœ… Sync completed" diff --git a/sync/sync.js b/sync/sync.js deleted file mode 100755 index c5dbf71..0000000 --- a/sync/sync.js +++ /dev/null @@ -1,260 +0,0 @@ -#!/usr/bin/env node -import 'dotenv/config'; -import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; -import { DynamoDBDocumentClient, ScanCommand } from '@aws-sdk/lib-dynamodb'; -import fs from 'fs/promises'; -import path from 'path'; -import { execSync } from 'child_process'; - -// AWS DynamoDB Configuration -const client = new DynamoDBClient({ - region: process.env.AWS_REGION || 'us-east-2', - credentials: { - accessKeyId: process.env.AWS_ACCESS_KEY_ID, - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, - }, -}); - -const docClient = DynamoDBDocumentClient.from(client); -const TABLE_NAME = process.env.DYNAMODB_TABLE || 'email-rules'; - -// Paths -const VIRTUAL_ALIASES_PATH = process.env.VIRTUAL_ALIASES_PATH; -const SIEVE_BASE_PATH = process.env.SIEVE_BASE_PATH; -const MAILSERVER_CONTAINER = process.env.MAILSERVER_CONTAINER || 'mailserver-new'; - -/** - * Generate Sieve script for Out-of-Office auto-reply - */ -function generateSieveScript(rule) { - const { ooo_message, ooo_content_type } = rule; - - // Escape special characters in the message - const escapedMessage = ooo_message.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); - - const script = `require ["vacation", "variables"]; - -# Auto-Reply / Out-of-Office -# Generated by Email Rules Sync System -# Last updated: ${new Date().toISOString()} - -if true { - vacation - :days 1 - :subject "Out of Office" - ${ooo_content_type === 'html' ? ':mime' : ''} - "${escapedMessage}"; -} -`; - - return script; -} - -/** - * Get Sieve script path for an email address - */ -function getSievePath(email) { - const [user, domain] = email.split('@'); - return path.join(SIEVE_BASE_PATH, domain, user, 'home', '.dovecot.sieve'); -} - -/** - * Write or remove Sieve script based on OOO status - */ -async function manageSieveScript(rule) { - const { email_address, ooo_active } = rule; - const [user, domain] = email_address.split('@'); - - // Check if mailbox exists first - const mailboxPath = path.join(SIEVE_BASE_PATH, domain, user); - - try { - await fs.access(mailboxPath); - } catch (error) { - // Mailbox doesn't exist - skip silently - if (ooo_active) { - console.log(`โš ๏ธ Skipping ${email_address} - mailbox not found (user might not exist yet)`); - } - return false; - } - - const sievePath = getSievePath(email_address); - const sieveDir = path.dirname(sievePath); - - try { - if (ooo_active) { - // Create Sieve script - const script = generateSieveScript(rule); - - // Ensure directory exists - await fs.mkdir(sieveDir, { recursive: true }); - - // Write Sieve script - await fs.writeFile(sievePath, script, 'utf8'); - console.log(`โœ… Created Sieve script for ${email_address}`); - - // Set proper permissions and ownership (important for mail server) - await fs.chmod(sievePath, 0o644); - - // Change ownership to mail server user (UID 5000) - try { - await fs.chown(sievePath, 5000, 5000); - } catch (error) { - console.warn(`โš ๏ธ Could not change ownership for ${sievePath} - run with sudo`); - } - - return true; - } else { - // Remove Sieve script if it exists - try { - await fs.unlink(sievePath); - console.log(`๐Ÿ—‘๏ธ Removed Sieve script for ${email_address}`); - } catch (error) { - if (error.code !== 'ENOENT') { - console.error(`โŒ Error removing Sieve for ${email_address}:`, error.message); - } - } - return false; - } - } catch (error) { - console.error(`โŒ Error managing Sieve for ${email_address}:`, error.message); - return false; - } -} - -/** - * Generate Postfix virtual aliases content - */ -function generateVirtualAliases(rules) { - const lines = [ - '# Virtual Aliases - Email Forwarding', - '# Generated by Email Rules Sync System', - `# Last updated: ${new Date().toISOString()}`, - '', - ]; - - for (const rule of rules) { - const { email_address, forwards } = rule; - - if (forwards && forwards.length > 0) { - // Add comment - lines.push(`# Forwarding for ${email_address}`); - - // Add forwarding rule - // Format: source_email destination1,destination2,destination3 - const destinations = forwards.join(','); - lines.push(`${email_address} ${destinations}`); - lines.push(''); - } - } - - return lines.join('\n'); -} - -/** - * Write virtual aliases file - */ -async function updateVirtualAliases(rules) { - try { - const content = generateVirtualAliases(rules); - await fs.writeFile(VIRTUAL_ALIASES_PATH, content, 'utf8'); - console.log(`โœ… Updated virtual aliases at ${VIRTUAL_ALIASES_PATH}`); - - // Set proper permissions - await fs.chmod(VIRTUAL_ALIASES_PATH, 0o644); - - return true; - } catch (error) { - console.error(`โŒ Error updating virtual aliases:`, error.message); - return false; - } -} - -/** - * Reload mail server services - */ -function reloadMailServer() { - try { - console.log('๐Ÿ”„ Reloading mail server services...'); - - // Reload Postfix - execSync(`docker exec ${MAILSERVER_CONTAINER} postfix reload`, { stdio: 'inherit' }); - console.log('โœ… Postfix reloaded'); - - // Reload Dovecot (for Sieve changes) - execSync(`docker exec ${MAILSERVER_CONTAINER} doveadm reload`, { stdio: 'inherit' }); - console.log('โœ… Dovecot reloaded'); - - return true; - } catch (error) { - console.error('โŒ Error reloading mail server:', error.message); - return false; - } -} - -/** - * Main sync function - */ -async function syncEmailRules() { - console.log('๐Ÿš€ Starting email rules sync...'); - console.log(`๐Ÿ“Š DynamoDB Table: ${TABLE_NAME}`); - console.log(`๐ŸŒ Region: ${process.env.AWS_REGION}`); - console.log(''); - - try { - // 1. Fetch all rules from DynamoDB - console.log('๐Ÿ“ฅ Fetching rules from DynamoDB...'); - const command = new ScanCommand({ - TableName: TABLE_NAME, - }); - - const response = await docClient.send(command); - const rules = response.Items || []; - - console.log(`โœ… Found ${rules.length} email rules`); - console.log(''); - - if (rules.length === 0) { - console.log('โ„น๏ธ No rules to sync. Exiting.'); - return; - } - - // 2. Process Sieve scripts (Out-of-Office) - console.log('๐Ÿ“ Processing Sieve scripts (Out-of-Office)...'); - let sieveCount = 0; - for (const rule of rules) { - const success = await manageSieveScript(rule); - if (success) sieveCount++; - } - console.log(`โœ… Processed ${sieveCount} Sieve scripts`); - console.log(''); - - // 3. Update virtual aliases (Forwarding) - console.log('๐Ÿ“ฎ Updating virtual aliases (Forwarding)...'); - const forwardingRules = rules.filter(r => r.forwards && r.forwards.length > 0); - console.log(`โœ… Found ${forwardingRules.length} forwarding rules`); - await updateVirtualAliases(rules); - console.log(''); - - // 4. Reload mail server - console.log('๐Ÿ”„ Applying changes to mail server...'); - reloadMailServer(); - console.log(''); - - // 5. Summary - console.log('โœจ Sync completed successfully!'); - console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'); - console.log(`Total Rules: ${rules.length}`); - console.log(`OOO Active: ${sieveCount}`); - console.log(`Forwarding Active: ${forwardingRules.length}`); - console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'); - - } catch (error) { - console.error('โŒ Sync failed:', error.message); - console.error(error); - process.exit(1); - } -} - -// Run sync -syncEmailRules(); diff --git a/sync/view-db.js b/sync/view-db.js deleted file mode 100755 index 929083e..0000000 --- a/sync/view-db.js +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env node -import 'dotenv/config'; -import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; -import { DynamoDBDocumentClient, ScanCommand } from '@aws-sdk/lib-dynamodb'; - -const client = new DynamoDBClient({ - region: process.env.AWS_REGION || 'us-east-2', - credentials: { - accessKeyId: process.env.AWS_ACCESS_KEY_ID, - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, - }, -}); - -const docClient = DynamoDBDocumentClient.from(client); -const TABLE_NAME = process.env.DYNAMODB_TABLE || 'email-rules'; - -async function viewDatabase() { - console.log('๐Ÿ“Š DynamoDB Table:', TABLE_NAME); - console.log('๐ŸŒ Region:', process.env.AWS_REGION); - console.log(''); - - try { - const command = new ScanCommand({ - TableName: TABLE_NAME, - }); - - const response = await docClient.send(command); - const rules = response.Items || []; - - console.log(`โœ… Found ${rules.length} email rules:\n`); - console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'); - - for (const rule of rules) { - console.log(`\n๐Ÿ“ง Email: ${rule.email_address}`); - console.log(` OOO Active: ${rule.ooo_active ? 'โœ… YES' : 'โŒ NO'}`); - if (rule.ooo_active) { - console.log(` OOO Message: "${rule.ooo_message.substring(0, 50)}${rule.ooo_message.length > 50 ? '...' : ''}"`); - console.log(` Content Type: ${rule.ooo_content_type}`); - } - console.log(` Forwards: ${rule.forwards && rule.forwards.length > 0 ? rule.forwards.join(', ') : 'None'}`); - console.log(` Last Updated: ${rule.last_updated || 'N/A'}`); - console.log(' โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€'); - } - - console.log('\nโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n'); - - } catch (error) { - console.error('โŒ Error fetching data:', error.message); - process.exit(1); - } -} - -viewDatabase();