feat: Containerize services with Docker Compose (PostgreSQL, Strapi CMS, Express Backend, Nginx Frontend) on custom ports >9000

This commit is contained in:
Vishva
2026-08-03 19:58:10 +05:30
parent 48f8ab9716
commit 6244fd4072
247 changed files with 56672 additions and 169 deletions
+180
View File
@@ -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;
}
}