Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Extensions env #22178

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/src/extensions/lib/sandbox/register/operation.ts
Expand Up @@ -24,7 +24,7 @@ export function registerOperationGenerator() {
return response.copy();
};

flowManager.addOperation(idCopied, handler);
flowManager.addOperation(idCopied, handler, false);

unregisterFunctions.push(() => {
flowManager.removeOperation(idCopied);
Expand Down
23 changes: 15 additions & 8 deletions api/src/extensions/manager.ts
@@ -1,4 +1,4 @@
import { useEnv } from '@directus/env';
import { useEnv, useExtensionsEnv } from '@directus/env';
import type {
ApiExtension,
BundleExtension,
Expand Down Expand Up @@ -66,6 +66,7 @@ const nodeResolve = nodeResolveDefault as unknown as typeof nodeResolveDefault.d
const __dirname = dirname(fileURLToPath(import.meta.url));

const env = useEnv();
const extensionsEnv = useExtensionsEnv();

const defaultOptions: ExtensionManagerOptions = {
schedule: true,
Expand Down Expand Up @@ -671,7 +672,7 @@ export class ExtensionManager {

const config = getModuleDefault(operationInstance);

const unregister = this.registerOperation(config);
const unregister = this.registerOperation({ config, isInternal: false });

this.unregisterFunctionMap.set(operation.name, async () => {
await unregister();
Expand Down Expand Up @@ -732,7 +733,7 @@ export class ExtensionManager {
for (const { config, name } of configs.operations) {
if (!extensionEnabled(name)) continue;

const unregister = this.registerOperation(config);
const unregister = this.registerOperation({ config, isInternal: false });

unregisterFunctions.push(unregister);
}
Expand Down Expand Up @@ -761,7 +762,7 @@ export class ExtensionManager {

const config = getModuleDefault(operationInstance);

this.registerOperation(config);
this.registerOperation({ config, isInternal: true });
}
}

Expand Down Expand Up @@ -847,7 +848,7 @@ export class ExtensionManager {

hookRegistrationCallback(hookRegistrationContext, {
services,
env,
env: { ...extensionsEnv },
database: getDatabase(),
emitter: this.localEmitter,
logger,
Expand All @@ -873,7 +874,7 @@ export class ExtensionManager {

endpointRegistrationCallback(scopedRouter, {
services,
env,
env: { ...extensionsEnv },
database: getDatabase(),
emitter: this.localEmitter,
logger,
Expand All @@ -890,10 +891,16 @@ export class ExtensionManager {
/**
* Register an individual operation
*/
private registerOperation(config: OperationApiConfig): PromiseCallback {
private registerOperation({
config,
isInternal,
}: {
config: OperationApiConfig;
isInternal: boolean;
}): PromiseCallback {
const flowManager = getFlowManager();

flowManager.addOperation(config.id, config.handler);
flowManager.addOperation(config.id, config.handler, isInternal);

const unregisterFunction = () => {
flowManager.removeOperation(config.id);
Expand Down
12 changes: 6 additions & 6 deletions api/src/flows.ts
Expand Up @@ -2,6 +2,7 @@ import { Action } from '@directus/constants';
import { useEnv } from '@directus/env';
import { ForbiddenError } from '@directus/errors';
import type { OperationHandler } from '@directus/extensions';
import { isSystemCollection } from '@directus/system-data';
import type { Accountability, ActionHandler, FilterHandler, Flow, Operation, SchemaOverview } from '@directus/types';
import { applyOptionsData, getRedactedString, isValidJSON, parseJSON, toArray } from '@directus/utils';
import type { Knex } from 'knex';
Expand All @@ -22,7 +23,6 @@ import { JobQueue } from './utils/job-queue.js';
import { mapValuesDeep } from './utils/map-values-deep.js';
import { redactObject } from './utils/redact-object.js';
import { scheduleSynchronizedJob, validateCron } from './utils/schedule.js';
import { isSystemCollection } from '@directus/system-data';

let flowManager: FlowManager | undefined;

Expand Down Expand Up @@ -53,7 +53,7 @@ interface FlowMessage {
class FlowManager {
private isLoaded = false;

private operations: Map<string, OperationHandler> = new Map();
private operations: Map<string, { handler: OperationHandler; isInternal: boolean }> = new Map();

private triggerHandlers: TriggerHandler[] = [];
private operationFlowHandlers: Record<string, any> = {};
Expand Down Expand Up @@ -97,8 +97,8 @@ class FlowManager {
messenger.publish<FlowMessage>('flows', { type: 'reload' });
}

public addOperation(id: string, operation: OperationHandler): void {
this.operations.set(id, operation);
public addOperation(id: string, handler: OperationHandler, isInternal: boolean): void {
this.operations.set(id, { handler, isInternal });
}

public removeOperation(id: string): void {
Expand Down Expand Up @@ -419,14 +419,14 @@ class FlowManager {
return { successor: null, status: 'unknown', data: null, options: null };
}

const handler = this.operations.get(operation.type)!;
const { handler: handler, isInternal } = this.operations.get(operation.type)!;

const options = applyOptionsData(operation.options, keyedData);

try {
let result = await handler(options, {
services,
env: useEnv(),
env: isInternal ? useEnv() : this.envs,
database: getDatabase(),
logger,
getSchema,
Expand Down
2 changes: 2 additions & 0 deletions packages/env/src/constants/defaults.ts
Expand Up @@ -104,6 +104,8 @@ export const DEFAULTS = {

IMPORT_IP_DENY_LIST: ['0.0.0.0', '169.254.169.254'],

EXTENSIONS_ENV_ALLOW_LIST: ['*'],

SERVE_APP: true,

RELATIONAL_BATCH_SIZE: 25000,
Expand Down
1 change: 1 addition & 0 deletions packages/env/src/index.ts
@@ -1 +1,2 @@
export { useEnv } from './lib/use-env.js';
export { useExtensionsEnv } from './lib/use-extensions-env.js';
34 changes: 34 additions & 0 deletions packages/env/src/lib/use-extensions-env.ts
@@ -0,0 +1,34 @@
import { toArray } from '@directus/utils';
import { pick } from 'lodash-es';
import type { Env } from '../types/env.js';
import { useEnv } from './use-env.js';

export const _cache: {
extensionsEnv: Env | undefined;
} = { extensionsEnv: undefined } as const;

export const useExtensionsEnv = () => {
if (_cache.extensionsEnv) {
return _cache.extensionsEnv;
}

const env = useEnv();

const extensionsEnvAllowList = env['EXTENSIONS_ENV_ALLOW_LIST']
? toArray(env['EXTENSIONS_ENV_ALLOW_LIST'] as string)
: [];

const extensionsEnv = extensionsEnvAllowList.includes('*') ? env : pick(env, extensionsEnvAllowList);

if (!extensionsEnvAllowList.includes('*')) {
for (const envKey of Object.keys(process.env)) {
if (!extensionsEnvAllowList.includes(envKey)) {
delete process.env[envKey];
}
}
}

_cache.extensionsEnv = extensionsEnv;

return _cache.extensionsEnv;
};