Merge pull request #1 from directus/main

update
This commit is contained in:
Adrian Dimitrov
2020-10-19 20:05:13 +03:00
committed by GitHub
26 changed files with 264 additions and 134 deletions

View File

@@ -119,6 +119,10 @@ router.get(
res.attachment(file.filename_download);
res.setHeader('Content-Type', file.type);
if (req.query.hasOwnProperty('download') === false) {
res.removeHeader('Content-Disposition');
}
stream.pipe(res);
})
);

View File

@@ -49,7 +49,7 @@ router.get(
router.get(
'/me',
asyncHandler(async (req, res, next) => {
if (!req.accountability?.user || !req.accountability?.role) {
if (!req.accountability?.user) {
throw new InvalidCredentialsException();
}

View File

@@ -58,11 +58,21 @@ router.get(
if (!req.accountability?.user) {
throw new InvalidCredentialsException();
}
const service = new UsersService({ accountability: req.accountability });
const item = await service.readByKey(req.accountability.user, req.sanitizedQuery);
try {
const item = await service.readByKey(req.accountability.user, req.sanitizedQuery);
res.locals.payload = { data: item || null };
} catch (error) {
if (error instanceof ForbiddenException) {
res.locals.payload = { data: { id: req.accountability.user } };
return next();
}
throw error;
}
res.locals.payload = { data: item || null };
return next();
}),
respond

View File

@@ -101,7 +101,7 @@ fields:
display: user
- collection: directus_files
field: modified_on
interface: dateTime
interface: datetime
locked: true
special: date-updated
width: half
@@ -111,4 +111,4 @@ fields:
display: datetime
- collection: directus_files
field: created_by
display: user
display: user

View File

@@ -7,7 +7,10 @@ export function parseFilter(filter: Filter, accountability: Accountability | nul
if (val === 'true') return true;
if (val === 'false') return false;
if (key === '_in' || key === '_nin') return toArray(val);
if (key === '_in' || key === '_nin') {
if (typeof val === 'string' && val.includes(',')) return val.split(',');
else return toArray(val);
}
if (val === '$NOW') return new Date();
if (val === '$CURRENT_USER') return accountability?.user || null;

View File

@@ -1,6 +1,7 @@
import { Accountability, Query, Sort, Filter, Meta } from '../types';
import logger from '../logger';
import { parseFilter } from '../utils/parse-filter';
import { flatten } from 'lodash';
export function sanitizeQuery(
rawQuery: Record<string, any>,
@@ -75,6 +76,9 @@ function sanitizeFields(rawFields: any) {
if (typeof rawFields === 'string') fields = rawFields.split(',');
else if (Array.isArray(rawFields)) fields = rawFields as string[];
// Case where array item includes CSV (fe fields[]=id,name):
fields = flatten(fields.map((field) => (field.includes(',') ? field.split(',') : field)));
return fields;
}

162
app/package-lock.json generated
View File

@@ -6601,51 +6601,6 @@
"tslint": "^5.20.1",
"webpack": "^4.0.0",
"yorkie": "^2.0.0"
},
"dependencies": {
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"fork-ts-checker-webpack-plugin-v5": {
"version": "npm:fork-ts-checker-webpack-plugin@5.2.0",
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.0.tgz",
"integrity": "sha512-NEKcI0+osT5bBFZ1SFGzJMQETjQWZrSvMO1g0nAR/w0t328Z41eN8BJEIZyFCl2HsuiJpa9AN474Nh2qLVwGLQ==",
"dev": true,
"optional": true,
"requires": {
"@babel/code-frame": "^7.8.3",
"@types/json-schema": "^7.0.5",
"chalk": "^4.1.0",
"cosmiconfig": "^6.0.0",
"deepmerge": "^4.2.2",
"fs-extra": "^9.0.0",
"memfs": "^3.1.2",
"minimatch": "^3.0.4",
"schema-utils": "2.7.0",
"semver": "^7.3.2",
"tapable": "^1.0.0"
}
},
"schema-utils": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
"dev": true,
"optional": true,
"requires": {
"@types/json-schema": "^7.0.4",
"ajv": "^6.12.2",
"ajv-keywords": "^3.4.1"
}
}
}
},
"@vue/cli-plugin-unit-jest": {
@@ -6785,17 +6740,6 @@
"unique-filename": "^1.1.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
@@ -6879,18 +6823,6 @@
"graceful-fs": "^4.1.6"
}
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@@ -7004,18 +6936,6 @@
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.0.0-beta.8",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.8.tgz",
"integrity": "sha512-oouKUQWWHbSihqSD7mhymGPX1OQ4hedzAHyvm8RdyHh6m3oIvoRF+NM45i/bhNOlo8jCnuJhaSUf/6oDjv978g==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
}
},
"wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
@@ -11744,6 +11664,51 @@
}
}
},
"fork-ts-checker-webpack-plugin-v5": {
"version": "npm:fork-ts-checker-webpack-plugin@5.2.0",
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.0.tgz",
"integrity": "sha512-NEKcI0+osT5bBFZ1SFGzJMQETjQWZrSvMO1g0nAR/w0t328Z41eN8BJEIZyFCl2HsuiJpa9AN474Nh2qLVwGLQ==",
"dev": true,
"optional": true,
"requires": {
"@babel/code-frame": "^7.8.3",
"@types/json-schema": "^7.0.5",
"chalk": "^4.1.0",
"cosmiconfig": "^6.0.0",
"deepmerge": "^4.2.2",
"fs-extra": "^9.0.0",
"memfs": "^3.1.2",
"minimatch": "^3.0.4",
"schema-utils": "2.7.0",
"semver": "^7.3.2",
"tapable": "^1.0.0"
},
"dependencies": {
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"schema-utils": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
"dev": true,
"optional": true,
"requires": {
"@types/json-schema": "^7.0.4",
"ajv": "^6.12.2",
"ajv-keywords": "^3.4.1"
}
}
}
},
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
@@ -20377,6 +20342,43 @@
}
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.0.0-beta.8",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.8.tgz",
"integrity": "sha512-oouKUQWWHbSihqSD7mhymGPX1OQ4hedzAHyvm8RdyHh6m3oIvoRF+NM45i/bhNOlo8jCnuJhaSUf/6oDjv978g==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
}
}
},
"vue-router": {
"version": "3.4.6",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.6.tgz",

View File

@@ -14,15 +14,7 @@
</template>
</v-info>
<router-view v-else-if="!hydrating && appAccess" />
<v-info v-else-if="appAccess === false" center :title="$t('no_app_access')" type="danger" icon="block">
{{ $t('no_app_access_copy') }}
<template #append>
<v-button to="/logout">Switch User</v-button>
</template>
</v-info>
<router-view v-else-if="!hydrating" />
<portal-target name="dialog-outlet" transition="transition-dialog" multiple />
<portal-target name="menu-outlet" transition="transition-bounce" multiple />
@@ -88,7 +80,7 @@ export default defineComponent({
document.body.classList.remove('light');
document.body.classList.remove('auto');
if (newUser !== undefined && newUser !== null) {
if (newUser !== undefined && newUser !== null && newUser.theme) {
document.body.classList.add(newUser.theme);
} else {
// Default to light mode
@@ -108,11 +100,6 @@ export default defineComponent({
return settingsStore.state?.settings?.custom_css || '';
});
const appAccess = computed(() => {
if (!userStore.state.currentUser) return true;
return userStore.state.currentUser?.role?.app_access || false;
});
const error = computed(() => appStore.state.error);
/**
@@ -124,7 +111,7 @@ export default defineComponent({
axios,
});
return { hydrating, brandStyle, appAccess, error, customCSS };
return { hydrating, brandStyle, error, customCSS };
},
});
</script>

View File

@@ -161,7 +161,7 @@ export default defineComponent({
}
}
if (props.slug === true) {
if (props.dbSafe === true) {
const dbSafeCharacters = 'abcdefghijklmnopqrstuvwxyz01234567890-_~ '.split('');
const isAllowed = dbSafeCharacters.includes(key) || systemKeys.includes(key);

View File

@@ -57,11 +57,11 @@ export async function hydrate(stores = useStores()) {
*/
await userStore.hydrate();
setLanguage((userStore.state.currentUser?.language as Language) || 'en-US');
await Promise.all(stores.filter(({ id }) => id !== 'userStore').map((store) => store.hydrate?.()));
await registerModules();
if (userStore.state.currentUser?.role) {
await setLanguage((userStore.state.currentUser?.language as Language) || 'en-US');
await Promise.all(stores.filter(({ id }) => id !== 'userStore').map((store) => store.hydrate?.()));
await registerModules();
}
} catch (error) {
appStore.state.error = error;
} finally {

View File

@@ -91,6 +91,8 @@ export default defineComponent({
};
function setIcon(icon: string | null) {
searchQuery.value = '';
emit('input', icon);
}
},

View File

@@ -1,5 +1,9 @@
/* stylelint-disable font-family-no-missing-generic-family-keyword */
.tox {
font-family: var(--family-sans-serif);
}
.tox .tox-tbtn {
margin: 2px 2px 4px 0;
color: var(--foreground-normal);
@@ -138,8 +142,9 @@ body.dark .tox .tox-toolbar__overflow {
}
.tox .tox-dialog__header {
padding: 16px 24px 0 24px;
padding: 20px;
color: var(--foreground-normal);
font-size: 16px;
background-color: var(--background-page);
}
@@ -156,18 +161,61 @@ body.dark .tox .tox-toolbar__overflow {
}
.tox .tox-textfield,
.tox .tox-listboxfield .tox-listbox,
.tox .tox-toolbar-textfield,
.tox .tox-selectfield select,
.tox .tox-textarea {
padding: 12px;
color: var(--foreground-normal);
font-family: monospace;
font-weight: 500;
font-size: 14px;
font-family: var(--family-sans-serif);
background-color: var(--background-page);
border: 2px solid var(--border-normal);
border-radius: var(--border-radius);
transition: var(--fast) var(--transition);
}
.tox .tox-textarea {
font-family: var(--family-monospace);
}
.tox .tox-textfield:focus,
.tox .tox-listboxfield .tox-listbox:focus,
.tox .tox-toolbar-textfield:focus,
.tox .tox-selectfield select:focus,
.tox .tox-textarea:focus {
background-color: var(--background-page);
}
.tox .tox-menu {
box-sizing: border-box;
padding: 4px !important;
color: var(--foreground-normal);
font-family: var(--family-sans-serif);
background-color: var(--background-subdued);
border: 2px solid var(--border-normal);
border-radius: var(--border-radius);
}
.tox .tox-collection__item {
border-radius: var(--border-radius);
}
.tox .tox-collection--list .tox-collection__item {
color: var(--foreground-normal);
}
.tox .tox-collection--list .tox-collection__item--active {
color: var(--foreground-normal) !important;
background-color: var(--background-page) !important;
}
.tox .tox-collection--list .tox-collection__item--enabled {
color: var(--foreground-normal);
background-color: var(--background-page);
}
.tox .tox-textfield:focus,
.tox .tox-selectfield select:focus,
.tox .tox-textarea:focus {
@@ -184,6 +232,7 @@ body.dark .tox .tox-toolbar__overflow {
color: var(--white);
font-weight: 500;
font-size: 16px;
font-family: var(--family-sans-serif);
line-height: 19px;
background-color: var(--primary);
border: 2px solid var(--primary);
@@ -228,12 +277,45 @@ body.dark .tox .tox-toolbar__overflow {
}
.tox .tox-form__group {
margin-top: 24px;
margin-bottom: 24px;
}
.tox .tox-label,
.tox .tox-toolbar-label {
margin-bottom: 10px;
margin-bottom: 4px;
color: var(--foreground-normal);
font-size: 14px;
font-weight: 500;
font-size: 16px;
}
.tox .tox-dialog__body-nav-item {
box-sizing: border-box;
width: 100%;
margin-bottom: 4px;
padding: 12px 16px;
color: var(--foreground-normal);
font-weight: 500;
font-size: 14px;
border-bottom: none;
border-radius: var(--border-radius);
transition: var(--fast) var(--transition);
transition-property: background-color, color;
}
.tox .tox-dialog__body-nav-item:hover {
background-color: var(--background-normal-alt);
}
.tox .tox-dialog__body-nav-item--active {
background-color: var(--background-normal-alt);
}
.tox .tox-dialog__body-nav-item--active:focus {
background-color: var(--background-normal-alt);
}
@media screen and (max-width: 767px) {
.tox .tox-dialog__body-nav-item {
text-align: center;
}
}

View File

@@ -162,8 +162,8 @@
"create_field": "Create Field",
"update_field": "Update Field",
"creating_new_field": "{collection}: New Field",
"updating_field_field": "{collection}: \"{field}\" Field",
"creating_new_field": "New Field ({collection})",
"updating_field_field": "{field} ({collection})",
"within_collection": "Within {collection}",
"field_standard": "Standard",

View File

@@ -49,7 +49,7 @@
<v-list-item to="/activity?action=comment" exact>
<v-list-item-icon>
<v-icon name="notes" />
<v-icon name="chat_bubble_outline" />
</v-list-item-icon>
<v-list-item-content>
{{ $t('comment') }}

View File

@@ -338,10 +338,14 @@ export default defineComponent({
table tr {
margin: 0;
padding: 0;
background-color: white;
background-color: var(--background-normal);
border-top: 1px solid var(--background-normal);
}
table thead tr {
background-color: var(--background-normal-alt);
}
table tr:nth-child(2n) {
background-color: var(--background-page);
}

View File

@@ -34,7 +34,7 @@
</v-list-item>
<v-list-item to="/files/mine" exact>
<v-list-item-icon><v-icon name="face" /></v-list-item-icon>
<v-list-item-icon><v-icon name="folder_shared" /></v-list-item-icon>
<v-list-item-content>{{ $t('my_files') }}</v-list-item-content>
</v-list-item>

View File

@@ -395,7 +395,7 @@ export default defineComponent({
}
function downloadFile() {
const filePath = getRootPath() + `assets/${props.primaryKey}`;
const filePath = getRootPath() + `assets/${props.primaryKey}?download`;
window.open(filePath, '_blank');
}

View File

@@ -49,7 +49,15 @@
<template #input>
<div class="label">
<span class="name" v-tooltip="field.name">{{ field.field }}</span>
<span class="name" v-tooltip="field.name">
{{ field.field }}
<v-icon
name="star"
class="required"
sup
v-if="field.schema && field.schema.is_nullable === false"
/>
</span>
<span v-if="field.meta" class="interface">{{ interfaceName }}</span>
<span v-else class="interface">{{ $t('db_only_click_to_configure') }}</span>
</div>
@@ -522,4 +530,8 @@ export default defineComponent({
--v-button-background-color: var(--danger);
--v-button-background-color-hover: var(--danger-125);
}
.required {
color: var(--primary);
}
</style>

View File

@@ -28,7 +28,7 @@
<template #activator="{ toggle, active }">
<v-icon class="more" :class="{ active }" name="more_horiz" @click="toggle" />
<div class="time">
<span class="dot" v-if="activity.revisions.length > 0" v-tooltip="editedOnFormatted" />
<span class="dot" v-tooltip="editedOnFormatted" />
{{ formattedTime }}
</div>
</template>

View File

@@ -52,6 +52,7 @@ import { debounce } from 'lodash';
import { FieldTree } from './types';
import FieldListItem from './field-list-item.vue';
import { useCollection } from '@/composables/use-collection';
import getAvailableOperatorsForType from './get-available-operators-for-type';
export default defineComponent({
components: { FieldFilter, FieldListItem },
@@ -189,12 +190,15 @@ export default defineComponent({
return { fieldTree, addFilterForField, filters, removeFilter, updateFilter, showArchiveToggle, archived };
function addFilterForField(fieldKey: string) {
const field = fieldsStore.getField(props.collection, fieldKey);
const defaultOperator = getAvailableOperatorsForType(field.type).operators[0];
emit('input', [
...props.value,
{
key: nanoid(),
field: fieldKey,
operator: 'contains',
operator: defaultOperator || 'contains',
value: '',
},
]);

View File

@@ -19,7 +19,7 @@ export default function getAvailableOperatorsForType(type: string) {
case 'string':
return {
type: 'text',
operators: ['eq', 'neq', 'contains', 'ncontains', 'empty', 'nempty', 'in', 'nin'],
operators: ['contains', 'ncontains', 'eq', 'neq', 'empty', 'nempty', 'in', 'nin'],
};
// Boolean
case 'boolean':

View File

@@ -191,11 +191,6 @@ export default defineComponent({
</script>
<style lang="scss" scoped>
.sidebar-detail {
--v-badge-color: var(--background-normal);
--v-badge-background-color: var(--foreground-normal);
}
.v-progress-linear {
margin: 24px 0;
}

View File

@@ -72,6 +72,8 @@ body {
--v-badge-offset-x: 2px;
--v-badge-offset-y: 4px;
--v-badge-border-color: var(--background-normal-alt);
--v-badge-background-color: var(--foreground-normal);
--v-badge-color: var(--background-normal);
display: contents;

View File

@@ -1,5 +1,13 @@
<template>
<div class="private-view" :class="{ theme }">
<v-info v-if="appAccess === false" center :title="$t('no_app_access')" type="danger" icon="block">
{{ $t('no_app_access_copy') }}
<template #append>
<v-button to="/logout">Switch User</v-button>
</template>
</v-info>
<div v-else class="private-view" :class="{ theme }">
<aside role="navigation" aria-label="Module Navigation" class="navigation" :class="{ 'is-open': navOpen }">
<module-bar />
<div class="module-nav alt-colors">
@@ -81,6 +89,11 @@ export default defineComponent({
const userStore = useUserStore();
const appStore = useAppStore();
const appAccess = computed(() => {
if (!userStore.state.currentUser) return true;
return userStore.state.currentUser?.role?.app_access || false;
});
const notificationsPreviewActive = ref(false);
const { sidebarOpen } = toRefs(appStore.state);
@@ -98,6 +111,7 @@ export default defineComponent({
sidebarOpen,
openSidebar,
notificationsPreviewActive,
appAccess,
};
function openSidebar(event: PointerEvent) {

View File

@@ -1,5 +1,5 @@
- name: Getting Started
icon: play_arrow
icon: play_circle_outline
to: "/getting-started"
children:
- name: Introduction

View File

@@ -17,29 +17,34 @@ get:
description: The key of the asset size configured in settings.
schema:
type: string
- name: w
- name: width
in: query
description: Width of the file in pixels.
schema:
type: integer
- name: h
- name: height
in: query
description: Height of the file in pixels.
schema:
type: integer
- name: f
- name: fit
in: query
description: Fit of the file
schema:
type: string
enum: [crop, contain]
- name: q
- name: quality
in: query
description: Quality of compression.
schema:
type: integer
minimum: 1
maximum: 100
- name: download
in: query
description: Download the asset to your computer
schema:
type: boolean
responses:
"200":
description: Successful request