Skip to content

aolose/emm

Repository files navigation

EMM - Easy Markdown Manager

Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge

A self-hosted markdown blog system built with SvelteKit and Bun. Designed for personal writing and content management, with a focus on simplicity and performance.

Contents

Screenshots

Desktop

file list

file list

file view

login

writing

file management

admin settings

Mobile

file list on mobile file view on mobile admin on mobile writing on mobile file management on mobile

Features

  • SSR + PWA — Server-side rendering with offline support via service workers
  • Responsive Design — Optimized for both desktop and mobile devices
  • Markdown Editor — Full-featured editor built on CodeMirror 6 with custom toolbar and paste-to-upload
  • Syntax Highlighting — Code blocks styled with highlight.js
  • Article Management — Create, edit, and organize markdown posts with version diff history
  • Tags — Categorize content with a tag system; Chinese titles auto-generate pinyin slugs
  • Comments — Built-in comment management with moderation support
  • File Management — Upload and manage static files and images
  • Image Viewer — Click-to-zoom image preview powered by Viewer.js
  • Image Compression — Automatic client-side image compression on upload
  • Visit Statistics — Track page views (PUV) with an admin dashboard
  • Blog Mode — Publish posts publicly or keep them private
  • RSS Feed — Auto-generated RSS feed for public posts
  • Sitemap & robots.txt — Auto-generated for search engine indexing
  • Turnstile Integration — Cloudflare Turnstile CAPTCHA with verified bot bypass
  • Firewall — IP-based access control, bot detection, custom rules with time scheduling
  • UA Collection Detection — Detect distributed crawlers by grouping IPs sharing the same User-Agent
  • Geo IP Location — Display visitor geographic information based on IP2Location Lite
  • Cloudflare Integration — Auto-push blocked IPs to Cloudflare IP Lists for edge-level filtering
  • IP Aggregation — Automatically merge blacklist IPs into /24 and /16 CIDR blocks to reduce list size
  • Backup & Restore — Export and import data for disaster recovery
  • Self-Hosted — Runs on your own server, all data stays with you

Tech Stack

Category Technology
Framework SvelteKit (SSR)
Runtime Bun
Database SQLite via bun:sqlite
Styling SCSS + clsx
Fonts SUIT · Noto Sans SC
Markdown Editor CodeMirror 6 + svelte-codemirror-editor
Markdown Renderer marked + highlight.js
Type Checking TypeScript

Getting Started

Prerequisites

Quick Start

# Clone the repository
git clone https://github.com/aolose/emm.git
cd emm

# Install dependencies
bun install

# Build for production
bun run build

# Start the server
bun run preview

The application will be available at http://localhost:4173.

Environment Variables

Variable Default Description
PORT 4173 Server listen port
ORIGIN http://localhost:4173 Public origin URL (required for RSS / sitemap)

Set them before starting the server:

PORT=3000 ORIGIN=https://blog.example.com bun run preview

Authentication

On first run, visit the /config page to set up your admin username and password. No registration system — single admin account.

Configuration

All settings are managed through the Admin UI (/admin/setting), stored in the SQLite database:

  • Blog Info: blog name, bio, SEO keywords/description, social links
  • Upload/Thumbnail Directories: configurable storage paths for uploaded files and generated thumbnails
  • Geo Location: ip2location lite token and database directory for IP-based country blocking
  • Comments: enable/disable comments, moderation toggle
  • Firewall Rules: IP/header/path-based access rules, rate limiting, custom responses

Runtime files and directories:

  • SQLite database — configured path (stores posts, tags, config, firewall rules, etc.)
  • Upload directory — configured in admin settings (defaults to data/upload)
  • Thumbnail directory — configured in admin settings (defaults to data/thumb)

Production Deployment

bun install
bun run build
# The built output is in the `dist/` directory
bun run dist/index.js

For long-running production use, consider a process manager:

# systemd example (/etc/systemd/system/emm.service)
[Unit]
Description=EMM Blog
After=network.target

[Service]
Type=simple
User=emm
WorkingDirectory=/home/emm/app
Environment=PORT=3000
Environment=ORIGIN=https://blog.example.com
ExecStart=bun run dist/index.js
Restart=on-failure

[Install]
WantedBy=multi-user.target

Or with PM2:

pm2 start dist/index.js --name emm --interpreter bun

Turnstile Anti-Crawl Setup

EMM integrates Cloudflare Turnstile CAPTCHA and includes a built-in search engine crawler whitelist bypass logic.

If you use Cloudflare proxy and have Turnstile enabled, you need to configure a Transform Rule to ensure SEO crawlers are not blocked.

For detailed steps, see: doc/turnstile.md

Security Reminder: Make sure to protect your origin server to prevent attackers from bypassing Cloudflare and directly accessing the origin IP to forge request headers. It is recommended to use Cloudflare Tunnel or restrict your origin firewall to only accept Cloudflare IP ranges.

Contributing

Issues and pull requests are welcome. Before submitting a PR:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/your-feature)
  3. Commit your changes
  4. Push and open a pull request

License

MIT © Aolose

About

A self-hosted markdown blog system built with SvelteKit and Bun. Designed for personal writing and content management,.

Topics

Resources

Stars

Watchers

Forks

Contributors