forked from directus/directus
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for
EXTENSIONS_LOCATION
setting (directus#20207)
Co-authored-by: ian <[email protected]> Co-authored-by: Brainslug <[email protected]> Co-authored-by: Pascal Jufer <[email protected]>
- Loading branch information
1 parent
013b893
commit 7df84c0
Showing
17 changed files
with
216 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@directus/api': minor | ||
--- | ||
|
||
Added configuration to allow extensions to be managed in a configured storage location |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,9 @@ dist/ | |
*.yml | ||
*.yaml | ||
*.md | ||
*.txt | ||
*.json | ||
*.scss | ||
*.css | ||
*.svg | ||
Dockerfile |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { join } from 'path'; | ||
import env from '../../env.js'; | ||
|
||
export const getExtensionsPath = () => { | ||
if (env['EXTENSIONS_LOCATION']) { | ||
return join(env['TEMP_PATH'], 'extensions'); | ||
} | ||
|
||
return env['EXTENSIONS_PATH']; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { NESTED_EXTENSION_TYPES } from '@directus/extensions'; | ||
import { ensureExtensionDirs } from '@directus/extensions/node'; | ||
import mid from 'node-machine-id'; | ||
import { createWriteStream } from 'node:fs'; | ||
import { mkdir } from 'node:fs/promises'; | ||
import { dirname, join, relative, resolve, sep } from 'node:path'; | ||
import { pipeline } from 'node:stream/promises'; | ||
import Queue from 'p-queue'; | ||
import env from '../../env.js'; | ||
import logger from '../../logger.js'; | ||
import { getMessenger } from '../../messenger.js'; | ||
import { getStorage } from '../../storage/index.js'; | ||
import { getExtensionsPath } from './get-extensions-path.js'; | ||
import { SyncStatus, getSyncStatus, setSyncStatus } from './sync-status.js'; | ||
|
||
export const syncExtensions = async (): Promise<void> => { | ||
const extensionsPath = getExtensionsPath(); | ||
|
||
if (!env['EXTENSIONS_LOCATION']) { | ||
// Safe to run with multiple instances since dirs are created with `recursive: true` | ||
return ensureExtensionDirs(extensionsPath, NESTED_EXTENSION_TYPES); | ||
} | ||
|
||
const messenger = getMessenger(); | ||
|
||
const isPrimaryProcess = | ||
String(process.env['NODE_APP_INSTANCE']) === '0' || process.env['NODE_APP_INSTANCE'] === undefined; | ||
|
||
const id = await mid.machineId(); | ||
|
||
const message = `extensions-sync/${id}`; | ||
|
||
if (isPrimaryProcess === false) { | ||
const isDone = (await getSyncStatus()) === SyncStatus.DONE; | ||
|
||
if (isDone) return; | ||
|
||
logger.trace('Extensions already being synced to this machine from another process.'); | ||
|
||
/** | ||
* Wait until the process that called the lock publishes a message that the syncing is complete | ||
*/ | ||
return new Promise((resolve) => { | ||
messenger.subscribe(message, () => resolve()); | ||
}); | ||
} | ||
|
||
// Ensure that the local extensions cache path exists | ||
await mkdir(extensionsPath, { recursive: true }); | ||
await setSyncStatus(SyncStatus.SYNCING); | ||
|
||
logger.trace('Syncing extensions from configured storage location...'); | ||
|
||
const storage = await getStorage(); | ||
|
||
const disk = storage.location(env['EXTENSIONS_LOCATION']); | ||
|
||
// Make sure we don't overload the file handles | ||
const queue = new Queue({ concurrency: 1000 }); | ||
|
||
for await (const filepath of disk.list(env['EXTENSIONS_PATH'])) { | ||
const readStream = await disk.read(filepath); | ||
|
||
// We want files to be stored in the root of `$TEMP_PATH/extensions`, so gotta remove the | ||
// extensions path on disk from the start of the file path | ||
const destPath = join(extensionsPath, relative(resolve(sep, env['EXTENSIONS_PATH']), resolve(sep, filepath))); | ||
|
||
// Ensure that the directory path exists | ||
await mkdir(dirname(destPath), { recursive: true }); | ||
|
||
const writeStream = createWriteStream(destPath); | ||
|
||
queue.add(() => pipeline(readStream, writeStream)); | ||
} | ||
|
||
await queue.onIdle(); | ||
|
||
await ensureExtensionDirs(extensionsPath, NESTED_EXTENSION_TYPES); | ||
|
||
await setSyncStatus(SyncStatus.DONE); | ||
messenger.publish(message, { ready: true }); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { exists } from 'fs-extra'; | ||
import { readFile, writeFile } from 'node:fs/promises'; | ||
import { join } from 'node:path'; | ||
import { getExtensionsPath } from './get-extensions-path.js'; | ||
|
||
export enum SyncStatus { | ||
UNKNOWN = 'UNKNOWN', | ||
SYNCING = 'SYNCING', | ||
DONE = 'DONE', | ||
} | ||
|
||
/** | ||
* Retrieves the sync status from the `.status` file in the local extensions folder | ||
*/ | ||
export const getSyncStatus = async () => { | ||
const statusFilePath = join(getExtensionsPath(), '.status'); | ||
|
||
if (await exists(statusFilePath)) { | ||
const status = await readFile(statusFilePath, 'utf8'); | ||
return status; | ||
} else { | ||
return SyncStatus.UNKNOWN; | ||
} | ||
}; | ||
|
||
export const setSyncStatus = async (status: SyncStatus) => { | ||
const statusFilePath = join(getExtensionsPath(), '.status'); | ||
await writeFile(statusFilePath, status); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.