b882c304b1
Admin login & user management (novaconium/ISSUES.md): session-based
login against a SQLite users table replaces the single-user HTTP Basic
Auth stopgap (admin_username/admin_password_hash and /admin/password-hash
are gone; one admin_auth_enabled flag, off by default with zero DB
footprint). New /admin/login, /admin/logout (POST-only, real page), and
/admin/users pages plus bin/create-admin-user.php.
First user created is the admin; everyone after is registered with a
unique normalized email and an optional group. /admin/* and drafts are
admin-only; Lib\Access gates page content from sidecars
(Access::require('group:members')) with login-redirect/404 responses —
public by default, static pages always public by construction. User
management covers disable/enable, delete, promote/demote, group, email,
and password, with last-active-admin lockout guards.
Also: Session::regenerate() against fixation, friendly missing-PDO-driver
errors in Lib\Db, docs at /admin/docs/access-control and updates across
admin-auth/drafts/sidecars/config/libraries and README/AGENTS.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
76 lines
6.9 KiB
Twig
76 lines
6.9 KiB
Twig
{% extends 'admin/docs/_layout/layout.twig' %}
|
|
|
|
{% block title %}Configuration{% endblock %}
|
|
|
|
{% block description %}How to override framework settings without editing novaconium/config.php.{% endblock %}
|
|
|
|
{% block robots %}noindex, nofollow{% endblock %}
|
|
|
|
{% block docs_content %}
|
|
<h1>Configuration</h1>
|
|
|
|
<p><code>novaconium/config.php</code> holds the framework defaults — <code>pages_dirs</code>, <code>cache_dir</code>, <code>debug</code>, <code>site_name</code>, <code>matomo_url</code>, <code>matomo_site_id</code>, <code>admin_auth_enabled</code>, <code>db_connections</code>, <code>draft_routes</code> — and is not meant to be edited per-project, same as everything else under <code>novaconium/</code>.</p>
|
|
|
|
<p><code>App/config.php</code> ships with the skeleton as an empty, commented placeholder — uncomment (or add) whichever keys you want to change, returning an array of just those:</p>
|
|
|
|
<pre><code><?php
|
|
// App/config.php
|
|
return [
|
|
'debug' => false,
|
|
];</code></pre>
|
|
|
|
<p><code>novaconium/bootstrap.php</code> (and <code>novaconium/bin/clear-cache.php</code>) check whether <code>App/config.php</code> exists and, if so, shallow-merge it over <code>novaconium/config.php</code>'s defaults with <code>array_merge()</code> — the same App-overrides-novaconium pattern used for pages and <code>Lib\</code> classes elsewhere. You only need to list the keys you're changing; anything you omit keeps the framework default.</p>
|
|
|
|
<p>Deleting <code>App/config.php</code> entirely is just as valid as leaving it in place returning an empty array — either way, every setting falls back to <code>novaconium/config.php</code>'s defaults.</p>
|
|
|
|
<h2>Site name</h2>
|
|
|
|
<p><code>site_name</code> (default <code>'My Site'</code>) is used by the root layout as the default page <code><title></code> (when a page doesn't override the <code>title</code> block), <code>og:site_name</code>, and the footer copyright line:</p>
|
|
|
|
<pre><code><?php
|
|
// App/config.php
|
|
return [
|
|
'site_name' => 'Nick Yeoman',
|
|
];</code></pre>
|
|
|
|
<p>See <a href="/admin/docs/seo">SEO</a> for the full list of overridable meta blocks. Pages that were already statically cached before this changes need <code>php novaconium/bin/clear-cache.php</code> to pick it up.</p>
|
|
|
|
<h2>Admin authentication</h2>
|
|
|
|
<p><code>admin_auth_enabled</code> (default <code>false</code>) gates every <code>/admin/*</code> route behind a session login against the <code>users</code> table — see <a href="/admin/docs/admin-auth">Admin authentication</a> for the full write-up, including roles (the first user is the admin; the rest are registered) and how the first user gets created. The same flag powers <a href="/admin/docs/access-control">Access control</a> (<code>Lib\Access</code>) for member/group-gated content. When left off, <code>/admin/*</code> is open, the login/users routes <code>404</code>, and <code>Access::require()</code> allows everything.</p>
|
|
|
|
<h2>Draft pages</h2>
|
|
|
|
<p><code>draft_routes</code> (default <code>[]</code>) is a list of routes only an authenticated admin can see — everyone else gets a plain <code>404</code>. Requires <code>admin_auth_enabled</code> above (and at least one user) to actually gate anything. See <a href="/admin/docs/drafts">Draft pages</a> for the full write-up, including why a cached draft page would be a security problem and how that's avoided.</p>
|
|
|
|
<h2>For developers: using <code>Cache.php</code> directly</h2>
|
|
|
|
<p><code>novaconium/src/Cache.php</code> is the class behind the <code>cache_dir</code> config key above — a small, dependency-free wrapper around writing/deleting the static HTML files under <code>public/cache/</code> that <a href="/admin/docs/caching">Static caching</a> describes. Like <code>Router</code> (see <code>/admin/docs/routing</code>'s "For developers" section), it's plain and easy to reason about in isolation: no Twig, no request state, just a path convention and some filesystem calls.</p>
|
|
|
|
<h3>How it fits in</h3>
|
|
|
|
<p><code>Cache</code> shows up in three places, all constructed the same way — <code>new Cache($config['cache_dir'])</code>:</p>
|
|
|
|
<ul>
|
|
<li><code>novaconium/bootstrap.php</code> constructs one and hands it to <code>Renderer</code>, which calls <code>$cache->write()</code> after rendering any page that has <strong>no</strong> sidecar (see <code>/admin/docs/sidecars</code>'s <code>Renderer.php</code> section) — <code>Renderer</code> is the only thing that ever writes to the cache.</li>
|
|
<li><code>novaconium/bin/clear-cache.php</code>, a standalone CLI script, constructs one and calls <code>$cache->clear()</code> — this is what <code>php novaconium/bin/clear-cache.php</code> runs.</li>
|
|
<li><code>novaconium/pages/admin/clear-cache/index.php</code>'s sidecar calls <code>$cache->clear()</code> too, but doesn't construct it — <code>$cache</code> is already in scope automatically inside every sidecar, the same instance <code>Renderer</code> is using, injected by <code>Renderer::runSidecar()</code> alongside <code>$params</code>.</li>
|
|
</ul>
|
|
|
|
<h3>Constructing it and its three methods</h3>
|
|
|
|
<pre><code>use App\Cache;
|
|
|
|
$cache = new Cache($config['cache_dir']);
|
|
|
|
$cache->path($requestUri); // string — the .html file a URI maps to, without touching the filesystem
|
|
$cache->write($requestUri, $html); // void — creates parent directories as needed, then writes the file
|
|
$cache->clear(); // void — recursively deletes everything under cache_dir, leaving the directory itself</code></pre>
|
|
|
|
<p>The constructor takes just one thing — <code>cache_dir</code>, a single absolute path (<code>novaconium/config.php</code> sets it to <code>public/cache</code>) — unlike <code>Router</code>/<code>Renderer</code>, which take the whole ordered <code>pagesDirs</code> list. That's because caching isn't part of the App-over-novaconium override mechanism; there's exactly one cache directory, not a searched list of them.</p>
|
|
|
|
<p><code>path()</code> mirrors the public URL tree directly: <code>/blog/hello-world</code> maps to <code>{cache_dir}/blog/hello-world/index.html</code>, matching the <code>.htaccess</code> rule that checks for that exact file before letting any request reach PHP. <code>write()</code> calls <code>path()</code> internally and creates any missing parent directories with <code>mkdir(..., true)</code> before writing. <code>clear()</code> recurses over every entry under <code>cache_dir</code> deleting files and subdirectories, but never deletes <code>cache_dir</code> itself — so a stray <code>public/cache/.gitkeep</code> (see <code>.gitignore</code>) survives a clear.</p>
|
|
|
|
<p>Because every method here is a straightforward filesystem operation with no hidden state beyond the one constructor argument, testing <code>Cache</code> in isolation is just a matter of pointing it at a temporary directory and asserting on what ends up on disk.</p>
|
|
{% endblock %}
|