2024-09-16 07:53:26 -04:00
|
|
|
require('dotenv').config(); // Load environment variables
|
|
|
|
const express = require('express');
|
|
|
|
const path = require('path');
|
|
|
|
const fs = require('fs');
|
|
|
|
const { marked } = require('marked');
|
|
|
|
const nodemailer = require('nodemailer');
|
|
|
|
const hljs = require('highlight.js');
|
|
|
|
|
|
|
|
const app = express();
|
|
|
|
|
|
|
|
// Set options for marked to use highlight.js for syntax highlighting
|
|
|
|
marked.setOptions({
|
|
|
|
highlight: function (code, language) {
|
|
|
|
// Check if the language is valid
|
|
|
|
const validLanguage = hljs.getLanguage(language) ? language : 'plaintext';
|
|
|
|
return hljs.highlight(validLanguage, code).value;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Set EJS as templating engine
|
|
|
|
app.set('view engine', 'ejs');
|
|
|
|
app.set('views', path.join(__dirname, 'views'));
|
|
|
|
|
|
|
|
// Middleware to parse URL-encoded bodies (form submissions)
|
|
|
|
app.use(express.urlencoded({ extended: false }));
|
|
|
|
|
|
|
|
// Serve static files (CSS, Images)
|
|
|
|
app.use(express.static(path.join(__dirname, 'public')));
|
|
|
|
|
|
|
|
// Function to load and parse markdown files and extract lead
|
|
|
|
function loadMarkdownWithLead(file) {
|
|
|
|
const markdownContent = fs.readFileSync(path.join(__dirname, 'markdown', file), 'utf-8');
|
|
|
|
|
|
|
|
let lead = '';
|
|
|
|
let contentMarkdown = markdownContent;
|
|
|
|
|
|
|
|
// Detect and extract the lead section
|
|
|
|
const leadKeyword = '<!-- lead -->';
|
|
|
|
if (contentMarkdown.includes(leadKeyword)) {
|
|
|
|
const [beforeLead, afterLead] = contentMarkdown.split(leadKeyword);
|
|
|
|
|
|
|
|
// Extract the first paragraph after the lead keyword
|
|
|
|
lead = afterLead.split('\n').find(line => line.trim() !== '').trim();
|
|
|
|
|
|
|
|
// Remove the lead from the main content
|
|
|
|
contentMarkdown = beforeLead + afterLead.replace(lead, '').trim();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert markdown to HTML
|
|
|
|
const contentHtml = marked.parse(contentMarkdown);
|
|
|
|
|
|
|
|
return { contentHtml, lead };
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function to convert a title (with spaces) into a URL-friendly slug (with dashes)
|
|
|
|
function titleToSlug(title) {
|
|
|
|
return title.replace(/\s+/g, '-').toLowerCase(); // Always lowercase the slug
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function to convert a slug (with dashes) back into a readable title (with spaces)
|
|
|
|
function slugToTitle(slug) {
|
|
|
|
return slug.replace(/-/g, ' ');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function to load all blog posts
|
|
|
|
function getAllBlogPosts() {
|
|
|
|
const blogFiles = fs.readdirSync(path.join(__dirname, 'markdown')).filter(file => file.endsWith('.md'));
|
|
|
|
return blogFiles.map(file => {
|
|
|
|
const title = file.replace('.md', '').replace(/-/g, ' '); // Keep original casing for title
|
|
|
|
const slug = titleToSlug(title); // Convert title to slug (lowercase)
|
|
|
|
return {
|
|
|
|
title, // Original casing title
|
|
|
|
slug
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Home Route (Blog Home)
|
|
|
|
app.get('/', (req, res) => {
|
|
|
|
const blogPosts = getAllBlogPosts();
|
|
|
|
res.render('index', { title: 'Raven Scott Blog', blogPosts });
|
|
|
|
});
|
|
|
|
|
|
|
|
// About Route
|
|
|
|
app.get('/about', (req, res) => {
|
|
|
|
res.render('about', { title: 'About Raven Scott' });
|
|
|
|
});
|
|
|
|
|
|
|
|
// Display the Request a Quote form
|
|
|
|
app.get('/contact', (req, res) => {
|
|
|
|
res.render('contact', { title: 'Contact Raven Scott', msg: undefined });
|
|
|
|
});
|
|
|
|
|
2024-09-16 08:07:36 -04:00
|
|
|
// Handle contact form submission
|
|
|
|
app.post('/contact', (req, res) => {
|
|
|
|
const { name, email, subject, message } = req.body;
|
|
|
|
|
|
|
|
// Validate form inputs (basic example)
|
|
|
|
if (!name || !email || !subject || !message) {
|
|
|
|
return res.render('contact', { title: 'Contact Raven Scott', msg: 'All fields are required.' });
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create email content
|
|
|
|
const output = `
|
|
|
|
<p>You have a new contact request from <strong>${name}</strong>.</p>
|
|
|
|
<h3>Contact Details</h3>
|
|
|
|
<ul>
|
|
|
|
<li><strong>Name:</strong> ${name}</li>
|
|
|
|
<li><strong>Email:</strong> ${email}</li>
|
|
|
|
<li><strong>Subject:</strong> ${subject}</li>
|
|
|
|
</ul>
|
|
|
|
<h3>Message</h3>
|
|
|
|
<p>${message}</p>
|
|
|
|
`;
|
|
|
|
|
|
|
|
// Set up Nodemailer transporter
|
|
|
|
let transporter = nodemailer.createTransport({
|
|
|
|
host: process.env.SMTP_HOST,
|
|
|
|
port: process.env.SMTP_PORT,
|
|
|
|
secure: false, // true for 465, false for other ports
|
|
|
|
auth: {
|
|
|
|
user: process.env.EMAIL_USER, // Email user from environment variables
|
|
|
|
pass: process.env.EMAIL_PASS, // Email password from environment variables
|
|
|
|
},
|
|
|
|
tls: {
|
|
|
|
rejectUnauthorized: false,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
// Set up email options
|
|
|
|
let mailOptions = {
|
|
|
|
from: `"${name}" <quote@node-geeks.com>`,
|
|
|
|
to: process.env.RECEIVER_EMAIL, // Your email address to receive contact form submissions
|
|
|
|
subject: subject,
|
|
|
|
html: output,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Send email
|
|
|
|
transporter.sendMail(mailOptions, (error, info) => {
|
|
|
|
if (error) {
|
|
|
|
console.error(error);
|
|
|
|
return res.render('contact', { title: 'Contact Raven Scott', msg: 'An error occurred. Please try again.' });
|
|
|
|
} else {
|
|
|
|
console.log('Email sent: ' + info.response);
|
|
|
|
return res.render('contact', { title: 'Contact Raven Scott', msg: 'Your message has been sent successfully!' });
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2024-09-16 07:53:26 -04:00
|
|
|
// Blog Post Route
|
|
|
|
app.get('/blog/:slug', (req, res) => {
|
|
|
|
const slug = req.params.slug;
|
|
|
|
const markdownFile = fs.readdirSync(path.join(__dirname, 'markdown'))
|
|
|
|
.find(file => titleToSlug(file.replace('.md', '')) === slug);
|
|
|
|
|
|
|
|
if (markdownFile) {
|
|
|
|
const originalTitle = markdownFile.replace('.md', ''); // Original title with casing
|
|
|
|
const blogPosts = getAllBlogPosts();
|
|
|
|
const { contentHtml, lead } = loadMarkdownWithLead(markdownFile);
|
|
|
|
|
|
|
|
res.render('blog-post', {
|
|
|
|
title: originalTitle, // Use the original title with casing
|
|
|
|
content: contentHtml,
|
|
|
|
lead: lead,
|
|
|
|
blogPosts
|
|
|
|
});
|
|
|
|
} else {
|
2024-09-16 08:21:29 -04:00
|
|
|
res.redirect('/'); // Redirect to the home page if the blog post is not found
|
2024-09-16 07:53:26 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-09-16 08:21:29 -04:00
|
|
|
// Global 404 handler for any other unmatched routes
|
|
|
|
app.use((req, res) => {
|
|
|
|
res.redirect('/'); // Redirect to the home page for any 404 error
|
|
|
|
});
|
|
|
|
|
2024-09-16 07:53:26 -04:00
|
|
|
// ================================
|
|
|
|
// Server Listening
|
|
|
|
// ================================
|
2024-09-16 07:55:16 -04:00
|
|
|
const PORT = process.env.PORT || 8899;
|
2024-09-16 07:53:26 -04:00
|
|
|
app.listen(PORT, () => {
|
|
|
|
console.log(`Server running on http://localhost:${PORT}`);
|
|
|
|
});
|