Skip to main content

Duda Content Service

๐Ÿ“– Overviewโ€‹

The Duda Content service manages site content operations including retrieving content library data, updating content, publishing content changes, and uploading resources like images and files to Duda sites.

Source File: external/Integrations/Duda/services/content.service.js
External API: Duda Content Library API
Primary Use: Site content management and resource uploads

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

configโ€‹

  • Operations: Read
  • Model: external/models/config.js
  • Usage Context: Fetch Duda API credentials for authentication

๐Ÿ”„ Data Flowโ€‹

sequenceDiagram
participant Client
participant ContentService
participant ConfigDB
participant DudaAPI

Client->>ContentService: Update content request
ContentService->>ConfigDB: Fetch API credentials
ConfigDB-->>ContentService: Return credentials

ContentService->>ContentService: Encode to Base64
ContentService->>ContentService: Validate request body

ContentService->>DudaAPI: POST /sites/:id/content
Note over ContentService,DudaAPI: Authorization: Basic {token}

DudaAPI-->>ContentService: Content updated
ContentService-->>Client: Return success

style ContentService fill:#e3f2fd
style DudaAPI fill:#fff4e6

๐Ÿ”ง Business Logic & Functionsโ€‹

getContent(dudaHeader, id)โ€‹

Purpose: Retrieve the complete content library data for a Duda site

Source: services/content.service.js

External API Endpoint: GET https://api.duda.co/api/sites/multiscreen/:id/content

Parameters:

  • dudaHeader (ObjectId) - Config ID containing Duda API credentials
  • id (String) - Duda site ID (site_name)

Returns: Promise<Object>

{
location_data: {
phones: Array,
emails: Array,
label: String,
social_accounts: Object,
address: Object,
geo: Object,
logo_url: String,
business_hours: Object
},
site_texts: Array,
site_images: Array,
business_data: Object,
additional_locations: Array
}

Business Logic Flow:

  1. Fetch API Credentials

    let query = await Config.findById(dudaHeader);
    if (!query) {
    throw notFound('Api token not found');
    }
  2. Generate Authentication

    const access_token = utils.base64(query);
  3. Call Duda API

    var options = {
    method: 'GET',
    url: `${DUDA_SITES_DOMAIN}/${id}/content`,
    headers: {
    'Content-Type': 'application/json',
    authorization: `Basic ${access_token}`,
    },
    };
    const response = await axios(options);

    return response.data;

API Request Example:

GET https://api.duda.co/api/sites/multiscreen/site_abc123/content
Authorization: Basic {base64_token}
Content-Type: application/json

API Response Example:

{
"location_data": {
"phones": [
{
"phoneNumber": "555-0100",
"label": "Main"
}
],
"emails": ["contact@example.com"],
"label": "Business Name",
"social_accounts": {
"facebook": "https://facebook.com/business",
"twitter": "https://twitter.com/business"
},
"address": {
"streetAddress": "123 Main St",
"city": "City",
"state": "State",
"zipCode": "12345"
}
},
"site_texts": [
{
"label": "Homepage Title",
"text": "Welcome to Our Website"
}
],
"site_images": [
{
"label": "Logo",
"url": "https://example.com/logo.png"
}
]
}

Error Handling:

  • 404 Not Found: API credentials not found or site doesn't exist
  • 401 Unauthorized: Invalid Duda credentials

Example Usage:

const content = await contentService.getContent(dudaConfigId, 'site_abc123');

console.log('Business Name:', content.location_data.label);
console.log('Phone:', content.location_data.phones[0].phoneNumber);

Key Business Rules:

  • Content Library: Returns structured content data that populates site widgets
  • Read-Only: Does not modify content, only retrieves
  • Complete Data: Returns all content library fields for the site

updateContent(dudaHeader, requestBody, id)โ€‹

Purpose: Update site content library with new data (business info, images, text)

Source: services/content.service.js

External API Endpoint: POST https://api.duda.co/api/sites/multiscreen/:id/content

Parameters:

  • dudaHeader (ObjectId) - Config ID
  • requestBody (Object) - Content updates
    • location_data (Object, optional) - Business location info
    • site_texts (Array, optional) - Text content updates
    • site_images (Array, optional) - Image updates
    • business_data (Object, optional) - Business data
  • id (String) - Duda site ID

Returns: Promise<Boolean> - Returns true on success

Business Logic Flow:

  1. Fetch Credentials

    let query = await Config.findById(dudaHeader);
    if (!query) {
    throw notFound('Api token not found');
    }
  2. Validate Request Body

    if (Object.keys(requestBody).length <= 0) {
    throw badRequest('Request body is required.');
    }
  3. Update Content

    const access_token = utils.base64(query);

    var options = {
    method: 'POST',
    url: `${DUDA_SITES_DOMAIN}/${id}/content`,
    headers: {
    'Content-Type': 'application/json',
    authorization: `Basic ${access_token}`,
    },
    data: requestBody,
    };
    await axios(options);

    return true;

