feat(seo-geo): 落盘自动补齐与 143 全量回填支持
Finish/交付写盘前自动回填 mindspace-geo,扩展 storage publications 扫描与 geo meta 解析,并增加 143 生产回填脚本。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Executable
+77
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Batch backfill SEO/GEO for all MindSpace HTML on production host 143.
|
||||
# 143 运行的是 release runtime 包(无 .git),因此从本机 rsync 工具脚本后再执行回填。
|
||||
#
|
||||
# Usage (from laptop):
|
||||
# bash scripts/backfill-mindspace-geo-prod.sh
|
||||
# bash scripts/backfill-mindspace-geo-prod.sh --dry-run
|
||||
|
||||
HOST="${MEMIND_PROD_HOST:-john@180.159.29.143}"
|
||||
LOCAL_REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
REMOTE_REPO="${MEMIND_PROD_REPO:-/Users/john/Project/Memind}"
|
||||
REMOTE_MINDSPACE_ROOT="${MEMIND_PROD_MINDSPACE_ROOT:-${REMOTE_REPO}/MindSpace}"
|
||||
REMOTE_STORAGE_ROOT="${MEMIND_PROD_STORAGE_ROOT:-/Users/john/MindSpace/data/mindspace}"
|
||||
REMOTE_PUBLIC_ROOT="${MEMIND_PROD_PUBLIC_ROOT:-${REMOTE_REPO}/public}"
|
||||
REMOTE_NODE="${MEMIND_PROD_NODE:-/opt/homebrew/Cellar/node@22/22.22.3/bin/node}"
|
||||
EXTRA_ARGS=("$@")
|
||||
|
||||
echo "==> 143 SEO/GEO backfill"
|
||||
echo " host=${HOST}"
|
||||
echo " local=${LOCAL_REPO}"
|
||||
echo " remote=${REMOTE_REPO}"
|
||||
echo " mindspace=${REMOTE_MINDSPACE_ROOT}"
|
||||
echo " storage=${REMOTE_STORAGE_ROOT}"
|
||||
echo " public=${REMOTE_PUBLIC_ROOT}"
|
||||
echo " node=${REMOTE_NODE}"
|
||||
|
||||
SEO_FILES=(
|
||||
chat-image-materialize.mjs
|
||||
chat-image-turn-scope.mjs
|
||||
html-document-injection.mjs
|
||||
mindspace-cover-meta.mjs
|
||||
mindspace-geo-backfill.mjs
|
||||
mindspace-geo-meta.mjs
|
||||
mindspace-og-tags.mjs
|
||||
mindspace-page-tag.mjs
|
||||
mindspace-seo-audit.mjs
|
||||
mindspace-seo-tags.mjs
|
||||
mindspace-thumbnail-png.mjs
|
||||
mindspace-thumbnails.mjs
|
||||
mindspace-workspace-thumbnails.mjs
|
||||
scripts/backfill-mindspace-geo.mjs
|
||||
scripts/check-mindspace-cover.mjs
|
||||
wechat/verify/page-artifact.mjs
|
||||
wechat/verify/share-preview.mjs
|
||||
)
|
||||
|
||||
ssh "${HOST}" "mkdir -p '${REMOTE_REPO}/scripts' '${REMOTE_REPO}/wechat/verify'"
|
||||
for rel in "${SEO_FILES[@]}"; do
|
||||
rsync -az "${LOCAL_REPO}/${rel}" "${HOST}:${REMOTE_REPO}/${rel}"
|
||||
done
|
||||
|
||||
ssh "${HOST}" "set -eo pipefail
|
||||
NODE_BIN='${REMOTE_NODE}'
|
||||
if ! test -x \"\${NODE_BIN}\"; then
|
||||
NODE_BIN=\$(ls /opt/homebrew/Cellar/node@22/*/bin/node 2>/dev/null | head -1)
|
||||
fi
|
||||
if ! test -x \"\${NODE_BIN}\"; then
|
||||
NODE_BIN=\$(ls /Users/john/.local/share/fnm/node-versions/*/installation/bin/node 2>/dev/null | head -1)
|
||||
fi
|
||||
test -x \"\${NODE_BIN}\" || { echo 'node not found on 143' >&2; exit 127; }
|
||||
cd '${REMOTE_REPO}'
|
||||
\"\${NODE_BIN}\" scripts/backfill-mindspace-geo.mjs \\
|
||||
--scope all \\
|
||||
--root '${REMOTE_MINDSPACE_ROOT}' \\
|
||||
--storage-root '${REMOTE_STORAGE_ROOT}' \\
|
||||
--public-root '${REMOTE_PUBLIC_ROOT}' \\
|
||||
${EXTRA_ARGS[*]:-}
|
||||
\"\${NODE_BIN}\" scripts/check-mindspace-cover.mjs \\
|
||||
--scope all \\
|
||||
--root '${REMOTE_MINDSPACE_ROOT}' \\
|
||||
--storage-root '${REMOTE_STORAGE_ROOT}' \\
|
||||
--public-root '${REMOTE_PUBLIC_ROOT}'
|
||||
"
|
||||
|
||||
echo "==> done"
|
||||
@@ -20,6 +20,7 @@ function parseArgs(argv) {
|
||||
let scope = 'all';
|
||||
let root = path.join(repoRoot, DEFAULT_PUBLISH_ROOT);
|
||||
let publicRoot = path.join(repoRoot, 'public');
|
||||
let storageRoot = null;
|
||||
let userId = null;
|
||||
let file = null;
|
||||
let dryRun = false;
|
||||
@@ -40,6 +41,9 @@ function parseArgs(argv) {
|
||||
} else if (arg === '--public-root' && argv[i + 1]) {
|
||||
publicRoot = path.resolve(argv[i + 1]);
|
||||
i += 1;
|
||||
} else if (arg === '--storage-root' && argv[i + 1]) {
|
||||
storageRoot = path.resolve(argv[i + 1]);
|
||||
i += 1;
|
||||
} else if (arg === '--user' && argv[i + 1]) {
|
||||
userId = argv[i + 1];
|
||||
i += 1;
|
||||
@@ -52,11 +56,38 @@ function parseArgs(argv) {
|
||||
}
|
||||
}
|
||||
|
||||
return { scope, root, publicRoot, userId, file, dryRun, forceGeo, fixWarnings };
|
||||
return {
|
||||
scope,
|
||||
root,
|
||||
publicRoot,
|
||||
storageRoot,
|
||||
userId,
|
||||
file,
|
||||
dryRun,
|
||||
forceGeo,
|
||||
fixWarnings,
|
||||
};
|
||||
}
|
||||
|
||||
const { scope, root, publicRoot, userId, file, dryRun, forceGeo, fixWarnings } = parseArgs(process.argv);
|
||||
const targets = collectHtmlTargets({ scope, mindspaceRoot: root, publicRoot, userId, file });
|
||||
const {
|
||||
scope,
|
||||
root,
|
||||
publicRoot,
|
||||
storageRoot,
|
||||
userId,
|
||||
file,
|
||||
dryRun,
|
||||
forceGeo,
|
||||
fixWarnings,
|
||||
} = parseArgs(process.argv);
|
||||
const targets = collectHtmlTargets({
|
||||
scope,
|
||||
mindspaceRoot: root,
|
||||
publicRoot,
|
||||
storageRoot,
|
||||
userId,
|
||||
file,
|
||||
});
|
||||
|
||||
let changed = 0;
|
||||
let skipped = 0;
|
||||
|
||||
@@ -23,6 +23,7 @@ function parseArgs(argv) {
|
||||
let scope = 'all';
|
||||
let root = path.join(repoRoot, DEFAULT_PUBLISH_ROOT);
|
||||
let publicRoot = path.join(repoRoot, 'public');
|
||||
let storageRoot = null;
|
||||
let userId = null;
|
||||
let file = null;
|
||||
let seo = true;
|
||||
@@ -36,6 +37,9 @@ function parseArgs(argv) {
|
||||
} else if (argv[i] === '--public-root' && argv[i + 1]) {
|
||||
publicRoot = path.resolve(argv[i + 1]);
|
||||
i += 1;
|
||||
} else if (argv[i] === '--storage-root' && argv[i + 1]) {
|
||||
storageRoot = path.resolve(argv[i + 1]);
|
||||
i += 1;
|
||||
} else if (argv[i] === '--user' && argv[i + 1]) {
|
||||
userId = argv[i + 1];
|
||||
i += 1;
|
||||
@@ -51,7 +55,7 @@ function parseArgs(argv) {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
return { scope, root, publicRoot, userId, file, seo };
|
||||
return { scope, root, publicRoot, storageRoot, userId, file, seo };
|
||||
}
|
||||
|
||||
function isPlatformOnlyPath(htmlPath, publicRoot) {
|
||||
@@ -60,8 +64,15 @@ function isPlatformOnlyPath(htmlPath, publicRoot) {
|
||||
return resolved.startsWith(`${resolvedPublicRoot}${path.sep}`);
|
||||
}
|
||||
|
||||
const { scope, root, publicRoot, userId, file, seo } = parseArgs(process.argv);
|
||||
const targets = collectHtmlTargets({ scope, mindspaceRoot: root, publicRoot, userId, file });
|
||||
const { scope, root, publicRoot, storageRoot, userId, file, seo } = parseArgs(process.argv);
|
||||
const targets = collectHtmlTargets({
|
||||
scope,
|
||||
mindspaceRoot: root,
|
||||
publicRoot,
|
||||
storageRoot,
|
||||
userId,
|
||||
file,
|
||||
});
|
||||
const results = [];
|
||||
|
||||
for (const htmlPath of targets) {
|
||||
|
||||
Reference in New Issue
Block a user