diff options
| -rw-r--r-- | CLAUDE.md | 72 | ||||
| -rw-r--r-- | Dockerfile | 29 |
2 files changed, 101 insertions, 0 deletions
diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..c2aae8b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,72 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Commands + +```bash +# Build and run +go build ./cmd/iblog +./iblog serve [--port 8080] [--root .] + +# Hot reload during development (uses air) +air # serves on :8081 per .air.toml + +# Run all tests +go test ./... + +# Run a single test +go test ./internal/db/... -run TestPostSearch + +# User management +./iblog user add <name> +./iblog user list +./iblog user passwd <name> +./iblog user delete <name> + +# Build frontend JS (from assets/lib/ or assets/admin/lib/) +cd assets/lib && bun run build +cd assets/admin/lib && bun run build +``` + +## Architecture + +iblog is a Go blog engine with an admin UI. It uses a hybrid model: posts are authored via a browser-based editor, stored in SQLite, and then rendered to static HTML files in `public/` for serving. + +**Data flow:** Admin UI (EditorJS) → SQLite (`data/iblog.db`) → static HTML (`public/`) → served to readers + +**Key packages:** + +- `cmd/iblog/` — CLI entry point with `serve` and `user` subcommands +- `internal/` (package `auth`) — htpasswd-compatible bcrypt password file; nginx can use the same file via `auth_basic_user_file` +- `internal/db/` — SQLite wrapper; schema includes `posts`, `pages`, `redirects`, `settings`, and an FTS5 virtual table (`pages_fts`) for full-text search +- `internal/server/` — Gin HTTP handlers: `adminserver.go` (admin CRUD, auth middleware, setup flow), `frontpage.go`, `posthandler.go`, `fileserver.go` +- `internal/builder/` — Converts EditorJS block JSON to HTML (`editorjs.go`) +- `internal/media/` — Image upload, processing (govips/libvips), and serving + +**Frontend assets** are embedded into the binary via `embed.go`: +- `assets/lib/` — Shared site JS (thumbhash, built with bun → `dist/`) +- `assets/admin/lib/` — Admin editor JS (EditorJS + plugins, built with bun → `dist/`) +- `assets/components/` — Custom web components served to the public site +- `assets/styles/` — CSS +- `templates/` — Go `html/template` files; `templates/admin/` for admin UI + +**Directory layout at runtime** (controlled by `--root`): +``` +data/iblog.db SQLite database +data/.passwords htpasswd file (bcrypt, 0600) +data/media/ uploaded media originals + processed images +public/ generated static HTML output +components/ optional user-supplied web components +styles/ optional user-supplied CSS overrides +``` + +**Setup flow:** On first run (no password file), all requests redirect to `/setup` where the first admin user is created. After that, `/admin/` routes require HTTP Basic Auth. + +**Static generation:** Saving a post in the admin UI writes HTML to `public/<slug>/index.html` and updates the FTS5 index. The `public/` directory is served as a fallback for any route not matched by dynamic handlers. + +## Notes + +- `govips` wraps libvips; libvips must be installed on the host (`brew install vips` on macOS). +- The JS bundles in `assets/*/lib/dist/` are committed — rebuild them with bun when changing JS dependencies. +- Posts store content as raw EditorJS JSON (`blocks` column) and render to HTML on publish, not on read. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..005b856 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +FROM golang:1.24-alpine AS builder + +RUN apk add --no-cache \ + gcc \ + musl-dev \ + vips-dev \ + pkgconfig + +WORKDIR /src +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . +RUN CGO_ENABLED=1 go build -o /iblog ./cmd/iblog + + +FROM alpine:3.21 + +RUN apk add --no-cache \ + vips \ + sqlite-libs \ + ca-certificates + +COPY --from=builder /iblog /usr/local/bin/iblog + +VOLUME /data +EXPOSE 8080 + +ENTRYPOINT ["iblog", "serve", "--root", "/data"] |
