|
1 | | -# Blogit |
| 1 | +# BLOGIT |
2 | 2 |
|
3 | 3 | [English](./README.md) | [中文](./README_CN.md) |
4 | 4 |
|
5 | | -Blogit is a pnpm monorepo for a Markdown-first blog system: |
| 5 | +<h1 align="center">A Git-powered, local-first blogging system</h1> |
6 | 6 |
|
7 | | -- `apps/blog`: public blog site built with Next.js 16 (App Router), deployed to Cloudflare via OpenNext. |
8 | | -- `apps/admin`: password-protected admin panel built with React + Vite, deployed on Cloudflare Pages Functions, writing blog content directly to GitHub with the Git Data API. |
| 7 | +- **Content as code**: content is code, and posts are Markdown files in your repository. |
| 8 | +- **Publishing as push**: publishing is pushing, directly through your existing Git/CI/CD workflow. |
| 9 | +- **Cloneable and forkable**: cloneable and forkable, the whole blogging system is portable and reproducible. |
| 10 | +- **No platform lock-in**: no platform lock-in and no dependency on third-party image hosting. |
| 11 | +- **SEO optimized by default**: powered by SSG static generation, with built-in `metadata`, `sitemap.xml`, and `robots.txt` for search-engine friendliness. |
| 12 | +- **Visual admin panel**: includes an admin system with Markdown block-level editing, so non-technical users can also write and manage content easily. |
9 | 13 |
|
10 | | -## Core Value |
| 14 | +## I. Quick Start |
11 | 15 |
|
12 | | -**Own your content. For real.** |
| 16 | +### 1. Use this template |
13 | 17 |
|
14 | | -Blogit is designed around content ownership instead of platform dependency: |
| 18 | +Click [Use this template](https://github.com/new?template_name=Blogit&template_owner=Hexi1997) to create your own repository (Public) from the Blogit template, then clone it locally and install dependencies. |
15 | 19 |
|
16 | | -- **Content as code**: posts are plain Markdown files in your own Git repo. |
17 | | -- **Local media, no image CDN lock-in**: image assets are stored with each post (`posts/<slug>/assets`), without third-party image hosting. |
18 | | -- **Writing as commit**: every edit is a Git commit with full history and diff. |
19 | | -- **Publishing as push**: publish by pushing changes through your existing CI/CD flow. |
20 | | -- **Cloneable and forkable**: your blog system is portable and reproducible. |
21 | | -- **No platform lock-in**: no proprietary CMS data silo; files stay with you. |
| 20 | +### 2. Update repo config in [config.ts](apps/admin/src/lib/config.ts) |
22 | 21 |
|
23 | | -## 1. What This Repo Includes |
| 22 | +Change it to the owner and repo name of the repository created in step 1. |
24 | 23 |
|
25 | | -- Markdown content lives in `apps/blog/posts/<slug>/index.md`. |
26 | | -- Per-post assets live in `apps/blog/posts/<slug>/assets/*`. |
27 | | -- Blog list index cache: `apps/blog/posts/_index.json`. |
28 | | -- Public blog runtime: |
29 | | - - SSG pages |
| 24 | +### 3. Generate a GitHub PAT and initialize local Admin variables |
| 25 | + |
| 26 | +Create a [GitHub Personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic) with repository write permissions, then copy [.dev.vars.example](apps/admin/.dev.vars.example) to `.dev.vars`: |
| 27 | + |
| 28 | +Then update values in `.dev.vars`: |
| 29 | + - `ADMIN_PAT=<your_github_pat>` |
| 30 | + - `ADMIN_PASSWORD_HASH=<sha256_of_password>` (you can generate it at https://emn178.github.io/online-tools/sha256.html) |
| 31 | + |
| 32 | +### 4. Configure Giscus comments and write Blog env vars |
| 33 | +> Giscus env vars are optional. If not configured, the comment area will not be shown. |
| 34 | +
|
| 35 | +Enable [Discussions](https://docs.github.com/en/discussions/quickstart#enabling-github-discussions-on-your-repository) in your repo and install [Giscus App](https://github.com/apps/giscus), then visit [giscus.app](https://giscus.app) to generate parameters and write them into [.env](apps/blog/.env): |
| 36 | + - `NEXT_PUBLIC_GISCUS_REPO` |
| 37 | + - `NEXT_PUBLIC_GISCUS_REPO_ID` |
| 38 | + - `NEXT_PUBLIC_GISCUS_CATEGORY` |
| 39 | + - `NEXT_PUBLIC_GISCUS_CATEGORY_ID` |
| 40 | + |
| 41 | +### 5. Configure GitHub Action environment variables |
| 42 | + |
| 43 | +Log in to Cloudflare, create an `Account API Token` (the `Edit Cloudflare Workers` template is enough with proper permissions), and get your `Account ID`. |
| 44 | + |
| 45 | +In your repository at `Settings -> Secrets and variables -> Actions -> Repository secrets`, configure: |
| 46 | + - `CLOUDFLARE_API_TOKEN` |
| 47 | + - `CLOUDFLARE_ACCOUNT_ID` |
| 48 | + - `ADMIN_PAT` |
| 49 | + - `ADMIN_PASSWORD_HASH` |
| 50 | + |
| 51 | +### 6. Commit and Push |
| 52 | + |
| 53 | + - Commit all changes to GitHub. |
| 54 | + - After GitHub workflows finish, you can find deployment URLs for `blogit-admin` and `blogit-blog` under `Cloudflare Workers and Pages`. |
| 55 | + - Get the deployment URL of `blogit-blog` and update `NEXT_PUBLIC_SITE_URL` in [.env](apps/blog/.env). |
| 56 | + |
| 57 | +## II. Project Overview |
| 58 | + |
| 59 | +### 1. Introduction |
| 60 | +Blogit is a pnpm monorepo Markdown blog system with two applications: |
| 61 | + |
| 62 | +- `apps/blog`: reader-facing blog site, built with Next.js 16 (App Router), deployed to Cloudflare via OpenNext. |
| 63 | +- `apps/admin`: admin application for visual blog CRUD operations. Built with React + Vite + Cloudflare Pages Functions, and writes content directly via GitHub Git Data API. |
| 64 | + |
| 65 | +### 2. Project Content |
| 66 | + |
| 67 | +- Post source of truth: `apps/blog/posts/<slug>/index.md` |
| 68 | +- Post assets: `apps/blog/posts/<slug>/assets/`* |
| 69 | +- Post index cache: [apps/blog/posts/_index.json](apps/blog/posts/_index.json) |
| 70 | +- Blog capabilities: |
| 71 | + - SSG static pages |
30 | 72 | - SEO (`sitemap.xml`, `robots.txt`, metadata) |
31 | 73 | - Giscus comments |
32 | | - - Math (KaTeX), code highlighting (Shiki), copy-code and image preview enhancements |
33 | | -- Admin runtime: |
34 | | - - Simple password login |
| 74 | + - KaTeX math, Shiki code highlighting, code copy, image preview enhancement |
| 75 | + - Tag system |
| 76 | +- Admin capabilities: |
| 77 | + - Password login |
35 | 78 | - Post list / create / edit / delete |
36 | | - - Atomic commits to GitHub (content + images + `_index.json` in one commit) |
| 79 | + - Atomic commit (content + images + `_index.json` in one commit) |
37 | 80 |
|
38 | | -## 2. Monorepo Structure |
| 81 | +## III. Directory Structure |
39 | 82 |
|
40 | 83 | ```text |
41 | | -. |
42 | 84 | ├── apps/ |
43 | | -│ ├── blog/ # Public blog (Next.js 16) |
| 85 | +│ ├── blog/ # Public blog |
44 | 86 | │ │ ├── app/ |
45 | 87 | │ │ ├── posts/ # Markdown source of truth |
46 | 88 | │ │ ├── public/ |
47 | 89 | │ │ └── scripts/ |
48 | | -│ └── admin/ # Admin panel (React + Vite + CF Pages Functions) |
49 | | -├── package.json # Workspace scripts |
| 90 | +│ └── admin/ # Admin |
| 91 | +├── package.json |
50 | 92 | └── pnpm-workspace.yaml |
51 | 93 | ``` |
52 | 94 |
|
53 | | -## 3. Prerequisites |
54 | | - |
55 | | -- Node.js 20+ recommended |
56 | | -- pnpm `10.30.0` (declared in root `packageManager`) |
57 | | -- A GitHub Personal Access Token (PAT) with repo write permission (for admin) |
58 | | -- Cloudflare account (for deployment) |
59 | | - |
60 | | -## 4. Install Dependencies |
| 95 | +## IV. Install Dependencies |
61 | 96 |
|
62 | 97 | ```bash |
63 | 98 | pnpm install |
64 | 99 | ``` |
65 | 100 |
|
66 | | -## 5. Run Locally |
| 101 | +## V. Local Development |
67 | 102 |
|
68 | | -### 5.1 Start Both Apps |
| 103 | +### 1. Start both apps |
69 | 104 |
|
70 | 105 | ```bash |
71 | 106 | pnpm dev |
72 | 107 | ``` |
73 | 108 |
|
74 | | -### 5.2 Start Individual Apps |
| 109 | +### 2. Start individually |
75 | 110 |
|
76 | 111 | ```bash |
77 | 112 | pnpm dev:blog |
78 | 113 | pnpm dev:admin |
79 | 114 | ``` |
80 | 115 |
|
81 | | -Default local endpoints: |
| 116 | +Common local URLs: |
82 | 117 |
|
83 | 118 | - Blog: `http://localhost:3000` |
84 | | -- Admin: Wrangler Pages dev prints its own URL (commonly `http://localhost:8788`) |
85 | | - |
86 | | -## 6. Environment Variables |
87 | | - |
88 | | -### 6.1 Blog (`apps/blog`) |
89 | | - |
90 | | -The blog expects: |
91 | | - |
92 | | -- `NEXT_PUBLIC_SITE_URL` (required): canonical site URL, used by metadata/sitemap/robots. |
93 | | - |
94 | | -Example: |
95 | | - |
96 | | -```bash |
97 | | -cd apps/blog |
98 | | -echo 'NEXT_PUBLIC_SITE_URL=https://your-blog-domain.com' > .env.local |
99 | | -``` |
100 | | - |
101 | | -### 6.2 Admin (`apps/admin`) |
102 | | - |
103 | | -Admin Pages Function `functions/api/auth/login.ts` uses: |
104 | | - |
105 | | -- `ADMIN_PAT`: GitHub PAT used by server-side login response and GitHub API writes. |
106 | | -- `ADMIN_PASSWORD_HASH`: SHA-256 hash of the login password. |
107 | | - |
108 | | -Create SHA-256 hash: |
109 | | - |
110 | | -```bash |
111 | | -printf "your-password" | shasum -a 256 |
112 | | -``` |
| 119 | +- Admin: printed by Wrangler Pages dev (`http://localhost:8788`) |
113 | 120 |
|
114 | | -Set secrets in Cloudflare Pages project (Production/Preview), and use `.dev.vars` for local wrangler dev. |
| 121 | +## VI. Blog Content Management |
115 | 122 |
|
116 | | -## 7. Blog Content Workflow |
117 | | - |
118 | | -### 7.1 Post Format |
119 | | - |
120 | | -Path: |
| 123 | +### 1. Directory Structure |
121 | 124 |
|
122 | 125 | ```text |
123 | 126 | apps/blog/posts/<slug>/ |
124 | 127 | ├── index.md |
125 | 128 | └── assets/ |
126 | 129 | ``` |
127 | 130 |
|
| 131 | +### 2. Frontmatter Example |
| 132 | + |
128 | 133 | Frontmatter example: |
129 | 134 |
|
130 | 135 | ```yaml |
131 | 136 | --- |
132 | | -title: "Post Title" |
| 137 | +title: "Post title" |
133 | 138 | date: "2026-03-05" |
134 | 139 | cover: "assets/cover.webp" |
135 | 140 | tags: |
136 | 141 | - nextjs |
137 | 142 | - cloudflare |
138 | | -source: "https://example.com/original-link" # optional external source |
| 143 | +source: "https://example.com/original-link" # optional external link post |
139 | 144 | --- |
140 | 145 | ``` |
141 | 146 |
|
142 | | -Notes: |
143 | | - |
144 | | -- If `cover` is missing, blog auto-falls back to first image in markdown, then `/default-cover.png`. |
145 | | -- If `source` is set, the list card links to external URL directly. |
146 | | -- Relative image paths are resolved from post assets. |
147 | | - |
148 | | -### 7.2 Index and Assets |
149 | | - |
150 | | -Useful scripts in `apps/blog`: |
151 | | - |
152 | | -- `pnpm --filter blog run generate-index`: regenerate `posts/_index.json`. |
153 | | -- `pnpm --filter blog run sync-assets`: sync `posts/*/assets` to `public/blog-assets` for static serving. |
154 | | - |
155 | | -Admin writes `_index.json` during save/delete operations, so manual generation is usually unnecessary when editing through admin. |
156 | | - |
157 | | -## 8. Build and Deploy |
158 | | - |
159 | | -### 8.1 Workspace Build |
160 | | - |
161 | | -```bash |
162 | | -pnpm build |
163 | | -``` |
164 | | - |
165 | | -### 8.2 Deploy Blog (OpenNext + Cloudflare) |
166 | | - |
167 | | -```bash |
168 | | -pnpm --filter blog run preview # local preview with OpenNext Cloudflare runtime |
169 | | -pnpm --filter blog run deploy # deploy worker/assets per wrangler config |
170 | | -``` |
171 | | - |
172 | | -### 8.3 Deploy Admin (Cloudflare Pages) |
173 | | - |
174 | | -```bash |
175 | | -pnpm --filter admin run build |
176 | | -pnpm --filter admin run deploy |
177 | | -``` |
178 | | - |
179 | | -## 9. Repo Configuration You Should Update |
180 | | - |
181 | | -Edit: |
182 | | - |
183 | | -- `apps/admin/src/lib/config.ts` |
184 | | - |
185 | | -`BLOG_REPO_CONFIG` defaults to: |
186 | | - |
187 | | -- `owner: "Hexi1997"` |
188 | | -- `repo: "Blogit"` |
189 | | -- `blogPath: "apps/blog/posts"` |
190 | | -- `branch: "main"` |
191 | | - |
192 | | -Change these to your own repo if you fork/use this project. |
193 | | - |
194 | | -## 10. Script Reference |
195 | | - |
196 | | -Root: |
197 | | - |
198 | | -- `pnpm dev`: run all app dev scripts in parallel |
199 | | -- `pnpm dev:blog` |
200 | | -- `pnpm dev:admin` |
201 | | -- `pnpm build` |
202 | | -- `pnpm build:blog` |
203 | | -- `pnpm build:admin` |
204 | | -- `pnpm preview:blog` |
| 147 | +### 3. Field Descriptions |
205 | 148 |
|
206 | | -Blog app: |
| 149 | +- **title** (required): post title |
| 150 | +- **date** (required): publish date, format: YYYY-MM-DD |
| 151 | +- **cover** (optional): cover image path; supports relative paths (for example, `assets/cover.jpg`) or external URLs (for example, `https://example.com/image.jpg`). If missing, the system tries the first image in markdown; if none exists, it falls back to `/default-cover.png`. |
| 152 | +- **source** (optional): external article URL. If set, clicking the card in the list redirects directly to this external URL (opens in a new tab), instead of the internal post detail page. The source link is also shown at the bottom of the post detail page. Useful for third-party content references. |
| 153 | +- **tags** (optional): post tags. Supports YAML array format, for example `tags: ["nextjs", "react"]` or multiline list format. |
207 | 154 |
|
208 | | -- `pnpm --filter blog run dev` |
209 | | -- `pnpm --filter blog run build` |
210 | | -- `pnpm --filter blog run preview` |
211 | | -- `pnpm --filter blog run deploy` |
212 | | -- `pnpm --filter blog run generate-index` |
213 | | -- `pnpm --filter blog run sync-assets` |
| 155 | +> **Important:** The site does not fetch content from external links automatically. The blog list still needs summary/description text, so you must provide content in the `index.md` body manually. The system extracts text from markdown and generates the card description. |
214 | 156 |
|
215 | | -Admin app: |
| 157 | +### 4. Index and Asset Sync |
216 | 158 |
|
217 | | -- `pnpm --filter admin run dev` |
218 | | -- `pnpm --filter admin run build` |
219 | | -- `pnpm --filter admin run deploy` |
| 159 | +- `pnpm --filter blog run generate-index`: rebuild `posts/_index.json` (automatically handled by [workflow](.github/workflows/sync-post-index.yml), no manual local run needed) |
| 160 | +- `pnpm --filter blog run sync-assets`: sync `posts/*/assets` to `public/blog-assets` (automatically handled by [workflow](.github/workflows/deploy-blog.yml), no manual local run needed) |
220 | 161 |
|
221 | | -## 11. License |
| 162 | +## VII. License |
222 | 163 |
|
223 | | -MIT License. See [LICENSE](./LICENSE). |
| 164 | +MIT License, see [LICENSE](./LICENSE). |
0 commit comments