c7718c683b
Memind CI / Test, build, and release guards (push) Successful in 1m36s
Publication routes and share shells were missing the Rybbit tracker, and bootstrap now reuses the stored MindSpace analytics secret when Rybbit env secret is absent. Co-authored-by: Cursor <cursoragent@cursor.com>
311 lines
10 KiB
JavaScript
311 lines
10 KiB
JavaScript
import { ensureMindSpaceConfig, loadMindSpaceConfig } from '../mindspace-config.mjs';
|
|
import { resolveMindSpaceRybbitConfig } from '../mindspace-rybbit.mjs';
|
|
import { createMindSearchConfigService } from '../mindsearch-config.mjs';
|
|
import { createMindSpaceService } from '../mindspace.mjs';
|
|
import {
|
|
assertMindSpaceServerAdapterContract,
|
|
createMindSpaceServerAdapter,
|
|
} from '../mindspace-server-adapter.mjs';
|
|
import { createPageDataPublicService } from '../page-data-public-service.mjs';
|
|
import { createPageDataService } from '../page-data-service.mjs';
|
|
import {
|
|
ensureAlgorithmConfig,
|
|
loadAlgorithmConfig,
|
|
recalculateHotScores,
|
|
} from '../plaza-algorithm.mjs';
|
|
import { createPlazaEventService } from '../plaza-events.mjs';
|
|
import { createPlazaInteractionService } from '../plaza-interactions.mjs';
|
|
import { createPlazaOpsService } from '../plaza-ops.mjs';
|
|
import { createPlazaPostService, formatPostRow } from '../plaza-posts.mjs';
|
|
import { createPlazaRecommendService } from '../plaza-recommend.mjs';
|
|
import { createPlazaRedis } from '../plaza-redis.mjs';
|
|
import { isPassiveCanaryRuntime } from './portal-runtime-role.mjs';
|
|
import { createPlazaSeoService } from '../plaza-seo.mjs';
|
|
import {
|
|
startPlazaTasks,
|
|
writebackPublications,
|
|
} from '../plaza-tasks.mjs';
|
|
import { createScheduleService } from '../schedule-service.mjs';
|
|
import { createFeedbackService } from '../user-feedback.mjs';
|
|
import { PUBLISH_KEY_UUID } from '../user-publish.mjs';
|
|
import { initSchema } from '../db.mjs';
|
|
|
|
export async function bootstrapPortalDomainServices({
|
|
pool,
|
|
h5Root,
|
|
env = process.env,
|
|
runtime,
|
|
analyticsConfig = {},
|
|
getUserAuth = () => null,
|
|
getSessionSnapshotService = () => null,
|
|
onMindSearchSchemaReady = () => {},
|
|
workspaceMaintenanceEnabled = true,
|
|
logger = console,
|
|
initSchemaFn = initSchema,
|
|
createMindSearchConfigServiceFn =
|
|
createMindSearchConfigService,
|
|
ensureMindSpaceConfigFn = ensureMindSpaceConfig,
|
|
loadMindSpaceConfigFn = loadMindSpaceConfig,
|
|
createScheduleServiceFn = createScheduleService,
|
|
createPageDataServiceFn = createPageDataService,
|
|
createPageDataPublicServiceFn =
|
|
createPageDataPublicService,
|
|
createFeedbackServiceFn = createFeedbackService,
|
|
createMindSpaceServiceFn = createMindSpaceService,
|
|
createMindSpaceServerAdapterFn =
|
|
createMindSpaceServerAdapter,
|
|
assertMindSpaceServerAdapterContractFn =
|
|
assertMindSpaceServerAdapterContract,
|
|
ensureAlgorithmConfigFn = ensureAlgorithmConfig,
|
|
loadAlgorithmConfigFn = loadAlgorithmConfig,
|
|
createPlazaRedisFn = createPlazaRedis,
|
|
createPlazaSeoServiceFn = createPlazaSeoService,
|
|
createPlazaInteractionServiceFn =
|
|
createPlazaInteractionService,
|
|
createPlazaEventServiceFn = createPlazaEventService,
|
|
createPlazaRecommendServiceFn =
|
|
createPlazaRecommendService,
|
|
createPlazaPostServiceFn = createPlazaPostService,
|
|
createPlazaOpsServiceFn = createPlazaOpsService,
|
|
startPlazaTasksFn = startPlazaTasks,
|
|
recalculateHotScoresFn = recalculateHotScores,
|
|
writebackPublicationsFn = writebackPublications,
|
|
formatPostRowFn = formatPostRow,
|
|
publishKeyUuid = PUBLISH_KEY_UUID,
|
|
} = {}) {
|
|
if (!pool || !runtime) {
|
|
throw new Error(
|
|
'bootstrapPortalDomainServices requires pool and runtime',
|
|
);
|
|
}
|
|
const mindSearchConfigService =
|
|
createMindSearchConfigServiceFn(pool);
|
|
await mindSearchConfigService.ensureSchema();
|
|
await onMindSearchSchemaReady({
|
|
pool,
|
|
mindSearchConfigService,
|
|
});
|
|
await initSchemaFn(pool);
|
|
await ensureMindSpaceConfigFn(pool, { env });
|
|
const storedMindSpaceConfig =
|
|
await loadMindSpaceConfigFn(pool, {
|
|
env,
|
|
includeAnalyticsSecret: true,
|
|
});
|
|
let resolvedAnalyticsConfig = analyticsConfig;
|
|
if (storedMindSpaceConfig?.analytics) {
|
|
resolvedAnalyticsConfig = {
|
|
...analyticsConfig,
|
|
...storedMindSpaceConfig.analytics,
|
|
enabled: Boolean(
|
|
storedMindSpaceConfig.analytics.enabled &&
|
|
storedMindSpaceConfig.analytics.websiteId &&
|
|
storedMindSpaceConfig.analytics.idSecret,
|
|
),
|
|
hostPath: '/analytics',
|
|
scriptPath: '/analytics/script.js',
|
|
};
|
|
}
|
|
const resolvedRybbitConfig = resolveMindSpaceRybbitConfig(env, {
|
|
idSecret: storedMindSpaceConfig?.analytics?.idSecret ?? '',
|
|
});
|
|
|
|
const scheduleService = createScheduleServiceFn(pool, {
|
|
defaultTimezone:
|
|
env.H5_DEFAULT_TIMEZONE || 'Asia/Shanghai',
|
|
});
|
|
const pageDataService = createPageDataServiceFn({
|
|
getUserAuth,
|
|
getPool: () => pool,
|
|
resolveWorkspaceRoot: async (user) => {
|
|
if (user?.workspaceRoot) return user.workspaceRoot;
|
|
const userAuth = getUserAuth();
|
|
if (!userAuth || !user?.id) return null;
|
|
return userAuth.resolveWorkingDir(user.id);
|
|
},
|
|
});
|
|
const pageDataPublicService =
|
|
createPageDataPublicServiceFn({
|
|
getPool: () => pool,
|
|
resolveH5Root: () => h5Root,
|
|
});
|
|
const feedbackService = createFeedbackServiceFn(pool);
|
|
const mindSpace = createMindSpaceServiceFn(pool, {
|
|
maxFileBytes: runtime.maxFileBytes,
|
|
aiDailyLimit: runtime.aiDailyLimit,
|
|
publicPageLimit: runtime.publicPageLimit,
|
|
monthlyViewLimit: runtime.monthlyViewLimit,
|
|
scheduleService,
|
|
});
|
|
|
|
const resolveUserIdForAgentSession = async (sessionId) => {
|
|
const [rows] = await pool.query(
|
|
`SELECT user_id FROM h5_user_sessions WHERE agent_session_id = ? LIMIT 1`,
|
|
[sessionId],
|
|
);
|
|
return rows[0]?.user_id ?? null;
|
|
};
|
|
const mindSpaceRuntimeAdapter =
|
|
assertMindSpaceServerAdapterContractFn(
|
|
createMindSpaceServerAdapterFn({
|
|
pool,
|
|
h5Root,
|
|
env,
|
|
maxFileBytes: runtime.maxFileBytes,
|
|
publicPageLimit: runtime.publicPageLimit,
|
|
resolveUserIdForAgentSession,
|
|
resolveWorkspaceRoot: (userId) =>
|
|
getUserAuth().resolveWorkingDir(userId),
|
|
resolveSessionSnapshot: (sessionId) =>
|
|
getSessionSnapshotService()?.get?.(sessionId),
|
|
syncWorkspaceAssetsEnabled:
|
|
workspaceMaintenanceEnabled,
|
|
remote: runtime.remote,
|
|
logger,
|
|
}),
|
|
);
|
|
await mindSpaceRuntimeAdapter.assertReady?.();
|
|
|
|
const mindSpaceServiceFacade =
|
|
mindSpaceRuntimeAdapter.serviceFacade;
|
|
const mindSpaceChatSave =
|
|
mindSpaceRuntimeAdapter.chatSaveService;
|
|
const mindSpaceConversationArtifacts =
|
|
mindSpaceRuntimeAdapter.conversationArtifactService;
|
|
const mindSpacePublicFinish =
|
|
mindSpaceRuntimeAdapter.publicFinishService;
|
|
const mindSpaceWorkspacePublicationDelivery =
|
|
mindSpaceRuntimeAdapter
|
|
.workspacePublicationDeliveryService;
|
|
const mindSpaceConversationPackageRegistry =
|
|
mindSpaceRuntimeAdapter.conversationPackageRegistry;
|
|
const mindSpaceAssets =
|
|
mindSpaceRuntimeAdapter.assetService;
|
|
const mindSpacePages = mindSpaceRuntimeAdapter.pageService;
|
|
const mindSpacePageSync =
|
|
mindSpaceRuntimeAdapter.pageSyncService;
|
|
const mindSpacePageLiveEdit =
|
|
mindSpaceRuntimeAdapter.pageLiveEditService;
|
|
const mindSpaceAssetAgent =
|
|
mindSpaceRuntimeAdapter.assetAgentService;
|
|
const mindSpacePublications =
|
|
mindSpaceRuntimeAdapter.publicationService;
|
|
const workspacePageDeliver =
|
|
mindSpaceRuntimeAdapter
|
|
.workspacePageDeliveryService;
|
|
|
|
const resolveUserIdByDirKey = async (dirKey) => {
|
|
let userId = dirKey;
|
|
if (!publishKeyUuid.test(dirKey)) {
|
|
const [rows] = await pool.query(
|
|
`SELECT id FROM h5_users WHERE username = ? LIMIT 1`,
|
|
[dirKey],
|
|
);
|
|
userId = rows[0]?.id;
|
|
}
|
|
return userId ?? null;
|
|
};
|
|
|
|
await ensureAlgorithmConfigFn(pool);
|
|
const plazaAlgorithmConfig =
|
|
await loadAlgorithmConfigFn(pool);
|
|
const plazaRedis = await createPlazaRedisFn(
|
|
env.PLAZA_REDIS_URL,
|
|
pool,
|
|
);
|
|
if (plazaRedis.enabled) {
|
|
logger.log('Plaza Redis enabled');
|
|
}
|
|
const plazaSeo = createPlazaSeoServiceFn(pool);
|
|
const plazaInteractions =
|
|
createPlazaInteractionServiceFn(pool, {
|
|
formatPostRow: formatPostRowFn,
|
|
plazaRedis,
|
|
});
|
|
const plazaEvents = createPlazaEventServiceFn(pool);
|
|
const plazaRecommend = createPlazaRecommendServiceFn(pool, {
|
|
eventService: plazaEvents,
|
|
formatPostRow: formatPostRowFn,
|
|
loadViewerReactions: (viewerId, postIds) =>
|
|
plazaInteractions.loadViewerReactions(
|
|
viewerId,
|
|
postIds,
|
|
),
|
|
algorithmConfig: plazaAlgorithmConfig,
|
|
});
|
|
let plazaOps = null;
|
|
const plazaPosts = createPlazaPostServiceFn(pool, {
|
|
loadViewerReactions: (viewerId, postIds) =>
|
|
plazaInteractions.loadViewerReactions(
|
|
viewerId,
|
|
postIds,
|
|
),
|
|
plazaRedis,
|
|
algorithmConfig: plazaAlgorithmConfig,
|
|
recommendService: plazaRecommend,
|
|
onPostPublished: (postId) =>
|
|
plazaSeo?.notifyPostPublished(postId),
|
|
loadFeaturedPosts: async (viewerId) => {
|
|
if (!plazaOps) {
|
|
return {
|
|
homepage_banner: [],
|
|
trending: [],
|
|
category_top: {},
|
|
};
|
|
}
|
|
return plazaOps.loadActiveFeaturedPosts(viewerId);
|
|
},
|
|
});
|
|
plazaOps = createPlazaOpsServiceFn(pool, {
|
|
formatPostRow: formatPostRowFn,
|
|
reviewPost: (...args) => plazaPosts.reviewPost(...args),
|
|
invalidateFeedCaches: () =>
|
|
plazaRedis?.invalidateFeedCaches?.(),
|
|
});
|
|
if (isPassiveCanaryRuntime(env)) {
|
|
logger.log('Passive candidate runtime: Plaza background tasks disabled');
|
|
} else {
|
|
startPlazaTasksFn({
|
|
pool,
|
|
plazaRedis,
|
|
recalculateHotScores: recalculateHotScoresFn,
|
|
writebackPublications: writebackPublicationsFn,
|
|
});
|
|
}
|
|
|
|
return {
|
|
mindSearchConfigService,
|
|
mindSpaceAnalyticsConfig: resolvedAnalyticsConfig,
|
|
mindSpaceRybbitConfig: resolvedRybbitConfig,
|
|
scheduleService,
|
|
pageDataService,
|
|
pageDataPublicService,
|
|
feedbackService,
|
|
mindSpace,
|
|
mindSpaceRuntimeAdapter,
|
|
mindSpaceServiceFacade,
|
|
mindSpaceChatSave,
|
|
mindSpaceConversationArtifacts,
|
|
mindSpacePublicFinish,
|
|
mindSpaceWorkspacePublicationDelivery,
|
|
mindSpaceConversationPackageRegistry,
|
|
mindSpaceAssets,
|
|
mindSpacePages,
|
|
mindSpacePageSync,
|
|
mindSpacePageLiveEdit,
|
|
mindSpaceAssetAgent,
|
|
mindSpacePublications,
|
|
workspacePageDeliver,
|
|
resolveUserIdByDirKey,
|
|
plazaRedis,
|
|
plazaSeo,
|
|
plazaInteractions,
|
|
plazaEvents,
|
|
plazaRecommend,
|
|
plazaPosts,
|
|
plazaOps,
|
|
mindSpaceCleanup:
|
|
mindSpaceRuntimeAdapter.cleanupService,
|
|
};
|
|
}
|