import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; import { DatabaseSync } from 'node:sqlite'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const strapiDbPath = path.join(__dirname, '..', 'strapi-cms', '.tmp', 'data.db'); const strapiUploadsDir = path.join(__dirname, '..', 'strapi-cms', 'public', 'uploads'); const targetPublicUploads = path.join(__dirname, '..', 'public', 'uploads'); const targetServerUploads = path.join(__dirname, '..', 'server', 'uploads'); function syncUploads() { try { if (!fs.existsSync(strapiUploadsDir)) return; if (!fs.existsSync(targetPublicUploads)) fs.mkdirSync(targetPublicUploads, { recursive: true }); if (!fs.existsSync(targetServerUploads)) fs.mkdirSync(targetServerUploads, { recursive: true }); const files = fs.readdirSync(strapiUploadsDir); files.forEach((f) => { if (f === '.gitkeep') return; const srcFile = path.join(strapiUploadsDir, f); const destPublic = path.join(targetPublicUploads, f); const destServer = path.join(targetServerUploads, f); if (!fs.existsSync(destPublic)) fs.copyFileSync(srcFile, destPublic); if (!fs.existsSync(destServer)) fs.copyFileSync(srcFile, destServer); }); } catch (e) { console.warn('Note: Upload sync warning:', e.message); } } export async function getStrapiBlogs() { syncUploads(); // 1. Primary: Fetch live blogs directly from Strapi REST API (PostgreSQL / Live Strapi DB) try { const strapiBaseUrl = process.env.STRAPI_URL || 'http://127.0.0.1:1337'; const response = await fetch(`${strapiBaseUrl}/api/blogs?populate=*`, { headers: { 'Accept': 'application/json' }, signal: AbortSignal.timeout(3000) }); if (response.ok) { const json = await response.json(); const items = json.data || []; if (Array.isArray(items) && items.length > 0) { const blogs = items.map((b) => { let coverUrl = b.coverImage?.url || b.ogImage?.url || null; if (!coverUrl) { const cat = (b.category || '').toLowerCase(); if (b.title?.includes('LLM')) coverUrl = '/assets/arvindh_llm_financial.jpg'; else if (b.title?.includes('Supply Chain')) coverUrl = '/assets/arvindh_supply_chain.jpg'; else if (b.title?.includes('PRODMAX')) coverUrl = '/assets/prodmax_dashboard.png'; else if (b.title?.includes('Incidents') || b.title?.includes('VR')) coverUrl = '/assets/whyus_vr_man.jpg'; else if (b.title?.includes('SCADA') || b.title?.includes('IT/OT')) coverUrl = '/assets/whyus_arab_meeting.jpg'; else if (b.title?.includes('Cleanroom')) coverUrl = 'https://images.unsplash.com/photo-1592478411213-6153e4ebc07d?auto=format&fit=crop&w=800&q=80'; else if (b.title?.includes('Spatial') || b.title?.includes('Maintenance')) coverUrl = '/assets/whyus_hologram.jpg'; else if (b.title?.includes('Energy')) coverUrl = '/assets/whyus_teamwork.png'; else if (b.title?.includes('Telemetry') || b.title?.includes('Sensor')) coverUrl = 'https://images.unsplash.com/photo-1581091226825-a6a2a5aee158?auto=format&fit=crop&w=800&q=80'; else if (b.title?.includes('Monoliths') || b.title?.includes('Micro-Frontends')) coverUrl = 'https://images.unsplash.com/photo-1519389950473-47ba0277781c?auto=format&fit=crop&w=800&q=80'; else if (b.title?.includes('Zero-Trust')) coverUrl = 'https://images.unsplash.com/photo-1563986768494-4dee2763ff3f?auto=format&fit=crop&w=800&q=80'; else if (b.title?.includes('Multi-Cloud')) coverUrl = 'https://images.unsplash.com/photo-1544197150-b99a580bb7a8?auto=format&fit=crop&w=800&q=80'; else if (cat.includes('ai')) coverUrl = '/assets/arvindh_agentic_ai.jpg'; else coverUrl = 'https://images.unsplash.com/photo-1519389950473-47ba0277781c?auto=format&fit=crop&w=800&q=80'; } const rawDate = b.publishDate || b.publishedAt || b.createdAt; const dateStr = rawDate ? new Date(rawDate).toLocaleDateString('en-US', { month: 'short', day: '2-digit', year: 'numeric' }) : new Date().toLocaleDateString('en-US', { month: 'short', day: '2-digit', year: 'numeric' }); return { id: b.id || b.documentId, documentId: b.documentId, category: b.category || 'Technology', title: b.title, excerpt: b.excerpt || '', content: b.content || '', readTime: b.readTime || '5 min read', date: dateStr, image: coverUrl, slug: b.slug, author: (typeof b.authorName === 'string' && b.authorName.trim()) ? b.authorName.trim() : (typeof b.author === 'string' && b.author.trim() ? b.author.trim() : (b.author?.firstname ? `${b.author.firstname} ${b.author.lastname || ''}`.trim() : (b.author?.username || 'Cavin Editorial Team'))), authorDesignation: b.authorDesignation || 'Chief AI Architect & Head of Data Engineering', editor: (typeof b.editorName === 'string' && b.editorName.trim()) ? b.editorName.trim() : (typeof b.editor === 'string' && b.editor.trim() ? b.editor.trim() : (b.editor?.firstname ? `${b.editor.firstname} ${b.editor.lastname || ''}`.trim() : (b.editor?.username || null))), editorDesignation: b.editorDesignation || null, tags: b.tags || [], metaTitle: b.metaTitle || null, metaDescription: b.metaDescription || null, keywords: b.keywords || null, canonicalURL: b.canonicalURL || null }; }); if (blogs.length > 0) return blogs; } } } catch (apiErr) { console.warn('Note: Live Strapi REST API fetch bypassed/failed, trying SQLite fallback:', apiErr.message); } // 2. Fallback: SQLite `.tmp/data.db` if Strapi API is offline try { if (!fs.existsSync(strapiDbPath)) return null; const db = new DatabaseSync(strapiDbPath); const rows = db.prepare('SELECT * FROM blogs WHERE published_at IS NOT NULL ORDER BY id ASC').all(); const mediaLinks = db.prepare("SELECT * FROM files_related_mph WHERE related_type = 'api::blog.blog'").all(); const files = db.prepare('SELECT * FROM files').all(); const adminUsers = db.prepare('SELECT id, firstname, lastname FROM admin_users').all(); const userMap = {}; adminUsers.forEach((u) => { userMap[u.id] = `${u.firstname || ''} ${u.lastname || ''}`.trim(); }); const seenSlugs = new Set(); const blogs = []; rows.forEach((b) => { if (!b.title || b.title.toLowerCase().startsWith('test 3')) return; if (seenSlugs.has(b.slug)) return; seenSlugs.add(b.slug); const link = mediaLinks.find((l) => l.related_id === b.id); const file = link ? files.find((f) => f.id === link.file_id) : null; let image = file ? file.url : null; if (!image) { const cat = (b.category || '').toLowerCase(); if (b.title.includes('LLM')) image = '/assets/arvindh_llm_financial.jpg'; else if (b.title.includes('Supply Chain')) image = '/assets/arvindh_supply_chain.jpg'; else if (b.title.includes('PRODMAX')) image = '/assets/prodmax_dashboard.png'; else if (b.title.includes('Incidents') || b.title.includes('VR')) image = '/assets/whyus_vr_man.jpg'; else if (b.title.includes('SCADA') || b.title.includes('IT/OT')) image = '/assets/whyus_arab_meeting.jpg'; else if (b.title.includes('Cleanroom')) image = 'https://images.unsplash.com/photo-1592478411213-6153e4ebc07d?auto=format&fit=crop&w=800&q=80'; else if (b.title.includes('Spatial') || b.title.includes('Maintenance')) image = '/assets/whyus_hologram.jpg'; else if (b.title.includes('Energy')) image = '/assets/whyus_teamwork.png'; else if (b.title.includes('Telemetry') || b.title.includes('Sensor')) image = 'https://images.unsplash.com/photo-1581091226825-a6a2a5aee158?auto=format&fit=crop&w=800&q=80'; else if (b.title.includes('Monoliths') || b.title.includes('Micro-Frontends')) image = 'https://images.unsplash.com/photo-1519389950473-47ba0277781c?auto=format&fit=crop&w=800&q=80'; else if (b.title.includes('Zero-Trust')) image = 'https://images.unsplash.com/photo-1563986768494-4dee2763ff3f?auto=format&fit=crop&w=800&q=80'; else if (b.title.includes('Multi-Cloud')) image = 'https://images.unsplash.com/photo-1544197150-b99a580bb7a8?auto=format&fit=crop&w=800&q=80'; else if (cat.includes('ai')) image = '/assets/arvindh_agentic_ai.jpg'; else image = 'https://images.unsplash.com/photo-1519389950473-47ba0277781c?auto=format&fit=crop&w=800&q=80'; } const dateStr = b.published_at ? new Date(Number(b.published_at)).toLocaleDateString('en-US', { month: 'short', day: '2-digit', year: 'numeric' }) : new Date(Number(b.created_at || Date.now())).toLocaleDateString('en-US', { month: 'short', day: '2-digit', year: 'numeric' }); const author = userMap[b.created_by_id] || 'Dr. Arvindh Ramachandran'; const authorRole = b.author_designation || 'Chief AI Architect & Head of Data Engineering'; const hasEditor = Boolean(b.editor_designation || (b.updated_by_id && b.updated_by_id !== b.created_by_id)); const editor = hasEditor ? (userMap[b.updated_by_id] && userMap[b.updated_by_id] !== author ? userMap[b.updated_by_id] : 'Cavin Editorial Board') : null; const editorRole = b.editor_designation || (hasEditor ? 'Senior Technical Reviewer' : null); blogs.push({ id: b.id, category: b.category || 'Technology', title: b.title, excerpt: b.excerpt || '', content: b.content || '', readTime: b.read_time || '5 min read', date: dateStr, image, slug: b.slug, author, authorDesignation: authorRole, editor, editorDesignation: editorRole, tags: b.tags ? JSON.parse(b.tags) : [] }); }); return blogs.length > 0 ? blogs : null; } catch (err) { console.warn('Note: Strapi SQLite DB sync check bypassed:', err.message); return null; } }