🌐 InstaSites Notifications
📖 Overview
The InstaSites Notifications module handles notifications for the InstaSites website builder product, specifically monitoring build failures and completion status for customer websites.
Environment Flag: INSTASITES_ENABLED=true
Trigger Types:
- Change Stream on
InstaSitesBuildStatuscollection (build failures) - Change Stream on
InstaSitesViewedStatuscollection (site view tracking)
Notification Types: bell (FCM), browser push
Location: notifications/services/instasites/
🏗️ Architecture
System Flow
graph TB
subgraph "Triggers"
BUILD[Build Failure<br/>Status Insert]
VIEW[Site Viewed<br/>Status Insert]
end
subgraph "Processing"
DETECT1[Detect Build Failure]
DETECT2[Detect Site Viewed]
FETCH[Fetch User & Domain]
VERIFY[Verify Preferences]
CREATE[Create Notifications]
end
subgraph "Delivery"
BELL[Bell Notification]
BROWSER[Browser Push]
end
BUILD --> DETECT1
VIEW --> DETECT2
DETECT1 --> FETCH
DETECT2 --> FETCH
FETCH --> VERIFY
VERIFY --> CREATE
CREATE --> BELL
CREATE --> BROWSER
⚙️ Configuration
Environment Variables
# Module flag
INSTASITES_ENABLED=true # Enable InstaSites notifications
# External dependencies (inherited)
GENERAL_SOCKET=http://localhost:4000 # For bell notifications
FIREBASE_CREDENTIALS=... # For browser push
Change Stream Configuration
Build Failure Stream
const buildFailureStream = InstaSitesBuildStatus.watch(
[
{
$match: {
operationType: 'insert',
'fullDocument.status': 'failed',
},
},
],
{
fullDocument: 'updateLookup',
},
);
buildFailureStream.on('change', async data => {
await processFailedNotification(data);
});
Site Viewed Stream
const viewedStream = InstaSitesViewedStatus.watch(
[
{
$match: {
operationType: 'insert',
},
},
],
{
fullDocument: 'updateLookup',
},
);
viewedStream.on('change', async data => {
await processViewedNotification(data);
});
Stream Filters:
- Build failure: Only
insertoperations withstatus: 'failed' - Site viewed: All
insertoperations for view tracking
📧 Notification Templates
1. Build Failed
Trigger: InstaSite build process fails
Type: bell, browser push
Recipients: Site creator
Notification Content:
{
title: "Instasite build failed",
body: "A instasite build is failed.",
click_action: "https://app.dashclicks.com/instasites/my-instasites",
module: "instasites",
type: "build_fail",
subType: "bell" // or "browser"
}
2. Site Viewed (Optional)
Trigger: Customer views their InstaSite
Type: bell, browser push
Recipients: Site creator
Notification Content:
{
title: "InstaSite viewed",
body: "Your InstaSite has been viewed by a customer",
click_action: "https://app.dashclicks.com/instasites/my-instasites",
module: "instasites",
type: "site_viewed",
subType: "bell" // or "browser"
}
🔍 Processing Logic
Build Failure Notification
// From services/instasites/failed-notifications.js
async function processFailedNotification(data) {
try {
const buildStatus = data.fullDocument;
// 1. Fetch user who created the site
const user = await User.findById(buildStatus.created_by);
if (!user) {
logger.warn({
message: 'User not found for InstaSite build failure',
build_status_id: buildStatus._id,
created_by: buildStatus.created_by,
});
return;
}
// 2. Get active domain for the account
const domainName = await getActiveDomain({
accountId: buildStatus.account_id?.toString(),
proto: true,
});
const baseURL = `${domainName}/instasites/my-instasites`;
// 3. Send bell notification
await processFCMv2({
verification: {
module: 'instasites',
type: 'build_fail',
subType: 'bell',
},
content: {
title: 'Instasite build failed',
body: 'A instasite build is failed.',
click_action: baseURL,
},
recipient: {
accountID: buildStatus.account_id,
users: [user._id],
},
user_check: true, // Verify user preferences
});
// 4. Send browser push notification
await processFCMv2({
verification: {
module: 'instasites',
type: 'build_fail',
subType: 'browser',
},
content: {
title: 'Instasite build failed',
body: 'A instasite build is failed.',
click_action: baseURL,
},
recipient: {
accountID: buildStatus.account_id,
users: [user._id],
},
user_check: true,
});
logger.info({
message: 'InstaSite build failure notification sent',
build_status_id: buildStatus._id,
user_id: user._id,
});
} catch (err) {
logger.error({
initiator: 'notification/instasite/failed-notification',
message: 'Error in instasite failed notification',
error: err,
});
}
}
Site Viewed Notification
// From services/instasites/viewed-notifications.js
async function processViewedNotification(data) {
try {
const viewedStatus = data.fullDocument;
// 1. Fetch site details
const site = await InstaSite.findById(viewedStatus.site_id);
if (!site) {
logger.warn({
message: 'InstaSite not found for viewed notification',
site_id: viewedStatus.site_id,
});
return;
}
// 2. Fetch site creator
const user = await User.findById(site.created_by);
if (!user) {
logger.warn({
message: 'User not found for site viewed notification',
user_id: site.created_by,
});
return;
}
// 3. Get active domain
const domainName = await getActiveDomain({
accountId: site.account_id?.toString(),
proto: true,
});
const baseURL = `${domainName}/instasites/my-instasites`;
// 4. Send notifications
await processFCMv2({
verification: {
module: 'instasites',
type: 'site_viewed',
subType: 'bell',
},
content: {
title: 'InstaSite viewed',
body: 'Your InstaSite has been viewed by a customer',
click_action: baseURL,
},
recipient: {
accountID: site.account_id,
users: [user._id],
},
user_check: true,
});
await processFCMv2({
verification: {
module: 'instasites',
type: 'site_viewed',
subType: 'browser',
},
content: {
title: 'InstaSite viewed',
body: 'Your InstaSite has been viewed by a customer',
click_action: baseURL,
},
recipient: {
accountID: site.account_id,
users: [user._id],
},
user_check: true,
});
logger.info({
message: 'InstaSite viewed notification sent',
site_id: site._id,
user_id: user._id,
});
} catch (err) {
logger.error({
initiator: 'notification/instasite/viewed-notification',
message: 'Error in site viewed notification',
error: err,
});
}
}
🚨 Error Handling
Change Stream Management
// Graceful shutdown
process.on('SIGINT', async () => {
logger.log({
initiator: 'notifications/instasites',
message: 'Closing InstaSites streams',
});
await buildFailureStream.close();
await viewedStream.close();
process.exit(0);
});
// Stream error handling
buildFailureStream.on('error', error => {
logger.error({
initiator: 'notifications/instasites/failed-stream',
message: 'Change stream error',
error: error,
});
});
viewedStream.on('error', error => {
logger.error({
initiator: 'notifications/instasites/viewed-stream',
message: 'Change stream error',
error: error,
});
});
💡 Examples
Example 1: Build Failure
Trigger Event:
// InstaSitesBuildStatus insert
{
operationType: 'insert',
fullDocument: {
_id: ObjectId("507f1f77bcf86cd799439011"),
account_id: ObjectId("507f1f77bcf86cd799439012"),
site_id: ObjectId("507f1f77bcf86cd799439013"),
created_by: ObjectId("507f1f77bcf86cd799439014"),
status: "failed",
error_message: "Template compilation error: Missing required asset",
build_duration: 45,
created_at: new Date("2025-10-13T14:30:00Z")
}
}
Resulting Notifications:
- Bell - "Instasite build failed"
- Browser - Same content via web push
User Action: Click notification to navigate to InstaSites dashboard
Example 2: Site Viewed
Trigger Event:
// InstaSitesViewedStatus insert
{
operationType: 'insert',
fullDocument: {
_id: ObjectId("507f1f77bcf86cd799439011"),
site_id: ObjectId("507f1f77bcf86cd799439013"),
viewer_ip: "192.168.1.100",
viewer_location: "New York, USA",
viewed_at: new Date("2025-10-13T15:00:00Z")
}
}
Resulting Notifications:
- Bell - "InstaSite viewed"
- Browser - "Your InstaSite has been viewed by a customer"
📈 Metrics
Key Metrics:
- Build success rate: ~95%
- Build failures per day: ~15
- Average build time: 2 minutes
- Site views per day: ~500
Monitoring:
// Build failures today
db.getCollection('instasites.build_status').count({
status: 'failed',
created_at: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) },
});
// Successful builds today
db.getCollection('instasites.build_status').count({
status: 'completed',
created_at: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) },
});
// Site views today
db.getCollection('instasites.viewed_status').count({
viewed_at: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) },
});
// Notifications sent
db.getCollection('notifications.queue').count({
origin: 'instasites',
created_at: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) },
});
🔧 Troubleshooting
Issue: Build failure notifications not received
Symptoms: Site build fails but no notification sent
Diagnosis:
// 1. Check build status record
db.getCollection('instasites.build_status').findOne({
site_id: ObjectId('site_id'),
status: 'failed',
});
// 2. Verify user exists
db.getCollection('users').findOne({
_id: ObjectId('user_id'),
});
// 3. Check change stream is running
// Look for log: "InstaSites failed stream started"
Solutions:
- Ensure
INSTASITES_ENABLED=true - Verify MongoDB replica set configuration
- Check user notification preferences for
instasitesmodule - Verify Firebase credentials
Issue: Duplicate notifications
Symptoms: Multiple notifications for same build failure
Cause: Change stream may replay events if not properly acknowledged
Solutions:
- Implement deduplication in processing logic
- Check for existing notification before creating new one
- Use resume tokens properly in change stream
Issue: Generic error message
Symptoms: Notification says "build failed" but no details
Enhancement: Consider including error details in notification body:
// Enhanced notification
{
title: 'Instasite build failed',
body: `Build failed: ${buildStatus.error_message}`,
click_action: baseURL
}
🔗 Integration Points
InstaSites Product Integration
- Build Pipeline - Monitors build process status
- Template Engine - Tracks template compilation errors
- Asset Management - Detects missing asset errors
- Domain Configuration - Links to customer domain setup
Queue Manager Integration
InstaSites builds are typically processed by Queue Manager:
- Build jobs queued for processing
- Status updates written to
InstaSitesBuildStatus - Failures trigger notification change stream
Module Type: Change Stream
Environment Flag: INSTASITES_ENABLED
Dependencies: MongoDB (replica set), Firebase (FCM)
Notification Channels: Bell, Browser Push (no email)
Primary Use Case: Website builder status monitoring
Status: Active - critical for product health monitoring