- Delete data-entry-app/ (1.6GB), reports-app/ (447MB), .auto-build-data/
- Saved ~1.4GB disk space (64% reduction: 2.2GB → 845MB)
Updated references across 38 files:
- .claude/rules/ paths: backend/modules/, src/modules/
- .claude/commands/validate.md: all validation paths
- docs/ (13 files): data-entry, telegram, README, CLAUDE.md
- scripts/ (3 files): backup-secrets, restore-secrets, test-docker
- security/ (2 files): git_cleanup, SECURITY_PROCEDURES
- deployment/ & shared/: updated all stale comments
All paths now reflect ultrathin monolith architecture:
- Backend: backend/modules/{reports,data_entry,telegram}/
- Frontend: src/modules/{reports,data-entry}/
- Shared: shared/{auth,database,routes}/
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
242 lines
6.9 KiB
Bash
242 lines
6.9 KiB
Bash
#!/bin/bash
|
|
# ============================================================================
|
|
# ROA2WEB - Backup Environment Secrets (Encrypted)
|
|
# ============================================================================
|
|
# This script creates encrypted backups of all .env files containing secrets
|
|
# Usage:
|
|
# ./scripts/backup-secrets.sh # Interactive (prompts for password)
|
|
# BACKUP_PASSWORD="your-pass" ./scripts/backup-secrets.sh # Non-interactive
|
|
#
|
|
# Requirements: openssl (usually pre-installed)
|
|
# Output: secrets-backup/YYYY-MM-DD/*.env.enc
|
|
|
|
set -e
|
|
|
|
# Colors for output
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
RED='\033[0;31m'
|
|
NC='\033[0m' # No Color
|
|
|
|
echo -e "${GREEN}=== ROA2WEB Secrets Backup Tool ===${NC}"
|
|
echo ""
|
|
|
|
# Check if openssl is installed
|
|
if ! command -v openssl &> /dev/null; then
|
|
echo -e "${RED}Error: openssl is not installed${NC}"
|
|
echo "Install with: sudo apt-get install openssl"
|
|
exit 1
|
|
fi
|
|
|
|
# Get password
|
|
if [ -z "$BACKUP_PASSWORD" ]; then
|
|
echo -e "${YELLOW}Enter encryption password (will be hidden):${NC}"
|
|
read -s BACKUP_PASSWORD
|
|
echo ""
|
|
|
|
if [ -z "$BACKUP_PASSWORD" ]; then
|
|
echo -e "${RED}Error: Password cannot be empty${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
echo -e "${YELLOW}Confirm password:${NC}"
|
|
read -s BACKUP_PASSWORD_CONFIRM
|
|
echo ""
|
|
|
|
if [ "$BACKUP_PASSWORD" != "$BACKUP_PASSWORD_CONFIRM" ]; then
|
|
echo -e "${RED}Error: Passwords do not match${NC}"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Create backup directory with timestamp
|
|
BACKUP_DATE=$(date +%Y-%m-%d_%H-%M-%S)
|
|
BACKUP_DIR="secrets-backup/${BACKUP_DATE}"
|
|
mkdir -p "${BACKUP_DIR}"
|
|
|
|
echo -e "${YELLOW}Backup directory: ${BACKUP_DIR}${NC}"
|
|
echo ""
|
|
|
|
# List of secret files to backup
|
|
SECRET_FILES=(
|
|
"backend/.env"
|
|
"backend/.env.prod"
|
|
"backend/modules/telegram/.env"
|
|
"backend/modules/telegram/.env.prod"
|
|
)
|
|
|
|
# List of secret directories to backup (will backup all files inside)
|
|
SECRET_DIRS=(
|
|
"secrets"
|
|
)
|
|
|
|
# Counter for backed up files
|
|
BACKED_UP=0
|
|
SKIPPED=0
|
|
|
|
# Backup each file
|
|
for file in "${SECRET_FILES[@]}"; do
|
|
if [ -f "$file" ]; then
|
|
echo -e "Encrypting: ${GREEN}$file${NC}"
|
|
|
|
# Extract filename
|
|
filename=$(basename "$file")
|
|
|
|
# Determine component for unique naming
|
|
if [[ "$file" == *"backend"* ]]; then
|
|
component="backend"
|
|
else
|
|
component="telegram-bot"
|
|
fi
|
|
|
|
output_file="${BACKUP_DIR}/${component}-${filename}.enc"
|
|
|
|
# Encrypt with AES-256-CBC using openssl
|
|
if echo "$BACKUP_PASSWORD" | openssl enc -aes-256-cbc -salt -pbkdf2 \
|
|
-in "$file" -out "$output_file" -pass stdin 2>/dev/null; then
|
|
echo -e " ✅ Saved to: ${output_file}"
|
|
BACKED_UP=$((BACKED_UP + 1))
|
|
else
|
|
echo -e " ${RED}❌ Failed to encrypt${NC}"
|
|
fi
|
|
else
|
|
echo -e "Skipping: ${YELLOW}$file${NC} (not found)"
|
|
SKIPPED=$((SKIPPED + 1))
|
|
fi
|
|
echo ""
|
|
done
|
|
|
|
# Backup directories
|
|
for dir in "${SECRET_DIRS[@]}"; do
|
|
if [ -d "$dir" ]; then
|
|
echo -e "Encrypting directory: ${GREEN}$dir${NC}"
|
|
|
|
# Create tar archive of the directory
|
|
dir_name=$(basename "$dir")
|
|
output_file="${BACKUP_DIR}/${dir_name}.tar.enc"
|
|
|
|
# Create tar and encrypt (pipe tar directly to openssl)
|
|
if tar -cf - "$dir" 2>/dev/null | \
|
|
openssl enc -aes-256-cbc -salt -pbkdf2 \
|
|
-out "$output_file" -pass pass:"$BACKUP_PASSWORD" 2>/dev/null; then
|
|
echo -e " ✅ Saved to: ${output_file}"
|
|
BACKED_UP=$((BACKED_UP + 1))
|
|
|
|
# Count files in directory
|
|
file_count=$(find "$dir" -type f | wc -l)
|
|
echo -e " 📁 Included ${file_count} file(s) from directory"
|
|
else
|
|
echo -e " ${RED}❌ Failed to encrypt directory${NC}"
|
|
fi
|
|
else
|
|
echo -e "Skipping: ${YELLOW}$dir${NC} (not found)"
|
|
SKIPPED=$((SKIPPED + 1))
|
|
fi
|
|
echo ""
|
|
done
|
|
|
|
# Create README in backup directory
|
|
cat > "${BACKUP_DIR}/README.md" <<EOF
|
|
# ROA2WEB Secrets Backup
|
|
|
|
**Date:** ${BACKUP_DATE}
|
|
**Backed up files:** ${BACKED_UP}
|
|
**Encryption:** AES-256-CBC with PBKDF2
|
|
|
|
## Files in this backup:
|
|
|
|
### Environment Files:
|
|
$(for file in "${SECRET_FILES[@]}"; do
|
|
if [ -f "$file" ]; then
|
|
filename=$(basename "$file")
|
|
if [[ "$file" == *"backend"* ]]; then
|
|
component="backend"
|
|
else
|
|
component="telegram-bot"
|
|
fi
|
|
echo "- ${component}-${filename}.enc (encrypted)"
|
|
fi
|
|
done)
|
|
|
|
### Directories:
|
|
$(for dir in "${SECRET_DIRS[@]}"; do
|
|
if [ -d "$dir" ]; then
|
|
dir_name=$(basename "$dir")
|
|
file_count=$(find "$dir" -type f 2>/dev/null | wc -l)
|
|
echo "- ${dir_name}.tar.enc (encrypted tar archive, ${file_count} files)"
|
|
fi
|
|
done)
|
|
|
|
## How to restore:
|
|
|
|
\`\`\`bash
|
|
# Restore all files automatically:
|
|
./scripts/restore-secrets.sh ${BACKUP_DATE}
|
|
|
|
# Or manually decrypt a single file:
|
|
openssl enc -aes-256-cbc -d -pbkdf2 -in backend-.env.enc -out .env
|
|
|
|
# When prompted, enter the encryption password
|
|
\`\`\`
|
|
|
|
## Manual restore to specific location:
|
|
|
|
\`\`\`bash
|
|
# Backend .env
|
|
openssl enc -aes-256-cbc -d -pbkdf2 \\
|
|
-in backend-.env.enc \\
|
|
-out ../../../backend/.env
|
|
|
|
# Backend .env.prod
|
|
openssl enc -aes-256-cbc -d -pbkdf2 \\
|
|
-in backend-.env.prod.enc \\
|
|
-out ../../../backend/.env.prod
|
|
|
|
# Telegram Bot .env
|
|
openssl enc -aes-256-cbc -d -pbkdf2 \\
|
|
-in telegram-bot-.env.enc \\
|
|
-out ../../../backend/modules/telegram/.env
|
|
|
|
# Telegram Bot .env.prod
|
|
openssl enc -aes-256-cbc -d -pbkdf2 \\
|
|
-in telegram-bot-.env.prod.enc \\
|
|
-out ../../../backend/modules/telegram/.env.prod
|
|
|
|
# Decrypt and extract secrets directory
|
|
openssl enc -aes-256-cbc -d -pbkdf2 -in secrets.tar.enc | \\
|
|
tar -xf - -C ../../..
|
|
\`\`\`
|
|
|
|
## Security Notes:
|
|
|
|
- Files encrypted with AES-256-CBC using OpenSSL
|
|
- Password-based encryption with PBKDF2 key derivation
|
|
- Keep the encryption password safe in your password manager
|
|
- Never commit decrypted .env files to git
|
|
|
|
## Password Storage Recommendation:
|
|
|
|
Store in password manager as:
|
|
- **Title:** ROA2WEB Secrets Backup Password
|
|
- **Type:** Secure Note or Password
|
|
- **Notes:** Encryption password for secrets-backup/${BACKUP_DATE}
|
|
EOF
|
|
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo -e "${GREEN}✅ Backup completed successfully${NC}"
|
|
echo ""
|
|
echo "📊 Summary:"
|
|
echo " - Backed up: ${BACKED_UP} files"
|
|
echo " - Skipped: ${SKIPPED} files"
|
|
echo " - Location: ${BACKUP_DIR}"
|
|
echo ""
|
|
echo "📝 To restore all files:"
|
|
echo " ./scripts/restore-secrets.sh ${BACKUP_DATE}"
|
|
echo ""
|
|
echo "📝 To restore a single file:"
|
|
echo " cd ${BACKUP_DIR}"
|
|
echo " openssl enc -aes-256-cbc -d -pbkdf2 -in backend-.env.enc -out .env"
|
|
echo ""
|
|
echo -e "${YELLOW}⚠️ IMPORTANT: Save encryption password in password manager!${NC}"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|