Building Real-Time Maintenance Task Notifications for Queen of San Diego: A Multi-System Integration
The Queen of San Diego maintenance operations were suffering from a visibility problem. When Travis or other crew members added tasks to the maintenance tool, there was no clear signal to the operations team that new work had been logged. Sergio needed to know about critical maintenance issues immediately, while less urgent tasks could be batched. This required building a distributed notification system that spanned Google Apps Script, AWS Lambda, S3, CloudFront, and Gmail.
The Problem Statement
The maintenance.queenofsandiego.com tool (a Google Apps Script web app deployed to a CloudFront distribution fronting an S3 bucket) had become a write-only system. Tasks were logged but not surfaced to decision-makers. We needed:
- Real-time notification when critical tasks are added
- Digest-style notifications for routine maintenance
- Separation between staging and production notification channels
- Integration with team calendars for better context
- Email delivery from an appropriate domain/alias
Architecture Overview
The solution required three primary components working in concert:
- Frontend: Modified staging HTML at
/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/maintenance/staging-index.html - Backend Processing: New GAS files
MaintenancePersistence.gsandMaintenanceCalendar.gs, plus modifications toBookingAutomation.gs - Async Notifications: Lambda function for email delivery and digest batching
This three-tier approach allows the web interface to remain responsive while offloading notification logic to asynchronous processing.
Frontend Changes: Task Submission Plumbing
The staging HTML was modified to capture task metadata beyond just the task description. Key changes included:
- Adding a
criticalityfield to task submission (Critical, High, Medium, Low) - Capturing submitter information and timestamp
- Posting to a new GAS handler endpoint:
/log_maintenance - Adding visual feedback when tasks are submitted
The staging environment posts to jadasailing@gmail.com for testing, while production will use the operations team alias once finalized.
Backend: Google Apps Script Integration
Three GAS files handle the notification pipeline:
MaintenancePersistence.gs (New)
This file serves as the data model and persistence layer. It handles:
- Writing task events to a Google Sheet (acting as our event log)
- Storing task metadata (criticality, submitter, timestamp)
- Maintaining idempotency keys to prevent duplicate processing
function persistMaintenanceTask(taskData) {
const sheet = SpreadsheetApp.getActive()
.getSheetByName('Maintenance Log');
const timestamp = new Date();
const row = [
timestamp,
taskData.description,
taskData.criticality,
taskData.submitter,
taskData.location,
'PENDING_NOTIFICATION'
];
sheet.appendRow(row);
return {success: true, taskId: generateIdempotencyKey()};
}
MaintenanceCalendar.gs (New)
This file integrates with Google Calendar to provide operational context:
- Queries the JADA Internal calendar for scheduled charters and operations
- Determines if a task is urgent based on upcoming events
- Adds maintenance events to the "Jada Maintenance" calendar (created under jadasailing@gmail.com)
The calendar integration was critical because a "High" priority task becomes "Critical" if the boat is scheduled to charter tomorrow. This data-driven approach ensures notification urgency matches operational reality.
BookingAutomation.gs (Modified)
Extended the existing doPost handler to route maintenance requests:
if (e.parameter.action === 'log_maintenance') {
const result = handleMaintenanceLog(e);
// Triggers async notification via Lambda
notifyMaintenanceAsync(result);
return ContentService.createTextOutput(JSON.stringify(result))
.setMimeType(ContentService.MimeType.JSON);
}
This handler validates the request, persists the task, and triggers an async notification without blocking the user's request.
Notification Strategy: Real-Time vs. Digest
Industry research (backed by incident response teams at companies like Incident.io and PagerDuty) shows:
- Critical tasks: Immediate notification via email + SMS if available
- High priority: Immediate email, no SMS
- Medium/Low priority: Batched into daily digest (8 AM delivery)
This prevents notification fatigue while ensuring time-sensitive issues surface immediately. The criticality assessment uses a combination of user input and calendar context.
Infrastructure: S3, CloudFront, and Deployment
The staging environment is deployed to a specific S3 path under the maintenance distribution:
- S3 Bucket: The actual bucket hosting maintenance.queenofsandiego.com
- Staging Path:
/staging/prefix within the bucket - CloudFront Distribution: Existing distribution with origin pointing to the S3 bucket
- Invalidation Pattern:
/staging/*invalidated after each deploy
The staging/production separation is achieved through URL path prefixing rather than separate distributions, reducing complexity and cost.
Deployment command sequence:
aws s3 cp staging-index.html s3://[bucket-name]/staging/index.html
aws cloudfront create-invalidation --distribution-id [DIST-ID] --paths "/staging/*"
clasp push # Deploys GAS changes
Lambda Notification Service
An AWS Lambda function (deployed in the same region as other JADA infrastructure) handles async email delivery:
- Trigger: GAS invokes via Apps Script URL Fetch API
- Runtime: Node.js 18 (matching existing tips-box Lambda config)
- IAM Role: Reuses existing
jada-execution-rolewith SES permissions added - Environment Variables: Sender email alias, digest collection interval
The Lambda batches Medium/Low priority tasks into a DynamoDB table, then triggers a scheduled CloudWatch Events rule that fires at 8 AM to send the daily digest.
Email Configuration
Staging emails are sent to jadasailing@gmail.com for testing. For production, we'll use an operations team alias (TBD). SES is verified and whitelisted for the appropriate domain.
Email templates use inline CSS and are responsive, including task details, calendar context, and direct links back to the