API Request Example:

POST https://api.duda.co/api/sites/multiscreen/site_abc123/content
Authorization: Basic {base64_token}
Content-Type: application/json

{
"location_data": {
"phones": [
{
"phoneNumber": "555-0200",
"label": "Main"
}
],
"emails": ["newcontact@example.com"],
"label": "Updated Business Name"
},
"site_texts": [
{
"label": "Homepage Title",
"text": "New Homepage Title"
}
]
}

Content Update Structure:

// Location data updates
{
location_data: {
phones: [{ phoneNumber: String, label: String }],
emails: [String],
label: String, // Business name
social_accounts: {
facebook: String,
twitter: String,
instagram: String,
linkedin: String
},
address: {
streetAddress: String,
city: String,
state: String,
zipCode: String,
country: String
},
geo: {
longitude: Number,
latitude: Number
},
logo_url: String,
business_hours: Object
},
site_texts: [
{
label: String, // Unique identifier
text: String // Content to display
}
],
site_images: [
{
label: String, // Unique identifier
url: String // Image URL
}
]
}

Error Handling:

  • 400 Bad Request: Empty request body or invalid data format
  • 404 Not Found: API credentials or site not found
  • 422 Unprocessable Entity: Invalid content structure (from Duda)

Example Usage:

// Update business phone and email
await contentService.updateContent(
dudaConfigId,
{
location_data: {
phones: [{ phoneNumber: '555-0300', label: 'Main' }],
emails: ['support@newdomain.com'],
},
},
'site_abc123',
);

Key Business Rules:

  • Partial Updates: Only provided fields are updated, others remain unchanged
  • Label Matching: site_texts and site_images matched by label field
  • Not Auto-Published: Content changes are not automatically published to live site
  • Requires Publish: Must call publishContent() or publishSites() to make changes live

publishContent(dudaHeader, id)โ€‹

Purpose: Publish content library changes to make them live on the site

Source: services/content.service.js

External API Endpoint: POST https://api.duda.co/api/sites/multiscreen/:id/content/publish

Parameters:

  • dudaHeader (ObjectId) - Config ID
  • id (String) - Duda site ID

Returns: Promise<Boolean> - Returns true on success

Business Logic Flow:

  1. Fetch Credentials

    let query = await Config.findById(dudaHeader);
    if (!query) {
    throw notFound('Api token not found');
    }
  2. Publish Content

    const access_token = utils.base64(query);

    var options = {
    method: 'POST',
    url: `${DUDA_SITES_DOMAIN}/${id}/content/publish`,
    headers: {
    'Content-Type': 'application/json',
    authorization: `Basic ${access_token}`,
    },
    };
    await axios(options);

    return true;

API Request Example:

POST https://api.duda.co/api/sites/multiscreen/site_abc123/content/publish
Authorization: Basic {base64_token}
Content-Type: application/json

Error Handling:

  • 404 Not Found: API credentials or site not found
  • 400 Bad Request: Site may not have unpublished content changes

Example Usage:

// Update and publish content
await contentService.updateContent(dudaConfigId, contentUpdates, siteId);
await contentService.publishContent(dudaConfigId, siteId);
// Content is now live

Key Business Rules:

  • Content-Only Publish: Publishes only content library changes, not design changes
  • Faster than Full Publish: Quicker than full site publish (publishSites())
  • Live Immediately: Changes visible on live site immediately after publish
  • No Rollback: Cannot undo publish operation

uploadedResources(dudaHeader, requestBody, id)โ€‹

Purpose: Upload images, files, or other resources to a Duda site

Source: services/content.service.js

External API Endpoint: POST https://api.duda.co/api/sites/resources/:id/upload

Parameters:

  • dudaHeader (ObjectId) - Config ID
  • requestBody (Object) - Upload data
    • resource_type (String) - Type: 'IMAGE', 'FILE', etc.
    • src (String) - URL of resource to upload (Duda fetches from URL)
    • alt (String, optional) - Alt text for images
  • id (String) - Duda site ID

Returns: Promise<Object>

{
url: String, // Uploaded resource URL on Duda CDN
resource_id: String // Unique resource identifier
}

Business Logic Flow:

  1. Fetch Credentials

    let query = await Config.findById(dudaHeader);
    if (!query) {
    throw notFound('Api token not found');
    }
  2. Validate Request

    if (Object.keys(requestBody).length <= 0) {
    throw badRequest('Request body is required.');
    }
  3. Upload Resource

    const access_token = utils.base64(query);

    var options = {
    method: 'POST',
    url: `${DUDA_SITES_DOMAIN}/resources/${id}/upload`,
    headers: {
    'Content-Type': 'application/json',
    authorization: `Basic ${access_token}`,
    },
    data: requestBody,
    };
    const response = await axios(options);

    return response.data;

