Files
pengzhan.dev/server/routes/post.js
T
haopengzhan 8c0b04db0e feat: initial release of pengzhan.dev personal editorial blog platform
- Editorial web platform built with Node.js, Express, EJS, and Vanilla CSS
- Markdown content engine with YAML frontmatter, bilingual variants, and hot-reload watcher
- Unified SQLite with FTS5 search indexing, query logging, and schema filters
- Centered editorial post layout with margin-docked left TOC and mobile bottom sheet
- WordPress-grade comment system with moderation dashboard and bulk operations under /guanli
- Complete SEO suite (JSON-LD structured data, RSS/Atom feeds, XML sitemap, robots.txt)
- CI/CD workflows for linting, security audits (Gitleaks), automated tests, and Docker releases
2026-09-16 23:08:53 -07:00

53 lines
1.8 KiB
JavaScript

const express = require('express');
const router = express.Router();
const contentService = require('../services/content');
const commentService = require('../services/comments');
function renderPost(req, res, next, slug, lang) {
const post = contentService.getPost(slug, lang);
if (!post) {
return next(); // Fall through to standalone pages or 404
}
const { prev, next: nextPost } = contentService.getAdjacentPosts(slug);
const comments = commentService.getCommentsForPost(slug);
// Set SEO metadata for post
res.locals.seo = {
title: `${post.title} - ${res.locals.site.title}`,
description: post.excerpt || res.locals.site.tagline,
canonicalUrl: `${res.locals.site.url}/${post.slug}${post.lang !== 'en' && !post.isDefault ? `/${post.lang}` : ''}`,
ogImage: post.cover ? (post.cover.startsWith('http') ? post.cover : `${res.locals.site.url}${post.cover}`) : res.locals.seo.ogImage,
type: 'article',
publishedTime: post.date.toISOString(),
author: post.author
};
res.render('pages/post', {
title: post.title,
post,
currentLang: lang || post.lang,
comments,
commentStatus: req.query.comment_status,
prevPost: prev,
nextPost
});
}
// Route for bilingual variant: /<post-slug>/<lang> (e.g. /my-post/zh)
router.get('/:slug/:lang([a-zA-Z]{2,5})', (req, res, next) => {
renderPost(req, res, next, req.params.slug, req.params.lang);
});
// Route for default language: /<post-slug>
router.get('/:slug', (req, res, next) => {
// Exclude reserved top-level prefixes if any
const reserved = ['blogs', 'search', 'quick-links', 'feed', 'rss.xml', 'sitemap.xml', 'robots.txt', 'api', 'plugins', 'public', 'posts-assets', 'guanli', 'admin'];
if (reserved.includes(req.params.slug)) {
return next();
}
renderPost(req, res, next, req.params.slug, null);
});
module.exports = router;