feat: content engine

This commit is contained in:
2026-02-21 19:08:06 +01:00
parent 3f1c37813a
commit a50b8d6393
32 changed files with 2816 additions and 189 deletions

View File

@@ -2,65 +2,115 @@
# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
# Define potential container names
CONTAINERS=("cms-infra-infra-cms-1" "at-mintel-directus-1")
echo "🔧 Checking for Directus containers to patch..."
for CONTAINER in "${CONTAINERS[@]}"; do
# Check if container exists and is running
if [ "$(docker ps -q -f name=^/${CONTAINER}$)" ]; then
echo "🔧 Applying core patch to Directus container: $CONTAINER..."
docker exec "$CONTAINER" node -e '
echo "🔧 Applying core patches to: $CONTAINER..."
# Capture output to determine if restart is needed
OUTPUT=$(docker exec -i "$CONTAINER" node << 'EOF'
const fs = require("node:fs");
// Try multiple potential paths for the node_modules location
const searchPaths = [
"/directus/node_modules/.pnpm/@directus+extensions@file+packages+extensions_deep-diff@1.0.2_express@4.21.2_graphql@16_244b87fbecd929c2d2240e7b3abc1fe4/node_modules/@directus/extensions/dist/node.js",
"/directus/node_modules/@directus/extensions/dist/node.js"
];
let targetPath = null;
for (const p of searchPaths) {
if (fs.existsSync(p)) {
targetPath = p;
break;
}
const { execSync } = require("node:child_process");
let patched = false;
try {
// 1. Patch @directus/extensions node.js (Entrypoints)
const findNodeCmd = "find /directus/node_modules -path \"*/@directus/extensions/dist/node.js\"";
const nodePaths = execSync(findNodeCmd).toString().trim().split("\n").filter(Boolean);
nodePaths.forEach(targetPath => {
let content = fs.readFileSync(targetPath, "utf8");
let modified = false;
const filterPatch = 'extension.host === "app" && (extension.entrypoint.app || extension.entrypoint)';
// Only replace if the OLD pattern exists
if (content.includes('extension.host === "app" && !!extension.entrypoint.app')) {
content = content.replace(/extension\.host === "app" && !!extension\.entrypoint\.app/g, filterPatch);
modified = true;
}
// Only replace if the OLD pattern exists for entrypoint
// We check if "extension.entrypoint.app" is present but NOT part of our patch
// This is a simple heuristic: if the patch string is NOT present, but the target IS.
if (!content.includes("(extension.entrypoint.app || extension.entrypoint)")) {
if (content.includes("extension.entrypoint.app")) {
content = content.replace(/extension\.entrypoint\.app/g, "(extension.entrypoint.app || extension.entrypoint)");
modified = true;
}
}
if (modified) {
fs.writeFileSync(targetPath, content);
console.log(`✅ Entrypoint patched.`);
patched = true;
}
});
// 2. Patch @directus/api manager.js (HTML Injection)
const findManagerCmd = "find /directus/node_modules -path \"*/@directus/api/dist/extensions/manager.js\"";
const managerPaths = execSync(findManagerCmd).toString().trim().split("\n").filter(Boolean);
managerPaths.forEach(targetPath => {
let content = fs.readFileSync(targetPath, "utf8");
const original = "head: wrapEmbeds('Custom Embed Head', this.hookEmbedsHead),";
const injection = "head: '<script type=\"module\" src=\"/extensions/sources/index.js\"></script>\\n' + wrapEmbeds('Custom Embed Head', this.hookEmbedsHead),";
if (content.includes(original) && !content.includes("/extensions/sources/index.js")) {
content = content.replace(original, injection);
fs.writeFileSync(targetPath, content);
console.log(`✅ Injection patched.`);
patched = true;
}
});
// 3. Patch @directus/api app.js (CSP for unsafe-inline)
const findAppCmd = "find /directus/node_modules -path \"*/@directus/api/dist/app.js\"";
const appPaths = execSync(findAppCmd).toString().trim().split("\n").filter(Boolean);
appPaths.forEach(targetPath => {
let content = fs.readFileSync(targetPath, "utf8");
let modified = false;
const original = "scriptSrc: [\"'self'\", \"'unsafe-eval'\"],";
const patchedStr = "scriptSrc: [\"'self'\", \"'unsafe-eval'\", \"'unsafe-inline'\"],";
if (content.includes(original)) {
content = content.replace(original, patchedStr);
modified = true;
}
if (modified) {
fs.writeFileSync(targetPath, content);
console.log(`✅ CSP patched in app.js.`);
patched = true;
}
});
if (patched) process.exit(100); // Signal restart needed
} catch (error) {
console.error("❌ Error applying patch:", error.message);
process.exit(1);
}
EOF
)
EXIT_CODE=$?
echo "$OUTPUT"
if (targetPath) {
let content = fs.readFileSync(targetPath, "utf8");
// Patch the filter: allow string entrypoints for modules
const filterPatch = "extension.host === \"app\" && (extension.entrypoint.app || extension.entrypoint)";
if (!content.includes(filterPatch)) {
content = content.replace(
/extension\.host === \"app\" && !!extension\.entrypoint\.app/g,
filterPatch
);
}
// Patch all imports: handle string entrypoints
if (!content.includes("(extension.entrypoint.app || extension.entrypoint)")) {
content = content.replace(
/extension\.entrypoint\.app/g,
"(extension.entrypoint.app || extension.entrypoint)"
);
}
fs.writeFileSync(targetPath, content);
console.log(`✅ Core patched successfully at ${targetPath}.`);
} else {
console.error("⚠️ Could not find @directus/extensions node.js to patch!");
}
'
echo "🔄 Restarting Directus container: $CONTAINER..."
docker restart "$CONTAINER"
if [ $EXIT_CODE -eq 100 ]; then
echo "🔄 Patches applied. Restarting Directus container: $CONTAINER..."
docker restart "$CONTAINER"
else
echo "✅ Container $CONTAINER is already patched. No restart needed."
fi
else
echo " Container $CONTAINER is not running or not found. Skipping patch."
echo " Container $CONTAINER not found. Skipping."
fi
done
echo "✨ Patching process finished."
echo "✨ All patches check complete."

View File

@@ -67,18 +67,10 @@ for PKG in "${EXTENSION_PACKAGES[@]}"; do
rm -rf "${FINAL_TARGET:?}"/*
# Copy build artifacts
if [ "$LINK_MODE" = true ]; then
if [ -f "$PKG_PATH/dist/index.js" ]; then
ln -sf "$PKG_PATH/dist/index.js" "$FINAL_TARGET/index.js"
elif [ -f "$PKG_PATH/index.js" ]; then
ln -sf "$PKG_PATH/index.js" "$FINAL_TARGET/index.js"
fi
else
if [ -f "$PKG_PATH/dist/index.js" ]; then
cp "$PKG_PATH/dist/index.js" "$FINAL_TARGET/index.js"
elif [ -f "$PKG_PATH/index.js" ]; then
cp "$PKG_PATH/index.js" "$FINAL_TARGET/index.js"
fi
if [ -f "$PKG_PATH/dist/index.js" ]; then
cp "$PKG_PATH/dist/index.js" "$FINAL_TARGET/index.js"
elif [ -f "$PKG_PATH/index.js" ]; then
cp "$PKG_PATH/index.js" "$FINAL_TARGET/index.js"
fi
if [ -f "$PKG_PATH/package.json" ]; then
@@ -106,7 +98,8 @@ for PKG in "${EXTENSION_PACKAGES[@]}"; do
if [ -d "$PKG_PATH/dist" ]; then
if [ "$LINK_MODE" = true ]; then
ln -sf "$PKG_PATH/dist" "$FINAL_TARGET/dist"
REL_PATH=$(python3 -c "import os; print(os.path.relpath('$PKG_PATH/dist', '$FINAL_TARGET'))")
ln -sf "$REL_PATH" "$FINAL_TARGET/dist"
else
cp -r "$PKG_PATH/dist" "$FINAL_TARGET/"
fi
@@ -120,7 +113,7 @@ for PKG in "${EXTENSION_PACKAGES[@]}"; do
done
# Cleanup: remove anything from extensions root that isn't in our whitelist
WHITELIST=("${EXTENSION_PACKAGES[@]}" "endpoints" "hooks" "layouts" "modules" "operations" "panels" "displays" "interfaces")
WHITELIST=("${EXTENSION_PACKAGES[@]}" "sources" "endpoints" "hooks" "layouts" "modules" "operations" "panels" "displays" "interfaces")
for TARGET_BASE in "${TARGET_DIRS[@]}"; do
echo "🧹 Cleaning up $TARGET_BASE..."

91
scripts/validate-cms.sh Executable file
View File

@@ -0,0 +1,91 @@
#!/bin/bash
# Configuration
CONTAINER="cms-infra-infra-cms-1"
echo "🔍 Validating Directus Extension Stability..."
# 1. Verify Patches
echo "🛠️ Checking Core Patches..."
docker exec -i "$CONTAINER" node << 'EOF'
const fs = require('node:fs');
const { execSync } = require('node:child_process');
let failures = 0;
// Check Node.js patch
const findNode = 'find /directus/node_modules -path "*/@directus/extensions/dist/node.js"';
const nodePaths = execSync(findNode).toString().trim().split('\n').filter(Boolean);
nodePaths.forEach(p => {
const c = fs.readFileSync(p, 'utf8');
if (!c.includes('(extension.entrypoint.app || extension.entrypoint)')) {
console.error('❌ Missing node.js patch at ' + p);
failures++;
}
});
// Check Manager.js patch
const findManager = 'find /directus/node_modules -path "*/@directus/api/dist/extensions/manager.js"';
const managerPaths = execSync(findManager).toString().trim().split('\n').filter(Boolean);
managerPaths.forEach(p => {
const c = fs.readFileSync(p, 'utf8');
if (!c.includes('/extensions/sources/index.js')) {
console.error('❌ Missing manager.js patch at ' + p);
failures++;
}
});
if (failures === 0) {
console.log('✅ Core patches are healthy.');
}
process.exit(failures > 0 ? 1 : 0);
EOF
if [ $? -ne 0 ]; then
echo "⚠️ Core patches missing! Run 'bash scripts/patch-cms.sh' to fix."
fi
# 2. Verify Module Bar
echo "📋 Checking Sidebar Configuration..."
docker exec -i "$CONTAINER" node << 'EOF'
const sqlite3 = require('/directus/node_modules/.pnpm/sqlite3@5.1.7/node_modules/sqlite3');
const db = new sqlite3.Database('/directus/database/data.db');
const managerIds = ["unified-dashboard", "acquisition-manager", "company-manager", "customer-manager", "feedback-commander", "people-manager"];
db.get('SELECT module_bar FROM directus_settings WHERE id = 1', (err, row) => {
if (err) { console.error('❌ DB Error:', err.message); process.exit(1); }
let mb = [];
try { mb = JSON.parse(row.module_bar || '[]'); } catch(e) { mb = []; }
const existingIds = mb.map(m => m.id);
const missing = managerIds.filter(id => !existingIds.includes(id));
const disabled = mb.filter(m => managerIds.includes(m.id) && m.enabled === false);
if (missing.length === 0 && disabled.length === 0) {
console.log('✅ Sidebar is healthy with all manager modules enabled.');
process.exit(0);
} else {
if (missing.length > 0) console.log('⚠️ Missing modules:', missing.join(', '));
if (disabled.length > 0) console.log('⚠️ Disabled modules:', disabled.map(m => m.id).join(', '));
console.log('🔧 Self-healing in progress...');
// Construct Golden State Module Bar
const goldenMB = [
{ type: 'module', id: 'content', enabled: true },
{ type: 'module', id: 'users', enabled: true },
{ type: 'module', id: 'files', enabled: true },
{ type: 'module', id: 'insights', enabled: true },
...managerIds.map(id => ({ type: 'module', id, enabled: true })),
{ type: 'module', id: 'settings', enabled: true }
];
db.run('UPDATE directus_settings SET module_bar = ? WHERE id = 1', [JSON.stringify(goldenMB)], function(err) {
if (err) { console.error('❌ Repair failed:', err.message); process.exit(1); }
console.log('✨ Sidebar repaired successfully!');
process.exit(0);
});
}
});
EOF