@directus/api
Version:
Directus is a real-time API and App dashboard for managing SQL database content
131 lines (130 loc) • 5.72 kB
JavaScript
import { randomUUID } from 'node:crypto';
import getDatabase from '../../database/index.js';
import { ExtensionsService } from '../../services/extensions.js';
import { getSchema } from '../../utils/get-schema.js';
import { list } from '@directus/extensions-registry';
/**
* Loads stored settings for all extensions. Creates empty new rows in extensions tables for
* extensions that don't have settings yet, and remove any settings for extensions that are no
* longer installed.
*/
export const getExtensionsSettings = async ({ local, module, registry, }) => {
const database = getDatabase();
const service = new ExtensionsService({
knex: database,
schema: await getSchema(),
});
const existingSettings = await service.extensionsItemService.readByQuery({ limit: -1 });
const newSettings = [];
const removedSettingIds = [];
const localSettings = existingSettings.filter(({ source }) => source === 'local');
const registrySettings = existingSettings.filter(({ source }) => source === 'registry');
const moduleSettings = existingSettings.filter(({ source }) => source === 'module');
const updateBundleEntriesSettings = (bundle, bundleSettings, allSettings) => {
const bundleEntriesSettings = allSettings.filter(({ bundle }) => bundle === bundleSettings.id);
// Remove settings of removed bundle entries from the DB
for (const entry of bundleEntriesSettings) {
const entryInBundle = bundle.entries.some(({ name }) => name === entry.folder);
if (entryInBundle)
continue;
removedSettingIds.push(entry.id);
}
// Add new bundle entries to the settings
for (const entry of bundle.entries) {
const settingsExist = bundleEntriesSettings.some(({ folder }) => folder === entry.name);
if (settingsExist)
continue;
newSettings.push({
id: randomUUID(),
enabled: bundleSettings.enabled,
source: bundleSettings.source,
bundle: bundleSettings.id,
folder: entry.name,
});
}
};
const generateSettingsEntry = async (folder, extension, source) => {
let marketplaceId;
if (source === 'registry') {
const marketplace = await list({ search: extension.name });
marketplaceId = marketplace.data.find((ext) => ext.name === extension.name)?.id;
}
const id = marketplaceId ?? randomUUID();
if (extension.type === 'bundle') {
newSettings.push({
id,
enabled: true,
source: source,
bundle: null,
folder: folder,
});
for (const entry of extension.entries) {
newSettings.push({
id: randomUUID(),
enabled: true,
source: source,
bundle: id,
folder: entry.name,
});
}
}
else {
newSettings.push({
id,
enabled: true,
source: source,
bundle: null,
folder: folder,
});
}
};
for (const [folder, extension] of local.entries()) {
const existingSettings = localSettings.find((settings) => settings.folder === folder);
if (existingSettings) {
if (extension.type === 'bundle') {
updateBundleEntriesSettings(extension, existingSettings, localSettings);
}
continue;
}
const settingsForName = localSettings.find((settings) => settings.folder === extension.name);
/*
* TODO: Consider removing this in follow-up versions after v10.10.0
*
* Previously, the package name (from package.json) was used to identify
* local extensions - now it's the folder name.
* If those two are different, we need to check for existing settings
* with the package name, too. On a match and if there's no other local extension
* with such a folder name, these settings can be taken over with the folder updated.
*/
if (settingsForName && !local.has(extension.name)) {
await service.extensionsItemService.updateOne(settingsForName.id, { folder });
continue;
}
await generateSettingsEntry(folder, extension, 'local');
}
for (const [folder, extension] of module.entries()) {
const existingSettings = moduleSettings.find((settings) => settings.folder === folder);
if (!existingSettings) {
await generateSettingsEntry(folder, extension, 'module');
}
else if (extension.type === 'bundle') {
updateBundleEntriesSettings(extension, existingSettings, moduleSettings);
}
}
for (const [folder, extension] of registry.entries()) {
const existingSettings = registrySettings.find((settings) => settings.folder === folder);
if (!existingSettings) {
await generateSettingsEntry(folder, extension, 'registry');
}
else if (extension.type === 'bundle') {
updateBundleEntriesSettings(extension, existingSettings, registrySettings);
}
}
if (removedSettingIds.length > 0) {
await service.extensionsItemService.deleteMany(removedSettingIds);
}
if (newSettings.length > 0) {
await service.extensionsItemService.createMany(newSettings);
}
return [...existingSettings.filter(({ id }) => !removedSettingIds.includes(id)), ...newSettings];
};