github emdash-cms/emdash emdash@0.30.0

11 hours ago

Minor Changes

  • #1890 82827d3 Thanks @swissky! - Adds a Backups page to admin settings: download a complete content backup (all content including drafts and trash, schema, taxonomies, menus, widgets, media metadata, and site settings — never user accounts or secrets) with one click, and optionally enable daily automatic backups to the site's storage bucket with configurable retention. A new backups:manage permission gates the feature to admins.

  • #2122 6bbf93a Thanks @bimsonz! - Expose the host Astro project's trailingSlash config to plugins via ctx.site.trailingSlash, so plugins that build absolute URLs (sitemaps, canonical, hreflang) can match the site's routing policy. Available to in-process and sandboxed plugins alike.

  • #1954 23a740a Thanks @swissky! - Adds a media_upload MCP tool that uploads a file from base64-encoded data or a public URL and registers it in the media library, so agent workflows can create media without dropping to the CLI or raw API. Uploads are deduplicated by content hash and respect the global MIME allowlist and maximum upload size.

  • #2007 6fb52b0 Thanks @khoinguyenpham04! - Adds emdash media repair-usage and the media_usage_repair MCP tool for repairing content media usage indexes by collection or across all collections; automation should inspect the structured repair status because non-complete results can still be successful invocations.

  • #2092 a27a5cc Thanks @khoinguyenpham04! - Adds coverage-aware media usage counts and read-only Used in details to the REST API and core client.

  • #1868 4c57ee2 Thanks @afonsojramos! - Invited users can now accept their invite by signing in with Google or GitHub, instead of only creating a passkey.

  • #2002 e52dea9 Thanks @jcheese1! - Adds explicitly declared, administrator-enabled plugin MCP tools with per-route permissions, plugin-scoped token access, install and update consent, structured output schemas, and invocation auditing.

  • #1985 3f8b778 Thanks @swissky! - Adds a cacheControl option for public plugin routes: successful GET responses carry the configured Cache-Control header, enabling CDN and browser caching for public plugin endpoints. Works for native, standard, and marketplace plugin formats. Private routes and errors keep the private, no-store default.

  • #1893 d4c565e Thanks @swissky! - Adds the auto-generated admin settings form for plugins that declare admin.settingsSchema. A gear icon on the plugin's card in Plugins opens a form generated from the schema (string, number, boolean, select, secret, url, and email fields), persisted to the plugin's KV store under settings: keys. Secret fields are write-only: the admin shows whether a value is set but never returns it. Editing plugin settings requires the plugins:manage permission.

  • #1957 65be947 Thanks @swissky! - Adds defaultTitle and defaultDescription options to getSeoMeta so pages with computed titles or descriptions can pass their own fallbacks while values set in the admin SEO panel still take precedence. Previously such pages had to drop down to getContentSeo and merge panel values by hand.

  • #1961 e649af8 Thanks @swissky! - Adds withEmDashRuntime() to emdash/middleware so request-free handlers (Cloudflare Queue consumers, custom scheduled() handlers) can access the EmDash runtime and invoke plugin routes directly — previously the runtime was only reachable through locals.emdash on an HTTP request.

