mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Fix strict mode errors in TS
This commit is contained in:
@@ -53,7 +53,7 @@ export default async function sendMail(options: EmailOptions) {
|
||||
const templateString = await readFile(path.join(__dirname, 'templates/base.liquid'), 'utf8');
|
||||
const html = await liquidEngine.parseAndRender(templateString, { html: options.html });
|
||||
|
||||
options.from = options.from || process.env.EMAIL_FROM;
|
||||
options.from = options.from || (process.env.EMAIL_FROM as string);
|
||||
|
||||
try {
|
||||
await transporter.sendMail({ ...options, html: html });
|
||||
|
||||
@@ -19,7 +19,7 @@ const authenticate: RequestHandler = asyncHandler(async (req, res, next) => {
|
||||
let payload: { id: string };
|
||||
|
||||
try {
|
||||
payload = jwt.verify(req.token, process.env.SECRET) as { id: string };
|
||||
payload = jwt.verify(req.token, process.env.SECRET as string) as { id: string };
|
||||
} catch (err) {
|
||||
if (err instanceof TokenExpiredError) {
|
||||
throw new InvalidCredentialsException('Token expired.');
|
||||
|
||||
@@ -18,13 +18,13 @@ const collectionExists: RequestHandler = asyncHandler(async (req, res, next) =>
|
||||
|
||||
req.collection = req.params.collection;
|
||||
|
||||
const { single } = await database
|
||||
const collectionInfo = await database
|
||||
.select('single')
|
||||
.from('directus_collections')
|
||||
.where({ collection: req.collection })
|
||||
.first();
|
||||
|
||||
req.single = single;
|
||||
req.single = collectionInfo?.single || false;
|
||||
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Meta } from '../types/meta';
|
||||
import logger from '../logger';
|
||||
|
||||
const sanitizeQuery: RequestHandler = (req, res, next) => {
|
||||
req.sanitizedQuery = {};
|
||||
if (!req.query) return;
|
||||
|
||||
const query: Query = {
|
||||
@@ -16,7 +17,11 @@ const sanitizeQuery: RequestHandler = (req, res, next) => {
|
||||
};
|
||||
|
||||
if (req.query.limit) {
|
||||
query.limit = sanitizeLimit(req.query.limit);
|
||||
const limit = sanitizeLimit(req.query.limit);
|
||||
|
||||
if (limit) {
|
||||
query.limit = limit;
|
||||
}
|
||||
}
|
||||
|
||||
if (req.query.sort) {
|
||||
|
||||
@@ -51,10 +51,12 @@ router.get(
|
||||
);
|
||||
}
|
||||
|
||||
const systemKeys = SYSTEM_ASSET_WHITELIST.map((size) => size.key);
|
||||
const systemKeys = SYSTEM_ASSET_WHITELIST.map((transformation) => transformation.key);
|
||||
const allKeys: string[] = [
|
||||
...systemKeys,
|
||||
...assetSettings.asset_shortcuts.map((size) => size.key),
|
||||
...assetSettings.asset_shortcuts.map(
|
||||
(transformation: Transformation) => transformation.key
|
||||
),
|
||||
];
|
||||
|
||||
// For use in the next request handler
|
||||
@@ -86,7 +88,10 @@ router.get(
|
||||
// Return file
|
||||
asyncHandler(async (req, res) => {
|
||||
const transformation: Transformation = res.locals.transformation.key
|
||||
? res.locals.shortcuts.find((size) => size.key === res.locals.transformation.key)
|
||||
? res.locals.shortcuts.find(
|
||||
(transformation: Transformation) =>
|
||||
transformation.key === res.locals.transformation.key
|
||||
)
|
||||
: res.locals.transformation;
|
||||
|
||||
const { stream, file } = await AssetsService.getAsset(req.params.pk, transformation);
|
||||
|
||||
@@ -59,7 +59,7 @@ router.post(
|
||||
if (mode === 'cookie') {
|
||||
res.cookie('directus_refresh_token', refreshToken, {
|
||||
httpOnly: true,
|
||||
maxAge: ms(process.env.REFRESH_TOKEN_TTL),
|
||||
maxAge: ms(process.env.REFRESH_TOKEN_TTL as string),
|
||||
secure: process.env.REFRESH_TOKEN_COOKIE_SECURE === 'true' ? true : false,
|
||||
sameSite:
|
||||
(process.env.REFRESH_TOKEN_COOKIE_SAME_SITE as 'lax' | 'strict' | 'none') ||
|
||||
@@ -100,7 +100,7 @@ router.post(
|
||||
if (mode === 'cookie') {
|
||||
res.cookie('directus_refresh_token', refreshToken, {
|
||||
httpOnly: true,
|
||||
maxAge: ms(process.env.REFRESH_TOKEN_TTL),
|
||||
maxAge: ms(process.env.REFRESH_TOKEN_TTL as string),
|
||||
secure: process.env.REFRESH_TOKEN_COOKIE_SECURE === 'true' ? true : false,
|
||||
sameSite:
|
||||
(process.env.REFRESH_TOKEN_COOKIE_SAME_SITE as 'lax' | 'strict' | 'none') ||
|
||||
@@ -132,7 +132,7 @@ router.post(
|
||||
|
||||
router.use(
|
||||
'/sso',
|
||||
session({ secret: process.env.SECRET, saveUninitialized: false, resave: false })
|
||||
session({ secret: process.env.SECRET as string, saveUninitialized: false, resave: false })
|
||||
);
|
||||
|
||||
router.use(grant.express()(getGrantConfig()));
|
||||
@@ -143,7 +143,7 @@ router.use(grant.express()(getGrantConfig()));
|
||||
router.get(
|
||||
'/sso/:provider/callback',
|
||||
asyncHandler(async (req, res) => {
|
||||
const email = getEmailFromProfile(req.params.provider, req.session.grant.response.profile);
|
||||
const email = getEmailFromProfile(req.params.provider, req.session!.grant.response.profile);
|
||||
|
||||
const { accessToken, refreshToken, expires, id } = await AuthService.authenticate(email);
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ router.post(
|
||||
throw new InvalidPayloadException(error.message);
|
||||
}
|
||||
|
||||
const field: Partial<Field> = req.body;
|
||||
const field: Partial<Field> & { field: string; database: { type: string } } = req.body;
|
||||
|
||||
const createdField = await FieldsService.createField(req.collection, field, {
|
||||
role: req.role,
|
||||
|
||||
@@ -5,7 +5,7 @@ import sanitizeQuery from '../middleware/sanitize-query';
|
||||
import collectionExists from '../middleware/collection-exists';
|
||||
import * as MetaService from '../services/meta';
|
||||
import { RouteNotFoundException } from '../exceptions';
|
||||
import { Accountability } from '../types';
|
||||
import { Accountability, PrimaryKey } from '../types';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -73,9 +73,9 @@ router.get(
|
||||
throw new RouteNotFoundException(req.path);
|
||||
}
|
||||
|
||||
const pk = req.params.pk.includes(',') ? req.params.pk.split(',') : req.params.pk;
|
||||
const primaryKey = req.params.pk.includes(',') ? req.params.pk.split(',') : req.params.pk;
|
||||
|
||||
const result = await ItemsService.readItem(req.collection, pk, req.sanitizedQuery, {
|
||||
const result = await ItemsService.readItem(req.collection, primaryKey, req.sanitizedQuery, {
|
||||
role: req.role,
|
||||
admin: req.admin,
|
||||
});
|
||||
@@ -130,21 +130,40 @@ router.patch(
|
||||
};
|
||||
|
||||
const primaryKey = req.params.pk.includes(',') ? req.params.pk.split(',') : req.params.pk;
|
||||
const updatedPrimaryKey = await ItemsService.updateItem(
|
||||
req.collection,
|
||||
primaryKey,
|
||||
req.body,
|
||||
accountability
|
||||
);
|
||||
|
||||
const result = await ItemsService.readItem(
|
||||
req.collection,
|
||||
updatedPrimaryKey,
|
||||
req.sanitizedQuery,
|
||||
accountability
|
||||
);
|
||||
if (Array.isArray(primaryKey)) {
|
||||
const updatedPrimaryKey = await ItemsService.updateItem(
|
||||
req.collection,
|
||||
primaryKey,
|
||||
req.body,
|
||||
accountability
|
||||
);
|
||||
|
||||
res.json({ data: result || null });
|
||||
const result = await ItemsService.readItem(
|
||||
req.collection,
|
||||
updatedPrimaryKey,
|
||||
req.sanitizedQuery,
|
||||
accountability
|
||||
);
|
||||
|
||||
res.json({ data: result || null });
|
||||
} else {
|
||||
const updatedPrimaryKey = await ItemsService.updateItem(
|
||||
req.collection,
|
||||
primaryKey,
|
||||
req.body,
|
||||
accountability
|
||||
);
|
||||
|
||||
const result = await ItemsService.readItem(
|
||||
req.collection,
|
||||
updatedPrimaryKey,
|
||||
req.sanitizedQuery,
|
||||
accountability
|
||||
);
|
||||
|
||||
res.json({ data: result || null });
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as ItemsService from './items';
|
||||
import { Accountability, Item, Query } from '../types';
|
||||
import { Accountability, Item, Query, PrimaryKey } from '../types';
|
||||
|
||||
export enum Action {
|
||||
CREATE = 'create',
|
||||
@@ -15,16 +15,16 @@ export const createActivity = async (data: Partial<Item>) => {
|
||||
return await ItemsService.createItem('directus_activity', data);
|
||||
};
|
||||
|
||||
export const readActivities = async (query?: Query, accountability?: Accountability) => {
|
||||
export const readActivities = async (query: Query, accountability?: Accountability) => {
|
||||
return await ItemsService.readItems('directus_activity', query, accountability);
|
||||
};
|
||||
|
||||
export const readActivity = async (
|
||||
pk: string | number,
|
||||
query?: Query,
|
||||
primaryKey: PrimaryKey,
|
||||
query: Query = {},
|
||||
accountability?: Accountability
|
||||
) => {
|
||||
return await ItemsService.readItem('directus_activity', pk, query, accountability);
|
||||
return await ItemsService.readItem('directus_activity', primaryKey, query, accountability);
|
||||
};
|
||||
|
||||
export const updateActivity = async (
|
||||
|
||||
@@ -9,8 +9,8 @@ import { Session } from '../types/sessions';
|
||||
type AuthenticateOptions = {
|
||||
email: string;
|
||||
password?: string;
|
||||
ip?: string;
|
||||
userAgent?: string;
|
||||
ip?: string | null;
|
||||
userAgent?: string | null;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -45,12 +45,14 @@ export const authenticate = async ({ email, password, ip, userAgent }: Authentic
|
||||
* Sign token with combination of server secret + user password hash
|
||||
* That way, old tokens are immediately invalidated whenever the user changes their password
|
||||
*/
|
||||
const accessToken = jwt.sign(payload, process.env.SECRET, {
|
||||
const accessToken = jwt.sign(payload, process.env.SECRET as string, {
|
||||
expiresIn: process.env.ACCESS_TOKEN_TTL,
|
||||
});
|
||||
|
||||
const refreshToken = nanoid(64);
|
||||
const refreshTokenExpiration = new Date(Date.now() + ms(process.env.REFRESH_TOKEN_TTL));
|
||||
const refreshTokenExpiration = new Date(
|
||||
Date.now() + ms(process.env.REFRESH_TOKEN_TTL as string)
|
||||
);
|
||||
|
||||
await database('directus_sessions').insert({
|
||||
token: refreshToken,
|
||||
@@ -63,7 +65,7 @@ export const authenticate = async ({ email, password, ip, userAgent }: Authentic
|
||||
return {
|
||||
accessToken,
|
||||
refreshToken,
|
||||
expires: ms(process.env.ACCESS_TOKEN_TTL) / 1000,
|
||||
expires: ms(process.env.ACCESS_TOKEN_TTL as string) / 1000,
|
||||
id: user.id,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -67,10 +67,10 @@ export const create = async (payload: any, accountability: Accountability) => {
|
||||
}))
|
||||
);
|
||||
|
||||
return await ItemsService.readItem('directus_collections', primaryKey);
|
||||
return await ItemsService.readItem('directus_collections', primaryKey, {});
|
||||
};
|
||||
|
||||
export const readAll = async (query?: Query, accountability?: Accountability) => {
|
||||
export const readAll = async (query: Query, accountability?: Accountability) => {
|
||||
const [tables, collections] = await Promise.all([
|
||||
schemaInspector.tableInfo(),
|
||||
ItemsService.readItems<Collection>('directus_collections', query, accountability),
|
||||
@@ -96,7 +96,7 @@ export const readAll = async (query?: Query, accountability?: Accountability) =>
|
||||
|
||||
export const readOne = async (
|
||||
collection: string,
|
||||
query?: Query,
|
||||
query: Query,
|
||||
accountability?: Accountability
|
||||
) => {
|
||||
const [table, collectionInfo] = await Promise.all([
|
||||
|
||||
@@ -2,7 +2,7 @@ import listFolders from '../utils/list-folders';
|
||||
import path from 'path';
|
||||
|
||||
export async function listExtensions(type: string) {
|
||||
const extensionsPath = process.env.EXTENSIONS_PATH;
|
||||
const extensionsPath = process.env.EXTENSIONS_PATH as string;
|
||||
const location = path.join(extensionsPath, type);
|
||||
|
||||
return await listFolders(location);
|
||||
|
||||
@@ -64,7 +64,7 @@ export const readOne = async (collection: string, field: string) => {
|
||||
|
||||
export const createField = async (
|
||||
collection: string,
|
||||
field: Partial<Field>,
|
||||
field: Partial<Field> & { field: string; database: { type: string } },
|
||||
accountability: Accountability
|
||||
) => {
|
||||
await database.schema.alterTable(collection, (table) => {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import database, { schemaInspector } from '../database';
|
||||
import { Query } from '../types/query';
|
||||
import runAST from '../database/run-ast';
|
||||
import getASTFromQuery from '../utils/get-ast-from-query';
|
||||
import { Accountability, Operation, Item } from '../types';
|
||||
import { Accountability, Operation, Item, Query, PrimaryKey } from '../types';
|
||||
import Knex from 'knex';
|
||||
|
||||
import * as PayloadService from './payload';
|
||||
@@ -43,27 +42,17 @@ async function saveActivityAndRevision(
|
||||
}
|
||||
}
|
||||
|
||||
export async function createItem(
|
||||
export async function createItem<T extends Partial<Item> | Partial<Item>[]>(
|
||||
collection: string,
|
||||
data: Partial<Item>[],
|
||||
data: T,
|
||||
accountability?: Accountability
|
||||
): Promise<(string | number)[]>;
|
||||
export async function createItem(
|
||||
collection: string,
|
||||
data: Partial<Item>,
|
||||
accountability?: Accountability
|
||||
): Promise<string | number>;
|
||||
export async function createItem(
|
||||
collection: string,
|
||||
data: Partial<Item> | Partial<Item>[],
|
||||
accountability?: Accountability
|
||||
): Promise<string | number | (string | number)[]> {
|
||||
): Promise<T extends Partial<Item>[] ? PrimaryKey[] : PrimaryKey> {
|
||||
const isBatch = Array.isArray(data);
|
||||
|
||||
return database.transaction(async (transaction) => {
|
||||
const primaryKeys = await database.transaction(async (transaction) => {
|
||||
let payloads = isBatch ? data : [data];
|
||||
|
||||
const primaryKeys: (string | number)[] = await Promise.all(
|
||||
const primaryKeys: PrimaryKey[] = await Promise.all(
|
||||
payloads.map(async (payload: Partial<Item>) => {
|
||||
if (accountability && accountability.admin === false) {
|
||||
payload = await PermissionsService.processValues(
|
||||
@@ -106,16 +95,20 @@ export async function createItem(
|
||||
);
|
||||
}
|
||||
|
||||
return primaryKeys[0];
|
||||
return primaryKeys[0] as PrimaryKey;
|
||||
})
|
||||
);
|
||||
|
||||
if (isBatch) {
|
||||
return primaryKeys;
|
||||
} else {
|
||||
return primaryKeys[0];
|
||||
}
|
||||
return primaryKeys;
|
||||
});
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
return primaryKeys as T extends Partial<Record<string, any>>[] ? PrimaryKey[] : PrimaryKey;
|
||||
} else {
|
||||
return primaryKeys[0] as T extends Partial<Record<string, any>>[]
|
||||
? PrimaryKey[]
|
||||
: PrimaryKey;
|
||||
}
|
||||
}
|
||||
|
||||
export const readItems = async <T = Partial<Item>>(
|
||||
@@ -130,23 +123,23 @@ export const readItems = async <T = Partial<Item>>(
|
||||
}
|
||||
|
||||
const records = await runAST(ast);
|
||||
return await PayloadService.processValues('read', collection, records);
|
||||
return (await PayloadService.processValues('read', collection, records)) as T[];
|
||||
};
|
||||
|
||||
export const readItem = async <T extends number | string | (number | string)[]>(
|
||||
export async function readItem<T extends PrimaryKey | PrimaryKey[]>(
|
||||
collection: string,
|
||||
pk: T,
|
||||
query: Query = {},
|
||||
primaryKey: T,
|
||||
query: Query,
|
||||
accountability?: Accountability,
|
||||
operation?: Operation
|
||||
): Promise<T extends number | string ? Partial<Item> : Partial<Item>[]> => {
|
||||
): Promise<T extends PrimaryKey ? Item : Item[]> {
|
||||
// We allow overriding the operation, so we can use the item read logic to validate permissions
|
||||
// for update and delete as well
|
||||
operation = operation || 'read';
|
||||
|
||||
const primaryKeyField = await schemaInspector.primary(collection);
|
||||
const primaryKeys: any[] = Array.isArray(pk) ? pk : [pk];
|
||||
const isBatch = Array.isArray(pk);
|
||||
const primaryKeys = (Array.isArray(primaryKey) ? primaryKey : [primaryKey]) as PrimaryKey[];
|
||||
const isBatch = Array.isArray(primaryKey);
|
||||
|
||||
if (isBatch) {
|
||||
query = {
|
||||
@@ -164,7 +157,7 @@ export const readItem = async <T extends number | string | (number | string)[]>(
|
||||
filter: {
|
||||
...(query.filter || {}),
|
||||
[primaryKeyField]: {
|
||||
_eq: pk,
|
||||
_eq: primaryKey,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -179,15 +172,15 @@ export const readItem = async <T extends number | string | (number | string)[]>(
|
||||
const records = await runAST(ast);
|
||||
const processedRecords = await PayloadService.processValues('read', collection, records);
|
||||
return isBatch ? processedRecords : processedRecords[0];
|
||||
};
|
||||
}
|
||||
|
||||
export const updateItem = async <T extends number | string | (number | string)[]>(
|
||||
export async function updateItem<T extends PrimaryKey | PrimaryKey[]>(
|
||||
collection: string,
|
||||
pk: T,
|
||||
primaryKey: T,
|
||||
data: Partial<Item>,
|
||||
accountability?: Accountability
|
||||
): Promise<T> => {
|
||||
const primaryKeys: any[] = Array.isArray(pk) ? pk : [pk];
|
||||
): Promise<T> {
|
||||
const primaryKeys = (Array.isArray(primaryKey) ? primaryKey : [primaryKey]) as PrimaryKey[];
|
||||
|
||||
await database.transaction(async (transaction) => {
|
||||
let payload = clone(data);
|
||||
@@ -238,13 +231,13 @@ export const updateItem = async <T extends number | string | (number | string)[]
|
||||
);
|
||||
}
|
||||
|
||||
return pk;
|
||||
return primaryKey as PrimaryKey;
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
return pk;
|
||||
};
|
||||
return primaryKey;
|
||||
}
|
||||
|
||||
export const deleteItem = async <T extends number | string | (number | string)[]>(
|
||||
collection: string,
|
||||
@@ -270,14 +263,16 @@ export const deleteItem = async <T extends number | string | (number | string)[]
|
||||
.where({ [primaryKeyField]: key })
|
||||
.delete();
|
||||
|
||||
await saveActivityAndRevision(
|
||||
ActivityService.Action.DELETE,
|
||||
collection,
|
||||
String(key),
|
||||
{},
|
||||
accountability,
|
||||
transaction
|
||||
);
|
||||
if (accountability) {
|
||||
await saveActivityAndRevision(
|
||||
ActivityService.Action.DELETE,
|
||||
collection,
|
||||
String(key),
|
||||
{},
|
||||
accountability,
|
||||
transaction
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -297,7 +292,7 @@ export const readSingleton = async (
|
||||
|
||||
if (!record) {
|
||||
const columns = await schemaInspector.columnInfo(collection);
|
||||
const defaults = {};
|
||||
const defaults: Record<string, any> = {};
|
||||
|
||||
for (const column of columns) {
|
||||
defaults[column.name] = column.default_value;
|
||||
|
||||
@@ -14,7 +14,7 @@ export const getMetaForQuery = async (collection: string, query: Query) => {
|
||||
return results.reduce((metaObject: Record<string, any>, value, index) => {
|
||||
return {
|
||||
...metaObject,
|
||||
[query.meta[index]]: value,
|
||||
[query.meta![index]]: value,
|
||||
};
|
||||
}, {});
|
||||
};
|
||||
|
||||
@@ -8,8 +8,9 @@ import argon2 from 'argon2';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import database from '../database';
|
||||
import { clone, isObject } from 'lodash';
|
||||
import { File, Relation, Item } from '../types';
|
||||
import { Relation, Item } from '../types';
|
||||
import * as ItemsService from './items';
|
||||
import { URL } from 'url';
|
||||
|
||||
type Operation = 'create' | 'read' | 'update';
|
||||
|
||||
@@ -41,7 +42,7 @@ const transformers: Transformers = {
|
||||
|
||||
return value;
|
||||
},
|
||||
async 'file-links'(operation, value, payload: File) {
|
||||
async 'file-links'(operation, value, payload) {
|
||||
if (operation === 'read' && payload && payload.storage && payload.filename_disk) {
|
||||
const publicKey = `STORAGE_${payload.storage.toUpperCase()}_PUBLIC_URL`;
|
||||
|
||||
@@ -64,16 +65,23 @@ const transformers: Transformers = {
|
||||
* @param payload The actual payload itself
|
||||
* @returns The updated payload
|
||||
*/
|
||||
export const processValues = async (
|
||||
|
||||
export async function processValues(
|
||||
operation: Operation,
|
||||
collection: string,
|
||||
payload: Partial<Item>
|
||||
): Promise<Partial<Item>>;
|
||||
export async function processValues(
|
||||
operation: Operation,
|
||||
collection: string,
|
||||
payload: Partial<Item>[]
|
||||
): Promise<Partial<Item>[]>;
|
||||
export async function processValues(
|
||||
operation: Operation,
|
||||
collection: string,
|
||||
payload: Partial<Item> | Partial<Item>[]
|
||||
) => {
|
||||
let processedPayload = clone(payload);
|
||||
|
||||
if (Array.isArray(payload) === false) {
|
||||
processedPayload = [processedPayload];
|
||||
}
|
||||
): Promise<Partial<Item> | Partial<Item>[]> {
|
||||
const processedPayload = Array.isArray(payload) ? clone(payload) : [clone(payload)];
|
||||
|
||||
const specialFieldsInCollection = await database
|
||||
.select('field', 'special')
|
||||
@@ -82,7 +90,7 @@ export const processValues = async (
|
||||
.whereNotNull('special');
|
||||
|
||||
await Promise.all(
|
||||
processedPayload.map(async (record: any) => {
|
||||
payload.map(async (record: any) => {
|
||||
await Promise.all(
|
||||
specialFieldsInCollection.map(async (field) => {
|
||||
record[field.field] = await processField(field, record, operation);
|
||||
@@ -91,26 +99,20 @@ export const processValues = async (
|
||||
})
|
||||
);
|
||||
|
||||
/** @TODO
|
||||
*
|
||||
* - Make config.ts file in root
|
||||
* - Have it cache settings / env vars a la graphql/dataloader (memory-cache)
|
||||
* - Have it have a function to reload env vars
|
||||
*/
|
||||
|
||||
// Return the payload in it's original format
|
||||
if (Array.isArray(payload) === false) {
|
||||
return processedPayload[0];
|
||||
if (Array.isArray(payload)) {
|
||||
return processedPayload;
|
||||
}
|
||||
|
||||
return processedPayload;
|
||||
};
|
||||
return processedPayload[0];
|
||||
}
|
||||
|
||||
async function processField(
|
||||
field: Pick<System, 'field' | 'special'>,
|
||||
payload: Partial<Item>,
|
||||
operation: Operation
|
||||
) {
|
||||
if (!field.special) return payload[field.field];
|
||||
|
||||
if (transformers.hasOwnProperty(field.special)) {
|
||||
return await transformers[field.special](operation, payload[field.field], payload);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ export const processAST = async (
|
||||
permissionsForCollections.find(
|
||||
(permission) => permission.collection === collection
|
||||
) === undefined
|
||||
);
|
||||
)!;
|
||||
|
||||
if (field) {
|
||||
throw new ForbiddenException(
|
||||
@@ -96,7 +96,9 @@ export const processAST = async (
|
||||
/**
|
||||
* Traverses the AST and returns an array of all collections that are being fetched
|
||||
*/
|
||||
function getCollectionsFromAST(ast: AST | NestedCollectionAST) {
|
||||
function getCollectionsFromAST(
|
||||
ast: AST | NestedCollectionAST
|
||||
): { collection: string; field: string }[] {
|
||||
const collections = [];
|
||||
|
||||
if (ast.type === 'collection') {
|
||||
@@ -114,16 +116,19 @@ export const processAST = async (
|
||||
}
|
||||
}
|
||||
|
||||
return collections;
|
||||
return collections as { collection: string; field: string }[];
|
||||
}
|
||||
|
||||
function validateFields(ast: AST | NestedCollectionAST) {
|
||||
if (ast.type === 'collection') {
|
||||
const collection = ast.name;
|
||||
|
||||
// We check the availability of the permissions in the step before this is run
|
||||
const permissions = permissionsForCollections.find(
|
||||
(permission) => permission.collection === collection
|
||||
);
|
||||
const allowedFields = permissions.fields.split(',');
|
||||
)!;
|
||||
|
||||
const allowedFields = permissions.fields?.split(',') || [];
|
||||
|
||||
for (const childAST of ast.children) {
|
||||
if (childAST.type === 'collection') {
|
||||
@@ -150,9 +155,10 @@ export const processAST = async (
|
||||
if (ast.type === 'collection') {
|
||||
const collection = ast.name;
|
||||
|
||||
// We check the availability of the permissions in the step before this is run
|
||||
const permissions = permissionsForCollections.find(
|
||||
(permission) => permission.collection === collection
|
||||
);
|
||||
)!;
|
||||
|
||||
ast.query = {
|
||||
...ast.query,
|
||||
@@ -162,7 +168,7 @@ export const processAST = async (
|
||||
},
|
||||
};
|
||||
|
||||
if (permissions.limit && ast.query.limit > permissions.limit) {
|
||||
if (permissions.limit && ast.query.limit && ast.query.limit > permissions.limit) {
|
||||
throw new ForbiddenException(
|
||||
`You can't read more than ${permissions.limit} items at a time.`
|
||||
);
|
||||
@@ -194,7 +200,7 @@ export const processValues = async (
|
||||
|
||||
if (!permission) throw new ForbiddenException();
|
||||
|
||||
const allowedFields = permission.fields.split(',');
|
||||
const allowedFields = permission.fields?.split(',') || [];
|
||||
|
||||
if (allowedFields.includes('*') === false) {
|
||||
const keysInData = Object.keys(data);
|
||||
@@ -229,7 +235,7 @@ export const checkAccess = async (
|
||||
operation: Operation,
|
||||
collection: string,
|
||||
pk: string | number,
|
||||
role: string
|
||||
role: string | null
|
||||
) => {
|
||||
try {
|
||||
const query: Query = {
|
||||
|
||||
@@ -10,13 +10,13 @@ export const createUser = async (data: Partial<Item>, accountability: Accountabi
|
||||
return await ItemsService.createItem('directus_users', data, accountability);
|
||||
};
|
||||
|
||||
export const readUsers = async (query?: Query, accountability?: Accountability) => {
|
||||
export const readUsers = async (query: Query, accountability?: Accountability) => {
|
||||
return await ItemsService.readItems('directus_users', query, accountability);
|
||||
};
|
||||
|
||||
export const readUser = async (
|
||||
pk: string | number,
|
||||
query?: Query,
|
||||
query: Query,
|
||||
accountability?: Accountability
|
||||
) => {
|
||||
return await ItemsService.readItem('directus_users', pk, query, accountability);
|
||||
@@ -44,14 +44,14 @@ export const inviteUser = async (email: string, role: string, accountability: Ac
|
||||
await createUser({ email, role, status: 'invited' }, accountability);
|
||||
|
||||
const payload = { email };
|
||||
const token = jwt.sign(payload, process.env.SECRET, { expiresIn: '7d' });
|
||||
const token = jwt.sign(payload, process.env.SECRET as string, { expiresIn: '7d' });
|
||||
const acceptURL = process.env.PUBLIC_URL + '/admin/accept-invite?token=' + token;
|
||||
|
||||
await sendInviteMail(email, acceptURL);
|
||||
};
|
||||
|
||||
export const acceptInvite = async (token: string, password: string) => {
|
||||
const { email } = jwt.verify(token, process.env.SECRET) as { email: string };
|
||||
const { email } = jwt.verify(token, process.env.SECRET as string) as { email: string };
|
||||
const user = await database
|
||||
.select('id', 'status')
|
||||
.from('directus_users')
|
||||
|
||||
@@ -47,11 +47,14 @@ function registerDrivers(storage: StorageManager) {
|
||||
|
||||
for (const [key, value] of Object.entries(process.env)) {
|
||||
if ((key.startsWith('STORAGE') && key.endsWith('DRIVER')) === false) continue;
|
||||
if (usedDrivers.includes(value) === false) usedDrivers.push(value);
|
||||
if (value && usedDrivers.includes(value) === false) usedDrivers.push(value);
|
||||
}
|
||||
|
||||
usedDrivers.forEach((driver) => {
|
||||
storage.registerDriver<Storage>(driver, getStorageDriver(driver));
|
||||
const storageDriver = getStorageDriver(driver);
|
||||
if (storageDriver) {
|
||||
storage.registerDriver<Storage>(driver, storageDriver);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export type Accountability = {
|
||||
role: string;
|
||||
user?: string;
|
||||
role: string | null;
|
||||
user?: string | null;
|
||||
admin?: boolean;
|
||||
|
||||
ip?: string;
|
||||
|
||||
8
src/types/express.d.ts
vendored
8
src/types/express.d.ts
vendored
@@ -10,12 +10,12 @@ export {};
|
||||
declare global {
|
||||
namespace Express {
|
||||
export interface Request {
|
||||
token: string;
|
||||
user: string;
|
||||
token: string | null;
|
||||
user: string | null;
|
||||
role: string | null;
|
||||
collection: string;
|
||||
admin: boolean;
|
||||
collection?: string;
|
||||
sanitizedQuery?: Query;
|
||||
sanitizedQuery: Query;
|
||||
single?: boolean;
|
||||
permissions?: Permission;
|
||||
}
|
||||
|
||||
@@ -4,3 +4,5 @@
|
||||
*/
|
||||
|
||||
export type Item = Record<string, any>;
|
||||
|
||||
export type PrimaryKey = string | number;
|
||||
|
||||
@@ -16,7 +16,7 @@ import database from '../database';
|
||||
export default async function getASTFromQuery(
|
||||
collection: string,
|
||||
query: Query,
|
||||
accountability: Accountability | null,
|
||||
accountability?: Accountability,
|
||||
operation?: Operation
|
||||
): Promise<AST> {
|
||||
/**
|
||||
@@ -108,7 +108,7 @@ export default async function getASTFromQuery(
|
||||
function parseFields(parentCollection: string, fields: string[]) {
|
||||
fields = convertWildcards(parentCollection, fields);
|
||||
|
||||
if (!fields) return null;
|
||||
if (!fields) return [];
|
||||
|
||||
const children: (NestedCollectionAST | FieldAST)[] = [];
|
||||
|
||||
@@ -132,13 +132,19 @@ export default async function getASTFromQuery(
|
||||
for (const [relationalField, nestedFields] of Object.entries(relationalStructure)) {
|
||||
const relatedCollection = getRelatedCollection(parentCollection, relationalField);
|
||||
|
||||
if (!relatedCollection) continue;
|
||||
|
||||
const relation = getRelation(parentCollection, relationalField);
|
||||
|
||||
if (!relation) continue;
|
||||
|
||||
const child: NestedCollectionAST = {
|
||||
type: 'collection',
|
||||
name: relatedCollection,
|
||||
fieldKey: relationalField,
|
||||
parentKey: 'id' /** @todo this needs to come from somewhere real */,
|
||||
relation: getRelation(parentCollection, relationalField),
|
||||
query: {} /** @todo inject nested query here */,
|
||||
relation: relation,
|
||||
query: {} /** @todo inject nested query here: ?deep[foo]=bar */,
|
||||
children: parseFields(relatedCollection, nestedFields).filter(
|
||||
filterEmptyChildCollections
|
||||
),
|
||||
@@ -175,7 +181,7 @@ export default async function getASTFromQuery(
|
||||
}
|
||||
}
|
||||
|
||||
function filterEmptyChildCollections(childAST: NestedCollectionAST) {
|
||||
function filterEmptyChildCollections(childAST: FieldAST | NestedCollectionAST) {
|
||||
if (childAST.type === 'collection' && childAST.children.length === 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { get } from 'lodash';
|
||||
// The path in JSON to fetch the email address from the profile.
|
||||
// Note: a lot of services use `email` as the path. We fall back to that as default, so no need to
|
||||
// map it here
|
||||
const profileMap = {};
|
||||
const profileMap: Record<string, string> = {};
|
||||
|
||||
/**
|
||||
* Extract the email address from a given user profile coming from a providers API
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
* Reads the environment variables to construct the configuration object required by Grant
|
||||
*/
|
||||
export default function getGrantConfig() {
|
||||
const enabledProviders = process.env.OAUTH_PROVIDERS.split(',').map((provider) =>
|
||||
provider.trim()
|
||||
);
|
||||
const enabledProviders = (process.env.OAUTH_PROVIDERS as string)
|
||||
.split(',')
|
||||
.map((provider) => provider.trim());
|
||||
|
||||
const config: any = {
|
||||
defaults: {
|
||||
|
||||
@@ -16,7 +16,7 @@ const IPTC_ENTRY_MARKER = Buffer.from([0x1c, 0x02]);
|
||||
export default function parseIPTC(buffer: Buffer) {
|
||||
if (!Buffer.isBuffer(buffer)) return {};
|
||||
|
||||
let iptc = {};
|
||||
let iptc: Record<string, any> = {};
|
||||
let lastIptcEntryPos = buffer.indexOf(IPTC_ENTRY_MARKER);
|
||||
|
||||
while (lastIptcEntryPos !== -1) {
|
||||
@@ -39,12 +39,14 @@ export default function parseIPTC(buffer: Buffer) {
|
||||
let iptcBlockTypeId = IPTC_ENTRY_TYPES.get(iptcBlockType);
|
||||
let iptcData = buffer.slice(iptcBlockDataPos, iptcBlockDataPos + iptcBlockSize).toString();
|
||||
|
||||
if (iptc[iptcBlockTypeId] == null) {
|
||||
iptc[iptcBlockTypeId] = iptcData;
|
||||
} else if (Array.isArray(iptc[iptcBlockTypeId])) {
|
||||
iptc[iptcBlockTypeId].push(iptcData);
|
||||
} else {
|
||||
iptc[iptcBlockTypeId] = [iptc[iptcBlockTypeId], iptcData];
|
||||
if (iptcBlockTypeId) {
|
||||
if (iptc[iptcBlockTypeId] == null) {
|
||||
iptc[iptcBlockTypeId] = iptcData;
|
||||
} else if (Array.isArray(iptc[iptcBlockTypeId])) {
|
||||
iptc[iptcBlockTypeId].push(iptcData);
|
||||
} else {
|
||||
iptc[iptcBlockTypeId] = [iptc[iptcBlockTypeId], iptcData];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user