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:
@@ -1,187 +1,31 @@
|
||||
# 自动生成的 GraphQL Federation 子图(v2.1 M0)
|
||||
# 源文件:iam.proto
|
||||
# 请勿手动修改;如需调整,改 proto 后重新运行 pnpm run proto:gen-graphql
|
||||
# ------------------------------------------------------
|
||||
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
|
||||
# ------------------------------------------------------
|
||||
|
||||
extend type Query
|
||||
|
||||
type UserInfo @key(fields: "id") {
|
||||
id: String
|
||||
email: String
|
||||
name: String
|
||||
roles: String
|
||||
permissions: String
|
||||
data_scope: String
|
||||
status: String
|
||||
type User {
|
||||
userId: ID!
|
||||
email: String!
|
||||
name: String!
|
||||
status: String!
|
||||
dataScope: String!
|
||||
}
|
||||
|
||||
input RegisterRequestInput {
|
||||
email: String
|
||||
password: String
|
||||
name: String
|
||||
type Role {
|
||||
roleId: ID!
|
||||
name: String!
|
||||
description: String
|
||||
roleType: String!
|
||||
level: Float!
|
||||
}
|
||||
|
||||
input LoginRequestInput {
|
||||
email: String
|
||||
password: String
|
||||
type UserDataScope {
|
||||
userId: ID!
|
||||
classScopeToken: String!
|
||||
studentScopeToken: String!
|
||||
}
|
||||
|
||||
input RefreshTokenRequestInput {
|
||||
refresh_token: String
|
||||
}
|
||||
|
||||
input LogoutRequestInput {
|
||||
refresh_token: String
|
||||
user_id: String
|
||||
}
|
||||
|
||||
input LogoutResponseInput {
|
||||
success: Boolean
|
||||
}
|
||||
|
||||
input AuthResponseInput {
|
||||
user: UserInfo
|
||||
tokens: TokenPair
|
||||
}
|
||||
|
||||
type TokenPair {
|
||||
access_token: String
|
||||
refresh_token: String
|
||||
expires_in: Int
|
||||
}
|
||||
|
||||
input GetUserInfoRequestInput {
|
||||
user_id: String
|
||||
}
|
||||
|
||||
input BatchGetUsersRequestInput {
|
||||
user_ids: String
|
||||
}
|
||||
|
||||
input BatchGetUsersResponseInput {
|
||||
users: UserInfo
|
||||
}
|
||||
|
||||
input GetEffectivePermissionsRequestInput {
|
||||
user_id: String
|
||||
}
|
||||
|
||||
input EffectivePermissionsResponseInput {
|
||||
permissions: String
|
||||
}
|
||||
|
||||
input GetEffectiveAccessRequestInput {
|
||||
user_id: String
|
||||
permission: String
|
||||
}
|
||||
|
||||
input EffectiveAccessResponseInput {
|
||||
allowed: Boolean
|
||||
data_scope: String
|
||||
}
|
||||
|
||||
input GetEffectiveDataScopeRequestInput {
|
||||
user_id: String
|
||||
}
|
||||
|
||||
input DataScopeResponseInput {
|
||||
data_scope: String
|
||||
}
|
||||
|
||||
input GetViewportsRequestInput {
|
||||
user_id: String
|
||||
}
|
||||
|
||||
input ViewportsResponseInput {
|
||||
viewports: ViewportItem
|
||||
}
|
||||
|
||||
type ViewportItem {
|
||||
key: String
|
||||
label: String
|
||||
route: String
|
||||
icon: String
|
||||
sort_order: String
|
||||
required_permission: String
|
||||
}
|
||||
|
||||
input GetPublicKeyRequestInput {
|
||||
}
|
||||
|
||||
input PublicKeyResponseInput {
|
||||
kid: String
|
||||
alg: String
|
||||
public_key_pem: String
|
||||
}
|
||||
|
||||
input GetChildrenByParentRequestInput {
|
||||
parent_id: String
|
||||
}
|
||||
|
||||
input ChildrenResponseInput {
|
||||
children: ChildInfo
|
||||
}
|
||||
|
||||
type ChildInfo {
|
||||
student_id: String
|
||||
name: String
|
||||
relation: String
|
||||
}
|
||||
|
||||
type EffectiveDataScope {
|
||||
user_id: String
|
||||
level: String
|
||||
scope_ids: String
|
||||
school_id: String
|
||||
}
|
||||
|
||||
input CreateUserRequestInput {
|
||||
email: String
|
||||
password: String
|
||||
name: String
|
||||
role_id: String
|
||||
data_scope: String
|
||||
}
|
||||
|
||||
input UpdateUserRequestInput {
|
||||
user_id: String
|
||||
name: String
|
||||
email: String
|
||||
status: String
|
||||
data_scope: String
|
||||
}
|
||||
|
||||
input DeleteUserRequestInput {
|
||||
user_id: String
|
||||
}
|
||||
|
||||
input DeleteUserResponseInput {
|
||||
success: Boolean
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
userInfo: UserInfo
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
effectiveDataScope: EffectiveDataScope
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
effectivePermissions: EffectivePermissionsResponse
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
effectiveAccess: EffectiveAccessResponse
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
viewports: ViewportsResponse
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
publicKey: PublicKeyResponse
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
childrenByParent: ChildrenResponse
|
||||
}
|
||||
type Query {
|
||||
user(userId: ID!): User
|
||||
role(roleId: ID!): Role
|
||||
dataScope(userId: ID!): UserDataScope
|
||||
}
|
||||
@@ -11,9 +11,10 @@
|
||||
*/
|
||||
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 { IamModule } from "../iam/iam.module.js";
|
||||
import { UserResolver } from "./resolvers/user.resolver.js";
|
||||
import { RoleResolver } from "./resolvers/role.resolver.js";
|
||||
import { DataScopeResolver } from "./resolvers/datascope.resolver.js";
|
||||
@@ -22,8 +23,9 @@ import { getRedis } from "../config/redis.js";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
IamModule,
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import "@edu/shared-ts/env-loader";
|
||||
import "reflect-metadata";
|
||||
import { NestFactory } from "@nestjs/core";
|
||||
import { Transport, MicroserviceOptions } from "@nestjs/microservices";
|
||||
|
||||
Reference in New Issue
Block a user