v0.1.8
Changed
-
Database migrations no longer ship a separate
.mariadb.sqlfile. The runner
resolves MariaDB toNNNN_name.mariadb.sql, then the MySQL file, then the bare
.sql(migrationFileCandidatesinrun-migrations.ts) — the DDL for the two
has been byte-for-byte identical in every tracked migration. The redundant
0013–0016.mariadb.sqlfiles are removed (MariaDB now reads the identical
.mysql.sql);0012_baselinekeeps its three-file set. Existing installs are
unaffected: those migrations are already recorded in_migrationsand never
re-read, and a fresh MariaDB install applies the same statements as before. A
future migration adds a MariaDB-specific file only if the DDL must diverge. -
Per-theme customization documents (Customizer mods, homepage design, blog
design, plus their draft copies) move out ofsite_settingsinto a dedicated
theme_designstable — one row per (site, theme, kind) with adoc/
draft_docpair, the same shape astemplate_parts.site_settingsis for
site-level preferences, not theme/plugin configuration (plugins already use
plugin_data). Migration0015_theme_designsadds the table; a one-time
application backfill on boot (theme-designs-migrate.ts) copies the legacy
theme_mods.* / theme_home.* / theme_blog.*(and*_draft.*) rows over and
deletes them, draft-only customizations included. No API or UI change.
Added
-
Themes gain a WordPress-style template hierarchy. A theme ships one JSON
block document per slot undertemplates/(index,front-page,single,
single-<type>,page,page-<slug>,singular,archive,404, …) and
shared chrome underparts/; the renderer resolves a request to an ordered
candidate list (template-hierarchy.ts) and uses the first that exists, so a
theme now owns page structure without touching the core EJS. New context
blocks —core.post-title,core.post-content,core.post-meta,
core.post-excerpt,core.featured-image,core.template-part— render the
current request's content inside a template. Theme builder → Templates
edits any template visually; edits are stored per-site in the new
theme_templatestable (migration0023_templates, draft/publish/reset like
template_parts) and a per-slug override still yields to a more specific theme
file. The bundled Default theme shipsindex/front-page/single/
page.demo/home.json,demo/blog.json, anddemo/footer.jsonstay as
back-compat fallbacks for thefront-page/home/footerslots. New CLI:
justflows theme templatesandjustflows theme scaffold <slug>; new SDK
exports:TEMPLATE_SLOTS,TemplateDocSchema,ThemeTemplatesManifestSchema. -
Admin → Emails adds a versioned system-email design and template editor with
global branding, typed variables, locale variants, draft/publish workflow,
desktop/mobile/plain-text previews, sanitized test sending, safe built-in
render fallbacks, and core account, password, two-factor, security, and
administrative templates. Published template/version identity follows each
delivery into the existing privacy-masked mail diagnostics, while security
templates remain mandatory. Access is gated by a dedicated
email-templates:read/email-templates:managecapability pair, separate
frommail:read/mail:manage, so administrators can grant template editing
per user or per custom role without exposing the mail delivery log.
(#63) -
Admin → Content → Trash adds recoverable, site-scoped deletion for every
built-in and custom content type, media, comments, and menus. Restore keeps
revisions and relationships intact and reports slug collisions explicitly;
configurable per-site retention drives a daily purge job, while manual purge
and empty-trash actions are administrator-only. Referenced media requires a
confirmation before permanent deletion, and trash, restore, and purge actions
are audited. (#108) -
Admin → Settings → Outgoing mail adds separate From, Reply-To, and envelope
sender identities, provider transport registration, full test-transport
responses, privacy-masked delivery/dead-letter logs, manual retry,
per-type suppression, delivery limits, and SPF/DKIM/DMARC guidance. Transport
secrets and retry payloads remain encrypted at rest;mail:readand
mail:managecapabilities control operational access. (#104) -
Admin → Users now supports site-local custom roles with a capability editor,
safe built-in defaults, assignment guards, audit events, SDK hooks, and
capability-first enforcement across the user and content APIs.
(#22) -
Per-user capability grants and explicit denies can be layered on a role,
with server-enforced content-type, locale, site, and ownership scopes plus a
human-readable effective-access preview. Policy changes revoke existing
cookies and cannot be applied to the acting administrator's own account.
(#53) -
Account Security lists database-backed device sessions, marks the current
device, revokes one session or all other sessions, and makes ordinary logout
end only the current device. This ships the session-control slice of the
larger identity roadmap; OIDC/OAuth, SAML, and administrator MFA policy still
remain before that roadmap item is complete.
(#54) -
SDK: access-policy contracts (
AccessPolicy,AccessScope, effective
capability/scope helpers), access-change hooks, resolved capability and
scope fields on authenticated plugin HTTP sessions, and a runtime capability
registry (ctx.capabilities.register()). Commerce and other extension-owned
capabilities are no longer hard-coded in core; only active plugins contribute
their domains to the role editor and authorization policy.
(#53) -
First-party Cookie Consent plugin (
plugins/consent): a categorized
consent banner and preference center (necessary, preferences, analytics,
marketing) with accept-all / reject-all parity, granular toggles, a keyboard-
and screen-reader-accessible modal that respectsprefers-reduced-motion, and
a re-open trigger. Every visitor-facing string is stored per site language and
the runtime picks the visitor's locale from<html lang>; translating the
banner does not invalidate stored consent. Admin → Extensions → Cookie Consent
also carries a full design panel — layout (bar / floating box / blocking
modal), placement (top, bottom, any corner), theme-inherited or explicit
colours (validated, applied as CSS custom properties), and panel/button radius
and width — with a live preview. It exposes a first-party
window.justflowsConsentAPI that the custom-code injector, Analytics, and
other plugins can query before loading anything non-essential; gates tagged
<script type="text/plain" data-jf-consent="…">snippets and off-site oEmbeds
behind their category with a per-embed unlock; and stores versioned consent
records (policy hash, timestamp, choices, locale, coarse device) that are
exportable as CSV and erasable per record — or turns record logging off
entirely so noplugin_datarows are written and no beacon is sent. Best-effort EU-only display uses the
visitor's timezone — no IP lookup, no third-party dependency, all logic and
storage first-party. A new synchronousanalytics.headfilter lets the plugin
defer the Analytics plugin's Google Tag until analytics consent is granted,
without blocking first paint.
(#113) -
SDK: a site cookie registry. Extensions declare every non-essential cookie
they set throughctx.cookies.declare({ name, category, purpose, … })— one
ofnecessary/preferences/analytics/marketing— and read the full
resolved registry (host cookies plus every active plugin's) with
ctx.cookies.list(). Operators re-classify any cookie by name in
Admin → Extensions → Cookie Consent, stored site-wide
(GET/PUT /api/cookies). The Cookie Consent plugin uses it to disclose
cookies per category in the preference center and to expire a category's
cookies the moment it is withdrawn;window.justflowsConsent.allowed(name)
resolves a single cookie against it.
(#113) -
Admin → System → Diagnostics adds administrator-only runtime, database,
migration, cache, plugin and typed-hook inspection; correlation IDs on every
HTTP response; bounded sanitized error retention; a persistent production
debug-mode warning; and explicitly confirmed, size-limited support bundles
containing exactly the redacted information previewed in the dashboard.
(#57) -
SDK: Published compatibility and deprecation policy for plugins, themes,
and CSS providers; their sharedengines.justflowsmanifest range is enforced
by the installer before a package leaves staging; plugins additionally receive
explicit Justflows, SDK package, and SDK API versions throughctx.runtime;
and CI snapshots the public SDK export surface so an export cannot disappear
without review and the required deprecation cycle. Existing top-level
justflowspackage ranges remain supported as a deprecated compatibility
alias.
(#20) -
Self-service password reset. A "Forgot password?" link on the sign-in and
registration screens emails a single-use, time-limited link
(JF_PASSWORD_RESET_TTL_MINUTES, default 60) that lets an administrator or a
user set a new password without shell access or a database edit. Tokens are
stored only as SHA-256 hashes, bound to one account, and invalidated on use,
on any password change, and on expiry; the request response is identical
whether or not the address exists, and both the request and the redemption are
rate limited per address and per IP. A completed reset revokes every session
but establishes none, so a second factor (#54) still applies at the next
sign-in. Administrators can disable the flow or restrict it to chosen roles
under Admin → Settings, and every request, completion and failure is written
to the audit log. When outgoing mail is not configured, the
justflows user reset-password --email you@example.comCLI command is the
documented fallback; withNODE_ENV=developmentthe reset link is also printed
to the server console. Newpassword_resetstable (migration
0017_password_resets).
(#93) -
Admin Home shows a dismissible "Welcome to JustFlows" discovery panel to
administrators: curated cards linking to the documentation, Marketplace and
roadmap onjustflows.com, the JustFlows Discord, and the in-app Updates page.
The cards are static and bundled — no remote feed, no injected markup or
scripts, no tracking — so Admin Home renders and works identically with no
network. Each administrator can minimize or
dismiss the panel and bring it back; the choice is stored per user (new
user_preferencestable, migration0016_user_preferences, and
GET/PUT /api/preferences) and mirrored tolocalStoragefor an instant,
offline-safe first paint. (#52) -
Admin → Security → Admin URL can move the administration entry path away
from/admin, with reserved-path validation, a reachability check and
automatic rollback, configurable 404/redirect behavior for the old path,
and aJF_ADMIN_PATH_RECOVERYenvironment override for proxy/cache recovery.
Sign-in and registration now follow the configured path:POST /api/auth/login
and/registerreturn aredirectTo, so the pre-session/loginpage no
longer sends an authenticated non-subscriber to a stale/admin(which, with
the default "not found" behavior for the old path, was a 404).
(#51) -
Each form built under Extensions → Forms has its own "Require a CAPTCHA on this
form" switch in the builder. When on, the form reuses the provider and keys
already configured under Settings → Discussion (Turnstile, hCaptcha, reCAPTCHA
v2, or reCAPTCHA v3) — no second key to enter. The widget renders in that form,
/justflows-forms/submitverifies the token server-side (reCAPTCHA v3 checks a
form-specific action and the score threshold) before storing or emailing the
submission, and the honeypot still runs first. The provider/verify/widget code
is now shared between comments and forms (apps/server/src/lib/captcha.ts);
the public CSP already widens whenever a provider is selected, so it covers
both. -
The
core.color-schemeblock gains design variants beyond buttons and icons:
a two/three-button segmented control, a single sun/moon toggle, a switch
control, a compact dropdown, plain text labels, and icon buttons with
tooltips — selectable per block in the page builder with a live preview and an
"Animate the icon change" option that honoursprefers-reduced-motion. Every
variant reuses the existing preference engine in/js/site-chrome.js
(pre-paint apply, explicit choice persisted, live OS tracking while on
System/Auto, CSP-safe) rather than duplicating theme-state logic. The single
toggle carriesdata-jf-theme="toggle"and the dropdown a
data-jf-color-scheme-select<select>; both are driven by the same
delegated listeners. Focus is now always visible on the controls.
(#60) -
core.color-schemeis also customizable per block:size(sm/md/lg),
radius(pill/rounded/square), and overridable icons and labels
(lightIcon/darkIcon/autoIcon,lightLabel/darkLabel/
autoLabel— blank keeps the defaults, author values are HTML-escaped). The
default theme now styles the widget entirely through--jf-color-scheme-*
custom properties (resting / hover / active / focus colors, border, radius,
spacing, font size) with theme-token fallbacks, so a theme or the Theme
Customizer can restyle it without overriding rules.--jf-color-scheme-hover-bg
/-hover-fgdefault to the resting colours (hover shows only the border
highlight) until set. The builder surfaces the five colour hooks in the
block's Theme-styling → All theme variables list, so an author can recolour
the selected/hover state of one instance without writing CSS.
(#60)