2e6b0c1818
- 新增 server/skills/ 模块: - schema.mjs:adm_skills / adm_skill_flags / adm_skill_invocations 三表建表 + seed - index.mjs:SkillService(listSkills / getSkill / invokeSkill / setSkillEnabled / setSkillFlag) - flag-resolver.mjs:优先级链 user > plan > caller_type > global - goosed-client.mjs:HTTPS + 自签名证书 + round-robin 双节点支持 - executors/goosed.mjs:一次性 goosed session,SSE 收集结果 - design-catalog.mjs:54 个 awesome-design-md DESIGN.md 本地 bundle - routes.mjs:/api/skills/* (public) + /admin-api/skills/* (admin) - 设计 DESIGN.md 无需 auth 即可访问(GET /api/skills/designs) - Admin 可一键 PATCH /admin-api/skills/:slug/enabled 开关任意 skill - GOOSED_URLS 环境变量支持本地单节点/103双节点兼容 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
140 lines
5.1 KiB
JavaScript
140 lines
5.1 KiB
JavaScript
import path from 'node:path';
|
|
import { createDbPool, isDatabaseConfigured } from './db.mjs';
|
|
import { projectRoot } from './load-env.mjs';
|
|
import { importMemind, resolveMemindLib } from './lib-path.mjs';
|
|
|
|
export async function bootstrapAdminServices() {
|
|
if (!isDatabaseConfigured()) {
|
|
throw new Error('数据库未配置');
|
|
}
|
|
|
|
const memindLib = resolveMemindLib();
|
|
const h5Root =
|
|
process.env.MEMIND_LIB_ROOT?.trim() ??
|
|
memindLib;
|
|
const pool = createDbPool();
|
|
const { loadCreateLlmProviderService } = await import('./llm-provider-loader.mjs');
|
|
const createLlmProviderService = await loadCreateLlmProviderService();
|
|
const { createWechatAdminService } = await importMemind('wechat-admin.mjs');
|
|
const { createWechatMpService, loadWechatMpConfig } = await importMemind('wechat-mp.mjs');
|
|
const { createPlazaPostService, formatPostRow } = await importMemind('plaza-posts.mjs');
|
|
const { createPlazaInteractionService } = await importMemind('plaza-interactions.mjs');
|
|
const { createPlazaOpsService } = await importMemind('plaza-ops.mjs');
|
|
const { createNoopPlazaRedis } = await importMemind('plaza-redis.mjs');
|
|
const { ensureAlgorithmConfig, loadAlgorithmConfig } = await importMemind('plaza-algorithm.mjs');
|
|
const {
|
|
ensureMindSpaceConfig,
|
|
loadMindSpaceConfig,
|
|
updateMindSpaceConfig,
|
|
} = await importMemind('mindspace-config.mjs');
|
|
const { createOpsApi } = await importMemind('admin-routes.mjs');
|
|
const { createWordFilterService, ensureWordFilterSchema } = await importMemind('word-filter.mjs');
|
|
const {
|
|
USER_COOKIE,
|
|
clearUserSessionCookie,
|
|
createUserAuth,
|
|
resolveCookieDomainForRequest,
|
|
userLoginCookies,
|
|
} = await importMemind('user-auth.mjs');
|
|
const {
|
|
ensurePlanCatalogSchema,
|
|
createPlanCatalogService,
|
|
createSubscriptionService,
|
|
} = await importMemind('billing-subscription.mjs');
|
|
|
|
const usersRoot =
|
|
process.env.H5_USERS_ROOT?.trim() ?? path.join(projectRoot, 'data', 'users');
|
|
const apiTarget = process.env.TKMIND_API_TARGET ?? 'https://127.0.0.1:18006';
|
|
const apiSecret = process.env.TKMIND_SERVER__SECRET_KEY ?? 'local-dev-secret';
|
|
const plazaRedis = createNoopPlazaRedis();
|
|
|
|
await ensureAlgorithmConfig(pool);
|
|
const algorithmConfig = await loadAlgorithmConfig(pool);
|
|
await ensureMindSpaceConfig(pool, { env: process.env, seedDefault: false });
|
|
const mindSpaceConfig = await loadMindSpaceConfig(pool, { env: process.env });
|
|
const plazaInteractions = createPlazaInteractionService(pool, { formatPostRow, plazaRedis });
|
|
|
|
let plazaOps = null;
|
|
const plazaPosts = createPlazaPostService(pool, {
|
|
loadViewerReactions: (viewerId, postIds) =>
|
|
plazaInteractions.loadViewerReactions(viewerId, postIds),
|
|
plazaRedis,
|
|
algorithmConfig,
|
|
onPostPublished: () => {},
|
|
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?.(),
|
|
});
|
|
|
|
const userAuth = createUserAuth(pool, {
|
|
usersRoot,
|
|
h5Root,
|
|
defaultSignupBalanceCents: Number(process.env.H5_SIGNUP_BALANCE_CENTS ?? 500),
|
|
});
|
|
|
|
const llmProviderService = createLlmProviderService(pool, {
|
|
apiTarget,
|
|
apiSecret,
|
|
});
|
|
const wechatMpConfig = loadWechatMpConfig();
|
|
const wechatMpService = createWechatMpService({
|
|
config: wechatMpConfig,
|
|
userAuth,
|
|
apiFetch: async () => {
|
|
throw new Error('admin api does not proxy WeChat chat sessions');
|
|
},
|
|
});
|
|
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) => wechatMpService.sendTextToUser(userId, text)
|
|
: null,
|
|
});
|
|
|
|
await ensureWordFilterSchema(pool);
|
|
const wordFilterService = createWordFilterService(pool);
|
|
|
|
await ensurePlanCatalogSchema(pool);
|
|
const planCatalogService = createPlanCatalogService(pool);
|
|
const subscriptionService = createSubscriptionService(pool, {
|
|
getPlanAsync: (planType) => planCatalogService.getPlan(planType),
|
|
});
|
|
|
|
const { ensureSkillSchema } = await import('./skills/schema.mjs');
|
|
await ensureSkillSchema(pool);
|
|
const { createSkillService } = await import('./skills/index.mjs');
|
|
const skillService = createSkillService(pool);
|
|
|
|
console.log(`Admin DB connected (${process.env.MYSQL_DATABASE ?? 'via DATABASE_URL'})`);
|
|
console.log(`Users root: ${usersRoot}`);
|
|
console.log(`Memind lib: ${memindLib}`);
|
|
|
|
return {
|
|
pool,
|
|
userAuth,
|
|
llmProviderService,
|
|
plazaOps,
|
|
createOpsApi,
|
|
wechatAdmin,
|
|
mindSpaceConfig,
|
|
loadMindSpaceConfig,
|
|
updateMindSpaceConfig,
|
|
wordFilterService,
|
|
planCatalogService,
|
|
subscriptionService,
|
|
skillService,
|
|
USER_COOKIE,
|
|
userLoginCookies,
|
|
clearUserSessionCookie,
|
|
resolveCookieDomainForRequest,
|
|
};
|
|
}
|