API Request Example:

POST https://api.duda.co/api/sites/resources/site_abc123/upload
Authorization: Basic {base64_token}
Content-Type: application/json

{
"resource_type": "IMAGE",
"src": "https://example.com/images/photo.jpg",
"alt": "Business Photo"
}

API Response Example:

{
"url": "https://irt-cdn.multiscreensite.com/abc123/photo.jpg",
"resource_id": "resource_xyz789"
}

Resource Types:

  • IMAGE: Image files (JPG, PNG, GIF, etc.)
  • FILE: Documents, PDFs, etc.
  • Other types supported by Duda

Error Handling:

  • 400 Bad Request: Empty body or invalid resource_type
  • 404 Not Found: Credentials or site not found
  • 422 Unprocessable Entity: Invalid src URL or unsupported file type

Example Usage:

// Upload logo image
const uploaded = await contentService.uploadedResources(
dudaConfigId,
{
resource_type: 'IMAGE',
src: 'https://mybucket.s3.amazonaws.com/logo.png',
alt: 'Company Logo',
},
'site_abc123',
);

console.log('Uploaded URL:', uploaded.url);

// Use uploaded URL in content update
await contentService.updateContent(
dudaConfigId,
{
location_data: {
logo_url: uploaded.url,
},
},
'site_abc123',
);

Key Business Rules:

  • URL-Based Upload: Duda fetches resource from provided URL (not direct file upload)
  • CDN Storage: Uploaded resources stored on Duda's CDN
  • Permanent Storage: Resources remain available after upload
  • Returns CDN URL: Use returned URL in content updates or design
  • Async Processing: Large files may take time to process

๐Ÿ”€ Integration Pointsโ€‹

Content Update Workflowโ€‹

// Complete content update and publish flow
async function updateAndPublishSiteContent(siteId, updates) {
// 1. Get current content
const currentContent = await contentService.getContent(dudaConfigId, siteId);

// 2. Merge with updates
const updatedContent = {
...currentContent,
location_data: {
...currentContent.location_data,
...updates.location_data,
},
};

// 3. Update content
await contentService.updateContent(dudaConfigId, updatedContent, siteId);

// 4. Publish changes
await contentService.publishContent(dudaConfigId, siteId);

return true;
}

Image Upload and Updateโ€‹

// Upload image and update site content
async function uploadAndSetLogo(siteId, logoUrl) {
// 1. Upload image to Duda CDN
const uploaded = await contentService.uploadedResources(
dudaConfigId,
{
resource_type: 'IMAGE',
src: logoUrl,
alt: 'Business Logo',
},
siteId,
);

// 2. Update content with new logo URL
await contentService.updateContent(
dudaConfigId,
{
location_data: {
logo_url: uploaded.url,
},
},
siteId,
);

// 3. Publish
await contentService.publishContent(dudaConfigId, siteId);

return uploaded.url;
}

๐Ÿงช Edge Cases & Special Handlingโ€‹

Empty Request Bodyโ€‹

Issue: Request body is empty
Handling: Throws 400 Bad Request

if (Object.keys(requestBody).length <= 0) {
throw badRequest('Request body is required.');
}

Missing Credentialsโ€‹

Issue: API credentials not found
Handling: Throws 404 Not Found

if (!query) {
throw notFound('Api token not found');
}

Content Not Publishedโ€‹

Issue: Content updated but not visible on live site
Handling: Must explicitly call publishContent() after updateContent()

Solution:

await contentService.updateContent(dudaConfigId, updates, siteId);
await contentService.publishContent(dudaConfigId, siteId); // Required!

Invalid Resource URLโ€‹

Issue: Upload fails because Duda can't fetch resource from URL
Handling: Duda API returns 422 error

Prevention: Ensure resource URL is publicly accessible before upload

Large File Uploadsโ€‹

Issue: Large files may timeout or take long to process
Handling: No explicit timeout handling in code

Recommendation: Implement retry logic for large uploads

โš ๏ธ Important Notesโ€‹

  1. Content vs Design: Content API manages content library, not page design/layout
  2. Publish Required: Content changes not live until published
  3. Partial Updates: updateContent() only modifies provided fields
  4. Label Matching: site_texts and site_images use label field as identifier
  5. URL-Based Upload: uploadedResources() requires publicly accessible URL
  6. CDN Storage: Uploaded resources hosted on Duda's CDN permanently
  7. No Direct File Upload: Cannot upload files directly, must provide URL
  8. Content Publish: publishContent() faster than full site publish
  9. No Rollback: Cannot undo content changes once published
  10. Business Hours: Duda has specific format for business_hours object
  11. Social Accounts: Supports Facebook, Twitter, Instagram, LinkedIn, etc.
  12. Multiple Phones/Emails: location_data supports arrays of contacts
๐Ÿ’ฌ

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:30 AM