Initial Commit

This commit is contained in:
klein panic
2024-10-20 17:49:24 -04:00
commit 36ab4a789e
33 changed files with 8858 additions and 0 deletions

10
routes/about.js Normal file
View File

@@ -0,0 +1,10 @@
const express = require('express');
const router = express.Router();
const path = require('path');
// Serve about.html
router.get('/', (req, res) => {
res.sendFile(path.join(__dirname, '../views/about.html'));
});
module.exports = router;

10
routes/art.js Normal file
View File

@@ -0,0 +1,10 @@
const express = require('express');
const router = express.Router();
const path = require('path');
// Route to serve the art page
router.get('/', (req, res) => {
res.sendFile(path.join(__dirname, '../views/art.html'));
});
module.exports = router;

107
routes/blog.js Normal file
View File

@@ -0,0 +1,107 @@
const express = require('express');
const router = express.Router();
const path = require('path');
const fs = require('fs');
const { marked } = require('marked');
const fm = require('front-matter'); // For parsing front matter
const RSS = require('rss'); // Import RSS package
// Directory containing Markdown blog posts
const blogDirectory = path.join(__dirname, '../blog');
// Function to get all Markdown files in the blog directory
function getBlogPosts() {
const files = fs.readdirSync(blogDirectory);
return files.filter(file => file.endsWith('.md')).map(file => {
const content = fs.readFileSync(path.join(blogDirectory, file), 'utf-8');
// Parse front matter
const parsed = fm(content);
const { title, date, preview } = parsed.attributes; // Get title, date, and preview from front matter
// Remove Markdown formatting by converting to HTML and stripping the tags
const htmlContent = marked(parsed.body);
const plainText = htmlContent.replace(/<\/?[^>]+(>|$)/g, ""); // Strip HTML tags from Markdown-converted content
// Truncate the plain text to create the preview
const generatedPreview = plainText.split(' ').slice(0, 30).join(' ').substring(0, 150); // First 150 characters
// Use the preview from front matter if available, otherwise use the generated plain-text preview
const finalPreview = preview || generatedPreview;
const slug = file.replace('.md', ''); // Remove file extension for slug
console.log(`Preview for ${title}: ${finalPreview}`); // Log the preview to verify
return {
title,
date,
preview: finalPreview, // Ensure the correct preview is sent
slug
};
});
}
// New Route to generate RSS feed - put this before the dynamic slug route
router.get('/rss.xml', (req, res) => {
const feed = new RSS({
title: 'The Klein Blog',
description: 'Latest updates from The Klein Blog',
feed_url: `${req.protocol}://${req.get('host')}/rss.xml`,
site_url: `${req.protocol}://${req.get('host')}`,
language: 'en',
pubDate: new Date().toUTCString(),
});
// Add each blog post to the RSS feed
const posts = getBlogPosts();
posts.forEach(post => {
feed.item({
title: post.title,
description: post.preview, // Short preview for the RSS feed
url: `${req.protocol}://${req.get('host')}/blog/${post.slug}`, // Link to the blog post
date: post.date, // Published date
});
});
// Set the content type to XML and send the RSS feed
res.set('Content-Type', 'application/rss+xml');
res.send(feed.xml());
});
// Route to serve the blog posts as JSON
router.get('/blog-posts', (req, res) => {
const posts = getBlogPosts(); // Get all blog posts
res.json(posts); // Return posts as JSON to be fetched by the client-side JS
});
// Route to serve the main blog page (serve blog.html)
router.get('/', (req, res) => {
// Render the blog.html file from the views directory
res.sendFile(path.join(__dirname, '../views/blog.html'));
});
marked.setOptions({
gfm: true, // GitHub-flavored Markdown
breaks: true, // Enable line breaks
smartypants: true, // Use smart quotes, dashes, etc.
sanitize: false, // This is the important option to allow HTML tags
});
// Route to serve individual blog posts dynamically
router.get('/:slug', (req, res) => {
const { slug } = req.params;
const filePath = path.join(blogDirectory, `${slug}.md`);
if (fs.existsSync(filePath)) {
const content = fs.readFileSync(filePath, 'utf-8');
const parsed = fm(content); // Parse front matter
const htmlContent = marked(parsed.body); // Convert markdown body to HTML
res.render('post', { content: htmlContent, title: parsed.attributes.title, date: parsed.attributes.date }); // Render post view
} else {
res.status(404).send('Blog post not found');
}
});
module.exports = router;

78
routes/contact.js Normal file
View File

