58 lines
3.3 KiB
Markdown
58 lines
3.3 KiB
Markdown
# blog-manager
|
|
|
|
A local web app for managing the portfolio blog: create, edit and delete posts in a markdown editor with live preview, upload images, rename and merge categories and tags, and publish through the existing git and deploy pipeline.
|
|
|
|
There is **no authentication**. The server listens on `0.0.0.0:3051`, so restrict access to that port with firewall rules.
|
|
|
|
## How it works
|
|
|
|
- **Store**: `portfolio/data/blog-posts.json`. The site imports it as `BLOG_POSTS` in `data/content.ts`. The manager edits this file in place, in the mounted portfolio checkout.
|
|
- **Images**: uploads go to `portfolio/public/img/blog/<slug>/` and are referenced as `img/blog/<slug>/<file>`.
|
|
- **Preview**: rendered with the site's own remark pipeline and styled with the site's own CSS, so it looks like the live post page.
|
|
- **Publish**:
|
|
1. Commits only `data/blog-posts.json` and `public/img/blog/`. Other changes in the checkout are never included.
|
|
2. Runs `git push`.
|
|
3. Calls the site's `POST /api/deploy` (optional). This triggers `scripts/deploy-watch.sh` on the server.
|
|
- **Safety**: writes are atomic. A save made from a stale copy, for example after a `git pull`, is rejected so you can reload. Browsers can't send cross-site writes, because requests whose `Origin` doesn't match the host are refused.
|
|
|
|
## Run with Docker
|
|
|
|
```bash
|
|
cp .env.example .env # set UID/GID, git identity, push token, deploy secret
|
|
docker compose up -d --build
|
|
```
|
|
|
|
Then open `http://<host>:3051`.
|
|
|
|
- The container mounts `../portfolio` at `/repo`. Set `PORTFOLIO_DIR` to use a different checkout.
|
|
- Set `BLOG_MANAGER_UID` and `BLOG_MANAGER_GID` to the owner of the checkout (`id -u` / `id -g`). Otherwise, on Linux, files the manager writes end up owned by a different user, and git and `deploy-watch` may then fail with permission errors.
|
|
- **Push credentials**:
|
|
- For an HTTPS remote, set `GIT_PUSH_USER` and `GIT_PUSH_TOKEN`. git receives them through `scripts/git-askpass.sh`, and they're never written to disk.
|
|
- Set `GIT_PUSH=false` to only commit and push by hand.
|
|
- If the manager runs on the deploy host against the same checkout that `deploy-watch` pulls, that pull is a no-op fast-forward after a publish.
|
|
|
|
## Run without Docker
|
|
|
|
```bash
|
|
npm install
|
|
npm run dev # http://0.0.0.0:3051, uses ../portfolio unless REPO_DIR is set
|
|
```
|
|
|
|
## Environment
|
|
|
|
| Variable | Default | Purpose |
|
|
|---|---|---|
|
|
| `REPO_DIR` | `../portfolio` (`/repo` in Docker) | Portfolio git checkout |
|
|
| `GIT_AUTHOR_NAME`, `GIT_AUTHOR_EMAIL` | none | Commit identity (needed unless the repo config has `user.*`) |
|
|
| `GIT_PUSH` | `true` | `false` = commit only |
|
|
| `GIT_PUSH_USER`, `GIT_PUSH_TOKEN` | none | HTTPS push credentials |
|
|
| `DEPLOY_URL`, `DEPLOY_TRIGGER_SECRET` | none | Live-site redeploy hook; skipped when unset |
|
|
| `BLOG_MANAGER_PORT`, `BLOG_MANAGER_UID`, `BLOG_MANAGER_GID`, `PORTFOLIO_DIR` | `3051`, `1000`, `1000`, `../portfolio` | Compose only |
|
|
|
|
## Notes
|
|
|
|
- Categories come from the posts themselves. You create one by typing it into a post, and it disappears when no post uses it. Renaming a category to an existing one merges the two.
|
|
- Renaming a post's slug moves its image folder and rewrites the references to it.
|
|
- Every saved post goes live on the next publish; there are no drafts.
|
|
- `lib/types.ts` mirrors `BlogPost` in `portfolio/data/content.ts`. Keep the two in sync.
|