feat(admin): Complete tenant management and module access control system
Major Features: - Tenant management CRUD (list, create, edit, delete, module configuration) - Dynamic module management system (modules table with 8 modules) - Multi-tenant module permission merging (ModuleService) - Module access control middleware (requireModule) - User module permission API (GET /api/v1/auth/me/modules) - Frontend module permission filtering (HomePage + TopNavigation) Module Integration: - RVW module integrated with PromptService (editorial + methodology) - All modules (RVW/PKB/ASL/DC) added authenticate + requireModule middleware - Fixed ReviewTask foreign key constraint (cross-schema issue) - Removed all MOCK_USER_ID, unified to request.user?.userId Prompt Management Enhancements: - Module names displayed in Chinese (RVW -> 智能审稿) - Enhanced version history with view content and rollback features - List page shows both activeVersion and draftVersion columns Database Changes: - Added platform_schema.modules table - Modified tenant_modules table (added index and UUID) - Removed ReviewTask foreign key to public.users (cross-schema fix) - Seeded 8 modules: RVW, PKB, ASL, DC, IIT, AIA, SSA, ST Documentation Updates: - Updated ADMIN module development status - Updated TODO checklist (89% progress) - Updated Prompt management plan (Phase 3.5.5 completed) - Added module authentication specification Files Changed: 80+ Status: All features tested and verified locally Next: User management module development
This commit is contained in:
@@ -233,3 +233,50 @@ export async function logout(
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前用户可访问的模块
|
||||
*
|
||||
* GET /api/v1/auth/me/modules
|
||||
*/
|
||||
export async function getUserModules(
|
||||
request: FastifyRequest,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
if (!request.user) {
|
||||
return reply.status(401).send({
|
||||
success: false,
|
||||
error: 'Unauthorized',
|
||||
message: '未认证',
|
||||
});
|
||||
}
|
||||
|
||||
// SUPER_ADMIN 和 PROMPT_ENGINEER 可以访问所有模块
|
||||
if (request.user.role === 'SUPER_ADMIN' || request.user.role === 'PROMPT_ENGINEER') {
|
||||
const { moduleService } = await import('./module.service.js');
|
||||
const allModules = await moduleService.getAllModules();
|
||||
return reply.status(200).send({
|
||||
success: true,
|
||||
data: allModules.map(m => m.code),
|
||||
});
|
||||
}
|
||||
|
||||
const { moduleService } = await import('./module.service.js');
|
||||
const result = await moduleService.getUserModules(request.user.userId);
|
||||
|
||||
return reply.status(200).send({
|
||||
success: true,
|
||||
data: result.modules,
|
||||
});
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : '获取用户模块失败';
|
||||
logger.error('获取用户模块失败', { error: message, userId: request.user?.userId });
|
||||
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
error: 'InternalServerError',
|
||||
message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { FastifyRequest, FastifyReply, FastifyInstance, preHandlerHookHandler }
|
||||
import { jwtService } from './jwt.service.js';
|
||||
import type { DecodedToken } from './jwt.service.js';
|
||||
import { logger } from '../logging/index.js';
|
||||
import { moduleService } from './module.service.js';
|
||||
|
||||
/**
|
||||
* 扩展 Fastify Request 类型
|
||||
@@ -224,6 +225,64 @@ export const requireSameTenant: preHandlerHookHandler = async (
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 模块访问检查中间件工厂
|
||||
*
|
||||
* 检查用户是否有权访问指定模块
|
||||
* 支持多租户模块权限合并
|
||||
*
|
||||
* @param moduleCode 模块代码 (RVW, PKB, ASL, DC, IIT, AIA)
|
||||
*
|
||||
* @example
|
||||
* fastify.post('/tasks', {
|
||||
* preHandler: [authenticate, requireModule('RVW')]
|
||||
* }, controller.createTask);
|
||||
*/
|
||||
export function requireModule(moduleCode: string): preHandlerHookHandler {
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
if (!request.user) {
|
||||
return reply.status(401).send({
|
||||
error: 'Unauthorized',
|
||||
message: '未认证',
|
||||
});
|
||||
}
|
||||
|
||||
// SUPER_ADMIN 可以访问所有模块
|
||||
if (request.user.role === 'SUPER_ADMIN') {
|
||||
return;
|
||||
}
|
||||
|
||||
// PROMPT_ENGINEER 可以访问所有模块(用于调试)
|
||||
if (request.user.role === 'PROMPT_ENGINEER') {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查用户是否有权访问该模块
|
||||
const canAccess = await moduleService.canAccessModule(
|
||||
request.user.userId,
|
||||
moduleCode
|
||||
);
|
||||
|
||||
if (!canAccess) {
|
||||
logger.warn('[Auth] 模块访问被拒绝', {
|
||||
userId: request.user.userId,
|
||||
role: request.user.role,
|
||||
moduleCode,
|
||||
});
|
||||
|
||||
return reply.status(403).send({
|
||||
error: 'Forbidden',
|
||||
message: `您没有访问 ${moduleCode} 模块的权限,请联系管理员开通`,
|
||||
});
|
||||
}
|
||||
|
||||
logger.debug('[Auth] 模块访问已授权', {
|
||||
userId: request.user.userId,
|
||||
moduleCode,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册认证插件到 Fastify
|
||||
*/
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
*/
|
||||
|
||||
import { FastifyInstance, FastifyPluginOptions } from 'fastify';
|
||||
import {
|
||||
import {
|
||||
loginWithPassword,
|
||||
loginWithVerificationCode,
|
||||
sendVerificationCode,
|
||||
getCurrentUser,
|
||||
getUserModules,
|
||||
changePassword,
|
||||
refreshToken,
|
||||
logout,
|
||||
@@ -120,6 +121,13 @@ export async function authRoutes(
|
||||
preHandler: [authenticate],
|
||||
}, getCurrentUser);
|
||||
|
||||
/**
|
||||
* 获取当前用户可访问的模块
|
||||
*/
|
||||
fastify.get('/me/modules', {
|
||||
preHandler: [authenticate],
|
||||
}, getUserModules);
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*/
|
||||
|
||||
@@ -25,11 +25,16 @@ export {
|
||||
requireRoles,
|
||||
requirePermission,
|
||||
requireSameTenant,
|
||||
requireModule,
|
||||
registerAuthPlugin,
|
||||
AuthenticationError,
|
||||
AuthorizationError,
|
||||
} from './auth.middleware.js';
|
||||
|
||||
// Module Service
|
||||
export { moduleService } from './module.service.js';
|
||||
export type { ModuleInfo, UserModulesResult } from './module.service.js';
|
||||
|
||||
// Auth Routes
|
||||
export { authRoutes } from './auth.routes.js';
|
||||
|
||||
|
||||
273
backend/src/common/auth/module.service.ts
Normal file
273
backend/src/common/auth/module.service.ts
Normal file
@@ -0,0 +1,273 @@
|
||||
/**
|
||||
* 模块权限服务
|
||||
*
|
||||
* 管理用户可访问的模块,支持多租户模块权限合并
|
||||
*/
|
||||
|
||||
import { prisma } from '../../config/database.js';
|
||||
import { logger } from '../logging/index.js';
|
||||
|
||||
/**
|
||||
* 模块信息
|
||||
*/
|
||||
export interface ModuleInfo {
|
||||
code: string;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
icon?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户模块访问结果
|
||||
*/
|
||||
export interface UserModulesResult {
|
||||
modules: string[]; // 可访问的模块代码列表
|
||||
moduleDetails: ModuleInfo[]; // 模块详细信息
|
||||
tenantModules: { // 按租户分组的模块
|
||||
tenantId: string;
|
||||
tenantName: string;
|
||||
isPrimary: boolean;
|
||||
modules: string[];
|
||||
}[];
|
||||
}
|
||||
|
||||
class ModuleService {
|
||||
/**
|
||||
* 获取所有可用模块
|
||||
*/
|
||||
async getAllModules(): Promise<ModuleInfo[]> {
|
||||
const modules = await prisma.modules.findMany({
|
||||
where: { is_active: true },
|
||||
orderBy: { sort_order: 'asc' },
|
||||
select: {
|
||||
code: true,
|
||||
name: true,
|
||||
description: true,
|
||||
icon: true,
|
||||
},
|
||||
});
|
||||
return modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户可访问的所有模块
|
||||
* 合并用户所属的所有租户的模块权限
|
||||
*/
|
||||
async getUserModules(userId: string): Promise<UserModulesResult> {
|
||||
try {
|
||||
// 1. 获取用户的主租户
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
select: {
|
||||
tenant_id: true,
|
||||
tenants: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
logger.warn('[ModuleService] 用户不存在', { userId });
|
||||
return { modules: [], moduleDetails: [], tenantModules: [] };
|
||||
}
|
||||
|
||||
// 2. 获取用户加入的其他租户
|
||||
const memberships = await prisma.tenant_members.findMany({
|
||||
where: { user_id: userId },
|
||||
select: {
|
||||
tenant_id: true,
|
||||
tenants: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 3. 构建租户列表(主租户 + 额外加入的租户)
|
||||
const tenantMap = new Map<string, { name: string; isPrimary: boolean }>();
|
||||
|
||||
// 主租户
|
||||
if (user.tenant_id && user.tenants) {
|
||||
tenantMap.set(user.tenant_id, {
|
||||
name: user.tenants.name,
|
||||
isPrimary: true,
|
||||
});
|
||||
}
|
||||
|
||||
// 额外加入的租户
|
||||
for (const m of memberships) {
|
||||
if (!tenantMap.has(m.tenant_id)) {
|
||||
tenantMap.set(m.tenant_id, {
|
||||
name: m.tenants.name,
|
||||
isPrimary: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const tenantIds = Array.from(tenantMap.keys());
|
||||
|
||||
if (tenantIds.length === 0) {
|
||||
logger.warn('[ModuleService] 用户没有关联任何租户', { userId });
|
||||
return { modules: [], moduleDetails: [], tenantModules: [] };
|
||||
}
|
||||
|
||||
// 4. 查询所有租户的已开通模块
|
||||
const tenantModulesData = await prisma.tenant_modules.findMany({
|
||||
where: {
|
||||
tenant_id: { in: tenantIds },
|
||||
is_enabled: true,
|
||||
OR: [
|
||||
{ expires_at: null },
|
||||
{ expires_at: { gt: new Date() } },
|
||||
],
|
||||
},
|
||||
select: {
|
||||
tenant_id: true,
|
||||
module_code: true,
|
||||
},
|
||||
});
|
||||
|
||||
// 5. 按租户分组模块
|
||||
const tenantModulesGrouped: UserModulesResult['tenantModules'] = [];
|
||||
const modulesByTenant = new Map<string, string[]>();
|
||||
|
||||
for (const tm of tenantModulesData) {
|
||||
if (!modulesByTenant.has(tm.tenant_id)) {
|
||||
modulesByTenant.set(tm.tenant_id, []);
|
||||
}
|
||||
modulesByTenant.get(tm.tenant_id)!.push(tm.module_code);
|
||||
}
|
||||
|
||||
Array.from(tenantMap.entries()).forEach(([tenantId, tenantInfo]) => {
|
||||
tenantModulesGrouped.push({
|
||||
tenantId,
|
||||
tenantName: tenantInfo.name,
|
||||
isPrimary: tenantInfo.isPrimary,
|
||||
modules: modulesByTenant.get(tenantId) || [],
|
||||
});
|
||||
});
|
||||
|
||||
// 6. 合并所有模块(去重)
|
||||
const moduleSet = new Set(tenantModulesData.map(tm => tm.module_code));
|
||||
const allModuleCodes = Array.from(moduleSet);
|
||||
|
||||
// 7. 获取模块详细信息
|
||||
const moduleDetails = await prisma.modules.findMany({
|
||||
where: {
|
||||
code: { in: allModuleCodes },
|
||||
is_active: true,
|
||||
},
|
||||
orderBy: { sort_order: 'asc' },
|
||||
select: {
|
||||
code: true,
|
||||
name: true,
|
||||
description: true,
|
||||
icon: true,
|
||||
},
|
||||
});
|
||||
|
||||
logger.debug('[ModuleService] 获取用户模块成功', {
|
||||
userId,
|
||||
tenantCount: tenantIds.length,
|
||||
moduleCount: allModuleCodes.length,
|
||||
});
|
||||
|
||||
return {
|
||||
modules: allModuleCodes,
|
||||
moduleDetails,
|
||||
tenantModules: tenantModulesGrouped,
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('[ModuleService] 获取用户模块失败', { userId, error });
|
||||
return { modules: [], moduleDetails: [], tenantModules: [] };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否有权访问指定模块
|
||||
*/
|
||||
async canAccessModule(userId: string, moduleCode: string): Promise<boolean> {
|
||||
try {
|
||||
const { modules } = await this.getUserModules(userId);
|
||||
return modules.includes(moduleCode);
|
||||
} catch (error) {
|
||||
logger.error('[ModuleService] 检查模块权限失败', { userId, moduleCode, error });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户的已开通模块
|
||||
*/
|
||||
async getTenantModules(tenantId: string): Promise<string[]> {
|
||||
const modules = await prisma.tenant_modules.findMany({
|
||||
where: {
|
||||
tenant_id: tenantId,
|
||||
is_enabled: true,
|
||||
OR: [
|
||||
{ expires_at: null },
|
||||
{ expires_at: { gt: new Date() } },
|
||||
],
|
||||
},
|
||||
select: { module_code: true },
|
||||
});
|
||||
return modules.map(m => m.module_code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置租户的模块配置
|
||||
*/
|
||||
async setTenantModules(
|
||||
tenantId: string,
|
||||
moduleConfigs: { code: string; enabled: boolean; expiresAt?: Date | null }[]
|
||||
): Promise<void> {
|
||||
for (const config of moduleConfigs) {
|
||||
// 先查询是否存在
|
||||
const existing = await prisma.tenant_modules.findUnique({
|
||||
where: {
|
||||
tenant_id_module_code: {
|
||||
tenant_id: tenantId,
|
||||
module_code: config.code,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
// 更新
|
||||
await prisma.tenant_modules.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
is_enabled: config.enabled,
|
||||
expires_at: config.expiresAt,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// 创建(使用 crypto.randomUUID 生成 id)
|
||||
const { randomUUID } = await import('crypto');
|
||||
await prisma.tenant_modules.create({
|
||||
data: {
|
||||
id: randomUUID(),
|
||||
tenant_id: tenantId,
|
||||
module_code: config.code,
|
||||
is_enabled: config.enabled,
|
||||
expires_at: config.expiresAt,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
logger.info('[ModuleService] 更新租户模块配置', {
|
||||
tenantId,
|
||||
moduleCount: moduleConfigs.length,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 导出单例
|
||||
export const moduleService = new ModuleService();
|
||||
|
||||
@@ -56,21 +56,43 @@ export async function listPrompts(
|
||||
|
||||
const templates = await promptService.listTemplates(module);
|
||||
|
||||
// 转换为 API 响应格式
|
||||
const result = templates.map(t => ({
|
||||
id: t.id,
|
||||
code: t.code,
|
||||
name: t.name,
|
||||
module: t.module,
|
||||
description: t.description,
|
||||
variables: t.variables,
|
||||
latestVersion: t.versions[0] ? {
|
||||
version: t.versions[0].version,
|
||||
status: t.versions[0].status,
|
||||
createdAt: t.versions[0].created_at,
|
||||
} : null,
|
||||
updatedAt: t.updated_at,
|
||||
}));
|
||||
// 转换为 API 响应格式,分别返回 ACTIVE 和 DRAFT 版本
|
||||
const result = templates.map(t => {
|
||||
const activeVersion = t.versions.find(v => v.status === 'ACTIVE');
|
||||
const draftVersion = t.versions.find(v => v.status === 'DRAFT');
|
||||
|
||||
// 调试日志
|
||||
console.log(`[PromptController] ${t.code} 版本数量: ${t.versions.length}`);
|
||||
console.log(` - ACTIVE: ${activeVersion ? 'v' + activeVersion.version : '无'}`);
|
||||
console.log(` - DRAFT: ${draftVersion ? 'v' + draftVersion.version : '无'}`);
|
||||
|
||||
return {
|
||||
id: t.id,
|
||||
code: t.code,
|
||||
name: t.name,
|
||||
module: t.module,
|
||||
description: t.description,
|
||||
variables: t.variables,
|
||||
activeVersion: activeVersion ? {
|
||||
version: activeVersion.version,
|
||||
status: activeVersion.status,
|
||||
createdAt: activeVersion.created_at,
|
||||
} : null,
|
||||
draftVersion: draftVersion ? {
|
||||
version: draftVersion.version,
|
||||
status: draftVersion.status,
|
||||
createdAt: draftVersion.created_at,
|
||||
} : null,
|
||||
latestVersion: t.versions[0] ? {
|
||||
version: t.versions[0].version,
|
||||
status: t.versions[0].status,
|
||||
createdAt: t.versions[0].created_at,
|
||||
} : null,
|
||||
updatedAt: t.updated_at,
|
||||
};
|
||||
});
|
||||
|
||||
console.log('[PromptController] 返回数据示例:', JSON.stringify(result[0], null, 2));
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
@@ -155,7 +177,7 @@ export async function saveDraft(
|
||||
try {
|
||||
const { code } = request.params;
|
||||
const { content, modelConfig, changelog } = request.body;
|
||||
const userId = (request as any).user?.id;
|
||||
const userId = (request as any).user?.userId;
|
||||
|
||||
const promptService = getPromptService(prisma);
|
||||
|
||||
@@ -202,7 +224,7 @@ export async function publishPrompt(
|
||||
) {
|
||||
try {
|
||||
const { code } = request.params;
|
||||
const userId = (request as any).user?.id;
|
||||
const userId = (request as any).user?.userId;
|
||||
|
||||
const promptService = getPromptService(prisma);
|
||||
|
||||
@@ -238,7 +260,7 @@ export async function rollbackPrompt(
|
||||
try {
|
||||
const { code } = request.params;
|
||||
const { version } = request.body;
|
||||
const userId = (request as any).user?.id;
|
||||
const userId = (request as any).user?.userId;
|
||||
|
||||
const promptService = getPromptService(prisma);
|
||||
|
||||
@@ -273,7 +295,7 @@ export async function setDebugMode(
|
||||
) {
|
||||
try {
|
||||
const { modules, enabled } = request.body;
|
||||
const userId = (request as any).user?.id;
|
||||
const userId = (request as any).user?.userId;
|
||||
|
||||
if (!userId) {
|
||||
return reply.status(401).send({
|
||||
@@ -317,7 +339,7 @@ export async function getDebugStatus(
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const userId = (request as any).user?.id;
|
||||
const userId = (request as any).user?.userId;
|
||||
|
||||
if (!userId) {
|
||||
return reply.status(401).send({
|
||||
@@ -416,3 +438,4 @@ export async function invalidateCache(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -98,3 +98,5 @@ export function getAllFallbackCodes(): string[] {
|
||||
return Object.keys(FALLBACK_PROMPTS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
testRender,
|
||||
invalidateCache,
|
||||
} from './prompt.controller.js';
|
||||
import { authenticate, requirePermission } from '../auth/auth.middleware.js';
|
||||
|
||||
// Schema 定义
|
||||
const listPromptsSchema = {
|
||||
@@ -156,68 +157,70 @@ const testRenderSchema = {
|
||||
* 注册 Prompt 管理路由
|
||||
*/
|
||||
export async function promptRoutes(fastify: FastifyInstance) {
|
||||
// 列表
|
||||
// 列表(需要认证 + prompt:view)
|
||||
fastify.get('/', {
|
||||
schema: listPromptsSchema,
|
||||
// preHandler: [fastify.authenticate, fastify.requirePermission('prompt:view')],
|
||||
preHandler: [authenticate, requirePermission('prompt:view')],
|
||||
handler: listPrompts,
|
||||
});
|
||||
|
||||
// 详情
|
||||
// 详情(需要认证 + prompt:view)
|
||||
fastify.get('/:code', {
|
||||
schema: getPromptDetailSchema,
|
||||
// preHandler: [fastify.authenticate, fastify.requirePermission('prompt:view')],
|
||||
preHandler: [authenticate, requirePermission('prompt:view')],
|
||||
handler: getPromptDetail,
|
||||
});
|
||||
|
||||
// 调试模式 - 获取状态(需要认证)
|
||||
// 注意:这个路由必须在 /:code 之前,否则会被 /:code 匹配
|
||||
fastify.get('/debug', {
|
||||
preHandler: [authenticate],
|
||||
handler: getDebugStatus,
|
||||
});
|
||||
|
||||
// 保存草稿(需要 prompt:edit)
|
||||
fastify.post('/:code/draft', {
|
||||
schema: saveDraftSchema,
|
||||
// preHandler: [fastify.authenticate, fastify.requirePermission('prompt:edit')],
|
||||
preHandler: [authenticate, requirePermission('prompt:edit')],
|
||||
handler: saveDraft,
|
||||
});
|
||||
|
||||
// 发布(需要 prompt:publish)
|
||||
fastify.post('/:code/publish', {
|
||||
schema: publishSchema,
|
||||
// preHandler: [fastify.authenticate, fastify.requirePermission('prompt:publish')],
|
||||
preHandler: [authenticate, requirePermission('prompt:publish')],
|
||||
handler: publishPrompt,
|
||||
});
|
||||
|
||||
// 回滚(需要 prompt:publish)
|
||||
fastify.post('/:code/rollback', {
|
||||
schema: rollbackSchema,
|
||||
// preHandler: [fastify.authenticate, fastify.requirePermission('prompt:publish')],
|
||||
preHandler: [authenticate, requirePermission('prompt:publish')],
|
||||
handler: rollbackPrompt,
|
||||
});
|
||||
|
||||
// 调试模式 - 获取状态
|
||||
fastify.get('/debug', {
|
||||
// preHandler: [fastify.authenticate],
|
||||
handler: getDebugStatus,
|
||||
});
|
||||
|
||||
// 调试模式 - 设置(需要 prompt:debug)
|
||||
fastify.post('/debug', {
|
||||
schema: setDebugModeSchema,
|
||||
// preHandler: [fastify.authenticate, fastify.requirePermission('prompt:debug')],
|
||||
preHandler: [authenticate, requirePermission('prompt:debug')],
|
||||
handler: setDebugMode,
|
||||
});
|
||||
|
||||
// 测试渲染
|
||||
// 测试渲染(需要 prompt:edit)
|
||||
fastify.post('/test-render', {
|
||||
schema: testRenderSchema,
|
||||
// preHandler: [fastify.authenticate, fastify.requirePermission('prompt:edit')],
|
||||
preHandler: [authenticate, requirePermission('prompt:edit')],
|
||||
handler: testRender,
|
||||
});
|
||||
|
||||
// 清除缓存
|
||||
// 清除缓存(需要 prompt:edit)
|
||||
fastify.post('/:code/invalidate-cache', {
|
||||
schema: getPromptDetailSchema,
|
||||
// preHandler: [fastify.authenticate, fastify.requirePermission('prompt:edit')],
|
||||
preHandler: [authenticate, requirePermission('prompt:edit')],
|
||||
handler: invalidateCache,
|
||||
});
|
||||
}
|
||||
|
||||
export default promptRoutes;
|
||||
|
||||
|
||||
|
||||
@@ -376,7 +376,7 @@ export class PromptService {
|
||||
include: {
|
||||
versions: {
|
||||
orderBy: { version: 'desc' },
|
||||
take: 1,
|
||||
// 返回所有版本,让 controller 自己过滤 ACTIVE 和 DRAFT
|
||||
},
|
||||
},
|
||||
orderBy: { code: 'asc' },
|
||||
|
||||
@@ -67,3 +67,5 @@ export interface VariableValidation {
|
||||
extraVariables: string[];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user