Skip to content

chore(release): 3.1.1 - #40

Merged
xiaodoudou merged 235 commits into
mainfrom
dev
Oct 11, 2026
Merged

xiaodoudou merged 235 commits into
mainfrom
dev

Conversation

@xiaodoudou

Copy link
Copy Markdown
Owner

Changed

  • Node 24 is the version Embed CMS requires, and Node 22 no longer is. engines was ^22.12.0. It is now ^24.0.0, so installing on Node 22 prints an engine warning. CI runs every job on Node 24, and the Docker example runs on node:24-bookworm-slim.

Fixed

  • cms-import and cms-import-remote crashed on Node 24 when their input ended right after a wrong answer. Asked again after the wrong answer, the question was put to a closed input, which Node 24 refuses with ERR_USE_AFTER_CLOSE. It now answers no, as it does when the input closes before any answer.

xiaodoudou and others added 30 commits December 13, 2022 16:48
Resources described in JavaScript files, a REST API and an admin app to edit them, records kept in a JSON file store
that replicates between nodes, a JWT login with its login page, a documentation page for the fields, and unit tests
for the API, the queries and replication.

Co-authored-by: Xiaodoudou <edouard.durand@gmail.com>
Co-authored-by: Zegfault <zegfault42@gmail.com>
Co-authored-by: Kong Yim <kongyim@gmail.com>
…r login

The MongoDB store connects with X.509 client certificates over TLS, and its protocol can be overridden. The login page
and its error messages are reworked, and the API routes tidied. request-promise and underscore are gone, along with
other unused libraries.

Co-authored-by: Xiaodoudou <edouard.durand@gmail.com>
Co-authored-by: Zegfault <zegfault42@gmail.com>
Co-authored-by: Kong Yim <kongyim@gmail.com>
- A system log page: colours from the terminal, search, autoscroll that stops when you scroll up, a count of warnings
  and errors, examples until it is set up, and a virtual log on Linux when a path is given.
- Records can be selected together, with Shift+click and Ctrl+A.
- A JWT session ends when the user's password changes; Basic authentication fixes.
- lodash instead of underscore, the logger instead of log4js, and localisation fixes.

Co-authored-by: Xiaodoudou <edouard.durand@gmail.com>
Co-authored-by: Zegfault <zegfault42@gmail.com>
Co-authored-by: Kong Yim <kongyim@gmail.com>
Authentication is dispatched between the login methods, explains itself in the debug log, and is documented. The login
screen waits for the translations.

Co-authored-by: Xiaodoudou <edouard.durand@gmail.com>
Co-authored-by: Zegfault <zegfault42@gmail.com>
A session key option, an async bootstrap, record updates over WebSockets, and Arabic among the locales. Fixes for the
anonymous user, the MongoDB connection options, sessions and replicated attachments; Ctrl+A selects text in a field
again when multiple selection is off.

Co-authored-by: Xiaodoudou <edouard.durand@gmail.com>
Co-authored-by: Zegfault <zegfault42@gmail.com>
Co-authored-by: Kong Yim <kongyim@gmail.com>
The xlsx import and the CMS import are fixed, the MongoDB store updated, and the dependencies brought up to date,
without the ones nothing used.

Co-authored-by: Xiaodoudou <edouard.durand@gmail.com>
Co-authored-by: Zegfault <zegfault42@gmail.com>
Co-authored-by: Kong Yim <kongyim@gmail.com>
Queries sent to the MongoDB store are sanitised; settings and dependencies updated.

Co-authored-by: Xiaodoudou <edouard.durand@gmail.com>
The admin is rebuilt on Vue 3 and Vuetify 3, served and built by Vite:
- a new omnibar, a virtual-scrolled record list, a table view, a theme switch, and reworked fields and paragraphs;
- localised values stored field first ([field].[locale]), a _updatedBy field on every record;
- images processed with sharp instead of imagemin, resized copies cached and removed with their attachment;
- system information and the log page over server-sent events, socket.io removed;
- plugins loaded at run time, Node 22, a documentation page per field type, the cms.json options and resource
  examples, and the unit tests rewritten.

Co-authored-by: Xiaodoudou <edouard.durand@gmail.com>
Co-authored-by: Zegfault <zegfault42@gmail.com>
Co-authored-by: Kong Yim <kongyim@gmail.com>
Smart cropping of images, Material Design icons as SVG, and fixes to image uploads, text field validation, disabled
fields and the paragraph menu; dependency updates.

