docs: rebrand README + add Docker and bare-metal deploy
- Rewrite README with RetroBlog branding, config table, and two deploy paths (Docker Compose + bare-metal/systemd). - Enable Next.js standalone output for slim runtime images. - Add multi-stage Dockerfile (builds better-sqlite3 natively, runs as non-root, persists /app/data), docker-compose.yml, and .dockerignore. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
node_modules
|
||||
.next
|
||||
.git
|
||||
.gitignore
|
||||
data
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
.env*
|
||||
README.md
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
docker-compose.yml
|
||||
npm-debug.log*
|
||||
.pnpm-debug.log*
|
||||
.DS_Store
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# ---- deps: install node_modules (native build tools for better-sqlite3) ----
|
||||
FROM node:22-bookworm-slim AS deps
|
||||
WORKDIR /app
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
python3 make g++ \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
RUN corepack enable
|
||||
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# ---- builder: compile the Next.js standalone server ----
|
||||
FROM node:22-bookworm-slim AS builder
|
||||
WORKDIR /app
|
||||
RUN corepack enable
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
RUN pnpm build
|
||||
|
||||
# ---- runner: minimal runtime image ----
|
||||
FROM node:22-bookworm-slim AS runner
|
||||
WORKDIR /app
|
||||
ENV NODE_ENV=production \
|
||||
NEXT_TELEMETRY_DISABLED=1 \
|
||||
PORT=3000 \
|
||||
HOSTNAME=0.0.0.0
|
||||
|
||||
# Non-root user; owns the data volume so SQLite can write.
|
||||
RUN addgroup --system --gid 1001 nodejs \
|
||||
&& adduser --system --uid 1001 nextjs
|
||||
|
||||
# Standalone output bundles the traced node_modules (incl. the better-sqlite3
|
||||
# native binary), so no install/rebuild is needed here.
|
||||
COPY --from=builder /app/.next/standalone ./
|
||||
COPY --from=builder /app/.next/static ./.next/static
|
||||
COPY --from=builder /app/public ./public
|
||||
|
||||
RUN mkdir -p /app/data && chown -R nextjs:nodejs /app/data
|
||||
VOLUME ["/app/data"]
|
||||
USER nextjs
|
||||
|
||||
EXPOSE 3000
|
||||
CMD ["node", "server.js"]
|
||||
@@ -1,50 +1,163 @@
|
||||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||
<div align="center">
|
||||
|
||||
## Getting Started
|
||||
# 🕹️ RetroBlog
|
||||
|
||||
First, run the development server:
|
||||
**A self-hosted blog engine with swappable OS / game-console skins.**
|
||||
|
||||
Write Markdown posts, flip between retro themes (Windows, PS1, PS2, PS3, Wii,
|
||||
NDS, Dreamcast, JV2002…), and run the whole thing from a single SQLite file.
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Markdown posts** — create / edit / delete with slugs, tags, excerpts, dates.
|
||||
- **Swappable skins** — pick the default theme; optionally let visitors switch.
|
||||
- **Admin panel** — single-password login, signed session cookie.
|
||||
- **Import / export** — JSON backup of settings and posts.
|
||||
- **Zero external services** — data lives in one SQLite file (`data/blog.db`).
|
||||
|
||||
Stack: Next.js 16 (App Router) · React 19 · better-sqlite3 · marked.
|
||||
|
||||
---
|
||||
|
||||
## Quick start (development)
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm install
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
Open <http://localhost:3000>. Admin panel at <http://localhost:3000/admin>
|
||||
(default password `admin` in dev).
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
All config is environment variables. Copy `.env.example` to `.env` and edit.
|
||||
|
||||
| Variable | Required | Default | Purpose |
|
||||
| ---------------------- | -------- | ---------------- | --------------------------------------------------------- |
|
||||
| `ADMIN_PASSWORD` | **prod** | `admin` | Password for `/admin`. **Set this in production.** |
|
||||
| `ADMIN_SESSION_SECRET` | **prod** | `ADMIN_PASSWORD` | Signs the admin session cookie. Use a long random string. |
|
||||
| `PORT` | no | `3000` | Port the server listens on. |
|
||||
|
||||
Generate a secret:
|
||||
|
||||
```bash
|
||||
openssl rand -hex 32
|
||||
```
|
||||
|
||||
**Data** is stored in `./data/blog.db` (created automatically). Back up that
|
||||
directory to back up the whole site.
|
||||
|
||||
---
|
||||
|
||||
## Deploy — Docker (recommended)
|
||||
|
||||
Needs Docker + the Compose plugin.
|
||||
|
||||
```bash
|
||||
# 1. Set production secrets
|
||||
cp .env.example .env
|
||||
$EDITOR .env # set ADMIN_PASSWORD and ADMIN_SESSION_SECRET
|
||||
|
||||
# 2. Build and run
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
Site is live on <http://localhost:3000>. The database persists in the
|
||||
`retroblog-data` Docker volume across rebuilds.
|
||||
|
||||
Common operations:
|
||||
|
||||
```bash
|
||||
docker compose logs -f # tail logs
|
||||
docker compose down # stop (keeps the data volume)
|
||||
docker compose up -d --build # update after pulling new code
|
||||
```
|
||||
|
||||
**Back up the database:**
|
||||
|
||||
```bash
|
||||
docker compose exec retroblog sh -c 'cat /app/data/blog.db' > backup-$(date +%F).db
|
||||
```
|
||||
|
||||
> Put a TLS-terminating reverse proxy (Caddy, nginx, Traefik) in front for
|
||||
> HTTPS in production. RetroBlog speaks plain HTTP on `PORT`.
|
||||
|
||||
---
|
||||
|
||||
## Deploy — bare metal
|
||||
|
||||
Needs Node.js 22+ and `pnpm` (`corepack enable`). A C toolchain
|
||||
(`build-essential`, `python3`) is required once, to compile better-sqlite3.
|
||||
|
||||
```bash
|
||||
# 1. Install deps and build
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm build
|
||||
|
||||
# 2. Set production secrets
|
||||
export ADMIN_PASSWORD='your-strong-password'
|
||||
export ADMIN_SESSION_SECRET="$(openssl rand -hex 32)"
|
||||
|
||||
# 3. Start
|
||||
pnpm start # serves on PORT (default 3000)
|
||||
```
|
||||
|
||||
### Run it as a service (systemd)
|
||||
|
||||
`/etc/systemd/system/retroblog.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=RetroBlog
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/retroblog
|
||||
ExecStart=/usr/bin/pnpm start
|
||||
Restart=on-failure
|
||||
Environment=NODE_ENV=production
|
||||
Environment=PORT=3000
|
||||
Environment=ADMIN_PASSWORD=your-strong-password
|
||||
Environment=ADMIN_SESSION_SECRET=your-long-random-secret
|
||||
User=www-data
|
||||
Group=www-data
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now retroblog
|
||||
```
|
||||
|
||||
The SQLite database lives in `WorkingDirectory/data/blog.db` — make sure the
|
||||
service `User` can write there, and back up that file.
|
||||
|
||||
---
|
||||
|
||||
## Admin panel
|
||||
|
||||
Visit [http://localhost:3000/admin](http://localhost:3000/admin) and sign in with
|
||||
`ADMIN_PASSWORD` (see `.env.example`; defaults to `admin` in dev). From there you can:
|
||||
Sign in at `/admin` with `ADMIN_PASSWORD`. From there:
|
||||
|
||||
- **Posts** — full create / edit / delete with Markdown bodies, slugs, tags, and dates.
|
||||
- **Settings** — branding (title, subtitle, footer, version), the default theme for
|
||||
- **Posts** — full create / edit / delete with Markdown bodies, slugs, tags, dates.
|
||||
- **Settings** — branding (title, subtitle, footer, version), default theme for
|
||||
new visitors, whether the public theme switcher is shown, and which skins it offers.
|
||||
- **Import / export** — download a JSON backup of settings and/or posts, and import
|
||||
one back (posts can replace or append).
|
||||
- **Import / export** — download a JSON backup of settings and/or posts, and
|
||||
import one back (posts can replace or append).
|
||||
|
||||
Auth is a single password kept in `ADMIN_PASSWORD`, with a signed session cookie
|
||||
(`ADMIN_SESSION_SECRET`). All `/admin/*` routes are gated by middleware.
|
||||
All `/admin/*` routes are gated by middleware behind the signed session cookie.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||
---
|
||||
|
||||
## Learn More
|
||||
## License
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
See repository.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
services:
|
||||
retroblog:
|
||||
build: .
|
||||
image: retroblog:latest
|
||||
container_name: retroblog
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
# CHANGE THESE before exposing to the internet.
|
||||
ADMIN_PASSWORD: ${ADMIN_PASSWORD:-admin}
|
||||
ADMIN_SESSION_SECRET: ${ADMIN_SESSION_SECRET:-change-me-to-a-long-random-string}
|
||||
volumes:
|
||||
# Persist the SQLite database across container rebuilds.
|
||||
- retroblog-data:/app/data
|
||||
|
||||
volumes:
|
||||
retroblog-data:
|
||||
+3
-1
@@ -1,7 +1,9 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
// Emit a self-contained server in .next/standalone for slim Docker images
|
||||
// and simple bare-metal deploys.
|
||||
output: "standalone",
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
Reference in New Issue
Block a user