Files
novaconium/App/config.php
T
code 37b6764431 Add content index: keywords, tags/categories, search, and XML sitemap
Ships Blog tags/categories, Internal search, and XML sitemap together as
one shared mechanism, plus a new meta keywords request, rather than three
separate ones - the "worth deciding together" call ISSUES.md made when
XML sitemap was first written.

Content stays in files. Per-page metadata is four Twig blocks in the
root layout, the same override mechanism already used for
title/description/og_* - keywords (rendered), tags/changefreq/priority
(not rendered, harvested only). App\ContentIndexer crawls every routable
page (Overlay::listPageDirs(), new) and pulls each block via
Renderer::renderForIndex() (new) calling Twig's own renderBlock() API,
not regex-parsing .twig source, so overrides and layout inheritance
resolve exactly like a real render. Rendered HTML is stripped and
indexed into a SQLite FTS5 table for search.

Off by default (content_index_enabled) - same posture as Matomo/admin
auth, since this is a real SQLite dependency plenty of sites won't want.
Verified true zero footprint when disabled: no data/novaconium.sqlite
gets created, all three consumer routes 404 like they don't exist.
content_index_auto (default true) reindexes lazily on first stale touch
of a consumer route, never on a normal page view; both that path and the
explicit `php novaconium/bin/index-content.php` share one reindex().

Two real bugs caught by testing, not review: a reentrancy bug where
/search's own sidecar calling ensureFresh() during the crawl triggered a
nested reindex() mid-transaction (fixed with a static re-entrancy guard),
and a wrong PDO constant in the search sidecar. Also fixed two unrelated
pre-existing bugs found while building this: an unescaped {{ }} in
admin/docs/sidecars that fataled Twig on any real render of that page,
and a stale "no database layer yet" claim there and in Lib\Input's
docblock, left over from before Lib\Db shipped.

Lib\Db's migrations_dir now accepts an ordered list of roots, not just
one path, so the content index's schema could ship as a framework
migration (novaconium/migrations/) without colliding with project
migrations in App/migrations/ - the two-root extension point flagged in
AGENTS.md when SQLite groundwork shipped. Migrations are tracked by path
relative to the repo root rather than bare filename so two roots with a
same-named file can't shadow each other.

New consumer routes: novaconium/pages/sitemap.xml/ and
novaconium/pages/search/ (framework defaults), App/pages/blog/tag/[tag]/
(project-owned, since blog/ is project content - the existing hand
-written post array in App/pages/blog/index.php is untouched). Added
tags to the 4 existing blog posts as a real demonstration.

Closes the Blog tags/categories, Internal search, and XML sitemap
backlog items in novaconium/ISSUES.md.
2026-07-14 17:35:30 +00:00

55 lines
2.3 KiB
PHP

<?php
// Override any subset of novaconium/config.php's defaults here — only list
// the keys you want to change. novaconium/bootstrap.php (and
// novaconium/bin/clear-cache.php) shallow-merge this over the framework
// defaults; see /admin/docs/config. Uncomment and adjust any of the
// examples below, or leave this file returning an empty array to keep
// every framework default as-is.
return [
// 'debug' => false,
// 'site_name' => 'My Site',
// Docs: /admin/docs/matomo
// 'matomo_url' => 'https://matomo.example.com/',
// 'matomo_site_id' => '1',
// Docs: /admin/docs/admin-auth — generate a hash with:
// php -r "echo password_hash('yourpassword', PASSWORD_DEFAULT), PHP_EOL;"
// or use the built-in /admin/password-hash form.
// 'admin_username' => 'admin',
// 'admin_password_hash' => '$2y$10$...',
// Docs: /admin/docs/database — adds (or overrides) named Lib\Db
// connections. This merges into db_connections by name rather than
// replacing the whole map, so adding 'legacy' here doesn't require
// repeating 'default' — see Lib\Db::config().
// 'db_connections' => [
// 'legacy' => [
// 'driver' => 'mysql',
// 'host' => 'localhost',
// 'port' => 3306,
// 'database' => 'legacy_app',
// 'username' => 'root',
// 'password' => '...',
// 'charset' => 'utf8mb4', // optional, defaults to utf8mb4
// 'migrations_dir' => __DIR__ . '/migrations/legacy', // optional
// ],
// ],
// Docs: /admin/docs/drafts — requires admin_password_hash above to be
// set to actually gate anything; open access otherwise, same as the
// rest of /admin/*.
// 'draft_routes' => ['blog/upcoming-post'],
// Docs: /admin/docs/content-index — powers /sitemap.xml, /search, and
// blog tag browsing. Off by default (depends on SQLite); enable with:
// 'content_index_enabled' => true,
//
// Or enable but skip the automatic lazy reindex, relying only on
// `php novaconium/bin/index-content.php` (e.g. from a deploy step):
// 'content_index_enabled' => true,
// 'content_index_auto' => false,
];