Core InstaSites Service
The core InstaSites service module orchestrates queueing, Duda operations, and notification workflows.
π Overviewβ
This service contains the bulk of InstaSites business logic: contact visibility filters, queue preparation, Duda API calls, notification retries, and rich retrieval helpers consumed by controllers. It is exported from internal/api/v1/instasites/services/index.js and is invoked by every route in the module.
File Path: internal/api/v1/instasites/services/index.js
ποΈ Collections Usedβ
π Full schema details: Database Collections Documentation
instasitesβ
- Operations: Read/Write
- Model:
shared/models/instasite.js - Usage Context: Stores the canonical site record, status (
PENDING,PUBLISHED, etc.), cached Duda metadata, and lifecycle timestamps.
instasites.additional_informationβ
- Operations: Read/Write
- Model:
shared/models/instasites-additional-information.js - Usage Context: Tracks recipients, notification state, email/SMS event IDs, viewed flags, and purchased/cancelled timestamps.
queueβ
- Operations: Read/Write
- Model:
shared/models/instasite-queue.js - Usage Context: Persists
instasite,instasite-csv, andinstasite-notificationsjobs for queue-manager workers.
instasites.templatesβ
- Operations: Read
- Model:
shared/models/instasite-template.js - Usage Context: Joined to enrich listings with template metadata and to determine informational vs e-commerce pricing.
instasites.templates.categoriesβ
- Operations: Read
- Model:
shared/models/instasite-template-category.js - Usage Context: Loaded to decorate template/category names and to identify e-commerce templates (
ECOMCAT).
crm.contactsβ
- Operations: Read/Write
- Model:
shared/models/contact.js - Usage Context: Provides business and person contacts for site creation, validates visibility permissions, and is updated when resending notifications.
communicationsβ
- Operations: Read
- Model:
shared/models/communication.js - Usage Context: Queried to hydrate email/SMS delivery statuses inside
getAdditionalInfo.
agency-websitesβ
addCSVToQueue({ account_id, parent_account, uid, template_id, contact_csv, notifications })β
Purpose: Registers a CSV import job to be processed asynchronously.
Highlights:
- Validates template existence.
- Stores the source file details (
bucket,key) and notification payload under atype: instasite-csvqueue entry.
processCSVQueue()β
Purpose: Drains outstanding CSV jobs, delegating to saveQueueData.
Flow:
- Pull all incomplete
type: instasite-csvqueue documents. - Call
saveQueueData(queue)for each, wrapping inPromise.allSettledso one failure doesnβt block others.
Helper Summary β saveQueueData(queue)
- Parses the CSV via
cvs-to-json.js. - Creates or updates person and business contacts (
create-business.js). - Enqueues individual InstaSite jobs and creates
InstasitesAdditionalInformationentries. - Emits socket notifications for real-time dashboards.
- Updates the CSV queue entry with success and error counts.
getCSVData({ account_id, page, limit })β
Purpose: Returns paginated CSV import queue logs for the account.
Highlights:
- Mirrors queue entries with template lookups.
- Uses
generatePaginationto align with UI tables.
processUpdatePriority()β
Purpose: Nightly job to gradually reduce priority for stale, unpublished sites.
Rules:
- Decrements priority by 1 (down to a floor of 1) if the site has status
NOT_PUBLISHED_YETandpriority_updated_atis more than 24 hours old.
Notification handlingβ
processNotificationQueue()β
Purpose: Attempts to send queued notifications (type: instasite-notifications).
Business Logic Flow:
- Fetch the next notification queue entry with fewer than 5 tries; increment its
triescounter immediately. - Craft a temporary JWT using
APP_SECRETto authorize downstream service calls. - Call
sendNotifications(queueItem, tempToken)(utility) to send email/SMS via shared Mail/SMS helpers. - On success, delete the queue entry; on partial failure, persist per-channel status to the queue record.
Key Business Rules:
- Maximum of 5 attempts; after that the job is skipped.
sendNotificationsupdatesInstasitesAdditionalInformationwith communication metadata.
Side Effects:
- May delete or update queue records; updates additional info via the helper.
resendEmail({ parent_account, uid, account_id, authToken, instasite_id, recipient_id, email })β
Purpose: Allows agents to resend launch emails with an updated address.
Business Logic Flow:
- Fetch
InstasitesAdditionalInformationand parent account domain. - Verify or update the recipient contact email (ensuring uniqueness across parent/sub accounts).
- Generate a view link, attempt to shorten it via
SHORT_URL_SERVICE; fall back to the long URL on failure. - Assemble the mail payload (subject, content, attachments via
generateFileBuffer) and send viaMail().send. - Replace stored email event IDs with the new communication ID.
Error Handling:
- Throws
badRequestif contact uniqueness fails or mail send returns no_id.
Side Effects:
- Updates recipient contact email if changed.
- Mutates
InstasitesAdditionalInformation.emailEventIds.
resendSMS({ account_id, uid, parent_account, authToken, instasite_id, recipient_id, phone })β
Purpose: Mirrors resendEmail, delivering updated SMS notifications.
Highlights:
- Validates phone uniqueness, updates contact if needed.
- Sends via
SMS().send, storing newsmsEventIdsfor the recipient. - Returns early if the provider rejects all numbers (throws
badRequest).
Publishing & plan managementβ
publishSite({ account_id, id, authToken })β
Purpose: Publishes a site on Duda and marks it purchased.
Business Logic Flow:
- Fetch the InstaSite; reject if already published.
- Start a Mongo session/transaction to update both
instasites(status βPUBLISHED, setpublished) andinstasites.additional_information(status +purchased). - Call Dudaβs
/sites/:builder_id/publishendpoint with the forwarded JWT +DUDA_TOKEN. - If the template is an e-commerce category, set the Duda plan (
/set-plan/8). - Commit the transaction and return the updated document.
Error Handling:
- Rolls back transactions on Mongo errors; Duda failures propagate.
setSitePlan({ account_id, id, tier, authToken })β
Purpose: Upgrades or downgrades the Duda plan tier (7β10) and republishes the site.
Business Logic Flow:
- Validate
tieris within the allowed set. - Transactionally update the InstaSite
tierandtier_updated_at. - Call Duda
/set-plan/:tierfollowed by/publishto apply the change.
unpublishSite({ account_id, authToken, id })β
Purpose: Unpublishes a site, marking it cancelled and triggering downstream notifications via the controller.
Business Logic Flow:
- Verify the site is currently
PUBLISHED. - Transactionally set status β
UNPUBLISHED, setcancelled, clearpublishedon both site and additional info. - Call Duda
/unpublish. - Controller handles FCM and email notifications after the service resolves.
Analytics, forms, and SSOβ
getAnalytics({ account_id, authToken, scopes, accBusinessId, admin, id, query, isAdminUser })β
Purpose: Fetches Duda analytics for a site or agency website while enforcing account scoping.
Highlights:
- Accepts query params (
from,to,dimension, etc.) validated upstream. - Supports admin requests accessing either
account_idorbusiness_idowned sites.
ssoToSite({ account_id, authToken, accBusinessId, id, query })β
Purpose: Issues an SSO URL for the Duda builder/editor.
Highlights:
- Calls Duda
/accounts/:builder_account/sso, passingsite_name. - Returns Dudaβs payload directly to the controller.
getSiteForms({ account_id, authToken, accBusinessId, id, query, isAdminUser })β
Purpose: Retrieves form submissions hosted on the Duda site.
Highlights:
- Uses the same scoping guard as analytics.
- Returns raw Duda form data (with a fallback when
forms.datais nested).
Helper Functionsβ
sendNotifications(queueItem, authToken) (from utils/sendNotifications.js)β
Purpose: Internal utility invoked by processNotificationQueue to deliver email/SMS blasts.
Key Behaviors:
- Loads recipients, shortens URLs per recipient, and sends via shared Mail/SMS classes.
- Records event IDs so resends can hydrate the latest communication history.
- Updates
InstasitesAdditionalInformationwithemailSelected,smsSelected, and event ID arrays.
Side Effects:
- Issues Wasabi downloads for attachments via
generateFileBuffer. - Calls
getActiveDomainto construct recipient-facing view links.
generateFileBuffer(attachment)β
Purpose: Fetches a Wasabi object into an in-memory buffer for Mail attachments. Relies on axios and the public download endpoint.
π Integration Pointsβ
Internal dependenciesβ
../../utilities:generatePagination,getActiveDomain,catch-errors, and Redis-backed utilities.../../utilities/mail&../../utilities/sms: send templated communications and return event metadata.../../utilities/logger: structured logging for queue and Duda failures.
External servicesβ
- Duda Internal API β Publish, unpublish, analytics, forms, plan changes.
- URL shortener (
SHORT_URL_SERVICE) β Optional, used for shareable preview links. - Wasabi β Stores attachments and template screenshots.
π§ͺ Edge Cases & Special Handlingβ
Contact visibility enforcementβ
Condition: Non-owner accounts with preferences.contacts.visibility !== 'entire'.
Handling: Aggregations join contacts with conditional $lookup pipelines that only return contacts owned or followed by the requester.
Notification retriesβ
Condition: type: instasite-notifications job fails Mail or SMS delivery.
Handling: The job remains in queue with status flags; repeated failures beyond 5 attempts stop retries.
Duda refresh guardβ
Condition: refresh_at still in the future.
Handling: getSiteById returns cached data without hitting Duda to reduce API load.
β οΈ Important Notesβ
- π¨ Transaction scope: Publishing, plan changes, and unpublishing wrap updates in Mongo transactions; ensure sessions are passed through if calling these methods manually.
- π‘ Short-link failures: The code gracefully logs and falls back to the long preview URL, so the link is still valid even when the shortener fails.
- π Notification queue pre-increment:
processNotificationQueueincrements thetriescounter before confirming a job exists. Manually inserted queues should account for that behavior.