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
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
const chokidar = require('chokidar');
|
||||
const path = require('path');
|
||||
const contentService = require('./content');
|
||||
const pluginManager = require('./plugin-manager');
|
||||
const { loadSiteConfig } = require('../config');
|
||||
|
||||
class WatcherService {
|
||||
constructor() {
|
||||
this.watcher = null;
|
||||
this.debounceTimer = null;
|
||||
}
|
||||
|
||||
start(contentDir, app) {
|
||||
console.log(`[Watcher] Starting filesystem watcher on ${contentDir}`);
|
||||
|
||||
this.watcher = chokidar.watch(contentDir, {
|
||||
ignored: [
|
||||
/(^|[\/\\])\../, // dotfiles
|
||||
/(^|[\/\\])data([\/\\]|$)/, // ignore entire data/ directory (site.db, journals, etc.)
|
||||
/\.db(-journal|-wal|-shm)?$/, // sqlite database files
|
||||
/node_modules/
|
||||
],
|
||||
persistent: true,
|
||||
ignoreInitial: true,
|
||||
awaitWriteFinish: {
|
||||
stabilityThreshold: 300,
|
||||
pollInterval: 100
|
||||
}
|
||||
});
|
||||
|
||||
const triggerReload = (event, filePath) => {
|
||||
const relative = path.relative(contentDir, filePath);
|
||||
console.log(`[Watcher] Detected ${event} on ${relative}`);
|
||||
|
||||
clearTimeout(this.debounceTimer);
|
||||
this.debounceTimer = setTimeout(() => {
|
||||
try {
|
||||
const freshConfig = loadSiteConfig();
|
||||
pluginManager.loadPlugins(contentDir, app, freshConfig);
|
||||
contentService.reload();
|
||||
console.log(`[Watcher] Hot reload finished successfully.`);
|
||||
} catch (err) {
|
||||
console.error(`[Watcher] Error during hot reload:`, err);
|
||||
}
|
||||
}, 250);
|
||||
};
|
||||
|
||||
this.watcher.on('add', (p) => triggerReload('add', p));
|
||||
this.watcher.on('change', (p) => triggerReload('change', p));
|
||||
this.watcher.on('unlink', (p) => triggerReload('unlink', p));
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.watcher) {
|
||||
this.watcher.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new WatcherService();
|
||||
Reference in New Issue
Block a user