feat: Containerize services with Docker Compose (PostgreSQL, Strapi CMS, Express Backend, Nginx Frontend) on custom ports >9000
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
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.author === 'string' ? b.author : (b.author?.firstname ? `${b.author.firstname} ${b.author.lastname || ''}`.trim() : (b.author?.username || 'Dr. Arvindh Ramachandran')),
|
||||
authorDesignation: b.authorDesignation || 'Chief AI Architect & Head of Data Engineering',
|
||||
editor: typeof b.editor === 'string' ? b.editor : (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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user