Skip to main content

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, and instasite-notifications jobs 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 a type: instasite-csv queue entry.

processCSVQueue()​

Purpose: Drains outstanding CSV jobs, delegating to saveQueueData.

Flow:

  1. Pull all incomplete type: instasite-csv queue documents.
  2. Call saveQueueData(queue) for each, wrapping in Promise.allSettled so 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 InstasitesAdditionalInformation entries.
  • 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 generatePagination to 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_YET and priority_updated_at is more than 24 hours old.

Notification handling​

processNotificationQueue()​

Purpose: Attempts to send queued notifications (type: instasite-notifications).

Business Logic Flow:

  1. Fetch the next notification queue entry with fewer than 5 tries; increment its tries counter immediately.
  2. Craft a temporary JWT using APP_SECRET to authorize downstream service calls.
  3. Call sendNotifications(queueItem, tempToken) (utility) to send email/SMS via shared Mail/SMS helpers.
  4. 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.
  • sendNotifications updates InstasitesAdditionalInformation with 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:

  1. Fetch InstasitesAdditionalInformation and parent account domain.
  2. Verify or update the recipient contact email (ensuring uniqueness across parent/sub accounts).
  3. Generate a view link, attempt to shorten it via SHORT_URL_SERVICE; fall back to the long URL on failure.
  4. Assemble the mail payload (subject, content, attachments via generateFileBuffer) and send via Mail().send.
  5. Replace stored email event IDs with the new communication ID.

Error Handling:

  • Throws badRequest if 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 new smsEventIds for 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:

  1. Fetch the InstaSite; reject if already published.
  2. Start a Mongo session/transaction to update both instasites (status β†’ PUBLISHED, set published) and instasites.additional_information (status + purchased).
  3. Call Duda’s /sites/:builder_id/publish endpoint with the forwarded JWT + DUDA_TOKEN.
  4. If the template is an e-commerce category, set the Duda plan (/set-plan/8).
  5. 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:

  1. Validate tier is within the allowed set.
  2. Transactionally update the InstaSite tier and tier_updated_at.
  3. Call Duda /set-plan/:tier followed by /publish to 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:

  1. Verify the site is currently PUBLISHED.
  2. Transactionally set status β†’ UNPUBLISHED, set cancelled, clear published on both site and additional info.
  3. Call Duda /unpublish.
  4. 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_id or business_id owned 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, passing site_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.data is 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 InstasitesAdditionalInformation with emailSelected, smsSelected, and event ID arrays.

Side Effects:

  • Issues Wasabi downloads for attachments via generateFileBuffer.
  • Calls getActiveDomain to 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: processNotificationQueue increments the tries counter before confirming a job exists. Manually inserted queues should account for that behavior.
πŸ’¬

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:31 AM