Co-authored-by: Xiaodoudou <edouard.durand@gmail.com>
Co-authored-by: Zegfault <zegfault42@gmail.com>
Every field can show a hint, records sort by their update time, and notifications show the error. Email and form
validation fixes, databases close cleanly, smart cropping's TensorFlow dependency removed, and sub-paragraph headers
placed right.

Co-authored-by: Xiaodoudou <edouard.durand@gmail.com>
Co-authored-by: Zegfault <zegfault42@gmail.com>
…itch

The log and system pages reconnect on their own, the remote import can use its cache (--use-cache), image and file
fields check their size limit and required state and show the size right, group permissions fixes, and a
disableDarkMode option.

Co-authored-by: Xiaodoudou <edouard.durand@gmail.com>
Co-authored-by: Zegfault <zegfault42@gmail.com>
Code and rich text fields format code properly with highlight.js, paragraphs show their outline in the form, the remote
import script is refactored, rich text validation and textarea scrolling fixed, and login and error handling fixes.
The unit tests are reworked.

Co-authored-by: Xiaodoudou <edouard.durand@gmail.com>
Co-authored-by: Zegfault <zegfault42@gmail.com>
… start

Image fields can store their files on OSS and keep the image metadata, paragraphs with bad data no longer break the
editor, the attachments API is fixed, a PostgreSQL store is started, and dependencies are updated.

Co-authored-by: Xiaodoudou <edouard.durand@gmail.com>
Co-authored-by: Zegfault <zegfault42@gmail.com>
…kend

- Backend unit and security tests (mocha), frontend tests (vitest), coverage, and a GitHub Actions workflow that runs
  them with the lint and the build.
- Dependencies updated; the private logger dependency and the unused packages removed; the project stands on its own,
  without its former owner's services.
- The admin redesigned: a design system with tokens in both themes, a collapsible navigation rail, a record editor
  with unsaved-state tracking, dialogs, toasts and an uploads panel, the table view, consistent field states, the field
  catalogue (one resource per family of field types) and one documentation page per field type.
- The backend audited and hardened: security settings with a legacy and a hardened profile, stronger authentication
  and sessions, HTTP headers, CSRF and body limits, authenticated and validated replication, query safety and
  performance work, and MongoDB and PostgreSQL drivers that pass one contract suite.
…uration, update CI actions

Admin polish, the recommended production configuration, CI on current actions

- The log page reworked, settings menu icons, Ctrl+K on resources and plugins, compact toasts and list rows,
  breadcrumb, dropdowns, JSON editors and rich text in the field style, the open record in the address.
- A record is saved before its files; saving is refused while a required field is empty; uploads without an extension
  keep their type; switches are saved as false.
- The recommended production configuration is documented and tested; the whole repository passes lint and knip.
- CI on Node 24 actions (checkout and setup-node v7) and a pinned runner; the legacy suite cleans up after a transient
  error.
Real components mounted with Vuetify: the record list, select fields, pillbox, colour picker, record editor, the app
(routing, address, unsaved-edits guard, breadcrumb), blocks, the JSON editor and the navigation. Fixed along the way:
a new record opens a blank editor again, a refused request is not logged as a server error, record names and labels
after the mustache 4 update, a record whose id overlaps the machine id can be changed and removed, and remove and
update throw instead of failing silently.
…inst the code

Documentation refactor: one source of truth per topic, checked against the code

A README that goes from nothing to a running CMS, a map of the docs, architecture, concepts and contributing pages, one
configuration reference, the API, the plugins and every field page rewritten against the code, diagrams in Mermaid,
and the bugs found on the way recorded.
…nent

Every bug from the documentation review fixed, and every component tested

- The 39 bugs found while the docs were checked, each with a regression test: among them, the login page and the admin
  agree on the theme, one JSON body limit with a clear 413, the cms command is a plain CMS in a project, installs from
  git come with the admin built, ?locale= stores values field first, and syncing one record checks it.
- Smart cropping uses sharp's attention strategy: no AI model, and every TensorFlow trace removed.
- Component tests for every remaining component, and the bugs they found; FormService and SchemaService tested.
- The remaining lint warnings fixed.
Green timestamps, levels coloured by severity and highlighted values on a terminal, plain text in files and pipes
(stdout and stderr checked separately), NO_COLOR and FORCE_COLOR. The log page shows the colours and reads the level of
a coloured line.
… README

