diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b6b791dc59..b5f778df2f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,6 +2,7 @@ /docs/*.md @benhaynes +/packages/sdk @joselcvarela /packages/shared @nickrum /packages/extensions-sdk @nickrum /packages/create-directus-extension @nickrum diff --git a/api/package.json b/api/package.json index 2ab9aa3754..bb53ffcd33 100644 --- a/api/package.json +++ b/api/package.json @@ -1,6 +1,6 @@ { "name": "directus", - "version": "9.0.0-rc.92", + "version": "9.0.0-rc.93", "license": "GPL-3.0-only", "homepage": "https://github.com/directus/directus#readme", "description": "Directus is a real-time API and App dashboard for managing SQL database content.", @@ -70,16 +70,16 @@ "example.env" ], "dependencies": { - "@directus/app": "9.0.0-rc.92", - "@directus/drive": "9.0.0-rc.92", - "@directus/drive-azure": "9.0.0-rc.92", - "@directus/drive-gcs": "9.0.0-rc.92", - "@directus/drive-s3": "9.0.0-rc.92", - "@directus/extensions-sdk": "9.0.0-rc.92", - "@directus/format-title": "9.0.0-rc.92", - "@directus/schema": "9.0.0-rc.92", - "@directus/shared": "9.0.0-rc.92", - "@directus/specs": "9.0.0-rc.92", + "@directus/app": "9.0.0-rc.93", + "@directus/drive": "9.0.0-rc.93", + "@directus/drive-azure": "9.0.0-rc.93", + "@directus/drive-gcs": "9.0.0-rc.93", + "@directus/drive-s3": "9.0.0-rc.93", + "@directus/extensions-sdk": "9.0.0-rc.93", + "@directus/format-title": "9.0.0-rc.93", + "@directus/schema": "9.0.0-rc.93", + "@directus/shared": "9.0.0-rc.93", + "@directus/specs": "9.0.0-rc.93", "@godaddy/terminus": "^4.9.0", "@rollup/plugin-alias": "^3.1.2", "@rollup/plugin-virtual": "^2.0.3", @@ -183,7 +183,7 @@ "@types/json2csv": "5.0.3", "@types/jsonwebtoken": "8.5.5", "@types/keyv": "3.1.3", - "@types/lodash": "4.14.172", + "@types/lodash": "4.14.173", "@types/mime-types": "2.1.1", "@types/ms": "0.7.31", "@types/node": "15.12.2", diff --git a/api/src/cache.ts b/api/src/cache.ts index 3371a6bed7..276f54b337 100644 --- a/api/src/cache.ts +++ b/api/src/cache.ts @@ -16,7 +16,7 @@ export function getCache(): { cache: Keyv | null; schemaCache: Keyv | null } { } if (env.CACHE_SCHEMA !== false && schemaCache === null) { - schemaCache = getKeyvInstance(typeof env.CACHE_SCHEMA === 'string' ? ms(env.CACHE_SCHEMA) : undefined); + schemaCache = getKeyvInstance(typeof env.CACHE_SCHEMA === 'string' ? ms(env.CACHE_SCHEMA) : undefined, '_schema'); schemaCache.on('error', (err) => logger.warn(err, `[cache] ${err}`)); } @@ -29,21 +29,25 @@ export async function flushCaches(): Promise { await cache?.clear(); } -function getKeyvInstance(ttl: number | undefined): Keyv { +function getKeyvInstance(ttl: number | undefined, namespaceSuffix?: string): Keyv { switch (env.CACHE_STORE) { case 'redis': - return new Keyv(getConfig('redis', ttl)); + return new Keyv(getConfig('redis', ttl, namespaceSuffix)); case 'memcache': - return new Keyv(getConfig('memcache', ttl)); + return new Keyv(getConfig('memcache', ttl, namespaceSuffix)); case 'memory': default: - return new Keyv(getConfig('memory', ttl)); + return new Keyv(getConfig('memory', ttl, namespaceSuffix)); } } -function getConfig(store: 'memory' | 'redis' | 'memcache' = 'memory', ttl: number | undefined): Options { +function getConfig( + store: 'memory' | 'redis' | 'memcache' = 'memory', + ttl: number | undefined, + namespaceSuffix = '' +): Options { const config: Options = { - namespace: env.CACHE_NAMESPACE, + namespace: `${env.CACHE_NAMESPACE}${namespaceSuffix}`, ttl, }; diff --git a/api/src/database/migrations/20210910A-move-module-setup.ts b/api/src/database/migrations/20210910A-move-module-setup.ts index 774430cbcc..3d8489b882 100644 --- a/api/src/database/migrations/20210910A-move-module-setup.ts +++ b/api/src/database/migrations/20210910A-move-module-setup.ts @@ -6,41 +6,7 @@ export async function up(knex: Knex): Promise { }); await knex.schema.alterTable('directus_settings', (table) => { - table.json('module_bar').defaultTo( - JSON.stringify([ - { - type: 'module', - id: 'collections', - enabled: true, - }, - { - type: 'module', - id: 'users', - enabled: true, - }, - { - type: 'module', - id: 'files', - enabled: true, - }, - { - type: 'module', - id: 'insights', - enabled: false, - }, - { - type: 'module', - id: 'docs', - enabled: true, - }, - { - type: 'module', - id: 'settings', - enabled: true, - locked: true, - }, - ]) - ); + table.json('module_bar'); }); } diff --git a/api/src/services/authentication.ts b/api/src/services/authentication.ts index 5f50c56951..57d703a2f5 100644 --- a/api/src/services/authentication.ts +++ b/api/src/services/authentication.ts @@ -160,10 +160,25 @@ export class AuthenticationService { } } - const payload = { + let payload = { id: user.id, }; + const customClaims = await emitter.emitAsync('auth.jwt.before', payload, { + event: 'auth.jwt.before', + action: 'jwt', + schema: this.schema, + payload: payload, + accountability: this.accountability, + status: 'pending', + user: user?.id, + database: this.knex, + }); + + if (customClaims) { + payload = customClaims.length > 0 ? customClaims.reduce((acc, val) => merge(acc, val), payload) : payload; + } + /** * @TODO * Sign token with combination of server secret + user password hash diff --git a/api/src/utils/url.ts b/api/src/utils/url.ts index a084a26757..42c68d267d 100644 --- a/api/src/utils/url.ts +++ b/api/src/utils/url.ts @@ -19,7 +19,7 @@ export class Url { !isProtocolRelative && !isRootRelative && !isPathRelative ? parsedUrl.protocol.substring(0, parsedUrl.protocol.length - 1) : null; - this.host = !isRootRelative && !isPathRelative ? parsedUrl.host : null; + this.host = !isRootRelative && !isPathRelative ? parsedUrl.hostname : null; this.port = parsedUrl.port !== '' ? parsedUrl.port : null; this.path = parsedUrl.pathname.split('/').filter((p) => p !== ''); this.query = Object.fromEntries(parsedUrl.searchParams.entries()); diff --git a/app/package.json b/app/package.json index 469e4f7edb..38c277d680 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "@directus/app", - "version": "9.0.0-rc.92", + "version": "9.0.0-rc.93", "private": false, "description": "Directus is an Open-Source Headless CMS & API for Managing Custom Databases", "author": "Rijk van Zanten ", @@ -27,10 +27,10 @@ }, "gitHead": "24621f3934dc77eb23441331040ed13c676ceffd", "devDependencies": { - "@directus/docs": "9.0.0-rc.92", - "@directus/extensions-sdk": "9.0.0-rc.92", - "@directus/format-title": "9.0.0-rc.92", - "@directus/shared": "9.0.0-rc.92", + "@directus/docs": "9.0.0-rc.93", + "@directus/extensions-sdk": "9.0.0-rc.93", + "@directus/format-title": "9.0.0-rc.93", + "@directus/shared": "9.0.0-rc.93", "@fullcalendar/core": "5.9.0", "@fullcalendar/daygrid": "5.9.0", "@fullcalendar/interaction": "5.9.0", @@ -38,7 +38,7 @@ "@fullcalendar/timegrid": "5.9.0", "@mapbox/mapbox-gl-draw": "1.3.0", "@mapbox/mapbox-gl-draw-static-mode": "1.0.1", - "@mapbox/mapbox-gl-geocoder": "4.7.3", + "@mapbox/mapbox-gl-geocoder": "4.7.4", "@popperjs/core": "2.9.3", "@rollup/plugin-yaml": "3.1.0", "@sindresorhus/slugify": "2.1.0", @@ -52,7 +52,7 @@ "@types/diff": "5.0.1", "@types/dompurify": "2.2.3", "@types/geojson": "7946.0.8", - "@types/lodash": "4.14.172", + "@types/lodash": "4.14.173", "@types/mapbox__mapbox-gl-draw": "1.2.3", "@types/mapbox__mapbox-gl-geocoder": "4.7.1", "@types/markdown-it": "12.2.1", @@ -67,14 +67,14 @@ "@vue/cli-plugin-typescript": "4.5.13", "@vue/cli-plugin-vuex": "4.5.13", "@vue/cli-service": "4.5.13", - "@vue/compiler-sfc": "3.2.11", + "@vue/compiler-sfc": "3.2.12", "apexcharts": "3.26.3", "axios": "0.21.4", "base-64": "1.0.0", "codemirror": "5.62.3", "copyfiles": "2.4.1", "cropperjs": "1.5.12", - "date-fns": "2.23.0", + "date-fns": "2.24.0", "diacritics": "1.3.0", "dompurify": "2.3.2", "escape-string-regexp": "5.0.0", @@ -89,15 +89,15 @@ "nanoid": "3.1.25", "p-queue": "7.1.0", "pinia": "2.0.0-rc.9", - "prettier": "2.4.0", + "prettier": "2.4.1", "pretty-ms": "7.0.1", "qrcode": "1.4.4", "rimraf": "3.0.2", - "sass": "1.41.0", + "sass": "1.41.1", "tinymce": "5.9.2", "typescript": "4.4.3", - "vite": "2.5.7", - "vue": "3.2.11", + "vite": "2.5.8", + "vue": "3.2.12", "vue-i18n": "9.1.7", "vue-router": "4.0.11", "vuedraggable": "4.1.0", diff --git a/app/src/components/v-button/v-button.vue b/app/src/components/v-button/v-button.vue index fcc2f0271b..884ad0d83e 100644 --- a/app/src/components/v-button/v-button.vue +++ b/app/src/components/v-button/v-button.vue @@ -20,6 +20,7 @@ tile, 'full-width': fullWidth, }, + kind, ]" :type="type" :disabled="disabled" @@ -55,6 +56,10 @@ export default defineComponent({ type: Boolean, default: false, }, + kind: { + type: String as PropType<'normal' | 'info' | 'success' | 'warning' | 'danger'>, + default: 'normal', + }, fullWidth: { type: Boolean, default: false, @@ -172,7 +177,6 @@ export default defineComponent({ return false; }); - return { sizeClass, onClick, component, isActiveRoute, toggle }; function onClick(event: MouseEvent) { @@ -203,9 +207,36 @@ export default defineComponent({ --v-button-min-width: 140px; } -.v-button { - display: inline-flex; - align-items: center; +.info { + --v-button-color: var(--white); + --v-button-color-hover: var(--white); + --v-button-background-color: var(--blue); + --v-button-background-color-hover: var(--blue-125); + --v-button-background-color-active: var(--blue); +} + +.success { + --v-button-color: var(--white); + --v-button-color-hover: var(--white); + --v-button-background-color: var(--success); + --v-button-background-color-hover: var(--success-125); + --v-button-background-color-active: var(--success); +} + +.warning { + --v-button-color: var(--white); + --v-button-color-hover: var(--white); + --v-button-background-color: var(--warning); + --v-button-background-color-hover: var(--warning-125); + --v-button-background-color-active: var(--warning); +} + +.danger { + --v-button-color: var(--white); + --v-button-color-hover: var(--white); + --v-button-background-color: var(--danger); + --v-button-background-color-hover: var(--danger-125); + --v-button-background-color-active: var(--danger); } .secondary { @@ -224,11 +255,6 @@ export default defineComponent({ --v-button-color-disabled: var(--foreground-normal); } -.warning { - --v-button-background-color: var(--warning); - --v-button-background-color-hover: var(--warning-125); -} - .warning.rounded { --v-button-background-color: var(--warning-10); --v-button-color: var(--warning); @@ -236,11 +262,6 @@ export default defineComponent({ --v-button-color-hover: var(--warning); } -.danger { - --v-button-background-color: var(--danger); - --v-button-background-color-hover: var(--danger-125); -} - .danger.rounded { --v-button-background-color: var(--danger-10); --v-button-color: var(--danger); diff --git a/app/src/components/v-checkbox/v-checkbox.vue b/app/src/components/v-checkbox/v-checkbox.vue index 395fef1240..89c1e03e58 100644 --- a/app/src/components/v-checkbox/v-checkbox.vue +++ b/app/src/components/v-checkbox/v-checkbox.vue @@ -21,7 +21,7 @@ diff --git a/app/src/displays/related-values/index.ts b/app/src/displays/related-values/index.ts index 3c38c0d833..e09e655030 100644 --- a/app/src/displays/related-values/index.ts +++ b/app/src/displays/related-values/index.ts @@ -1,11 +1,10 @@ -import useCollection from '@/composables/use-collection'; import { defineDisplay } from '@directus/shared/utils'; import adjustFieldsForDisplays from '@/utils/adjust-fields-for-displays'; -import { getFieldsFromTemplate } from '@/utils/get-fields-from-template'; +import { getFieldsFromTemplate } from '@directus/shared/utils'; import getRelatedCollection from '@/utils/get-related-collection'; -import { ref } from 'vue'; import options from './options.vue'; import DisplayRelatedValues from './related-values.vue'; +import { useFieldsStore } from '@/stores'; type Options = { template: string; @@ -22,7 +21,8 @@ export default defineDisplay({ groups: ['m2m', 'm2o', 'o2m'], fields: (options: Options | null, { field, collection }) => { const relatedCollection = getRelatedCollection(collection, field); - const { primaryKeyField } = useCollection(ref(relatedCollection as unknown as string)); + const fieldsStore = useFieldsStore(); + const primaryKeyField = fieldsStore.getPrimaryKeyFieldForCollection(relatedCollection); if (!relatedCollection) return []; @@ -30,8 +30,8 @@ export default defineDisplay({ ? adjustFieldsForDisplays(getFieldsFromTemplate(options.template), relatedCollection as unknown as string) : []; - if (primaryKeyField.value && !fields.includes(primaryKeyField.value.field)) { - fields.push(primaryKeyField.value.field); + if (primaryKeyField && !fields.includes(primaryKeyField.field)) { + fields.push(primaryKeyField.field); } return fields; diff --git a/app/src/displays/related-values/options.vue b/app/src/displays/related-values/options.vue index de81f51021..ad1e8d9bdc 100644 --- a/app/src/displays/related-values/options.vue +++ b/app/src/displays/related-values/options.vue @@ -12,8 +12,7 @@