2026-07-26 21:21:03 -04:00
#!/bin/bash
# =============================================================================
2026-07-26 21:31:05 -04:00
# Added to git repository 07-26-2026
2026-07-26 21:21:03 -04:00
# NAS08 → NAS16 Sync Script
# Source : NAS08 (192.168.150.35) /mnt/kingdezigns08/Public
# Dest : NAS16 (192.168.150.40) /export/kingdezignsnas-16/Public
#
# Behavior:
# - Copies only changed/new files from NAS08 to NAS16
# - Does NOT delete files on NAS16 that are missing from NAS08
# - Preserves file timestamps and symlinks
# - Skips permission syncing (NFS all_squash incompatible with chmod)
# - Skips directory timestamp syncing (NFS all_squash incompatible with utimes)
# - Auto-corrects destination ownership/permissions before sync if needed
# - Sends an HTML status email after every run
# - Logs every run with timestamps
#
# Schedule via OMV Task Scheduler: 30 2 */2 * *
# =============================================================================
# --- Configuration -----------------------------------------------------------
SOURCE = "/mnt/kingdezigns08/Public/"
DEST = "/export/kingdezignsnas-16/Public/"
LOG_DIR = "/var/log/nas-sync"
LOG_FILE = " ${ LOG_DIR } /nas08_to_nas16_ $( date +%Y-%m-%d) .log "
LOCK_FILE = "/tmp/nas08_to_nas16.lock"
REPORT_TO = "rufus.king@kingdezigns.com"
REPORT_FROM = "nas-sync@kingdezigns16.local"
NOW = $( date '+%Y-%m-%d %H:%M:%S' )
NFS_MOUNT = "/mnt/kingdezigns08"
DEST_EXPECTED_OWNER = "www-data"
DEST_EXPECTED_GROUP = "ncshare"
# --- HTML escape helper ------------------------------------------------------
html_escape( ) { sed 's/&/\&/g; s/</\</g; s/>/\>/g; s/"/\"/g' ; }
# --- Safety Checks -----------------------------------------------------------
# Ensure log directory exists and is owned by the running user.
mkdir -p " $LOG_DIR "
if [ " $( stat -c '%U' " $LOG_DIR " ) " != " $( whoami) " ] ; then
sudo chown -R " $( whoami) " :users " $LOG_DIR " 2>/dev/null || true
fi
# Prevent overlapping runs — but self-heal stale lock files.
if [ -f " $LOCK_FILE " ] ; then
LOCK_PID = $( cat " $LOCK_FILE " 2>/dev/null)
if [ -n " $LOCK_PID " ] && kill -0 " $LOCK_PID " 2>/dev/null; then
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] ERROR: Sync already running as PID $LOCK_PID . Exiting. " | tee -a " $LOG_FILE "
exit 1
else
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] WARNING: Stale lock file found (PID $LOCK_PID no longer running). Removing and continuing. " | tee -a " $LOG_FILE "
rm -f " $LOCK_FILE "
fi
fi
echo $$ > " $LOCK_FILE "
# Wait for NFS mount to be fully ready (up to 30 seconds)
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] Checking NFS mount readiness... " | tee -a " $LOG_FILE "
WAIT = 0
until mountpoint -q " $NFS_MOUNT " ; do
sleep 5
WAIT = $(( WAIT + 5 ))
if [ $WAIT -ge 30 ] ; then
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] ERROR: NFS mount $NFS_MOUNT not ready after 30 seconds. Aborting. " | tee -a " $LOG_FILE "
rm -f " $LOCK_FILE "
exit 1
fi
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] Waiting for NFS mount... ( ${ WAIT } s) " | tee -a " $LOG_FILE "
done
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] NFS mount ready. " | tee -a " $LOG_FILE "
# Verify source is mounted and not empty
if [ ! -d " $SOURCE " ] || [ -z " $( ls -A " $SOURCE " 2>/dev/null) " ] ; then
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] ERROR: Source ' $SOURCE ' is not mounted or is empty. Aborting to prevent data loss. " | tee -a " $LOG_FILE "
rm -f " $LOCK_FILE "
exit 1
fi
# Verify destination exists
if [ ! -d " $DEST " ] ; then
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] ERROR: Destination ' $DEST ' does not exist. Aborting. " | tee -a " $LOG_FILE "
rm -f " $LOCK_FILE "
exit 1
fi
# --- Destination Permission Precheck -----------------------------------------
# NFS all_squash maps all writes to www-data:ncshare (anonuid=33, anongid=1001).
# If the destination tree is not owned by www-data:ncshare, rsync will fail to
# set permissions and timestamps. Self-correct before the sync runs so this
# never requires manual intervention.
PERM_CORRECTED = false
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] Verifying destination ownership and permissions... " | tee -a " $LOG_FILE "
DEST_OWNER = $( stat -c '%U' " $DEST " 2>/dev/null)
DEST_GROUP = $( stat -c '%G' " $DEST " 2>/dev/null)
if [ " $DEST_OWNER " != " $DEST_EXPECTED_OWNER " ] || [ " $DEST_GROUP " != " $DEST_EXPECTED_GROUP " ] ; then
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] WARNING: Destination ownership is ${ DEST_OWNER } : ${ DEST_GROUP } — expected ${ DEST_EXPECTED_OWNER } : ${ DEST_EXPECTED_GROUP } . Correcting now... " | tee -a " $LOG_FILE "
sudo chown -R " ${ DEST_EXPECTED_OWNER } : ${ DEST_EXPECTED_GROUP } " " $DEST " 2>& 1 | tee -a " $LOG_FILE "
sudo chmod -R 2770 " $DEST " 2>& 1 | tee -a " $LOG_FILE "
sudo setfacl -R -m " g: ${ DEST_EXPECTED_GROUP } :rwx " " $DEST " 2>& 1 | tee -a " $LOG_FILE "
sudo setfacl -R -d -m " g: ${ DEST_EXPECTED_GROUP } :rwx " " $DEST " 2>& 1 | tee -a " $LOG_FILE "
PERM_CORRECTED = true
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] Destination permissions corrected successfully. " | tee -a " $LOG_FILE "
else
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] Destination ownership OK ( ${ DEST_OWNER } : ${ DEST_GROUP } ). " | tee -a " $LOG_FILE "
fi
# --- Run Sync ----------------------------------------------------------------
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] ===== NAS08 → NAS16 Sync Started ===== " | tee -a " $LOG_FILE "
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] Source : $SOURCE " | tee -a " $LOG_FILE "
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] Dest : $DEST " | tee -a " $LOG_FILE "
START_TIME = $( date +%s)
# --archive : recursive, preserve symlinks, owner, group, devices
# --no-perms : skip chmod — NFS all_squash prevents non-owner chmod
# --omit-dir-times : skip directory utimes — NFS all_squash prevents non-owner utimes on dirs
# File timestamps are still preserved exactly.
# --update : skip files that are newer on destination
RSYNC_OUTPUT = $( rsync \
--archive \
--no-perms \
--omit-dir-times \
--update \
--human-readable \
--itemize-changes \
--stats \
--exclude= '.Trash-1000/' \
" $SOURCE " \
" $DEST " 2>& 1)
RSYNC_EXIT = $?
END_TIME = $( date +%s)
ELAPSED = $(( END_TIME - START_TIME ))
ELAPSED_FMT = $( printf '%dm %ds' $(( ELAPSED/60)) $(( ELAPSED%60)) )
# Log full rsync output
echo " $RSYNC_OUTPUT " >> " $LOG_FILE "
# --- Parse rsync stats -------------------------------------------------------
FILES_TOTAL = $( echo " $RSYNC_OUTPUT " | grep "Number of files:" | grep -oP '[\d,]+' | head -1 | tr -d ',' )
FILES_TRANSFERRED = $( echo " $RSYNC_OUTPUT " | grep "Number of regular files transferred:" | grep -oP '[\d,]+' | head -1 | tr -d ',' )
FILES_CREATED = $( echo " $RSYNC_OUTPUT " | grep "Number of created files:" | grep -oP '[\d,]+' | head -1 | tr -d ',' )
TOTAL_SIZE = $( echo " $RSYNC_OUTPUT " | grep "Total file size:" | grep -oP '[0-9.,]+\s*[KMGT]?' | head -1)
TRANSFERRED_SIZE = $( echo " $RSYNC_OUTPUT " | grep "Total transferred file size:" | grep -oP '[0-9.,]+\s*[KMGT]?' | head -1)
TRANSFER_RATE = $( echo " $RSYNC_OUTPUT " | grep "sent.*bytes.*received" | grep -oP '[\d.,]+\s*[KMGT]?bytes/sec' | head -1)
FILES_TOTAL = ${ FILES_TOTAL :- 0 }
FILES_TRANSFERRED = ${ FILES_TRANSFERRED :- 0 }
FILES_CREATED = ${ FILES_CREATED :- 0 }
TOTAL_SIZE = ${ TOTAL_SIZE :- "unknown" }
TRANSFERRED_SIZE = ${ TRANSFERRED_SIZE :- "0" }
TRANSFER_RATE = ${ TRANSFER_RATE :- "unknown" }
# --- Parse per-folder activity -----------------------------------------------
declare -A FOLDER_COUNTS
declare -A FOLDER_ERRORS
while IFS = read -r LINE; do
if echo " $LINE " | grep -qE '^>f' ; then
FILEPATH = $( echo " $LINE " | sed 's/^[^ ]* //' )
TOP = $( echo " $FILEPATH " | cut -d'/' -f1)
if [ -n " $TOP " ] ; then
FOLDER_COUNTS[ " $TOP " ] = $(( ${ FOLDER_COUNTS [ " $TOP " ] :- 0 } + 1 ))
fi
fi
if echo " $LINE " | grep -qE "failed:|Permission denied|No such file|error:" ; then
ERR_FOLDER = $( echo " $LINE " | grep -oP '"[^"]*"' | head -1 | tr -d '"' | xargs basename 2>/dev/null || echo "unknown" )
FOLDER_ERRORS[ " $ERR_FOLDER " ] = $( echo " $LINE " | html_escape)
fi
done <<< " $RSYNC_OUTPUT "
# Build folder activity rows
FOLDER_ROWS = ""
FOLDERS_WITH_CHANGES = 0
FOLDERS_WITH_ERRORS = 0
for FOLDER in " ${ !FOLDER_COUNTS[@] } " ; do
COUNT = ${ FOLDER_COUNTS [ $FOLDER ] }
FOLDERS_WITH_CHANGES = $(( FOLDERS_WITH_CHANGES + 1 ))
FOLDER_ESC = $( echo " $FOLDER " | html_escape)
FOLDER_ROWS += "
<tr style = 'background:#f0fdf4;' >
<td style = 'padding:7px 12px;font-size:12px;font-family:monospace;color:#111827;border-bottom:1px solid #e5e7eb;' >& #128193; ${FOLDER_ESC}</td>
<td style = 'padding:7px 12px;font-size:12px;text-align:center;border-bottom:1px solid #e5e7eb;' >
<span style = 'background:#1a7f4b;color:white;padding:2px 10px;border-radius:12px;font-size:11px;font-weight:700;' >${ COUNT } file( s) updated</span>
</td>
</tr>"
done
for FOLDER in " ${ !FOLDER_ERRORS[@] } " ; do
FOLDERS_WITH_ERRORS = $(( FOLDERS_WITH_ERRORS + 1 ))
FOLDER_ESC = $( echo " $FOLDER " | html_escape)
ERR_ESC = ${ FOLDER_ERRORS [ $FOLDER ] }
FOLDER_ROWS += "
<tr style = 'background:#fff1f2;' >
<td style = 'padding:7px 12px;font-size:12px;font-family:monospace;color:#7f1d1d;border-bottom:1px solid #fecdd3;' >& #9888; ${FOLDER_ESC}</td>
<td style = 'padding:7px 12px;font-size:11px;color:#b91c1c;border-bottom:1px solid #fecdd3;' >${ ERR_ESC } </td>
</tr>"
done
if [ -z " $FOLDER_ROWS " ] ; then
FOLDER_ROWS = "
<tr>
<td colspan = '2' style = 'padding:12px;font-size:12px;color:#6b7280;text-align:center;border-bottom:1px solid #e5e7eb;' >No folder activity — all files are already up to date.</td>
</tr>"
fi
# --- Determine overall status ------------------------------------------------
if [ $RSYNC_EXIT -eq 0 ] && [ " $FOLDERS_WITH_ERRORS " -eq 0 ] ; then
OVERALL_STATUS = "SUCCESS"
BANNER_BG = "#1a7f4b"
BANNER_ICON = "✅"
BANNER_MSG = "Sync completed successfully — no errors detected."
STATUS_BADGE_BG = "#1a7f4b"
CARD_HEADER_BG = "#f0fdf4"
elif [ " $FOLDERS_WITH_ERRORS " -gt 0 ] ; then
OVERALL_STATUS = "WARNING"
BANNER_BG = "#b45309"
BANNER_ICON = "⚠️"
BANNER_MSG = "Sync completed with warnings — some folders had issues."
STATUS_BADGE_BG = "#b45309"
CARD_HEADER_BG = "#fffbeb"
else
OVERALL_STATUS = "FAILED"
BANNER_BG = "#b91c1c"
BANNER_ICON = "🚨"
BANNER_MSG = "Sync failed — check the log for details."
STATUS_BADGE_BG = "#b91c1c"
CARD_HEADER_BG = "#fff1f2"
fi
# Include permission correction notice in subject if it ran
PERM_NOTE = ""
if [ " $PERM_CORRECTED " = true ] ; then
PERM_NOTE = " [permissions auto-corrected]"
fi
SUBJECT = " [NAS Sync] NAS08 → NAS16 — ${ OVERALL_STATUS } ${ PERM_NOTE } — ${ NOW } "
# --- Log result --------------------------------------------------------------
if [ $RSYNC_EXIT -eq 0 ] ; then
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] ===== Sync Completed Successfully ===== " | tee -a " $LOG_FILE "
else
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] ERROR: rsync exited with code $RSYNC_EXIT . Check log: $LOG_FILE " | tee -a " $LOG_FILE "
fi
# --- Build HTML email --------------------------------------------------------
RAW_LOG_ESC = $( echo " $RSYNC_OUTPUT " | html_escape)
# Permission correction notice block (only shown if correction ran)
if [ " $PERM_CORRECTED " = true ] ; then
PERM_NOTICE_BLOCK = "
<div style = 'margin-bottom:20px;padding:12px 16px;background:#fffbeb;border:1px solid #fac775;border-radius:6px;' >
<div style = 'font-size:11px;font-weight:700;color:#633806;text-transform:uppercase;letter-spacing:.5px;margin-bottom:4px;' >& #9888; Destination Permissions Auto-Corrected</div>
<div style = 'font-size:12px;color:#633806;' >Destination ownership was not <code>www-data:ncshare</code>. chown, chmod, and setfacl were applied automatically before the sync ran.</div>
</div>"
else
PERM_NOTICE_BLOCK = ""
fi
HTML = $( cat <<HTMLEOF
<!DOCTYPE html>
<html lang = "en" >
<head><meta charset = "UTF-8" ><meta name = "viewport" content = "width=device-width,initial-scale=1" ></head>
<body style = "margin:0;padding:0;background:#f1f5f9;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;" >
<table width = "100%" cellspacing = "0" cellpadding = "0" style = "background:#f1f5f9;padding:30px 0;" >
<tr><td align = "center" >
<table width = "720" cellspacing = "0" cellpadding = "0" style = "max-width:720px;width:100%;" >
<!-- Header -->
<tr><td style = "background:#0f172a;border-radius:10px 10px 0 0;padding:28px 32px;" >
<table width = "100%" cellspacing = "0" cellpadding = "0" ><tr>
<td>
<div style = "font-size:11px;color:#94a3b8;letter-spacing:1px;text-transform:uppercase;" >NAS File Sync Monitor</div>
<div style = "font-size:26px;font-weight:800;color:white;margin-top:4px;" >& #128260; NAS08 → NAS16</div>
<div style = "font-size:13px;color:#94a3b8;margin-top:4px;" >${ NOW } & nbsp; | & nbsp; Schedule: every other day at 2:30 AM</div>
</td>
<td align = "right" >
<div style = " background: ${ BANNER_BG } ;color:white;padding:10px 20px;border-radius:8px;font-size:14px;font-weight:700; " >${ BANNER_ICON } ${ OVERALL_STATUS } </div>
</td>
</tr></table>
</td></tr>
<!-- Status Banner -->
<tr><td style = " background: ${ BANNER_BG } ;padding:12px 32px; " >
<span style = "color:white;font-size:14px;font-weight:600;" >${ BANNER_ICON } & nbsp; ${ BANNER_MSG } </span>
</td></tr>
<!-- Sync Result Card -->
<tr><td style = "padding:28px 24px 8px;" >
<div style = "background:white;border-radius:10px;box-shadow:0 1px 6px rgba(0,0,0,.10);margin-bottom:28px;overflow:hidden;border:1px solid #e5e7eb;" >
<div style = " background: ${ CARD_HEADER_BG } ;padding:16px 22px;border-bottom:1px solid #e5e7eb; " >
<table width = "100%" cellspacing = "0" cellpadding = "0" ><tr>
<td>
<span style = "font-size:18px;font-weight:700;color:#111827;" >& #128196; Sync Results</span>
<span style = "margin-left:8px;font-size:12px;color:#6b7280;" >NAS08 Public & rarr; NAS16 Public</span>
</td>
<td align = "right" >
<span style = " background: ${ STATUS_BADGE_BG } ;color:white;padding:5px 14px;border-radius:20px;font-size:12px;font-weight:700;letter-spacing:.5px; " >${ OVERALL_STATUS } </span>
</td>
</tr></table>
</div>
<div style = "padding:20px 22px;" >
${ PERM_NOTICE_BLOCK }
<!-- Stats tiles -->
<table width = "100%" cellspacing = "0" cellpadding = "0" >
<tr>
<td style = "width:18%;padding:8px 10px;background:#f9fafb;border-radius:6px;text-align:center;" >
<div style = "font-size:11px;color:#9ca3af;text-transform:uppercase;letter-spacing:.5px;" >Scanned</div>
<div style = "font-size:20px;font-weight:700;color:#111827;margin-top:2px;" >${ FILES_TOTAL } </div>
</td><td style = "width:2%;" ></td>
<td style = "width:18%;padding:8px 10px;background:#f9fafb;border-radius:6px;text-align:center;" >
<div style = "font-size:11px;color:#9ca3af;text-transform:uppercase;letter-spacing:.5px;" >Transferred</div>
<div style = "font-size:20px;font-weight:700;color:#111827;margin-top:2px;" >${ FILES_TRANSFERRED } </div>
</td><td style = "width:2%;" ></td>
<td style = "width:18%;padding:8px 10px;background:#f9fafb;border-radius:6px;text-align:center;" >
<div style = "font-size:11px;color:#9ca3af;text-transform:uppercase;letter-spacing:.5px;" >New Files</div>
<div style = "font-size:20px;font-weight:700;color:#111827;margin-top:2px;" >${ FILES_CREATED } </div>
</td><td style = "width:2%;" ></td>
<td style = "width:18%;padding:8px 10px;background:#f9fafb;border-radius:6px;text-align:center;" >
<div style = "font-size:11px;color:#9ca3af;text-transform:uppercase;letter-spacing:.5px;" >Data Moved</div>
<div style = "font-size:20px;font-weight:700;color:#111827;margin-top:2px;" >${ TRANSFERRED_SIZE } </div>
</td><td style = "width:2%;" ></td>
<td style = "width:18%;padding:8px 10px;background:#f9fafb;border-radius:6px;text-align:center;" >
<div style = "font-size:11px;color:#9ca3af;text-transform:uppercase;letter-spacing:.5px;" >Duration</div>
<div style = "font-size:20px;font-weight:700;color:#111827;margin-top:2px;" >${ ELAPSED_FMT } </div>
</td>
</tr>
</table>
<!-- Source / Dest details -->
<div style = "margin-top:16px;padding:12px 14px;background:#f8fafc;border-radius:6px;border:1px solid #e5e7eb;" >
<table width = "100%" cellspacing = "0" cellpadding = "0" >
<tr>
<td style = "font-size:12px;color:#6b7280;width:90px;padding-bottom:5px;" >Source</td>
<td style = "font-size:12px;font-family:monospace;color:#111827;padding-bottom:5px;" >NAS08 ( 192.168.150.35) & rarr; ${ SOURCE } </td>
</tr>
<tr>
<td style = "font-size:12px;color:#6b7280;padding-bottom:5px;" >Destination</td>
<td style = "font-size:12px;font-family:monospace;color:#111827;padding-bottom:5px;" >NAS16 ( 192.168.150.40) & rarr; ${ DEST } </td>
</tr>
<tr>
<td style = "font-size:12px;color:#6b7280;padding-bottom:5px;" >Transfer Rate</td>
<td style = "font-size:12px;font-family:monospace;color:#111827;padding-bottom:5px;" >${ TRANSFER_RATE } </td>
</tr>
<tr>
<td style = "font-size:12px;color:#6b7280;" >Total Source</td>
<td style = "font-size:12px;font-family:monospace;color:#111827;" >${ TOTAL_SIZE } </td>
</tr>
</table>
</div>
<!-- Folder Activity — collapsible -->
<details style = "margin-top:14px;" >
<summary style = "cursor:pointer;font-size:12px;color:#6b7280;font-weight:600;letter-spacing:.3px;" >& #9654; Folder Activity — ${FOLDERS_WITH_CHANGES} updated | ${FOLDERS_WITH_ERRORS} error(s)</summary>
<div style = "margin-top:8px;overflow-x:auto;border-radius:6px;border:1px solid #e5e7eb;" >
<table width = "100%" cellspacing = "0" cellpadding = "0" style = "border-collapse:collapse;" >
<thead>
<tr style = "background:#0f172a;" >
<th style = "padding:8px 12px;font-size:11px;color:#94a3b8;text-align:left;text-transform:uppercase;letter-spacing:.5px;" >Folder</th>
<th style = "padding:8px 12px;font-size:11px;color:#94a3b8;text-align:center;text-transform:uppercase;letter-spacing:.5px;width:200px;" >Status</th>
</tr>
</thead>
<tbody>${ FOLDER_ROWS } </tbody>
</table>
</div>
</details>
<!-- Full rsync log — collapsible -->
<details style = "margin-top:10px;" >
<summary style = "cursor:pointer;font-size:12px;color:#6b7280;font-weight:600;letter-spacing:.3px;" >& #9654; Full rsync Log — click to expand</summary>
<pre style = "margin-top:8px;background:#1e293b;color:#e2e8f0;padding:14px;border-radius:6px;font-size:11px;line-height:1.6;overflow-x:auto;white-space:pre-wrap;" >${ RAW_LOG_ESC } </pre>
</details>
</div>
</div>
</td></tr>
<!-- Summary -->
<tr><td style = "padding:0 24px 28px;" >
<div style = "background:white;border-radius:10px;border:1px solid #e5e7eb;padding:18px 22px;" >
<div style = "font-size:13px;font-weight:700;color:#374151;margin-bottom:12px;text-transform:uppercase;letter-spacing:.5px;" >& #128203; Report Summary</div>
<table width = "100%" cellspacing = "0" cellpadding = "0" >
<tr>
<td style = "font-size:13px;color:#6b7280;padding-bottom:6px;" >Host</td>
<td style = "font-size:13px;color:#111827;font-weight:600;padding-bottom:6px;" >NAS16 ( kingdezigns16) </td>
</tr>
<tr>
<td style = "font-size:13px;color:#6b7280;padding-bottom:6px;" >Run Time</td>
<td style = "font-size:13px;color:#111827;font-weight:600;padding-bottom:6px;" >${ NOW } </td>
</tr>
<tr>
<td style = "font-size:13px;color:#6b7280;padding-bottom:6px;" >rsync Exit Code</td>
<td style = "font-size:13px;font-weight:700;padding-bottom:6px;" >${ RSYNC_EXIT } </td>
</tr>
<tr>
<td style = "font-size:13px;color:#6b7280;padding-bottom:6px;" >Permissions Auto-Corrected</td>
<td style = "font-size:13px;font-weight:700;padding-bottom:6px;" >${ PERM_CORRECTED } </td>
</tr>
<tr>
<td style = "font-size:13px;color:#6b7280;padding-bottom:6px;" >Folders Updated</td>
<td style = "font-size:13px;color:#111827;font-weight:600;padding-bottom:6px;" >${ FOLDERS_WITH_CHANGES } </td>
</tr>
<tr>
<td style = "font-size:13px;color:#6b7280;padding-bottom:6px;" >Folder Errors</td>
<td style = "font-size:13px;font-weight:700;padding-bottom:6px;" >${ FOLDERS_WITH_ERRORS } </td>
</tr>
<tr>
<td style = "font-size:13px;color:#6b7280;" >Log File</td>
<td style = "font-size:13px;font-family:monospace;color:#111827;" >${ LOG_FILE } </td>
</tr>
</table>
</div>
</td></tr>
<!-- Footer -->
<tr><td style = "background:#0f172a;border-radius:0 0 10px 10px;padding:16px 32px;text-align:center;" >
<span style = "font-size:11px;color:#64748b;" >Automated NAS Sync Monitor & nbsp; | & nbsp; NAS16 & nbsp; | & nbsp; KingDezigns Infrastructure</span>
</td></tr>
</table>
</td></tr>
</table>
</body>
</html>
HTMLEOF
)
# --- Send email --------------------------------------------------------------
send_email( ) {
if command -v sendmail & >/dev/null; then
{ echo " To: ${ REPORT_TO } " ; echo " From: ${ REPORT_FROM } " ; echo " Subject: ${ SUBJECT } "
echo "MIME-Version: 1.0" ; echo "Content-Type: text/html; charset=UTF-8" ; echo ""
echo " $HTML " ; } | sendmail -t
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] Report sent via sendmail to ${ REPORT_TO } " | tee -a " $LOG_FILE "
elif command -v mailx & >/dev/null; then
echo " $HTML " | mailx -a "Content-Type: text/html" -s " $SUBJECT " " $REPORT_TO "
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] Report sent via mailx to ${ REPORT_TO } " | tee -a " $LOG_FILE "
elif command -v mail & >/dev/null; then
echo " $HTML " | mail -a "Content-Type: text/html; charset=UTF-8" -s " $SUBJECT " " $REPORT_TO "
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] Report sent via mail to ${ REPORT_TO } " | tee -a " $LOG_FILE "
else
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] ERROR: No mail transport found. " | tee -a " $LOG_FILE "
FALLBACK = " /tmp/nas_sync_report_ $( date +%Y%m%d_%H%M%S) .html "
echo " $HTML " > " $FALLBACK "
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] HTML report saved to: $FALLBACK " | tee -a " $LOG_FILE "
fi
}
send_email
# --- Cleanup -----------------------------------------------------------------
rm -f " $LOCK_FILE "
# Rotate logs older than 30 days
find " $LOG_DIR " -name "nas08_to_nas16_*.log" -mtime +30 -delete
echo " [ $( date '+%Y-%m-%d %H:%M:%S' ) ] Done — Status: ${ OVERALL_STATUS } | Transferred: ${ FILES_TRANSFERRED } file(s) | Duration: ${ ELAPSED_FMT } " | tee -a " $LOG_FILE "
exit $RSYNC_EXIT