1ca06e58e7
Memind CI / Test, build, and release guards (push) Successful in 8m42s
Wait for HTML materialization before releasing delivery contracts, pass verified MindSpace URLs through proactive WeChat sends, resend on reconcile when pages land late, and report deferred customer-service delivery as unsent. Co-authored-by: Cursor <cursoragent@cursor.com>
211 lines
9.3 KiB
JavaScript
211 lines
9.3 KiB
JavaScript
// admin-bootstrap.mjs
|
|
//
|
|
// Service container for the standalone memind_adm process.
|
|
//
|
|
// Builds only the domain services the back-office routes need (user-auth, LLM
|
|
// providers, and the minimal plaza graph behind the ops console). It deliberately
|
|
// does NOT start any user-facing daemons — no thumbnail watcher, no asset-sync
|
|
// watcher, no plaza hot-score scheduler, no goosed auto-sync. Those belong to the
|
|
// public server. Side-effecting actions (e.g. push selected LLM key to goosed)
|
|
// stay behind explicit admin routes.
|
|
//
|
|
// The same factory functions imported here are the ones server.mjs uses, so the
|
|
// domain logic has a single source of truth; only the wiring differs.
|
|
import path from 'node:path';
|
|
import {
|
|
createPlanCatalogService,
|
|
createSubscriptionService,
|
|
ensurePlanCatalogSchema,
|
|
} from './billing-subscription.mjs';
|
|
import {
|
|
createPageTemplateCatalogService,
|
|
ensureTemplateCatalogSchema,
|
|
} from './page-template-catalog.mjs';
|
|
import { createDbPool, ensureAssetGatewaySchema, isDatabaseConfigured } from './db.mjs';
|
|
import { createUserAuth } from './user-auth.mjs';
|
|
import { createLlmProviderService } from './llm-providers.mjs';
|
|
import { createAssetGatewayConfigService } from './asset-gateway.mjs';
|
|
import { createImageMakeAdminConfigService } from './image-make-admin-config.mjs';
|
|
import { createMemoryV2AdminConfigService } from './memory-v2-admin-config.mjs';
|
|
import { createMemoryV2AdminOpsService } from './memory-v2-admin-ops.mjs';
|
|
import { createGoalRunAdminOpsService } from './goal-run-admin-ops.mjs';
|
|
import { createOrchestratorAdminConfigService } from './services/orchestrator/admin-config.mjs';
|
|
import { createOrchestratorObservabilityService } from './services/orchestrator/observability.mjs';
|
|
import { createSkillRuntimeAdminConfigService } from './skill-runtime-admin-config.mjs';
|
|
import { createSystemDisclosurePolicyService } from './system-disclosure-policy.mjs';
|
|
import { createAgentCodeRunAdminConfigService } from './agent-code-run-admin-config.mjs';
|
|
import { createMindSearchConfigService } from './mindsearch-config.mjs';
|
|
import { createWechatScheduleLlmConfigService } from './wechat-schedule-llm-config.mjs';
|
|
import { createWechatIntentRouterConfigService } from './wechat-intent-router-config.mjs';
|
|
import { createPlazaPostService, formatPostRow } from './plaza-posts.mjs';
|
|
import { createPlazaInteractionService } from './plaza-interactions.mjs';
|
|
import { createPlazaOpsService } from './plaza-ops.mjs';
|
|
import { createNoopPlazaRedis } from './plaza-redis.mjs';
|
|
import { ensureAlgorithmConfig, loadAlgorithmConfig } from './plaza-algorithm.mjs';
|
|
import { createNotificationDispatcher } from './notification-dispatcher.mjs';
|
|
import { createWechatAdminService } from './wechat-admin.mjs';
|
|
import { createWechatMpService, loadWechatMpConfig } from './wechat-mp.mjs';
|
|
import { createAdminSystemTestService } from './admin-system-tests.mjs';
|
|
|
|
const noop = () => {};
|
|
|
|
/**
|
|
* Build the admin service container.
|
|
* @param {object} [env]
|
|
* @param {string} [env.h5Root] Repo root used for workspace path resolution.
|
|
* @param {string} [env.usersRoot] User workspace root.
|
|
* @param {string} [env.apiTarget] Goosed/relay API target.
|
|
* @param {string} [env.apiSecret] Goosed/relay API secret.
|
|
* @param {number} [env.defaultSignupBalanceCents]
|
|
* @param {boolean} [env.ensureAdminUser] Ensure an admin account exists on boot (default true).
|
|
* @returns {Promise<{pool, userAuth, llmProviderService, memoryV2ConfigService, adminSystemTestService, plazaPosts, plazaOps, wechatAdmin}>}
|
|
*/
|
|
export async function createAdminServices(env = {}) {
|
|
if (!isDatabaseConfigured()) {
|
|
throw new Error('admin-server requires a database (set DATABASE_URL or MYSQL_* env)');
|
|
}
|
|
|
|
const h5Root = env.h5Root ?? process.cwd();
|
|
const usersRoot = env.usersRoot ?? process.env.H5_USERS_ROOT ?? path.join(h5Root, 'users');
|
|
const apiTarget = env.apiTarget ?? process.env.TKMIND_API_TARGET ?? 'https://127.0.0.1:18006';
|
|
const apiSecret = env.apiSecret ?? process.env.TKMIND_SERVER__SECRET_KEY ?? 'local-dev-secret';
|
|
const defaultSignupBalanceCents = Number(
|
|
env.defaultSignupBalanceCents ?? process.env.H5_SIGNUP_BALANCE_CENTS ?? 500,
|
|
);
|
|
|
|
const pool = createDbPool();
|
|
// The back-office process can boot before the public Portal. Create only the
|
|
// optional control-plane tables here instead of requiring the public boot path.
|
|
await ensureAssetGatewaySchema(pool);
|
|
await ensurePlanCatalogSchema(pool);
|
|
await ensureTemplateCatalogSchema(pool);
|
|
|
|
// --- plaza graph (review queue, reports, featured, analytics, creators) ---
|
|
const plazaRedis = createNoopPlazaRedis();
|
|
await ensureAlgorithmConfig(pool);
|
|
const algorithmConfig = await loadAlgorithmConfig(pool);
|
|
|
|
const plazaInteractions = createPlazaInteractionService(pool, { formatPostRow, plazaRedis });
|
|
|
|
let plazaOps = null;
|
|
const plazaPosts = createPlazaPostService(pool, {
|
|
loadViewerReactions: (viewerId, postIds) =>
|
|
plazaInteractions.loadViewerReactions(viewerId, postIds),
|
|
plazaRedis,
|
|
algorithmConfig,
|
|
onPostPublished: noop,
|
|
loadFeaturedPosts: async (viewerId) => {
|
|
if (!plazaOps) return { homepage_banner: [], trending: [], category_top: {} };
|
|
return plazaOps.loadActiveFeaturedPosts(viewerId);
|
|
},
|
|
});
|
|
plazaOps = createPlazaOpsService(pool, {
|
|
formatPostRow,
|
|
reviewPost: (...args) => plazaPosts.reviewPost(...args),
|
|
invalidateFeedCaches: () => plazaRedis?.invalidateFeedCaches?.(),
|
|
});
|
|
|
|
// --- platform super-admin services ---
|
|
const planCatalogService = createPlanCatalogService(pool);
|
|
const subscriptionService = createSubscriptionService(pool, {
|
|
getPlanAsync: (planType) => planCatalogService.getPlan(planType),
|
|
});
|
|
subscriptionService._planCatalogService = planCatalogService;
|
|
const userAuth = createUserAuth(pool, {
|
|
usersRoot,
|
|
h5Root,
|
|
defaultSignupBalanceCents,
|
|
subscriptionService,
|
|
});
|
|
const templateCatalogService = createPageTemplateCatalogService(pool, {
|
|
userAuth,
|
|
h5Root,
|
|
});
|
|
await templateCatalogService.ensureReady();
|
|
if (env.ensureAdminUser !== false) {
|
|
await userAuth.ensureAdminUser();
|
|
}
|
|
|
|
const llmProviderService = createLlmProviderService(pool, { apiTarget, apiSecret });
|
|
const assetGatewayConfigService = createAssetGatewayConfigService(pool, { llmProviderService });
|
|
const imageMakeAdminConfigService = createImageMakeAdminConfigService(pool, {
|
|
env: process.env,
|
|
llmProviderService,
|
|
});
|
|
await imageMakeAdminConfigService.ensureSchema();
|
|
const memoryV2ConfigService = createMemoryV2AdminConfigService(pool);
|
|
const memoryV2AdminOpsService = createMemoryV2AdminOpsService(pool);
|
|
const goalRunAdminOpsService = createGoalRunAdminOpsService(pool, { env: process.env });
|
|
const orchestratorConfigService = createOrchestratorAdminConfigService(pool);
|
|
await orchestratorConfigService.ensureSchema();
|
|
const orchestratorObservabilityService = createOrchestratorObservabilityService({
|
|
pool,
|
|
configService: orchestratorConfigService,
|
|
});
|
|
const mindSearchConfigService = createMindSearchConfigService(pool);
|
|
await mindSearchConfigService.ensureSchema();
|
|
const skillRuntimeConfigService = createSkillRuntimeAdminConfigService(pool, { h5Root });
|
|
const systemDisclosurePolicyService = createSystemDisclosurePolicyService(pool, {
|
|
autoRefresh: false,
|
|
});
|
|
await systemDisclosurePolicyService.initialize();
|
|
const agentCodeRunPolicyService = createAgentCodeRunAdminConfigService(pool, { env: process.env });
|
|
const wechatScheduleLlmConfigService = createWechatScheduleLlmConfigService(pool);
|
|
const wechatIntentRouterConfigService = createWechatIntentRouterConfigService(pool);
|
|
const adminSystemTestService = createAdminSystemTestService({
|
|
pool,
|
|
userAuth,
|
|
portalBaseUrl: process.env.SYSTEM_TEST_PORTAL_BASE_URL
|
|
?? `http://127.0.0.1:${process.env.H5_PORT ?? 8081}`,
|
|
});
|
|
const wechatMpConfig = loadWechatMpConfig();
|
|
const wechatMpService = createWechatMpService({
|
|
config: wechatMpConfig,
|
|
userAuth,
|
|
apiFetch: async () => {
|
|
throw new Error('admin bootstrap does not proxy WeChat chat sessions');
|
|
},
|
|
});
|
|
const notificationDispatcher = createNotificationDispatcher({
|
|
sendWechatTextToUser: wechatMpService?.enabled
|
|
? (userId, text, options) => wechatMpService.sendTextToUser(userId, text, options)
|
|
: null,
|
|
});
|
|
userAuth.setRechargeNotifier(async ({ userId, title, body, dedupeKey }) => {
|
|
await notificationDispatcher.sendRechargeSuccess({ userId, title, body, dedupeKey });
|
|
});
|
|
const wechatAdmin = createWechatAdminService(pool, {
|
|
config: wechatMpConfig,
|
|
scheduleEnabled: process.env.H5_SCHEDULE_ENABLED === '1',
|
|
reminderWorkerEnabled: process.env.H5_REMINDER_WORKER_ENABLED === '1',
|
|
sendWechatTextToUser: wechatMpService?.enabled
|
|
? (userId, text) => notificationDispatcher.sendScheduleNotification({ userId, text })
|
|
: null,
|
|
});
|
|
|
|
return {
|
|
pool,
|
|
userAuth,
|
|
llmProviderService,
|
|
assetGatewayConfigService,
|
|
imageMakeAdminConfigService,
|
|
memoryV2ConfigService,
|
|
memoryV2AdminOpsService,
|
|
goalRunAdminOpsService,
|
|
orchestratorConfigService,
|
|
orchestratorObservabilityService,
|
|
mindSearchConfigService,
|
|
skillRuntimeConfigService,
|
|
systemDisclosurePolicyService,
|
|
agentCodeRunPolicyService,
|
|
wechatScheduleLlmConfigService,
|
|
wechatIntentRouterConfigService,
|
|
adminSystemTestService,
|
|
plazaPosts,
|
|
plazaOps,
|
|
wechatAdmin,
|
|
subscriptionService,
|
|
templateCatalogService,
|
|
};
|
|
}
|