diff --git a/package.json b/package.json index ff47c40..6da2f10 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "shrimp-server", - "version": "0.5.0", + "version": "0.6.0", "private": true, "description": "Shrimp 框架后端 — 声明式字段驱动的 Node.js 管理后台框架(Express + Mongoose + JWT)", "main": "src/main/index.ts", @@ -10,14 +10,6 @@ "types": "./src/main/index.ts", "default": "./src/main/index.ts" }, - "./expmon": { - "types": "./src/main/expmon.ts", - "default": "./src/main/expmon.ts" - }, - "./types": { - "types": "./src/main/types.ts", - "default": "./src/main/types.ts" - }, "./package.json": "./package.json" }, "files": [ diff --git a/src/IShrimpType.ts b/src/IShrimpType.ts index 583eea6..e05bd31 100644 --- a/src/IShrimpType.ts +++ b/src/IShrimpType.ts @@ -1,6 +1,12 @@ /** - * Shrimp 前后端共用类型定义 - * 定义错误码、协议、通用数据格式 + * Shrimp 类型契约 — 前后端 wire protocol 定义 + * + * @contractVersion 1.0.0 + * + * 改动此文件需要: + * 1. 升 @contractVersion(语义化版本) + * 2. 同步到 shrimp-admin/src/IShrimpType.ts,更新其 @syncedFrom + * 3. 通知消费侧对齐 */ /** 业务错误码 */ diff --git a/src/main/expmon.ts b/src/main/expmon.ts deleted file mode 100644 index 66826c0..0000000 --- a/src/main/expmon.ts +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Shrimp expmon 入口 — Express + Mongoose 整合方案 - * - * 在 core 之上把 forMongo(MongoShrimp + 数据源)和 forExpress(buildShrimpRouter - * + JWT)拼起来,导出开箱即用的 ShrimpServer 装配体。 - * - * 这一入口需要消费方安装 peer:express / mongoose / jsonwebtoken / bcrypt。 - * 不需要时改从 'shrimp-server' 导入,不会触发任何 peer。 - * - * 想换掉其中一块的高级用法: - * - 自己造 ITokenAuth(看 createJwtTokenAuth 实现) - * - 自己造 dataSource(实现 ShrimpCrud 接口) - * - 直接 buildShrimpRouter(deps) 拼自己的 Express app,绕开本类 - */ - -import { Router } from "express" -import { MongoShrimp, UserSchema, RoleSchema, OperationLogSchema } from "../forMongo/MongoShrimp" -import { buildShrimpRouter } from "../forExpress/buildShrimpRouter" -import { createJwtTokenAuth } from "../forExpress/jwtAuth" -import type { ITokenAuth, IUserAccessor } from "../forExpress/types" -import type { RootShrimpPower } from "../Shrimp/ShrimpPower" -import type { IUser, IRole, WithId } from "../IShrimpType" - -// 再导出所有 core 的符号,'shrimp-server/expmon' 单一入口就够用 -export * from "./index" - -// ===== forMongo / forExpress 公开符号 ===== -export { MongoShrimp, UserSchema, RoleSchema, OperationLogSchema } from "../forMongo/MongoShrimp" -export { MongoShrimpCrud } from "../forMongo/MongoShrimpCrud" -export { buildShrimpRouter } from "../forExpress/buildShrimpRouter" -export { createJwtTokenAuth } from "../forExpress/jwtAuth" -export type { IJwtTokenAuthOptions } from "../forExpress/jwtAuth" -export type { ITokenAuth, IUserAccessor, IShrimpRouterDeps } from "../forExpress/types" - -// ============== ShrimpServer ============== - -export class ShrimpServer extends MongoShrimp implements IUserAccessor { - router: Router - jwtSecret: string - config: Record - globalOptions: Record = {} - forcedOptionAccess = new Map() - - constructor(jwtSecret: string, config: Record = {}, mongodbUri: string = "mongodb://localhost:27017/shrimp") { - super(mongodbUri) - this.jwtSecret = jwtSecret - this.config = config - - this.router = buildShrimpRouter({ - shrimp: this, - userModule: this.userModule, - roleModule: this.roleModule, - operationLogModule: this.operationLogModule, - tokenAuth: this.createTokenAuth(), - config: this.config, - globalOptions: this.globalOptions, - forcedOptionAccess: this.forcedOptionAccess, - userAccessor: this, - }) - } - - registerOptions(key: string, options: any, access?: RootShrimpPower) { - this.globalOptions[key] = options - if (access) { - this.forcedOptionAccess.set(key, access) - } - } - - /** 创建 Token 鉴权,默认 JWT。子类可覆盖换成 session 之类。 */ - protected createTokenAuth(): ITokenAuth { - return createJwtTokenAuth(this.jwtSecret) - } - - // ============== 可覆盖的用户/角色字段访问钩子(实现 IUserAccessor) ============== - - getJwtPayload(user: WithId): Record { - return { _id: user._id, roleId: user.roleId } - } - - isUserActive(user: WithId): boolean { - return user.active === true - } - - getUserRoleIds(user: WithId): string[] { - return Array.isArray(user.roleId) ? user.roleId.map(String) : user.roleId ? [String(user.roleId)] : [] - } - - isRoleSuperAdmin(role: WithId | null): boolean { - return !!role?.isSuperAdmin - } - - getRolePowers(role: WithId | null): string[] { - return role?.powers || [] - } - - getUserDisplayName(user: WithId): string { - return user.nickname || "" - } -} diff --git a/src/main/index.ts b/src/main/index.ts index d3805c0..e4b5538 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,19 +1,28 @@ /** - * Shrimp 核心入口 — 0 重依赖 + * Shrimp 单一入口 — 完整后端栈 * - * 只暴露 Shrimp 框架本体 + 内存数据源 + 类型定义,不引入 - * express / mongoose / jsonwebtoken。bcrypt 是 lazy import, - * 不走 Password 字段就不会触发。 + * 包含框架核心 + 类型契约 + Express + Mongoose + JWT 装配体。 * - * 适用场景: - * - 只想用 Shrimp 的 RPC dispatch + 模块/字段声明 + 内存 CRUD - * - 自己接 HTTP / 自己写存储层 - * - 跑测试 / 跑无 DB 的 demo + * 安装 peer:express / mongoose / jsonwebtoken / bcrypt。 * - * 需要完整后台(Express + Mongoose + JWT)请从 'shrimp-server/expmon' 导入。 + * 前端只想要类型契约,**不要**从这里 import — 见 shrimp-admin 的 + * IShrimpType.ts(契约副本),避免把 mongoose/express 牵进前端 bundle。 + * + * 想换掉其中一块的高级用法: + * - 自己造 ITokenAuth(看 createJwtTokenAuth 实现) + * - 自己造 dataSource(实现 ShrimpCrud 接口) + * - 直接 buildShrimpRouter(deps) 拼自己的 Express app,绕开 ShrimpServer */ -// ===== 类型 / 接口 / 枚举 ===== +import { Router } from "express" +import { MongoShrimp } from "../forMongo/MongoShrimp" +import { buildShrimpRouter } from "../forExpress/buildShrimpRouter" +import { createJwtTokenAuth } from "../forExpress/jwtAuth" +import type { ITokenAuth, IUserAccessor } from "../forExpress/types" +import type { RootShrimpPower } from "../Shrimp/ShrimpPower" +import type { IUser, IRole, WithId } from "../IShrimpType" + +// ===== 类型契约 / 枚举 ===== export { ShrimpCode, ShrimpError, @@ -64,9 +73,83 @@ export type { ShrimpCrud } from "../Shrimp/ShrimpCrud" export { UserModule, RoleModule } from "../Shrimp/UserRoleModule" export { OperationLogModule } from "../Shrimp/OperationLogModule" -// ===== 密码哈希(可注入,默认 bcrypt lazy import) ===== +// ===== 密码哈希 ===== export { bcryptHasher, plainHasher } from "../PasswordHasher" export type { IPasswordHasher } from "../PasswordHasher" -// ===== 数据源(纯内存,无外部依赖) ===== +// ===== 数据源 ===== export { MemoryShrimpCrud } from "../MemoryShrimpCrud" +export { MongoShrimp, UserSchema, RoleSchema, OperationLogSchema } from "../forMongo/MongoShrimp" +export { MongoShrimpCrud } from "../forMongo/MongoShrimpCrud" + +// ===== Express 装配 ===== +export { buildShrimpRouter } from "../forExpress/buildShrimpRouter" +export { createJwtTokenAuth } from "../forExpress/jwtAuth" +export type { IJwtTokenAuthOptions } from "../forExpress/jwtAuth" +export type { ITokenAuth, IUserAccessor, IShrimpRouterDeps } from "../forExpress/types" + +// ============== ShrimpServer ============== + +export class ShrimpServer extends MongoShrimp implements IUserAccessor { + router: Router + jwtSecret: string + config: Record + globalOptions: Record = {} + forcedOptionAccess = new Map() + + constructor(jwtSecret: string, config: Record = {}, mongodbUri: string = "mongodb://localhost:27017/shrimp") { + super(mongodbUri) + this.jwtSecret = jwtSecret + this.config = config + + this.router = buildShrimpRouter({ + shrimp: this, + userModule: this.userModule, + roleModule: this.roleModule, + operationLogModule: this.operationLogModule, + tokenAuth: this.createTokenAuth(), + config: this.config, + globalOptions: this.globalOptions, + forcedOptionAccess: this.forcedOptionAccess, + userAccessor: this, + }) + } + + registerOptions(key: string, options: any, access?: RootShrimpPower) { + this.globalOptions[key] = options + if (access) { + this.forcedOptionAccess.set(key, access) + } + } + + /** 创建 Token 鉴权,默认 JWT。子类可覆盖换成 session 之类。 */ + protected createTokenAuth(): ITokenAuth { + return createJwtTokenAuth(this.jwtSecret) + } + + // ============== 可覆盖的用户/角色字段访问钩子(实现 IUserAccessor) ============== + + getJwtPayload(user: WithId): Record { + return { _id: user._id, roleId: user.roleId } + } + + isUserActive(user: WithId): boolean { + return user.active === true + } + + getUserRoleIds(user: WithId): string[] { + return Array.isArray(user.roleId) ? user.roleId.map(String) : user.roleId ? [String(user.roleId)] : [] + } + + isRoleSuperAdmin(role: WithId | null): boolean { + return !!role?.isSuperAdmin + } + + getRolePowers(role: WithId | null): string[] { + return role?.powers || [] + } + + getUserDisplayName(user: WithId): string { + return user.nickname || "" + } +} diff --git a/src/main/types.ts b/src/main/types.ts deleted file mode 100644 index d8fc75f..0000000 --- a/src/main/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Shrimp 纯类型入口 - * - * 只导出 IShrimpType.ts 的全部协议(类型 + ShrimpCode enum + ShrimpError class), - * 前端 shrimp-admin / 任何只需要协议定义的消费方从这里 import, - * 不会触发任何运行时依赖。 - */ - -export * from "../IShrimpType"