feat: content engine
This commit is contained in:
@@ -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."
|
||||
|
||||
@@ -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
91
scripts/validate-cms.sh
Executable 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
|
||||
Reference in New Issue
Block a user