← back to Dear Bubbe Nextjs

app/api/shopify/blogs/route.ts

249 lines

import { NextRequest, NextResponse } from 'next/server'
import fs from 'fs'
import path from 'path'

// Mock Shopify blogs data - replace with actual Shopify API integration
const MOCK_BLOGS = [
  {
    id: 'blog_001',
    title: '5 Trending Wallcovering Styles for Modern Homes',
    createdAt: '2024-11-20T10:30:00Z',
    publishedAt: '2024-11-20T12:00:00Z',
    tags: ['trends', 'modern', 'interior-design'],
    handle: 'trending-wallcovering-styles-modern-homes',
    summary: 'Discover the latest wallcovering trends that are transforming modern interior spaces.',
    excerpt: 'From bold geometric patterns to subtle textured designs, explore the wallcovering styles that are defining contemporary home decor in 2024.'
  },
  {
    id: 'blog_002',
    title: 'How to Choose the Perfect Wallcovering for Your Dining Room',
    createdAt: '2024-11-19T14:15:00Z',
    publishedAt: '2024-11-19T16:00:00Z',
    tags: ['dining-room', 'tips', 'selection-guide'],
    handle: 'choose-perfect-wallcovering-dining-room',
    summary: 'A comprehensive guide to selecting wallcoverings that enhance your dining experience.',
    excerpt: 'Learn how to select wallcoverings that create the perfect ambiance for memorable dining experiences, considering factors like color psychology, pattern scale, and lighting.'
  },
  {
    id: 'blog_003',
    title: 'Vintage Damask: Bringing Classic Elegance to Contemporary Spaces',
    createdAt: '2024-11-18T09:45:00Z',
    publishedAt: null, // Draft
    tags: ['vintage', 'damask', 'classic', 'elegance'],
    handle: 'vintage-damask-classic-elegance-contemporary-spaces',
    summary: 'Explore how traditional damask patterns can be integrated into modern interior design.',
    excerpt: 'Discover the timeless appeal of damask wallcoverings and how to incorporate these classic patterns into contemporary interior design schemes.'
  },
  {
    id: 'blog_004',
    title: 'Tropical Themes: Creating a Resort Feel at Home',
    createdAt: '2024-11-17T11:20:00Z',
    publishedAt: '2024-11-17T13:30:00Z',
    tags: ['tropical', 'resort', 'vacation', 'nature'],
    handle: 'tropical-themes-resort-feel-home',
    summary: 'Transform your living spaces with tropical-inspired wallcovering designs.',
    excerpt: 'Bring the relaxing atmosphere of a tropical resort into your home with nature-inspired wallcovering patterns featuring lush foliage and exotic motifs.'
  },
  {
    id: 'blog_005',
    title: 'Abstract Art on Your Walls: Making a Bold Statement',
    createdAt: '2024-11-16T16:00:00Z',
    publishedAt: '2024-11-16T18:00:00Z',
    tags: ['abstract', 'art', 'bold', 'statement'],
    handle: 'abstract-art-walls-bold-statement',
    summary: 'How abstract wallcovering designs can serve as stunning focal points in any room.',
    excerpt: 'Explore how abstract wallcovering designs can transform your walls into captivating art pieces that reflect your personal style and creativity.'
  }
]

// Get generated blog posts from the generate/blog API
function getGeneratedBlogs() {
  try {
    const blogsDir = path.join(process.cwd(), 'data', 'generated-blogs')
    if (!fs.existsSync(blogsDir)) {
      return []
    }

    const files = fs.readdirSync(blogsDir)
      .filter(file => file.endsWith('.json'))
      .sort()
      .reverse()
      .slice(0, 10) // Latest 10

    return files.map(file => {
      try {
        const content = fs.readFileSync(path.join(blogsDir, file), 'utf-8')
        const blog = JSON.parse(content)
        return {
          id: `generated_${file.replace('.json', '')}`,
          title: blog.title,
          createdAt: blog.createdAt,
          publishedAt: blog.status === 'published' ? blog.createdAt : null,
          tags: blog.tags || [],
          handle: blog.title.toLowerCase().replace(/[^a-z0-9\s-]/g, '').replace(/\s+/g, '-'),
          summary: blog.excerpt,
          excerpt: blog.content.substring(0, 200) + '...',
          isGenerated: true,
          sku: blog.sku,
          wordCount: blog.wordCount,
          readingTime: blog.readingTime
        }
      } catch (e) {
        console.error('Error parsing generated blog file:', file, e)
        return null
      }
    }).filter((blog): blog is NonNullable<typeof blog> => blog !== null)
  } catch (error) {
    console.error('Error reading generated blogs:', error)
    return []
  }
}

