aboutsummaryrefslogtreecommitdiffstats
path: root/AGENTS.md
diff options
context:
space:
mode:
Diffstat (limited to 'AGENTS.md')
-rw-r--r--AGENTS.md28
1 files changed, 22 insertions, 6 deletions
diff --git a/AGENTS.md b/AGENTS.md
index 18bc043..5e94b3d 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -103,7 +103,8 @@ internal/web/ server.go (host router, middleware, render helpers)
is a single segment on purpose (a `/static/` prefix pattern conflicts
with `/{page}/{post}`); static files must stay flat. The public
`GET /tag/{tag}` is a literal first segment, so it beats `/{page}/{post}`
- on every host (`tag` is reserved too). A `{page}` wildcard never matches
+ on every host (`tag` is reserved too), and so is `GET /search` (`search`
+ reserved). A `{page}` wildcard never matches
a trailing slash, so listings put `?p=N` straight after `base` (`/`,
`/news`, `/tag/go`) — `/news/?p=2` would be a 404.
- **Auth**: login lives only at `/webadmin` on the root domain (deliberately
@@ -155,9 +156,11 @@ internal/web/ server.go (host router, middleware, render helpers)
`handlers_layout.go`, `/b/{sub}/layout…`): a blog page is six areas —
`header`, `left`, `above`, `below` (the two custom-HTML slots inside the
main column, one module at most), `right`, `footer` — each an ordered
- list of modules (`kind`: title, logo, menu, archive, recent, tags,
- tagcloud, html, rss, text, sitemap; `moduleKinds` says which kinds an area
- accepts). Modules have an optional `title` (heading), `body` (raw HTML or
+ list of modules (`kind`: title, logo, menu, search, archive, recent, tags,
+ tagcloud, html, rss, text, sitemap — the list is also a CHECK constraint
+ on `modules.kind`, so a new kind needs a migration; `moduleKinds` says
+ which kinds an area accepts; `moduleHasSettings` takes the module because a header search box
+ has nothing to edit). Modules have an optional `title` (heading), `body` (raw HTML or
footer text) and `count` (recent posts; tags to list, 0 = all). `blogView`
builds a `Layout` (`buildLayout`, areas that are switched off are dropped)
and only fetches the archive index / recent posts / tag counts when a
@@ -291,6 +294,19 @@ internal/web/ server.go (host router, middleware, render helpers)
which also holds the article loop + pager shared by `blog/page.html`
and `blog/tag.html`); the two modules are `tags` (by use, `count`) and
`tagcloud` (alphabetical, `cloudSizes` → `tc-1…tc-5`).
+- **Search** (`web/search.go`, `handleBlogSearch`, `GET /search?q=&p=`,
+ `blog/search.html`): the `search` module is a plain GET form (header bar or
+ side box, `searchform` in `layouts/blog.html`; the results page shows one
+ too). `searchPattern` turns the query into the regex both Postgres and Go
+ run: words `regexp.QuoteMeta`'d and joined with `.*`, so the reader's words
+ are literal, case does not matter (`~*` / `(?is)`) and spaces mean
+ "anything in between", in order. `SearchPublishedPosts` matches it against
+ `title || '\n' || body_md` of published posts from every page, 20 per page
+ (`searchPerPage`); queries are cut at 100 runes. Results are title, date
+ and `searchSnippet` (the Markdown around the first match, escaped, the
+ match in `<mark>`; the body's start when only the title matched). A blank
+ or unmatched query is a normal 200, not a 404; the pager is the page's
+ own (`&p=N` after `?q=`), not the `postlist` one.
- **Slugs**: auto-generated from the title; on collision generated slugs
get `-2`, `-3`…, user-typed slugs return a 409 with a message.
`slug.Clean` is `Make` without the "untitled" fallback, for inputs that
@@ -322,7 +338,7 @@ superadmin password to `admin`. Production refuses both.
`internal/db/migrations/control/` for users and the registry; goose
`-- +goose Up/Down` sections; they run automatically at startup. Never
edit an applied migration. Blog chain so far: `00001_init`, `00002_language`,
- `00003_files`, `00004_tags`; control: `00001_init`, `00002_upload_limit`. Both chains were re-baselined at 00001 after
+ `00003_files`, `00004_tags`, `00005_search`; control: `00001_init`, `00002_upload_limit`. Both chains were re-baselined at 00001 after
the move to per-blog databases; deployments from before it have
`goose_db_version` rows 2–7 in the control DB that must be deleted once
(README "Upgrading from a single database") or the next control migration
@@ -338,5 +354,5 @@ superadmin password to `admin`. Production refuses both.
## Things deliberately not built (ask before adding)
Drafts/preview, custom CSS, comments, multiple blogs per user, email,
-user self-registration, custom domains per blog, image resizing, search,
+user self-registration, custom domains per blog, image resizing,
sanitising the custom HTML module (owner's decision, see above).