Every protection is on by default and the legacy profile is gone. The bug and audit notes leave the docs now that they
are covered. The README gets a centred header with badges, a screenshot of the default resources, and says the default
store is a key-value store saved to a file.
A delete goes to the replication peers right away again, cms-import prints no empty progress bars into logs, the xlsx
import answers 400 without a file, express-session defaults stated, quiet test runs, eslint 10, and canvas and
rollup-plugin-node-polyfills dropped.
…ngoDB and PostgreSQL

Record stores: LevelDB and SQLite beside the JSON file, MongoDB and PostgreSQL

dbEngine.type can be sqlite or leveldb; LevelDB is the default for new content, and a resource that still has a
db.json keeps it until it is migrated with the tool that moves content between the local engines. A storage engines
page with measurements and a crash test, and the benchmark for all five engines.
The version becomes 3.0.0 and the license GPL-3.0-only. Versions up to 2.6.1 were MIT: the license file keeps their
notice, with its copyright line, as the MIT license requires, so the brand test leaves that one file out.
…l alias

Text fields: autocomplete "nope", so Brave stops offering an email alias on emails and links

The shared mixin set autocomplete back to "off" after Vuetify rendered the field, and browsers ignore "off" for what they take for an
email or a sign-up form. Text fields now get an invalid token, passwords keep "new-password". The email field is a plain text box.
Icon buttons everywhere: move, delete, fold, add, Edit JSON and Object Properties. Selects are a button and a list like the other
dropdowns of the admin, and booleans a checkbox. Nesting is an indentation with a thin rule, not boxes inside boxes: a container is
a header line (fold chevron, name, ghost icon buttons) over its body, and the root object has no box. List items are rows
(position, field, move and delete icons); items that are objects or lists have their own header line; "Add item" is one dashed
button under the rows, and "Delete All" an icon on the header, moved there by a small hook (the library builds it under the rows).
A type chip on mixed items and added properties; the name field of Object properties is emptied after each add. Tables keep their
bordered look with ghost row actions. Works in the dark theme and on a phone; tokens only.

The boolean of the catalogue's Settings field is declared as a checkbox (format: 'checkbox'). The object field page describes this
editor (header lines, icons, Object Properties, lists, added properties) and its five screenshots show it.
Remove the Cms Config page and its routes, and list Syslog in the CMS menu

The page rewrote cms.json from the browser and restarted the server: it let an admin change the secrets and ports, and a mistake could
leave the CMS unable to start. /admin/cms-config is gone, with the helpers only it used. Syslog moves from the System menu to the
CMS group, next to Users, Groups and Settings.

The names of the plugin pages (Syslog, Replicator, Import, Sync) are translation keys now, in the menus, the breadcrumb, the tab and the
quick switcher (Syslog reads 系统日志 in Chinese). Their display name does not change: it is what the `plugins` right of a group names.
…a stalled page

Syslog: a very long log no longer fills the disk, the memory or a stalled page

syslog.maxLineLength cuts a line on the page (10000 characters, the file keeps it whole), syslog.maxFileSize copies a log file past
10 MB to <path>.1 and empties it, syslog.maxClientBuffer drops a page that stopped reading (8 MB). Each is off at 0.
Login page: a softer, more finished look, still following the system theme

An accent glow behind the card, a gradient strip along its top edge, a larger logo, a deeper shadow and a short fade-up when it appears.
…al since boot

node-os-utils has no bandwidth on Windows, so the server keeps the previous byte counters and works out bytes per second between two reports. The card shows the rate (the total is its tooltip, and what shows until a second report gives a rate).
npm audit reported 6 high severity vulnerabilities, all one chain: braces
(a stack exhaustion with deeply nested patterns, advisory GHSA-vfj7-8cjw-p6xm,
for every version up to the latest 3.0.3, with no patched release) reached
through micromatch, fast-glob, globby, del and del-cli. Only dev tools were
affected: npm audit --omit=dev found nothing.

del-cli was used by one script, dev, to remove src/plugins. That is one
line of Node (fs.rmSync with force, which does not mind a missing folder),
so the dependency and its 27 packages are gone, and npm audit finds 0
vulnerabilities, shipped and dev alike. The lockfile loses those 28 packages
and changes no version.
The dev script carried the whole call to fs.rmSync, with its quotes
escaped, inside package.json. It is now scripts/clean-plugins.js, and the
dev script reads: node scripts/clean-plugins.js && cross-env ... vite.

