Narravo: A Modern Next.js Blog Engine
Narravo is a sleek, minimal, and feature-rich blog engine designed for developers who appreciate modern web technologies and a robust content management experience. Built with the latest Next.js App Router, TypeScript, and Drizzle ORM, it offers a powerful foundation for your next personal blog or content platform.
✨ Key Features:
- Next.js 15 App Router: Server Components, Server Actions, ISR, and smart caching tuned for fast content delivery.
- React 19 + TypeScript 5.9: Strict typing, modern React patterns, and explicit null handling across the stack.
- Tailwind CSS Design System: Utility-first styling with theme tokens, dark mode, and Radix-powered primitives.
- PostgreSQL + Drizzle ORM: Type-safe queries, migrations, and transactions with first-class PostgreSQL support.
- Auth.js (NextAuth): GitHub/Google OAuth, admin allowlists, and session management ready to go.
- Two-Factor Authentication (2FA): TOTP authenticator apps, WebAuthn passkeys (Face ID, Touch ID, YubiKey), recovery codes, and trusted device management.
- Config Service & Feature Flags: Database-backed settings power runtime toggles (themes, rate limits, UI modules).
- WordPress WXR Import: Resume-safe imports covering posts, media, redirects, excerpts, and CLI/admin workflows.
- Data Operations Toolkit: One-command backup/export, selective restore, purging, and manifest verification with audit logs.
- Comments, Media & Reactions: Threaded discussions, image/video uploads, emoji reactions, honeypot/rate-limited submissions.
- Admin Control Center: Moderation queue, system settings, analytics dashboards, and import/backups in one place.
- S3/R2 Media Storage: Presigned uploads with validation for AWS S3 or Cloudflare R2 plus local fallbacks.
- Analytics & Observability: Privacy-aware trending metrics, Core Web Vitals RUM pipeline, Server-Timing headers, and perf scripts.
- Security Hardening: CSP, HSTS, DOMPurify sanitization, hashed IP analytics, rate limits, and audit-friendly logging.
- Dockerized Development & Testing: Docker Compose Postgres, Vitest test suites, and perf/load tooling baked in.
👀 DEMO SITE: (My personal blog) Knowledge Tome
Get Narravo up and running in minutes!
Prerequisites:
# 0) Start PostgreSQL database with Docker Compose
docker compose up -d db
# 1) Install project dependencies
pnpm install
# 2) Configure environment variables
cp .env.example .env
# IMPORTANT: Ensure DATABASE_URL, NEXTAUTH_SECRET, ADMIN_EMAILS, and ANALYTICS_IP_SALT are set.
# Adjust NEXTAUTH_URL and OAuth provider IDs as needed for your environment.
# 3) Run database migrations (create tables)
pnpm drizzle:migrate
# 4) Seed essential configuration defaults
pnpm seed:config
# 5) (Optional) Seed demo posts and comments
pnpm seed:posts
# 6) Start the development server
pnpm dev
# Open your browser to http://localhost:3000
# 7) (First-time setup) Sign in with OAuth and set up 2FA
# - Sign in with GitHub or Google (your email must be in ADMIN_EMAILS)
# - Navigate to /admin/security to enable Two-Factor Authentication
# - Choose TOTP (authenticator app) or Passkey (biometric/hardware key)💡 Manual DB Setup: If you prefer a manual PostgreSQL instance, update
DATABASE_URLin your.envfile and skip thedocker compose up -d dbstep.🔐 Admin Access: Only emails listed in
ADMIN_EMAILScan access the admin dashboard at/admin. After first login, you'll be prompted to set up Two-Factor Authentication for enhanced security.
Navravo ships with first-class data lifecycle tooling powered by Drizzle and S3-compatible manifests, accessible via both CLI scripts and the Admin UI at /admin/data-operations.
- Full Backups:
pnpm backupcreates a ZIP archive with JSON table exports, manifest hashes, and (optionally) media references. - Audit Friendly: Commands log counts, checksum data, and skip reasons to aid compliance reviews.
- Admin UI: Export backups directly from the admin dashboard with progress tracking.
- Selective Restore:
pnpm restore -- <backup.zip>supports dry runs, slug/date filters, and skipping users/configuration. - Smart Conflict Handling: Choose how to handle existing content during restore operations.
- Media Awareness: Manifests capture attachment URLs and hashes so you can verify remote assets before rehydration.
- Soft Purge: Remove published posts while preserving drafts, users, and configuration.
- Hard Purge: Complete data reset removing all posts, comments, categories, tags, redirects, and uploaded files.
- Confirmation Required: All destructive operations require explicit confirmation in the UI.
- Complete Trail: All data operations (export, restore, purge) are logged with timestamps, user info, and operation details.
- Compliance Ready: Detailed audit logs support security reviews and compliance requirements.
# Create a verbose backup without media payloads
pnpm backup -- --skip-media --verbose
# Preview restore scope for specific slugs before applying changes
pnpm restore -- backups/blog-2025-09-01.zip --dry-run --slugs my-first-post,second-postNarravo is designed for flexible deployment. You can host it on your own infrastructure using Docker or deploy it to a serverless platform like Vercel.
- Option A: Docker Compose - A single VM setup using Docker Compose to run the application and database. This is a good option for self-hosting.
- Option B: Vercel + Neon - A serverless setup using Vercel for the application and Neon for the database. This is a good option for a managed, scalable solution.
For detailed deployment instructions, please refer to the Production Deployment Guide.
- App Router Layout: The
src/apptree separates public routes ((public)), authentication ((auth)), and admin tooling ((admin)), leaning on React Server Components by default. Middleware injects route context and handles legacy redirect resolution at the edge. - Domain Modules: Business logic lives in
src/libwith clear boundaries for posts, comments, reactions, imports, backups, rate limiting, analytics, 2FA, and configuration. Server Actions validate input with Zod and revalidate cache tags after mutations. - Authentication & Security: Auth.js handles OAuth sessions with admin allowlists. Two-factor authentication (TOTP/WebAuthn) protects admin routes with 8-hour verification windows, trusted device management, and comprehensive audit logging.
- Data & Configuration: Drizzle ORM drives the schema (
drizzle/schema.ts) with migrations and transactions. A Config Service exposes database-backed feature flags (banner, theming, rate limits, render badges, etc.) to both server and client code. - Engagement Pipeline: Comments support nested threads, media attachments, moderation queues, and reaction toggles. Rate limiting, honeypots, media validation, and audit logging protect the workflow.
- Content Migration: The WordPress importer coordinates resumable jobs, media rewriting, redirect creation, and excerpt rebuilding from either the Admin UI or CLI scripts.
- Data Operations: Export, restore, and purge operations with manifest verification, selective filtering, and audit trails accessible via CLI or admin UI.
- Observability: Privacy-aware analytics aggregate daily view counts, power trending widgets, and surface dashboards. Real User Monitoring collects Core Web Vitals, while Server-Timing instrumentation and perf scripts keep regressions in check.
All environment variables are documented in detail within the .env.example file. Copy it to .env and adjust as needed:
cp .env.example .envRequired Variables:
DATABASE_URL: PostgreSQL connection string (e.g.,postgres://user:pass@localhost:5432/narravo)NEXTAUTH_SECRET: Strong random string for session/JWT encryption (generate withopenssl rand -base64 32)NEXTAUTH_URL: Your application's URL (e.g.,http://localhost:3000orhttps://yourdomain.com)ADMIN_EMAILS: Comma-separated list of emails for admin access (case-insensitive)
Authentication & Security:
- OAuth Providers:
GITHUB_ID,GITHUB_SECRET,GOOGLE_ID,GOOGLE_SECRET(optional but recommended) - Analytics Privacy:
ANALYTICS_IP_SALT— Salt for hashing IP addresses before persisting view events (strongly recommended)
Media Storage (Optional):
Choose one of the following:
- AWS S3:
S3_REGION,S3_ACCESS_KEY_ID,S3_SECRET_ACCESS_KEY,S3_BUCKET,S3_ENDPOINT(optional for S3-compatible services) - Cloudflare R2:
R2_REGION,R2_ACCESS_KEY_ID,R2_SECRET_ACCESS_KEY,R2_BUCKET,R2_ENDPOINT
If neither is configured, media will be stored locally in the public/uploads directory.
Performance & Observability (Optional):
NEXT_PUBLIC_RUM_SAMPLING_RATE— Client-side Real User Monitoring sampling rate (default:0.1= 10%)RUM_SAMPLING_RATE— Server-side RUM sampling rate (default:0.1)PERF_LOG_SLOW_QUERIES— Enable verbose query timing logs in development/CI (default:false)
Content Configuration (Optional):
EXCERPT_MAX_CHARS— Maximum characters for auto-generated excerpts (default: varies by algorithm)EXCERPT_ELLIPSIS— Ellipsis string for truncated excerpts (default:…)EXCERPT_INCLUDE_BLOCK_CODE— Include code blocks in excerpts (default:false)
Narravo uses Drizzle ORM for type-safe database interactions and migrations.
- Generate Migrations: Create new migration files based on schema changes.
pnpm drizzle:generate
- Apply Migrations: Run pending migrations (production-safe).
pnpm drizzle:migrate
- Check Migration Status: Verify which migrations have been applied.
pnpm drizzle:check
- Sync Migration Tracking: Fix migration tracking after using
drizzle:push.CONFIRM_MIGRATION_SYNC=yes pnpm drizzle:sync
- Push Schema (Dev Only): Quickly sync schema during local development.
pnpm drizzle:push
- Seed Configuration: Essential for initial setup and default settings.
pnpm seed:config
- Seed Demo Content: Populate your blog with sample posts and comments.
pnpm seed:posts
📖 For detailed migration workflows and troubleshooting, see the Database Migration Guide.
Narravo ships with first-class data lifecycle tooling powered by Drizzle and S3-compatible manifests.
- Full Backups:
pnpm backupcreates a ZIP archive with JSON table exports, manifest hashes, and (optionally) media references. - Selective Restore:
pnpm restore -- <backup.zip> [--slugs slug-a,slug-b] [--start-date YYYY-MM-DD]supports dry runs, slug/date filters, and skipping users/configuration. - Media Awareness: Manifests capture attachment URLs and hashes so you can verify remote assets before rehydration.
- Audit Friendly: Both commands log counts, checksum data, and skip reasons to aid compliance reviews.
# Create a verbose backup without media payloads
pnpm backup -- --skip-media --verbose
# Preview restore scope for specific slugs before applying changes
pnpm restore -- backups/blog-2025-09-01.zip --dry-run --slugs my-first-post,second-postA quick reference for common development tasks:
| Command | Description |
|---|---|
pnpm dev |
Start the Next.js development server |
pnpm build |
Create a production-ready build |
pnpm start |
Start the production server |
pnpm typecheck |
Run TypeScript type checks |
pnpm test |
Execute the full test suite (Vitest) |
pnpm test:watch |
Run tests in watch mode |
pnpm drizzle:generate |
Generate new Drizzle migrations |
pnpm drizzle:migrate |
Apply pending migrations (production-safe) |
pnpm drizzle:push |
Sync schema directly (development only) |
pnpm drizzle:check |
Check migration status and database state |
pnpm drizzle:sync |
Fix migration tracking (after push) |
pnpm seed:config |
Seed default configuration values |
pnpm seed:posts |
Seed demo posts and comments |
pnpm wxr:import -- path=… |
Launch the WordPress importer via CLI (supports flags) |
pnpm backup |
Create a manifested ZIP backup (see Backups section) |
pnpm restore -- <file> |
Restore or dry run a backup archive |
pnpm cleanup:uploads |
Remove orphaned temporary uploads (see below) |
pnpm perf:benchmark |
Run the combined performance benchmark suite |
pnpm perf:lighthouse |
Execute Lighthouse CI checks |
pnpm perf:loadtest |
Run Autocannon smoke load testing |
pnpm perf:analyze |
Build with bundle analyzer enabled |
pnpm perf:weekly |
Produce a weekly performance rollup report |
Narravo tracks all image/video uploads in the database. When users add images to posts in the editor, they're initially marked as "temporary". Once the post is saved/published, these uploads are committed. The cleanup script removes orphaned temporary uploads:
# Dry run - see what would be deleted
npm run cleanup:uploads -- --dry-run
# Delete temporary uploads older than 24 hours (default)
npm run cleanup:uploads
# Delete temporary uploads older than 48 hours
npm run cleanup:uploads -- --age-hours=48Recommended: Set up a cron job to run this cleanup daily or weekly to prevent accumulation of orphaned uploads from abandoned post drafts.
A high-level overview of the project's directory layout:
src/app/ # Next.js App Router routes
├── (admin)/ # Protected admin routes (posts, users, analytics, security, data-operations)
├── (auth)/ # Authentication routes (signin, 2FA verification)
├── (public)/ # Public-facing pages (posts, archives, search)
└── api/ # API routes (2FA, comments, reactions, media uploads)
src/components/ # Reusable UI components (admin, auth, public)
src/lib/ # Server-side services and business logic
├── 2fa/ # Two-factor authentication (TOTP, WebAuthn, recovery codes)
├── excerpts/ # Excerpt generation algorithms
└── *.ts # Domain modules (posts, comments, analytics, config, etc.)
src/types/ # TypeScript types (consolidated)
drizzle/ # Drizzle ORM schema and database migrations
scripts/ # Utility scripts (seeding, imports, backups, migrations)
tests/ # Unit and integration tests (Vitest)
docs/ # Project documentation and specifications
Narravo includes a powerful and resilient WordPress import tool to migrate your content from a WordPress WXR export file.
Import Capabilities:
- Comprehensive Content: Imports posts, comments (with threading), categories, and tags.
- Flexible Statuses: Choose which post statuses to import (e.g.,
publish,draft). - Media & SEO: Downloads media to S3/R2, rewrites content URLs, and creates 301 redirects from old WordPress post URLs.
- Offline Import Support: Import media from local uploads directory when original site is offline or unreachable.
- Enhanced Purge: Complete data reset option that removes all posts, comments, categories, tags, redirects, and uploaded files.
- Resilient Process: Features include dry-runs, real-time progress, job cancellation, and detailed error logging.
- Admin UI & CLI: Manage imports through the Admin Dashboard or automate them with a CLI script.
For a complete guide, see the WordPress Import Documentation.
The wxr:import script supports several command-line flags to customize the import process:
path=<file>: (Required) The path to your WXR export file.--verbose: Enables detailed logging for troubleshooting.--dry-run: Simulates the import without making any changes to the database.--skip-media: Skips downloading and processing of any media files.--rebuild-excerpts: Forces regeneration of excerpts for all posts, even if they already have one.--purge: Deletes all existing posts, comments, tags, categories, redirects, and uploaded files before starting the import. Use with caution.uploads=<path>: Specifies the path to a local folder containing your WordPressuploadsdirectory. Use this for offline imports where the original site is not reachable.root=<pattern>: A regular expression to match the root URL of your old site (e.g.,^https?://my-old-site.com). Required when usinguploads.allowedHosts=<hosts>: Comma-separated list of allowed domains for media downloads (e.g.,example.com,cdn.example.com).concurrency=<number>: Number of simultaneous media downloads (1-10, default: 4).
This command runs an import using a local backup of media files, purging all existing data and files first.
pnpm wxr:import -- path=./export.xml --purge uploads=/path/to/wp-backup/uploads root='^https?://my-old-site\.com$' --verbose- Trending & Dashboards: View aggregation tables power the public "Trending Posts" widget and the
/admin/analyticsdashboard with sparklines, totals, and configurable date windows. - Privacy-Aware Metrics: View events dedupe by session/IP, respect bot filters, and hash IP addresses when
ANALYTICS_IP_SALTis set. - Real User Monitoring: Drop
<RUMCollector />into layouts to post Core Web Vitals to/api/rum. Control sampling withNEXT_PUBLIC_RUM_SAMPLING_RATE(client) andRUM_SAMPLING_RATE(server), both defaulting to 10%. - Performance Instrumentation: Middleware adds Server-Timing headers, and the config flag
VIEW.PUBLIC-SHOW-RENDER-BADGEsurfaces render times in the UI. Perf scripts (perf:*) automate Lighthouse, bundle analysis, and load testing—seedocs/perf/README.md. - Weekly Rollups & Benchmarks: Use
pnpm perf:benchmarkorpnpm perf:weeklyto archive reports indocs/perf/and catch regressions early.
An optional About Me section can appear in the public sidebar.
- Enable/disable: SITE.ABOUT-ME.ENABLED (boolean)
- Title: SITE.ABOUT-ME.TITLE (string)
- Content: SITE.ABOUT-ME.CONTENT (string)
You can manage these in the Admin Dashboard:
- Navigate to Admin -> System -> About Me
- Toggle enable, edit the title and content, then Save Changes
When enabled, the About Me section renders above the Recent posts list.
- Sanitized Content: All HTML passes through DOMPurify allowlists before storage/rendering, and markdown editors sanitize on save.
- Strict Headers: CSP, HSTS, Referrer-Policy, and X-Content-Type-Options are enabled via Next config and middleware defaults.
- Rate Limiting & Abuse Controls: Shared helpers cap comment/reaction/import rates, enforce submission delays, and add honeypots.
- Safe Media Handling: Attachments validate MIME types, file signatures, byte limits, and generate poster images for videos.
- Privacy Respecting Analytics: View tracking dedupes sessions, hashes IPs with
ANALYTICS_IP_SALT, and honors DNT where possible. - Two-Factor Authentication: Admin routes protected with 2FA enforcement, with 8-hour verification windows and secure session management.
Narravo includes a robust 2FA implementation protecting admin routes with multiple authentication methods and comprehensive security features.
Features:
- Multiple Methods: TOTP (Google Authenticator, Authy, etc.), WebAuthn/Passkeys (Face ID, Touch ID, YubiKey), Recovery codes
- Trusted Devices: Optional 30-day device trust to reduce friction for frequent users
- Security Activity Log: Comprehensive audit trail of all 2FA events (enable, disable, verification attempts, device management)
- Admin Enforcement: Admins must complete 2FA setup before accessing admin dashboard
- 8-Hour Verification Window: Re-verification required after 8 hours for security-sensitive operations
- Rate Limiting: Protection against brute force attacks with progressive backoff
- Recovery Options: Backup codes and passkey fallbacks prevent lockouts
Technical Implementation:
- TOTP via
@levminer/speakeasywith 30-second window and drift tolerance - WebAuthn via SimpleWebAuthn with RP ID validation and credential counter protection
- Trusted device tokens use SHA-256 hashed identifiers with 30-day expiration
- Security activity logged to database with IP, user agent, and detailed event context
- Client-side guard component (TwoFactorGuard) enforces verification UI
- Server-side helpers (requireAdmin2FA) protect API routes
- Test suite: 120+ tests across 15+ test files covering TOTP, WebAuthn, rate limiting, trusted devices, security logging, admin enforcement, and all API endpoints
Narravo has a comprehensive test suite with 500+ tests covering core functionality, security features, and edge cases.
Run Tests:
pnpm test # Run all tests
pnpm test:watch # Run tests in watch mode
pnpm typecheck # Type checking onlyTest Coverage Includes:
- 2FA & Security: TOTP verification, WebAuthn flows, passkey support, recovery codes, trusted devices, rate limiting, admin enforcement, security activity logging (60+ tests across 15+ test files)
- API Endpoints: All admin APIs (config, data operations, user management, purge), 2FA endpoints (TOTP, WebAuthn, recovery, trusted devices), uploads, metrics, redirects, import jobs (100+ tests)
- Server Actions: Post deletion with cascade, WordPress import job management, theme preferences (64+ tests)
- Middleware: Request flow security, redirects (date-based and database), caching, authentication checks (27+ tests)
- Content Management: Post creation, WordPress imports (30+ specialized import tests), markdown processing, excerpt generation, taxonomy, blocks parsing
- Analytics: View tracking, deduplication, bot detection, privacy features, trending metrics
- Comments & Moderation: Threading, timing attacks, honeypots, auto-approval, path resolution
- Data Operations: Backup/restore workflows, manifest verification, purge operations, audit logging
- Configuration: Feature flags, boolean parsing, system settings, user preferences
- Utilities: Logger with log levels, frame-src CSP configuration, admin access control, date formatting, HTML normalization
Test Infrastructure:
- Vitest as the test runner with TypeScript strict mode
- React Testing Library for component testing
- In-memory SQLite database for fast, isolated unit tests
- Comprehensive mocking strategies for Auth.js, Next.js, and external services
- No external dependencies required for running tests
- All tests pass TypeScript strict null checks and build validation
Quality Standards:
- ✅ All tests pass
pnpm typecheck(TypeScript strict mode) - ✅ All tests pass
pnpm build(production build validation) - ✅ All tests pass
pnpm test(100% pass rate) - ✅ Comprehensive security test coverage (2FA, authentication, authorization)
- ✅ Critical path coverage for all admin functionality
This project is licensed under the Apache License, Version 2.0. See the LICENSE and NOTICE files for more details.