diff --git a/content/configuration/files.md b/content/configuration/files.md index 5f064f0c..83e12867 100644 --- a/content/configuration/files.md +++ b/content/configuration/files.md @@ -181,8 +181,9 @@ The output limit applies at every step of a transformation, not only to the fina ## Imports -| Variable | Description | Default Value | -| ------------------------- | ----------------------------------------------------- | --------------- | -| `IMPORT_EXPORT_NAMESPACE` | Redis namespace for storing import/export information | `import-export` | -| `IMPORT_TIMEOUT` | Allowed duration for importing files | `1m` | -| `IMPORT_CONCURRENT_MAX` | Maximum simultainous imports | `10` | +| Variable | Description | Default Value | +| ------------------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| `IMPORT_EXPORT_NAMESPACE` | Redis namespace for storing import/export information. | `directus:import-export` | +| `IMPORT_TIMEOUT` | Allowed duration for importing files. Shared across receiving the upload and parsing it. | `1h` | +| `IMPORT_MAX_CONCURRENCY` | Maximum simultaneous imports. | `20` | +| `IMPORT_MAX_FILE_SIZE` | Maximum size of an uploaded import file. See [Security & Limits](/configuration/security-limits). | `50mb` | diff --git a/content/configuration/security-limits.md b/content/configuration/security-limits.md index 0fd9eee5..4689fb62 100644 --- a/content/configuration/security-limits.md +++ b/content/configuration/security-limits.md @@ -186,6 +186,6 @@ Allows you to configure hard technical limits, to prevent abuse and optimize for | `QUERYSTRING_MAX_PARSE_DEPTH` | The maximum object depth when parsing URL query parameters using the querystring format | `10` | | `QUERYSTRING_ARRAY_LIMIT` | The array limit when parsing URL query parameters using the querystring format | `500` | | `MAX_IMPORT_ERRORS` | The maximum number of validation errors permitted while importing records before the process is cancelled and the errors returned. | `1000` | -| `IMPORT_MAX_FILE_SIZE` | The maximum size of a file uploaded to the import endpoint. Accepts number of bytes, or human readable string; no limit by default. Returns `413` when exceeded. | | +| `IMPORT_MAX_FILE_SIZE` | The maximum size of a file uploaded to the data import, schema diff and schema apply endpoints . Accepts number of bytes, or human readable string. Returns `413` when exceeded. | `50mb` | [1] Defaults to `auth_login`, `auth_refresh`, `auth_password_request`, `auth_password_reset`, `users_register`, `users_register_verify`, `users_invite_accept`, `users_me_tfa_generate`, `users_me_tfa_enable`, `users_me_tfa_disable`, and `utils_cache_clear`. Setting your own list overrides these. diff --git a/content/guides/02.content/2.editor.md b/content/guides/02.content/2.editor.md index 07985108..eacbec39 100644 --- a/content/guides/02.content/2.editor.md +++ b/content/guides/02.content/2.editor.md @@ -26,6 +26,26 @@ settings, the App will automatically open the item page when selecting the colle :: +## Saving Items + +Click :icon{name="material-symbols:check"} in the page header to save your changes. By default this saves the item and returns you to the collection page. + +Click :icon{name="material-symbols:more-vert"} to pick a different action for the current save: + +| Action | Description | +| ----------------------- | -------------------------------------------------------------------------- | +| **Save and Quit** | Saves the item and returns to the collection page. | +| **Save and Stay** | Saves the item and keeps you on the item page. | +| **Save and Create New** | Saves the item and opens an empty item page for the same collection. | +| **Save as Copy** | Saves your changes as a new item, leaving the original untouched. | +| **Discard All Changes** | Reverts your unsaved changes and keeps you on the item page. | + +To change what the save button does across the project, set **Default Save Action** in the settings module. The action you choose becomes the button's behavior, and the remaining actions stay available in the :icon{name="material-symbols:more-vert"} menu. + +::callout{icon="i-lucide-info"} +**Save and Create New** falls back to **Save and Quit** on [singletons](/guides/data-model/collections) and wherever the user's policies do not grant create access on the collection. +:: + ## Duplicating Items ![Item duplicating](/img/3ac21f31-a1e0-4506-a2cc-86a3682d4bf6.webp) diff --git a/content/guides/13.security/1.best-practices.md b/content/guides/13.security/1.best-practices.md index 902d6bcc..ec89eef3 100644 --- a/content/guides/13.security/1.best-practices.md +++ b/content/guides/13.security/1.best-practices.md @@ -25,8 +25,11 @@ Flows run arbitrary operations. They can execute custom scripts, make HTTP reque Updating `directus_settings` changes behavior for every user in the project. Fields on this collection include the project name, theming, authentication policy, and URLs used in outbound email templates. A non-administrator with update access can inject CSS into the Data Studio through the theming group, change links in password-reset emails, or disable password requirements. +Read access matters too. `directus_settings` stores the API keys for your configured AI providers in fields such as `ai_openai_api_key`, so a policy that grants read on the whole collection hands those credentials to every user it applies to. + - Grant update on `directus_settings` to administrators only. - If non-administrators need to edit a specific field, scope field permissions tightly, and pair the policy with a [Content Security Policy](/configuration/security-limits#csp) that restricts inline styles and untrusted sources. +- Scope read on `directus_settings` to the minimum fields your users actually need. Policies created before Directus 12.2.0 grant read on every field. See [Version 12 breaking changes](/releases/breaking-changes/version-12) for the field list applied to new policies. ### Users, Roles & Policies diff --git a/content/releases/2.changelog.md b/content/releases/2.changelog.md index b1a32537..eb995fbd 100644 --- a/content/releases/2.changelog.md +++ b/content/releases/2.changelog.md @@ -12,10 +12,14 @@ Each month, some of the Directus team talk through what’s new including core r ## July 2026 - - -- Directus 12.2.0 has a breaking change: the WYSIWYG (rich text) interface is now built on [Tiptap](https://tiptap.dev) instead of TinyMCE. Existing fields keep working, but stored HTML is normalized to supported markup on first edit and the **Options Override** (`tinymceOverrides`) option is deprecated. See the [Version 12 breaking changes](/releases/breaking-changes/version-12#wysiwyg-editor-rebuilt-on-tiptap). +- [Directus 12.2.0](https://github.com/directus/directus/releases/tag/v12.2.0) has a breaking change: the WYSIWYG (rich text) interface is now built on [Tiptap](https://tiptap.dev) instead of TinyMCE. Existing fields keep working, but stored HTML is normalized to supported markup on first edit and the **Options Override** (`tinymceOverrides`) option is deprecated. See the [Version 12 breaking changes](/releases/breaking-changes/version-12#wysiwyg-editor-rebuilt-on-tiptap). +- 12.2.0 also tightens several defaults. Imports are capped at `50mb`, image transformations are capped at 3000px of output, and new App Access policies read a limited set of `directus_settings` fields instead of all of them. Review the [full list](/releases/breaking-changes/version-12) before upgrading. - Rebuilt the WYSIWYG editor on Tiptap, an actively maintained and license-compatible library, with a redesigned toolbar that collapses overflowing tools into a Show More menu. +- Added a `POST /utils/import` endpoint for importing flat data into multiple collections in a single request. Directus orders the collections by their relationships and remaps primary keys along with the foreign keys that reference them. +- Schema snapshots can now be [scoped to a subset of collections](/tutorials/migration/promoting-changes-between-environments-in-directus), and the diff endpoint takes a `mode` parameter for producing an additive diff that leaves the target's other collections alone. +- Added a [**Default Save Action**](/guides/content/editor#saving-items) project setting, which controls what the save button does when editing an item. +- Added [JSON path filtering](/guides/content/explore#filtering-json-fields) to Studio filters, so you can filter on values inside JSON columns from the filter interface. +- Added [`LICENSE_KEY_MANAGEMENT_ENABLED`](/licensing/overview) for controlling whether license keys can be managed through the Studio. ## March 2026 diff --git a/content/releases/3.breaking-changes/3.version-12.md b/content/releases/3.breaking-changes/3.version-12.md index 25070752..b39adec9 100644 --- a/content/releases/3.breaking-changes/3.version-12.md +++ b/content/releases/3.breaking-changes/3.version-12.md @@ -6,8 +6,6 @@ description: Breaking changes may require action on your part before upgrading. ## Version 12.2.0 - - ### WYSIWYG editor rebuilt on Tiptap The WYSIWYG interface (`input-rich-text-html`) has replaced its TinyMCE editor with [Tiptap](https://tiptap.dev). The interface id, option keys, and HTML storage format are unchanged, so existing fields continue to work without migration. Two changes may require action. @@ -42,6 +40,52 @@ If you style or process stored HTML by tag, class, or attribute, review affected This option passed raw TinyMCE configuration to the editor and no longer has any effect. Existing values are ignored and log a deprecation warning in the browser console, and the option is hidden for new fields. It will be removed in a future release. Configure the toolbar, custom formats, font families, and font sizes through the interface's dedicated options instead. +### App Access policies read a limited set of settings fields + +The minimal permissions attached to a policy with **App Access** enabled previously granted read access to every field on `directus_settings`, including admin-only configuration and AI provider credentials. New policies now grant read access to only the fields a non-admin user needs: + +`id`, `project_url`, `project_logo`, `module_bar`, `storage_asset_transform`, `storage_asset_presets`, `custom_aspect_ratios`, `basemaps`, `mapbox_key`, `visual_editor_urls`, `collaborative_editing_enabled`, `report_error_url`, `ai_openai_compatible_models`, `ai_openai_allowed_models`, `ai_anthropic_allowed_models`, `ai_google_allowed_models`, `mcp_prompts_collection`, `default_save_action`, `ai_translation_default_model`, `ai_translation_glossary`, and `ai_translation_style_guide`. + +Existing policies are not modified, so upgrading does not change what your current users can read. This means any policy created before this release still exposes the full settings collection, including `ai_openai_api_key` and the other provider keys. Audit your App Access policies and scope the `directus_settings` read permission to the fields you need. + +If an extension or frontend of yours reads a settings field that a new policy no longer grants, add that field explicitly to the policy's read permission. + +### Imports are capped at 50mb by default + +`IMPORT_MAX_FILE_SIZE` limits the size of files uploaded to the import endpoints, and of schema snapshots uploaded to `/schema/diff` and `/schema/apply`. It defaults to `50mb`. + +Imports were previously unrestricted, so a file that used to be processed now fails with `413 Content Too Large` once it passes the limit. Raise [`IMPORT_MAX_FILE_SIZE`](/configuration/security-limits) to restore the previous behavior. + +### Image transformation output is capped at 3000px + +`ASSETS_TRANSFORM_IMAGE_MAX_OUTPUT_DIMENSION` limits the pixel dimensions a transformation is allowed to output, and defaults to `3000`. A transformation projecting a width or height above the limit is rejected with an [`ILLEGAL_ASSET_TRANSFORMATION`](/guides/connect/errors) error. + +The limit applies at every step of a transformation rather than only to the final dimensions, so a preset that scales an image up to 10000px before scaling it back down is rejected. Review your storage asset presets and the transformation parameters your frontend requests, then raise [`ASSETS_TRANSFORM_IMAGE_MAX_OUTPUT_DIMENSION`](/configuration/files) if you need larger output. + +### Valueless boolean query flags now count as true + +Boolean query flags are now read through a shared helper that treats a flag carrying no value (`?flag`) as true. Two endpoints change behavior as a result: + +- `POST /utils/import/:collection?background` previously evaluated to false, so the import ran synchronously. It now runs in the background. +- `POST /schema/apply?force` previously evaluated to false, so version and database vendor checks still applied. It now bypasses them. + +Pass an explicit value (`?background=false`, `?force=false`) wherever you relied on the previous behavior. + +`POST /schema/diff` changes in the opposite direction. It previously treated the mere presence of `force` as true, so even `?force=false` bypassed the checks. It now respects the value you pass. + +### SDK `schemaDiff` takes an options object + +`schemaDiff` accepted `force` as a positional second argument. It now takes an options object, so the new `mode` parameter can be passed alongside it. + +```js +schemaDiff(snapshot, true); // [!code --] +schemaDiff(snapshot, { force: true }); // [!code ++] +``` + +### `DeploymentProjectsService.readByExternalId` takes a deployment ID + +External IDs are only unique within a provider, so this method now takes the deployment ID as its first argument: `readByExternalId(deploymentId, externalId)`. Update any extension that calls it directly. + ## Version 12.1.0 ### Removal of /hash endpoints