feat(portal-shell): v2.0 P0 shadcn standardization + security + streaming + error handling
- shadcn/ui 标准化:废弃纸感令牌,统一 bg-background/text-foreground 等 - Tailwind v4 + @theme inline,移除 tailwind.config.js - React 19 use() + Suspense 流式渲染,首屏骨架秒出 - 三级错误边界:Route → Section → Widget 层层兜底 - 错误上报:useErrorReport → sendBeacon → /api/log mock 端点 - 三层安全边界:L1 角色门禁 / L2 权限点门禁 / L3 数据范围 - 权限位图 base36 压缩:67 权限点 → ~14 字符,JWT 体积减少 ≥ 99% - notify 统一 Toast 封装,禁止业务直接 import sonner - PluginBoundary 替代 PluginLoader(错误边界 + Suspense + Skeleton 三件套) 验证:typecheck 0 错误 / lint 0 错误 / build 6 路由生成成功
This commit is contained in:
74
services/config-service/src/graphql/generated/schema.graphql
Normal file
74
services/config-service/src/graphql/generated/schema.graphql
Normal file
@@ -0,0 +1,74 @@
|
||||
# ------------------------------------------------------
|
||||
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
|
||||
# ------------------------------------------------------
|
||||
|
||||
type PluginRegistry {
|
||||
pluginId: ID!
|
||||
category: String!
|
||||
version: String!
|
||||
displayName: String!
|
||||
description: String!
|
||||
isBuiltin: Boolean!
|
||||
isActive: Boolean!
|
||||
}
|
||||
|
||||
type LayoutTemplateGql {
|
||||
layoutId: ID!
|
||||
displayName: String!
|
||||
description: String
|
||||
isActive: Boolean!
|
||||
}
|
||||
|
||||
type UserLayoutOverrideGql {
|
||||
userId: ID!
|
||||
activeLayout: String!
|
||||
updatedAt: String!
|
||||
}
|
||||
|
||||
type PluginConfigLayoutGql {
|
||||
layoutId: ID!
|
||||
displayName: String!
|
||||
description: String!
|
||||
availableSlots: [String!]!
|
||||
layoutSchemaJson: String!
|
||||
}
|
||||
|
||||
type PluginConfigSlotGql {
|
||||
slotName: String!
|
||||
navItems: [String!]!
|
||||
}
|
||||
|
||||
type PluginConfigPlacementGql {
|
||||
pluginId: ID!
|
||||
slot: String!
|
||||
sortOrder: Int!
|
||||
sizeJson: String!
|
||||
propsJson: String!
|
||||
isVisible: Boolean!
|
||||
}
|
||||
|
||||
type PluginConfigRegistryItemGql {
|
||||
pluginId: ID!
|
||||
category: String!
|
||||
version: String!
|
||||
displayName: String!
|
||||
description: String!
|
||||
requiredRoles: [String!]!
|
||||
isBuiltin: Boolean!
|
||||
isActive: Boolean!
|
||||
}
|
||||
|
||||
type PluginConfigResponseGql {
|
||||
activeLayout: PluginConfigLayoutGql
|
||||
slots: [PluginConfigSlotGql!]!
|
||||
plugins: [PluginConfigPlacementGql!]!
|
||||
registry: [PluginConfigRegistryItemGql!]!
|
||||
}
|
||||
|
||||
type Query {
|
||||
plugin(pluginId: ID!): PluginRegistry
|
||||
plugins: [PluginRegistry!]!
|
||||
layoutTemplates: [LayoutTemplateGql!]!
|
||||
userLayoutOverride(userId: ID!): UserLayoutOverrideGql
|
||||
pluginConfig(userId: ID!, role: String = "student"): PluginConfigResponseGql!
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
*/
|
||||
import { Module } from "@nestjs/common";
|
||||
import { GraphQLModule } from "@nestjs/graphql";
|
||||
import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo";
|
||||
import { ApolloFederationDriver, ApolloDriverConfig } from "@nestjs/apollo";
|
||||
import { join } from "node:path";
|
||||
import { GraphqlContext } from "@edu/shared-ts/federation";
|
||||
import { ConfigModule } from "../config-config/config.module.js";
|
||||
@@ -27,7 +27,7 @@ import { DataLoaderService } from "./dataloader.service.js";
|
||||
imports: [
|
||||
ConfigModule,
|
||||
GraphQLModule.forRoot<ApolloDriverConfig>({
|
||||
driver: ApolloDriver,
|
||||
driver: ApolloFederationDriver,
|
||||
// Federation 2 子图
|
||||
autoSchemaFile: {
|
||||
path: join(process.cwd(), "src/graphql/generated/schema.graphql"),
|
||||
|
||||
@@ -20,12 +20,17 @@ export class RouterAuthGuard extends BaseRouterAuthGuard {
|
||||
}
|
||||
|
||||
override canActivate(ctx: ExecutionContext): boolean {
|
||||
// DEV_MODE 优先放行(开发态跳过所有校验)
|
||||
if (process.env.DEV_MODE === "true") {
|
||||
return true;
|
||||
}
|
||||
|
||||
const req = ctx.switchToHttp().getRequest<{
|
||||
url: string;
|
||||
}>();
|
||||
|
||||
// 仅 GraphQL 端点需要校验,REST 路由放行
|
||||
if (!req.url?.startsWith("/graphql")) {
|
||||
if (!req?.url?.startsWith("/graphql")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user