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 bybuilder_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:
- Normalize pagination parameters and build a base
$orquery to include global + account-specific templates. - If the caller lacks
systemscope, forceactive: trueand maintain account-level isolation. - Apply search filters:
- Push regex match when
queryis present. - Convert category filters into
$andconditions, handling both single and multiple IDs.
- Push regex match when
- Execute two Mongo operations in parallel:
countDocumentsfor pagination totals.- Sorted
findquery (alphabetical by title) with limit/skip.
- Fetch the
Websitesproduct from_store.productsand split Stripe prices by metadata (informationalvse-com). - 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.
- Return
{ data, pagination }, wheredatais JSON-serialized templates.
Key Rules:
- E-commerce pricing is determined by comparing
category_idormain_category_idto the hard-codedECOMCATObjectId. main_category_idsaccepts either an array or a comma-separated string.- Non-system callers never see inactive templates.
Error Modes:
| Code | Scenario | Response |
|---|---|---|
| 400 | Invalid page/limit (non-numeric) โ handled upstream by validation | Joi validation failure |
| 500 | Duda/Stripe metadata missing | Caught 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 alongsideDUDA_TOKENfor 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 (defaultfalsefor global templates, coerced totruefor account-scoped ones).
Business Logic Flow:
- Load both referenced categories; throw
notFoundif either is missing. - Guard against duplicates by checking existing templates with the same
builder_id. - Build request headers with account ID, bearer token, and Duda token.
- Fetch template metadata from
DUDA_INTERNAL_ENDPOINT /sites/templates/:builder_id:- Reject with
badRequestwhen Duda returns no data (invalid ID).
- Reject with
- Generate per-device preview URLs:
- Use
generateThumbnailSetfor screenshot storage. - Construct
all,desktop,tablet, andmobilepreview links via string replacements.
- Use
- Assemble template payload:
- Always
global: trueinitially. - For non-system scopes, assign
account_idand forceactive: trueto keep it visible immediately.
- Always
- Save with
new InstasiteTemplate(options).save()and returntoJSON()payload.
Key Rules:
- Only users with
systemorsystem.instasitesscope 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:
| Code | Scenario | Throws |
|---|---|---|
| 404 | Category or main category missing | notFound('Category not found') / notFound('Main category not found') |
| 400 | Duplicate template (same builder_id) | badRequest('Template already exists') |
| 400 | Invalid Duda builder ID | badRequest('Invalid builder id provided') |
โ๏ธ Configurationโ
| Variable | Purpose | Notes |
|---|---|---|
DUDA_INTERNAL_ENDPOINT | Base URL for internal Duda proxy calls | Required |
DUDA_TOKEN | App-level builder token forwarded in every Duda request | Required |
WASABI_* / WASABI_PUBLIC_IMAGE_DOWNLOAD | Used implicitly by generateThumbnailSet for preview storage | Required for thumbnail capture |
๐งช Testing Touchpointsโ
internal/api/v1/instasites/tests/templates.test.jsasserts controller + service flows, including pricing hydration.- Tests rely on fixtures that mock Duda responses and thumbnail uploads.