diff --git a/package.json b/package.json index 970cfd8..1f45d3c 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,10 @@ { - "name": "code-center-module", - "version": "0.0.1", + "name": "@kevisual/code-center-module", + "version": "0.0.7-alpha.2", "description": "", - "main": "index.js", + "main": "dist/system.mjs", + "module": "dist/system.mjs", + "types": "dist/system.d.ts", "scripts": { "watch": "rollup -c rollup.config.mjs -w", "dev": "cross-env NODE_ENV=development nodemon --delay 2.5 -e js,cjs,mjs --exec node dist/app.mjs", @@ -10,15 +12,23 @@ "dev:watch": "cross-env NODE_ENV=development concurrently -n \"Watch,Dev\" -c \"green,blue\" \"npm run watch\" \"sleep 1 && npm run dev\" ", "build": "rimraf dist && rollup -c rollup.config.mjs" }, + "files": [ + "dist", + "src" + ], "keywords": [], "author": "abearxiong ", "license": "MIT", "type": "module", + "publishConfig": { + "access": "public" + }, "dependencies": { "@kevisual/auth": "1.0.5", "@kevisual/router": "^0.0.7", "@kevisual/use-config": "^1.0.8", "ioredis": "^5.5.0", + "nanoid": "^5.1.2", "pg": "^8.13.3", "sequelize": "^6.37.5", "socket.io": "^4.8.1", @@ -32,7 +42,6 @@ "@rollup/plugin-node-resolve": "^16.0.0", "@rollup/plugin-replace": "^6.0.2", "@rollup/plugin-typescript": "^12.1.2", - "rollup-plugin-esbuild": "^6.2.0", "@types/archiver": "^6.0.3", "@types/crypto-js": "^4.2.2", "@types/formidable": "^3.4.5", @@ -48,8 +57,21 @@ "rollup": "^4.34.8", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-dts": "^6.1.1", + "rollup-plugin-esbuild": "^6.2.1", "tape": "^5.9.0", "tsx": "^4.19.3", "typescript": "^5.7.3" + }, + "exports": { + ".": { + "import": "./dist/system.mjs", + "require": "./dist/system.cjs", + "types": "./dist/system.d.ts" + }, + "./lib": { + "import": "./dist/lib.mjs", + "require": "./dist/lib.cjs", + "types": "./dist/lib.d.ts" + } } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index adbd3f6..5a6fb84 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: ioredis: specifier: ^5.5.0 version: 5.5.0 + nanoid: + specifier: ^5.1.2 + version: 5.1.2 pg: specifier: ^8.13.3 version: 8.13.3 @@ -100,8 +103,8 @@ importers: specifier: ^6.1.1 version: 6.1.1(rollup@4.34.8)(typescript@5.7.3) rollup-plugin-esbuild: - specifier: ^6.2.0 - version: 6.2.0(esbuild@0.25.0)(rollup@4.34.8) + specifier: ^6.2.1 + version: 6.2.1(esbuild@0.25.0)(rollup@4.34.8) tape: specifier: ^5.9.0 version: 5.9.0 @@ -1172,6 +1175,11 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + nanoid@5.1.2: + resolution: {integrity: sha512-b+CiXQCNMUGe0Ri64S9SXFcP9hogjAJ2Rd6GdVxhPLRm7mhGaM7VgOvCAJ1ZshfHbqVDI3uqTI5C8/GaKuLI7g==} + engines: {node: ^18 || >=20} + hasBin: true + negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -1375,8 +1383,8 @@ packages: rollup: ^3.29.4 || ^4 typescript: ^4.5 || ^5.0 - rollup-plugin-esbuild@6.2.0: - resolution: {integrity: sha512-LbkHaCahA6ceyWzAd6md2yajNS+HfZmZ5o58ShkZp0cQeZOnZECG2D2xWFNBq5SF6X6pfMK2udkZ+wRtvpzyVQ==} + rollup-plugin-esbuild@6.2.1: + resolution: {integrity: sha512-jTNOMGoMRhs0JuueJrJqbW8tOwxumaWYq+V5i+PD+8ecSCVkuX27tGW7BXqDgoULQ55rO7IdNxPcnsWtshz3AA==} engines: {node: '>=14.18.0'} peerDependencies: esbuild: '>=0.18.0' @@ -2809,6 +2817,8 @@ snapshots: ms@2.1.3: {} + nanoid@5.1.2: {} + negotiator@0.6.3: {} node-forge@1.3.1: {} @@ -3006,7 +3016,7 @@ snapshots: optionalDependencies: '@babel/code-frame': 7.26.2 - rollup-plugin-esbuild@6.2.0(esbuild@0.25.0)(rollup@4.34.8): + rollup-plugin-esbuild@6.2.1(esbuild@0.25.0)(rollup@4.34.8): dependencies: debug: 4.4.0(supports-color@5.5.0) es-module-lexer: 1.6.0 diff --git a/rollup.config.mjs b/rollup.config.mjs index ad75ff4..03cedb7 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -1,23 +1,39 @@ import resolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; import json from '@rollup/plugin-json'; -import * as glob from 'fast-glob'; import path from 'path'; import esbuild from 'rollup-plugin-esbuild'; import alias from '@rollup/plugin-alias'; import replace from '@rollup/plugin-replace'; import pkgs from './package.json' with { type: 'json' }; +import dts from 'rollup-plugin-dts'; const isDev = process.env.NODE_ENV === 'development'; const version = pkgs.version|| '1.0.0'; +const external = [ + /@kevisual\/router(\/.*)?/, //, // 路由 + /@kevisual\/use-config(\/.*)?/, // + + 'sequelize', // 数据库 orm + 'ioredis', // redis + 'socket.io', // socket.io + 'minio', // minio + + 'pm2', + + 'pg', // pg + 'pino', // pino + 'pino-pretty', // pino-pretty + '@msgpack/msgpack', // msgpack +] /** * @type {import('rollup').RollupOptions} */ const config = { - input: './src/index.ts', + input: './src/lib.ts', output: { dir: './dist', - entryFileNames: 'app.mjs', + entryFileNames: 'lib.mjs', chunkFileNames: '[name]-[hash].mjs', format: 'esm', }, @@ -31,26 +47,6 @@ const config = { // only esbuild needs to be configured entries: [ { find: '@', replacement: path.resolve('src') }, // 配置 @ 为 src 目录 - { find: 'http', replacement: 'node:http' }, - { find: 'https', replacement: 'node:https' }, - { find: 'fs', replacement: 'node:fs' }, - { find: 'path', replacement: 'node:path' }, - { find: 'crypto', replacement: 'node:crypto' }, - { find: 'zlib', replacement: 'node:zlib' }, - { find: 'stream', replacement: 'node:stream' }, - { find: 'net', replacement: 'node:net' }, - { find: 'tty', replacement: 'node:tty' }, - { find: 'tls', replacement: 'node:tls' }, - { find: 'buffer', replacement: 'node:buffer' }, - { find: 'timers', replacement: 'node:timers' }, - // { find: 'string_decoder', replacement: 'node:string_decoder' }, - { find: 'dns', replacement: 'node:dns' }, - { find: 'domain', replacement: 'node:domain' }, - { find: 'os', replacement: 'node:os' }, - { find: 'events', replacement: 'node:events' }, - { find: 'url', replacement: 'node:url' }, - { find: 'assert', replacement: 'node:assert' }, - { find: 'util', replacement: 'node:util' }, ], }), resolve({ @@ -64,21 +60,96 @@ const config = { }), json(), ], - external: [ - /@kevisual\/router(\/.*)?/, //, // 路由 - /@kevisual\/use-config(\/.*)?/, // - - 'sequelize', // 数据库 orm - 'ioredis', // redis - 'socket.io', // socket.io - 'minio', // minio - - 'pm2', - - 'pg', // pg - 'pino', // pino - 'pino-pretty', // pino-pretty - '@msgpack/msgpack', // msgpack + external: external, +}; +const configCjs = { + input: './src/lib.ts', + output: { + dir: './dist', + entryFileNames: 'lib.cjs', + chunkFileNames: '[name]-[hash].cjs', + format: 'cjs', + }, + plugins: [ + replace({ + preventAssignment: true, // 防止意外赋值 + DEV_SERVER: JSON.stringify(isDev), // 替换 process.env.NODE_ENV + VERSION: JSON.stringify(version), // 替换版本号 + }), + alias({ + // only esbuild needs to be configured + entries: [ + { find: '@', replacement: path.resolve('src') }, // 配置 @ 为 src 目录 + ], + }), + resolve({ + preferBuiltins: true, // 强制优先使用内置模块 + }), + commonjs(), + esbuild({ + target: 'node22', // 目标为 Node.js 14 + minify: false, // 启用代码压缩 + tsconfig: 'tsconfig.json', + }), + json(), + ], + external: external, +}; +const dtsConfig = { + input: './src/lib.ts', + output: { + dir: './dist', + entryFileNames: 'lib.d.ts', + format: 'esm', + }, + plugins: [ + dts(), ], }; -export default config; +const systemConfig = [ + { + input: './src/system.ts', + output: { + dir: './dist', + entryFileNames: 'system.mjs', + chunkFileNames: '[name]-[hash].mjs', + format: 'esm', + }, + plugins: [ + replace({ + preventAssignment: true, // 防止意外赋值 + DEV_SERVER: JSON.stringify(isDev), // 替换 process.env.NODE_ENV + VERSION: JSON.stringify(version), // 替换版本号 + }), + alias({ + entries: [ + { find: '@', replacement: path.resolve('src') }, // 配置 @ 为 src 目录 + ], + }), + resolve({ + preferBuiltins: true, // 强制优先使用内置模块 + }), + commonjs(), + esbuild({ + target: 'node22', // 目标为 Node.js 14 + minify: false, // 启用代码压缩 + tsconfig: 'tsconfig.json', + }), + json(), + ], + external: [...external, ], + }, + { + input: './src/system.ts', + output: { + dir: './dist', + entryFileNames: 'system.d.ts', + format: 'esm', + }, + plugins: [ + dts(), + ], + }, +] + +export default [config, dtsConfig, ...systemConfig]; diff --git a/src/index.ts b/src/lib.ts similarity index 50% rename from src/index.ts rename to src/lib.ts index 670b7e2..cc9572d 100644 --- a/src/index.ts +++ b/src/lib.ts @@ -1,14 +1,21 @@ +/** + * 自己初始化redis和sequelize,的模块,放到useContextKey当中 + */ import { app } from './app.ts'; -import { UserServices } from './models/user.ts'; -import { Org } from './models/org.ts'; +import { UserServices, UserInit } from './models/user.ts'; +import { Org, OrgInit } from './models/org.ts'; import { useContextKey } from '@kevisual/use-config/context'; import { Sequelize } from 'sequelize'; import { Redis } from 'ioredis'; export const User = UserServices; -export { Org }; +export { Org, OrgInit, UserInit }; export const redis = useContextKey('redis'); export const sequelize = useContextKey('sequelize'); -export const UserModel = useContextKey('UserModel'); -export const OrgModel = useContextKey('OrgModel'); +export const UserModel = useContextKey('UserModel', () => UserServices); +export const OrgModel = useContextKey('OrgModel', () => Org); export { app }; +export const init = () => { + OrgInit(); + UserInit(); +}; diff --git a/src/models/org.ts b/src/models/org.ts index 8717f07..ec6f0df 100644 --- a/src/models/org.ts +++ b/src/models/org.ts @@ -1,6 +1,5 @@ import { DataTypes, Model, Sequelize } from 'sequelize'; import { useContextKey } from '@kevisual/use-config/context'; -const sequelize = useContextKey('sequelize'); export class Org extends Model { declare id: string; declare username: string; @@ -8,37 +7,40 @@ export class Org extends Model { declare users: { role: string; uid: string }[]; } -Org.init( - { - id: { - type: DataTypes.UUID, - primaryKey: true, - defaultValue: DataTypes.UUIDV4, +export const OrgInit = (newSequelize?: any) => { + const sequelize = useContextKey('sequelize'); + Org.init( + { + id: { + type: DataTypes.UUID, + primaryKey: true, + defaultValue: DataTypes.UUIDV4, + }, + username: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + }, + description: { + type: DataTypes.STRING, + allowNull: true, + }, + users: { + type: DataTypes.JSONB, + allowNull: true, + defaultValue: [], + }, }, - username: { - type: DataTypes.STRING, - allowNull: false, - unique: true, + { + sequelize: newSequelize || sequelize, + modelName: 'cf_org', + paranoid: true, }, - description: { - type: DataTypes.STRING, - allowNull: true, - }, - users: { - type: DataTypes.JSONB, - allowNull: true, - defaultValue: [], - }, - }, - { - sequelize, - modelName: 'cf_org', - paranoid: true, - }, -); - -Org.sync({ alter: true, logging: false }).catch((e) => { - console.error('Org sync', e); -}); + ); + Org.sync({ alter: true, logging: false }).catch((e) => { + console.error('Org sync', e); + }); + return Org; +}; useContextKey('OrgModel', () => Org); diff --git a/src/models/user.ts b/src/models/user.ts index 68637cb..b15ce37 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -154,80 +154,83 @@ export class User extends Model { await redis.del(`user:${this.id}:orgs`); } } -User.init( - { - id: { - type: DataTypes.UUID, - primaryKey: true, - defaultValue: DataTypes.UUIDV4, +export const UserInit = (newSequelize?: any) => { + const sequelize = useContextKey('sequelize'); + User.init( + { + id: { + type: DataTypes.UUID, + primaryKey: true, + defaultValue: DataTypes.UUIDV4, + }, + username: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + // 用户名或者手机号 + // 创建后避免修改的字段,当注册用户后,用户名注册则默认不能用手机号 + }, + nickname: { + type: DataTypes.TEXT, + allowNull: true, + }, + alias: { + type: DataTypes.TEXT, + allowNull: true, // 别名,网络请求的别名,需要唯一,不能和username重复 + }, + password: { + type: DataTypes.STRING, + allowNull: true, + }, + email: { + type: DataTypes.STRING, + allowNull: true, + }, + avatar: { + type: DataTypes.TEXT, + allowNull: true, + }, + salt: { + type: DataTypes.STRING, + allowNull: true, + }, + description: { + type: DataTypes.TEXT, + }, + type: { + type: DataTypes.STRING, + defaultValue: 'user', + }, + owner: { + type: DataTypes.UUID, + }, + orgId: { + type: DataTypes.UUID, + }, + needChangePassword: { + type: DataTypes.BOOLEAN, + defaultValue: false, + }, + data: { + type: DataTypes.JSONB, + defaultValue: {}, + }, }, - username: { - type: DataTypes.STRING, - allowNull: false, - unique: true, - // 用户名或者手机号 - // 创建后避免修改的字段,当注册用户后,用户名注册则默认不能用手机号 + { + sequelize: newSequelize || sequelize, + tableName: 'cf_user', // codeflow user + paranoid: true, }, - nickname: { - type: DataTypes.TEXT, - allowNull: true, - }, - alias: { - type: DataTypes.TEXT, - allowNull: true, // 别名,网络请求的别名,需要唯一,不能和username重复 - }, - password: { - type: DataTypes.STRING, - allowNull: true, - }, - email: { - type: DataTypes.STRING, - allowNull: true, - }, - avatar: { - type: DataTypes.TEXT, - allowNull: true, - }, - salt: { - type: DataTypes.STRING, - allowNull: true, - }, - description: { - type: DataTypes.TEXT, - }, - type: { - type: DataTypes.STRING, - defaultValue: 'user', - }, - owner: { - type: DataTypes.UUID, - }, - orgId: { - type: DataTypes.UUID, - }, - needChangePassword: { - type: DataTypes.BOOLEAN, - defaultValue: false, - }, - data: { - type: DataTypes.JSON, - defaultValue: {}, - }, - }, - { - sequelize, - tableName: 'cf_user', // codeflow user - paranoid: true, - }, -); -User.sync({ alter: true, logging: false }) - .then((res) => { - initializeUser(); - }) - .catch((err) => { - console.error('Sync User error', err); - }); - + ); + User.sync({ alter: true, logging: false }) + .then((res) => { + initializeUser(); + }) + .catch((err) => { + console.error('Sync User error', err); + }); + return User; +}; const letter = 'abcdefghijklmnopqrstuvwxyz'; const custom = customAlphabet(letter, 6); export const initializeUser = async (pwd = custom()) => { diff --git a/src/modules/redis.ts b/src/modules/redis.ts new file mode 100644 index 0000000..826294d --- /dev/null +++ b/src/modules/redis.ts @@ -0,0 +1,33 @@ +import { Redis } from 'ioredis'; +import { useConfig } from '@kevisual/use-config'; + +const config = useConfig<{ + redis: ConstructorParameters; +}>(); +// 配置 Redis 连接 +export const redis = new Redis({ + host: 'localhost', // Redis 服务器的主机名或 IP 地址 + port: 6379, // Redis 服务器的端口号 + // password: 'your_password', // Redis 的密码 (如果有) + db: 0, // 要使用的 Redis 数据库索引 (0-15) + keyPrefix: '', // key 前缀 + retryStrategy(times) { + // 连接重试策略 + return Math.min(times * 50, 2000); // 每次重试时延迟增加 + }, + maxRetriesPerRequest: null, // 允许请求重试的次数 (如果需要无限次重试) + ...config.redis, +}); + +// 监听连接事件 +redis.on('connect', () => { + console.log('Redis 连接成功'); +}); + +redis.on('error', (err) => { + console.error('Redis 连接错误', err); +}); + +// 初始化 Redis 客户端 +export const redisPublisher = new Redis(); // 用于发布消息 +export const redisSubscriber = new Redis(); // 用于订阅消息 diff --git a/src/modules/sequelize.ts b/src/modules/sequelize.ts new file mode 100644 index 0000000..61c8d9d --- /dev/null +++ b/src/modules/sequelize.ts @@ -0,0 +1,26 @@ +import { useConfig } from '@kevisual/use-config'; +import { Sequelize } from 'sequelize'; + +type PostgresConfig = { + postgres: { + username: string; + password: string; + host: string; + port: number; + database: string; + }; +}; +const config = useConfig(); + +const postgresConfig = config.postgres; + +if (!postgresConfig) { + console.error('postgres config is required'); + process.exit(1); +} +// connect to db +export const sequelize = new Sequelize({ + dialect: 'postgres', + ...postgresConfig, + // logging: false, +}); diff --git a/src/system.ts b/src/system.ts new file mode 100644 index 0000000..ab466a4 --- /dev/null +++ b/src/system.ts @@ -0,0 +1,29 @@ +/** + * 直接开发业务代码,把redis和sequelize的初始化放到库当中。 + */ +import { useConfig } from '@kevisual/use-config'; +import { app } from './app.ts'; +import * as sequelizeLib from './modules/sequelize.ts'; +export const sequelize = useContextKey('sequelize', () => sequelizeLib.sequelize); + +import { UserServices, UserInit } from './models/user.ts'; +import { Org, OrgInit } from './models/org.ts'; + +import * as redisLib from './modules/redis.ts'; +import { useContextKey } from '@kevisual/use-config/context'; +useConfig(); + +export const redis = useContextKey('redis', () => redisLib.redis); +export const redisPublisher = useContextKey('redisPublisher', () => redisLib.redisPublisher); +export const redisSubscriber = useContextKey('redisSubscriber', () => redisLib.redisSubscriber); + +export const UserModel = useContextKey('UserModel', () => UserServices); +export const OrgModel = useContextKey('OrgModel', () => Org); +export { app }; +export const User = UserServices; +export { Org, OrgInit, UserInit }; + +export const init = () => { + OrgInit(); + UserInit(); +};