Patch Changes

  • #1505 fbb04ab Thanks @swissky! - The admin shell now falls back to the Site Icon configured in Settings → General for its favicon, so the EmDash backend is branded like the public site. An explicit build-time admin.favicon still takes precedence, and the default EmDash mark is used when neither is set.

  • #1901 07c2b0f Thanks @scottbuscemi! - Fixes same-page anchor links (e.g. #section) in Portable Text content opening in a new tab. On-page jumps now always stay in the same tab, even when the link's "open in new window" option is set.

  • #2098 460efe6 Thanks @rhaegalll! - Fixes API responses to include the success field documented in the REST API reference: successful responses are now { success: true, data } and error responses { success: false, error }. The existing data and error fields are unchanged, so existing clients keep working.

  • #1992 7b9e558 Thanks @swissky! - Fixes a circular redirect loop when a slug rename is reverted. Renaming a slug back to a previous value now removes the stale redirect that shadowed the restored URL, so the page stays reachable.

  • #2137 aa7ef09 Thanks @khoinguyenpham04! - Fixes the admin boot screen so it respects the saved light or dark theme before the dashboard loads.

  • #1955 c180240 Thanks @logelog! - Fixes emdash plugin bundle and emdash plugin validate on Windows by importing generated plugin artifacts through portable file URLs.

  • #1979 039c5dc Thanks @masonjames! - Fixes browser uploads for custom S3-compatible storage adapters by allowing their runtime upload origin in the admin Content Security Policy.

  • #2037 b66d8a0 Thanks @swissky! - Fixes oversized image renditions on Cloudflare: transforms without an explicit quality were encoded near-losslessly by the Images binding (a 2048px WebP came out ~900 KB instead of ~100 KB). Lossy transforms (WebP/AVIF/JPEG) now default to quality 85, matching Cloudflare's image-resizing default. PNG output keeps no explicit quality — an explicit PNG quality switches the binding to lossy PNG8, which is not a safe default for a lossless format. An explicit ?q= in the request URL still wins for every format.

    Note: image responses are cached with Cache-Control: immutable, max-age=31536000 keyed on the request URL, so previously-served oversized renditions stay cached at the edge/browser until they expire. To benefit immediately after upgrading, purge the Cloudflare cache for your /_image* URLs (or the whole site cache).

  • #2129 fc6cdfb Thanks @dchaudhari7177! - The content API now answers 500 NOT_CONFIGURED instead of 401 UNAUTHORIZED when the EmDash runtime failed to initialize. Previously the permission check ran first, so a server fault (broken DB binding, init failure) was reported as an authentication error even for requests carrying a valid token — and retry logic correctly treating 4xx as non-retryable gave up instead of retrying. The 13 affected content routes now check initialization before permissions, matching the schema routes.

  • #2153 8d6b20b Thanks @swissky! - Stops draft-only saves and autosaves on published entries from advancing updatedAt. On revision-supporting collections, staging or discarding a pending draft leaves live content untouched, so sitemap <lastmod> and JSON-LD dateModified no longer register a modification until Publish actually changes the live entry.

  • #1079 1b1ff42 Thanks @eyupcanakman! - Fixes the admin content list returning a generic CONTENT_LIST_ERROR when a collection's backing table is missing the deleted_at column. It now returns COLLECTION_SCHEMA_MISMATCH naming the affected collection, so the schema problem is visible instead of an opaque failure.

  • #1898 e2c7549 Thanks @swissky! - Fixes plugin storage query() and count() failing on PostgreSQL with "operator does not exist: boolean = integer" whenever a where filter was supplied.

  • #1945 6c96ac8 Thanks @swissky! - Fixes unsaved inline (visual) editor changes being silently lost when navigating away, e.g. via the browser back button. Edits are now flushed with a keepalive request when the page unloads.

  • #1902 7ff08db Thanks @marcusbellamyshaw-cell! - Fixes the login, signup, and invite-accept pages showing the stock EmDash mark and name even when a custom admin.logo/admin.siteName is configured for white-labeling. These pre-authentication pages now render the configured logo and site name (completing the scope approved in #639/PR #705, which wired branding into the sidebar and setup wizard but missed the pages users see before signing in), falling back to the default EmDash mark when no custom branding is configured.

  • #2080 de44730 Thanks @logelog! - Fixes trusted plugin API routes receiving empty site metadata and relative URLs by using the same site context as plugin hooks.

  • #2018 32be60b Thanks @ttmx! - Adds schedulerless cron access, scheduled publication timestamps, and external authentication support to plugin routes.

  • #2152 b22a3d5 Thanks @vedanshujain! - fix(core): compare plugin-storage numeric filters and ordering numerically on Postgres. _plugin_storage.data is a text column, so after the ::jsonb cast (#1898) the extracted value is still text and numeric guards compared lexically — { stock: { gte: 10 } } over 9/10/100 matched 9 (over-count/oversell) and numeric orderBy sorted [10, 100, 9]. Numeric comparisons now use a type-guarded ::numeric cast and ordering uses the jsonb-native value, so results are numeric and parity-correct with SQLite. Non-numeric stored values yield no match instead of erroring.

  • #1980 32f8261 Thanks @swissky! - Fixes plugin storage startsWith queries treating %, _, and \ in the prefix as LIKE wildcards instead of literal characters.

  • #2036 4b7fd08 Thanks @swissky! - Fixes publishing an entry whose staged slug is already used by another entry in the same locale failing with an opaque database error. Publish now returns a validation error naming the conflicting slug and entry, which the admin can show inline, and leaves the live entry untouched.

  • #2082 cf6e4fa Thanks @logelog! - Fixes production build warnings when database adapters do not provide the optional cold-start query coalescing dialect.

  • #2084 d303738 Thanks @logelog! - Avoid production render warnings when Astro's built-in CSP is disabled while preserving JSON-LD script hashes when it is enabled.

  • #1958 6a79b03 Thanks @swissky! - Warns at build time when @astrojs/react is not registered in the Astro config. Without it the admin UI builds successfully but never hydrates, leaving the page stuck on "Loading EmDash..." with no error.

  • #2128 34b3017 Thanks @jcheese1! - Fixes fresh database setup failures for sites with large seed schemas.

  • #2081 6890edd Thanks @logelog! - Fixes sandboxed plugins receiving empty site metadata and relative URLs from ctx.url().

  • #2050 cfdb8f0 Thanks @edrpls! - Fixes admin-account recovery when emdash:setup_complete is set but the users table is empty (a wiped or sanitised database copied between environments). /api/setup/status already resumes the wizard at the admin step for this state, but the admin-step routes rejected it with SETUP_COMPLETE, leaving no UI path to an admin account. Both routes now reject on the flag only when a user also exists; the fully-set-up and admin-exists rejections are unchanged.

  • #1845 ca660c6 Thanks @marcusbellamyshaw-cell! - Fixes OAuth login (Google/GitHub) failing with "Failed to start OAuth flow" on Astro 6+ Cloudflare deployments. The OAuth start and callback routes read provider credentials via Astro.locals.runtime.env, which Astro 6 removed — accessing it now throws instead of returning undefined, so the existing optional-chaining fallback never ran. Both routes now read Cloudflare bindings through a build-time virtual module instead, falling back to import.meta.env on non-Cloudflare adapters exactly as before.

  • #1838 c4a2ffd Thanks @marcusbellamyshaw-cell! - Fixes admin forms (e.g. Create Taxonomy) showing a generic "Invalid request data" error instead of the actual validation problem. Field-level validation messages from the server are now shown to the user.

  • #1962 c24b7d3 Thanks @MA2153! - Fixes taxonomy-filtered collection listings reading the entire collection on SQLite/D1 when the term is selective. Such listings now seek the matching entries directly, cutting D1 rows-read from tens of thousands to the page size.

  • #2085 84173dd Thanks @logelog! - Watch resolved sandbox plugin entry files so rebuilding a plugin invalidates its embedded virtual module during development.

  • #2030 c350e86 Thanks @swissky! - Fixes MCP OAuth discovery advertising http:// URLs on Cloudflare and other proxied deployments. The /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server routes now honor siteUrl on anonymous requests, so MCP clients can attach without setting EMDASH_SITE_URL as an env var.

  • Updated dependencies [82827d3, 2cb8dd3, f961f6e, aa7ef09, 1329cc0, a6b7424, 44e2685, 3176f4f, 1c15097, aa7ef09, aa7ef09, a272e58, 64b2e73, 7ff08db, aa7ef09, aa7ef09, c80cab6, 4c57ee2, e52dea9, 3f8b778, d4c565e, 07c9f21, 2da4a40, b871de2, 9c858b1, 1d7550b, 2da14cc, aa7ef09, aa7ef09, 22b96e5, 8903aef, b5a60c1]:

    • @emdash-cms/admin@0.30.0
    • @emdash-cms/auth@0.30.0
    • @emdash-cms/plugin-types@0.3.0
    • @emdash-cms/registry-client@0.3.4
    • @emdash-cms/gutenberg-to-portable-text@0.30.0

Don't miss a new emdash release

NewReleases is sending notifications on new releases.