fix: MindSpace remote sync, thumbnail fallback, and Plaza URL guards

Ensure page sync runs via pageSyncService in remote mode, fall back to
workspace HTML when storage assets are missing, and prevent production
Portal from linking to loopback Plaza URLs baked in at build time.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-05 07:57:04 +08:00
parent 4781bbc156
commit d3239ff292
20 changed files with 646 additions and 58 deletions
+17 -1
View File
@@ -81,7 +81,18 @@ async function writeFile(targetPath, content) {
async function buildFrontend() {
if (skipBuild) return;
console.log('==> 构建 Portal 前端 dist');
await run('npm', ['run', 'build']);
const buildEnv = {
...process.env,
VITE_PLAZA_BASE:
process.env.VITE_PLAZA_BASE ??
process.env.PLAZA_PUBLIC_BASE ??
'https://plaza.tkmind.cn',
VITE_MINDSPACE_BASE:
process.env.VITE_MINDSPACE_BASE ??
process.env.H5_PUBLIC_BASE_URL ??
'https://m.tkmind.cn',
};
await run('npm', ['run', 'build'], { env: buildEnv });
}
async function bundleServer() {
@@ -397,6 +408,11 @@ async function writeMetadata() {
' TKMIND_API_TARGETS / TKMIND_API_TARGET / TKMIND_API_TARGET_1',
' H5_USERS_ROOT / MINDSPACE_STORAGE_ROOT / MEMIND_SHARED_PUBLISH_ROOT',
'',
'Frontend compile-time public URLs (baked into dist at build):',
' VITE_PLAZA_BASE defaults to https://plaza.tkmind.cn in build-portal-runtime.mjs',
' VITE_MINDSPACE_BASE defaults to H5_PUBLIC_BASE_URL or https://m.tkmind.cn',
' Do not build production dist with VITE_PLAZA_BASE=http://127.0.0.1:* in .env',
'',
'Deployment and operations transport:',
' H5 public domain: m.tkmind.cn',
' Current public path: m.tkmind.cn -> 105 nginx -> 103 Portal :8081',
@@ -0,0 +1,68 @@
import fs from 'node:fs';
import path from 'node:path';
const root = path.resolve(new URL('..', import.meta.url).pathname);
function read(relativePath) {
return fs.readFileSync(path.join(root, relativePath), 'utf8');
}
function assertIncludes(label, source, snippet) {
if (!source.includes(snippet)) {
throw new Error(`${label} is missing required guard snippet:\n${snippet}`);
}
}
const serverSource = read('server.mjs');
const pagesSource = read('mindspace-pages.mjs');
const contractSource = read('mindspace-server-adapter-contract.mjs');
assertIncludes(
'server.mjs syncUserGeneratedPages',
serverSource,
'await mindSpacePageSync.syncUserGeneratedPages(userId);',
);
if (serverSource.includes("mindSpaceServerRuntime.adapterKind === 'remote'")) {
throw new Error(
'server.mjs must not skip page sync in remote mode; use pageSyncService RPC instead',
);
}
assertIncludes(
'mindspace-server-adapter-contract.mjs',
contractSource,
"pageSyncService: Object.freeze(['syncUserGeneratedPages'])",
);
assertIncludes(
'mindspace-pages.mjs',
pagesSource,
'readPageContentWithWorkspaceFallback',
);
assertIncludes(
'mindspace-pages.mjs workspace fallback',
pagesSource,
"snapshot.relative_path ?? null",
);
const publicSiteBasesSource = read('src/utils/public-site-bases.mjs');
const publicUrlSource = read('src/utils/publicUrl.ts');
const buildRuntimeSource = read('scripts/build-portal-runtime.mjs');
assertIncludes(
'public-site-bases.mjs loopback guard',
publicSiteBasesSource,
'isLoopbackPublicBase',
);
assertIncludes(
'publicUrl.ts uses public-site-bases',
publicUrlSource,
"from './public-site-bases.mjs'",
);
assertIncludes(
'build-portal-runtime.mjs production plaza default',
buildRuntimeSource,
'https://plaza.tkmind.cn',
);
console.log('mindspace page sync + thumbnail regression guards ok');