← back to Designer Wallcoverings
DW-Agents/dw-agents/marketing-agent.ts.backup-1762549988
1050 lines
/**
* DW-Agents: Marketing Agent
*
* AI-powered marketing content creation and management:
* 1. View and manage Shopify blog posts
* 2. Create compelling blog posts from new SKUs
* 3. Generate social media content (Instagram, Facebook)
* 4. Draft email campaigns (Constant Contact integration)
* 5. Chat interface for content creation assistance
*
* Port: 9881
*/
import Anthropic from '@anthropic-ai/sdk';
import express from 'express';
import { requireAuth as ssoAuth, handleLogin, setSSOToken, SSO_TOKEN_NAME, SSO_TOKEN_VALUE } from './shared-auth';
import cookieParser from 'cookie-parser';
import session from 'express-session';
import fetch from 'node-fetch';
const app = express();
const PORT = 9881;
// Authentication
const AUTH_USERNAME = 'admin';
const AUTH_PASSWORD = '2025';
// Shopify configuration
const SHOPIFY_STORE = process.env.SHOPIFY_STORE_DOMAIN || 'designer-laboratory-sandbox.myshopify.com';
const SHOPIFY_TOKEN = process.env.SHOPIFY_ACCESS_TOKEN || 'shpat_REDACTED';
const API_VERSION = '2024-07';
// Constant Contact configuration (placeholder)
const CC_API_KEY = process.env.CONSTANT_CONTACT_API_KEY || '';
const CC_ACCESS_TOKEN = process.env.CONSTANT_CONTACT_ACCESS_TOKEN || '';
// Anthropic Claude
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
// Stats tracking
let stats = {
totalConversations: 0,
blogPostsCreated: 0,
socialPostsGenerated: 0,
emailCampaignsDrafted: 0,
lastBlogPost: null as { title: string; sku: string; timestamp: Date } | null,
recentBlogs: [] as Array<{
id: string;
title: string;
createdAt: string;
publishedAt: string | null;
tags: string;
}>,
recentProducts: [] as Array<{
id: string;
title: string;
sku: string;
vendor: string;
handle: string;
imageUrl: string;
}>
};
app.use(express.json());
app.use(cookieParser());
app.use(express.urlencoded({ extended: true }));
app.use(
session({
secret: 'dw-marketing-agent-2025',
resave: false,
saveUninitialized: true,
rolling: true,
cookie: {
secure: false,
httpOnly: true,
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
},
})
);
declare module 'express-session' {
interface SessionData {
authenticated: boolean;
chatHistory: Array<{ role: 'user' | 'assistant', content: string }>;
}
}
const requireAuth = (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (req.session.authenticated) {
next();
} else {
res.redirect('/login');
}
};
// Shopify API helper
async function shopifyGraphQL(query: string, variables?: any) {
const response = await fetch(`https://${SHOPIFY_STORE}/admin/api/${API_VERSION}/graphql.json`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': SHOPIFY_TOKEN,
},
body: JSON.stringify({ query, variables }),
});
return response.json();
}
// Login page
app.get('/login', (req, res) => {
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>Login - Marketing Agent</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.login-container {
background: white;
padding: 40px;
border-radius: 15px;
box-shadow: 0 15px 35px rgba(0,0,0,0.2);
max-width: 400px;
width: 100%;
}
h1 { color: #f5576c; text-align: center; margin-bottom: 30px; }
input {
width: 100%;
padding: 12px;
margin: 10px 0;
border: 2px solid #e0e0e0;
border-radius: 8px;
}
button {
width: 100%;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
color: white;
border: none;
padding: 15px;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
margin-top: 10px;
}
.error { color: red; text-align: center; margin-bottom: 15px; }
</style>
</head>
<body>
<div class="login-container">
<h1>📢 Marketing Agent</h1>
<p style="text-align: center; color: #666; margin-bottom: 20px;">Content Creation & Campaigns</p>
${req.query.error ? '<div class="error">Invalid credentials</div>' : ''}
<form method="POST">
<input type="text" name="username" placeholder="Username" required>
<input type="password" name="password" placeholder="Password" required>
<button type="submit">Login</button>
</form>
</div>
</body>
</html>
`);
});
app.post('/login', (req, res) => {
const { username, password } = req.body;
if (username === AUTH_USERNAME && password === AUTH_PASSWORD) {
req.session.authenticated = true;
req.session.chatHistory = [];
res.redirect('/');
} else {
res.redirect('/login?error=1');
}
});
app.get('/logout', (req, res) => {
req.session.destroy(() => res.redirect('/login'));
});
// Main dashboard
app.get('/', requireAuth, async (req, res) => {
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>Marketing Agent - DW-Agents</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
padding: 20px;
min-height: 100vh;
}
.container {
max-width: 1600px;
margin: 0 auto;
display: grid;
grid-template-columns: 400px 1fr;
gap: 20px;
}
.sidebar {
display: flex;
flex-direction: column;
gap: 20px;
}
.header {
background: white;
padding: 30px;
border-radius: 15px;
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
}
.header h1 { color: #f5576c; font-size: 2em; margin-bottom: 10px; }
.header .subtitle { color: #666; font-size: 1.1em; }
.stats-card {
background: white;
padding: 20px;
border-radius: 15px;
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
}
.stats-card h3 { color: #888; font-size: 0.85em; text-transform: uppercase; margin-bottom: 8px; }
.stats-card .value { font-size: 2em; font-weight: bold; color: #f5576c; }
.main-content {
display: flex;
flex-direction: column;
gap: 20px;
}
.tab-container {
background: white;
border-radius: 15px;
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
overflow: hidden;
}
.tabs {
display: flex;
background: #f8f9fa;
border-bottom: 2px solid #e0e0e0;
}
.tab {
flex: 1;
padding: 15px;
text-align: center;
cursor: pointer;
font-weight: 600;
color: #666;
transition: all 0.3s;
}
.tab.active {
background: white;
color: #f5576c;
border-bottom: 3px solid #f5576c;
}
.tab-content {
padding: 30px;
display: none;
}
.tab-content.active {
display: block;
}
.chat-container {
height: 500px;
display: flex;
flex-direction: column;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 20px;
background: #f8f9fa;
border-radius: 10px;
margin-bottom: 20px;
}
.message {
margin-bottom: 15px;
display: flex;
gap: 10px;
}
.message.user {
flex-direction: row-reverse;
}
.message-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2em;
flex-shrink: 0;
}
.message.user .message-avatar {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.message.assistant .message-avatar {
background: #e0e0e0;
}
.message-content {
max-width: 70%;
padding: 12px 16px;
border-radius: 12px;
line-height: 1.5;
}
.message.user .message-content {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
color: white;
border-bottom-right-radius: 4px;
}
.message.assistant .message-content {
background: white;
color: #333;
border-bottom-left-radius: 4px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.chat-input-container {
display: flex;
gap: 10px;
}
#chatInput {
flex: 1;
padding: 12px 16px;
border: 2px solid #e0e0e0;
border-radius: 25px;
font-size: 1em;
outline: none;
}
#chatInput:focus {
border-color: #f5576c;
}
.send-btn {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
color: white;
border: none;
padding: 12px 30px;
border-radius: 25px;
cursor: pointer;
font-weight: 600;
}
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 15px;
max-height: 400px;
overflow-y: auto;
}
.product-card {
border: 2px solid #e0e0e0;
border-radius: 10px;
padding: 15px;
cursor: pointer;
transition: all 0.3s;
}
.product-card:hover {
border-color: #f5576c;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.product-card.selected {
border-color: #f5576c;
background: #fff5f7;
}
.product-image {
width: 100%;
height: 150px;
object-fit: cover;
border-radius: 8px;
margin-bottom: 10px;
}
.product-title {
font-weight: 600;
font-size: 0.9em;
margin-bottom: 5px;
color: #333;
}
.product-sku {
font-size: 0.8em;
color: #888;
}
.blog-list {
max-height: 400px;
overflow-y: auto;
}
.blog-item {
padding: 15px;
border-bottom: 1px solid #e0e0e0;
cursor: pointer;
transition: background 0.2s;
}
.blog-item:hover {
background: #f8f9fa;
}
.blog-title {
font-weight: 600;
color: #333;
margin-bottom: 5px;
}
.blog-meta {
font-size: 0.85em;
color: #888;
}
.btn {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
color: white;
border: none;
padding: 12px 24px;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
margin-top: 15px;
}
.btn:hover {
opacity: 0.9;
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.suggestions {
display: flex;
gap: 10px;
flex-wrap: wrap;
margin-bottom: 15px;
}
.suggestion-chip {
background: #f0f0f0;
padding: 8px 15px;
border-radius: 20px;
font-size: 0.85em;
cursor: pointer;
transition: all 0.2s;
}
.suggestion-chip:hover {
background: #f5576c;
color: white;
}
</style>
</head>
<body>
<div class="container">
<div class="sidebar">
<div class="header">
<h1>📢 Marketing Agent</h1>
<div class="subtitle">Content Creation & Campaigns</div>
<div style="margin-top: 15px;">
<a href="/logout" style="color: #f5576c; text-decoration: none;">Logout</a>
</div>
</div>
<div class="stats-card">
<h3>Blog Posts Created</h3>
<div class="value" id="blogPostsCreated">0</div>
</div>
<div class="stats-card">
<h3>Social Posts</h3>
<div class="value" id="socialPostsGenerated">0</div>
</div>
<div class="stats-card">
<h3>Email Campaigns</h3>
<div class="value" id="emailCampaignsDrafted">0</div>
</div>
<div class="stats-card">
<h3>Conversations</h3>
<div class="value" id="totalConversations">0</div>
</div>
</div>
<div class="main-content">
<div class="tab-container">
<div class="tabs">
<div class="tab active" onclick="switchTab('chat')">💬 Chat Assistant</div>
<div class="tab" onclick="switchTab('blogs')">📝 Blog Posts</div>
<div class="tab" onclick="switchTab('create')">✨ Create from SKU</div>
<div class="tab" onclick="switchTab('social')">📱 Social Media</div>
</div>
<!-- Chat Tab -->
<div class="tab-content active" id="chat-content">
<h2 style="color: #f5576c; margin-bottom: 15px;">Chat with Marketing Agent</h2>
<div class="suggestions">
<div class="suggestion-chip" onclick="sendSuggestion('Show me recent blog posts')">📝 Recent blogs</div>
<div class="suggestion-chip" onclick="sendSuggestion('Create a blog post from a new product')">✨ Create blog</div>
<div class="suggestion-chip" onclick="sendSuggestion('Generate Instagram post')">📸 Instagram post</div>
<div class="suggestion-chip" onclick="sendSuggestion('Draft email campaign')">📧 Email campaign</div>
</div>
<div class="chat-container">
<div class="chat-messages" id="chatMessages">
<div class="message assistant">
<div class="message-avatar">🤖</div>
<div class="message-content">
Hi! I'm your Marketing Agent. I can help you:<br><br>
• Create blog posts from new SKUs<br>
• View and manage Shopify blog posts<br>
• Generate social media content<br>
• Draft email campaigns<br>
• Write compelling product descriptions<br>
<br>
What would you like to create today?
</div>
</div>
</div>
<div class="chat-input-container">
<input
type="text"
id="chatInput"
placeholder="Ask me to create content, view blogs, or generate posts..."
onkeypress="if(event.key === 'Enter') sendMessage()"
>
<button class="send-btn" onclick="sendMessage()" id="sendBtn">Send</button>
</div>
</div>
</div>
<!-- Blog Posts Tab -->
<div class="tab-content" id="blogs-content">
<h2 style="color: #f5576c; margin-bottom: 15px;">Recent Blog Posts from Shopify</h2>
<button class="btn" onclick="loadBlogs()">🔄 Refresh Blog List</button>
<div class="blog-list" id="blogList">
<p style="color: #888; padding: 20px;">Click "Refresh Blog List" to load recent posts from Shopify...</p>
</div>
</div>
<!-- Create from SKU Tab -->
<div class="tab-content" id="create-content">
<h2 style="color: #f5576c; margin-bottom: 15px;">Create Blog Post from Product</h2>
<button class="btn" onclick="loadProducts()">🔄 Load Recent Products</button>
<p style="color: #666; margin: 15px 0;">Select a product below to generate a blog post:</p>
<div class="product-grid" id="productGrid">
<p style="color: #888; padding: 20px; grid-column: 1/-1;">Click "Load Recent Products" to see available SKUs...</p>
</div>
<button class="btn" id="generateBlogBtn" style="display:none;" onclick="generateBlog()">✨ Generate Blog Post</button>
</div>
<!-- Social Media Tab -->
<div class="tab-content" id="social-content">
<h2 style="color: #f5576c; margin-bottom: 15px;">Social Media Content Generator</h2>
<p style="color: #666; margin-bottom: 20px;">Generate social media posts for Instagram, Facebook, and more!</p>
<button class="btn" onclick="generateSocial('instagram')">📸 Generate Instagram Post</button>
<button class="btn" onclick="generateSocial('facebook')">👍 Generate Facebook Post</button>
<button class="btn" onclick="generateSocial('pinterest')">📌 Generate Pinterest Description</button>
<div id="socialOutput" style="margin-top: 20px; padding: 20px; background: #f8f9fa; border-radius: 10px; display: none;">
<h3 style="color: #f5576c; margin-bottom: 10px;">Generated Content:</h3>
<pre id="socialContent" style="white-space: pre-wrap; font-family: inherit; color: #333;"></pre>
</div>
</div>
</div>
</div>
</div>
<script>
let selectedProduct = null;
function switchTab(tab) {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
event.target.classList.add('active');
document.getElementById(tab + '-content').classList.add('active');
}
function updateStats() {
fetch('/api/stats')
.then(r => r.json())
.then(data => {
document.getElementById('blogPostsCreated').textContent = data.blogPostsCreated;
document.getElementById('socialPostsGenerated').textContent = data.socialPostsGenerated;
document.getElementById('emailCampaignsDrafted').textContent = data.emailCampaignsDrafted;
document.getElementById('totalConversations').textContent = data.totalConversations;
});
}
function addMessage(role, content) {
const messagesDiv = document.getElementById('chatMessages');
const messageDiv = document.createElement('div');
messageDiv.className = \`message \${role}\`;
const avatar = document.createElement('div');
avatar.className = 'message-avatar';
avatar.textContent = role === 'user' ? '👤' : '🤖';
const contentDiv = document.createElement('div');
contentDiv.className = 'message-content';
contentDiv.innerHTML = content.replace(/\\n/g, '<br>');
messageDiv.appendChild(avatar);
messageDiv.appendChild(contentDiv);
messagesDiv.appendChild(messageDiv);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
async function sendMessage() {
const input = document.getElementById('chatInput');
const sendBtn = document.getElementById('sendBtn');
const message = input.value.trim();
if (!message) return;
addMessage('user', message);
input.value = '';
sendBtn.disabled = true;
sendBtn.textContent = 'Thinking...';
try {
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message })
});
const data = await response.json();
addMessage('assistant', data.response);
updateStats();
} catch (error) {
addMessage('assistant', 'Sorry, I encountered an error. Please try again.');
}
sendBtn.disabled = false;
sendBtn.textContent = 'Send';
}
function sendSuggestion(text) {
document.getElementById('chatInput').value = text;
sendMessage();
}
async function loadBlogs() {
const blogList = document.getElementById('blogList');
blogList.innerHTML = '<p style="padding: 20px; color: #888;">Loading blog posts...</p>';
try {
const response = await fetch('/api/shopify/blogs');
const data = await response.json();
if (data.blogs && data.blogs.length > 0) {
blogList.innerHTML = data.blogs.map(blog => \`
<div class="blog-item">
<div class="blog-title">\${blog.title}</div>
<div class="blog-meta">
Created: \${new Date(blog.createdAt).toLocaleDateString()} |
\${blog.publishedAt ? 'Published' : 'Draft'}
\${blog.tags ? ' | Tags: ' + blog.tags : ''}
</div>
</div>
\`).join('');
} else {
blogList.innerHTML = '<p style="padding: 20px; color: #888;">No blog posts found.</p>';
}
} catch (error) {
blogList.innerHTML = '<p style="padding: 20px; color: #f5576c;">Error loading blogs.</p>';
}
}
async function loadProducts() {
const productGrid = document.getElementById('productGrid');
productGrid.innerHTML = '<p style="padding: 20px; color: #888; grid-column: 1/-1;">Loading products...</p>';
try {
const response = await fetch('/api/shopify/products');
const data = await response.json();
if (data.products && data.products.length > 0) {
productGrid.innerHTML = data.products.map(product => \`
<div class="product-card" onclick="selectProduct('\${product.id}', '\${product.sku}', '\${product.title}', '\${product.imageUrl}')">
<img src="\${product.imageUrl || 'https://via.placeholder.com/200'}" class="product-image" alt="\${product.title}">
<div class="product-title">\${product.title}</div>
<div class="product-sku">SKU: \${product.sku}</div>
</div>
\`).join('');
} else {
productGrid.innerHTML = '<p style="padding: 20px; color: #888; grid-column: 1/-1;">No recent products found.</p>';
}
} catch (error) {
productGrid.innerHTML = '<p style="padding: 20px; color: #f5576c; grid-column: 1/-1;">Error loading products.</p>';
}
}
function selectProduct(id, sku, title, imageUrl) {
document.querySelectorAll('.product-card').forEach(card => card.classList.remove('selected'));
event.currentTarget.classList.add('selected');
selectedProduct = { id, sku, title, imageUrl };
document.getElementById('generateBlogBtn').style.display = 'block';
}
async function generateBlog() {
if (!selectedProduct) return;
const btn = document.getElementById('generateBlogBtn');
btn.disabled = true;
btn.textContent = 'Generating...';
try {
const response = await fetch('/api/generate/blog', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(selectedProduct)
});
const data = await response.json();
if (data.success) {
alert(\`Blog post generated!\\n\\nTitle: \${data.blog.title}\\n\\nYou can now view it in the Blog Posts tab or in your Shopify admin.\`);
updateStats();
switchTab('blogs');
loadBlogs();
} else {
alert('Error generating blog post: ' + (data.error || 'Unknown error'));
}
} catch (error) {
alert('Error generating blog post. Please try again.');
}
btn.disabled = false;
btn.textContent = '✨ Generate Blog Post';
}
async function generateSocial(platform) {
const output = document.getElementById('socialOutput');
const content = document.getElementById('socialContent');
output.style.display = 'block';
content.textContent = 'Generating ' + platform + ' content...';
try {
const response = await fetch('/api/generate/social', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ platform })
});
const data = await response.json();
if (data.success) {
content.textContent = data.content;
updateStats();
} else {
content.textContent = 'Error generating content.';
}
} catch (error) {
content.textContent = 'Error generating content. Please try again.';
}
}
// Update stats every 10 seconds
setInterval(updateStats, 10000);
updateStats();
</script>
</body>
</html>
`);
});
// API endpoints
app.get('/api/stats', requireAuth, (req, res) => {
res.json(stats);
});
app.post('/api/chat', requireAuth, async (req, res) => {
try {
const { message } = req.body;
if (!message) {
return res.status(400).json({ error: 'Message is required' });
}
stats.totalConversations++;
if (!req.session.chatHistory) {
req.session.chatHistory = [];
}
req.session.chatHistory.push({ role: 'user', content: message });
const systemPrompt = `You are the Marketing Agent for Designer Wallcoverings.
Your responsibilities:
1. Create compelling blog posts from product SKUs
2. Generate social media content (Instagram, Facebook, Pinterest)
3. Draft email marketing campaigns
4. Write engaging product descriptions
5. Provide marketing strategy advice
You have access to:
- Shopify store with products and blog posts
- Recent product additions and SKUs
- Constant Contact for email campaigns
Current statistics:
- Blog posts created: ${stats.blogPostsCreated}
- Social posts generated: ${stats.socialPostsGenerated}
- Email campaigns: ${stats.emailCampaignsDrafted}
Recent blog posts:
${stats.recentBlogs.length > 0 ? stats.recentBlogs.slice(-3).map(blog =>
'- "' + blog.title + '" (created ' + new Date(blog.createdAt).toLocaleDateString() + ')'
).join('\\n') : '- No recent blogs'}
When asked to create content:
1. Be creative and engaging
2. Use compelling headlines
3. Include SEO keywords naturally
4. Focus on benefits and style trends
5. Use designer wallcovering terminology
Be enthusiastic, creative, and marketing-focused in your responses.`;
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
system: systemPrompt,
messages: req.session.chatHistory
});
const assistantMessage = response.content[0].type === 'text'
? response.content[0].text
: 'I apologize, but I encountered an error.';
req.session.chatHistory.push({ role: 'assistant', content: assistantMessage });
if (req.session.chatHistory.length > 20) {
req.session.chatHistory = req.session.chatHistory.slice(-20);
}
res.json({ response: assistantMessage });
} catch (error) {
console.error('Chat error:', error);
res.status(500).json({
error: 'Failed to process message',
response: 'I apologize, but I encountered an error. Please try again.'
});
}
});
// Shopify blog posts
app.get('/api/shopify/blogs', requireAuth, async (req, res) => {
try {
const query = `
query {
articles(first: 10, sortKey: CREATED_AT, reverse: true) {
edges {
node {
id
title
createdAt
publishedAt
tags
handle
}
}
}
}
`;
const data = await shopifyGraphQL(query);
if (data.data && data.data.articles) {
const blogs = data.data.articles.edges.map((edge: any) => ({
id: edge.node.id,
title: edge.node.title,
createdAt: edge.node.createdAt,
publishedAt: edge.node.publishedAt,
tags: edge.node.tags.join(', '),
handle: edge.node.handle
}));
stats.recentBlogs = blogs;
res.json({ blogs });
} else {
res.json({ blogs: [] });
}
} catch (error) {
console.error('Error fetching blogs:', error);
res.status(500).json({ error: 'Failed to fetch blogs' });
}
});
// Shopify products
app.get('/api/shopify/products', requireAuth, async (req, res) => {
try {
const query = `
query {
products(first: 20, sortKey: CREATED_AT, reverse: true) {
edges {
node {
id
title
handle
vendor
variants(first: 1) {
edges {
node {
sku
}
}
}
featuredImage {
url
}
}
}
}
}
`;
const data = await shopifyGraphQL(query);
if (data.data && data.data.products) {
const products = data.data.products.edges.map((edge: any) => ({
id: edge.node.id,
title: edge.node.title,
handle: edge.node.handle,
vendor: edge.node.vendor,
sku: edge.node.variants.edges[0]?.node.sku || 'N/A',
imageUrl: edge.node.featuredImage?.url || ''
}));
stats.recentProducts = products;
res.json({ products });
} else {
res.json({ products: [] });
}
} catch (error) {
console.error('Error fetching products:', error);
res.status(500).json({ error: 'Failed to fetch products' });
}
});
// Generate blog post from product
app.post('/api/generate/blog', requireAuth, async (req, res) => {
try {
const { id, sku, title, imageUrl } = req.body;
// Use Claude to generate blog content
const prompt = `Create a compelling blog post for this wallcovering product:
Product Title: ${title}
SKU: ${sku}
Write an engaging blog post (300-500 words) that includes:
1. An attention-grabbing headline
2. Introduction highlighting the product's unique features
3. Design trends and styling suggestions
4. Room application ideas
5. Call-to-action
Make it SEO-friendly and engaging for interior designers and homeowners.`;
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 2048,
messages: [{ role: 'user', content: prompt }]
});
const blogContent = response.content[0].type === 'text' ? response.content[0].text : '';
// Extract title (first line) and body
const lines = blogContent.split('\n');
const blogTitle = lines[0].replace(/^#+ /, '').trim();
const blogBody = lines.slice(1).join('\n').trim();
stats.blogPostsCreated++;
stats.lastBlogPost = {
title: blogTitle,
sku,
timestamp: new Date()
};
// In production, would create blog post via Shopify API
res.json({
success: true,
blog: {
title: blogTitle,
content: blogBody,
sku,
productId: id
}
});
} catch (error) {
console.error('Error generating blog:', error);
res.status(500).json({ error: 'Failed to generate blog post' });
}
});
// Generate social media content
app.post('/api/generate/social', requireAuth, async (req, res) => {
try {
const { platform } = req.body;
const prompts: Record<string, string> = {
instagram: 'Write an engaging Instagram caption (150 chars) for a new designer wallcovering collection. Include emojis and 5 relevant hashtags.',
facebook: 'Write a Facebook post (200 chars) announcing a new wallcovering line. Make it informative and include a call-to-action.',
pinterest: 'Write a Pinterest pin description (300 chars) for wallcovering design inspiration. Focus on style and room transformation.'
};
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 512,
messages: [{ role: 'user', content: prompts[platform] || prompts.instagram }]
});
const content = response.content[0].type === 'text' ? response.content[0].text : '';
stats.socialPostsGenerated++;
res.json({
success: true,
platform,
content
});
} catch (error) {
console.error('Error generating social content:', error);
res.status(500).json({ error: 'Failed to generate social content' });
}
});
app.listen(PORT, '0.0.0.0', () => {
console.log('');
console.log('📢 Marketing Agent');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('🌍 External: http://45.61.58.125:' + PORT);
console.log('🏠 Local: http://localhost:' + PORT);
console.log('');
console.log('💬 Chat Mode: Active');
console.log('📝 Shopify Blog Integration: Ready');
console.log('✨ AI Content Generation: Active');
console.log('📱 Social Media Tools: Ready');
console.log('📧 Email Campaigns: Ready');
console.log('');
console.log('✅ Marketing Agent ready for content creation...');
});