@directus/api 32.1.1 → 33.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/chat/constants/system-prompt.d.ts +1 -0
- package/dist/ai/chat/constants/system-prompt.js +51 -0
- package/dist/ai/chat/controllers/chat.post.d.ts +2 -0
- package/dist/ai/chat/controllers/chat.post.js +47 -0
- package/dist/ai/chat/lib/create-ui-stream.d.ts +15 -0
- package/dist/ai/chat/lib/create-ui-stream.js +42 -0
- package/dist/ai/chat/middleware/load-settings.d.ts +2 -0
- package/dist/ai/chat/middleware/load-settings.js +18 -0
- package/dist/ai/chat/models/chat-request.d.ts +34 -0
- package/dist/ai/chat/models/chat-request.js +26 -0
- package/dist/ai/chat/models/providers.d.ts +9 -0
- package/dist/ai/chat/models/providers.js +9 -0
- package/dist/ai/chat/router.d.ts +1 -0
- package/dist/ai/chat/router.js +5 -0
- package/dist/ai/chat/utils/chat-request-tool-to-ai-sdk-tool.d.ts +9 -0
- package/dist/ai/chat/utils/chat-request-tool-to-ai-sdk-tool.js +38 -0
- package/dist/ai/chat/utils/fix-error-tool-calls.d.ts +12 -0
- package/dist/ai/chat/utils/fix-error-tool-calls.js +30 -0
- package/dist/ai/chat/utils/parse-json-schema-7.d.ts +13 -0
- package/dist/ai/chat/utils/parse-json-schema-7.js +75 -0
- package/dist/{mcp → ai/mcp}/server.d.ts +13 -16
- package/dist/{mcp → ai/mcp}/server.js +4 -13
- package/dist/ai/mcp/types.d.ts +15 -0
- package/dist/{mcp/tools/assets.js → ai/tools/assets/index.js} +8 -5
- package/dist/{mcp/tools/collections.js → ai/tools/collections/index.js} +7 -4
- package/dist/{mcp/tools/fields.js → ai/tools/fields/index.js} +12 -9
- package/dist/{mcp/tools/files.js → ai/tools/files/index.js} +11 -5
- package/dist/{mcp/tools/flows.js → ai/tools/flows/index.js} +11 -5
- package/dist/{mcp/tools/folders.js → ai/tools/folders/index.js} +12 -5
- package/dist/ai/tools/index.d.ts +15 -0
- package/dist/ai/tools/index.js +29 -0
- package/dist/{mcp/tools/items.js → ai/tools/items/index.js} +13 -6
- package/dist/{mcp/tools/prompts/items.md → ai/tools/items/prompt.md} +19 -15
- package/dist/{mcp/tools/operations.d.ts → ai/tools/operations/index.d.ts} +46 -0
- package/dist/{mcp/tools/operations.js → ai/tools/operations/index.js} +12 -5
- package/dist/{mcp/tools/relations.js → ai/tools/relations/index.js} +7 -4
- package/dist/{mcp/tools/schema.d.ts → ai/tools/schema/index.d.ts} +1 -1
- package/dist/{mcp/tools/schema.js → ai/tools/schema/index.js} +9 -6
- package/dist/{mcp/tools/system.js → ai/tools/system/index.js} +7 -4
- package/dist/{mcp/tools/trigger-flow.js → ai/tools/trigger-flow/index.js} +8 -5
- package/dist/{mcp → ai/tools}/types.d.ts +1 -17
- package/dist/ai/tools/utils.d.ts +9 -0
- package/dist/ai/tools/utils.js +17 -0
- package/dist/app.js +11 -6
- package/dist/auth/drivers/ldap.js +2 -2
- package/dist/auth/drivers/local.js +1 -1
- package/dist/auth/drivers/oauth2.d.ts +1 -2
- package/dist/auth/drivers/oauth2.js +22 -17
- package/dist/auth/drivers/openid.d.ts +1 -2
- package/dist/auth/drivers/openid.js +18 -13
- package/dist/auth/drivers/saml.js +6 -3
- package/dist/auth/utils/generate-callback-url.d.ts +11 -0
- package/dist/auth/utils/generate-callback-url.js +40 -0
- package/dist/auth/utils/is-login-redirect-allowed.d.ts +7 -0
- package/dist/{utils → auth/utils}/is-login-redirect-allowed.js +12 -9
- package/dist/cache.js +2 -2
- package/dist/cli/commands/bootstrap/index.js +2 -2
- package/dist/cli/commands/database/install.js +1 -1
- package/dist/cli/commands/database/migrate.js +1 -1
- package/dist/cli/commands/init/index.js +2 -2
- package/dist/cli/commands/roles/create.js +4 -4
- package/dist/cli/commands/schema/apply.js +3 -3
- package/dist/cli/commands/schema/snapshot.js +1 -1
- package/dist/cli/utils/create-db-connection.d.ts +1 -1
- package/dist/cli/utils/create-db-connection.js +1 -1
- package/dist/cli/utils/create-env/index.js +1 -1
- package/dist/constants.d.ts +7 -3
- package/dist/constants.js +7 -3
- package/dist/controllers/access.js +1 -1
- package/dist/controllers/assets.js +40 -3
- package/dist/controllers/extensions.js +1 -1
- package/dist/controllers/fields.js +2 -2
- package/dist/controllers/files.js +1 -1
- package/dist/controllers/items.js +1 -1
- package/dist/controllers/mcp.js +1 -1
- package/dist/controllers/not-found.js +1 -1
- package/dist/controllers/relations.js +1 -1
- package/dist/database/errors/dialects/mysql.d.ts +1 -1
- package/dist/database/errors/dialects/postgres.d.ts +1 -1
- package/dist/database/errors/dialects/sqlite.d.ts +1 -1
- package/dist/database/errors/translate.d.ts +1 -1
- package/dist/database/errors/translate.js +1 -1
- package/dist/database/helpers/date/dialects/mssql.js +1 -1
- package/dist/database/helpers/date/dialects/mysql.js +1 -1
- package/dist/database/helpers/date/types.js +1 -1
- package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +1 -0
- package/dist/database/helpers/schema/dialects/cockroachdb.js +24 -1
- package/dist/database/helpers/schema/dialects/mssql.d.ts +1 -1
- package/dist/database/helpers/schema/dialects/mysql.d.ts +2 -1
- package/dist/database/helpers/schema/dialects/mysql.js +16 -3
- package/dist/database/helpers/schema/dialects/postgres.d.ts +1 -1
- package/dist/database/helpers/schema/types.d.ts +13 -0
- package/dist/database/helpers/schema/types.js +24 -0
- package/dist/database/index.js +4 -4
- package/dist/database/migrations/20220429A-add-flows.js +1 -1
- package/dist/database/migrations/20230526A-migrate-translation-strings.js +1 -1
- package/dist/database/migrations/20231009A-update-csv-fields-to-text.js +1 -1
- package/dist/database/migrations/20240204A-marketplace.js +9 -7
- package/dist/database/migrations/20240311A-deprecate-webhooks.d.ts +15 -0
- package/dist/database/migrations/20240311A-deprecate-webhooks.js +1 -1
- package/dist/database/migrations/20240806A-permissions-policies.js +3 -3
- package/dist/database/migrations/20240924A-migrate-legacy-comments.js +1 -1
- package/dist/database/migrations/20251014A-add-project-owner.js +1 -1
- package/dist/database/migrations/20251103A-add-ai-settings.d.ts +3 -0
- package/dist/database/migrations/20251103A-add-ai-settings.js +14 -0
- package/dist/database/migrations/20251224A-remove-webhooks.d.ts +3 -0
- package/dist/database/migrations/20251224A-remove-webhooks.js +19 -0
- package/dist/database/migrations/20260113A-add-revisions-index.d.ts +3 -0
- package/dist/database/migrations/20260113A-add-revisions-index.js +41 -0
- package/dist/database/migrations/run.js +3 -3
- package/dist/database/run-ast/lib/apply-query/filter/get-filter-type.d.ts +2 -2
- package/dist/database/run-ast/lib/apply-query/filter/get-filter-type.js +1 -1
- package/dist/database/run-ast/lib/apply-query/filter/operator.js +1 -1
- package/dist/database/run-ast/lib/apply-query/sort.js +1 -1
- package/dist/database/run-ast/run-ast.js +1 -1
- package/dist/database/run-ast/utils/get-column-pre-processor.js +2 -2
- package/dist/database/run-ast/utils/get-column.js +1 -1
- package/dist/database/seeds/run.js +3 -3
- package/dist/extensions/lib/get-extensions-path.js +1 -1
- package/dist/extensions/lib/get-extensions-settings.js +1 -1
- package/dist/extensions/lib/get-extensions.js +1 -1
- package/dist/extensions/lib/get-shared-deps-mapping.js +3 -3
- package/dist/extensions/lib/installation/manager.js +8 -12
- package/dist/extensions/lib/sandbox/register/route.d.ts +1 -1
- package/dist/extensions/lib/sync/status.d.ts +11 -0
- package/dist/extensions/lib/sync/status.js +34 -0
- package/dist/extensions/lib/sync/sync.d.ts +6 -0
- package/dist/extensions/lib/sync/sync.js +90 -0
- package/dist/extensions/lib/sync/tracker.d.ts +18 -0
- package/dist/extensions/lib/sync/tracker.js +71 -0
- package/dist/extensions/lib/sync/utils.d.ts +24 -0
- package/dist/extensions/lib/sync/utils.js +62 -0
- package/dist/extensions/manager.d.ts +9 -5
- package/dist/extensions/manager.js +36 -19
- package/dist/flows.d.ts +1 -1
- package/dist/logger/index.js +1 -1
- package/dist/logger/logs-stream.d.ts +1 -1
- package/dist/logger/logs-stream.js +1 -1
- package/dist/mailer.js +1 -1
- package/dist/metrics/lib/create-metrics.js +2 -2
- package/dist/middleware/authenticate.js +3 -3
- package/dist/middleware/collection-exists.js +1 -1
- package/dist/middleware/extract-token.js +1 -1
- package/dist/middleware/graphql.js +2 -2
- package/dist/middleware/respond.js +2 -2
- package/dist/middleware/validate-batch.js +1 -1
- package/dist/operations/exec/index.js +2 -1
- package/dist/operations/mail/index.js +1 -1
- package/dist/operations/mail/rate-limiter.js +2 -2
- package/dist/permissions/cache.js +5 -0
- package/dist/permissions/lib/fetch-policies.d.ts +1 -1
- package/dist/permissions/lib/fetch-roles-tree.d.ts +6 -3
- package/dist/permissions/lib/fetch-roles-tree.js +5 -27
- package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.js +1 -1
- package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js +1 -1
- package/dist/permissions/modules/fetch-global-access/fetch-global-access.d.ts +9 -7
- package/dist/permissions/modules/fetch-global-access/fetch-global-access.js +17 -9
- package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.js +2 -2
- package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.d.ts +1 -1
- package/dist/permissions/modules/process-ast/lib/inject-cases.js +1 -1
- package/dist/permissions/modules/process-ast/process-ast.js +1 -1
- package/dist/permissions/modules/process-payload/process-payload.js +1 -1
- package/dist/permissions/modules/validate-access/lib/validate-item-access.d.ts +13 -1
- package/dist/permissions/modules/validate-access/lib/validate-item-access.js +54 -6
- package/dist/permissions/modules/validate-access/validate-access.js +3 -2
- package/dist/permissions/utils/fetch-raw-permissions.d.ts +1 -1
- package/dist/permissions/utils/fetch-share-info.d.ts +1 -1
- package/dist/permissions/utils/fetch-share-info.js +1 -1
- package/dist/permissions/utils/filter-policies-by-ip.js +1 -1
- package/dist/permissions/utils/get-permissions-for-share.js +8 -8
- package/dist/permissions/utils/with-cache.d.ts +8 -6
- package/dist/permissions/utils/with-cache.js +12 -10
- package/dist/rate-limiter.js +1 -1
- package/dist/request/is-denied-ip.js +2 -2
- package/dist/schedules/project.js +1 -1
- package/dist/schedules/telemetry.js +1 -1
- package/dist/schedules/tus.js +1 -1
- package/dist/server.js +4 -4
- package/dist/services/assets/name-deduper.d.ts +7 -0
- package/dist/services/assets/name-deduper.js +23 -0
- package/dist/services/assets.d.ts +17 -3
- package/dist/services/assets.js +133 -13
- package/dist/services/authentication.js +6 -6
- package/dist/services/collections.js +1 -1
- package/dist/services/comments.js +2 -2
- package/dist/services/extensions.d.ts +1 -1
- package/dist/services/extensions.js +4 -0
- package/dist/services/files/utils/get-metadata.d.ts +1 -1
- package/dist/services/files/utils/get-metadata.js +1 -1
- package/dist/services/files.d.ts +1 -1
- package/dist/services/files.js +4 -4
- package/dist/services/folders.d.ts +27 -2
- package/dist/services/folders.js +75 -0
- package/dist/services/graphql/index.d.ts +1 -1
- package/dist/services/graphql/index.js +1 -1
- package/dist/services/graphql/resolvers/mutation.js +1 -1
- package/dist/services/graphql/schema/get-types.d.ts +1 -1
- package/dist/services/graphql/schema/read.js +1 -1
- package/dist/services/graphql/subscription.d.ts +1 -1
- package/dist/services/graphql/types/date.js +1 -1
- package/dist/services/graphql/types/hash.js +1 -1
- package/dist/services/graphql/utils/add-path-to-validation-error.js +1 -1
- package/dist/services/import-export.d.ts +2 -2
- package/dist/services/import-export.js +6 -7
- package/dist/services/index.d.ts +0 -1
- package/dist/services/index.js +0 -1
- package/dist/services/mail/index.js +2 -2
- package/dist/services/mail/rate-limiter.js +2 -2
- package/dist/services/notifications.js +2 -2
- package/dist/services/payload.js +21 -1
- package/dist/services/roles.js +2 -2
- package/dist/services/schema.js +1 -1
- package/dist/services/server.js +12 -4
- package/dist/services/settings.js +2 -2
- package/dist/services/tfa.js +1 -1
- package/dist/services/translations.js +1 -1
- package/dist/services/tus/data-store.d.ts +1 -3
- package/dist/services/tus/data-store.js +2 -5
- package/dist/services/tus/server.js +9 -9
- package/dist/services/users.js +4 -4
- package/dist/services/versions.js +1 -1
- package/dist/telemetry/lib/send-report.d.ts +1 -1
- package/dist/telemetry/lib/send-report.js +1 -1
- package/dist/telemetry/lib/track.js +1 -1
- package/dist/telemetry/utils/get-settings.d.ts +15 -0
- package/dist/telemetry/utils/get-settings.js +13 -1
- package/dist/test-utils/README.md +95 -24
- package/dist/test-utils/cache.d.ts +2 -2
- package/dist/test-utils/cache.js +2 -2
- package/dist/test-utils/knex.js +1 -1
- package/dist/test-utils/{fields-service.d.ts → services/fields-service.d.ts} +1 -1
- package/dist/test-utils/{fields-service.js → services/fields-service.js} +3 -2
- package/dist/test-utils/services/files-service.d.ts +28 -0
- package/dist/test-utils/services/files-service.js +34 -0
- package/dist/test-utils/services/folders-service.d.ts +28 -0
- package/dist/test-utils/services/folders-service.js +33 -0
- package/dist/types/collection.d.ts +1 -1
- package/dist/utils/async-handler.d.ts +1 -1
- package/dist/utils/calculate-field-depth.js +1 -1
- package/dist/utils/compress.js +1 -1
- package/dist/utils/deep-map-response.js +2 -2
- package/dist/utils/encrypt.d.ts +2 -0
- package/dist/utils/encrypt.js +64 -0
- package/dist/utils/get-accountability-for-role.js +2 -2
- package/dist/utils/get-accountability-for-token.js +4 -4
- package/dist/utils/get-cache-key.js +3 -3
- package/dist/utils/get-field-system-rows.js +1 -1
- package/dist/utils/get-ip-from-req.d.ts +1 -1
- package/dist/utils/get-ip-from-req.js +1 -1
- package/dist/utils/get-local-type.js +7 -3
- package/dist/utils/get-service.js +1 -3
- package/dist/utils/get-snapshot-diff.js +1 -1
- package/dist/utils/is-url-allowed.js +1 -1
- package/dist/utils/jwt.js +1 -1
- package/dist/utils/require-text.d.ts +1 -0
- package/dist/utils/require-text.js +4 -0
- package/dist/utils/require-yaml.js +2 -2
- package/dist/utils/sanitize-schema.d.ts +1 -1
- package/dist/utils/should-clear-cache.d.ts +1 -1
- package/dist/utils/should-skip-cache.js +2 -2
- package/dist/utils/validate-diff.js +1 -1
- package/dist/utils/validate-snapshot.js +3 -3
- package/dist/utils/validate-storage.js +2 -2
- package/dist/utils/verify-session-jwt.js +1 -1
- package/dist/utils/versioning/deep-map-with-schema.js +2 -2
- package/dist/websocket/controllers/base.d.ts +2 -2
- package/dist/websocket/controllers/base.js +3 -3
- package/dist/websocket/controllers/graphql.d.ts +1 -1
- package/dist/websocket/controllers/graphql.js +1 -1
- package/dist/websocket/controllers/logs.d.ts +1 -1
- package/dist/websocket/controllers/rest.d.ts +1 -1
- package/dist/websocket/controllers/rest.js +2 -2
- package/dist/websocket/handlers/heartbeat.js +1 -1
- package/dist/websocket/handlers/items.js +2 -2
- package/dist/websocket/handlers/subscribe.js +1 -1
- package/dist/websocket/types.d.ts +1 -1
- package/dist/websocket/utils/wait-for-message.js +1 -1
- package/package.json +34 -28
- package/dist/controllers/webhooks.d.ts +0 -2
- package/dist/controllers/webhooks.js +0 -74
- package/dist/extensions/lib/sync-extensions.d.ts +0 -3
- package/dist/extensions/lib/sync-extensions.js +0 -70
- package/dist/extensions/lib/sync-status.d.ts +0 -10
- package/dist/extensions/lib/sync-status.js +0 -27
- package/dist/mcp/tools/index.d.ts +0 -15
- package/dist/mcp/tools/index.js +0 -29
- package/dist/mcp/tools/prompts/index.d.ts +0 -16
- package/dist/mcp/tools/prompts/index.js +0 -19
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.d.ts +0 -5
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.js +0 -7
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.d.ts +0 -5
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.js +0 -10
- package/dist/permissions/modules/fetch-global-access/types.d.ts +0 -4
- package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.d.ts +0 -4
- package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.js +0 -27
- package/dist/services/webhooks.d.ts +0 -14
- package/dist/services/webhooks.js +0 -32
- package/dist/utils/get-date-formatted.d.ts +0 -1
- package/dist/utils/get-date-formatted.js +0 -10
- package/dist/utils/ip-in-networks.d.ts +0 -6
- package/dist/utils/ip-in-networks.js +0 -13
- package/dist/utils/is-login-redirect-allowed.d.ts +0 -4
- /package/dist/{mcp → ai/mcp}/index.d.ts +0 -0
- /package/dist/{mcp → ai/mcp}/index.js +0 -0
- /package/dist/{mcp → ai/mcp}/transport.d.ts +0 -0
- /package/dist/{mcp → ai/mcp}/transport.js +0 -0
- /package/dist/{mcp → ai/mcp}/types.js +0 -0
- /package/dist/{mcp/tools/assets.d.ts → ai/tools/assets/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/assets.md → ai/tools/assets/prompt.md} +0 -0
- /package/dist/{mcp/tools/collections.d.ts → ai/tools/collections/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/collections.md → ai/tools/collections/prompt.md} +0 -0
- /package/dist/{mcp/define.d.ts → ai/tools/define-tool.d.ts} +0 -0
- /package/dist/{mcp/define.js → ai/tools/define-tool.js} +0 -0
- /package/dist/{mcp/tools/fields.d.ts → ai/tools/fields/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/fields.md → ai/tools/fields/prompt.md} +0 -0
- /package/dist/{mcp/tools/files.d.ts → ai/tools/files/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/files.md → ai/tools/files/prompt.md} +0 -0
- /package/dist/{mcp/tools/flows.d.ts → ai/tools/flows/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/flows.md → ai/tools/flows/prompt.md} +0 -0
- /package/dist/{mcp/tools/folders.d.ts → ai/tools/folders/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/folders.md → ai/tools/folders/prompt.md} +0 -0
- /package/dist/{mcp/tools/items.d.ts → ai/tools/items/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/operations.md → ai/tools/operations/prompt.md} +0 -0
- /package/dist/{mcp/tools/relations.d.ts → ai/tools/relations/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/relations.md → ai/tools/relations/prompt.md} +0 -0
- /package/dist/{mcp/tools/prompts/schema.md → ai/tools/schema/prompt.md} +0 -0
- /package/dist/{mcp → ai/tools}/schema.d.ts +0 -0
- /package/dist/{mcp → ai/tools}/schema.js +0 -0
- /package/dist/{mcp/tools/system.d.ts → ai/tools/system/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/system-prompt-description.md → ai/tools/system/prompt-description.md} +0 -0
- /package/dist/{mcp/tools/prompts/system-prompt.md → ai/tools/system/prompt.md} +0 -0
- /package/dist/{mcp/tools/trigger-flow.d.ts → ai/tools/trigger-flow/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/trigger-flow.md → ai/tools/trigger-flow/prompt.md} +0 -0
- /package/dist/{permissions/modules/fetch-global-access → ai/tools}/types.js +0 -0
- /package/dist/test-utils/{items-service.d.ts → services/items-service.d.ts} +0 -0
- /package/dist/test-utils/{items-service.js → services/items-service.js} +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
1
2
|
import { processChunk, toBoolean } from '@directus/utils';
|
|
2
3
|
import { omit } from 'lodash-es';
|
|
3
|
-
import { randomUUID } from 'node:crypto';
|
|
4
4
|
import { useLogger } from '../../logger/index.js';
|
|
5
5
|
import { fetchPermissions } from '../../permissions/lib/fetch-permissions.js';
|
|
6
6
|
import { fetchPolicies } from '../../permissions/lib/fetch-policies.js';
|
|
7
7
|
import { fetchRolesTree } from '../../permissions/lib/fetch-roles-tree.js';
|
|
8
|
+
import { mergePermissions } from '../../permissions/utils/merge-permissions.js';
|
|
8
9
|
import { getSchema } from '../../utils/get-schema.js';
|
|
9
10
|
import { getSchemaInspector } from '../index.js';
|
|
10
|
-
import { mergePermissions } from '../../permissions/utils/merge-permissions.js';
|
|
11
11
|
async function fetchRoleAccess(roles, context) {
|
|
12
12
|
const roleAccess = {
|
|
13
13
|
admin_access: false,
|
|
@@ -184,7 +184,7 @@ export async function down(knex) {
|
|
|
184
184
|
// role permissions to be inserted once all processing is completed
|
|
185
185
|
const rolePermissions = [];
|
|
186
186
|
for (const role of roles) {
|
|
187
|
-
const roleTree = await fetchRolesTree(role.id, knex);
|
|
187
|
+
const roleTree = await fetchRolesTree(role.id, { knex });
|
|
188
188
|
let roleAccess = null;
|
|
189
189
|
if (role.id !== null) {
|
|
190
190
|
roleAccess = await fetchRoleAccess(roleTree, context);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import { toBoolean } from '@directus/utils';
|
|
3
|
+
import { email } from 'zod';
|
|
3
4
|
import { SettingsService } from '../../services/settings.js';
|
|
4
5
|
import { getSchema } from '../../utils/get-schema.js';
|
|
5
|
-
import { email } from 'zod';
|
|
6
6
|
export async function up(knex) {
|
|
7
7
|
await knex.schema.alterTable('directus_settings', (table) => {
|
|
8
8
|
table.string('project_owner');
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export async function up(knex) {
|
|
2
|
+
await knex.schema.alterTable('directus_settings', (table) => {
|
|
3
|
+
table.text('ai_openai_api_key');
|
|
4
|
+
table.text('ai_anthropic_api_key');
|
|
5
|
+
table.text('ai_system_prompt');
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
export async function down(knex) {
|
|
9
|
+
await knex.schema.alterTable('directus_settings', (table) => {
|
|
10
|
+
table.dropColumn('ai_openai_api_key');
|
|
11
|
+
table.dropColumn('ai_anthropic_api_key');
|
|
12
|
+
table.dropColumn('ai_system_prompt');
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export async function up(knex) {
|
|
2
|
+
await knex.schema.dropTable('directus_webhooks');
|
|
3
|
+
}
|
|
4
|
+
export async function down(knex) {
|
|
5
|
+
await knex.schema.createTable('directus_webhooks', (table) => {
|
|
6
|
+
table.increments('id');
|
|
7
|
+
table.string('name', 255).notNullable();
|
|
8
|
+
table.string('method', 10).notNullable().defaultTo('POST');
|
|
9
|
+
table.text('url').notNullable();
|
|
10
|
+
table.string('status', 10).notNullable().defaultTo('active');
|
|
11
|
+
table.boolean('data').notNullable().defaultTo(true);
|
|
12
|
+
table.string('actions', 100).notNullable();
|
|
13
|
+
table.text('collections').notNullable();
|
|
14
|
+
table.json('headers');
|
|
15
|
+
table.boolean('was_active_before_deprecation').notNullable().defaultTo(false);
|
|
16
|
+
table.uuid('migrated_flow');
|
|
17
|
+
table.foreign('migrated_flow').references('directus_flows.id').onDelete('SET NULL');
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { FieldsService } from '../../services/fields.js';
|
|
2
|
+
import { getSchema } from '../../utils/get-schema.js';
|
|
3
|
+
import { transaction } from '../../utils/transaction.js';
|
|
4
|
+
import { getHelpers } from '../helpers/index.js';
|
|
5
|
+
import { getDatabaseClient } from '../index.js';
|
|
6
|
+
const RETENTION_INDEXES = [
|
|
7
|
+
// MySQL and MariaDB are ignored because they already have an index on revisions.activity
|
|
8
|
+
{ collection: 'directus_revisions', field: 'activity', ignore: ['mysql', 'mariadb'] },
|
|
9
|
+
];
|
|
10
|
+
export async function up(knex) {
|
|
11
|
+
const client = getDatabaseClient(knex);
|
|
12
|
+
const helpers = getHelpers(knex);
|
|
13
|
+
const schema = await getSchema();
|
|
14
|
+
const service = new FieldsService({ knex, schema });
|
|
15
|
+
for (const { collection, field, ignore } of RETENTION_INDEXES) {
|
|
16
|
+
if (ignore.includes(client))
|
|
17
|
+
continue;
|
|
18
|
+
const existingColumn = await service.columnInfo(collection, field);
|
|
19
|
+
if (!existingColumn.is_indexed) {
|
|
20
|
+
await helpers.schema.createIndex(collection, field, { attemptConcurrentIndex: true });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export async function down(knex) {
|
|
25
|
+
const client = getDatabaseClient(knex);
|
|
26
|
+
const helpers = getHelpers(knex);
|
|
27
|
+
const schema = await getSchema();
|
|
28
|
+
const service = new FieldsService({ knex, schema });
|
|
29
|
+
for (const { collection, field, ignore } of RETENTION_INDEXES) {
|
|
30
|
+
if (ignore.includes(client))
|
|
31
|
+
continue;
|
|
32
|
+
const existingColumn = await service.columnInfo(collection, field);
|
|
33
|
+
if (existingColumn.is_indexed) {
|
|
34
|
+
await transaction(knex, async (trx) => {
|
|
35
|
+
await trx.schema.alterTable(collection, async (table) => {
|
|
36
|
+
table.dropIndex([field], helpers.schema.generateIndexName('index', collection, field));
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { dirname } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import path from 'path';
|
|
1
4
|
import { useEnv } from '@directus/env';
|
|
2
5
|
import formatTitle from '@directus/format-title';
|
|
3
6
|
import fse from 'fs-extra';
|
|
4
7
|
import { orderBy } from 'lodash-es';
|
|
5
|
-
import { dirname } from 'node:path';
|
|
6
|
-
import { fileURLToPath } from 'node:url';
|
|
7
|
-
import path from 'path';
|
|
8
8
|
import { flushCaches } from '../../cache.js';
|
|
9
9
|
import { useLogger } from '../../logger/index.js';
|
|
10
10
|
import getModuleDefault from '../../utils/get-module-default.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { FieldOverview } from '@directus/types';
|
|
2
2
|
export declare function getFilterType(fields: Record<string, FieldOverview>, key: string, collection?: string): {
|
|
3
|
-
type: "string" | "boolean" | "binary" | "
|
|
3
|
+
type: "string" | "boolean" | "binary" | "text" | "unknown" | "integer" | "date" | "float" | "alias" | "uuid" | "time" | "dateTime" | "timestamp" | "bigInteger" | "decimal" | "json" | "hash" | "csv" | "geometry" | "geometry.Point" | "geometry.LineString" | "geometry.Polygon" | "geometry.MultiPoint" | "geometry.MultiLineString" | "geometry.MultiPolygon";
|
|
4
4
|
special?: never;
|
|
5
5
|
} | {
|
|
6
|
-
type: "string" | "boolean" | "binary" | "
|
|
6
|
+
type: "string" | "boolean" | "binary" | "text" | "unknown" | "integer" | "date" | "float" | "alias" | "uuid" | "time" | "dateTime" | "timestamp" | "bigInteger" | "decimal" | "json" | "hash" | "csv" | "geometry" | "geometry.Point" | "geometry.LineString" | "geometry.Polygon" | "geometry.MultiPoint" | "geometry.MultiLineString" | "geometry.MultiPolygon";
|
|
7
7
|
special: string[];
|
|
8
8
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { parseFilterKey } from '../../../../../utils/parse-filter-key.js';
|
|
2
1
|
import { InvalidQueryError } from '@directus/errors';
|
|
3
2
|
import { getFunctionsForType, getOutputTypeForFunction } from '@directus/utils';
|
|
3
|
+
import { parseFilterKey } from '../../../../../utils/parse-filter-key.js';
|
|
4
4
|
export function getFilterType(fields, key, collection = 'unknown') {
|
|
5
5
|
const { fieldName, functionName } = parseFilterKey(key);
|
|
6
6
|
const field = fields[fieldName];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { getColumn } from '../../../utils/get-column.js';
|
|
2
1
|
import { getOutputTypeForFunction } from '@directus/utils';
|
|
3
2
|
import { getHelpers } from '../../../../helpers/index.js';
|
|
3
|
+
import { getColumn } from '../../../utils/get-column.js';
|
|
4
4
|
export function applyOperator(knex, dbQuery, schema, key, operator, compareValue, logical = 'and', originalCollectionName) {
|
|
5
5
|
const helpers = getHelpers(knex);
|
|
6
6
|
const [table, column] = key.split('.');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getColumnPath } from '../../../../utils/get-column-path.js';
|
|
2
|
-
import { getColumn } from '../../utils/get-column.js';
|
|
3
2
|
import { getRelationInfo } from '../../../../utils/get-relation-info.js';
|
|
3
|
+
import { getColumn } from '../../utils/get-column.js';
|
|
4
4
|
import { addJoin } from './add-join.js';
|
|
5
5
|
export function applySort(knex, schema, rootQuery, sort, aggregate, collection, aliasMap, returnRecords = false) {
|
|
6
6
|
const relations = schema.relations;
|
|
@@ -48,7 +48,7 @@ export async function runAst(originalAST, schema, accountability, options) {
|
|
|
48
48
|
if (!rawItems)
|
|
49
49
|
return null;
|
|
50
50
|
// Run the items through the special transforms
|
|
51
|
-
const payloadService = new PayloadService(collection, { knex, schema });
|
|
51
|
+
const payloadService = new PayloadService(collection, { accountability, knex, schema });
|
|
52
52
|
let items = await payloadService.processValues('read', rawItems, query.alias ?? {}, query.aggregate ?? {});
|
|
53
53
|
if (!items || (Array.isArray(items) && items.length === 0))
|
|
54
54
|
return items;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { joinFilterWithCases } from '../lib/apply-query/join-filter-with-cases.js';
|
|
2
|
-
import { getColumn } from './get-column.js';
|
|
3
1
|
import { parseFilterKey } from '../../../utils/parse-filter-key.js';
|
|
4
2
|
import { getHelpers } from '../../helpers/index.js';
|
|
3
|
+
import { joinFilterWithCases } from '../lib/apply-query/join-filter-with-cases.js';
|
|
5
4
|
import { applyCaseWhen } from './apply-case-when.js';
|
|
5
|
+
import { getColumn } from './get-column.js';
|
|
6
6
|
import { getNodeAlias } from './get-field-alias.js';
|
|
7
7
|
export function getColumnPreprocessor(knex, schema, table, cases, permissions, aliasMap, permissionsOnly) {
|
|
8
8
|
const helpers = getHelpers(knex);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { REGEX_BETWEEN_PARENS } from '@directus/constants';
|
|
2
|
+
import { InvalidQueryError } from '@directus/errors';
|
|
2
3
|
import { getFunctionsForType } from '@directus/utils';
|
|
3
4
|
import { getFunctions } from '../../helpers/index.js';
|
|
4
|
-
import { InvalidQueryError } from '@directus/errors';
|
|
5
5
|
import { applyFunctionToColumnName } from './apply-function-to-column-name.js';
|
|
6
6
|
/**
|
|
7
7
|
* Return column prefixed by table. If column includes functions (like `year(date_created)`), the
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import fse from 'fs-extra';
|
|
2
|
-
import yaml from 'js-yaml';
|
|
3
|
-
import { isObject } from 'lodash-es';
|
|
4
1
|
import { dirname } from 'node:path';
|
|
5
2
|
import { fileURLToPath } from 'node:url';
|
|
6
3
|
import path from 'path';
|
|
4
|
+
import fse from 'fs-extra';
|
|
5
|
+
import yaml from 'js-yaml';
|
|
6
|
+
import { isObject } from 'lodash-es';
|
|
7
7
|
import { getHelpers } from '../helpers/index.js';
|
|
8
8
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
export default async function runSeed(database) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { list } from '@directus/extensions-registry';
|
|
2
3
|
import getDatabase from '../../database/index.js';
|
|
3
4
|
import { ExtensionsService } from '../../services/extensions.js';
|
|
4
5
|
import { getSchema } from '../../utils/get-schema.js';
|
|
5
|
-
import { list } from '@directus/extensions-registry';
|
|
6
6
|
/**
|
|
7
7
|
* Loads stored settings for all extensions. Creates empty new rows in extensions tables for
|
|
8
8
|
* extensions that don't have settings yet, and remove any settings for extensions that are no
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
1
2
|
import { useEnv } from '@directus/env';
|
|
2
3
|
import { resolveFsExtensions, resolveModuleExtensions } from '@directus/extensions/node';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
4
|
import { getExtensionsPath } from './get-extensions-path.js';
|
|
5
5
|
export const getExtensions = async () => {
|
|
6
6
|
const env = useEnv();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { useEnv } from '@directus/env';
|
|
2
|
-
import { resolvePackage } from '@directus/utils/node';
|
|
3
|
-
import { escapeRegExp } from 'lodash-es';
|
|
4
1
|
import { readdir } from 'node:fs/promises';
|
|
5
2
|
import { dirname } from 'node:path';
|
|
6
3
|
import { fileURLToPath } from 'node:url';
|
|
7
4
|
import path from 'path';
|
|
5
|
+
import { useEnv } from '@directus/env';
|
|
6
|
+
import { resolvePackage } from '@directus/utils/node';
|
|
7
|
+
import { escapeRegExp } from 'lodash-es';
|
|
8
8
|
import { useLogger } from '../../logger/index.js';
|
|
9
9
|
import { Url } from '../../utils/url.js';
|
|
10
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import { mkdir, readFile, rm } from 'node:fs/promises';
|
|
2
|
+
import { Readable } from 'node:stream';
|
|
3
|
+
import { join } from 'path';
|
|
1
4
|
import { useEnv } from '@directus/env';
|
|
2
5
|
import { ErrorCode, isDirectusError, ServiceUnavailableError } from '@directus/errors';
|
|
3
6
|
import { EXTENSION_PKG_KEY, ExtensionManifest } from '@directus/extensions';
|
|
4
7
|
import { download } from '@directus/extensions-registry';
|
|
5
8
|
import DriverLocal from '@directus/storage-driver-local';
|
|
6
9
|
import { move, remove } from 'fs-extra';
|
|
7
|
-
import { mkdir, readFile, rm } from 'node:fs/promises';
|
|
8
|
-
import { Readable } from 'node:stream';
|
|
9
10
|
import Queue from 'p-queue';
|
|
10
|
-
import { join } from 'path';
|
|
11
11
|
import { extract } from 'tar';
|
|
12
12
|
import { useLogger } from '../../../logger/index.js';
|
|
13
13
|
import { getStorage } from '../../../storage/index.js';
|
|
@@ -63,11 +63,9 @@ export class InstallationManager {
|
|
|
63
63
|
}
|
|
64
64
|
await queue.onIdle();
|
|
65
65
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
await move(join(tempDir, extractedPath), dest, { overwrite: true });
|
|
70
|
-
}
|
|
66
|
+
// move to regular local extensions folder
|
|
67
|
+
const dest = join(this.extensionPath, '.registry', versionId);
|
|
68
|
+
await move(join(tempDir, extractedPath), dest, { overwrite: true });
|
|
71
69
|
}
|
|
72
70
|
catch (err) {
|
|
73
71
|
logger.warn(err);
|
|
@@ -92,9 +90,7 @@ export class InstallationManager {
|
|
|
92
90
|
}
|
|
93
91
|
await queue.onIdle();
|
|
94
92
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
await remove(path);
|
|
98
|
-
}
|
|
93
|
+
const path = join(this.extensionPath, '.registry', folder);
|
|
94
|
+
await remove(path);
|
|
99
95
|
}
|
|
100
96
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import type { IncomingHttpHeaders } from 'node:http';
|
|
1
2
|
import type { Router } from 'express';
|
|
2
3
|
import type { Reference } from 'isolated-vm';
|
|
3
|
-
import type { IncomingHttpHeaders } from 'node:http';
|
|
4
4
|
export declare function registerRouteGenerator(endpointName: string, endpointRouter: Router): {
|
|
5
5
|
register: (path: Reference<string>, method: Reference<"GET" | "POST" | "PUT" | "PATCH" | "DELETE">, cb: Reference<(req: {
|
|
6
6
|
url: string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const SyncStatus: {
|
|
2
|
+
readonly SYNCING: "SYNCING";
|
|
3
|
+
readonly IDLE: "IDLE";
|
|
4
|
+
};
|
|
5
|
+
export type SyncStatus = keyof typeof SyncStatus;
|
|
6
|
+
export declare function getSyncStatus(): Promise<SyncStatus>;
|
|
7
|
+
export declare function setSyncStatus(status: SyncStatus): Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Checks the filesystem lock file if we are currently synchronizing
|
|
10
|
+
*/
|
|
11
|
+
export declare function isSynchronizing(): Promise<boolean>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions to write a `.status` file on the filesystem to indicate active synchronization
|
|
3
|
+
*/
|
|
4
|
+
import { rm, writeFile } from 'node:fs/promises';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { exists } from 'fs-extra';
|
|
7
|
+
import { getExtensionsPath } from '../get-extensions-path.js';
|
|
8
|
+
export const SyncStatus = {
|
|
9
|
+
SYNCING: 'SYNCING',
|
|
10
|
+
IDLE: 'IDLE',
|
|
11
|
+
};
|
|
12
|
+
export async function getSyncStatus() {
|
|
13
|
+
const statusFilePath = join(getExtensionsPath(), '.status');
|
|
14
|
+
if (await exists(statusFilePath)) {
|
|
15
|
+
return SyncStatus.SYNCING;
|
|
16
|
+
}
|
|
17
|
+
return SyncStatus.IDLE;
|
|
18
|
+
}
|
|
19
|
+
export async function setSyncStatus(status) {
|
|
20
|
+
const statusFilePath = join(getExtensionsPath(), '.status');
|
|
21
|
+
if (status === SyncStatus.SYNCING) {
|
|
22
|
+
await writeFile(statusFilePath, '');
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
await rm(statusFilePath);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Checks the filesystem lock file if we are currently synchronizing
|
|
30
|
+
*/
|
|
31
|
+
export async function isSynchronizing() {
|
|
32
|
+
const status = await getSyncStatus();
|
|
33
|
+
return status === SyncStatus.SYNCING;
|
|
34
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createWriteStream } from 'node:fs';
|
|
2
|
+
import { mkdir, rm } from 'node:fs/promises';
|
|
3
|
+
import { dirname, join, relative, resolve, sep } from 'node:path';
|
|
4
|
+
import { pipeline } from 'node:stream/promises';
|
|
5
|
+
import { useEnv } from '@directus/env';
|
|
6
|
+
import { normalizePath } from '@directus/utils';
|
|
7
|
+
import mid from 'node-machine-id';
|
|
8
|
+
import Queue from 'p-queue';
|
|
9
|
+
import { useBus } from '../../../bus/index.js';
|
|
10
|
+
import { useLock } from '../../../lock/index.js';
|
|
11
|
+
import { useLogger } from '../../../logger/index.js';
|
|
12
|
+
import { getStorage } from '../../../storage/index.js';
|
|
13
|
+
import { getExtensionsPath } from '../get-extensions-path.js';
|
|
14
|
+
import { isSynchronizing, setSyncStatus, SyncStatus } from './status.js';
|
|
15
|
+
import { SyncFileTracker } from './tracker.js';
|
|
16
|
+
import { compareFileMetadata, getSyncPaths } from './utils.js';
|
|
17
|
+
export async function syncExtensions(options) {
|
|
18
|
+
if (options?.skipSync === true)
|
|
19
|
+
return;
|
|
20
|
+
const env = useEnv();
|
|
21
|
+
const lock = useLock();
|
|
22
|
+
const messenger = useBus();
|
|
23
|
+
const logger = useLogger();
|
|
24
|
+
if (options?.forceSync !== true && (await isSynchronizing())) {
|
|
25
|
+
logger.debug('Extensions are already being synced to this directory from another process.');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const machineId = await mid.machineId();
|
|
29
|
+
const machineKey = `extensions-sync/${machineId}`;
|
|
30
|
+
const processId = await lock.increment(machineKey);
|
|
31
|
+
if (processId !== 1) {
|
|
32
|
+
logger.debug('Extensions are already being synced to this machine from another process.');
|
|
33
|
+
// Wait until the process that called the lock publishes a message that the syncing is complete
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
messenger.subscribe(machineKey, () => resolve());
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
logger.debug('Syncing extensions from configured storage location...');
|
|
40
|
+
// Ensure that the local extensions cache path exists
|
|
41
|
+
await mkdir(getExtensionsPath(), { recursive: true });
|
|
42
|
+
await setSyncStatus(SyncStatus.SYNCING);
|
|
43
|
+
const { localExtensionsPath, remoteExtensionsPath } = getSyncPaths(options?.partialSync);
|
|
44
|
+
const storage = await getStorage();
|
|
45
|
+
const disk = storage.location(env['EXTENSIONS_LOCATION']);
|
|
46
|
+
// check if we are only removing the local directory
|
|
47
|
+
if (options?.partialSync) {
|
|
48
|
+
const remoteExists = await disk.exists(normalizePath(join(remoteExtensionsPath, 'package.json')));
|
|
49
|
+
if (remoteExists === false) {
|
|
50
|
+
await rm(localExtensionsPath, { recursive: true, force: true });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Make sure we don't overload the file handles
|
|
55
|
+
const queue = new Queue({ concurrency: 1000 });
|
|
56
|
+
// start file tracker
|
|
57
|
+
const fileTracker = new SyncFileTracker();
|
|
58
|
+
const localFileCount = await fileTracker.readLocalFiles(localExtensionsPath);
|
|
59
|
+
const hasLocalFiles = localFileCount > 0;
|
|
60
|
+
for await (const filepath of disk.list(remoteExtensionsPath)) {
|
|
61
|
+
// We want files to be stored in the root of `$TEMP_PATH/extensions`, so gotta remove the
|
|
62
|
+
// extensions path on disk from the start of the file path
|
|
63
|
+
const relativePath = relative(resolve(sep, remoteExtensionsPath), resolve(sep, filepath));
|
|
64
|
+
const destinationPath = join(localExtensionsPath, relativePath);
|
|
65
|
+
await fileTracker.passedFile(relativePath);
|
|
66
|
+
// No need to check metadata when force is enabled
|
|
67
|
+
if (options?.forceSync !== true && hasLocalFiles) {
|
|
68
|
+
const fileUnchanged = await compareFileMetadata(destinationPath, filepath, disk);
|
|
69
|
+
if (fileUnchanged)
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
// Ensure that the directory path exists
|
|
73
|
+
await mkdir(dirname(destinationPath), { recursive: true });
|
|
74
|
+
// write remote file to the local filesystem
|
|
75
|
+
const readStream = await disk.read(filepath);
|
|
76
|
+
const writeStream = createWriteStream(destinationPath);
|
|
77
|
+
queue.add(() => pipeline(readStream, writeStream));
|
|
78
|
+
}
|
|
79
|
+
// wait for the queue to finish
|
|
80
|
+
await queue.onIdle();
|
|
81
|
+
// cleanup dangling local files
|
|
82
|
+
await fileTracker.cleanup(localExtensionsPath);
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
// release various locking mechanisms
|
|
86
|
+
messenger.publish(machineKey, { ready: true });
|
|
87
|
+
await lock.delete(machineKey);
|
|
88
|
+
await setSyncStatus(SyncStatus.IDLE);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare class SyncFileTracker {
|
|
2
|
+
private localFiles;
|
|
3
|
+
private trackedDirs;
|
|
4
|
+
constructor();
|
|
5
|
+
/**
|
|
6
|
+
* Reads all files recusrively in the provided directory
|
|
7
|
+
* @returns the number of files read
|
|
8
|
+
*/
|
|
9
|
+
readLocalFiles(localExtensionsPath: string): Promise<number>;
|
|
10
|
+
/**
|
|
11
|
+
* Removes a file from the locally tracked files
|
|
12
|
+
*/
|
|
13
|
+
passedFile(filePath: string): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Removes left over tracked files that were not processed
|
|
16
|
+
*/
|
|
17
|
+
cleanup(localExtensionsPath: string): Promise<void>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* class to help tracking file status between local and remote
|
|
3
|
+
*/
|
|
4
|
+
import { readdir, rm } from 'node:fs/promises';
|
|
5
|
+
import { dirname, join, relative } from 'node:path';
|
|
6
|
+
import { pathDepth } from './utils.js';
|
|
7
|
+
export class SyncFileTracker {
|
|
8
|
+
localFiles;
|
|
9
|
+
trackedDirs;
|
|
10
|
+
constructor() {
|
|
11
|
+
this.localFiles = new Set();
|
|
12
|
+
this.trackedDirs = new Set();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Reads all files recusrively in the provided directory
|
|
16
|
+
* @returns the number of files read
|
|
17
|
+
*/
|
|
18
|
+
async readLocalFiles(localExtensionsPath) {
|
|
19
|
+
const entries = await readdir(localExtensionsPath, { recursive: true, withFileTypes: true }).catch(() => {
|
|
20
|
+
/* path doesnt exist */
|
|
21
|
+
});
|
|
22
|
+
if (!entries)
|
|
23
|
+
return 0;
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
if (!entry.isFile())
|
|
26
|
+
continue;
|
|
27
|
+
const relativePath = join(relative(localExtensionsPath, entry.parentPath), entry.name);
|
|
28
|
+
this.localFiles.add(relativePath);
|
|
29
|
+
}
|
|
30
|
+
return this.localFiles.size;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Removes a file from the locally tracked files
|
|
34
|
+
*/
|
|
35
|
+
async passedFile(filePath) {
|
|
36
|
+
this.localFiles.delete(filePath);
|
|
37
|
+
let currentDir = dirname(filePath);
|
|
38
|
+
while (currentDir !== '.') {
|
|
39
|
+
if (this.trackedDirs.has(currentDir))
|
|
40
|
+
break;
|
|
41
|
+
this.trackedDirs.add(currentDir);
|
|
42
|
+
currentDir = dirname(currentDir);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Removes left over tracked files that were not processed
|
|
47
|
+
*/
|
|
48
|
+
async cleanup(localExtensionsPath) {
|
|
49
|
+
const removeDirs = new Set();
|
|
50
|
+
for (const removeFile of this.localFiles) {
|
|
51
|
+
if (removeFile === '.status')
|
|
52
|
+
continue;
|
|
53
|
+
let currentDir = dirname(removeFile);
|
|
54
|
+
while (currentDir !== localExtensionsPath && currentDir !== '.') {
|
|
55
|
+
if (this.trackedDirs.has(currentDir))
|
|
56
|
+
break;
|
|
57
|
+
removeDirs.add(currentDir);
|
|
58
|
+
currentDir = dirname(currentDir);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// sort directory by depth so we can remove the highest level directory recursively
|
|
62
|
+
const removeDirsRecursive = Array.from(removeDirs)
|
|
63
|
+
.sort((a, b) => pathDepth(b) - pathDepth(a))
|
|
64
|
+
.filter((d) => !removeDirs.has(dirname(d)));
|
|
65
|
+
for (const dir of removeDirsRecursive) {
|
|
66
|
+
const relativePath = join(localExtensionsPath, dir);
|
|
67
|
+
// removing local folder that does not exist in the remote storage
|
|
68
|
+
await rm(relativePath, { recursive: true, force: true });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Driver } from '@directus/storage';
|
|
2
|
+
/**
|
|
3
|
+
* Returns the directory depth of the provided path
|
|
4
|
+
*/
|
|
5
|
+
export declare function pathDepth(path: string): number;
|
|
6
|
+
/**
|
|
7
|
+
* Reads the size and modified date of a file if it exists
|
|
8
|
+
*/
|
|
9
|
+
export declare function fsStat(path: string): Promise<{
|
|
10
|
+
size: number;
|
|
11
|
+
modified: Date;
|
|
12
|
+
} | null>;
|
|
13
|
+
/**
|
|
14
|
+
* Builds up the local and remote paths to use with syncing
|
|
15
|
+
*/
|
|
16
|
+
export declare function getSyncPaths(partialPath: string | undefined): {
|
|
17
|
+
localExtensionsPath: string;
|
|
18
|
+
remoteExtensionsPath: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Retrieve the stats for local and remote files and check if they are the same
|
|
22
|
+
* Returns false if files are differnt else true
|
|
23
|
+
*/
|
|
24
|
+
export declare function compareFileMetadata(localPath: string, remotePath: string, disk: Driver): Promise<boolean>;
|