@@ -0,0 +1,78 @@
const express = require('express');
const router = express.Router();
const path = require('path');
const nodemailer = require('nodemailer');
const rateLimit = require('express-rate-limit');
const fs = require('fs');
require('dotenv').config(); // Load environment variables
// Define the log file path
const logFilePath = path.join(__dirname, '../stderr.log');
// Helper function to log errors to stderr.log
function logError(message) {
const logMessage = `${new Date().toISOString()} - ${message}\n`;
fs.appendFile(logFilePath, logMessage, (err) => {
if (err) {
console.error('Error writing to stderr.log:', err);
}
});
}
// Nodemailer setup using environment variables
const transporter = nodemailer.createTransport({
service: 'gmail', // Use your email service (like Gmail, Outlook, etc.)
auth: {
user: process.env.EMAIL_USER, // Fetch email from environment variable
pass: process.env.EMAIL_PASSWORD // Fetch password from environment variable
}
});
// Rate limiter to allow only one submission per 24 hours per IP
const contactFormLimiter = rateLimit({
windowMs: 24 * 60 * 60 * 1000, // 24 hours
max: 1, // Limit each IP to 1 request per window
message: 'You have already submitted a message. Please try again after 24 hours.'
});
// Route to serve the contact page
router.get('/', (req, res) => {
res.sendFile(path.join(__dirname, '../views/contact.html'));
});
// Route to handle form submissions
router.post('/submit-contact-form', contactFormLimiter, (req, res) => {
const { name, email, message } = req.body;
// Backend validation for the form fields
if (!name || !email || !message) {
logError('Form submission failed: Missing fields.');
return res.status(400).json({ success: false, message: 'All fields are required.' });
}
// Check if environment variables are set
if (!process.env.EMAIL_USER || !process.env.EMAIL_PASSWORD) {
logError('Error: EMAIL_USER or EMAIL_PASSWORD environment variables not set.');
return res.status(500).json({ success: false, message: 'Server configuration error: Environment variables not set.' });
}
// Email options for sending the message
const mailOptions = {
from: email, // Sender's email
to: process.env.EMAIL_USER, // Your email where you'll receive messages
subject: `New Contact Form Submission from ${name}`,
text: `You have received a new message from ${name} (${email}):\n\n${message}`
};
// Send the email
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
logError(`Error sending email: ${error.message}`);
return res.status(500).json({ success: false, message: 'Error sending email.' });
}
logError(`Email sent successfully: ${info.response}`);
res.json({ success: true, message: 'Your message has been sent successfully.' });
});
});
module.exports = router;

44
routes/index.js Normal file
View File

@@ -0,0 +1,44 @@
const express = require('express');
const router = express.Router();
const path = require('path');
// Function to detect curl requests
function isCurl(req) {
const userAgent = req.headers['user-agent'];
return userAgent && userAgent.includes('curl');
}
// Serve index.html for browser requests or plain text for curl
router.get('/', (req, res) => {
if (isCurl(req)) {
res.type('text/plain');
res.send(`
_ ___ _ _____ _
| |/ / | (_) | __ \\ (_)
| ' /| | ___ _ _ __ | |__) |_ _ _ __ _ ___
| < | |/ _ \\ | '_ \\| ___/ _\` | '_ \\| |/ __|
| . \\| | __/ | | | | | | (_| | | | | | (__
|_|\\_\\_|\\___|_|_| |_|_| \\__,_|_| |_|_|\\___|
My Awesome Website
Source: https://github.com/your-username/your-repo
+ ------------------------- + -------------------------------------- + -------------------- +
| TITLE | DESCRIPTION | VIDEO |
+ ------------------------- + -------------------------------------- + -------------------- +
| | | |
| | | |
| | | |
| | | |
+ ------------------------- + -------------------------------------- + -------------------- +
last updated: ${new Date().toUTCString()}
`);
} else {
// Render HTML for normal browser requests
res.sendFile(path.join(__dirname, '../views/index.html'));
}
});
module.exports = router;

9
routes/monitor.js Normal file
View File

@@ -0,0 +1,9 @@
const express = require('express');
const router = express.Router();
const path = require('path');
router.get('/', (req, res) => {
res.sendFile(path.join(__dirname, '../views/monitor.html'));
});
module.exports = router;

31
routes/projects.js Normal file
View File

@@ -0,0 +1,31 @@
const express = require('express');
const router = express.Router();
const axios = require('axios');
const path = require('path');
// Route for /projects page
router.get('/', (req, res) => {
res.sendFile(path.join(__dirname, '../views/projects.html'));
});
// Route for fetching GitHub projects dynamically via JavaScript
router.get('/fetch', async (req, res) => {
const page = req.query.page || 1;
const per_page = req.query.per_page || 100; // Maximum is 100 items per page
try {
// Fetch user's GitHub repositories with pagination
const response = await axios.get(`https://api.github.com/users/kleinpanic/repos`, {
params: {
page,
per_page,
},
});
res.json(response.data);
} catch (error) {
res.status(500).json({ error: 'Unable to fetch GitHub repositories.' });
}
});
module.exports = router;