The behaviour is the one of the one-liner: a folder is emptied, a missing
folder is no error, and a symlink (src/plugins can point to the plugins of
the project) is removed without touching what it points to.
…stream-to-array

Both places collected the chunks of a stream with stream-to-array and
joined them with Buffer.concat. Node's stream/consumers does the same in
one call (buffer(stream)), so the dependency goes.
cms-import and cms-import-remote use prompt for one thing: a yes or no
before they write. prompt is a general library that was last released in
2022 and brings an abandoned stack for it: winston 2 (end of life since
2018), colors, eyes, cycle, revalidator and two versions of async, 12
packages in all, in the tools that hold the credentials of a CMS.

lib/util/confirm.js asks the question with Node's readline. Anything but
a whole yes or no asks again, and an input that closes (a pipe that ended,
Ctrl+D) answers no, so nothing is written when nobody answered. The two
commands answer no and exit 1 as before.
… enter

In production the sessions are files in <data>/.sessions.json, and a
session holds the login token of the person in clear text. The folder was
made with the permissions of the umask (usually 0755) and its files were
0644 on a normal system: any other account of the machine, or a container
that shares the volume, could read a token and use it until it expires, 24
hours.

ensurePrivateFolder sets the folder to 0700 at every start, also when an
earlier version made it. The permissions of the folder decide who can reach
the files in it, so the files need no change. Turning on the encryption that
session-file-store offers was tried and left out: a clear-text session
file that is read once the encryption is on throws inside the store's
callback and stops the process.
The folder of the sessions holds the login tokens of the people who are
signed in. A backup has no use for them, and a token in a backup that is
copied off the machine is a login that can be taken, for as long as it
lasts. cms-backup files now skips <data>/.sessions.json and says so; the
rest of the data folder, hidden files included, is copied as before.
Everyone signs in again after a restore.
build(deps): replace del-cli with a script, clearing 6 high audit warnings
build(deps): replace prompt and stream-to-array with Node built-ins
fix(security): keep the session files in a folder only the server can enter
Moves the Unreleased entries of the changelog under 3.0.7, adds its
compare link and bumps the version of package.json.
Knip 6 finds ten default exports in src/utils that nothing imports, as every
caller uses the named exports, so the default objects go. playwright-core is
loaded on demand by the screenshot scripts, so it is listed as an ignored
dependency. Three entries in the ignore list are no longer needed and knip
still exits 0 without them.
chore(knip): clean up what knip 6 reports
The proxy rewrote Host to the backend port while the browser kept its own Origin, so the CSRF check refused every cookie-authenticated write and the updates websocket in development.
The buttons are pushed to the right end of the bar, so the title no longer floats in the middle when the block has no summary text.
A resource's group can be a list of names, ['Content', 'Blog'], to file it in a group inside a group. The sidebar shows the tree, the collapsed rail shows the top group with the groups inside it in its pop-up, and the breadcrumb, the quick switcher and the title dropdown follow. Every group can get an icon in Settings, a group inside another as 'Content / Blog'.

The sidebar is also cheaper to resize: while the pointer moves only a CSS variable changes, and the app re-renders when it is let go. The width stops at what the longest visible name needs.
Lists every key of a file in resources/ with its type, default and what it does, explains nested menu groups, and documents displayItem and extraSources, which were not described anywhere.
feat(admin): nest menu groups to any depth
engines was ^22.12.0, which npm reads as Node 22 only, so Node 24 got an engine warning. It now allows ^22.12.0 || ^24.0.0, and a node-24 job runs the unit, frontend, integration and build steps next to the Node 22 ones. It is a separate job because the rules of main ask for checks named test and drivers.
The Docker example builds and runs on node:24-bookworm-slim. The commit check, the drivers job and the npm publish run on Node 24. The test job stays on Node 22, the oldest supported line, beside the node-24 job.
After a wrong answer, confirm asked the question again with rl.prompt(). When the input ended in the same chunk (printf 'what\n' | cms-import), the interface was already closed, and Node 24 throws ERR_USE_AFTER_CLOSE there where Node 22 let it pass. The question is now asked again only while the input is open, so a closed input still counts as no.
engines is now ^24.0.0, every CI job runs on Node 24, and the separate node-24 job is gone because test is the Node 24 job now. The README, CONTRIBUTING, the create-site skill and the changelog say Node 24.
build: require Node 24 and drop Node 22
@xiaodoudou
xiaodoudou merged commit 036a88b into main Oct 11, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant