Skip to main content

Template Service

The template service exposes read and write operations for the InstaSites template catalog. It lives at internal/api/v1/instasites/services/templates.service.js and is consumed by /instasites/templates routes.

๐Ÿ“– Overviewโ€‹

The service provides two entry points:

  • getTemplates โ€” paginated listing with rich metadata, pricing hydration, and account-level scoping.
  • createTemplate โ€” validates Duda builder IDs, captures preview assets, and persists a new template record.

Downstream controllers attach validation (see templates.validation.js) before invoking these methods.

๐Ÿ—„๏ธ Collections Usedโ€‹

๐Ÿ“š Full schema details: Database Collections Documentation

instasites.templatesโ€‹

  • Operations: Read/Write
  • Model: shared/models/instasite-template.js
  • Usage: Stores template metadata, preview URLs, thumbnails, global/account scope, and activation state.

instasites.templates.categoriesโ€‹

  • Operations: Read
  • Model: shared/models/instasite-template-category.js
  • Usage: Verifies category and main-category identifiers before template creation.

_store.productsโ€‹

  • Operations: Read
  • Model: shared/models/store-product.js
  • Usage: Hydrates Stripe product and price IDs so the UI can surface e-commerce vs informational pricing.

๐Ÿ”Œ External Dependenciesโ€‹

  • Duda Internal API (DUDA_INTERNAL_ENDPOINT) โ€” Fetches template metadata by builder_id.
  • Duda Builder Token (DUDA_TOKEN) โ€” Injected into outbound headers for Duda lookups.
  • Thumbnail Generator (generateThumbnailSet) โ€” Captures screenshots, uploads to Wasabi, and returns per-device URLs.
  • Axios โ€” Issues Duda HTTP requests.
  • Shared Utility (generatePagination) โ€” Produces consistent pagination metadata.

๐Ÿง  Function Referenceโ€‹

getTemplates({ account_id, scopes, page, limit, filters, category_id, main_category_ids, query })โ€‹

Purpose: Returns a scoped, paginated list of templates enriched with pricing metadata.

Inputs:

  • account_id โ€” Mongo ObjectId string that scopes non-system users to their own templates.
  • scopes โ€” Array of JWT scopes (e.g., system, system.instasites); system scopes unlock inactive and global templates.
  • page, limit โ€” Pagination controls (default 1/25) parsed into integers.
  • filters โ€” Reserved for future expansion (currently unused but passed through validation).
  • category_id โ€” Optional category filter; restricts results to a single sub-category.
  • main_category_ids โ€” Optional list or CSV string of main categories.
  • query โ€” Case-insensitive title search.

Business Logic Flow:

  1. Normalize pagination parameters and build a base $or query to include global + account-specific templates.
  2. If the caller lacks system scope, force active: true and maintain account-level isolation.
  3. Apply search filters:
    • Push regex match when query is present.
    • Convert category filters into $and conditions, handling both single and multiple IDs.
  4. Execute two Mongo operations in parallel:
    • countDocuments for pagination totals.
    • Sorted find query (alphabetical by title) with limit/skip.
  5. Fetch the Websites product from _store.products and split Stripe prices by metadata (informational vs e-com).
  6. Map each template to include:
    • pricing.price_ids โ€” Either e-commerce or informational price ID list.
    • pricing.product_id โ€” Stripe product identifier for checkout bundling.
  7. Return { data, pagination }, where data is JSON-serialized templates.

Key Rules:

  • E-commerce pricing is determined by comparing category_id or main_category_id to the hard-coded ECOMCAT ObjectId.
  • main_category_ids accepts either an array or a comma-separated string.
  • Non-system callers never see inactive templates.

Error Modes:

CodeScenarioResponse
400Invalid page/limit (non-numeric) โ€” handled upstream by validationJoi validation failure
500Duda/Stripe metadata missingCaught by controller; returns generic error

createTemplate({ account_id, authToken, scopes, title, builder_id, category_id, main_category_id, active })โ€‹

Purpose: Seeds a new template from an existing Duda builder site and stores preview assets for catalog use.

Inputs:

  • account_id โ€” Account creating the template; only stored when scope is not system-level.
  • authToken โ€” Bearer token forwarded from middleware; used alongside DUDA_TOKEN for Duda requests.
  • scopes โ€” Determines whether the template is global or account-specific.
  • title โ€” Display name for the template card.
  • builder_id โ€” Duda template/site identifier.
  • category_id, main_category_id โ€” Taxonomy nodes required for filtering.
  • active โ€” Optional boolean (default false for global templates, coerced to true for account-scoped ones).

Business Logic Flow:

  1. Load both referenced categories; throw notFound if either is missing.
  2. Guard against duplicates by checking existing templates with the same builder_id.
  3. Build request headers with account ID, bearer token, and Duda token.
  4. Fetch template metadata from DUDA_INTERNAL_ENDPOINT /sites/templates/:builder_id:
    • Reject with badRequest when Duda returns no data (invalid ID).
  5. Generate per-device preview URLs:
    • Use generateThumbnailSet for screenshot storage.
    • Construct all, desktop, tablet, and mobile preview links via string replacements.
  6. Assemble template payload:
    • Always global: true initially.
    • For non-system scopes, assign account_id and force active: true to keep it visible immediately.
  7. Save with new InstasiteTemplate(options).save() and return toJSON() payload.

Key Rules:

  • Only users with system or system.instasites scope can create dormant (active: false) global templates.
  • Preview URLs always use HTTPS and swap /preview/ for /site/ to match the dashboard viewer.
  • The helper trusts environment variables for Wasabi credentials; failures bubble up to the controller.

Error Modes:

CodeScenarioThrows
404Category or main category missingnotFound('Category not found') / notFound('Main category not found')
400Duplicate template (same builder_id)badRequest('Template already exists')
400Invalid Duda builder IDbadRequest('Invalid builder id provided')

โš™๏ธ Configurationโ€‹

VariablePurposeNotes
DUDA_INTERNAL_ENDPOINTBase URL for internal Duda proxy callsRequired
DUDA_TOKENApp-level builder token forwarded in every Duda requestRequired
WASABI_* / WASABI_PUBLIC_IMAGE_DOWNLOADUsed implicitly by generateThumbnailSet for preview storageRequired for thumbnail capture

๐Ÿงช Testing Touchpointsโ€‹

  • internal/api/v1/instasites/tests/templates.test.js asserts controller + service flows, including pricing hydration.
  • Tests rely on fixtures that mock Duda responses and thumbnail uploads.
๐Ÿ’ฌ

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