mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
TS Config Modernization Program Part 3 of many (#17904)
* noImplicitOverride: true * noImplicitReturns: true * noPropertyAccessFromIndexSignature: true
This commit is contained in:
@@ -2,8 +2,8 @@
|
||||
export const sqlFieldFormatter = (schema: Record<string, any>, table: string) => {
|
||||
const fields = [];
|
||||
// Exclude alias fields, unable to selected in DB
|
||||
for (const field of Object.keys(schema.collections[table].fields)) {
|
||||
if (schema.collections[table].fields[field].type !== 'alias') {
|
||||
for (const field of Object.keys(schema['collections'][table].fields)) {
|
||||
if (schema['collections'][table].fields[field].type !== 'alias') {
|
||||
fields.push(field);
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,8 @@ export const sqlFieldFormatter = (schema: Record<string, any>, table: string) =>
|
||||
export const sqlFieldList = (schema: Record<string, any>, table: string) => {
|
||||
const fields = [];
|
||||
// Exclude alias fields, unable to selected in DB
|
||||
for (const field of Object.keys(schema.collections[table].fields)) {
|
||||
if (schema.collections[table].fields[field].type !== 'alias') {
|
||||
for (const field of Object.keys(schema['collections'][table].fields)) {
|
||||
if (schema['collections'][table].fields[field].type !== 'alias') {
|
||||
fields.push(field);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ export default async function createApp(): Promise<express.Application> {
|
||||
|
||||
validateEnv(['KEY', 'SECRET']);
|
||||
|
||||
if (!new Url(env.PUBLIC_URL).isAbsolute()) {
|
||||
if (!new Url(env['PUBLIC_URL']).isAbsolute()) {
|
||||
logger.warn('PUBLIC_URL should be a full URL');
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ export default async function createApp(): Promise<express.Application> {
|
||||
const app = express();
|
||||
|
||||
app.disable('x-powered-by');
|
||||
app.set('trust proxy', env.IP_TRUST_PROXY);
|
||||
app.set('trust proxy', env['IP_TRUST_PROXY']);
|
||||
app.set('query parser', (str: string) => qs.parse(str, { depth: 10 }));
|
||||
|
||||
app.use(
|
||||
@@ -128,7 +128,7 @@ export default async function createApp(): Promise<express.Application> {
|
||||
)
|
||||
);
|
||||
|
||||
if (env.HSTS_ENABLED) {
|
||||
if (env['HSTS_ENABLED']) {
|
||||
app.use(helmet.hsts(getConfigFromEnv('HSTS_', ['HSTS_ENABLED'])));
|
||||
}
|
||||
|
||||
@@ -143,14 +143,14 @@ export default async function createApp(): Promise<express.Application> {
|
||||
next();
|
||||
});
|
||||
|
||||
if (env.CORS_ENABLED === true) {
|
||||
if (env['CORS_ENABLED'] === true) {
|
||||
app.use(cors);
|
||||
}
|
||||
|
||||
app.use((req, res, next) => {
|
||||
(
|
||||
express.json({
|
||||
limit: env.MAX_PAYLOAD_SIZE,
|
||||
limit: env['MAX_PAYLOAD_SIZE'],
|
||||
}) as RequestHandler
|
||||
)(req, res, (err: any) => {
|
||||
if (err) {
|
||||
@@ -166,8 +166,8 @@ export default async function createApp(): Promise<express.Application> {
|
||||
app.use(extractToken);
|
||||
|
||||
app.get('/', (_req, res, next) => {
|
||||
if (env.ROOT_REDIRECT) {
|
||||
res.redirect(env.ROOT_REDIRECT);
|
||||
if (env['ROOT_REDIRECT']) {
|
||||
res.redirect(env['ROOT_REDIRECT']);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
@@ -176,12 +176,12 @@ export default async function createApp(): Promise<express.Application> {
|
||||
app.get('/robots.txt', (_, res) => {
|
||||
res.set('Content-Type', 'text/plain');
|
||||
res.status(200);
|
||||
res.send(env.ROBOTS_TXT);
|
||||
res.send(env['ROBOTS_TXT']);
|
||||
});
|
||||
|
||||
if (env.SERVE_APP) {
|
||||
if (env['SERVE_APP']) {
|
||||
const adminPath = require.resolve('@directus/app');
|
||||
const adminUrl = new Url(env.PUBLIC_URL).addPath('admin');
|
||||
const adminUrl = new Url(env['PUBLIC_URL']).addPath('admin');
|
||||
|
||||
const embeds = extensionManager.getEmbeds();
|
||||
|
||||
@@ -209,10 +209,10 @@ export default async function createApp(): Promise<express.Application> {
|
||||
}
|
||||
|
||||
// use the rate limiter - all routes for now
|
||||
if (env.RATE_LIMITER_GLOBAL_ENABLED === true) {
|
||||
if (env['RATE_LIMITER_GLOBAL_ENABLED'] === true) {
|
||||
app.use(rateLimiterGlobal);
|
||||
}
|
||||
if (env.RATE_LIMITER_ENABLED === true) {
|
||||
if (env['RATE_LIMITER_ENABLED'] === true) {
|
||||
app.use(rateLimiter);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { AuthDriverOptions } from './types';
|
||||
import { getConfigFromEnv } from './utils/get-config-from-env';
|
||||
import { getSchema } from './utils/get-schema';
|
||||
|
||||
const providerNames = toArray(env.AUTH_PROVIDERS);
|
||||
const providerNames = toArray(env['AUTH_PROVIDERS']);
|
||||
|
||||
const providers: Map<string, AuthDriver> = new Map();
|
||||
|
||||
@@ -26,12 +26,12 @@ export async function registerAuthProviders(): Promise<void> {
|
||||
const options = { knex: getDatabase(), schema: await getSchema() };
|
||||
|
||||
// Register default provider if not disabled
|
||||
if (!env.AUTH_DISABLE_DEFAULT) {
|
||||
if (!env['AUTH_DISABLE_DEFAULT']) {
|
||||
const defaultProvider = getProviderInstance('local', options)!;
|
||||
providers.set(DEFAULT_AUTH_PROVIDER, defaultProvider);
|
||||
}
|
||||
|
||||
if (!env.AUTH_PROVIDERS) {
|
||||
if (!env['AUTH_PROVIDERS']) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -83,4 +83,6 @@ function getProviderInstance(
|
||||
case 'saml':
|
||||
return new SAMLAuthDriver(options, config);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -62,12 +62,12 @@ export class LDAPAuthDriver extends AuthDriver {
|
||||
bindPassword === undefined ||
|
||||
!userDn ||
|
||||
!provider ||
|
||||
(!clientUrl && !config.client?.socketPath)
|
||||
(!clientUrl && !config['client']?.socketPath)
|
||||
) {
|
||||
throw new InvalidConfigException('Invalid provider config', { provider });
|
||||
}
|
||||
|
||||
const clientConfig = typeof config.client === 'object' ? config.client : {};
|
||||
const clientConfig = typeof config['client'] === 'object' ? config['client'] : {};
|
||||
|
||||
this.bindClient = ldap.createClient({ url: clientUrl, reconnect: true, ...clientConfig });
|
||||
this.bindClient.on('error', (err: Error) => {
|
||||
@@ -147,8 +147,8 @@ export class LDAPAuthDriver extends AuthDriver {
|
||||
|
||||
res.on('searchEntry', ({ object }: SearchEntry) => {
|
||||
const user: UserInfo = {
|
||||
dn: object.dn,
|
||||
userAccountControl: Number(getEntryValue(object.userAccountControl) ?? 0),
|
||||
dn: object['dn'],
|
||||
userAccountControl: Number(getEntryValue(object['userAccountControl']) ?? 0),
|
||||
};
|
||||
|
||||
const firstName = getEntryValue(object[firstNameAttribute]);
|
||||
@@ -160,7 +160,7 @@ export class LDAPAuthDriver extends AuthDriver {
|
||||
const email = getEntryValue(object[mailAttribute]);
|
||||
if (email) user.email = email;
|
||||
|
||||
const uid = getEntryValue(object.uid);
|
||||
const uid = getEntryValue(object['uid']);
|
||||
if (uid) user.uid = uid;
|
||||
|
||||
resolve(user);
|
||||
@@ -197,10 +197,10 @@ export class LDAPAuthDriver extends AuthDriver {
|
||||
}
|
||||
|
||||
res.on('searchEntry', ({ object }: SearchEntry) => {
|
||||
if (typeof object.cn === 'object') {
|
||||
userGroups = [...userGroups, ...object.cn];
|
||||
} else if (object.cn) {
|
||||
userGroups.push(object.cn);
|
||||
if (typeof object['cn'] === 'object') {
|
||||
userGroups = [...userGroups, ...object['cn']];
|
||||
} else if (object['cn']) {
|
||||
userGroups.push(object['cn']);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -227,7 +227,7 @@ export class LDAPAuthDriver extends AuthDriver {
|
||||
}
|
||||
|
||||
async getUserID(payload: Record<string, any>): Promise<string> {
|
||||
if (!payload.identifier) {
|
||||
if (!payload['identifier']) {
|
||||
throw new InvalidCredentialsException();
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ export class LDAPAuthDriver extends AuthDriver {
|
||||
userDn,
|
||||
new EqualityFilter({
|
||||
attribute: userAttribute ?? 'cn',
|
||||
value: payload.identifier,
|
||||
value: payload['identifier'],
|
||||
}),
|
||||
userScope ?? 'one'
|
||||
);
|
||||
@@ -288,7 +288,7 @@ export class LDAPAuthDriver extends AuthDriver {
|
||||
|
||||
try {
|
||||
await this.usersService.createOne({
|
||||
provider: this.config.provider,
|
||||
provider: this.config['provider'],
|
||||
first_name: userInfo.firstName,
|
||||
last_name: userInfo.lastName,
|
||||
email: userInfo.email,
|
||||
@@ -312,9 +312,9 @@ export class LDAPAuthDriver extends AuthDriver {
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const clientConfig = typeof this.config.client === 'object' ? this.config.client : {};
|
||||
const clientConfig = typeof this.config['client'] === 'object' ? this.config['client'] : {};
|
||||
const client = ldap.createClient({
|
||||
url: this.config.clientUrl,
|
||||
url: this.config['clientUrl'],
|
||||
...clientConfig,
|
||||
reconnect: false,
|
||||
});
|
||||
@@ -334,11 +334,11 @@ export class LDAPAuthDriver extends AuthDriver {
|
||||
});
|
||||
}
|
||||
|
||||
async login(user: User, payload: Record<string, any>): Promise<void> {
|
||||
await this.verify(user, payload.password);
|
||||
override async login(user: User, payload: Record<string, any>): Promise<void> {
|
||||
await this.verify(user, payload['password']);
|
||||
}
|
||||
|
||||
async refresh(user: User): Promise<void> {
|
||||
override async refresh(user: User): Promise<void> {
|
||||
await this.validateBindClient();
|
||||
|
||||
const userInfo = await this.fetchUserInfo(user.external_identifier!);
|
||||
@@ -415,20 +415,20 @@ export function createLDAPAuthRouter(provider: string): Router {
|
||||
} as Record<string, Record<string, any>>;
|
||||
|
||||
if (mode === 'json') {
|
||||
payload.data.refresh_token = refreshToken;
|
||||
payload['data']['refresh_token'] = refreshToken;
|
||||
}
|
||||
|
||||
if (mode === 'cookie') {
|
||||
res.cookie(env.REFRESH_TOKEN_COOKIE_NAME, refreshToken, {
|
||||
res.cookie(env['REFRESH_TOKEN_COOKIE_NAME'], refreshToken, {
|
||||
httpOnly: true,
|
||||
domain: env.REFRESH_TOKEN_COOKIE_DOMAIN,
|
||||
maxAge: getMilliseconds(env.REFRESH_TOKEN_TTL),
|
||||
secure: env.REFRESH_TOKEN_COOKIE_SECURE ?? false,
|
||||
sameSite: (env.REFRESH_TOKEN_COOKIE_SAME_SITE as 'lax' | 'strict' | 'none') || 'strict',
|
||||
domain: env['REFRESH_TOKEN_COOKIE_DOMAIN'],
|
||||
maxAge: getMilliseconds(env['REFRESH_TOKEN_TTL']),
|
||||
secure: env['REFRESH_TOKEN_COOKIE_SECURE'] ?? false,
|
||||
sameSite: (env['REFRESH_TOKEN_COOKIE_SAME_SITE'] as 'lax' | 'strict' | 'none') || 'strict',
|
||||
});
|
||||
}
|
||||
|
||||
res.locals.payload = payload;
|
||||
res.locals['payload'] = payload;
|
||||
|
||||
return next();
|
||||
}),
|
||||
|
||||
@@ -16,14 +16,14 @@ import { AuthDriver } from '../auth';
|
||||
|
||||
export class LocalAuthDriver extends AuthDriver {
|
||||
async getUserID(payload: Record<string, any>): Promise<string> {
|
||||
if (!payload.email) {
|
||||
if (!payload['email']) {
|
||||
throw new InvalidCredentialsException();
|
||||
}
|
||||
|
||||
const user = await this.knex
|
||||
.select('id')
|
||||
.from('directus_users')
|
||||
.whereRaw('LOWER(??) = ?', ['email', payload.email.toLowerCase()])
|
||||
.whereRaw('LOWER(??) = ?', ['email', payload['email'].toLowerCase()])
|
||||
.first();
|
||||
|
||||
if (!user) {
|
||||
@@ -39,8 +39,8 @@ export class LocalAuthDriver extends AuthDriver {
|
||||
}
|
||||
}
|
||||
|
||||
async login(user: User, payload: Record<string, any>): Promise<void> {
|
||||
await this.verify(user, payload.password);
|
||||
override async login(user: User, payload: Record<string, any>): Promise<void> {
|
||||
await this.verify(user, payload['password']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export function createLocalAuthRouter(provider: string): Router {
|
||||
router.post(
|
||||
'/',
|
||||
asyncHandler(async (req, res, next) => {
|
||||
const STALL_TIME = env.LOGIN_STALL_TIME;
|
||||
const STALL_TIME = env['LOGIN_STALL_TIME'];
|
||||
const timeStart = performance.now();
|
||||
|
||||
const accountability: Accountability = {
|
||||
@@ -96,14 +96,14 @@ export function createLocalAuthRouter(provider: string): Router {
|
||||
} as Record<string, Record<string, any>>;
|
||||
|
||||
if (mode === 'json') {
|
||||
payload.data.refresh_token = refreshToken;
|
||||
payload['data']['refresh_token'] = refreshToken;
|
||||
}
|
||||
|
||||
if (mode === 'cookie') {
|
||||
res.cookie(env.REFRESH_TOKEN_COOKIE_NAME, refreshToken, COOKIE_OPTIONS);
|
||||
res.cookie(env['REFRESH_TOKEN_COOKIE_NAME'], refreshToken, COOKIE_OPTIONS);
|
||||
}
|
||||
|
||||
res.locals.payload = payload;
|
||||
res.locals['payload'] = payload;
|
||||
|
||||
return next();
|
||||
}),
|
||||
|
||||
@@ -37,11 +37,11 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
|
||||
|
||||
const { authorizeUrl, accessUrl, profileUrl, clientId, clientSecret, ...additionalConfig } = config;
|
||||
|
||||
if (!authorizeUrl || !accessUrl || !profileUrl || !clientId || !clientSecret || !additionalConfig.provider) {
|
||||
throw new InvalidConfigException('Invalid provider config', { provider: additionalConfig.provider });
|
||||
if (!authorizeUrl || !accessUrl || !profileUrl || !clientId || !clientSecret || !additionalConfig['provider']) {
|
||||
throw new InvalidConfigException('Invalid provider config', { provider: additionalConfig['provider'] });
|
||||
}
|
||||
|
||||
const redirectUrl = new Url(env.PUBLIC_URL).addPath('auth', 'login', additionalConfig.provider, 'callback');
|
||||
const redirectUrl = new Url(env['PUBLIC_URL']).addPath('auth', 'login', additionalConfig['provider'], 'callback');
|
||||
|
||||
this.redirectUrl = redirectUrl.toString();
|
||||
this.usersService = new UsersService({ knex: this.knex, schema: this.schema });
|
||||
@@ -51,12 +51,12 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
|
||||
authorization_endpoint: authorizeUrl,
|
||||
token_endpoint: accessUrl,
|
||||
userinfo_endpoint: profileUrl,
|
||||
issuer: additionalConfig.provider,
|
||||
issuer: additionalConfig['provider'],
|
||||
});
|
||||
|
||||
const clientOptionsOverrides = getConfigFromEnv(
|
||||
`AUTH_${config.provider.toUpperCase()}_CLIENT_`,
|
||||
[`AUTH_${config.provider.toUpperCase()}_CLIENT_ID`, `AUTH_${config.provider.toUpperCase()}_CLIENT_SECRET`],
|
||||
`AUTH_${config['provider'].toUpperCase()}_CLIENT_`,
|
||||
[`AUTH_${config['provider'].toUpperCase()}_CLIENT_ID`, `AUTH_${config['provider'].toUpperCase()}_CLIENT_SECRET`],
|
||||
'underscore'
|
||||
);
|
||||
|
||||
@@ -76,10 +76,10 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
|
||||
generateAuthUrl(codeVerifier: string, prompt = false): string {
|
||||
try {
|
||||
const codeChallenge = generators.codeChallenge(codeVerifier);
|
||||
const paramsConfig = typeof this.config.params === 'object' ? this.config.params : {};
|
||||
const paramsConfig = typeof this.config['params'] === 'object' ? this.config['params'] : {};
|
||||
|
||||
return this.client.authorizationUrl({
|
||||
scope: this.config.scope ?? 'email',
|
||||
scope: this.config['scope'] ?? 'email',
|
||||
access_type: 'offline',
|
||||
prompt: prompt ? 'consent' : undefined,
|
||||
...paramsConfig,
|
||||
@@ -103,8 +103,8 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
|
||||
return user?.id;
|
||||
}
|
||||
|
||||
async getUserID(payload: Record<string, any>): Promise<string> {
|
||||
if (!payload.code || !payload.codeVerifier || !payload.state) {
|
||||
override async getUserID(payload: Record<string, any>): Promise<string> {
|
||||
if (!payload['code'] || !payload['codeVerifier'] || !payload['state']) {
|
||||
logger.warn('[OAuth2] No code, codeVerifier or state in payload');
|
||||
throw new InvalidCredentialsException();
|
||||
}
|
||||
@@ -115,8 +115,8 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
|
||||
try {
|
||||
tokenSet = await this.client.oauthCallback(
|
||||
this.redirectUrl,
|
||||
{ code: payload.code, state: payload.state },
|
||||
{ code_verifier: payload.codeVerifier, state: generators.codeChallenge(payload.codeVerifier) }
|
||||
{ code: payload['code'], state: payload['state'] },
|
||||
{ code_verifier: payload['codeVerifier'], state: generators.codeChallenge(payload['codeVerifier']) }
|
||||
);
|
||||
userInfo = await this.client.userinfo(tokenSet.access_token!);
|
||||
} catch (e) {
|
||||
@@ -158,11 +158,11 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
|
||||
try {
|
||||
await this.usersService.createOne({
|
||||
provider,
|
||||
first_name: userInfo[this.config.firstNameKey],
|
||||
last_name: userInfo[this.config.lastNameKey],
|
||||
first_name: userInfo[this.config['firstNameKey']],
|
||||
last_name: userInfo[this.config['lastNameKey']],
|
||||
email: email,
|
||||
external_identifier: identifier,
|
||||
role: this.config.defaultRoleId,
|
||||
role: this.config['defaultRoleId'],
|
||||
auth_data: tokenSet.refresh_token && JSON.stringify({ refreshToken: tokenSet.refresh_token }),
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -176,11 +176,11 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
|
||||
return (await this.fetchUserId(identifier)) as string;
|
||||
}
|
||||
|
||||
async login(user: User): Promise<void> {
|
||||
override async login(user: User): Promise<void> {
|
||||
return this.refresh(user);
|
||||
}
|
||||
|
||||
async refresh(user: User): Promise<void> {
|
||||
override async refresh(user: User): Promise<void> {
|
||||
let authData = user.auth_data as AuthData;
|
||||
|
||||
if (typeof authData === 'string') {
|
||||
@@ -191,9 +191,9 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
|
||||
}
|
||||
}
|
||||
|
||||
if (authData?.refreshToken) {
|
||||
if (authData?.['refreshToken']) {
|
||||
try {
|
||||
const tokenSet = await this.client.refresh(authData.refreshToken);
|
||||
const tokenSet = await this.client.refresh(authData['refreshToken']);
|
||||
// Update user refreshToken if provided
|
||||
if (tokenSet.refresh_token) {
|
||||
await this.usersService.updateOne(user.id, {
|
||||
@@ -239,11 +239,15 @@ export function createOAuth2AuthRouter(providerName: string): Router {
|
||||
(req, res) => {
|
||||
const provider = getAuthProvider(providerName) as OAuth2AuthDriver;
|
||||
const codeVerifier = provider.generateCodeVerifier();
|
||||
const prompt = !!req.query.prompt;
|
||||
const token = jwt.sign({ verifier: codeVerifier, redirect: req.query.redirect, prompt }, env.SECRET as string, {
|
||||
expiresIn: '5m',
|
||||
issuer: 'directus',
|
||||
});
|
||||
const prompt = !!req.query['prompt'];
|
||||
const token = jwt.sign(
|
||||
{ verifier: codeVerifier, redirect: req.query['redirect'], prompt },
|
||||
env['SECRET'] as string,
|
||||
{
|
||||
expiresIn: '5m',
|
||||
issuer: 'directus',
|
||||
}
|
||||
);
|
||||
|
||||
res.cookie(`oauth2.${providerName}`, token, {
|
||||
httpOnly: true,
|
||||
@@ -269,7 +273,9 @@ export function createOAuth2AuthRouter(providerName: string): Router {
|
||||
let tokenData;
|
||||
|
||||
try {
|
||||
tokenData = jwt.verify(req.cookies[`oauth2.${providerName}`], env.SECRET as string, { issuer: 'directus' }) as {
|
||||
tokenData = jwt.verify(req.cookies[`oauth2.${providerName}`], env['SECRET'] as string, {
|
||||
issuer: 'directus',
|
||||
}) as {
|
||||
verifier: string;
|
||||
redirect?: string;
|
||||
prompt: boolean;
|
||||
@@ -302,9 +308,9 @@ export function createOAuth2AuthRouter(providerName: string): Router {
|
||||
try {
|
||||
res.clearCookie(`oauth2.${providerName}`);
|
||||
authResponse = await authenticationService.login(providerName, {
|
||||
code: req.query.code,
|
||||
code: req.query['code'],
|
||||
codeVerifier: verifier,
|
||||
state: req.query.state,
|
||||
state: req.query['state'],
|
||||
});
|
||||
} catch (error: any) {
|
||||
// Prompt user for a new refresh_token if invalidated
|
||||
@@ -331,18 +337,18 @@ export function createOAuth2AuthRouter(providerName: string): Router {
|
||||
const { accessToken, refreshToken, expires } = authResponse;
|
||||
|
||||
if (redirect) {
|
||||
res.cookie(env.REFRESH_TOKEN_COOKIE_NAME, refreshToken, {
|
||||
res.cookie(env['REFRESH_TOKEN_COOKIE_NAME'], refreshToken, {
|
||||
httpOnly: true,
|
||||
domain: env.REFRESH_TOKEN_COOKIE_DOMAIN,
|
||||
maxAge: getMilliseconds(env.REFRESH_TOKEN_TTL),
|
||||
secure: env.REFRESH_TOKEN_COOKIE_SECURE ?? false,
|
||||
sameSite: (env.REFRESH_TOKEN_COOKIE_SAME_SITE as 'lax' | 'strict' | 'none') || 'strict',
|
||||
domain: env['REFRESH_TOKEN_COOKIE_DOMAIN'],
|
||||
maxAge: getMilliseconds(env['REFRESH_TOKEN_TTL']),
|
||||
secure: env['REFRESH_TOKEN_COOKIE_SECURE'] ?? false,
|
||||
sameSite: (env['REFRESH_TOKEN_COOKIE_SAME_SITE'] as 'lax' | 'strict' | 'none') || 'strict',
|
||||
});
|
||||
|
||||
return res.redirect(redirect);
|
||||
}
|
||||
|
||||
res.locals.payload = {
|
||||
res.locals['payload'] = {
|
||||
data: { access_token: accessToken, refresh_token: refreshToken, expires },
|
||||
};
|
||||
|
||||
|
||||
@@ -37,15 +37,15 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
|
||||
|
||||
const { issuerUrl, clientId, clientSecret, ...additionalConfig } = config;
|
||||
|
||||
if (!issuerUrl || !clientId || !clientSecret || !additionalConfig.provider) {
|
||||
throw new InvalidConfigException('Invalid provider config', { provider: additionalConfig.provider });
|
||||
if (!issuerUrl || !clientId || !clientSecret || !additionalConfig['provider']) {
|
||||
throw new InvalidConfigException('Invalid provider config', { provider: additionalConfig['provider'] });
|
||||
}
|
||||
|
||||
const redirectUrl = new Url(env.PUBLIC_URL).addPath('auth', 'login', additionalConfig.provider, 'callback');
|
||||
const redirectUrl = new Url(env['PUBLIC_URL']).addPath('auth', 'login', additionalConfig['provider'], 'callback');
|
||||
|
||||
const clientOptionsOverrides = getConfigFromEnv(
|
||||
`AUTH_${config.provider.toUpperCase()}_CLIENT_`,
|
||||
[`AUTH_${config.provider.toUpperCase()}_CLIENT_ID`, `AUTH_${config.provider.toUpperCase()}_CLIENT_SECRET`],
|
||||
`AUTH_${config['provider'].toUpperCase()}_CLIENT_`,
|
||||
[`AUTH_${config['provider'].toUpperCase()}_CLIENT_ID`, `AUTH_${config['provider'].toUpperCase()}_CLIENT_SECRET`],
|
||||
'underscore'
|
||||
);
|
||||
|
||||
@@ -55,11 +55,11 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
|
||||
this.client = new Promise((resolve, reject) => {
|
||||
Issuer.discover(issuerUrl)
|
||||
.then((issuer) => {
|
||||
const supportedTypes = issuer.metadata.response_types_supported as string[] | undefined;
|
||||
const supportedTypes = issuer.metadata['response_types_supported'] as string[] | undefined;
|
||||
if (!supportedTypes?.includes('code')) {
|
||||
reject(
|
||||
new InvalidConfigException('OpenID provider does not support required code flow', {
|
||||
provider: additionalConfig.provider,
|
||||
provider: additionalConfig['provider'],
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -89,10 +89,10 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
|
||||
try {
|
||||
const client = await this.client;
|
||||
const codeChallenge = generators.codeChallenge(codeVerifier);
|
||||
const paramsConfig = typeof this.config.params === 'object' ? this.config.params : {};
|
||||
const paramsConfig = typeof this.config['params'] === 'object' ? this.config['params'] : {};
|
||||
|
||||
return client.authorizationUrl({
|
||||
scope: this.config.scope ?? 'openid profile email',
|
||||
scope: this.config['scope'] ?? 'openid profile email',
|
||||
access_type: 'offline',
|
||||
prompt: prompt ? 'consent' : undefined,
|
||||
...paramsConfig,
|
||||
@@ -117,8 +117,8 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
|
||||
return user?.id;
|
||||
}
|
||||
|
||||
async getUserID(payload: Record<string, any>): Promise<string> {
|
||||
if (!payload.code || !payload.codeVerifier || !payload.state) {
|
||||
override async getUserID(payload: Record<string, any>): Promise<string> {
|
||||
if (!payload['code'] || !payload['codeVerifier'] || !payload['state']) {
|
||||
logger.warn('[OpenID] No code, codeVerifier or state in payload');
|
||||
throw new InvalidCredentialsException();
|
||||
}
|
||||
@@ -128,15 +128,15 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
|
||||
|
||||
try {
|
||||
const client = await this.client;
|
||||
const codeChallenge = generators.codeChallenge(payload.codeVerifier);
|
||||
const codeChallenge = generators.codeChallenge(payload['codeVerifier']);
|
||||
tokenSet = await client.callback(
|
||||
this.redirectUrl,
|
||||
{ code: payload.code, state: payload.state },
|
||||
{ code_verifier: payload.codeVerifier, state: codeChallenge, nonce: codeChallenge }
|
||||
{ code: payload['code'], state: payload['state'] },
|
||||
{ code_verifier: payload['codeVerifier'], state: codeChallenge, nonce: codeChallenge }
|
||||
);
|
||||
userInfo = tokenSet.claims();
|
||||
|
||||
if (client.issuer.metadata.userinfo_endpoint) {
|
||||
if (client.issuer.metadata['userinfo_endpoint']) {
|
||||
userInfo = {
|
||||
...userInfo,
|
||||
...(await client.userinfo(tokenSet.access_token!)),
|
||||
@@ -151,7 +151,7 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
|
||||
|
||||
const { provider, identifierKey, allowPublicRegistration, requireVerifiedEmail } = this.config;
|
||||
|
||||
const email = userInfo.email ? String(userInfo.email) : undefined;
|
||||
const email = userInfo['email'] ? String(userInfo['email']) : undefined;
|
||||
// Fallback to email if explicit identifier not found
|
||||
const identifier = userInfo[identifierKey ?? 'sub'] ? String(userInfo[identifierKey ?? 'sub']) : email;
|
||||
|
||||
@@ -172,7 +172,7 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
|
||||
return userId;
|
||||
}
|
||||
|
||||
const isEmailVerified = !requireVerifiedEmail || userInfo.email_verified;
|
||||
const isEmailVerified = !requireVerifiedEmail || userInfo['email_verified'];
|
||||
|
||||
// Is public registration allowed?
|
||||
if (!allowPublicRegistration || !isEmailVerified) {
|
||||
@@ -183,11 +183,11 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
|
||||
try {
|
||||
await this.usersService.createOne({
|
||||
provider,
|
||||
first_name: userInfo.given_name,
|
||||
last_name: userInfo.family_name,
|
||||
first_name: userInfo['given_name'],
|
||||
last_name: userInfo['family_name'],
|
||||
email: email,
|
||||
external_identifier: identifier,
|
||||
role: this.config.defaultRoleId,
|
||||
role: this.config['defaultRoleId'],
|
||||
auth_data: tokenSet.refresh_token && JSON.stringify({ refreshToken: tokenSet.refresh_token }),
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -201,11 +201,11 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
|
||||
return (await this.fetchUserId(identifier)) as string;
|
||||
}
|
||||
|
||||
async login(user: User): Promise<void> {
|
||||
override async login(user: User): Promise<void> {
|
||||
return this.refresh(user);
|
||||
}
|
||||
|
||||
async refresh(user: User): Promise<void> {
|
||||
override async refresh(user: User): Promise<void> {
|
||||
let authData = user.auth_data as AuthData;
|
||||
|
||||
if (typeof authData === 'string') {
|
||||
@@ -216,10 +216,10 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
|
||||
}
|
||||
}
|
||||
|
||||
if (authData?.refreshToken) {
|
||||
if (authData?.['refreshToken']) {
|
||||
try {
|
||||
const client = await this.client;
|
||||
const tokenSet = await client.refresh(authData.refreshToken);
|
||||
const tokenSet = await client.refresh(authData['refreshToken']);
|
||||
// Update user refreshToken if provided
|
||||
if (tokenSet.refresh_token) {
|
||||
await this.usersService.updateOne(user.id, {
|
||||
@@ -265,11 +265,15 @@ export function createOpenIDAuthRouter(providerName: string): Router {
|
||||
asyncHandler(async (req, res) => {
|
||||
const provider = getAuthProvider(providerName) as OpenIDAuthDriver;
|
||||
const codeVerifier = provider.generateCodeVerifier();
|
||||
const prompt = !!req.query.prompt;
|
||||
const token = jwt.sign({ verifier: codeVerifier, redirect: req.query.redirect, prompt }, env.SECRET as string, {
|
||||
expiresIn: '5m',
|
||||
issuer: 'directus',
|
||||
});
|
||||
const prompt = !!req.query['prompt'];
|
||||
const token = jwt.sign(
|
||||
{ verifier: codeVerifier, redirect: req.query['redirect'], prompt },
|
||||
env['SECRET'] as string,
|
||||
{
|
||||
expiresIn: '5m',
|
||||
issuer: 'directus',
|
||||
}
|
||||
);
|
||||
|
||||
res.cookie(`openid.${providerName}`, token, {
|
||||
httpOnly: true,
|
||||
@@ -296,7 +300,9 @@ export function createOpenIDAuthRouter(providerName: string): Router {
|
||||
let tokenData;
|
||||
|
||||
try {
|
||||
tokenData = jwt.verify(req.cookies[`openid.${providerName}`], env.SECRET as string, { issuer: 'directus' }) as {
|
||||
tokenData = jwt.verify(req.cookies[`openid.${providerName}`], env['SECRET'] as string, {
|
||||
issuer: 'directus',
|
||||
}) as {
|
||||
verifier: string;
|
||||
redirect?: string;
|
||||
prompt: boolean;
|
||||
@@ -329,9 +335,9 @@ export function createOpenIDAuthRouter(providerName: string): Router {
|
||||
try {
|
||||
res.clearCookie(`openid.${providerName}`);
|
||||
authResponse = await authenticationService.login(providerName, {
|
||||
code: req.query.code,
|
||||
code: req.query['code'],
|
||||
codeVerifier: verifier,
|
||||
state: req.query.state,
|
||||
state: req.query['state'],
|
||||
});
|
||||
} catch (error: any) {
|
||||
// Prompt user for a new refresh_token if invalidated
|
||||
@@ -360,18 +366,18 @@ export function createOpenIDAuthRouter(providerName: string): Router {
|
||||
const { accessToken, refreshToken, expires } = authResponse;
|
||||
|
||||
if (redirect) {
|
||||
res.cookie(env.REFRESH_TOKEN_COOKIE_NAME, refreshToken, {
|
||||
res.cookie(env['REFRESH_TOKEN_COOKIE_NAME'], refreshToken, {
|
||||
httpOnly: true,
|
||||
domain: env.REFRESH_TOKEN_COOKIE_DOMAIN,
|
||||
maxAge: getMilliseconds(env.REFRESH_TOKEN_TTL),
|
||||
secure: env.REFRESH_TOKEN_COOKIE_SECURE ?? false,
|
||||
sameSite: (env.REFRESH_TOKEN_COOKIE_SAME_SITE as 'lax' | 'strict' | 'none') || 'strict',
|
||||
domain: env['REFRESH_TOKEN_COOKIE_DOMAIN'],
|
||||
maxAge: getMilliseconds(env['REFRESH_TOKEN_TTL']),
|
||||
secure: env['REFRESH_TOKEN_COOKIE_SECURE'] ?? false,
|
||||
sameSite: (env['REFRESH_TOKEN_COOKIE_SAME_SITE'] as 'lax' | 'strict' | 'none') || 'strict',
|
||||
});
|
||||
|
||||
return res.redirect(redirect);
|
||||
}
|
||||
|
||||
res.locals.payload = {
|
||||
res.locals['payload'] = {
|
||||
data: { access_token: accessToken, refresh_token: refreshToken, expires },
|
||||
};
|
||||
|
||||
|
||||
@@ -30,8 +30,8 @@ export class SAMLAuthDriver extends LocalAuthDriver {
|
||||
this.config = config;
|
||||
this.usersService = new UsersService({ knex: this.knex, schema: this.schema });
|
||||
|
||||
this.sp = samlify.ServiceProvider(getConfigFromEnv(`AUTH_${config.provider.toUpperCase()}_SP`));
|
||||
this.idp = samlify.IdentityProvider(getConfigFromEnv(`AUTH_${config.provider.toUpperCase()}_IDP`));
|
||||
this.sp = samlify.ServiceProvider(getConfigFromEnv(`AUTH_${config['provider'].toUpperCase()}_SP`));
|
||||
this.idp = samlify.IdentityProvider(getConfigFromEnv(`AUTH_${config['provider'].toUpperCase()}_IDP`));
|
||||
}
|
||||
|
||||
async fetchUserID(identifier: string) {
|
||||
@@ -44,7 +44,7 @@ export class SAMLAuthDriver extends LocalAuthDriver {
|
||||
return user?.id;
|
||||
}
|
||||
|
||||
async getUserID(payload: Record<string, any>) {
|
||||
override async getUserID(payload: Record<string, any>) {
|
||||
const { provider, emailKey, identifierKey, givenNameKey, familyNameKey, allowPublicRegistration } = this.config;
|
||||
|
||||
const email = payload[emailKey ?? 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'];
|
||||
@@ -69,7 +69,7 @@ export class SAMLAuthDriver extends LocalAuthDriver {
|
||||
last_name: lastName,
|
||||
email: email,
|
||||
external_identifier: identifier.toLowerCase(),
|
||||
role: this.config.defaultRoleId,
|
||||
role: this.config['defaultRoleId'],
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof RecordNotUniqueException) {
|
||||
@@ -82,7 +82,7 @@ export class SAMLAuthDriver extends LocalAuthDriver {
|
||||
}
|
||||
|
||||
// There's no local checks to be done when the user is authenticated in the IDP
|
||||
async login(_user: User): Promise<void> {
|
||||
override async login(_user: User): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -105,8 +105,8 @@ export function createSAMLAuthRouter(providerName: string) {
|
||||
const { context: url } = await sp.createLoginRequest(idp, 'redirect');
|
||||
const parsedUrl = new URL(url);
|
||||
|
||||
if (req.query.redirect) {
|
||||
parsedUrl.searchParams.append('RelayState', req.query.redirect as string);
|
||||
if (req.query['redirect']) {
|
||||
parsedUrl.searchParams.append('RelayState', req.query['redirect'] as string);
|
||||
}
|
||||
|
||||
return res.redirect(parsedUrl.toString());
|
||||
@@ -121,12 +121,12 @@ export function createSAMLAuthRouter(providerName: string) {
|
||||
|
||||
const authService = new AuthenticationService({ accountability: req.accountability, schema: req.schema });
|
||||
|
||||
if (req.cookies[env.REFRESH_TOKEN_COOKIE_NAME]) {
|
||||
const currentRefreshToken = req.cookies[env.REFRESH_TOKEN_COOKIE_NAME];
|
||||
if (req.cookies[env['REFRESH_TOKEN_COOKIE_NAME']]) {
|
||||
const currentRefreshToken = req.cookies[env['REFRESH_TOKEN_COOKIE_NAME']];
|
||||
|
||||
if (currentRefreshToken) {
|
||||
await authService.logout(currentRefreshToken);
|
||||
res.clearCookie(env.REFRESH_TOKEN_COOKIE_NAME, COOKIE_OPTIONS);
|
||||
res.clearCookie(env['REFRESH_TOKEN_COOKIE_NAME'], COOKIE_OPTIONS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ export function createSAMLAuthRouter(providerName: string) {
|
||||
const authService = new AuthenticationService({ accountability: req.accountability, schema: req.schema });
|
||||
const { accessToken, refreshToken, expires } = await authService.login(providerName, extract.attributes);
|
||||
|
||||
res.locals.payload = {
|
||||
res.locals['payload'] = {
|
||||
data: {
|
||||
access_token: accessToken,
|
||||
refresh_token: refreshToken,
|
||||
@@ -156,7 +156,7 @@ export function createSAMLAuthRouter(providerName: string) {
|
||||
};
|
||||
|
||||
if (relayState) {
|
||||
res.cookie(env.REFRESH_TOKEN_COOKIE_NAME, refreshToken, COOKIE_OPTIONS);
|
||||
res.cookie(env['REFRESH_TOKEN_COOKIE_NAME'], refreshToken, COOKIE_OPTIONS);
|
||||
return res.redirect(relayState);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,16 @@ type Store = 'memory' | 'redis' | 'memcache';
|
||||
|
||||
const messenger = getMessenger();
|
||||
|
||||
if (env.MESSENGER_STORE === 'redis' && env.CACHE_STORE === 'memory' && env.CACHE_AUTO_PURGE && !messengerSubscribed) {
|
||||
if (
|
||||
env['MESSENGER_STORE'] === 'redis' &&
|
||||
env['CACHE_STORE'] === 'memory' &&
|
||||
env['CACHE_AUTO_PURGE'] &&
|
||||
!messengerSubscribed
|
||||
) {
|
||||
messengerSubscribed = true;
|
||||
|
||||
messenger.subscribe('schemaChanged', async (opts) => {
|
||||
if (cache && opts?.autoPurgeCache !== false) {
|
||||
if (cache && opts?.['autoPurgeCache'] !== false) {
|
||||
await cache.clear();
|
||||
}
|
||||
});
|
||||
@@ -37,29 +42,29 @@ export function getCache(): {
|
||||
localSchemaCache: Keyv;
|
||||
lockCache: Keyv;
|
||||
} {
|
||||
if (env.CACHE_ENABLED === true && cache === null) {
|
||||
if (env['CACHE_ENABLED'] === true && cache === null) {
|
||||
validateEnv(['CACHE_NAMESPACE', 'CACHE_TTL', 'CACHE_STORE']);
|
||||
cache = getKeyvInstance(env.CACHE_STORE, getMilliseconds(env.CACHE_TTL));
|
||||
cache = getKeyvInstance(env['CACHE_STORE'], getMilliseconds(env['CACHE_TTL']));
|
||||
cache.on('error', (err) => logger.warn(err, `[cache] ${err}`));
|
||||
}
|
||||
|
||||
if (systemCache === null) {
|
||||
systemCache = getKeyvInstance(env.CACHE_STORE, getMilliseconds(env.CACHE_SYSTEM_TTL), '_system');
|
||||
systemCache = getKeyvInstance(env['CACHE_STORE'], getMilliseconds(env['CACHE_SYSTEM_TTL']), '_system');
|
||||
systemCache.on('error', (err) => logger.warn(err, `[system-cache] ${err}`));
|
||||
}
|
||||
|
||||
if (sharedSchemaCache === null) {
|
||||
sharedSchemaCache = getKeyvInstance(env.CACHE_STORE, getMilliseconds(env.CACHE_SYSTEM_TTL), '_schema_shared');
|
||||
sharedSchemaCache = getKeyvInstance(env['CACHE_STORE'], getMilliseconds(env['CACHE_SYSTEM_TTL']), '_schema_shared');
|
||||
sharedSchemaCache.on('error', (err) => logger.warn(err, `[shared-schema-cache] ${err}`));
|
||||
}
|
||||
|
||||
if (localSchemaCache === null) {
|
||||
localSchemaCache = getKeyvInstance('memory', getMilliseconds(env.CACHE_SYSTEM_TTL), '_schema');
|
||||
localSchemaCache = getKeyvInstance('memory', getMilliseconds(env['CACHE_SYSTEM_TTL']), '_schema');
|
||||
localSchemaCache.on('error', (err) => logger.warn(err, `[schema-cache] ${err}`));
|
||||
}
|
||||
|
||||
if (lockCache === null) {
|
||||
lockCache = getKeyvInstance(env.CACHE_STORE, undefined, '_lock');
|
||||
lockCache = getKeyvInstance(env['CACHE_STORE'], undefined, '_lock');
|
||||
lockCache.on('error', (err) => logger.warn(err, `[lock-cache] ${err}`));
|
||||
}
|
||||
|
||||
@@ -156,14 +161,14 @@ function getKeyvInstance(store: Store, ttl: number | undefined, namespaceSuffix?
|
||||
|
||||
function getConfig(store: Store = 'memory', ttl: number | undefined, namespaceSuffix = ''): Options<any> {
|
||||
const config: Options<any> = {
|
||||
namespace: `${env.CACHE_NAMESPACE}${namespaceSuffix}`,
|
||||
namespace: `${env['CACHE_NAMESPACE']}${namespaceSuffix}`,
|
||||
ttl,
|
||||
};
|
||||
|
||||
if (store === 'redis') {
|
||||
const KeyvRedis = require('@keyv/redis');
|
||||
|
||||
config.store = new KeyvRedis(env.CACHE_REDIS || getConfigFromEnv('CACHE_REDIS_'));
|
||||
config.store = new KeyvRedis(env['CACHE_REDIS'] || getConfigFromEnv('CACHE_REDIS_'));
|
||||
}
|
||||
|
||||
if (store === 'memcache') {
|
||||
@@ -171,7 +176,9 @@ function getConfig(store: Store = 'memory', ttl: number | undefined, namespaceSu
|
||||
|
||||
// keyv-memcache uses memjs which only accepts a comma separated string instead of an array,
|
||||
// so we need to join array into a string when applicable. See #7986
|
||||
const cacheMemcache = Array.isArray(env.CACHE_MEMCACHE) ? env.CACHE_MEMCACHE.join(',') : env.CACHE_MEMCACHE;
|
||||
const cacheMemcache = Array.isArray(env['CACHE_MEMCACHE'])
|
||||
? env['CACHE_MEMCACHE'].join(',')
|
||||
: env['CACHE_MEMCACHE'];
|
||||
|
||||
config.store = new KeyvMemcache(cacheMemcache);
|
||||
}
|
||||
|
||||
@@ -32,9 +32,9 @@ export default async function bootstrap({ skipAdminInit }: { skipAdminInit?: boo
|
||||
logger.info('Skipping creation of default Admin user and role...');
|
||||
}
|
||||
|
||||
if (env.PROJECT_NAME && typeof env.PROJECT_NAME === 'string' && env.PROJECT_NAME.length > 0) {
|
||||
if (env['PROJECT_NAME'] && typeof env['PROJECT_NAME'] === 'string' && env['PROJECT_NAME'].length > 0) {
|
||||
const settingsService = new SettingsService({ schema });
|
||||
await settingsService.upsertSingleton({ project_name: env.PROJECT_NAME });
|
||||
await settingsService.upsertSingleton({ project_name: env['PROJECT_NAME'] });
|
||||
}
|
||||
} else {
|
||||
logger.info('Database already initialized, skipping install');
|
||||
@@ -60,6 +60,8 @@ async function waitForDatabase(database: Knex) {
|
||||
|
||||
// This will throw and exit the process if the database is not available
|
||||
await validateDatabaseConnection(database);
|
||||
|
||||
return database;
|
||||
}
|
||||
|
||||
async function createDefaultAdmin(schema: SchemaOverview) {
|
||||
@@ -72,14 +74,14 @@ async function createDefaultAdmin(schema: SchemaOverview) {
|
||||
logger.info('Adding first admin user...');
|
||||
const usersService = new UsersService({ schema });
|
||||
|
||||
let adminEmail = env.ADMIN_EMAIL;
|
||||
let adminEmail = env['ADMIN_EMAIL'];
|
||||
|
||||
if (!adminEmail) {
|
||||
logger.info('No admin email provided. Defaulting to "admin@example.com"');
|
||||
adminEmail = 'admin@example.com';
|
||||
}
|
||||
|
||||
let adminPassword = env.ADMIN_PASSWORD;
|
||||
let adminPassword = env['ADMIN_PASSWORD'];
|
||||
|
||||
if (!adminPassword) {
|
||||
adminPassword = nanoid(12);
|
||||
|
||||
@@ -35,7 +35,7 @@ export default function createDBConnection(client: Driver, credentials: Credenti
|
||||
|
||||
if (client === 'pg' || client === 'cockroachdb') {
|
||||
const { ssl } = credentials as Credentials;
|
||||
connection['ssl'] = ssl;
|
||||
connection.ssl = ssl;
|
||||
}
|
||||
|
||||
if (client === 'mssql') {
|
||||
|
||||
@@ -33,7 +33,7 @@ export default async function createEnv(
|
||||
};
|
||||
|
||||
for (const [key, value] of Object.entries(credentials)) {
|
||||
config.database[`DB_${key.toUpperCase()}`] = value;
|
||||
config['database'][`DB_${key.toUpperCase()}`] = value;
|
||||
}
|
||||
|
||||
const configAsStrings: any = {};
|
||||
|
||||
@@ -55,10 +55,10 @@ export const UUID_REGEX = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a
|
||||
|
||||
export const COOKIE_OPTIONS: CookieOptions = {
|
||||
httpOnly: true,
|
||||
domain: env.REFRESH_TOKEN_COOKIE_DOMAIN,
|
||||
maxAge: getMilliseconds(env.REFRESH_TOKEN_TTL),
|
||||
secure: env.REFRESH_TOKEN_COOKIE_SECURE ?? false,
|
||||
sameSite: (env.REFRESH_TOKEN_COOKIE_SAME_SITE as 'lax' | 'strict' | 'none') || 'strict',
|
||||
domain: env['REFRESH_TOKEN_COOKIE_DOMAIN'],
|
||||
maxAge: getMilliseconds(env['REFRESH_TOKEN_TTL']),
|
||||
secure: env['REFRESH_TOKEN_COOKIE_SECURE'] ?? false,
|
||||
sameSite: (env['REFRESH_TOKEN_COOKIE_SAME_SITE'] as 'lax' | 'strict' | 'none') || 'strict',
|
||||
};
|
||||
|
||||
export const OAS_REQUIRED_SCHEMAS = ['Diff', 'Schema', 'Query', 'x-metadata'];
|
||||
|
||||
@@ -36,7 +36,7 @@ const readHandler = asyncHandler(async (req, res, next) => {
|
||||
|
||||
const meta = await metaService.getMetaForQuery('directus_activity', req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = {
|
||||
res.locals['payload'] = {
|
||||
data: result,
|
||||
meta,
|
||||
};
|
||||
@@ -55,9 +55,9 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const record = await service.readOne(req.params.pk, req.sanitizedQuery);
|
||||
const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = {
|
||||
res.locals['payload'] = {
|
||||
data: record || null,
|
||||
};
|
||||
|
||||
@@ -98,7 +98,7 @@ router.post(
|
||||
try {
|
||||
const record = await service.readOne(primaryKey, req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = {
|
||||
res.locals['payload'] = {
|
||||
data: record || null,
|
||||
};
|
||||
} catch (error: any) {
|
||||
@@ -132,12 +132,12 @@ router.patch(
|
||||
throw new InvalidPayloadException(error.message);
|
||||
}
|
||||
|
||||
const primaryKey = await service.updateOne(req.params.pk, req.body);
|
||||
const primaryKey = await service.updateOne(req.params['pk'], req.body);
|
||||
|
||||
try {
|
||||
const record = await service.readOne(primaryKey, req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = {
|
||||
res.locals['payload'] = {
|
||||
data: record || null,
|
||||
};
|
||||
} catch (error: any) {
|
||||
@@ -165,13 +165,13 @@ router.delete(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const item = await adminService.readOne(req.params.pk, { fields: ['action'] });
|
||||
const item = await adminService.readOne(req.params['pk'], { fields: ['action'] });
|
||||
|
||||
if (!item || item.action !== 'comment') {
|
||||
if (!item || item['action'] !== 'comment') {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
|
||||
await service.deleteOne(req.params.pk);
|
||||
await service.deleteOne(req.params['pk']);
|
||||
|
||||
return next();
|
||||
}),
|
||||
|
||||
@@ -62,9 +62,9 @@ router.get(
|
||||
}
|
||||
|
||||
// Check against ASSETS_TRANSFORM_MAX_OPERATIONS
|
||||
if (transforms.length > Number(env.ASSETS_TRANSFORM_MAX_OPERATIONS)) {
|
||||
if (transforms.length > Number(env['ASSETS_TRANSFORM_MAX_OPERATIONS'])) {
|
||||
throw new InvalidQueryException(
|
||||
`"transforms" Parameter is only allowed ${env.ASSETS_TRANSFORM_MAX_OPERATIONS} transformations.`
|
||||
`"transforms" Parameter is only allowed ${env['ASSETS_TRANSFORM_MAX_OPERATIONS']} transformations.`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -77,37 +77,39 @@ router.get(
|
||||
}
|
||||
});
|
||||
|
||||
transformation.transforms = transforms;
|
||||
transformation['transforms'] = transforms;
|
||||
}
|
||||
|
||||
const systemKeys = SYSTEM_ASSET_ALLOW_LIST.map((transformation) => transformation.key!);
|
||||
const systemKeys = SYSTEM_ASSET_ALLOW_LIST.map((transformation) => transformation['key']!);
|
||||
const allKeys: string[] = [
|
||||
...systemKeys,
|
||||
...(assetSettings.storage_asset_presets || []).map((transformation: TransformationParams) => transformation.key),
|
||||
...(assetSettings.storage_asset_presets || []).map(
|
||||
(transformation: TransformationParams) => transformation['key']
|
||||
),
|
||||
];
|
||||
|
||||
// For use in the next request handler
|
||||
res.locals.shortcuts = [...SYSTEM_ASSET_ALLOW_LIST, ...(assetSettings.storage_asset_presets || [])];
|
||||
res.locals.transformation = transformation;
|
||||
res.locals['shortcuts'] = [...SYSTEM_ASSET_ALLOW_LIST, ...(assetSettings.storage_asset_presets || [])];
|
||||
res.locals['transformation'] = transformation;
|
||||
|
||||
if (
|
||||
Object.keys(transformation).length === 0 ||
|
||||
('transforms' in transformation && transformation.transforms!.length === 0)
|
||||
('transforms' in transformation && transformation['transforms']!.length === 0)
|
||||
) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (assetSettings.storage_asset_transform === 'all') {
|
||||
if (transformation.key && allKeys.includes(transformation.key as string) === false) {
|
||||
throw new InvalidQueryException(`Key "${transformation.key}" isn't configured.`);
|
||||
if (transformation['key'] && allKeys.includes(transformation['key'] as string) === false) {
|
||||
throw new InvalidQueryException(`Key "${transformation['key']}" isn't configured.`);
|
||||
}
|
||||
|
||||
return next();
|
||||
} else if (assetSettings.storage_asset_transform === 'presets') {
|
||||
if (allKeys.includes(transformation.key as string)) return next();
|
||||
if (allKeys.includes(transformation['key'] as string)) return next();
|
||||
throw new InvalidQueryException(`Only configured presets can be used in asset generation.`);
|
||||
} else {
|
||||
if (transformation.key && systemKeys.includes(transformation.key as string)) return next();
|
||||
if (transformation['key'] && systemKeys.includes(transformation['key'] as string)) return next();
|
||||
throw new InvalidQueryException(`Dynamic asset generation has been disabled for this project.`);
|
||||
}
|
||||
}),
|
||||
@@ -130,18 +132,18 @@ router.get(
|
||||
|
||||
// Return file
|
||||
asyncHandler(async (req, res) => {
|
||||
const id = req.params.pk?.substring(0, 36);
|
||||
const id = req.params['pk']?.substring(0, 36);
|
||||
|
||||
const service = new AssetsService({
|
||||
accountability: req.accountability,
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const transformation: TransformationParams | TransformationPreset = res.locals.transformation.key
|
||||
? (res.locals.shortcuts as TransformationPreset[]).find(
|
||||
(transformation) => transformation.key === res.locals.transformation.key
|
||||
const transformation: TransformationParams | TransformationPreset = res.locals['transformation'].key
|
||||
? (res.locals['shortcuts'] as TransformationPreset[]).find(
|
||||
(transformation) => transformation['key'] === res.locals['transformation'].key
|
||||
)
|
||||
: res.locals.transformation;
|
||||
: res.locals['transformation'];
|
||||
|
||||
let range: Range | undefined = undefined;
|
||||
|
||||
@@ -165,10 +167,10 @@ router.get(
|
||||
|
||||
const { stream, file, stat } = await service.getAsset(id, transformation, range);
|
||||
|
||||
res.attachment(req.params.filename ?? file.filename_download);
|
||||
res.attachment(req.params['filename'] ?? file.filename_download);
|
||||
res.setHeader('Content-Type', file.type);
|
||||
res.setHeader('Accept-Ranges', 'bytes');
|
||||
res.setHeader('Cache-Control', getCacheControlHeader(req, getMilliseconds(env.ASSETS_CACHE_TTL), false, true));
|
||||
res.setHeader('Cache-Control', getCacheControlHeader(req, getMilliseconds(env['ASSETS_CACHE_TTL']), false, true));
|
||||
|
||||
const unixTime = Date.parse(file.modified_on);
|
||||
if (!Number.isNaN(unixTime)) {
|
||||
@@ -227,6 +229,8 @@ router.get(
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return undefined;
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ for (const authProvider of authProviders) {
|
||||
router.use(`/login/${authProvider.name}`, authRouter);
|
||||
}
|
||||
|
||||
if (!env.AUTH_DISABLE_DEFAULT) {
|
||||
if (!env['AUTH_DISABLE_DEFAULT']) {
|
||||
router.use('/login', createLocalAuthRouter(DEFAULT_AUTH_PROVIDER));
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ router.post(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const currentRefreshToken = req.body.refresh_token || req.cookies[env.REFRESH_TOKEN_COOKIE_NAME];
|
||||
const currentRefreshToken = req.body.refresh_token || req.cookies[env['REFRESH_TOKEN_COOKIE_NAME']];
|
||||
|
||||
if (!currentRefreshToken) {
|
||||
throw new InvalidPayloadException(`"refresh_token" is required in either the JSON payload or Cookie`);
|
||||
@@ -92,14 +92,14 @@ router.post(
|
||||
} as Record<string, Record<string, any>>;
|
||||
|
||||
if (mode === 'json') {
|
||||
payload.data.refresh_token = refreshToken;
|
||||
payload['data']['refresh_token'] = refreshToken;
|
||||
}
|
||||
|
||||
if (mode === 'cookie') {
|
||||
res.cookie(env.REFRESH_TOKEN_COOKIE_NAME, refreshToken, COOKIE_OPTIONS);
|
||||
res.cookie(env['REFRESH_TOKEN_COOKIE_NAME'], refreshToken, COOKIE_OPTIONS);
|
||||
}
|
||||
|
||||
res.locals.payload = payload;
|
||||
res.locals['payload'] = payload;
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -124,7 +124,7 @@ router.post(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const currentRefreshToken = req.body.refresh_token || req.cookies[env.REFRESH_TOKEN_COOKIE_NAME];
|
||||
const currentRefreshToken = req.body.refresh_token || req.cookies[env['REFRESH_TOKEN_COOKIE_NAME']];
|
||||
|
||||
if (!currentRefreshToken) {
|
||||
throw new InvalidPayloadException(`"refresh_token" is required in either the JSON payload or Cookie`);
|
||||
@@ -132,12 +132,12 @@ router.post(
|
||||
|
||||
await authenticationService.logout(currentRefreshToken);
|
||||
|
||||
if (req.cookies[env.REFRESH_TOKEN_COOKIE_NAME]) {
|
||||
res.clearCookie(env.REFRESH_TOKEN_COOKIE_NAME, {
|
||||
if (req.cookies[env['REFRESH_TOKEN_COOKIE_NAME']]) {
|
||||
res.clearCookie(env['REFRESH_TOKEN_COOKIE_NAME'], {
|
||||
httpOnly: true,
|
||||
domain: env.REFRESH_TOKEN_COOKIE_DOMAIN,
|
||||
secure: env.REFRESH_TOKEN_COOKIE_SECURE ?? false,
|
||||
sameSite: (env.REFRESH_TOKEN_COOKIE_SAME_SITE as 'lax' | 'strict' | 'none') || 'strict',
|
||||
domain: env['REFRESH_TOKEN_COOKIE_DOMAIN'],
|
||||
secure: env['REFRESH_TOKEN_COOKIE_SECURE'] ?? false,
|
||||
sameSite: (env['REFRESH_TOKEN_COOKIE_SAME_SITE'] as 'lax' | 'strict' | 'none') || 'strict',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -213,9 +213,9 @@ router.post(
|
||||
router.get(
|
||||
'/',
|
||||
asyncHandler(async (req, res, next) => {
|
||||
res.locals.payload = {
|
||||
res.locals['payload'] = {
|
||||
data: getAuthProviders(),
|
||||
disableDefault: env.AUTH_DISABLE_DEFAULT,
|
||||
disableDefault: env['AUTH_DISABLE_DEFAULT'],
|
||||
};
|
||||
return next();
|
||||
}),
|
||||
|
||||
@@ -19,11 +19,11 @@ router.post(
|
||||
if (Array.isArray(req.body)) {
|
||||
const collectionKey = await collectionsService.createMany(req.body);
|
||||
const records = await collectionsService.readMany(collectionKey);
|
||||
res.locals.payload = { data: records || null };
|
||||
res.locals['payload'] = { data: records || null };
|
||||
} else {
|
||||
const collectionKey = await collectionsService.createOne(req.body);
|
||||
const record = await collectionsService.readOne(collectionKey);
|
||||
res.locals.payload = { data: record || null };
|
||||
res.locals['payload'] = { data: record || null };
|
||||
}
|
||||
|
||||
return next();
|
||||
@@ -52,7 +52,7 @@ const readHandler = asyncHandler(async (req, res, next) => {
|
||||
|
||||
const meta = await metaService.getMetaForQuery('directus_collections', {});
|
||||
|
||||
res.locals.payload = { data: result, meta };
|
||||
res.locals['payload'] = { data: result, meta };
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -67,8 +67,8 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const collection = await collectionsService.readOne(req.params.collection);
|
||||
res.locals.payload = { data: collection || null };
|
||||
const collection = await collectionsService.readOne(req.params['collection']);
|
||||
res.locals['payload'] = { data: collection || null };
|
||||
|
||||
return next();
|
||||
}),
|
||||
@@ -87,7 +87,7 @@ router.patch(
|
||||
|
||||
try {
|
||||
const collections = await collectionsService.readMany(collectionKeys);
|
||||
res.locals.payload = { data: collections || null };
|
||||
res.locals['payload'] = { data: collections || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -109,11 +109,11 @@ router.patch(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await collectionsService.updateOne(req.params.collection, req.body);
|
||||
await collectionsService.updateOne(req.params['collection'], req.body);
|
||||
|
||||
try {
|
||||
const collection = await collectionsService.readOne(req.params.collection);
|
||||
res.locals.payload = { data: collection || null };
|
||||
const collection = await collectionsService.readOne(req.params['collection']);
|
||||
res.locals['payload'] = { data: collection || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -135,7 +135,7 @@ router.delete(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await collectionsService.deleteOne(req.params.collection);
|
||||
await collectionsService.deleteOne(req.params['collection']);
|
||||
|
||||
return next();
|
||||
}),
|
||||
|
||||
@@ -33,10 +33,10 @@ router.post(
|
||||
try {
|
||||
if (Array.isArray(req.body)) {
|
||||
const items = await service.readMany(savedKeys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: items };
|
||||
res.locals['payload'] = { data: items };
|
||||
} else {
|
||||
const item = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
||||
res.locals.payload = { data: item };
|
||||
res.locals['payload'] = { data: item };
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
@@ -64,7 +64,7 @@ const readHandler = asyncHandler(async (req, res, next) => {
|
||||
const records = await service.readByQuery(req.sanitizedQuery);
|
||||
const meta = await metaService.getMetaForQuery(req.collection, req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: records || null, meta };
|
||||
res.locals['payload'] = { data: records || null, meta };
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -79,9 +79,9 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const record = await service.readOne(req.params.pk, req.sanitizedQuery);
|
||||
const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: record || null };
|
||||
res.locals['payload'] = { data: record || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -109,7 +109,7 @@ router.patch(
|
||||
|
||||
try {
|
||||
const result = await service.readMany(keys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: result };
|
||||
res.locals['payload'] = { data: result };
|
||||
} catch (error) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -131,11 +131,11 @@ router.patch(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const primaryKey = await service.updateOne(req.params.pk, req.body);
|
||||
const primaryKey = await service.updateOne(req.params['pk'], req.body);
|
||||
|
||||
try {
|
||||
const item = await service.readOne(primaryKey, req.sanitizedQuery);
|
||||
res.locals.payload = { data: item || null };
|
||||
res.locals['payload'] = { data: item || null };
|
||||
} catch (error) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -179,7 +179,7 @@ router.delete(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await service.deleteOne(req.params.pk);
|
||||
await service.deleteOne(req.params['pk']);
|
||||
|
||||
return next();
|
||||
}),
|
||||
|
||||
@@ -15,7 +15,7 @@ const router = Router();
|
||||
router.get(
|
||||
'/:type',
|
||||
asyncHandler(async (req, res, next) => {
|
||||
const type = depluralize(req.params.type as Plural<string>);
|
||||
const type = depluralize(req.params['type'] as Plural<string>);
|
||||
|
||||
if (!isIn(type, EXTENSION_TYPES)) {
|
||||
throw new RouteNotFoundException(req.path);
|
||||
@@ -25,7 +25,7 @@ router.get(
|
||||
|
||||
const extensions = extensionManager.getExtensionsList(type);
|
||||
|
||||
res.locals.payload = {
|
||||
res.locals['payload'] = {
|
||||
data: extensions,
|
||||
};
|
||||
|
||||
@@ -45,7 +45,10 @@ router.get(
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', 'application/javascript; charset=UTF-8');
|
||||
res.setHeader('Cache-Control', getCacheControlHeader(req, getMilliseconds(env.EXTENSIONS_CACHE_TTL), false, false));
|
||||
res.setHeader(
|
||||
'Cache-Control',
|
||||
getCacheControlHeader(req, getMilliseconds(env['EXTENSIONS_CACHE_TTL']), false, false)
|
||||
);
|
||||
res.setHeader('Vary', 'Origin, Cache-Control');
|
||||
res.end(extensionSource);
|
||||
})
|
||||
|
||||
@@ -23,7 +23,7 @@ router.get(
|
||||
});
|
||||
const fields = await service.readAll();
|
||||
|
||||
res.locals.payload = { data: fields || null };
|
||||
res.locals['payload'] = { data: fields || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -37,9 +37,9 @@ router.get(
|
||||
accountability: req.accountability,
|
||||
schema: req.schema,
|
||||
});
|
||||
const fields = await service.readAll(req.params.collection);
|
||||
const fields = await service.readAll(req.params['collection']);
|
||||
|
||||
res.locals.payload = { data: fields || null };
|
||||
res.locals['payload'] = { data: fields || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -54,9 +54,9 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const field = await service.readOne(req.params.collection, req.params.field);
|
||||
const field = await service.readOne(req.params['collection'], req.params['field']);
|
||||
|
||||
res.locals.payload = { data: field || null };
|
||||
res.locals['payload'] = { data: field || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -96,11 +96,11 @@ router.post(
|
||||
|
||||
const field: Partial<Field> & { field: string; type: Type | null } = req.body;
|
||||
|
||||
await service.createField(req.params.collection, field);
|
||||
await service.createField(req.params['collection'], field);
|
||||
|
||||
try {
|
||||
const createdField = await service.readOne(req.params.collection, field.field);
|
||||
res.locals.payload = { data: createdField || null };
|
||||
const createdField = await service.readOne(req.params['collection'], field.field);
|
||||
res.locals['payload'] = { data: createdField || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -128,15 +128,15 @@ router.patch(
|
||||
}
|
||||
|
||||
for (const field of req.body) {
|
||||
await service.updateField(req.params.collection, field);
|
||||
await service.updateField(req.params['collection'], field);
|
||||
}
|
||||
|
||||
try {
|
||||
const results: any = [];
|
||||
for (const field of req.body) {
|
||||
const updatedField = await service.readOne(req.params.collection, field.field);
|
||||
const updatedField = await service.readOne(req.params['collection'], field.field);
|
||||
results.push(updatedField);
|
||||
res.locals.payload = { data: results || null };
|
||||
res.locals['payload'] = { data: results || null };
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
@@ -186,13 +186,13 @@ router.patch(
|
||||
|
||||
const fieldData: Partial<Field> & { field: string; type: Type } = req.body;
|
||||
|
||||
if (!fieldData.field) fieldData.field = req.params.field;
|
||||
if (!fieldData.field) fieldData.field = req.params['field'];
|
||||
|
||||
await service.updateField(req.params.collection, fieldData);
|
||||
await service.updateField(req.params['collection'], fieldData);
|
||||
|
||||
try {
|
||||
const updatedField = await service.readOne(req.params.collection, req.params.field);
|
||||
res.locals.payload = { data: updatedField || null };
|
||||
const updatedField = await service.readOne(req.params['collection'], req.params['field']);
|
||||
res.locals['payload'] = { data: updatedField || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -214,7 +214,7 @@ router.delete(
|
||||
accountability: req.accountability,
|
||||
schema: req.schema,
|
||||
});
|
||||
await service.deleteField(req.params.collection, req.params.field);
|
||||
await service.deleteField(req.params['collection'], req.params['field']);
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
|
||||
@@ -38,7 +38,7 @@ export const multipartHandler: RequestHandler = (req, res, next) => {
|
||||
const savedFiles: PrimaryKey[] = [];
|
||||
const service = new FilesService({ accountability: req.accountability, schema: req.schema });
|
||||
|
||||
const existingPrimaryKey = req.params.pk || undefined;
|
||||
const existingPrimaryKey = req.params['pk'] || undefined;
|
||||
|
||||
/**
|
||||
* The order of the fields in multipart/form-data is important. We require that all fields
|
||||
@@ -46,7 +46,7 @@ export const multipartHandler: RequestHandler = (req, res, next) => {
|
||||
* the row in directus_files async during the upload of the actual file.
|
||||
*/
|
||||
|
||||
let disk: string = toArray(env.STORAGE_LOCATIONS)[0];
|
||||
let disk: string = toArray(env['STORAGE_LOCATIONS'])[0];
|
||||
let payload: any = {};
|
||||
let fileCount = 0;
|
||||
|
||||
@@ -95,6 +95,8 @@ export const multipartHandler: RequestHandler = (req, res, next) => {
|
||||
} catch (error: any) {
|
||||
busboy.emit('error', error);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
|
||||
busboy.on('error', (error: Error) => {
|
||||
@@ -113,7 +115,7 @@ export const multipartHandler: RequestHandler = (req, res, next) => {
|
||||
return next(new InvalidPayloadException(`No files were included in the body`));
|
||||
}
|
||||
|
||||
res.locals.savedFiles = savedFiles;
|
||||
res.locals['savedFiles'] = savedFiles;
|
||||
return next();
|
||||
}
|
||||
}
|
||||
@@ -130,7 +132,7 @@ router.post(
|
||||
let keys: PrimaryKey | PrimaryKey[] = [];
|
||||
|
||||
if (req.is('multipart/form-data')) {
|
||||
keys = res.locals.savedFiles;
|
||||
keys = res.locals['savedFiles'];
|
||||
} else {
|
||||
keys = await service.createOne(req.body);
|
||||
}
|
||||
@@ -139,14 +141,14 @@ router.post(
|
||||
if (Array.isArray(keys) && keys.length > 1) {
|
||||
const records = await service.readMany(keys, req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = {
|
||||
res.locals['payload'] = {
|
||||
data: records,
|
||||
};
|
||||
} else {
|
||||
const key = Array.isArray(keys) ? keys[0] : keys;
|
||||
const record = await service.readOne(key, req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = {
|
||||
res.locals['payload'] = {
|
||||
data: record,
|
||||
};
|
||||
}
|
||||
@@ -186,7 +188,7 @@ router.post(
|
||||
|
||||
try {
|
||||
const record = await service.readOne(primaryKey, req.sanitizedQuery);
|
||||
res.locals.payload = { data: record || null };
|
||||
res.locals['payload'] = { data: record || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -223,7 +225,7 @@ const readHandler = asyncHandler(async (req, res, next) => {
|
||||
|
||||
const meta = await metaService.getMetaForQuery('directus_files', req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: result, meta };
|
||||
res.locals['payload'] = { data: result, meta };
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -238,8 +240,8 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const record = await service.readOne(req.params.pk, req.sanitizedQuery);
|
||||
res.locals.payload = { data: record || null };
|
||||
const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
||||
res.locals['payload'] = { data: record || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -267,7 +269,7 @@ router.patch(
|
||||
|
||||
try {
|
||||
const result = await service.readMany(keys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: result || null };
|
||||
res.locals['payload'] = { data: result || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -290,11 +292,11 @@ router.patch(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await service.updateOne(req.params.pk, req.body);
|
||||
await service.updateOne(req.params['pk'], req.body);
|
||||
|
||||
try {
|
||||
const record = await service.readOne(req.params.pk, req.sanitizedQuery);
|
||||
res.locals.payload = { data: record || null };
|
||||
const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
||||
res.locals['payload'] = { data: record || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -339,7 +341,7 @@ router.delete(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await service.deleteOne(req.params.pk);
|
||||
await service.deleteOne(req.params['pk']);
|
||||
|
||||
return next();
|
||||
}),
|
||||
|
||||
@@ -18,7 +18,7 @@ const webhookFlowHandler = asyncHandler(async (req, res, next) => {
|
||||
const flowManager = getFlowManager();
|
||||
|
||||
const result = await flowManager.runWebhookFlow(
|
||||
`${req.method}-${req.params.pk}`,
|
||||
`${req.method}-${req.params['pk']}`,
|
||||
{
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
@@ -32,7 +32,7 @@ const webhookFlowHandler = asyncHandler(async (req, res, next) => {
|
||||
}
|
||||
);
|
||||
|
||||
res.locals.payload = result;
|
||||
res.locals['payload'] = result;
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -60,10 +60,10 @@ router.post(
|
||||
try {
|
||||
if (Array.isArray(req.body)) {
|
||||
const items = await service.readMany(savedKeys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: items };
|
||||
res.locals['payload'] = { data: items };
|
||||
} else {
|
||||
const item = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
||||
res.locals.payload = { data: item };
|
||||
res.locals['payload'] = { data: item };
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
@@ -91,7 +91,7 @@ const readHandler = asyncHandler(async (req, res, next) => {
|
||||
const records = await service.readByQuery(req.sanitizedQuery);
|
||||
const meta = await metaService.getMetaForQuery(req.collection, req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: records || null, meta };
|
||||
res.locals['payload'] = { data: records || null, meta };
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -106,9 +106,9 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const record = await service.readOne(req.params.pk, req.sanitizedQuery);
|
||||
const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: record || null };
|
||||
res.locals['payload'] = { data: record || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -136,7 +136,7 @@ router.patch(
|
||||
|
||||
try {
|
||||
const result = await service.readMany(keys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: result };
|
||||
res.locals['payload'] = { data: result };
|
||||
} catch (error) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -158,11 +158,11 @@ router.patch(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const primaryKey = await service.updateOne(req.params.pk, req.body);
|
||||
const primaryKey = await service.updateOne(req.params['pk'], req.body);
|
||||
|
||||
try {
|
||||
const item = await service.readOne(primaryKey, req.sanitizedQuery);
|
||||
res.locals.payload = { data: item || null };
|
||||
res.locals['payload'] = { data: item || null };
|
||||
} catch (error) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -206,7 +206,7 @@ router.delete(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await service.deleteOne(req.params.pk);
|
||||
await service.deleteOne(req.params['pk']);
|
||||
|
||||
return next();
|
||||
}),
|
||||
|
||||
@@ -33,10 +33,10 @@ router.post(
|
||||
try {
|
||||
if (Array.isArray(req.body)) {
|
||||
const records = await service.readMany(savedKeys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: records };
|
||||
res.locals['payload'] = { data: records };
|
||||
} else {
|
||||
const record = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
||||
res.locals.payload = { data: record };
|
||||
res.locals['payload'] = { data: record };
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
@@ -73,7 +73,7 @@ const readHandler = asyncHandler(async (req, res, next) => {
|
||||
|
||||
const meta = await metaService.getMetaForQuery('directus_folders', req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: result, meta };
|
||||
res.locals['payload'] = { data: result, meta };
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -87,9 +87,9 @@ router.get(
|
||||
accountability: req.accountability,
|
||||
schema: req.schema,
|
||||
});
|
||||
const record = await service.readOne(req.params.pk, req.sanitizedQuery);
|
||||
const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: record || null };
|
||||
res.locals['payload'] = { data: record || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -117,7 +117,7 @@ router.patch(
|
||||
|
||||
try {
|
||||
const result = await service.readMany(keys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: result || null };
|
||||
res.locals['payload'] = { data: result || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -139,11 +139,11 @@ router.patch(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const primaryKey = await service.updateOne(req.params.pk, req.body);
|
||||
const primaryKey = await service.updateOne(req.params['pk'], req.body);
|
||||
|
||||
try {
|
||||
const record = await service.readOne(primaryKey, req.sanitizedQuery);
|
||||
res.locals.payload = { data: record || null };
|
||||
res.locals['payload'] = { data: record || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -188,7 +188,7 @@ router.delete(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await service.deleteOne(req.params.pk);
|
||||
await service.deleteOne(req.params['pk']);
|
||||
|
||||
return next();
|
||||
}),
|
||||
|
||||
@@ -16,10 +16,10 @@ router.use(
|
||||
scope: 'system',
|
||||
});
|
||||
|
||||
res.locals.payload = await service.execute(res.locals.graphqlParams);
|
||||
res.locals['payload'] = await service.execute(res.locals['graphqlParams']);
|
||||
|
||||
if (res.locals.payload?.errors?.length > 0) {
|
||||
res.locals.cache = false;
|
||||
if (res.locals['payload']?.errors?.length > 0) {
|
||||
res.locals['cache'] = false;
|
||||
}
|
||||
|
||||
return next();
|
||||
@@ -37,10 +37,10 @@ router.use(
|
||||
scope: 'items',
|
||||
});
|
||||
|
||||
res.locals.payload = await service.execute(res.locals.graphqlParams);
|
||||
res.locals['payload'] = await service.execute(res.locals['graphqlParams']);
|
||||
|
||||
if (res.locals.payload?.errors?.length > 0) {
|
||||
res.locals.cache = false;
|
||||
if (res.locals['payload']?.errors?.length > 0) {
|
||||
res.locals['cache'] = false;
|
||||
}
|
||||
|
||||
return next();
|
||||
|
||||
@@ -14,7 +14,7 @@ router.post(
|
||||
'/:collection',
|
||||
collectionExists,
|
||||
asyncHandler(async (req, res, next) => {
|
||||
if (req.params.collection.startsWith('directus_')) throw new ForbiddenException();
|
||||
if (req.params['collection'].startsWith('directus_')) throw new ForbiddenException();
|
||||
|
||||
if (req.singleton) {
|
||||
throw new RouteNotFoundException(req.path);
|
||||
@@ -38,10 +38,10 @@ router.post(
|
||||
try {
|
||||
if (Array.isArray(req.body)) {
|
||||
const result = await service.readMany(savedKeys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: result || null };
|
||||
res.locals['payload'] = { data: result || null };
|
||||
} else {
|
||||
const result = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
||||
res.locals.payload = { data: result || null };
|
||||
res.locals['payload'] = { data: result || null };
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
@@ -57,7 +57,7 @@ router.post(
|
||||
);
|
||||
|
||||
const readHandler = asyncHandler(async (req, res, next) => {
|
||||
if (req.params.collection.startsWith('directus_')) throw new ForbiddenException();
|
||||
if (req.params['collection'].startsWith('directus_')) throw new ForbiddenException();
|
||||
|
||||
const service = new ItemsService(req.collection, {
|
||||
accountability: req.accountability,
|
||||
@@ -81,7 +81,7 @@ const readHandler = asyncHandler(async (req, res, next) => {
|
||||
|
||||
const meta = await metaService.getMetaForQuery(req.collection, req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = {
|
||||
res.locals['payload'] = {
|
||||
meta: meta,
|
||||
data: result,
|
||||
};
|
||||
@@ -96,16 +96,16 @@ router.get(
|
||||
'/:collection/:pk',
|
||||
collectionExists,
|
||||
asyncHandler(async (req, res, next) => {
|
||||
if (req.params.collection.startsWith('directus_')) throw new ForbiddenException();
|
||||
if (req.params['collection'].startsWith('directus_')) throw new ForbiddenException();
|
||||
|
||||
const service = new ItemsService(req.collection, {
|
||||
accountability: req.accountability,
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const result = await service.readOne(req.params.pk, req.sanitizedQuery);
|
||||
const result = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = {
|
||||
res.locals['payload'] = {
|
||||
data: result || null,
|
||||
};
|
||||
|
||||
@@ -119,7 +119,7 @@ router.patch(
|
||||
collectionExists,
|
||||
validateBatch('update'),
|
||||
asyncHandler(async (req, res, next) => {
|
||||
if (req.params.collection.startsWith('directus_')) throw new ForbiddenException();
|
||||
if (req.params['collection'].startsWith('directus_')) throw new ForbiddenException();
|
||||
|
||||
const service = new ItemsService(req.collection, {
|
||||
accountability: req.accountability,
|
||||
@@ -130,7 +130,7 @@ router.patch(
|
||||
await service.upsertSingleton(req.body);
|
||||
const item = await service.readSingleton(req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: item || null };
|
||||
res.locals['payload'] = { data: item || null };
|
||||
return next();
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ router.patch(
|
||||
|
||||
try {
|
||||
const result = await service.readMany(keys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: result };
|
||||
res.locals['payload'] = { data: result };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -165,7 +165,7 @@ router.patch(
|
||||
'/:collection/:pk',
|
||||
collectionExists,
|
||||
asyncHandler(async (req, res, next) => {
|
||||
if (req.params.collection.startsWith('directus_')) throw new ForbiddenException();
|
||||
if (req.params['collection'].startsWith('directus_')) throw new ForbiddenException();
|
||||
|
||||
if (req.singleton) {
|
||||
throw new RouteNotFoundException(req.path);
|
||||
@@ -176,11 +176,11 @@ router.patch(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const updatedPrimaryKey = await service.updateOne(req.params.pk, req.body);
|
||||
const updatedPrimaryKey = await service.updateOne(req.params['pk'], req.body);
|
||||
|
||||
try {
|
||||
const result = await service.readOne(updatedPrimaryKey, req.sanitizedQuery);
|
||||
res.locals.payload = { data: result || null };
|
||||
res.locals['payload'] = { data: result || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -199,7 +199,7 @@ router.delete(
|
||||
collectionExists,
|
||||
validateBatch('delete'),
|
||||
asyncHandler(async (req, res, next) => {
|
||||
if (req.params.collection.startsWith('directus_')) throw new ForbiddenException();
|
||||
if (req.params['collection'].startsWith('directus_')) throw new ForbiddenException();
|
||||
|
||||
const service = new ItemsService(req.collection, {
|
||||
accountability: req.accountability,
|
||||
@@ -224,14 +224,14 @@ router.delete(
|
||||
'/:collection/:pk',
|
||||
collectionExists,
|
||||
asyncHandler(async (req, res, next) => {
|
||||
if (req.params.collection.startsWith('directus_')) throw new ForbiddenException();
|
||||
if (req.params['collection'].startsWith('directus_')) throw new ForbiddenException();
|
||||
|
||||
const service = new ItemsService(req.collection, {
|
||||
accountability: req.accountability,
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await service.deleteOne(req.params.pk);
|
||||
await service.deleteOne(req.params['pk']);
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
|
||||
@@ -33,10 +33,10 @@ router.post(
|
||||
try {
|
||||
if (Array.isArray(req.body)) {
|
||||
const records = await service.readMany(savedKeys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: records };
|
||||
res.locals['payload'] = { data: records };
|
||||
} else {
|
||||
const record = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
||||
res.locals.payload = { data: record };
|
||||
res.locals['payload'] = { data: record };
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
@@ -73,7 +73,7 @@ const readHandler = asyncHandler(async (req, res, next) => {
|
||||
|
||||
const meta = await metaService.getMetaForQuery('directus_notifications', req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: result, meta };
|
||||
res.locals['payload'] = { data: result, meta };
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -88,9 +88,9 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const record = await service.readOne(req.params.pk, req.sanitizedQuery);
|
||||
const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: record || null };
|
||||
res.locals['payload'] = { data: record || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -118,7 +118,7 @@ router.patch(
|
||||
|
||||
try {
|
||||
const result = await service.readMany(keys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: result };
|
||||
res.locals['payload'] = { data: result };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -140,11 +140,11 @@ router.patch(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const primaryKey = await service.updateOne(req.params.pk, req.body);
|
||||
const primaryKey = await service.updateOne(req.params['pk'], req.body);
|
||||
|
||||
try {
|
||||
const record = await service.readOne(primaryKey, req.sanitizedQuery);
|
||||
res.locals.payload = { data: record };
|
||||
res.locals['payload'] = { data: record };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -189,7 +189,7 @@ router.delete(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await service.deleteOne(req.params.pk);
|
||||
await service.deleteOne(req.params['pk']);
|
||||
|
||||
return next();
|
||||
}),
|
||||
|
||||
@@ -33,10 +33,10 @@ router.post(
|
||||
try {
|
||||
if (Array.isArray(req.body)) {
|
||||
const items = await service.readMany(savedKeys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: items };
|
||||
res.locals['payload'] = { data: items };
|
||||
} else {
|
||||
const item = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
||||
res.locals.payload = { data: item };
|
||||
res.locals['payload'] = { data: item };
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
@@ -64,7 +64,7 @@ const readHandler = asyncHandler(async (req, res, next) => {
|
||||
const records = await service.readByQuery(req.sanitizedQuery);
|
||||
const meta = await metaService.getMetaForQuery(req.collection, req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: records || null, meta };
|
||||
res.locals['payload'] = { data: records || null, meta };
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -79,9 +79,9 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const record = await service.readOne(req.params.pk, req.sanitizedQuery);
|
||||
const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: record || null };
|
||||
res.locals['payload'] = { data: record || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -109,7 +109,7 @@ router.patch(
|
||||
|
||||
try {
|
||||
const result = await service.readMany(keys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: result };
|
||||
res.locals['payload'] = { data: result };
|
||||
} catch (error) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -131,11 +131,11 @@ router.patch(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const primaryKey = await service.updateOne(req.params.pk, req.body);
|
||||
const primaryKey = await service.updateOne(req.params['pk'], req.body);
|
||||
|
||||
try {
|
||||
const item = await service.readOne(primaryKey, req.sanitizedQuery);
|
||||
res.locals.payload = { data: item || null };
|
||||
res.locals['payload'] = { data: item || null };
|
||||
} catch (error) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -179,7 +179,7 @@ router.delete(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await service.deleteOne(req.params.pk);
|
||||
await service.deleteOne(req.params['pk']);
|
||||
|
||||
return next();
|
||||
}),
|
||||
|
||||
@@ -33,10 +33,10 @@ router.post(
|
||||
try {
|
||||
if (Array.isArray(req.body)) {
|
||||
const items = await service.readMany(savedKeys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: items };
|
||||
res.locals['payload'] = { data: items };
|
||||
} else {
|
||||
const item = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
||||
res.locals.payload = { data: item };
|
||||
res.locals['payload'] = { data: item };
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
@@ -64,7 +64,7 @@ const readHandler = asyncHandler(async (req, res, next) => {
|
||||
const records = await service.readByQuery(req.sanitizedQuery);
|
||||
const meta = await metaService.getMetaForQuery(req.collection, req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: records || null, meta };
|
||||
res.locals['payload'] = { data: records || null, meta };
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -79,9 +79,9 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const record = await service.readOne(req.params.pk, req.sanitizedQuery);
|
||||
const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: record || null };
|
||||
res.locals['payload'] = { data: record || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -109,7 +109,7 @@ router.patch(
|
||||
|
||||
try {
|
||||
const result = await service.readMany(keys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: result };
|
||||
res.locals['payload'] = { data: result };
|
||||
} catch (error) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -131,11 +131,11 @@ router.patch(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const primaryKey = await service.updateOne(req.params.pk, req.body);
|
||||
const primaryKey = await service.updateOne(req.params['pk'], req.body);
|
||||
|
||||
try {
|
||||
const item = await service.readOne(primaryKey, req.sanitizedQuery);
|
||||
res.locals.payload = { data: item || null };
|
||||
res.locals['payload'] = { data: item || null };
|
||||
} catch (error) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -179,7 +179,7 @@ router.delete(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await service.deleteOne(req.params.pk);
|
||||
await service.deleteOne(req.params['pk']);
|
||||
|
||||
return next();
|
||||
}),
|
||||
|
||||
@@ -33,10 +33,10 @@ router.post(
|
||||
try {
|
||||
if (Array.isArray(req.body)) {
|
||||
const items = await service.readMany(savedKeys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: items };
|
||||
res.locals['payload'] = { data: items };
|
||||
} else {
|
||||
const item = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
||||
res.locals.payload = { data: item };
|
||||
res.locals['payload'] = { data: item };
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
@@ -73,7 +73,7 @@ const readHandler = asyncHandler(async (req, res, next) => {
|
||||
|
||||
const meta = await metaService.getMetaForQuery('directus_permissions', req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: result, meta };
|
||||
res.locals['payload'] = { data: result, meta };
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -89,9 +89,9 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const record = await service.readOne(req.params.pk, req.sanitizedQuery);
|
||||
const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: record };
|
||||
res.locals['payload'] = { data: record };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -119,7 +119,7 @@ router.patch(
|
||||
|
||||
try {
|
||||
const result = await service.readMany(keys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: result };
|
||||
res.locals['payload'] = { data: result };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -141,11 +141,11 @@ router.patch(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const primaryKey = await service.updateOne(req.params.pk, req.body);
|
||||
const primaryKey = await service.updateOne(req.params['pk'], req.body);
|
||||
|
||||
try {
|
||||
const item = await service.readOne(primaryKey, req.sanitizedQuery);
|
||||
res.locals.payload = { data: item || null };
|
||||
res.locals['payload'] = { data: item || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -190,7 +190,7 @@ router.delete(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await service.deleteOne(req.params.pk);
|
||||
await service.deleteOne(req.params['pk']);
|
||||
|
||||
return next();
|
||||
}),
|
||||
|
||||
@@ -33,10 +33,10 @@ router.post(
|
||||
try {
|
||||
if (Array.isArray(req.body)) {
|
||||
const records = await service.readMany(savedKeys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: records };
|
||||
res.locals['payload'] = { data: records };
|
||||
} else {
|
||||
const record = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
||||
res.locals.payload = { data: record };
|
||||
res.locals['payload'] = { data: record };
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
@@ -73,7 +73,7 @@ const readHandler = asyncHandler(async (req, res, next) => {
|
||||
|
||||
const meta = await metaService.getMetaForQuery('directus_presets', req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: result, meta };
|
||||
res.locals['payload'] = { data: result, meta };
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -88,9 +88,9 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const record = await service.readOne(req.params.pk, req.sanitizedQuery);
|
||||
const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: record || null };
|
||||
res.locals['payload'] = { data: record || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -118,7 +118,7 @@ router.patch(
|
||||
|
||||
try {
|
||||
const result = await service.readMany(keys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: result };
|
||||
res.locals['payload'] = { data: result };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -140,11 +140,11 @@ router.patch(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const primaryKey = await service.updateOne(req.params.pk, req.body);
|
||||
const primaryKey = await service.updateOne(req.params['pk'], req.body);
|
||||
|
||||
try {
|
||||
const record = await service.readOne(primaryKey, req.sanitizedQuery);
|
||||
res.locals.payload = { data: record };
|
||||
res.locals['payload'] = { data: record };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -189,7 +189,7 @@ router.delete(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await service.deleteOne(req.params.pk);
|
||||
await service.deleteOne(req.params['pk']);
|
||||
|
||||
return next();
|
||||
}),
|
||||
|
||||
@@ -20,7 +20,7 @@ router.get(
|
||||
});
|
||||
|
||||
const relations = await service.readAll();
|
||||
res.locals.payload = { data: relations || null };
|
||||
res.locals['payload'] = { data: relations || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -34,9 +34,9 @@ router.get(
|
||||
accountability: req.accountability,
|
||||
schema: req.schema,
|
||||
});
|
||||
const relations = await service.readAll(req.params.collection);
|
||||
const relations = await service.readAll(req.params['collection']);
|
||||
|
||||
res.locals.payload = { data: relations || null };
|
||||
res.locals['payload'] = { data: relations || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -51,9 +51,9 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const relation = await service.readOne(req.params.collection, req.params.field);
|
||||
const relation = await service.readOne(req.params['collection'], req.params['field']);
|
||||
|
||||
res.locals.payload = { data: relation || null };
|
||||
res.locals['payload'] = { data: relation || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -89,7 +89,7 @@ router.post(
|
||||
|
||||
try {
|
||||
const createdRelation = await service.readOne(req.body.collection, req.body.field);
|
||||
res.locals.payload = { data: createdRelation || null };
|
||||
res.locals['payload'] = { data: createdRelation || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -130,11 +130,11 @@ router.patch(
|
||||
throw new InvalidPayloadException(error.message);
|
||||
}
|
||||
|
||||
await service.updateOne(req.params.collection, req.params.field, req.body);
|
||||
await service.updateOne(req.params['collection'], req.params['field'], req.body);
|
||||
|
||||
try {
|
||||
const updatedField = await service.readOne(req.params.collection, req.params.field);
|
||||
res.locals.payload = { data: updatedField || null };
|
||||
const updatedField = await service.readOne(req.params['collection'], req.params['field']);
|
||||
res.locals['payload'] = { data: updatedField || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -156,7 +156,7 @@ router.delete(
|
||||
accountability: req.accountability,
|
||||
schema: req.schema,
|
||||
});
|
||||
await service.deleteOne(req.params.collection, req.params.field);
|
||||
await service.deleteOne(req.params['collection'], req.params['field']);
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
|
||||
@@ -22,7 +22,7 @@ const readHandler = asyncHandler(async (req, res, next) => {
|
||||
const records = await service.readByQuery(req.sanitizedQuery);
|
||||
const meta = await metaService.getMetaForQuery('directus_revisions', req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: records || null, meta };
|
||||
res.locals['payload'] = { data: records || null, meta };
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -37,9 +37,9 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const record = await service.readOne(req.params.pk, req.sanitizedQuery);
|
||||
const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: record || null };
|
||||
res.locals['payload'] = { data: record || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
|
||||
@@ -33,10 +33,10 @@ router.post(
|
||||
try {
|
||||
if (Array.isArray(req.body)) {
|
||||
const items = await service.readMany(savedKeys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: items };
|
||||
res.locals['payload'] = { data: items };
|
||||
} else {
|
||||
const item = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
||||
res.locals.payload = { data: item };
|
||||
res.locals['payload'] = { data: item };
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
@@ -65,7 +65,7 @@ const readHandler = asyncHandler(async (req, res, next) => {
|
||||
const records = await service.readByQuery(req.sanitizedQuery);
|
||||
const meta = await metaService.getMetaForQuery('directus_roles', req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: records || null, meta };
|
||||
res.locals['payload'] = { data: records || null, meta };
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -80,9 +80,9 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const record = await service.readOne(req.params.pk, req.sanitizedQuery);
|
||||
const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: record || null };
|
||||
res.locals['payload'] = { data: record || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -110,7 +110,7 @@ router.patch(
|
||||
|
||||
try {
|
||||
const result = await service.readMany(keys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: result };
|
||||
res.locals['payload'] = { data: result };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -132,11 +132,11 @@ router.patch(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const primaryKey = await service.updateOne(req.params.pk, req.body);
|
||||
const primaryKey = await service.updateOne(req.params['pk'], req.body);
|
||||
|
||||
try {
|
||||
const item = await service.readOne(primaryKey, req.sanitizedQuery);
|
||||
res.locals.payload = { data: item || null };
|
||||
res.locals['payload'] = { data: item || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -181,7 +181,7 @@ router.delete(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await service.deleteOne(req.params.pk);
|
||||
await service.deleteOne(req.params['pk']);
|
||||
|
||||
return next();
|
||||
}),
|
||||
|
||||
@@ -17,7 +17,7 @@ router.get(
|
||||
asyncHandler(async (req, res, next) => {
|
||||
const service = new SchemaService({ accountability: req.accountability });
|
||||
const currentSnapshot = await service.snapshot();
|
||||
res.locals.payload = { data: currentSnapshot };
|
||||
res.locals['payload'] = { data: currentSnapshot };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -36,7 +36,7 @@ router.post(
|
||||
const schemaMultipartHandler: RequestHandler = (req, res, next) => {
|
||||
if (req.is('application/json')) {
|
||||
if (Object.keys(req.body).length === 0) throw new InvalidPayloadException(`No data was included in the body`);
|
||||
res.locals.uploadedSnapshot = req.body;
|
||||
res.locals['uploadedSnapshot'] = req.body;
|
||||
return next();
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ const schemaMultipartHandler: RequestHandler = (req, res, next) => {
|
||||
|
||||
if (!uploadedSnapshot) throw new InvalidPayloadException(`No file was included in the body`);
|
||||
|
||||
res.locals.uploadedSnapshot = uploadedSnapshot;
|
||||
res.locals['uploadedSnapshot'] = uploadedSnapshot;
|
||||
|
||||
return next();
|
||||
} catch (error: any) {
|
||||
@@ -104,14 +104,14 @@ router.post(
|
||||
asyncHandler(schemaMultipartHandler),
|
||||
asyncHandler(async (req, res, next) => {
|
||||
const service = new SchemaService({ accountability: req.accountability });
|
||||
const snapshot: Snapshot = res.locals.uploadedSnapshot;
|
||||
const snapshot: Snapshot = res.locals['uploadedSnapshot'];
|
||||
|
||||
const currentSnapshot = await service.snapshot();
|
||||
const snapshotDiff = await service.diff(snapshot, { currentSnapshot, force: 'force' in req.query });
|
||||
if (!snapshotDiff) return next();
|
||||
|
||||
const currentSnapshotHash = getVersionedHash(currentSnapshot);
|
||||
res.locals.payload = { data: { hash: currentSnapshotHash, diff: snapshotDiff } };
|
||||
res.locals['payload'] = { data: { hash: currentSnapshotHash, diff: snapshotDiff } };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
|
||||
@@ -15,7 +15,7 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
res.locals.payload = await service.oas.generate();
|
||||
res.locals['payload'] = await service.oas.generate();
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -34,13 +34,13 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const scope = req.params.scope || 'items';
|
||||
const scope = req.params['scope'] || 'items';
|
||||
|
||||
if (['items', 'system'].includes(scope) === false) throw new RouteNotFoundException(req.path);
|
||||
|
||||
const info = await serverService.serverInfo();
|
||||
const result = await service.graphql.generate(scope as 'items' | 'system');
|
||||
const filename = info.project.project_name + '_' + format(new Date(), 'yyyy-MM-dd') + '.graphql';
|
||||
const filename = info['project'].project_name + '_' + format(new Date(), 'yyyy-MM-dd') + '.graphql';
|
||||
|
||||
res.attachment(filename);
|
||||
res.send(result);
|
||||
@@ -55,7 +55,7 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
const data = await service.serverInfo();
|
||||
res.locals.payload = { data };
|
||||
res.locals['payload'] = { data };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -73,9 +73,9 @@ router.get(
|
||||
|
||||
res.setHeader('Content-Type', 'application/health+json');
|
||||
|
||||
if (data.status === 'error') res.status(503);
|
||||
res.locals.payload = data;
|
||||
res.locals.cache = false;
|
||||
if (data['status'] === 'error') res.status(503);
|
||||
res.locals['payload'] = data;
|
||||
res.locals['cache'] = false;
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
|
||||
@@ -17,7 +17,7 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
const records = await service.readSingleton(req.sanitizedQuery);
|
||||
res.locals.payload = { data: records || null };
|
||||
res.locals['payload'] = { data: records || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -34,7 +34,7 @@ router.patch(
|
||||
|
||||
try {
|
||||
const record = await service.readSingleton(req.sanitizedQuery);
|
||||
res.locals.payload = { data: record || null };
|
||||
res.locals['payload'] = { data: record || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
|
||||
@@ -36,9 +36,9 @@ router.post(
|
||||
|
||||
const { accessToken, refreshToken, expires } = await service.login(req.body);
|
||||
|
||||
res.cookie(env.REFRESH_TOKEN_COOKIE_NAME, refreshToken, COOKIE_OPTIONS);
|
||||
res.cookie(env['REFRESH_TOKEN_COOKIE_NAME'], refreshToken, COOKIE_OPTIONS);
|
||||
|
||||
res.locals.payload = { data: { access_token: accessToken, expires } };
|
||||
res.locals['payload'] = { data: { access_token: accessToken, expires } };
|
||||
|
||||
return next();
|
||||
}),
|
||||
@@ -92,10 +92,10 @@ router.post(
|
||||
try {
|
||||
if (Array.isArray(req.body)) {
|
||||
const items = await service.readMany(savedKeys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: items };
|
||||
res.locals['payload'] = { data: items };
|
||||
} else {
|
||||
const item = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
||||
res.locals.payload = { data: item };
|
||||
res.locals['payload'] = { data: item };
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
@@ -118,7 +118,7 @@ const readHandler = asyncHandler(async (req, res, next) => {
|
||||
|
||||
const records = await service.readByQuery(req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: records || null };
|
||||
res.locals['payload'] = { data: records || null };
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -132,7 +132,7 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const record = await service.readOne(req.params.pk, {
|
||||
const record = await service.readOne(req.params['pk'], {
|
||||
fields: ['id', 'collection', 'item', 'password', 'max_uses', 'times_used', 'date_start', 'date_end'],
|
||||
filter: {
|
||||
_and: [
|
||||
@@ -168,7 +168,7 @@ router.get(
|
||||
},
|
||||
});
|
||||
|
||||
res.locals.payload = { data: record || null };
|
||||
res.locals['payload'] = { data: record || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -182,9 +182,9 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const record = await service.readOne(req.params.pk, req.sanitizedQuery);
|
||||
const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: record || null };
|
||||
res.locals['payload'] = { data: record || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -212,7 +212,7 @@ router.patch(
|
||||
|
||||
try {
|
||||
const result = await service.readMany(keys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: result };
|
||||
res.locals['payload'] = { data: result };
|
||||
} catch (error) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -234,11 +234,11 @@ router.patch(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const primaryKey = await service.updateOne(req.params.pk, req.body);
|
||||
const primaryKey = await service.updateOne(req.params['pk'], req.body);
|
||||
|
||||
try {
|
||||
const item = await service.readOne(primaryKey, req.sanitizedQuery);
|
||||
res.locals.payload = { data: item || null };
|
||||
res.locals['payload'] = { data: item || null };
|
||||
} catch (error) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -282,7 +282,7 @@ router.delete(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await service.deleteOne(req.params.pk);
|
||||
await service.deleteOne(req.params['pk']);
|
||||
|
||||
return next();
|
||||
}),
|
||||
|
||||
@@ -35,10 +35,10 @@ router.post(
|
||||
try {
|
||||
if (Array.isArray(req.body)) {
|
||||
const items = await service.readMany(savedKeys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: items };
|
||||
res.locals['payload'] = { data: items };
|
||||
} else {
|
||||
const item = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
||||
res.locals.payload = { data: item };
|
||||
res.locals['payload'] = { data: item };
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
@@ -67,7 +67,7 @@ const readHandler = asyncHandler(async (req, res, next) => {
|
||||
const item = await service.readByQuery(req.sanitizedQuery);
|
||||
const meta = await metaService.getMetaForQuery('directus_users', req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: item || null, meta };
|
||||
res.locals['payload'] = { data: item || null, meta };
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -86,7 +86,7 @@ router.get(
|
||||
app_access: false,
|
||||
},
|
||||
};
|
||||
res.locals.payload = { data: user };
|
||||
res.locals['payload'] = { data: user };
|
||||
return next();
|
||||
}
|
||||
|
||||
@@ -101,10 +101,10 @@ router.get(
|
||||
|
||||
try {
|
||||
const item = await service.readOne(req.accountability.user, req.sanitizedQuery);
|
||||
res.locals.payload = { data: item || null };
|
||||
res.locals['payload'] = { data: item || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
res.locals.payload = { data: { id: req.accountability.user } };
|
||||
res.locals['payload'] = { data: { id: req.accountability.user } };
|
||||
return next();
|
||||
}
|
||||
|
||||
@@ -125,9 +125,9 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const items = await service.readOne(req.params.pk, req.sanitizedQuery);
|
||||
const items = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: items || null };
|
||||
res.locals['payload'] = { data: items || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -148,7 +148,7 @@ router.patch(
|
||||
const primaryKey = await service.updateOne(req.accountability.user, req.body);
|
||||
const item = await service.readOne(primaryKey, req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: item || null };
|
||||
res.locals['payload'] = { data: item || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -195,7 +195,7 @@ router.patch(
|
||||
|
||||
try {
|
||||
const result = await service.readMany(keys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: result };
|
||||
res.locals['payload'] = { data: result };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -217,11 +217,11 @@ router.patch(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const primaryKey = await service.updateOne(req.params.pk, req.body);
|
||||
const primaryKey = await service.updateOne(req.params['pk'], req.body);
|
||||
|
||||
try {
|
||||
const item = await service.readOne(primaryKey, req.sanitizedQuery);
|
||||
res.locals.payload = { data: item || null };
|
||||
res.locals['payload'] = { data: item || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -266,7 +266,7 @@ router.delete(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await service.deleteOne(req.params.pk);
|
||||
await service.deleteOne(req.params['pk']);
|
||||
|
||||
return next();
|
||||
}),
|
||||
@@ -339,7 +339,7 @@ router.post(
|
||||
|
||||
const { url, secret } = await service.generateTFA(req.accountability.user);
|
||||
|
||||
res.locals.payload = { data: { secret, otpauth_url: url } };
|
||||
res.locals['payload'] = { data: { secret, otpauth_url: url } };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -469,7 +469,7 @@ router.post(
|
||||
throw new InvalidCredentialsException();
|
||||
}
|
||||
|
||||
if (!req.accountability.admin || !req.params.pk) {
|
||||
if (!req.accountability.admin || !req.params['pk']) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
|
||||
@@ -478,7 +478,7 @@ router.post(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await service.disableTFA(req.params.pk);
|
||||
await service.disableTFA(req.params['pk']);
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
|
||||
@@ -23,10 +23,10 @@ router.get(
|
||||
asyncHandler(async (req, res) => {
|
||||
const { nanoid } = await import('nanoid');
|
||||
|
||||
if (req.query && req.query.length && Number(req.query.length) > 500)
|
||||
if (req.query && req.query['length'] && Number(req.query['length']) > 500)
|
||||
throw new InvalidQueryException(`"length" can't be more than 500 characters`);
|
||||
|
||||
const string = nanoid(req.query?.length ? Number(req.query.length) : 32);
|
||||
const string = nanoid(req.query?.['length'] ? Number(req.query['length']) : 32);
|
||||
|
||||
return res.json({ data: string });
|
||||
})
|
||||
@@ -91,7 +91,7 @@ router.post(
|
||||
accountability: req.accountability,
|
||||
schema: req.schema,
|
||||
});
|
||||
await service.revert(req.params.revision);
|
||||
await service.revert(req.params['revision']);
|
||||
next();
|
||||
}),
|
||||
respond
|
||||
@@ -124,7 +124,7 @@ router.post(
|
||||
|
||||
busboy.on('file', async (_fieldname, fileStream, { mimeType }) => {
|
||||
try {
|
||||
await service.import(req.params.collection, mimeType, fileStream);
|
||||
await service.import(req.params['collection'], mimeType, fileStream);
|
||||
} catch (err: any) {
|
||||
return next(err);
|
||||
}
|
||||
@@ -158,7 +158,7 @@ router.post(
|
||||
const sanitizedQuery = sanitizeQuery(req.body.query, req.accountability ?? null);
|
||||
|
||||
// We're not awaiting this, as it's supposed to run async in the background
|
||||
service.exportToFile(req.params.collection, sanitizedQuery, req.body.format, {
|
||||
service.exportToFile(req.params['collection'], sanitizedQuery, req.body.format, {
|
||||
file: req.body.file,
|
||||
});
|
||||
|
||||
|
||||
@@ -33,10 +33,10 @@ router.post(
|
||||
try {
|
||||
if (Array.isArray(req.body)) {
|
||||
const items = await service.readMany(savedKeys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: items };
|
||||
res.locals['payload'] = { data: items };
|
||||
} else {
|
||||
const item = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
||||
res.locals.payload = { data: item };
|
||||
res.locals['payload'] = { data: item };
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
@@ -64,7 +64,7 @@ const readHandler = asyncHandler(async (req, res, next) => {
|
||||
const records = await service.readByQuery(req.sanitizedQuery);
|
||||
const meta = await metaService.getMetaForQuery(req.collection, req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: records || null, meta };
|
||||
res.locals['payload'] = { data: records || null, meta };
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -79,9 +79,9 @@ router.get(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const record = await service.readOne(req.params.pk, req.sanitizedQuery);
|
||||
const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
||||
|
||||
res.locals.payload = { data: record || null };
|
||||
res.locals['payload'] = { data: record || null };
|
||||
return next();
|
||||
}),
|
||||
respond
|
||||
@@ -107,7 +107,7 @@ router.patch(
|
||||
|
||||
try {
|
||||
const result = await service.readMany(keys, req.sanitizedQuery);
|
||||
res.locals.payload = { data: result };
|
||||
res.locals['payload'] = { data: result };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -129,11 +129,11 @@ router.patch(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
const primaryKey = await service.updateOne(req.params.pk, req.body);
|
||||
const primaryKey = await service.updateOne(req.params['pk'], req.body);
|
||||
|
||||
try {
|
||||
const item = await service.readOne(primaryKey, req.sanitizedQuery);
|
||||
res.locals.payload = { data: item || null };
|
||||
res.locals['payload'] = { data: item || null };
|
||||
} catch (error: any) {
|
||||
if (error instanceof ForbiddenException) {
|
||||
return next();
|
||||
@@ -177,7 +177,7 @@ router.delete(
|
||||
schema: req.schema,
|
||||
});
|
||||
|
||||
await service.deleteOne(req.params.pk);
|
||||
await service.deleteOne(req.params['pk']);
|
||||
|
||||
return next();
|
||||
}),
|
||||
|
||||
@@ -2,7 +2,7 @@ import { DateHelper } from '../types';
|
||||
import { parseISO } from 'date-fns';
|
||||
|
||||
export class DateHelperMSSQL extends DateHelper {
|
||||
writeTimestamp(date: string): Date {
|
||||
override writeTimestamp(date: string): Date {
|
||||
const parsedDate = parseISO(date);
|
||||
return new Date(parsedDate.getTime() + parsedDate.getTimezoneOffset() * 60000);
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@ import { DateHelper } from '../types';
|
||||
import { parseISO } from 'date-fns';
|
||||
|
||||
export class DateHelperMySQL extends DateHelper {
|
||||
readTimestampString(date: string): string {
|
||||
override readTimestampString(date: string): string {
|
||||
const parsedDate = new Date(date);
|
||||
return new Date(parsedDate.getTime() - parsedDate.getTimezoneOffset() * 60000).toISOString();
|
||||
}
|
||||
|
||||
writeTimestamp(date: string): Date {
|
||||
override writeTimestamp(date: string): Date {
|
||||
const parsedDate = parseISO(date);
|
||||
return new Date(parsedDate.getTime() + parsedDate.getTimezoneOffset() * 60000);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { DateHelper } from '../types';
|
||||
|
||||
export class DateHelperOracle extends DateHelper {
|
||||
fieldFlagForField(fieldType: string): string {
|
||||
override fieldFlagForField(fieldType: string): string {
|
||||
switch (fieldType) {
|
||||
case 'dateTime':
|
||||
return 'cast-datetime';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { DateHelper } from '../types';
|
||||
|
||||
export class DateHelperSQLite extends DateHelper {
|
||||
parse(date: string | Date): string {
|
||||
override parse(date: string | Date): string {
|
||||
if (!date) {
|
||||
return date;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ export class DateHelperSQLite extends DateHelper {
|
||||
return String(new Date(date).getTime());
|
||||
}
|
||||
|
||||
fieldFlagForField(fieldType: string): string {
|
||||
override fieldFlagForField(fieldType: string): string {
|
||||
switch (fieldType) {
|
||||
case 'timestamp':
|
||||
return 'cast-timestamp';
|
||||
|
||||
@@ -10,7 +10,7 @@ export type FnHelperOptions = {
|
||||
};
|
||||
|
||||
export abstract class FnHelper extends DatabaseHelper {
|
||||
constructor(protected knex: Knex, protected schema: SchemaOverview) {
|
||||
constructor(knex: Knex, protected schema: SchemaOverview) {
|
||||
super(knex);
|
||||
this.schema = schema;
|
||||
}
|
||||
|
||||
@@ -4,33 +4,40 @@ import type { GeoJSONGeometry } from 'wellknown';
|
||||
import { GeometryHelper } from '../types';
|
||||
|
||||
export class GeometryHelperMSSQL extends GeometryHelper {
|
||||
isTrue(expression: Knex.Raw) {
|
||||
override isTrue(expression: Knex.Raw) {
|
||||
return expression.wrap(``, ` = 1`);
|
||||
}
|
||||
isFalse(expression: Knex.Raw) {
|
||||
|
||||
override isFalse(expression: Knex.Raw) {
|
||||
return expression.wrap(``, ` = 0`);
|
||||
}
|
||||
createColumn(table: Knex.CreateTableBuilder, field: RawField | Field) {
|
||||
|
||||
override createColumn(table: Knex.CreateTableBuilder, field: RawField | Field) {
|
||||
if (field.type.split('.')[1]) {
|
||||
field.meta!.special = [field.type];
|
||||
}
|
||||
return table.specificType(field.field, 'geometry');
|
||||
}
|
||||
asText(table: string, column: string): Knex.Raw {
|
||||
|
||||
override asText(table: string, column: string): Knex.Raw {
|
||||
return this.knex.raw('??.??.STAsText() as ??', [table, column, column]);
|
||||
}
|
||||
fromText(text: string): Knex.Raw {
|
||||
|
||||
override fromText(text: string): Knex.Raw {
|
||||
return this.knex.raw('geometry::STGeomFromText(?, 4326)', text);
|
||||
}
|
||||
_intersects(key: string, geojson: GeoJSONGeometry): Knex.Raw {
|
||||
|
||||
override _intersects(key: string, geojson: GeoJSONGeometry): Knex.Raw {
|
||||
const geometry = this.fromGeoJSON(geojson);
|
||||
return this.knex.raw('??.STIntersects(?)', [key, geometry]);
|
||||
}
|
||||
_intersects_bbox(key: string, geojson: GeoJSONGeometry): Knex.Raw {
|
||||
|
||||
override _intersects_bbox(key: string, geojson: GeoJSONGeometry): Knex.Raw {
|
||||
const geometry = this.fromGeoJSON(geojson);
|
||||
return this.knex.raw('??.STEnvelope().STIntersects(?.STEnvelope())', [key, geometry]);
|
||||
}
|
||||
collect(table: string, column: string): Knex.Raw {
|
||||
|
||||
override collect(table: string, column: string): Knex.Raw {
|
||||
return this.knex.raw('geometry::CollectionAggregate(??.??).STAsText()', [table, column]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,17 @@ import type { Knex } from 'knex';
|
||||
import { GeometryHelper } from '../types';
|
||||
|
||||
export class GeometryHelperMySQL extends GeometryHelper {
|
||||
collect(table: string, column: string): Knex.Raw {
|
||||
override collect(table: string, column: string): Knex.Raw {
|
||||
return this.knex.raw(
|
||||
`concat('geometrycollection(', group_concat(? separator ', '), ')'`,
|
||||
this.asText(table, column)
|
||||
);
|
||||
}
|
||||
fromText(text: string): Knex.Raw {
|
||||
|
||||
override fromText(text: string): Knex.Raw {
|
||||
return this.knex.raw('st_geomfromtext(?)', text);
|
||||
}
|
||||
|
||||
asGeoJSON(table: string, column: string): Knex.Raw {
|
||||
return this.knex.raw('st_asgeojson(??.??) as ??', [table, column, column]);
|
||||
}
|
||||
|
||||
@@ -4,36 +4,44 @@ import type { GeoJSONGeometry } from 'wellknown';
|
||||
import { GeometryHelper } from '../types';
|
||||
|
||||
export class GeometryHelperOracle extends GeometryHelper {
|
||||
isTrue(expression: Knex.Raw) {
|
||||
override isTrue(expression: Knex.Raw) {
|
||||
return expression.wrap(``, ` = 'TRUE'`);
|
||||
}
|
||||
isFalse(expression: Knex.Raw) {
|
||||
|
||||
override isFalse(expression: Knex.Raw) {
|
||||
return expression.wrap(``, ` = 'FALSE'`);
|
||||
}
|
||||
createColumn(table: Knex.CreateTableBuilder, field: RawField | Field) {
|
||||
|
||||
override createColumn(table: Knex.CreateTableBuilder, field: RawField | Field) {
|
||||
if (field.type.split('.')[1]) {
|
||||
field.meta!.special = [field.type];
|
||||
}
|
||||
return table.specificType(field.field, 'sdo_geometry');
|
||||
}
|
||||
asText(table: string, column: string): Knex.Raw {
|
||||
|
||||
override asText(table: string, column: string): Knex.Raw {
|
||||
return this.knex.raw('sdo_util.to_wktgeometry(??.??) as ??', [table, column, column]);
|
||||
}
|
||||
|
||||
asGeoJSON(table: string, column: string): Knex.Raw {
|
||||
return this.knex.raw('sdo_util.to_geojson(??.??) as ??', [table, column, column]);
|
||||
}
|
||||
fromText(text: string): Knex.Raw {
|
||||
|
||||
override fromText(text: string): Knex.Raw {
|
||||
return this.knex.raw('sdo_geometry(?, 4326)', text);
|
||||
}
|
||||
_intersects(key: string, geojson: GeoJSONGeometry): Knex.Raw {
|
||||
|
||||
override _intersects(key: string, geojson: GeoJSONGeometry): Knex.Raw {
|
||||
const geometry = this.fromGeoJSON(geojson);
|
||||
return this.knex.raw(`sdo_overlapbdyintersect(??, ?)`, [key, geometry]);
|
||||
}
|
||||
_intersects_bbox(key: string, geojson: GeoJSONGeometry): Knex.Raw {
|
||||
|
||||
override _intersects_bbox(key: string, geojson: GeoJSONGeometry): Knex.Raw {
|
||||
const geometry = this.fromGeoJSON(geojson);
|
||||
return this.knex.raw(`sdo_overlapbdyintersect(sdo_geom.sdo_mbr(??), sdo_geom.sdo_mbr(?))`, [key, geometry]);
|
||||
}
|
||||
collect(table: string, column: string): Knex.Raw {
|
||||
|
||||
override collect(table: string, column: string): Knex.Raw {
|
||||
return this.knex.raw(`concat('geometrycollection(', listagg(?, ', '), ')'`, this.asText(table, column));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,18 +4,21 @@ import type { GeoJSONGeometry } from 'wellknown';
|
||||
import { GeometryHelper } from '../types';
|
||||
|
||||
export class GeometryHelperPostgres extends GeometryHelper {
|
||||
async supported() {
|
||||
override async supported() {
|
||||
const res = await this.knex.select('oid').from('pg_proc').where({ proname: 'postgis_version' });
|
||||
return res.length > 0;
|
||||
}
|
||||
createColumn(table: Knex.CreateTableBuilder, field: RawField | Field) {
|
||||
|
||||
override createColumn(table: Knex.CreateTableBuilder, field: RawField | Field) {
|
||||
const type = field.type.split('.')[1] ?? 'geometry';
|
||||
return table.specificType(field.field, `geometry(${type}, 4326)`);
|
||||
}
|
||||
_intersects_bbox(key: string, geojson: GeoJSONGeometry): Knex.Raw {
|
||||
|
||||
override _intersects_bbox(key: string, geojson: GeoJSONGeometry): Knex.Raw {
|
||||
const geometry = this.fromGeoJSON(geojson);
|
||||
return this.knex.raw('?? && ?', [key, geometry]);
|
||||
}
|
||||
|
||||
asGeoJSON(table: string, column: string): Knex.Raw {
|
||||
return this.knex.raw('st_asgeojson(??.??) as ??', [table, column, column]);
|
||||
}
|
||||
|
||||
@@ -3,12 +3,13 @@ import type { Knex } from 'knex';
|
||||
import { GeometryHelper } from '../types';
|
||||
|
||||
export class GeometryHelperRedshift extends GeometryHelper {
|
||||
createColumn(table: Knex.CreateTableBuilder, field: RawField | Field) {
|
||||
override createColumn(table: Knex.CreateTableBuilder, field: RawField | Field) {
|
||||
if (field.type.split('.')[1]) {
|
||||
field.meta!.special = [field.type];
|
||||
}
|
||||
return table.specificType(field.field, 'geometry');
|
||||
}
|
||||
|
||||
asGeoJSON(table: string, column: string): Knex.Raw {
|
||||
return this.knex.raw('st_asgeojson(??.??) as ??', [table, column, column]);
|
||||
}
|
||||
|
||||
@@ -2,10 +2,11 @@ import type { Knex } from 'knex';
|
||||
import { GeometryHelper } from '../types';
|
||||
|
||||
export class GeometryHelperSQLite extends GeometryHelper {
|
||||
async supported() {
|
||||
override async supported() {
|
||||
const res = await this.knex.select('name').from('pragma_function_list').where({ name: 'spatialite_version' });
|
||||
return res.length > 0;
|
||||
}
|
||||
|
||||
asGeoJSON(table: string, column: string): Knex.Raw {
|
||||
return this.knex.raw('asgeojson(??.??) as ??', [table, column, column]);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { KNEX_TYPES } from '@directus/shared/constants';
|
||||
import { Options, SchemaHelper } from '../types';
|
||||
|
||||
export class SchemaHelperCockroachDb extends SchemaHelper {
|
||||
async changeToType(
|
||||
override async changeToType(
|
||||
table: string,
|
||||
column: string,
|
||||
type: (typeof KNEX_TYPES)[number],
|
||||
@@ -11,7 +11,7 @@ export class SchemaHelperCockroachDb extends SchemaHelper {
|
||||
await this.changeToTypeByCopy(table, column, type, options);
|
||||
}
|
||||
|
||||
constraintName(existingName: string): string {
|
||||
override constraintName(existingName: string): string {
|
||||
const suffix = '_replaced';
|
||||
// CockroachDB does not allow for dropping/creating constraints with the same
|
||||
// name in a single transaction. reference issue #14873
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Knex } from 'knex';
|
||||
import { SchemaHelper } from '../types';
|
||||
|
||||
export class SchemaHelperMSSQL extends SchemaHelper {
|
||||
applyLimit(rootQuery: Knex.QueryBuilder, limit: number): void {
|
||||
override applyLimit(rootQuery: Knex.QueryBuilder, limit: number): void {
|
||||
// The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries,
|
||||
// and common table expressions, unless TOP, OFFSET or FOR XML is also specified.
|
||||
if (limit === -1) {
|
||||
@@ -12,12 +12,12 @@ export class SchemaHelperMSSQL extends SchemaHelper {
|
||||
}
|
||||
}
|
||||
|
||||
applyOffset(rootQuery: Knex.QueryBuilder, offset: number): void {
|
||||
override applyOffset(rootQuery: Knex.QueryBuilder, offset: number): void {
|
||||
rootQuery.offset(offset);
|
||||
rootQuery.orderBy(1);
|
||||
}
|
||||
|
||||
formatUUID(uuid: string): string {
|
||||
override formatUUID(uuid: string): string {
|
||||
return uuid.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { getDatabaseVersion } from '../../../../database';
|
||||
import { SchemaHelper } from '../types';
|
||||
|
||||
export class SchemaHelperMySQL extends SchemaHelper {
|
||||
applyMultiRelationalSort(
|
||||
override applyMultiRelationalSort(
|
||||
knex: Knex,
|
||||
dbQuery: Knex.QueryBuilder,
|
||||
table: string,
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { Field, Relation, Type } from '@directus/shared/types';
|
||||
import { Options, SchemaHelper } from '../types';
|
||||
|
||||
export class SchemaHelperOracle extends SchemaHelper {
|
||||
async changeToType(
|
||||
override async changeToType(
|
||||
table: string,
|
||||
column: string,
|
||||
type: (typeof KNEX_TYPES)[number],
|
||||
@@ -12,11 +12,11 @@ export class SchemaHelperOracle extends SchemaHelper {
|
||||
await this.changeToTypeByCopy(table, column, type, options);
|
||||
}
|
||||
|
||||
castA2oPrimaryKey(): string {
|
||||
override castA2oPrimaryKey(): string {
|
||||
return 'CAST(?? AS VARCHAR2(255))';
|
||||
}
|
||||
|
||||
preRelationChange(relation: Partial<Relation>): void {
|
||||
override preRelationChange(relation: Partial<Relation>): void {
|
||||
if (relation.collection === relation.related_collection) {
|
||||
// Constraints are not allowed on self referencing relationships
|
||||
// Setting NO ACTION throws - ORA-00905: missing keyword
|
||||
@@ -26,7 +26,7 @@ export class SchemaHelperOracle extends SchemaHelper {
|
||||
}
|
||||
}
|
||||
|
||||
processFieldType(field: Field): Type {
|
||||
override processFieldType(field: Field): Type {
|
||||
if (field.type === 'integer') {
|
||||
if (field.schema?.numeric_precision === 20) {
|
||||
return 'bigInteger';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SchemaHelper } from '../types';
|
||||
|
||||
export class SchemaHelperSQLite extends SchemaHelper {
|
||||
async preColumnChange(): Promise<boolean> {
|
||||
override async preColumnChange(): Promise<boolean> {
|
||||
const foreignCheckStatus = (await this.knex.raw('PRAGMA foreign_keys'))[0].foreign_keys === 1;
|
||||
|
||||
if (foreignCheckStatus) {
|
||||
@@ -11,7 +11,7 @@ export class SchemaHelperSQLite extends SchemaHelper {
|
||||
return foreignCheckStatus;
|
||||
}
|
||||
|
||||
async postColumnChange(): Promise<void> {
|
||||
override async postColumnChange(): Promise<void> {
|
||||
await this.knex.raw('PRAGMA foreign_keys = ON');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function getDatabase(): Knex {
|
||||
break;
|
||||
|
||||
case 'oracledb':
|
||||
if (!env.DB_CONNECT_STRING) {
|
||||
if (!env['DB_CONNECT_STRING']) {
|
||||
requiredEnvVars.push('DB_HOST', 'DB_PORT', 'DB_DATABASE', 'DB_USER', 'DB_PASSWORD');
|
||||
} else {
|
||||
requiredEnvVars.push('DB_USER', 'DB_PASSWORD', 'DB_CONNECT_STRING');
|
||||
@@ -54,7 +54,7 @@ export default function getDatabase(): Knex {
|
||||
}
|
||||
break;
|
||||
case 'mssql':
|
||||
if (!env.DB_TYPE || env.DB_TYPE === 'default') {
|
||||
if (!env['DB_TYPE'] || env['DB_TYPE'] === 'default') {
|
||||
requiredEnvVars.push('DB_HOST', 'DB_PORT', 'DB_DATABASE', 'DB_USER', 'DB_PASSWORD');
|
||||
}
|
||||
break;
|
||||
@@ -241,7 +241,7 @@ export async function validateMigrations(): Promise<boolean> {
|
||||
try {
|
||||
let migrationFiles = await fse.readdir(path.join(__dirname, 'migrations'));
|
||||
|
||||
const customMigrationsPath = path.resolve(env.EXTENSIONS_PATH, 'migrations');
|
||||
const customMigrationsPath = path.resolve(env['EXTENSIONS_PATH'], 'migrations');
|
||||
|
||||
let customMigrationFiles =
|
||||
((await fse.pathExists(customMigrationsPath)) && (await fse.readdir(customMigrationsPath))) || [];
|
||||
@@ -297,11 +297,11 @@ async function validateDatabaseCharset(database?: Knex): Promise<void> {
|
||||
|
||||
const tables = await database('information_schema.tables')
|
||||
.select({ name: 'TABLE_NAME', collation: 'TABLE_COLLATION' })
|
||||
.where({ TABLE_SCHEMA: env.DB_DATABASE });
|
||||
.where({ TABLE_SCHEMA: env['DB_DATABASE'] });
|
||||
|
||||
const columns = await database('information_schema.columns')
|
||||
.select({ table_name: 'TABLE_NAME', name: 'COLUMN_NAME', collation: 'COLLATION_NAME' })
|
||||
.where({ TABLE_SCHEMA: env.DB_DATABASE })
|
||||
.where({ TABLE_SCHEMA: env['DB_DATABASE'] })
|
||||
.whereNot({ COLLATION_NAME: collation });
|
||||
|
||||
let inconsistencies = '';
|
||||
|
||||
@@ -66,25 +66,25 @@ export async function down(knex: Knex): Promise<void> {
|
||||
// Put all data under 'exif' and rename/move keys afterwards
|
||||
const newMetadata: { exif: Record<string, unknown>; icc?: unknown; iptc?: unknown } = { exif: prevMetadata };
|
||||
|
||||
if (newMetadata.exif.ifd0) {
|
||||
newMetadata.exif.image = newMetadata.exif.ifd0;
|
||||
delete newMetadata.exif.ifd0;
|
||||
if (newMetadata.exif['ifd0']) {
|
||||
newMetadata.exif['image'] = newMetadata.exif['ifd0'];
|
||||
delete newMetadata.exif['ifd0'];
|
||||
}
|
||||
if (newMetadata.exif.ifd1) {
|
||||
newMetadata.exif.thumbnail = newMetadata.exif.ifd1;
|
||||
delete newMetadata.exif.ifd1;
|
||||
if (newMetadata.exif['ifd1']) {
|
||||
newMetadata.exif['thumbnail'] = newMetadata.exif['ifd1'];
|
||||
delete newMetadata.exif['ifd1'];
|
||||
}
|
||||
if (newMetadata.exif.interop) {
|
||||
newMetadata.exif.interoperability = newMetadata.exif.interop;
|
||||
delete newMetadata.exif.interop;
|
||||
if (newMetadata.exif['interop']) {
|
||||
newMetadata.exif['interoperability'] = newMetadata.exif['interop'];
|
||||
delete newMetadata.exif['interop'];
|
||||
}
|
||||
if (newMetadata.exif.icc) {
|
||||
newMetadata.icc = newMetadata.exif.icc;
|
||||
delete newMetadata.exif.icc;
|
||||
if (newMetadata.exif['icc']) {
|
||||
newMetadata.icc = newMetadata.exif['icc'];
|
||||
delete newMetadata.exif['icc'];
|
||||
}
|
||||
if (newMetadata.exif.iptc) {
|
||||
newMetadata.iptc = newMetadata.exif.iptc;
|
||||
delete newMetadata.exif.iptc;
|
||||
if (newMetadata.exif['iptc']) {
|
||||
newMetadata.iptc = newMetadata.exif['iptc'];
|
||||
delete newMetadata.exif['iptc'];
|
||||
}
|
||||
|
||||
await knex('directus_files')
|
||||
|
||||
@@ -13,7 +13,7 @@ import formatTitle from '@directus/format-title';
|
||||
export default async function run(database: Knex, direction: 'up' | 'down' | 'latest', log = true): Promise<void> {
|
||||
let migrationFiles = await fse.readdir(__dirname);
|
||||
|
||||
const customMigrationsPath = path.resolve(env.EXTENSIONS_PATH, 'migrations');
|
||||
const customMigrationsPath = path.resolve(env['EXTENSIONS_PATH'], 'migrations');
|
||||
let customMigrationFiles =
|
||||
((await fse.pathExists(customMigrationsPath)) && (await fse.readdir(customMigrationsPath))) || [];
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ export default async function runAST(
|
||||
const payloadService = new PayloadService(collection, { knex, schema });
|
||||
let items: null | Item | Item[] = await payloadService.processValues('read', rawItems);
|
||||
|
||||
if (!items || items.length === 0) return items;
|
||||
if (!items || (Array.isArray(items) && items.length === 0)) return items;
|
||||
|
||||
// Apply the `_in` filters to the nested collection batches
|
||||
const nestedNodes = applyParentFilters(schema, nestedCollectionNodes, items);
|
||||
@@ -100,8 +100,8 @@ export default async function runAST(
|
||||
while (hasMore) {
|
||||
const node = merge({}, nestedNode, {
|
||||
query: {
|
||||
limit: env.RELATIONAL_BATCH_SIZE,
|
||||
offset: batchCount * env.RELATIONAL_BATCH_SIZE,
|
||||
limit: env['RELATIONAL_BATCH_SIZE'],
|
||||
offset: batchCount * env['RELATIONAL_BATCH_SIZE'],
|
||||
page: null,
|
||||
},
|
||||
});
|
||||
@@ -112,7 +112,7 @@ export default async function runAST(
|
||||
items = mergeWithParentItems(schema, nestedItems, items, nestedNode);
|
||||
}
|
||||
|
||||
if (!nestedItems || nestedItems.length < env.RELATIONAL_BATCH_SIZE) {
|
||||
if (!nestedItems || nestedItems.length < env['RELATIONAL_BATCH_SIZE']) {
|
||||
hasMore = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,6 @@ import { requireYAML } from '../../../utils/require-yaml';
|
||||
|
||||
const systemData = requireYAML(require.resolve('./collections.yaml'));
|
||||
|
||||
export const systemCollectionRows: CollectionMeta[] = systemData.data.map((row: Record<string, any>) => {
|
||||
return merge({ system: true }, systemData.defaults, row);
|
||||
export const systemCollectionRows: CollectionMeta[] = systemData['data'].map((row: Record<string, any>) => {
|
||||
return merge({ system: true }, systemData['defaults'], row);
|
||||
});
|
||||
|
||||
@@ -18,16 +18,16 @@ for (const filepath of fieldData) {
|
||||
|
||||
const systemFields = requireYAML(path.resolve(__dirname, filepath));
|
||||
|
||||
(systemFields.fields as FieldMeta[]).forEach((field, index) => {
|
||||
(systemFields['fields'] as FieldMeta[]).forEach((field, index) => {
|
||||
const systemField = merge({ system: true }, defaults, field, {
|
||||
collection: systemFields.table,
|
||||
collection: systemFields['table'],
|
||||
sort: index + 1,
|
||||
});
|
||||
|
||||
// Dynamically populate auth providers field
|
||||
if (systemField.collection === 'directus_users' && systemField.field === 'provider') {
|
||||
getAuthProviders().forEach(({ name }) => {
|
||||
systemField.options?.choices?.push({
|
||||
systemField.options?.['choices']?.push({
|
||||
text: formatTitle(name),
|
||||
value: name,
|
||||
});
|
||||
|
||||
@@ -14,26 +14,26 @@ describe('env processed values', async () => {
|
||||
const env = ((await vi.importActual('../src/env')) as { default: Record<string, any> }).default;
|
||||
|
||||
test('Number value should be a number', () => {
|
||||
expect(env.NUMBER).toStrictEqual(1234);
|
||||
expect(env['NUMBER']).toStrictEqual(1234);
|
||||
});
|
||||
|
||||
test('Number value casted as string should be a string', () => {
|
||||
expect(env.NUMBER_CAST_AS_STRING).toStrictEqual('1234');
|
||||
expect(env['NUMBER_CAST_AS_STRING']).toStrictEqual('1234');
|
||||
});
|
||||
|
||||
test('Value casted as regex', () => {
|
||||
expect(env.REGEX).toBeInstanceOf(RegExp);
|
||||
expect(env['REGEX']).toBeInstanceOf(RegExp);
|
||||
});
|
||||
|
||||
test('CSV value should be an array', () => {
|
||||
expect(env.CSV).toStrictEqual(['one', 'two', 'three', 'four']);
|
||||
expect(env['CSV']).toStrictEqual(['one', 'two', 'three', 'four']);
|
||||
});
|
||||
|
||||
test('CSV value casted as string should be a string', () => {
|
||||
expect(env.CSV_CAST_AS_STRING).toStrictEqual('one,two,three,four');
|
||||
expect(env['CSV_CAST_AS_STRING']).toStrictEqual('one,two,three,four');
|
||||
});
|
||||
|
||||
test('Multiple type cast', () => {
|
||||
expect(env.MULTIPLE).toStrictEqual(['https://example.com', /\.example2\.com$/]);
|
||||
expect(env['MULTIPLE']).toStrictEqual(['https://example.com', /\.example2\.com$/]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -343,7 +343,7 @@ export function refreshEnv(): void {
|
||||
}
|
||||
|
||||
function processConfiguration() {
|
||||
const configPath = path.resolve(process.env.CONFIG_PATH || defaults.CONFIG_PATH);
|
||||
const configPath = path.resolve(process.env['CONFIG_PATH'] || defaults['CONFIG_PATH']);
|
||||
|
||||
if (fs.existsSync(configPath) === false) return {};
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ type Options = {
|
||||
|
||||
const defaultOptions: Options = {
|
||||
schedule: true,
|
||||
watch: env.EXTENSIONS_AUTO_RELOAD && env.NODE_ENV !== 'development',
|
||||
watch: env['EXTENSIONS_AUTO_RELOAD'] && env['NODE_ENV'] !== 'development',
|
||||
};
|
||||
|
||||
class ExtensionManager {
|
||||
@@ -237,7 +237,7 @@ class ExtensionManager {
|
||||
|
||||
private async load(): Promise<void> {
|
||||
try {
|
||||
await ensureExtensionDirs(env.EXTENSIONS_PATH, NESTED_EXTENSION_TYPES);
|
||||
await ensureExtensionDirs(env['EXTENSIONS_PATH'], NESTED_EXTENSION_TYPES);
|
||||
|
||||
this.extensions = await this.getExtensions();
|
||||
} catch (err: any) {
|
||||
@@ -250,7 +250,7 @@ class ExtensionManager {
|
||||
await this.registerOperations();
|
||||
await this.registerBundles();
|
||||
|
||||
if (env.SERVE_APP) {
|
||||
if (env['SERVE_APP']) {
|
||||
this.appExtensions = await this.generateExtensionBundle();
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ class ExtensionManager {
|
||||
|
||||
this.apiEmitter.offAll();
|
||||
|
||||
if (env.SERVE_APP) {
|
||||
if (env['SERVE_APP']) {
|
||||
this.appExtensions = null;
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ class ExtensionManager {
|
||||
logger.info('Watching extensions for changes...');
|
||||
|
||||
const localExtensionPaths = NESTED_EXTENSION_TYPES.flatMap((type) => {
|
||||
const typeDir = path.posix.join(pathToRelativeUrl(env.EXTENSIONS_PATH), pluralize(type));
|
||||
const typeDir = path.posix.join(pathToRelativeUrl(env['EXTENSIONS_PATH']), pluralize(type));
|
||||
|
||||
if (isIn(type, HYBRID_EXTENSION_TYPES)) {
|
||||
return [path.posix.join(typeDir, '*', 'app.js'), path.posix.join(typeDir, '*', 'api.js')];
|
||||
@@ -323,12 +323,12 @@ class ExtensionManager {
|
||||
}
|
||||
|
||||
private async getExtensions(): Promise<Extension[]> {
|
||||
const packageExtensions = await getPackageExtensions(env.PACKAGE_FILE_LOCATION);
|
||||
const localPackageExtensions = await resolvePackageExtensions(env.EXTENSIONS_PATH);
|
||||
const localExtensions = await getLocalExtensions(env.EXTENSIONS_PATH);
|
||||
const packageExtensions = await getPackageExtensions(env['PACKAGE_FILE_LOCATION']);
|
||||
const localPackageExtensions = await resolvePackageExtensions(env['EXTENSIONS_PATH']);
|
||||
const localExtensions = await getLocalExtensions(env['EXTENSIONS_PATH']);
|
||||
|
||||
return [...packageExtensions, ...localPackageExtensions, ...localExtensions].filter(
|
||||
(extension) => env.SERVE_APP || APP_EXTENSION_TYPES.includes(extension.type as any) === false
|
||||
(extension) => env['SERVE_APP'] || APP_EXTENSION_TYPES.includes(extension.type as any) === false
|
||||
);
|
||||
}
|
||||
|
||||
@@ -370,7 +370,7 @@ class ExtensionManager {
|
||||
const depName = appDir.find((file) => depRegex.test(file));
|
||||
|
||||
if (depName) {
|
||||
const depUrl = new Url(env.PUBLIC_URL).addPath('admin', 'assets', depName);
|
||||
const depUrl = new Url(env['PUBLIC_URL']).addPath('admin', 'assets', depName);
|
||||
|
||||
depsMapping[dep] = depUrl.toString({ rootRelative: true });
|
||||
} else {
|
||||
|
||||
@@ -76,7 +76,7 @@ class FlowManager {
|
||||
const messenger = getMessenger();
|
||||
|
||||
messenger.subscribe('flows', (event) => {
|
||||
if (event.type === 'reload') {
|
||||
if (event['type'] === 'reload') {
|
||||
this.reloadQueue.enqueue(async () => {
|
||||
if (this.isLoaded) {
|
||||
await this.unload();
|
||||
@@ -146,13 +146,13 @@ class FlowManager {
|
||||
if (flow.trigger === 'event') {
|
||||
let events: string[] = [];
|
||||
|
||||
if (flow.options?.scope) {
|
||||
events = toArray(flow.options.scope)
|
||||
if (flow.options?.['scope']) {
|
||||
events = toArray(flow.options['scope'])
|
||||
.map((scope: string) => {
|
||||
if (['items.create', 'items.update', 'items.delete'].includes(scope)) {
|
||||
if (!flow.options?.collections) return [];
|
||||
if (!flow.options?.['collections']) return [];
|
||||
|
||||
return toArray(flow.options.collections).map((collection: string) => {
|
||||
return toArray(flow.options['collections']).map((collection: string) => {
|
||||
if (collection.startsWith('directus_')) {
|
||||
const action = scope.split('.')[1];
|
||||
return collection.substring(9) + '.' + action;
|
||||
@@ -167,15 +167,15 @@ class FlowManager {
|
||||
.flat();
|
||||
}
|
||||
|
||||
if (flow.options.type === 'filter') {
|
||||
if (flow.options['type'] === 'filter') {
|
||||
const handler: FilterHandler = (payload, meta, context) =>
|
||||
this.executeFlow(
|
||||
flow,
|
||||
{ payload, ...meta },
|
||||
{
|
||||
accountability: context.accountability,
|
||||
database: context.database,
|
||||
getSchema: context.schema ? () => context.schema : getSchema,
|
||||
accountability: context['accountability'],
|
||||
database: context['database'],
|
||||
getSchema: context['schema'] ? () => context['schema'] : getSchema,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -184,12 +184,12 @@ class FlowManager {
|
||||
id: flow.id,
|
||||
events: events.map((event) => ({ type: 'filter', name: event, handler })),
|
||||
});
|
||||
} else if (flow.options.type === 'action') {
|
||||
} else if (flow.options['type'] === 'action') {
|
||||
const handler: ActionHandler = (meta, context) =>
|
||||
this.executeFlow(flow, meta, {
|
||||
accountability: context.accountability,
|
||||
accountability: context['accountability'],
|
||||
database: getDatabase(),
|
||||
getSchema: context.schema ? () => context.schema : getSchema,
|
||||
getSchema: context['schema'] ? () => context['schema'] : getSchema,
|
||||
});
|
||||
|
||||
events.forEach((event) => emitter.onAction(event, handler));
|
||||
@@ -199,8 +199,8 @@ class FlowManager {
|
||||
});
|
||||
}
|
||||
} else if (flow.trigger === 'schedule') {
|
||||
if (validate(flow.options.cron)) {
|
||||
const task = schedule(flow.options.cron, async () => {
|
||||
if (validate(flow.options['cron'])) {
|
||||
const task = schedule(flow.options['cron'], async () => {
|
||||
try {
|
||||
await this.executeFlow(flow);
|
||||
} catch (error: any) {
|
||||
@@ -210,7 +210,7 @@ class FlowManager {
|
||||
|
||||
this.triggerHandlers.push({ id: flow.id, events: [{ type: flow.trigger, task }] });
|
||||
} else {
|
||||
logger.warn(`Couldn't register cron trigger. Provided cron is invalid: ${flow.options.cron}`);
|
||||
logger.warn(`Couldn't register cron trigger. Provided cron is invalid: ${flow.options['cron']}`);
|
||||
}
|
||||
} else if (flow.trigger === 'operation') {
|
||||
const handler = (data: unknown, context: Record<string, unknown>) => this.executeFlow(flow, data, context);
|
||||
@@ -218,23 +218,24 @@ class FlowManager {
|
||||
this.operationFlowHandlers[flow.id] = handler;
|
||||
} else if (flow.trigger === 'webhook') {
|
||||
const handler = (data: unknown, context: Record<string, unknown>) => {
|
||||
if (flow.options.async) {
|
||||
if (flow.options['async']) {
|
||||
this.executeFlow(flow, data, context);
|
||||
return undefined;
|
||||
} else {
|
||||
return this.executeFlow(flow, data, context);
|
||||
}
|
||||
};
|
||||
|
||||
const method = flow.options?.method ?? 'GET';
|
||||
const method = flow.options?.['method'] ?? 'GET';
|
||||
|
||||
// Default return to $last for webhooks
|
||||
flow.options.return = flow.options.return ?? '$last';
|
||||
flow.options['return'] = flow.options['return'] ?? '$last';
|
||||
|
||||
this.webhookFlowHandlers[`${method}-${flow.id}`] = handler;
|
||||
} else if (flow.trigger === 'manual') {
|
||||
const handler = (data: unknown, context: Record<string, unknown>) => {
|
||||
const enabledCollections = flow.options?.collections ?? [];
|
||||
const targetCollection = (data as Record<string, any>)?.body.collection;
|
||||
const enabledCollections = flow.options?.['collections'] ?? [];
|
||||
const targetCollection = (data as Record<string, any>)?.['body'].collection;
|
||||
|
||||
if (!targetCollection) {
|
||||
logger.warn(`Manual trigger requires "collection" to be specified in the payload`);
|
||||
@@ -251,15 +252,16 @@ class FlowManager {
|
||||
throw new exceptions.ForbiddenException();
|
||||
}
|
||||
|
||||
if (flow.options.async) {
|
||||
if (flow.options['async']) {
|
||||
this.executeFlow(flow, data, context);
|
||||
return undefined;
|
||||
} else {
|
||||
return this.executeFlow(flow, data, context);
|
||||
}
|
||||
};
|
||||
|
||||
// Default return to $last for manual
|
||||
flow.options.return = '$last';
|
||||
flow.options['return'] = '$last';
|
||||
|
||||
this.webhookFlowHandlers[`POST-${flow.id}`] = handler;
|
||||
}
|
||||
@@ -293,14 +295,14 @@ class FlowManager {
|
||||
}
|
||||
|
||||
private async executeFlow(flow: Flow, data: unknown = null, context: Record<string, unknown> = {}): Promise<unknown> {
|
||||
const database = (context.database as Knex) ?? getDatabase();
|
||||
const schema = (context.schema as SchemaOverview) ?? (await getSchema({ database }));
|
||||
const database = (context['database'] as Knex) ?? getDatabase();
|
||||
const schema = (context['schema'] as SchemaOverview) ?? (await getSchema({ database }));
|
||||
|
||||
const keyedData: Record<string, unknown> = {
|
||||
[TRIGGER_KEY]: data,
|
||||
[LAST_KEY]: data,
|
||||
[ACCOUNTABILITY_KEY]: context?.accountability ?? null,
|
||||
[ENV_KEY]: pick(env, env.FLOWS_ENV_ALLOW_LIST ? toArray(env.FLOWS_ENV_ALLOW_LIST) : []),
|
||||
[ACCOUNTABILITY_KEY]: context?.['accountability'] ?? null,
|
||||
[ENV_KEY]: pick(env, env['FLOWS_ENV_ALLOW_LIST'] ? toArray(env['FLOWS_ENV_ALLOW_LIST']) : []),
|
||||
};
|
||||
|
||||
let nextOperation = flow.operation;
|
||||
@@ -330,7 +332,7 @@ class FlowManager {
|
||||
schema: schema,
|
||||
});
|
||||
|
||||
const accountability = context?.accountability as Accountability | undefined;
|
||||
const accountability = context?.['accountability'] as Accountability | undefined;
|
||||
|
||||
const activity = await activityService.createOne({
|
||||
action: Action.RUN,
|
||||
@@ -360,14 +362,14 @@ class FlowManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (flow.trigger === 'event' && flow.options.type === 'filter' && lastOperationStatus === 'reject') {
|
||||
if (flow.trigger === 'event' && flow.options['type'] === 'filter' && lastOperationStatus === 'reject') {
|
||||
throw keyedData[LAST_KEY];
|
||||
}
|
||||
|
||||
if (flow.options.return === '$all') {
|
||||
if (flow.options['return'] === '$all') {
|
||||
return keyedData;
|
||||
} else if (flow.options.return) {
|
||||
return get(keyedData, flow.options.return);
|
||||
} else if (flow.options['return']) {
|
||||
return get(keyedData, flow.options['return']);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
@@ -9,21 +9,21 @@ import { getConfigFromEnv } from './utils/get-config-from-env';
|
||||
import { redactHeaderCookie } from './utils/redact-header-cookies';
|
||||
|
||||
const pinoOptions: LoggerOptions = {
|
||||
level: env.LOG_LEVEL || 'info',
|
||||
level: env['LOG_LEVEL'] || 'info',
|
||||
redact: {
|
||||
paths: ['req.headers.authorization', `req.cookies.${env.REFRESH_TOKEN_COOKIE_NAME}`],
|
||||
paths: ['req.headers.authorization', `req.cookies.${env['REFRESH_TOKEN_COOKIE_NAME']}`],
|
||||
censor: '--redact--',
|
||||
},
|
||||
};
|
||||
const httpLoggerOptions: LoggerOptions = {
|
||||
level: env.LOG_LEVEL || 'info',
|
||||
level: env['LOG_LEVEL'] || 'info',
|
||||
redact: {
|
||||
paths: ['req.headers.authorization', `req.cookies.${env.REFRESH_TOKEN_COOKIE_NAME}`],
|
||||
paths: ['req.headers.authorization', `req.cookies.${env['REFRESH_TOKEN_COOKIE_NAME']}`],
|
||||
censor: '--redact--',
|
||||
},
|
||||
};
|
||||
|
||||
if (env.LOG_STYLE !== 'raw') {
|
||||
if (env['LOG_STYLE'] !== 'raw') {
|
||||
pinoOptions.transport = {
|
||||
target: 'pino-pretty',
|
||||
options: {
|
||||
@@ -48,10 +48,10 @@ if (env.LOG_STYLE !== 'raw') {
|
||||
const loggerEnvConfig = getConfigFromEnv('LOGGER_', 'LOGGER_HTTP');
|
||||
|
||||
// Expose custom log levels into formatter function
|
||||
if (loggerEnvConfig.levels) {
|
||||
if (loggerEnvConfig['levels']) {
|
||||
const customLogLevels: { [key: string]: string } = {};
|
||||
|
||||
for (const el of toArray(loggerEnvConfig.levels)) {
|
||||
for (const el of toArray(loggerEnvConfig['levels'])) {
|
||||
const key_val = el.split(':');
|
||||
customLogLevels[key_val[0].trim()] = key_val[1].trim();
|
||||
}
|
||||
@@ -73,7 +73,7 @@ if (loggerEnvConfig.levels) {
|
||||
},
|
||||
};
|
||||
|
||||
delete loggerEnvConfig.levels;
|
||||
delete loggerEnvConfig['levels'];
|
||||
}
|
||||
|
||||
const logger = pino(merge(pinoOptions, loggerEnvConfig));
|
||||
@@ -87,10 +87,10 @@ export const expressLogger = pinoHTTP({
|
||||
req(request: Request) {
|
||||
const output = stdSerializers.req(request);
|
||||
output.url = redactQuery(output.url);
|
||||
if (output.headers?.cookie) {
|
||||
output.headers.cookie = redactHeaderCookie(output.headers.cookie, [
|
||||
if (output.headers?.['cookie']) {
|
||||
output.headers['cookie'] = redactHeaderCookie(output.headers['cookie'], [
|
||||
'access_token',
|
||||
`${env.REFRESH_TOKEN_COOKIE_NAME}`,
|
||||
`${env['REFRESH_TOKEN_COOKIE_NAME']}`,
|
||||
]);
|
||||
}
|
||||
return output;
|
||||
@@ -99,7 +99,7 @@ export const expressLogger = pinoHTTP({
|
||||
if (response.headers?.['set-cookie']) {
|
||||
response.headers['set-cookie'] = redactHeaderCookie(response.headers['set-cookie'], [
|
||||
'access_token',
|
||||
`${env.REFRESH_TOKEN_COOKIE_NAME}`,
|
||||
`${env['REFRESH_TOKEN_COOKIE_NAME']}`,
|
||||
]);
|
||||
}
|
||||
return response;
|
||||
|
||||
@@ -8,13 +8,13 @@ let transporter: Transporter;
|
||||
export default function getMailer(): Transporter {
|
||||
if (transporter) return transporter;
|
||||
|
||||
const transportName = env.EMAIL_TRANSPORT.toLowerCase();
|
||||
const transportName = env['EMAIL_TRANSPORT'].toLowerCase();
|
||||
|
||||
if (transportName === 'sendmail') {
|
||||
transporter = nodemailer.createTransport({
|
||||
sendmail: true,
|
||||
newline: env.EMAIL_SENDMAIL_NEW_LINE || 'unix',
|
||||
path: env.EMAIL_SENDMAIL_PATH || '/usr/sbin/sendmail',
|
||||
newline: env['EMAIL_SENDMAIL_NEW_LINE'] || 'unix',
|
||||
path: env['EMAIL_SENDMAIL_PATH'] || '/usr/sbin/sendmail',
|
||||
});
|
||||
} else if (transportName === 'ses') {
|
||||
const aws = require('@aws-sdk/client-ses');
|
||||
@@ -29,22 +29,22 @@ export default function getMailer(): Transporter {
|
||||
} else if (transportName === 'smtp') {
|
||||
let auth: boolean | { user?: string; pass?: string } = false;
|
||||
|
||||
if (env.EMAIL_SMTP_USER || env.EMAIL_SMTP_PASSWORD) {
|
||||
if (env['EMAIL_SMTP_USER'] || env['EMAIL_SMTP_PASSWORD']) {
|
||||
auth = {
|
||||
user: env.EMAIL_SMTP_USER,
|
||||
pass: env.EMAIL_SMTP_PASSWORD,
|
||||
user: env['EMAIL_SMTP_USER'],
|
||||
pass: env['EMAIL_SMTP_PASSWORD'],
|
||||
};
|
||||
}
|
||||
|
||||
const tls: Record<string, unknown> = getConfigFromEnv('EMAIL_SMTP_TLS_');
|
||||
|
||||
transporter = nodemailer.createTransport({
|
||||
name: env.EMAIL_SMTP_NAME,
|
||||
pool: env.EMAIL_SMTP_POOL,
|
||||
host: env.EMAIL_SMTP_HOST,
|
||||
port: env.EMAIL_SMTP_PORT,
|
||||
secure: env.EMAIL_SMTP_SECURE,
|
||||
ignoreTLS: env.EMAIL_SMTP_IGNORE_TLS,
|
||||
name: env['EMAIL_SMTP_NAME'],
|
||||
pool: env['EMAIL_SMTP_POOL'],
|
||||
host: env['EMAIL_SMTP_HOST'],
|
||||
port: env['EMAIL_SMTP_PORT'],
|
||||
secure: env['EMAIL_SMTP_SECURE'],
|
||||
ignoreTLS: env['EMAIL_SMTP_IGNORE_TLS'],
|
||||
auth,
|
||||
tls,
|
||||
} as Record<string, unknown>);
|
||||
@@ -53,17 +53,17 @@ export default function getMailer(): Transporter {
|
||||
transporter = nodemailer.createTransport(
|
||||
mg({
|
||||
auth: {
|
||||
api_key: env.EMAIL_MAILGUN_API_KEY,
|
||||
domain: env.EMAIL_MAILGUN_DOMAIN,
|
||||
api_key: env['EMAIL_MAILGUN_API_KEY'],
|
||||
domain: env['EMAIL_MAILGUN_DOMAIN'],
|
||||
},
|
||||
host: env.EMAIL_MAILGUN_HOST || 'api.mailgun.net',
|
||||
host: env['EMAIL_MAILGUN_HOST'] || 'api.mailgun.net',
|
||||
}) as any
|
||||
);
|
||||
} else if (transportName === 'sendgrid') {
|
||||
const sg = require('nodemailer-sendgrid');
|
||||
transporter = nodemailer.createTransport(
|
||||
sg({
|
||||
apiKey: env.EMAIL_SENDGRID_API_KEY,
|
||||
apiKey: env['EMAIL_SENDGRID_API_KEY'],
|
||||
}) as any
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -40,9 +40,9 @@ export class MessengerRedis implements Messenger {
|
||||
constructor() {
|
||||
const config = getConfigFromEnv('MESSENGER_REDIS');
|
||||
|
||||
this.pub = new IORedis(env.MESSENGER_REDIS ?? config);
|
||||
this.sub = new IORedis(env.MESSENGER_REDIS ?? config);
|
||||
this.namespace = env.MESSENGER_NAMESPACE ?? 'directus';
|
||||
this.pub = new IORedis(env['MESSENGER_REDIS'] ?? config);
|
||||
this.sub = new IORedis(env['MESSENGER_REDIS'] ?? config);
|
||||
this.namespace = env['MESSENGER_NAMESPACE'] ?? 'directus';
|
||||
}
|
||||
|
||||
publish(channel: string, payload: Record<string, any>) {
|
||||
@@ -71,7 +71,7 @@ let messenger: Messenger;
|
||||
export function getMessenger() {
|
||||
if (messenger) return messenger;
|
||||
|
||||
if (env.MESSENGER_STORE === 'redis') {
|
||||
if (env['MESSENGER_STORE'] === 'redis') {
|
||||
messenger = new MessengerRedis();
|
||||
} else {
|
||||
messenger = new MessengerMemory();
|
||||
|
||||
@@ -96,7 +96,7 @@ test('Sets accountability to payload contents if valid token is passed', async (
|
||||
share,
|
||||
share_scope: shareScope,
|
||||
},
|
||||
env.SECRET,
|
||||
env['SECRET'],
|
||||
{ issuer: 'directus' }
|
||||
);
|
||||
|
||||
@@ -145,7 +145,7 @@ test('Sets accountability to payload contents if valid token is passed', async (
|
||||
share,
|
||||
share_scope: shareScope,
|
||||
},
|
||||
env.SECRET,
|
||||
env['SECRET'],
|
||||
{ issuer: 'directus' }
|
||||
);
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ export const handler = async (req: Request, res: Response, next: NextFunction) =
|
||||
|
||||
if (req.token) {
|
||||
if (isDirectusJWT(req.token)) {
|
||||
const payload = verifyAccessJWT(req.token, env.SECRET);
|
||||
const payload = verifyAccessJWT(req.token, env['SECRET']);
|
||||
|
||||
req.accountability.role = payload.role;
|
||||
req.accountability.admin = payload.admin_access === true || payload.admin_access == 1;
|
||||
|
||||
@@ -11,11 +11,11 @@ const checkCacheMiddleware: RequestHandler = asyncHandler(async (req, res, next)
|
||||
const { cache } = getCache();
|
||||
|
||||
if (req.method.toLowerCase() !== 'get' && req.originalUrl?.startsWith('/graphql') === false) return next();
|
||||
if (env.CACHE_ENABLED !== true) return next();
|
||||
if (env['CACHE_ENABLED'] !== true) return next();
|
||||
if (!cache) return next();
|
||||
|
||||
if (shouldSkipCache(req)) {
|
||||
if (env.CACHE_STATUS_HEADER) res.setHeader(`${env.CACHE_STATUS_HEADER}`, 'MISS');
|
||||
if (env['CACHE_STATUS_HEADER']) res.setHeader(`${env['CACHE_STATUS_HEADER']}`, 'MISS');
|
||||
return next();
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ const checkCacheMiddleware: RequestHandler = asyncHandler(async (req, res, next)
|
||||
cachedData = await getCacheValue(cache, key);
|
||||
} catch (err: any) {
|
||||
logger.warn(err, `[cache] Couldn't read key ${key}. ${err.message}`);
|
||||
if (env.CACHE_STATUS_HEADER) res.setHeader(`${env.CACHE_STATUS_HEADER}`, 'MISS');
|
||||
if (env['CACHE_STATUS_HEADER']) res.setHeader(`${env['CACHE_STATUS_HEADER']}`, 'MISS');
|
||||
return next();
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ const checkCacheMiddleware: RequestHandler = asyncHandler(async (req, res, next)
|
||||
cacheExpiryDate = (await getCacheValue(cache, `${key}__expires_at`))?.exp;
|
||||
} catch (err: any) {
|
||||
logger.warn(err, `[cache] Couldn't read key ${`${key}__expires_at`}. ${err.message}`);
|
||||
if (env.CACHE_STATUS_HEADER) res.setHeader(`${env.CACHE_STATUS_HEADER}`, 'MISS');
|
||||
if (env['CACHE_STATUS_HEADER']) res.setHeader(`${env['CACHE_STATUS_HEADER']}`, 'MISS');
|
||||
return next();
|
||||
}
|
||||
|
||||
@@ -46,11 +46,11 @@ const checkCacheMiddleware: RequestHandler = asyncHandler(async (req, res, next)
|
||||
|
||||
res.setHeader('Cache-Control', getCacheControlHeader(req, cacheTTL, true, true));
|
||||
res.setHeader('Vary', 'Origin, Cache-Control');
|
||||
if (env.CACHE_STATUS_HEADER) res.setHeader(`${env.CACHE_STATUS_HEADER}`, 'HIT');
|
||||
if (env['CACHE_STATUS_HEADER']) res.setHeader(`${env['CACHE_STATUS_HEADER']}`, 'HIT');
|
||||
|
||||
return res.json(cachedData);
|
||||
} else {
|
||||
if (env.CACHE_STATUS_HEADER) res.setHeader(`${env.CACHE_STATUS_HEADER}`, 'MISS');
|
||||
if (env['CACHE_STATUS_HEADER']) res.setHeader(`${env['CACHE_STATUS_HEADER']}`, 'MISS');
|
||||
return next();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,13 +8,13 @@ import { ForbiddenException } from '../exceptions';
|
||||
import asyncHandler from '../utils/async-handler';
|
||||
|
||||
const collectionExists: RequestHandler = asyncHandler(async (req, res, next) => {
|
||||
if (!req.params.collection) return next();
|
||||
if (!req.params['collection']) return next();
|
||||
|
||||
if (req.params.collection in req.schema.collections === false) {
|
||||
if (req.params['collection'] in req.schema.collections === false) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
|
||||
req.collection = req.params.collection;
|
||||
req.collection = req.params['collection'];
|
||||
|
||||
if (req.collection.startsWith('directus_')) {
|
||||
const systemRow = systemCollectionRows.find((collection) => {
|
||||
|
||||
@@ -4,14 +4,14 @@ import env from '../env';
|
||||
|
||||
let corsMiddleware: RequestHandler = (req, res, next) => next();
|
||||
|
||||
if (env.CORS_ENABLED === true) {
|
||||
if (env['CORS_ENABLED'] === true) {
|
||||
corsMiddleware = cors({
|
||||
origin: env.CORS_ORIGIN || true,
|
||||
methods: env.CORS_METHODS || 'GET,POST,PATCH,DELETE',
|
||||
allowedHeaders: env.CORS_ALLOWED_HEADERS,
|
||||
exposedHeaders: env.CORS_EXPOSED_HEADERS,
|
||||
credentials: env.CORS_CREDENTIALS || undefined,
|
||||
maxAge: env.CORS_MAX_AGE || undefined,
|
||||
origin: env['CORS_ORIGIN'] || true,
|
||||
methods: env['CORS_METHODS'] || 'GET,POST,PATCH,DELETE',
|
||||
allowedHeaders: env['CORS_ALLOWED_HEADERS'],
|
||||
exposedHeaders: env['CORS_EXPOSED_HEADERS'],
|
||||
credentials: env['CORS_CREDENTIALS'] || undefined,
|
||||
maxAge: env['CORS_MAX_AGE'] || undefined,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ const errorHandler: ErrorRequestHandler = (err, req, res, _next) => {
|
||||
}
|
||||
|
||||
for (const err of errors) {
|
||||
if (env.NODE_ENV === 'development') {
|
||||
if (env['NODE_ENV'] === 'development') {
|
||||
err.extensions = {
|
||||
...(err.extensions || {}),
|
||||
stack: err.stack,
|
||||
@@ -54,7 +54,7 @@ const errorHandler: ErrorRequestHandler = (err, req, res, _next) => {
|
||||
});
|
||||
|
||||
if (err instanceof MethodNotAllowedException) {
|
||||
res.header('Allow', err.extensions.allow.join(', '));
|
||||
res.header('Allow', err.extensions['allow'].join(', '));
|
||||
}
|
||||
} else {
|
||||
logger.error(err);
|
||||
|
||||
@@ -12,8 +12,8 @@ import type { RequestHandler } from 'express';
|
||||
const extractToken: RequestHandler = (req, res, next) => {
|
||||
let token: string | null = null;
|
||||
|
||||
if (req.query && req.query.access_token) {
|
||||
token = req.query.access_token as string;
|
||||
if (req.query && req.query['access_token']) {
|
||||
token = req.query['access_token'] as string;
|
||||
}
|
||||
|
||||
if (req.headers && req.headers.authorization) {
|
||||
|
||||
@@ -16,11 +16,11 @@ export const parseGraphQL: RequestHandler = asyncHandler(async (req, res, next)
|
||||
let document: DocumentNode;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
query = (req.query.query as string | undefined) || null;
|
||||
query = (req.query['query'] as string | undefined) || null;
|
||||
|
||||
if (req.query.variables) {
|
||||
if (req.query['variables']) {
|
||||
try {
|
||||
variables = parseJSON(req.query.variables as string);
|
||||
variables = parseJSON(req.query['variables'] as string);
|
||||
} catch {
|
||||
throw new InvalidQueryException(`Variables are invalid JSON.`);
|
||||
}
|
||||
@@ -28,7 +28,7 @@ export const parseGraphQL: RequestHandler = asyncHandler(async (req, res, next)
|
||||
variables = {};
|
||||
}
|
||||
|
||||
operationName = (req.query.operationName as string | undefined) || null;
|
||||
operationName = (req.query['operationName'] as string | undefined) || null;
|
||||
} else {
|
||||
query = req.body.query || null;
|
||||
variables = req.body.variables || null;
|
||||
@@ -58,10 +58,16 @@ export const parseGraphQL: RequestHandler = asyncHandler(async (req, res, next)
|
||||
|
||||
// Prevent caching responses when mutations are made
|
||||
if (operationAST?.operation === 'mutation') {
|
||||
res.locals.cache = false;
|
||||
res.locals['cache'] = false;
|
||||
}
|
||||
|
||||
res.locals.graphqlParams = { document, query, variables, operationName, contextValue: { req, res } } as GraphQLParams;
|
||||
res.locals['graphqlParams'] = {
|
||||
document,
|
||||
query,
|
||||
variables,
|
||||
operationName,
|
||||
contextValue: { req, res },
|
||||
} as GraphQLParams;
|
||||
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ const RATE_LIMITER_GLOBAL_KEY = 'global-rate-limit';
|
||||
let checkRateLimit: RequestHandler = (_req, _res, next) => next();
|
||||
export let rateLimiterGlobal: RateLimiterRedis | RateLimiterMemcache | RateLimiterMemory;
|
||||
|
||||
if (env.RATE_LIMITER_GLOBAL_ENABLED === true) {
|
||||
if (env['RATE_LIMITER_GLOBAL_ENABLED'] === true) {
|
||||
validateEnv(['RATE_LIMITER_GLOBAL_STORE', 'RATE_LIMITER_GLOBAL_DURATION', 'RATE_LIMITER_GLOBAL_POINTS']);
|
||||
validateConfiguration();
|
||||
|
||||
@@ -27,7 +27,7 @@ if (env.RATE_LIMITER_GLOBAL_ENABLED === true) {
|
||||
|
||||
res.set('Retry-After', String(Math.round(rateLimiterRes.msBeforeNext / 1000)));
|
||||
throw new HitRateLimitException(`Too many requests, retry after ${ms(rateLimiterRes.msBeforeNext)}.`, {
|
||||
limit: +env.RATE_LIMITER_GLOBAL_POINTS,
|
||||
limit: +env['RATE_LIMITER_GLOBAL_POINTS'],
|
||||
reset: new Date(Date.now() + rateLimiterRes.msBeforeNext),
|
||||
});
|
||||
}
|
||||
@@ -39,13 +39,13 @@ if (env.RATE_LIMITER_GLOBAL_ENABLED === true) {
|
||||
export default checkRateLimit;
|
||||
|
||||
function validateConfiguration() {
|
||||
if (env.RATE_LIMITER_ENABLED !== true) {
|
||||
if (env['RATE_LIMITER_ENABLED'] !== true) {
|
||||
logger.error(`The IP based rate limiter needs to be enabled when using the global rate limiter.`);
|
||||
process.exit(1);
|
||||
}
|
||||
const globalPointsPerSec =
|
||||
Number(env.RATE_LIMITER_GLOBAL_POINTS) / Math.max(Number(env.RATE_LIMITER_GLOBAL_DURATION), 1);
|
||||
const regularPointsPerSec = Number(env.RATE_LIMITER_POINTS) / Math.max(Number(env.RATE_LIMITER_DURATION), 1);
|
||||
Number(env['RATE_LIMITER_GLOBAL_POINTS']) / Math.max(Number(env['RATE_LIMITER_GLOBAL_DURATION']), 1);
|
||||
const regularPointsPerSec = Number(env['RATE_LIMITER_POINTS']) / Math.max(Number(env['RATE_LIMITER_DURATION']), 1);
|
||||
if (globalPointsPerSec <= regularPointsPerSec) {
|
||||
logger.error(`The global rate limiter needs to allow more requests per second than the IP based rate limiter.`);
|
||||
process.exit(1);
|
||||
|
||||
@@ -11,7 +11,7 @@ import { validateEnv } from '../utils/validate-env';
|
||||
let checkRateLimit: RequestHandler = (_req, _res, next) => next();
|
||||
export let rateLimiter: RateLimiterRedis | RateLimiterMemcache | RateLimiterMemory;
|
||||
|
||||
if (env.RATE_LIMITER_ENABLED === true) {
|
||||
if (env['RATE_LIMITER_ENABLED'] === true) {
|
||||
validateEnv(['RATE_LIMITER_STORE', 'RATE_LIMITER_DURATION', 'RATE_LIMITER_POINTS']);
|
||||
|
||||
rateLimiter = createRateLimiter('RATE_LIMITER');
|
||||
@@ -24,7 +24,7 @@ if (env.RATE_LIMITER_ENABLED === true) {
|
||||
|
||||
res.set('Retry-After', String(Math.round(rateLimiterRes.msBeforeNext / 1000)));
|
||||
throw new HitRateLimitException(`Too many requests, retry after ${ms(rateLimiterRes.msBeforeNext)}.`, {
|
||||
limit: +env.RATE_LIMITER_POINTS,
|
||||
limit: +env['RATE_LIMITER_POINTS'],
|
||||
reset: new Date(Date.now() + rateLimiterRes.msBeforeNext),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,30 +16,30 @@ export const respond: RequestHandler = asyncHandler(async (req, res) => {
|
||||
|
||||
let exceedsMaxSize = false;
|
||||
|
||||
if (env.CACHE_VALUE_MAX_SIZE !== false) {
|
||||
const valueSize = res.locals.payload ? stringByteSize(JSON.stringify(res.locals.payload)) : 0;
|
||||
const maxSize = parseBytesConfiguration(env.CACHE_VALUE_MAX_SIZE);
|
||||
if (env['CACHE_VALUE_MAX_SIZE'] !== false) {
|
||||
const valueSize = res.locals['payload'] ? stringByteSize(JSON.stringify(res.locals['payload'])) : 0;
|
||||
const maxSize = parseBytesConfiguration(env['CACHE_VALUE_MAX_SIZE']);
|
||||
exceedsMaxSize = valueSize > maxSize;
|
||||
}
|
||||
|
||||
if (
|
||||
(req.method.toLowerCase() === 'get' || req.originalUrl?.startsWith('/graphql')) &&
|
||||
env.CACHE_ENABLED === true &&
|
||||
env['CACHE_ENABLED'] === true &&
|
||||
cache &&
|
||||
!req.sanitizedQuery.export &&
|
||||
res.locals.cache !== false &&
|
||||
res.locals['cache'] !== false &&
|
||||
exceedsMaxSize === false
|
||||
) {
|
||||
const key = getCacheKey(req);
|
||||
|
||||
try {
|
||||
await setCacheValue(cache, key, res.locals.payload, getMilliseconds(env.CACHE_TTL));
|
||||
await setCacheValue(cache, `${key}__expires_at`, { exp: Date.now() + getMilliseconds(env.CACHE_TTL, 0) });
|
||||
await setCacheValue(cache, key, res.locals['payload'], getMilliseconds(env['CACHE_TTL']));
|
||||
await setCacheValue(cache, `${key}__expires_at`, { exp: Date.now() + getMilliseconds(env['CACHE_TTL'], 0) });
|
||||
} catch (err: any) {
|
||||
logger.warn(err, `[cache] Couldn't set key ${key}. ${err}`);
|
||||
}
|
||||
|
||||
res.setHeader('Cache-Control', getCacheControlHeader(req, getMilliseconds(env.CACHE_TTL), true, true));
|
||||
res.setHeader('Cache-Control', getCacheControlHeader(req, getMilliseconds(env['CACHE_TTL']), true, true));
|
||||
res.setHeader('Vary', 'Origin, Cache-Control');
|
||||
} else {
|
||||
// Don't cache anything by default
|
||||
@@ -63,32 +63,32 @@ export const respond: RequestHandler = asyncHandler(async (req, res) => {
|
||||
if (req.sanitizedQuery.export === 'json') {
|
||||
res.attachment(`${filename}.json`);
|
||||
res.set('Content-Type', 'application/json');
|
||||
return res.status(200).send(exportService.transform(res.locals.payload?.data, 'json'));
|
||||
return res.status(200).send(exportService.transform(res.locals['payload']?.data, 'json'));
|
||||
}
|
||||
|
||||
if (req.sanitizedQuery.export === 'xml') {
|
||||
res.attachment(`${filename}.xml`);
|
||||
res.set('Content-Type', 'text/xml');
|
||||
return res.status(200).send(exportService.transform(res.locals.payload?.data, 'xml'));
|
||||
return res.status(200).send(exportService.transform(res.locals['payload']?.data, 'xml'));
|
||||
}
|
||||
|
||||
if (req.sanitizedQuery.export === 'csv') {
|
||||
res.attachment(`${filename}.csv`);
|
||||
res.set('Content-Type', 'text/csv');
|
||||
return res.status(200).send(exportService.transform(res.locals.payload?.data, 'csv'));
|
||||
return res.status(200).send(exportService.transform(res.locals['payload']?.data, 'csv'));
|
||||
}
|
||||
|
||||
if (req.sanitizedQuery.export === 'yaml') {
|
||||
res.attachment(`${filename}.yaml`);
|
||||
res.set('Content-Type', 'text/yaml');
|
||||
return res.status(200).send(exportService.transform(res.locals.payload?.data, 'yaml'));
|
||||
return res.status(200).send(exportService.transform(res.locals['payload']?.data, 'yaml'));
|
||||
}
|
||||
}
|
||||
|
||||
if (Buffer.isBuffer(res.locals.payload)) {
|
||||
return res.end(res.locals.payload);
|
||||
} else if (res.locals.payload) {
|
||||
return res.json(res.locals.payload);
|
||||
if (Buffer.isBuffer(res.locals['payload'])) {
|
||||
return res.end(res.locals['payload']);
|
||||
} else if (res.locals['payload']) {
|
||||
return res.json(res.locals['payload']);
|
||||
} else {
|
||||
return res.status(204).end();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ const sanitizeQueryMiddleware: RequestHandler = (req, _res, next) => {
|
||||
|
||||
req.sanitizedQuery = sanitizeQuery(
|
||||
{
|
||||
fields: req.query.fields || '*',
|
||||
fields: req.query['fields'] || '*',
|
||||
...req.query,
|
||||
},
|
||||
req.accountability || null
|
||||
|
||||
@@ -9,10 +9,10 @@ type Options = {
|
||||
export default defineOperationApi<Options>({
|
||||
id: 'exec',
|
||||
handler: async ({ code }, { data, env }) => {
|
||||
const allowedModules = env.FLOWS_EXEC_ALLOWED_MODULES ? toArray(env.FLOWS_EXEC_ALLOWED_MODULES) : [];
|
||||
const allowedModules = env['FLOWS_EXEC_ALLOWED_MODULES'] ? toArray(env['FLOWS_EXEC_ALLOWED_MODULES']) : [];
|
||||
const allowedModulesBuiltIn: string[] = [];
|
||||
const allowedModulesExternal: string[] = [];
|
||||
const allowedEnv = data.$env ?? {};
|
||||
const allowedEnv = data['$env'] ?? {};
|
||||
|
||||
const opts: NodeVMOptions = {
|
||||
eval: false,
|
||||
|
||||
@@ -16,7 +16,7 @@ export function createRateLimiter(
|
||||
configPrefix = 'RATE_LIMITER',
|
||||
configOverrides?: IRateLimiterOptionsOverrides
|
||||
): RateLimiterAbstract {
|
||||
switch (env.RATE_LIMITER_STORE) {
|
||||
switch (env['RATE_LIMITER_STORE']) {
|
||||
case 'redis':
|
||||
return new RateLimiterRedis(getConfig('redis', configPrefix, configOverrides));
|
||||
case 'memcache':
|
||||
|
||||
@@ -4,11 +4,11 @@ import { getEnv } from '../env';
|
||||
export const validateIP = async (ip: string, url: string) => {
|
||||
const env = getEnv();
|
||||
|
||||
if (env.IMPORT_IP_DENY_LIST.includes(ip)) {
|
||||
if (env['IMPORT_IP_DENY_LIST'].includes(ip)) {
|
||||
throw new Error(`Requested URL "${url}" resolves to a denied IP address`);
|
||||
}
|
||||
|
||||
if (env.IMPORT_IP_DENY_LIST.includes('0.0.0.0')) {
|
||||
if (env['IMPORT_IP_DENY_LIST'].includes('0.0.0.0')) {
|
||||
const networkInterfaces = os.networkInterfaces();
|
||||
|
||||
for (const networkInfo of Object.values(networkInterfaces)) {
|
||||
|
||||
@@ -94,7 +94,7 @@ export async function createServer(): Promise<http.Server> {
|
||||
return server;
|
||||
|
||||
async function beforeShutdown() {
|
||||
if (env.NODE_ENV !== 'development') {
|
||||
if (env['NODE_ENV'] !== 'development') {
|
||||
logger.info('Shutting down...');
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,7 @@ export async function createServer(): Promise<http.Server> {
|
||||
}
|
||||
);
|
||||
|
||||
if (env.NODE_ENV !== 'development') {
|
||||
if (env['NODE_ENV'] !== 'development') {
|
||||
logger.info('Directus shut down OK. Bye bye!');
|
||||
}
|
||||
}
|
||||
@@ -126,8 +126,8 @@ export async function createServer(): Promise<http.Server> {
|
||||
export async function startServer(): Promise<void> {
|
||||
const server = await createServer();
|
||||
|
||||
const host = env.HOST;
|
||||
const port = env.PORT;
|
||||
const host = env['HOST'];
|
||||
const port = env['PORT'];
|
||||
|
||||
server
|
||||
.listen(port, host, () => {
|
||||
|
||||
@@ -23,11 +23,11 @@ export class ActivityService extends ItemsService {
|
||||
this.usersService = new UsersService({ schema: this.schema });
|
||||
}
|
||||
|
||||
async createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey> {
|
||||
if (data.action === Action.COMMENT && typeof data.comment === 'string') {
|
||||
override async createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey> {
|
||||
if (data['action'] === Action.COMMENT && typeof data['comment'] === 'string') {
|
||||
const usersRegExp = new RegExp(/@[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}/gi);
|
||||
|
||||
const mentions = uniq(data.comment.match(usersRegExp) ?? []);
|
||||
const mentions = uniq(data['comment'].match(usersRegExp) ?? []);
|
||||
|
||||
const sender = await this.usersService.readOne(this.accountability!.user!, {
|
||||
fields: ['id', 'first_name', 'last_name', 'email'],
|
||||
@@ -42,9 +42,9 @@ export class ActivityService extends ItemsService {
|
||||
|
||||
const accountability: Accountability = {
|
||||
user: userID,
|
||||
role: user.role?.id ?? null,
|
||||
admin: user.role?.admin_access ?? null,
|
||||
app: user.role?.app_access ?? null,
|
||||
role: user['role']?.id ?? null,
|
||||
admin: user['role']?.admin_access ?? null,
|
||||
app: user['role']?.app_access ?? null,
|
||||
};
|
||||
|
||||
accountability.permissions = await getPermissions(accountability, this.schema);
|
||||
@@ -53,7 +53,7 @@ export class ActivityService extends ItemsService {
|
||||
const usersService = new UsersService({ schema: this.schema, accountability });
|
||||
|
||||
try {
|
||||
await authorizationService.checkAccess('read', data.collection, data.item);
|
||||
await authorizationService.checkAccess('read', data['collection'], data['item']);
|
||||
|
||||
const templateData = await usersService.readByQuery({
|
||||
fields: ['id', 'first_name', 'last_name', 'email'],
|
||||
@@ -61,11 +61,11 @@ export class ActivityService extends ItemsService {
|
||||
});
|
||||
|
||||
const userPreviews = templateData.reduce((acc, user) => {
|
||||
acc[user.id] = `<em>${userName(user)}</em>`;
|
||||
acc[user['id']] = `<em>${userName(user)}</em>`;
|
||||
return acc;
|
||||
}, {} as Record<string, string>);
|
||||
|
||||
let comment = data.comment;
|
||||
let comment = data['comment'];
|
||||
|
||||
for (const mention of mentions) {
|
||||
const uuid = mention.substring(1);
|
||||
@@ -83,18 +83,18 @@ ${userName(sender)} has mentioned you in a comment:
|
||||
|
||||
${comment}
|
||||
|
||||
<a href="${new Url(env.PUBLIC_URL)
|
||||
.addPath('admin', 'content', data.collection, data.item)
|
||||
<a href="${new Url(env['PUBLIC_URL'])
|
||||
.addPath('admin', 'content', data['collection'], data['item'])
|
||||
.toString()}">Click here to view.</a>
|
||||
`;
|
||||
|
||||
await this.notificationsService.createOne({
|
||||
recipient: userID,
|
||||
sender: sender.id,
|
||||
subject: `You were mentioned in ${data.collection}`,
|
||||
sender: sender['id'],
|
||||
subject: `You were mentioned in ${data['collection']}`,
|
||||
message,
|
||||
collection: data.collection,
|
||||
item: data.item,
|
||||
collection: data['collection'],
|
||||
item: data['item'],
|
||||
});
|
||||
} catch (err: any) {
|
||||
if (err instanceof ForbiddenException) {
|
||||
|
||||
@@ -145,8 +145,8 @@ export class AssetsService {
|
||||
if (
|
||||
!width ||
|
||||
!height ||
|
||||
width > env.ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION ||
|
||||
height > env.ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION
|
||||
width > env['ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION'] ||
|
||||
height > env['ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION']
|
||||
) {
|
||||
throw new IllegalAssetTransformation(
|
||||
`Image is too large to be transformed, or image size couldn't be determined.`
|
||||
@@ -155,7 +155,7 @@ export class AssetsService {
|
||||
|
||||
const { queue, process } = sharp.counters();
|
||||
|
||||
if (queue + process > env.ASSETS_TRANSFORM_MAX_CONCURRENT) {
|
||||
if (queue + process > env['ASSETS_TRANSFORM_MAX_CONCURRENT']) {
|
||||
throw new ServiceUnavailableException('Server too busy', {
|
||||
service: 'files',
|
||||
});
|
||||
@@ -164,13 +164,13 @@ export class AssetsService {
|
||||
const readStream = await storage.location(file.storage).read(file.filename_disk, range);
|
||||
|
||||
const transformer = sharp({
|
||||
limitInputPixels: Math.pow(env.ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION, 2),
|
||||
limitInputPixels: Math.pow(env['ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION'], 2),
|
||||
sequentialRead: true,
|
||||
failOn: env.ASSETS_INVALID_IMAGE_SENSITIVITY_LEVEL,
|
||||
failOn: env['ASSETS_INVALID_IMAGE_SENSITIVITY_LEVEL'],
|
||||
});
|
||||
|
||||
transformer.timeout({
|
||||
seconds: clamp(Math.round(getMilliseconds(env.ASSETS_TRANSFORM_TIMEOUT, 0) / 1000), 1, 3600),
|
||||
seconds: clamp(Math.round(getMilliseconds(env['ASSETS_TRANSFORM_TIMEOUT'], 0) / 1000), 1, 3600),
|
||||
});
|
||||
|
||||
if (transforms.find((transform) => transform[0] === 'rotate') === undefined) transformer.rotate();
|
||||
|
||||
@@ -50,7 +50,7 @@ export class AuthenticationService {
|
||||
): Promise<LoginResult> {
|
||||
const { nanoid } = await import('nanoid');
|
||||
|
||||
const STALL_TIME = env.LOGIN_STALL_TIME;
|
||||
const STALL_TIME = env['LOGIN_STALL_TIME'];
|
||||
const timeStart = performance.now();
|
||||
|
||||
const provider = getAuthProvider(providerName);
|
||||
@@ -203,13 +203,13 @@ export class AuthenticationService {
|
||||
}
|
||||
);
|
||||
|
||||
const accessToken = jwt.sign(customClaims, env.SECRET as string, {
|
||||
expiresIn: env.ACCESS_TOKEN_TTL,
|
||||
const accessToken = jwt.sign(customClaims, env['SECRET'] as string, {
|
||||
expiresIn: env['ACCESS_TOKEN_TTL'],
|
||||
issuer: 'directus',
|
||||
});
|
||||
|
||||
const refreshToken = nanoid(64);
|
||||
const refreshTokenExpiration = new Date(Date.now() + getMilliseconds(env.REFRESH_TOKEN_TTL, 0));
|
||||
const refreshTokenExpiration = new Date(Date.now() + getMilliseconds(env['REFRESH_TOKEN_TTL'], 0));
|
||||
|
||||
await this.knex('directus_sessions').insert({
|
||||
token: refreshToken,
|
||||
@@ -247,7 +247,7 @@ export class AuthenticationService {
|
||||
return {
|
||||
accessToken,
|
||||
refreshToken,
|
||||
expires: getMilliseconds(env.ACCESS_TOKEN_TTL),
|
||||
expires: getMilliseconds(env['ACCESS_TOKEN_TTL']),
|
||||
id: user.id,
|
||||
};
|
||||
}
|
||||
@@ -358,13 +358,13 @@ export class AuthenticationService {
|
||||
}
|
||||
);
|
||||
|
||||
const accessToken = jwt.sign(customClaims, env.SECRET as string, {
|
||||
expiresIn: env.ACCESS_TOKEN_TTL,
|
||||
const accessToken = jwt.sign(customClaims, env['SECRET'] as string, {
|
||||
expiresIn: env['ACCESS_TOKEN_TTL'],
|
||||
issuer: 'directus',
|
||||
});
|
||||
|
||||
const newRefreshToken = nanoid(64);
|
||||
const refreshTokenExpiration = new Date(Date.now() + getMilliseconds(env.REFRESH_TOKEN_TTL, 0));
|
||||
const refreshTokenExpiration = new Date(Date.now() + getMilliseconds(env['REFRESH_TOKEN_TTL'], 0));
|
||||
|
||||
await this.knex('directus_sessions')
|
||||
.update({
|
||||
@@ -380,7 +380,7 @@ export class AuthenticationService {
|
||||
return {
|
||||
accessToken,
|
||||
refreshToken: newRefreshToken,
|
||||
expires: getMilliseconds(env.ACCESS_TOKEN_TTL),
|
||||
expires: getMilliseconds(env['ACCESS_TOKEN_TTL']),
|
||||
id: record.user_id,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ export class CollectionsService {
|
||||
|
||||
return payload.collection;
|
||||
} finally {
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ export class CollectionsService {
|
||||
|
||||
return collections;
|
||||
} finally {
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
|
||||
@@ -306,8 +306,8 @@ export class CollectionsService {
|
||||
}
|
||||
}
|
||||
|
||||
if (env.DB_EXCLUDE_TABLES) {
|
||||
return collections.filter((collection) => env.DB_EXCLUDE_TABLES.includes(collection.collection) === false);
|
||||
if (env['DB_EXCLUDE_TABLES']) {
|
||||
return collections.filter((collection) => env['DB_EXCLUDE_TABLES'].includes(collection.collection) === false);
|
||||
}
|
||||
|
||||
return collections;
|
||||
@@ -396,7 +396,7 @@ export class CollectionsService {
|
||||
|
||||
return collectionKey;
|
||||
} finally {
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
|
||||
@@ -452,7 +452,7 @@ export class CollectionsService {
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
|
||||
@@ -502,7 +502,7 @@ export class CollectionsService {
|
||||
|
||||
return collectionKeys;
|
||||
} finally {
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
|
||||
@@ -631,7 +631,7 @@ export class CollectionsService {
|
||||
|
||||
return collectionKey;
|
||||
} finally {
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
|
||||
@@ -679,7 +679,7 @@ export class CollectionsService {
|
||||
|
||||
return collectionKeys;
|
||||
} finally {
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -337,7 +337,7 @@ export class FieldsService {
|
||||
await this.helpers.schema.postColumnChange();
|
||||
}
|
||||
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
|
||||
@@ -456,7 +456,7 @@ export class FieldsService {
|
||||
await this.helpers.schema.postColumnChange();
|
||||
}
|
||||
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
|
||||
@@ -571,11 +571,11 @@ export class FieldsService {
|
||||
const collectionMetaUpdates: Record<string, null> = {};
|
||||
|
||||
if (collectionMeta?.archive_field === field) {
|
||||
collectionMetaUpdates.archive_field = null;
|
||||
collectionMetaUpdates['archive_field'] = null;
|
||||
}
|
||||
|
||||
if (collectionMeta?.sort_field === field) {
|
||||
collectionMetaUpdates.sort_field = null;
|
||||
collectionMetaUpdates['sort_field'] = null;
|
||||
}
|
||||
|
||||
if (Object.keys(collectionMetaUpdates).length > 0) {
|
||||
@@ -622,7 +622,7 @@ export class FieldsService {
|
||||
await this.helpers.schema.postColumnChange();
|
||||
}
|
||||
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ export class FilesService extends ItemsService {
|
||||
|
||||
await sudoService.updateOne(primaryKey, payload, { emitEvents: false });
|
||||
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ export class FilesService extends ItemsService {
|
||||
/**
|
||||
* Extract metadata from a buffer's content
|
||||
*/
|
||||
async getMetadata(stream: Readable, allowList = env.FILE_METADATA_ALLOW_LIST): Promise<Metadata> {
|
||||
async getMetadata(stream: Readable, allowList = env['FILE_METADATA_ALLOW_LIST']): Promise<Metadata> {
|
||||
return new Promise((resolve, reject) => {
|
||||
pipeline(
|
||||
stream,
|
||||
@@ -214,14 +214,14 @@ export class FilesService extends ItemsService {
|
||||
}
|
||||
}
|
||||
|
||||
if (fullMetadata?.iptc?.Caption && typeof fullMetadata.iptc.Caption === 'string') {
|
||||
metadata.description = fullMetadata.iptc?.Caption;
|
||||
if (fullMetadata?.iptc?.['Caption'] && typeof fullMetadata.iptc['Caption'] === 'string') {
|
||||
metadata.description = fullMetadata.iptc?.['Caption'];
|
||||
}
|
||||
if (fullMetadata?.iptc?.Headline && typeof fullMetadata.iptc.Headline === 'string') {
|
||||
metadata.title = fullMetadata.iptc.Headline;
|
||||
if (fullMetadata?.iptc?.['Headline'] && typeof fullMetadata.iptc['Headline'] === 'string') {
|
||||
metadata.title = fullMetadata.iptc['Headline'];
|
||||
}
|
||||
if (fullMetadata?.iptc?.Keywords) {
|
||||
metadata.tags = fullMetadata.iptc.Keywords;
|
||||
if (fullMetadata?.iptc?.['Keywords']) {
|
||||
metadata.tags = fullMetadata.iptc['Keywords'];
|
||||
}
|
||||
|
||||
if (allowList === '*' || allowList?.[0] === '*') {
|
||||
@@ -277,7 +277,7 @@ export class FilesService extends ItemsService {
|
||||
|
||||
const payload = {
|
||||
filename_download: filename,
|
||||
storage: toArray(env.STORAGE_LOCATIONS)[0],
|
||||
storage: toArray(env['STORAGE_LOCATIONS'])[0],
|
||||
type: fileResponse.headers['content-type'],
|
||||
title: formatTitle(filename),
|
||||
...(body || {}),
|
||||
@@ -290,7 +290,7 @@ export class FilesService extends ItemsService {
|
||||
* Create a file (only applicable when it is not a multipart/data POST request)
|
||||
* Useful for associating metadata with existing file in storage
|
||||
*/
|
||||
async createOne(data: Partial<File>, opts?: MutationOptions): Promise<PrimaryKey> {
|
||||
override async createOne(data: Partial<File>, opts?: MutationOptions): Promise<PrimaryKey> {
|
||||
if (!data.type) {
|
||||
throw new InvalidPayloadException(`"type" is required`);
|
||||
}
|
||||
@@ -302,7 +302,7 @@ export class FilesService extends ItemsService {
|
||||
/**
|
||||
* Delete a file
|
||||
*/
|
||||
async deleteOne(key: PrimaryKey, opts?: MutationOptions): Promise<PrimaryKey> {
|
||||
override async deleteOne(key: PrimaryKey, opts?: MutationOptions): Promise<PrimaryKey> {
|
||||
await this.deleteMany([key], opts);
|
||||
return key;
|
||||
}
|
||||
@@ -310,7 +310,7 @@ export class FilesService extends ItemsService {
|
||||
/**
|
||||
* Delete multiple files
|
||||
*/
|
||||
async deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
override async deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
const storage = await getStorage();
|
||||
const files = await super.readMany(keys, { fields: ['id', 'storage'], limit: -1 });
|
||||
|
||||
@@ -321,15 +321,15 @@ export class FilesService extends ItemsService {
|
||||
await super.deleteMany(keys);
|
||||
|
||||
for (const file of files) {
|
||||
const disk = storage.location(file.storage);
|
||||
const disk = storage.location(file['storage']);
|
||||
|
||||
// Delete file + thumbnails
|
||||
for await (const filepath of disk.list(file.id)) {
|
||||
for await (const filepath of disk.list(file['id'])) {
|
||||
await disk.delete(filepath);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ export class FlowsService extends ItemsService<FlowRaw> {
|
||||
super('directus_flows', options);
|
||||
}
|
||||
|
||||
async createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey> {
|
||||
override async createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey> {
|
||||
const flowManager = getFlowManager();
|
||||
|
||||
const result = await super.createOne(data, opts);
|
||||
@@ -17,7 +17,7 @@ export class FlowsService extends ItemsService<FlowRaw> {
|
||||
return result;
|
||||
}
|
||||
|
||||
async createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
override async createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
const flowManager = getFlowManager();
|
||||
|
||||
const result = await super.createMany(data, opts);
|
||||
@@ -26,7 +26,7 @@ export class FlowsService extends ItemsService<FlowRaw> {
|
||||
return result;
|
||||
}
|
||||
|
||||
async updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
override async updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
const flowManager = getFlowManager();
|
||||
|
||||
const result = await super.updateBatch(data, opts);
|
||||
@@ -35,7 +35,7 @@ export class FlowsService extends ItemsService<FlowRaw> {
|
||||
return result;
|
||||
}
|
||||
|
||||
async updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
override async updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
const flowManager = getFlowManager();
|
||||
|
||||
const result = await super.updateMany(keys, data, opts);
|
||||
@@ -44,7 +44,7 @@ export class FlowsService extends ItemsService<FlowRaw> {
|
||||
return result;
|
||||
}
|
||||
|
||||
async deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
override async deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
const flowManager = getFlowManager();
|
||||
|
||||
// this is to prevent foreign key constraint error on directus_operations resolve/reject during cascade deletion
|
||||
|
||||
@@ -93,7 +93,7 @@ import processError from './utils/process-error';
|
||||
|
||||
const validationRules = Array.from(specifiedRules);
|
||||
|
||||
if (env.GRAPHQL_INTROSPECTION === false) {
|
||||
if (env['GRAPHQL_INTROSPECTION'] === false) {
|
||||
validationRules.push(NoSchemaIntrospectionCustomRule);
|
||||
}
|
||||
|
||||
@@ -158,9 +158,10 @@ export class GraphQLService {
|
||||
|
||||
const formattedResult: FormattedExecutionResult = {};
|
||||
|
||||
if (result.data) formattedResult.data = result.data;
|
||||
if (result.errors) formattedResult.errors = result.errors.map((error) => processError(this.accountability, error));
|
||||
if (result.extensions) formattedResult.extensions = result.extensions;
|
||||
if (result['data']) formattedResult.data = result['data'];
|
||||
if (result['errors'])
|
||||
formattedResult.errors = result['errors'].map((error) => processError(this.accountability, error));
|
||||
if (result['extensions']) formattedResult.extensions = result['extensions'];
|
||||
|
||||
return formattedResult;
|
||||
}
|
||||
@@ -313,11 +314,11 @@ export class GraphQLService {
|
||||
.reduce((acc, collection) => {
|
||||
const collectionName = this.scope === 'items' ? collection.collection : collection.collection.substring(9);
|
||||
|
||||
acc[`delete_${collectionName}_items`] = DeleteCollectionTypes.many.getResolver(
|
||||
acc[`delete_${collectionName}_items`] = DeleteCollectionTypes['many'].getResolver(
|
||||
`delete_${collection.collection}_items`
|
||||
);
|
||||
|
||||
acc[`delete_${collectionName}_item`] = DeleteCollectionTypes.one.getResolver(
|
||||
acc[`delete_${collectionName}_item`] = DeleteCollectionTypes['one'].getResolver(
|
||||
`delete_${collection.collection}_item`
|
||||
);
|
||||
|
||||
@@ -519,7 +520,7 @@ export class GraphQLService {
|
||||
|
||||
path = path.reverse().slice(0, -1);
|
||||
|
||||
let parent = context.data;
|
||||
let parent = context['data'];
|
||||
|
||||
for (const pathPart of path) {
|
||||
parent = parent[pathPart];
|
||||
@@ -981,7 +982,7 @@ export class GraphQLService {
|
||||
),
|
||||
resolve: async ({ info, context }: { info: GraphQLResolveInfo; context: Record<string, any> }) => {
|
||||
const result = await self.resolveQuery(info);
|
||||
context.data = result;
|
||||
context['data'] = result;
|
||||
return result;
|
||||
},
|
||||
};
|
||||
@@ -1035,7 +1036,7 @@ export class GraphQLService {
|
||||
},
|
||||
resolve: async ({ info, context }: { info: GraphQLResolveInfo; context: Record<string, any> }) => {
|
||||
const result = await self.resolveQuery(info);
|
||||
context.data = result;
|
||||
context['data'] = result;
|
||||
|
||||
return result;
|
||||
},
|
||||
@@ -1050,7 +1051,7 @@ export class GraphQLService {
|
||||
},
|
||||
resolve: async ({ info, context }: { info: GraphQLResolveInfo; context: Record<string, any> }) => {
|
||||
const result = await self.resolveQuery(info);
|
||||
context.data = result;
|
||||
context['data'] = result;
|
||||
return result;
|
||||
},
|
||||
});
|
||||
@@ -1264,14 +1265,14 @@ export class GraphQLService {
|
||||
}
|
||||
}
|
||||
|
||||
DeleteCollectionTypes.many = schemaComposer.createObjectTC({
|
||||
DeleteCollectionTypes['many'] = schemaComposer.createObjectTC({
|
||||
name: `delete_many`,
|
||||
fields: {
|
||||
ids: new GraphQLNonNull(new GraphQLList(GraphQLID)),
|
||||
},
|
||||
});
|
||||
|
||||
DeleteCollectionTypes.one = schemaComposer.createObjectTC({
|
||||
DeleteCollectionTypes['one'] = schemaComposer.createObjectTC({
|
||||
name: `delete_one`,
|
||||
fields: {
|
||||
id: new GraphQLNonNull(GraphQLID),
|
||||
@@ -1279,9 +1280,9 @@ export class GraphQLService {
|
||||
});
|
||||
|
||||
for (const collection of Object.values(schema.delete.collections)) {
|
||||
DeleteCollectionTypes.many.addResolver({
|
||||
DeleteCollectionTypes['many'].addResolver({
|
||||
name: `delete_${collection.collection}_items`,
|
||||
type: DeleteCollectionTypes.many,
|
||||
type: DeleteCollectionTypes['many'],
|
||||
args: {
|
||||
ids: new GraphQLNonNull(new GraphQLList(GraphQLID)),
|
||||
},
|
||||
@@ -1289,9 +1290,9 @@ export class GraphQLService {
|
||||
await self.resolveMutation(args, info),
|
||||
});
|
||||
|
||||
DeleteCollectionTypes.one.addResolver({
|
||||
DeleteCollectionTypes['one'].addResolver({
|
||||
name: `delete_${collection.collection}_item`,
|
||||
type: DeleteCollectionTypes.one,
|
||||
type: DeleteCollectionTypes['one'],
|
||||
args: {
|
||||
id: new GraphQLNonNull(GraphQLID),
|
||||
},
|
||||
@@ -1330,13 +1331,13 @@ export class GraphQLService {
|
||||
collection = collection.slice(0, -6);
|
||||
}
|
||||
}
|
||||
if (args.id) {
|
||||
if (args['id']) {
|
||||
query.filter = {
|
||||
_and: [
|
||||
query.filter || {},
|
||||
{
|
||||
[this.schema.collections[collection].primary]: {
|
||||
_eq: args.id,
|
||||
_eq: args['id'],
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1354,7 +1355,7 @@ export class GraphQLService {
|
||||
|
||||
const result = await this.read(collection, query);
|
||||
|
||||
if (args.id) {
|
||||
if (args['id']) {
|
||||
return result?.[0] || null;
|
||||
}
|
||||
|
||||
@@ -1362,8 +1363,8 @@ export class GraphQLService {
|
||||
// for every entry in result add a group field based on query.group;
|
||||
const aggregateKeys = Object.keys(query.aggregate ?? {});
|
||||
|
||||
result.map((field: Item) => {
|
||||
field.group = omit(field, aggregateKeys);
|
||||
result['map']((field: Item) => {
|
||||
field['group'] = omit(field, aggregateKeys);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1395,7 +1396,7 @@ export class GraphQLService {
|
||||
if (collection.endsWith('_item')) collection = collection.slice(0, -5);
|
||||
|
||||
if (singleton && action === 'update') {
|
||||
return await this.upsertSingleton(collection, args.data, query);
|
||||
return await this.upsertSingleton(collection, args['data'], query);
|
||||
}
|
||||
|
||||
const service = this.getService(collection);
|
||||
@@ -1404,22 +1405,24 @@ export class GraphQLService {
|
||||
try {
|
||||
if (single) {
|
||||
if (action === 'create') {
|
||||
const key = await service.createOne(args.data);
|
||||
const key = await service.createOne(args['data']);
|
||||
return hasQuery ? await service.readOne(key, query) : true;
|
||||
}
|
||||
|
||||
if (action === 'update') {
|
||||
const key = await service.updateOne(args.id, args.data);
|
||||
const key = await service.updateOne(args['id'], args['data']);
|
||||
return hasQuery ? await service.readOne(key, query) : true;
|
||||
}
|
||||
|
||||
if (action === 'delete') {
|
||||
await service.deleteOne(args.id);
|
||||
return { id: args.id };
|
||||
await service.deleteOne(args['id']);
|
||||
return { id: args['id'] };
|
||||
}
|
||||
|
||||
return undefined;
|
||||
} else {
|
||||
if (action === 'create') {
|
||||
const keys = await service.createMany(args.data);
|
||||
const keys = await service.createMany(args['data']);
|
||||
return hasQuery ? await service.readMany(keys, query) : true;
|
||||
}
|
||||
|
||||
@@ -1427,18 +1430,20 @@ export class GraphQLService {
|
||||
const keys: PrimaryKey[] = [];
|
||||
|
||||
if (batchUpdate) {
|
||||
keys.push(...(await service.updateBatch(args.data)));
|
||||
keys.push(...(await service.updateBatch(args['data'])));
|
||||
} else {
|
||||
keys.push(...(await service.updateMany(args.ids, args.data)));
|
||||
keys.push(...(await service.updateMany(args['ids'], args['data'])));
|
||||
}
|
||||
|
||||
return hasQuery ? await service.readMany(keys, query) : true;
|
||||
}
|
||||
|
||||
if (action === 'delete') {
|
||||
const keys = await service.deleteMany(args.ids);
|
||||
const keys = await service.deleteMany(args['ids']);
|
||||
return { ids: keys };
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
} catch (err: any) {
|
||||
return this.formatError(err);
|
||||
@@ -1490,7 +1495,7 @@ export class GraphQLService {
|
||||
* of arguments
|
||||
*/
|
||||
parseArgs(args: readonly ArgumentNode[], variableValues: GraphQLResolveInfo['variableValues']): Record<string, any> {
|
||||
if (!args || args.length === 0) return {};
|
||||
if (!args || args['length'] === 0) return {};
|
||||
|
||||
const parse = (node: ValueNode): any => {
|
||||
switch (node.kind) {
|
||||
@@ -1515,7 +1520,7 @@ export class GraphQLService {
|
||||
}
|
||||
};
|
||||
|
||||
const argsObject = Object.fromEntries(args.map((arg) => [arg.name.value, parse(arg.value)]));
|
||||
const argsObject = Object.fromEntries(args['map']((arg) => [arg.name.value, parse(arg.value)]));
|
||||
|
||||
return argsObject;
|
||||
}
|
||||
@@ -1712,10 +1717,10 @@ export class GraphQLService {
|
||||
*/
|
||||
formatError(error: BaseException | BaseException[]): GraphQLError {
|
||||
if (Array.isArray(error)) {
|
||||
error[0].extensions.code = error[0].code;
|
||||
error[0].extensions['code'] = error[0].code;
|
||||
return new GraphQLError(error[0].message, undefined, undefined, undefined, undefined, error[0]);
|
||||
}
|
||||
error.extensions.code = error.code;
|
||||
error.extensions['code'] = error.code;
|
||||
return new GraphQLError(error.message, undefined, undefined, undefined, undefined, error);
|
||||
}
|
||||
|
||||
@@ -1938,7 +1943,7 @@ export class GraphQLService {
|
||||
const service = new GraphQLService({
|
||||
schema: this.schema,
|
||||
accountability: this.accountability,
|
||||
scope: args.scope ?? 'items',
|
||||
scope: args['scope'] ?? 'items',
|
||||
});
|
||||
return service.getSchema('sdl');
|
||||
},
|
||||
@@ -2009,19 +2014,19 @@ export class GraphQLService {
|
||||
schema: this.schema,
|
||||
});
|
||||
const result = await authenticationService.login(DEFAULT_AUTH_PROVIDER, args, args?.otp);
|
||||
if (args.mode === 'cookie') {
|
||||
res?.cookie(env.REFRESH_TOKEN_COOKIE_NAME, result.refreshToken, {
|
||||
if (args['mode'] === 'cookie') {
|
||||
res?.cookie(env['REFRESH_TOKEN_COOKIE_NAME'], result['refreshToken'], {
|
||||
httpOnly: true,
|
||||
domain: env.REFRESH_TOKEN_COOKIE_DOMAIN,
|
||||
maxAge: getMilliseconds(env.REFRESH_TOKEN_TTL),
|
||||
secure: env.REFRESH_TOKEN_COOKIE_SECURE ?? false,
|
||||
sameSite: (env.REFRESH_TOKEN_COOKIE_SAME_SITE as 'lax' | 'strict' | 'none') || 'strict',
|
||||
domain: env['REFRESH_TOKEN_COOKIE_DOMAIN'],
|
||||
maxAge: getMilliseconds(env['REFRESH_TOKEN_TTL']),
|
||||
secure: env['REFRESH_TOKEN_COOKIE_SECURE'] ?? false,
|
||||
sameSite: (env['REFRESH_TOKEN_COOKIE_SAME_SITE'] as 'lax' | 'strict' | 'none') || 'strict',
|
||||
});
|
||||
}
|
||||
return {
|
||||
access_token: result.accessToken,
|
||||
expires: result.expires,
|
||||
refresh_token: result.refreshToken,
|
||||
access_token: result['accessToken'],
|
||||
expires: result['expires'],
|
||||
refresh_token: result['refreshToken'],
|
||||
};
|
||||
},
|
||||
},
|
||||
@@ -2046,24 +2051,24 @@ export class GraphQLService {
|
||||
accountability: accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
const currentRefreshToken = args.refresh_token || req?.cookies[env.REFRESH_TOKEN_COOKIE_NAME];
|
||||
const currentRefreshToken = args['refresh_token'] || req?.cookies[env['REFRESH_TOKEN_COOKIE_NAME']];
|
||||
if (!currentRefreshToken) {
|
||||
throw new InvalidPayloadException(`"refresh_token" is required in either the JSON payload or Cookie`);
|
||||
}
|
||||
const result = await authenticationService.refresh(currentRefreshToken);
|
||||
if (args.mode === 'cookie') {
|
||||
res?.cookie(env.REFRESH_TOKEN_COOKIE_NAME, result.refreshToken, {
|
||||
if (args['mode'] === 'cookie') {
|
||||
res?.cookie(env['REFRESH_TOKEN_COOKIE_NAME'], result['refreshToken'], {
|
||||
httpOnly: true,
|
||||
domain: env.REFRESH_TOKEN_COOKIE_DOMAIN,
|
||||
maxAge: getMilliseconds(env.REFRESH_TOKEN_TTL),
|
||||
secure: env.REFRESH_TOKEN_COOKIE_SECURE ?? false,
|
||||
sameSite: (env.REFRESH_TOKEN_COOKIE_SAME_SITE as 'lax' | 'strict' | 'none') || 'strict',
|
||||
domain: env['REFRESH_TOKEN_COOKIE_DOMAIN'],
|
||||
maxAge: getMilliseconds(env['REFRESH_TOKEN_TTL']),
|
||||
secure: env['REFRESH_TOKEN_COOKIE_SECURE'] ?? false,
|
||||
sameSite: (env['REFRESH_TOKEN_COOKIE_SAME_SITE'] as 'lax' | 'strict' | 'none') || 'strict',
|
||||
});
|
||||
}
|
||||
return {
|
||||
access_token: result.accessToken,
|
||||
expires: result.expires,
|
||||
refresh_token: result.refreshToken,
|
||||
access_token: result['accessToken'],
|
||||
expires: result['expires'],
|
||||
refresh_token: result['refreshToken'],
|
||||
};
|
||||
},
|
||||
},
|
||||
@@ -2087,7 +2092,7 @@ export class GraphQLService {
|
||||
accountability: accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
const currentRefreshToken = args.refresh_token || req?.cookies[env.REFRESH_TOKEN_COOKIE_NAME];
|
||||
const currentRefreshToken = args['refresh_token'] || req?.cookies[env['REFRESH_TOKEN_COOKIE_NAME']];
|
||||
if (!currentRefreshToken) {
|
||||
throw new InvalidPayloadException(`"refresh_token" is required in either the JSON payload or Cookie`);
|
||||
}
|
||||
@@ -2114,7 +2119,7 @@ export class GraphQLService {
|
||||
const service = new UsersService({ accountability, schema: this.schema });
|
||||
|
||||
try {
|
||||
await service.requestPasswordReset(args.email, args.reset_url || null);
|
||||
await service.requestPasswordReset(args['email'], args['reset_url'] || null);
|
||||
} catch (err: any) {
|
||||
if (err instanceof InvalidPayloadException) {
|
||||
throw err;
|
||||
@@ -2142,7 +2147,7 @@ export class GraphQLService {
|
||||
if (origin) accountability.origin = origin;
|
||||
|
||||
const service = new UsersService({ accountability, schema: this.schema });
|
||||
await service.resetPassword(args.token, args.password);
|
||||
await service.resetPassword(args['token'], args['password']);
|
||||
return true;
|
||||
},
|
||||
},
|
||||
@@ -2167,7 +2172,7 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
await authService.verifyPassword(this.accountability.user, args.password);
|
||||
await authService.verifyPassword(this.accountability.user, args['password']);
|
||||
const { url, secret } = await service.generateTFA(this.accountability.user);
|
||||
return { secret, otpauth_url: url };
|
||||
},
|
||||
@@ -2185,7 +2190,7 @@ export class GraphQLService {
|
||||
schema: this.schema,
|
||||
});
|
||||
|
||||
await service.enableTFA(this.accountability.user, args.otp, args.secret);
|
||||
await service.enableTFA(this.accountability.user, args['otp'], args['secret']);
|
||||
return true;
|
||||
},
|
||||
},
|
||||
@@ -2200,7 +2205,7 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
const otpValid = await service.verifyOTP(this.accountability.user, args.otp);
|
||||
const otpValid = await service.verifyOTP(this.accountability.user, args['otp']);
|
||||
if (otpValid === false) {
|
||||
throw new InvalidPayloadException(`"otp" is invalid`);
|
||||
}
|
||||
@@ -2214,7 +2219,7 @@ export class GraphQLService {
|
||||
string: new GraphQLNonNull(GraphQLString),
|
||||
},
|
||||
resolve: async (_, args) => {
|
||||
return await generateHash(args.string);
|
||||
return await generateHash(args['string']);
|
||||
},
|
||||
},
|
||||
utils_hash_verify: {
|
||||
@@ -2224,7 +2229,7 @@ export class GraphQLService {
|
||||
hash: new GraphQLNonNull(GraphQLString),
|
||||
},
|
||||
resolve: async (_, args) => {
|
||||
return await argon2.verify(args.hash, args.string);
|
||||
return await argon2.verify(args['hash'], args['string']);
|
||||
},
|
||||
},
|
||||
utils_sort: {
|
||||
@@ -2240,7 +2245,7 @@ export class GraphQLService {
|
||||
schema: this.schema,
|
||||
});
|
||||
const { item, to } = args;
|
||||
await service.sort(args.collection, { item, to });
|
||||
await service.sort(args['collection'], { item, to });
|
||||
return true;
|
||||
},
|
||||
},
|
||||
@@ -2254,7 +2259,7 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
await service.revert(args.revision);
|
||||
await service.revert(args['revision']);
|
||||
return true;
|
||||
},
|
||||
},
|
||||
@@ -2284,7 +2289,7 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
await service.acceptInvite(args.token, args.password);
|
||||
await service.acceptInvite(args['token'], args['password']);
|
||||
return true;
|
||||
},
|
||||
},
|
||||
@@ -2339,7 +2344,7 @@ export class GraphQLService {
|
||||
schema: this.schema,
|
||||
});
|
||||
|
||||
return await collectionsService.readOne(args.name);
|
||||
return await collectionsService.readOne(args['name']);
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -2406,7 +2411,7 @@ export class GraphQLService {
|
||||
schema: this.schema,
|
||||
});
|
||||
|
||||
return await service.readAll(args.collection);
|
||||
return await service.readAll(args['collection']);
|
||||
},
|
||||
},
|
||||
fields_by_name: {
|
||||
@@ -2420,7 +2425,7 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
return await service.readOne(args.collection, args.field);
|
||||
return await service.readOne(args['collection'], args['field']);
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -2479,7 +2484,7 @@ export class GraphQLService {
|
||||
schema: this.schema,
|
||||
});
|
||||
|
||||
return await service.readAll(args.collection);
|
||||
return await service.readAll(args['collection']);
|
||||
},
|
||||
},
|
||||
relations_by_name: {
|
||||
@@ -2493,7 +2498,7 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
return await service.readOne(args.collection, args.field);
|
||||
return await service.readOne(args['collection'], args['field']);
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -2517,7 +2522,7 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
const collectionKey = await collectionsService.createOne(args.data);
|
||||
const collectionKey = await collectionsService.createOne(args['data']);
|
||||
return await collectionsService.readOne(collectionKey);
|
||||
},
|
||||
},
|
||||
@@ -2534,7 +2539,7 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
const collectionKey = await collectionsService.updateOne(args.collection, args.data);
|
||||
const collectionKey = await collectionsService.updateOne(args['collection'], args['data']);
|
||||
return await collectionsService.readOne(collectionKey);
|
||||
},
|
||||
},
|
||||
@@ -2553,8 +2558,8 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
await collectionsService.deleteOne(args.collection);
|
||||
return { collection: args.collection };
|
||||
await collectionsService.deleteOne(args['collection']);
|
||||
return { collection: args['collection'] };
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -2571,8 +2576,8 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
await service.createField(args.collection, args.data);
|
||||
return await service.readOne(args.collection, args.data.field);
|
||||
await service.createField(args['collection'], args['data']);
|
||||
return await service.readOne(args['collection'], args['data'].field);
|
||||
},
|
||||
},
|
||||
update_fields_item: {
|
||||
@@ -2587,11 +2592,11 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
await service.updateField(args.collection, {
|
||||
...args.data,
|
||||
field: args.field,
|
||||
await service.updateField(args['collection'], {
|
||||
...args['data'],
|
||||
field: args['field'],
|
||||
});
|
||||
return await service.readOne(args.collection, args.data.field);
|
||||
return await service.readOne(args['collection'], args['data'].field);
|
||||
},
|
||||
},
|
||||
delete_fields_item: {
|
||||
@@ -2611,7 +2616,7 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
await service.deleteField(args.collection, args.field);
|
||||
await service.deleteField(args['collection'], args['field']);
|
||||
const { collection, field } = args;
|
||||
return { collection, field };
|
||||
},
|
||||
@@ -2630,8 +2635,8 @@ export class GraphQLService {
|
||||
schema: this.schema,
|
||||
});
|
||||
|
||||
await relationsService.createOne(args.data);
|
||||
return await relationsService.readOne(args.data.collection, args.data.field);
|
||||
await relationsService.createOne(args['data']);
|
||||
return await relationsService.readOne(args['data'].collection, args['data'].field);
|
||||
},
|
||||
},
|
||||
update_relations_item: {
|
||||
@@ -2647,8 +2652,8 @@ export class GraphQLService {
|
||||
schema: this.schema,
|
||||
});
|
||||
|
||||
await relationsService.updateOne(args.collection, args.field, args.data);
|
||||
return await relationsService.readOne(args.data.collection, args.data.field);
|
||||
await relationsService.updateOne(args['collection'], args['field'], args['data']);
|
||||
return await relationsService.readOne(args['data'].collection, args['data'].field);
|
||||
},
|
||||
},
|
||||
delete_relations_item: {
|
||||
@@ -2668,8 +2673,8 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
await relationsService.deleteOne(args.collection, args.field);
|
||||
return { collection: args.collection, field: args.field };
|
||||
await relationsService.deleteOne(args['collection'], args['field']);
|
||||
return { collection: args['collection'], field: args['field'] };
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -2708,7 +2713,7 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
});
|
||||
|
||||
await service.updateOne(this.accountability.user, args.data);
|
||||
await service.updateOne(this.accountability.user, args['data']);
|
||||
|
||||
if ('directus_users' in ReadCollectionTypes) {
|
||||
const selections = this.replaceFragmentsInSelections(
|
||||
@@ -2778,7 +2783,7 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
const primaryKey = await service.updateOne(args.id, { comment: args.comment });
|
||||
const primaryKey = await service.updateOne(args['id'], { comment: args['comment'] });
|
||||
|
||||
if ('directus_activity' in ReadCollectionTypes) {
|
||||
const selections = this.replaceFragmentsInSelections(
|
||||
@@ -2799,7 +2804,7 @@ export class GraphQLService {
|
||||
if ('directus_activity' in schema.delete.collections) {
|
||||
schemaComposer.Mutation.addFields({
|
||||
delete_comment: {
|
||||
type: DeleteCollectionTypes.one,
|
||||
type: DeleteCollectionTypes['one'],
|
||||
args: {
|
||||
id: new GraphQLNonNull(GraphQLID),
|
||||
},
|
||||
@@ -2808,8 +2813,8 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
await service.deleteOne(args.id);
|
||||
return { id: args.id };
|
||||
await service.deleteOne(args['id']);
|
||||
return { id: args['id'] };
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -2828,7 +2833,7 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
const primaryKey = await service.importOne(args.url, args.data);
|
||||
const primaryKey = await service.importOne(args['url'], args['data']);
|
||||
|
||||
if ('directus_files' in ReadCollectionTypes) {
|
||||
const selections = this.replaceFragmentsInSelections(
|
||||
@@ -2859,7 +2864,7 @@ export class GraphQLService {
|
||||
accountability: this.accountability,
|
||||
schema: this.schema,
|
||||
});
|
||||
await service.inviteUser(args.email, args.role, args.invite_url || null);
|
||||
await service.inviteUser(args['email'], args['role'], args['invite_url'] || null);
|
||||
return true;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -224,26 +224,26 @@ export class ExportService {
|
||||
count: ['*'],
|
||||
},
|
||||
})
|
||||
.then((result) => Number(result?.[0]?.count ?? 0));
|
||||
.then((result) => Number(result?.[0]?.['count'] ?? 0));
|
||||
|
||||
const count = query.limit ? Math.min(totalCount, query.limit) : totalCount;
|
||||
|
||||
const requestedLimit = query.limit ?? -1;
|
||||
const batchesRequired = Math.ceil(count / env.EXPORT_BATCH_SIZE);
|
||||
const batchesRequired = Math.ceil(count / env['EXPORT_BATCH_SIZE']);
|
||||
|
||||
let readCount = 0;
|
||||
|
||||
for (let batch = 0; batch < batchesRequired; batch++) {
|
||||
let limit = env.EXPORT_BATCH_SIZE;
|
||||
let limit = env['EXPORT_BATCH_SIZE'];
|
||||
|
||||
if (requestedLimit > 0 && env.EXPORT_BATCH_SIZE > requestedLimit - readCount) {
|
||||
if (requestedLimit > 0 && env['EXPORT_BATCH_SIZE'] > requestedLimit - readCount) {
|
||||
limit = requestedLimit - readCount;
|
||||
}
|
||||
|
||||
const result = await service.readByQuery({
|
||||
...query,
|
||||
limit,
|
||||
offset: batch * env.EXPORT_BATCH_SIZE,
|
||||
offset: batch * env['EXPORT_BATCH_SIZE'],
|
||||
});
|
||||
|
||||
readCount += result.length;
|
||||
@@ -265,7 +265,7 @@ export class ExportService {
|
||||
schema: this.schema,
|
||||
});
|
||||
|
||||
const storage: string = toArray(env.STORAGE_LOCATIONS)[0];
|
||||
const storage: string = toArray(env['STORAGE_LOCATIONS'])[0];
|
||||
|
||||
const title = `export-${collection}-${getDateFormatted()}`;
|
||||
const filename = `${title}.${format}`;
|
||||
|
||||
@@ -90,12 +90,12 @@ describe('Integration Tests', () => {
|
||||
it(`the returned UUID primary key for MS SQL should be uppercase`, async () => {
|
||||
vi.mocked(getDatabaseClient).mockReturnValue('mssql');
|
||||
|
||||
const table = schemas.system.tables[0];
|
||||
const table = schemas['system'].tables[0];
|
||||
|
||||
const itemsService = new ItemsService(table, {
|
||||
knex: db,
|
||||
accountability: { role: 'admin', admin: true },
|
||||
schema: schemas.system.schema,
|
||||
schema: schemas['system'].schema,
|
||||
});
|
||||
|
||||
tracker.on.insert(table).responseOnce(item);
|
||||
|
||||
@@ -265,7 +265,7 @@ export class ItemsService<Item extends AnyItem = AnyItem> implements AbstractSer
|
||||
}
|
||||
}
|
||||
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
|
||||
@@ -308,7 +308,7 @@ export class ItemsService<Item extends AnyItem = AnyItem> implements AbstractSer
|
||||
}
|
||||
}
|
||||
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
|
||||
@@ -491,7 +491,7 @@ export class ItemsService<Item extends AnyItem = AnyItem> implements AbstractSer
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
}
|
||||
@@ -668,7 +668,7 @@ export class ItemsService<Item extends AnyItem = AnyItem> implements AbstractSer
|
||||
}
|
||||
});
|
||||
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
|
||||
@@ -755,7 +755,7 @@ export class ItemsService<Item extends AnyItem = AnyItem> implements AbstractSer
|
||||
return primaryKeys;
|
||||
});
|
||||
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
|
||||
@@ -844,7 +844,7 @@ export class ItemsService<Item extends AnyItem = AnyItem> implements AbstractSer
|
||||
}
|
||||
});
|
||||
|
||||
if (this.cache && env.CACHE_AUTO_PURGE && opts?.autoPurgeCache !== false) {
|
||||
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
||||
await this.cache.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import type { AbstractServiceOptions } from '../../types';
|
||||
import { Url } from '../../utils/url';
|
||||
|
||||
const liquidEngine = new Liquid({
|
||||
root: [path.resolve(env.EXTENSIONS_PATH, 'templates'), path.resolve(__dirname, 'templates')],
|
||||
root: [path.resolve(env['EXTENSIONS_PATH'], 'templates'), path.resolve(__dirname, 'templates')],
|
||||
extname: '.liquid',
|
||||
});
|
||||
|
||||
@@ -36,7 +36,7 @@ export class MailService {
|
||||
this.knex = opts?.knex || getDatabase();
|
||||
this.mailer = getMailer();
|
||||
|
||||
if (env.EMAIL_VERIFY_SETUP) {
|
||||
if (env['EMAIL_VERIFY_SETUP']) {
|
||||
this.mailer.verify((error) => {
|
||||
if (error) {
|
||||
logger.warn(`Email connection failed:`);
|
||||
@@ -52,7 +52,7 @@ export class MailService {
|
||||
|
||||
const defaultTemplateData = await this.getDefaultTemplateData();
|
||||
|
||||
const from = `${defaultTemplateData.projectName} <${options.from || (env.EMAIL_FROM as string)}>`;
|
||||
const from = `${defaultTemplateData.projectName} <${options.from || (env['EMAIL_FROM'] as string)}>`;
|
||||
|
||||
if (template) {
|
||||
let templateData = template.data;
|
||||
@@ -78,7 +78,7 @@ export class MailService {
|
||||
}
|
||||
|
||||
private async renderTemplate(template: string, variables: Record<string, any>) {
|
||||
const customTemplatePath = path.resolve(env.EXTENSIONS_PATH, 'templates', template + '.liquid');
|
||||
const customTemplatePath = path.resolve(env['EXTENSIONS_PATH'], 'templates', template + '.liquid');
|
||||
const systemTemplatePath = path.join(__dirname, 'templates', template + '.liquid');
|
||||
|
||||
const templatePath = (await fse.pathExists(customTemplatePath)) ? customTemplatePath : systemTemplatePath;
|
||||
@@ -107,7 +107,7 @@ export class MailService {
|
||||
};
|
||||
|
||||
function getProjectLogoURL(logoID?: string) {
|
||||
const projectLogoUrl = new Url(env.PUBLIC_URL);
|
||||
const projectLogoUrl = new Url(env['PUBLIC_URL']);
|
||||
|
||||
if (logoID) {
|
||||
projectLogoUrl.addPath('assets', logoID);
|
||||
|
||||
@@ -24,6 +24,7 @@ export class MetaService {
|
||||
query.meta.map((metaVal: string) => {
|
||||
if (metaVal === 'total_count') return this.totalCount(collection);
|
||||
if (metaVal === 'filter_count') return this.filterCount(collection, query);
|
||||
return undefined;
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ export class NotificationsService extends ItemsService {
|
||||
this.mailService = new MailService({ schema: this.schema, accountability: this.accountability });
|
||||
}
|
||||
|
||||
async createOne(data: Partial<Notification>, opts?: MutationOptions): Promise<PrimaryKey> {
|
||||
override async createOne(data: Partial<Notification>, opts?: MutationOptions): Promise<PrimaryKey> {
|
||||
const response = await super.createOne(data, opts);
|
||||
|
||||
await this.sendEmail(data);
|
||||
@@ -26,7 +26,7 @@ export class NotificationsService extends ItemsService {
|
||||
return response;
|
||||
}
|
||||
|
||||
async createMany(data: Partial<Notification>[], opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
override async createMany(data: Partial<Notification>[], opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
const response = await super.createMany(data, opts);
|
||||
|
||||
for (const notification of data) {
|
||||
@@ -41,18 +41,18 @@ export class NotificationsService extends ItemsService {
|
||||
const user = await this.usersService.readOne(data.recipient, {
|
||||
fields: ['id', 'email', 'email_notifications', 'role.app_access'],
|
||||
});
|
||||
const manageUserAccountUrl = new Url(env.PUBLIC_URL).addPath('admin', 'users', user.id).toString();
|
||||
const manageUserAccountUrl = new Url(env['PUBLIC_URL']).addPath('admin', 'users', user['id']).toString();
|
||||
|
||||
const html = data.message ? md(data.message) : '';
|
||||
|
||||
if (user.email && user.email_notifications === true) {
|
||||
if (user['email'] && user['email_notifications'] === true) {
|
||||
try {
|
||||
await this.mailService.send({
|
||||
template: {
|
||||
name: 'base',
|
||||
data: user.role?.app_access ? { url: manageUserAccountUrl, html } : { html },
|
||||
data: user['role']?.app_access ? { url: manageUserAccountUrl, html } : { html },
|
||||
},
|
||||
to: user.email,
|
||||
to: user['email'],
|
||||
subject: data.subject,
|
||||
});
|
||||
} catch (error: any) {
|
||||
|
||||
@@ -8,7 +8,7 @@ export class OperationsService extends ItemsService<OperationRaw> {
|
||||
super('directus_operations', options);
|
||||
}
|
||||
|
||||
async createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey> {
|
||||
override async createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey> {
|
||||
const flowManager = getFlowManager();
|
||||
|
||||
const result = await super.createOne(data, opts);
|
||||
@@ -17,7 +17,7 @@ export class OperationsService extends ItemsService<OperationRaw> {
|
||||
return result;
|
||||
}
|
||||
|
||||
async createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
override async createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
const flowManager = getFlowManager();
|
||||
|
||||
const result = await super.createMany(data, opts);
|
||||
@@ -26,7 +26,7 @@ export class OperationsService extends ItemsService<OperationRaw> {
|
||||
return result;
|
||||
}
|
||||
|
||||
async updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
override async updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
const flowManager = getFlowManager();
|
||||
|
||||
const result = await super.updateBatch(data, opts);
|
||||
@@ -35,7 +35,7 @@ export class OperationsService extends ItemsService<OperationRaw> {
|
||||
return result;
|
||||
}
|
||||
|
||||
async updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
override async updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
const flowManager = getFlowManager();
|
||||
|
||||
const result = await super.updateMany(keys, data, opts);
|
||||
@@ -44,7 +44,7 @@ export class OperationsService extends ItemsService<OperationRaw> {
|
||||
return result;
|
||||
}
|
||||
|
||||
async deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
override async deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]> {
|
||||
const flowManager = getFlowManager();
|
||||
|
||||
const result = await super.deleteMany(keys, opts);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user