interface ShopifyBlogsQuery {
  limit?: number
  sortKey?: 'CREATED_AT' | 'UPDATED_AT' | 'TITLE'
  reverse?: boolean
  published?: boolean
}

async function fetchShopifyBlogs(queryParams: ShopifyBlogsQuery) {
  const { limit = 20, sortKey = 'CREATED_AT', reverse = true, published } = queryParams

  // Combine mock blogs with generated blogs
  let allBlogs = [...MOCK_BLOGS, ...getGeneratedBlogs()]

  // Apply published filter
  if (published !== undefined) {
    allBlogs = allBlogs.filter(blog => {
      if (!blog) return false
      return published ? blog.publishedAt !== null : blog.publishedAt === null
    })
  }

  // Apply sorting
  allBlogs.sort((a, b) => {
    let comparison = 0
    switch (sortKey) {
      case 'TITLE':
        comparison = a.title.localeCompare(b.title)
        break
      case 'CREATED_AT':
      case 'UPDATED_AT':
      default:
        comparison = new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
        break
    }
    return reverse ? -comparison : comparison
  })

  // Apply limit
  allBlogs = allBlogs.slice(0, limit)

  return {
    data: {
      articles: {
        edges: allBlogs.map(blog => ({
          node: blog
        }))
      }
    }
  }
}

export async function GET(request: NextRequest) {
  try {
    const { searchParams } = new URL(request.url)
    
    const queryParams: ShopifyBlogsQuery = {
      limit: searchParams.get('limit') ? parseInt(searchParams.get('limit')!) : 20,
      sortKey: (searchParams.get('sortKey') as any) || 'CREATED_AT',
      reverse: searchParams.get('reverse') !== 'false',
      published: searchParams.get('published') ? searchParams.get('published') === 'true' : undefined
    }

    console.log('[SHOPIFY] Fetching blog posts with params:', queryParams)

    const shopifyResponse = await fetchShopifyBlogs(queryParams)

    if (shopifyResponse.data?.articles) {
      const blogs = shopifyResponse.data.articles.edges.map((edge: any) => ({
        id: edge.node.id,
        title: edge.node.title,
        createdAt: edge.node.createdAt,
        publishedAt: edge.node.publishedAt,
        tags: Array.isArray(edge.node.tags) ? edge.node.tags.join(', ') : edge.node.tags || '',
        handle: edge.node.handle,
        summary: edge.node.summary,
        excerpt: edge.node.excerpt,
        isGenerated: edge.node.isGenerated || false,
        sku: edge.node.sku || null,
        wordCount: edge.node.wordCount,
        readingTime: edge.node.readingTime
      }))

      console.log(`[SHOPIFY] Retrieved ${blogs.length} blog posts`)

      // Count published vs draft
      const publishedCount = blogs.filter(blog => blog.publishedAt).length
      const draftCount = blogs.filter(blog => !blog.publishedAt).length
      const generatedCount = blogs.filter(blog => blog.isGenerated).length

      return NextResponse.json({
        blogs,
        count: blogs.length,
        stats: {
          total: blogs.length,
          published: publishedCount,
          drafts: draftCount,
          generated: generatedCount
        }
      })
    } else {
      return NextResponse.json({
        blogs: [],
        count: 0,
        stats: { total: 0, published: 0, drafts: 0, generated: 0 },
        error: 'No blog posts found'
      })
    }
  } catch (error) {
    console.error('[SHOPIFY] Error fetching blog posts:', error)
    return NextResponse.json({
      error: 'Failed to fetch blog posts from Shopify',
      details: error instanceof Error ? error.message : String(error)
    }, { status: 500 })
  }
}

export async function POST(request: NextRequest) {
  try {
    // This could be used to create new blog posts via GraphQL mutation
    const body = await request.json()
    
    console.log('[SHOPIFY] Create blog post request:', body)
    
    // Mock successful creation
    const newBlog = {
      id: `blog_${Date.now()}`,
      title: body.title || 'New Blog Post',
      handle: (body.title || 'new-blog-post').toLowerCase().replace(/[^a-z0-9\s-]/g, '').replace(/\s+/g, '-'),
      createdAt: new Date().toISOString(),
      publishedAt: body.published ? new Date().toISOString() : null,
      tags: body.tags || [],
      summary: body.summary || 'New blog post summary',
      excerpt: body.excerpt || body.content?.substring(0, 200) + '...' || 'Blog post excerpt'
    }
    
    return NextResponse.json({
      success: true,
      blog: newBlog,
      message: 'Blog post created successfully (mock)'
    })
  } catch (error) {
    console.error('[SHOPIFY] Error creating blog post:', error)
    return NextResponse.json({
      error: 'Failed to create blog post',
      details: error instanceof Error ? error.message : String(error)
    }, { status: 500 })
  }
}