@alemonjs/db
Advanced tools
| import { Attributes, FindOptions, Model, ModelAttributes, ModelCtor, ModelOptions, ModelStatic, Optional } from 'sequelize'; | ||
| declare function findAllValues<M extends Model>(this: ModelStatic<M>, options?: FindOptions<Attributes<M>>): Promise<Attributes<M>[]>; | ||
| declare function findOneValue<M extends Model>(this: ModelStatic<M>, options?: FindOptions<Attributes<M>>): Promise<Attributes<M>>; | ||
| declare function findOneRandomValue<M extends Model>(this: ModelStatic<M>, options?: FindOptions<Attributes<M>>): Promise<Attributes<M>>; | ||
| declare function findAllCurrentValues<M extends Model>(this: ModelStatic<M>, options?: FindOptions<Attributes<M>>): Promise<Attributes<M>[]>; | ||
| declare function findOneCurrentValue<M extends Model>(this: ModelStatic<M>, options?: FindOptions<Attributes<M>>): Promise<Attributes<M>>; | ||
| declare function findOrCreateValue<M extends Model>(this: ModelStatic<M>, options?: any): Promise<[Attributes<M>, boolean]>; | ||
| declare function deleted<M extends Model>(this: ModelStatic<M>, options?: any): Promise<number>; | ||
| export type AttributesModel<T extends ModelCtor<Model>> = T extends ModelCtor<infer M> ? (M extends Model<infer U, object> ? U : never) : never; | ||
| type ModelBaseProps = { | ||
| id: number; | ||
| created_at?: Date | null; | ||
| updated_at?: Date | null; | ||
| deleted_at?: Date | null; | ||
| }; | ||
| type InitConfig<T> = ModelAttributes<Model<T & ModelBaseProps>, Optional<Attributes<Model<T & ModelBaseProps>>, keyof ModelBaseProps>>; | ||
| type Base = { | ||
| [key in string]: unknown; | ||
| }; | ||
| export declare class BaseModel<T = ModelBaseProps> extends Model<T & ModelBaseProps> { | ||
| static findAllValues: typeof findAllValues; | ||
| static findOneValue: typeof findOneValue; | ||
| static findOneRandomValue: typeof findOneRandomValue; | ||
| static findAllCurrentValues: typeof findAllCurrentValues; | ||
| static findOneCurrentValue: typeof findOneCurrentValue; | ||
| static findOrCreateValue: typeof findOrCreateValue; | ||
| static deleted: typeof deleted; | ||
| static generate<T extends Base>(config: InitConfig<T>, options?: ModelOptions): ModelCtor<Model<T & ModelBaseProps>>; | ||
| } | ||
| export {}; |
| import { DataTypes, Model } from 'sequelize'; | ||
| function findAllValues(options) { | ||
| return this.findAll({ | ||
| ...options, | ||
| raw: true | ||
| }); | ||
| } | ||
| function findOneValue(options = {}) { | ||
| return this.findOne({ | ||
| ...options, | ||
| raw: true | ||
| }); | ||
| } | ||
| function findOneRandomValue(options = {}) { | ||
| return this.findOne({ | ||
| ...options, | ||
| order: this.sequelize.random(), | ||
| raw: true | ||
| }); | ||
| } | ||
| function findAllCurrentValues(options = {}) { | ||
| const where = options?.where ?? {}; | ||
| where['deleted_at'] = null; | ||
| return this.findAll({ | ||
| ...options, | ||
| where: where, | ||
| raw: true | ||
| }); | ||
| } | ||
| function findOneCurrentValue(options = {}) { | ||
| const where = options?.where ?? {}; | ||
| where['deleted_at'] = null; | ||
| return this.findOne({ | ||
| ...options, | ||
| where: where, | ||
| raw: true | ||
| }); | ||
| } | ||
| function findOrCreateValue(options = {}) { | ||
| return this.findOrCreate({ | ||
| ...options, | ||
| raw: true | ||
| }); | ||
| } | ||
| function deleted(options = {}) { | ||
| const where = options?.where ?? {}; | ||
| where['deleted_at'] = null; | ||
| return this.update({ | ||
| deleted_at: new Date() | ||
| }, { | ||
| ...options, | ||
| where: where | ||
| }).then(result => result?.[0] || 0); | ||
| } | ||
| const initModelOptions = { | ||
| freezeTableName: true, | ||
| timestamps: true, | ||
| deletedAt: 'deleted_at', | ||
| createdAt: 'created_at', | ||
| updatedAt: 'updated_at' | ||
| }; | ||
| const MoDelInitOptions = { | ||
| id: { | ||
| type: DataTypes.BIGINT, | ||
| primaryKey: true, | ||
| autoIncrement: true, | ||
| allowNull: false | ||
| }, | ||
| created_at: { | ||
| type: DataTypes.DATE, | ||
| defaultValue: DataTypes.NOW, | ||
| comment: '创建时间' | ||
| }, | ||
| updated_at: { | ||
| type: DataTypes.DATE, | ||
| defaultValue: DataTypes.NOW, | ||
| comment: '更新时间' | ||
| }, | ||
| deleted_at: { | ||
| type: DataTypes.DATE, | ||
| allowNull: true, | ||
| comment: '删除时间' | ||
| } | ||
| }; | ||
| class BaseModel extends Model { | ||
| static findAllValues = findAllValues; | ||
| static findOneValue = findOneValue; | ||
| static findOneRandomValue = findOneRandomValue; | ||
| static findAllCurrentValues = findAllCurrentValues; | ||
| static findOneCurrentValue = findOneCurrentValue; | ||
| static findOrCreateValue = findOrCreateValue; | ||
| static deleted = deleted; | ||
| static generate(config, options = {}) { | ||
| this.init({ | ||
| ...MoDelInitOptions, | ||
| ...config | ||
| }, { | ||
| ...initModelOptions, | ||
| sequelize: this.sequelize, | ||
| ...options | ||
| }); | ||
| return this; | ||
| } | ||
| } | ||
| export { BaseModel }; |
+30
-4
@@ -6,4 +6,2 @@ services: | ||
| environment: | ||
| # 密码 Mm002580! | ||
| # 用户 root | ||
| MYSQL_ROOT_PASSWORD: 'Mm002580!' | ||
@@ -17,7 +15,35 @@ MYSQL_ROOT_HOST: '%' | ||
| - ./init.sql:/docker-entrypoint-initdb.d/docker-dbinit.sql | ||
| - mysql-data:/var/lib/mysql # 添加 MySQL 数据持久化 | ||
| restart: unless-stopped | ||
| redis: | ||
| image: redis:6.2-alpine | ||
| container_name: redis-container | ||
| image: redis:7-alpine | ||
| container_name: redis-persistent | ||
| command: | ||
| - redis-server | ||
| - --appendonly yes # 启用 AOF 持久化 | ||
| - --appendfsync everysec # 每秒同步一次 | ||
| - --save 900 1 # RDB 持久化:900秒内至少1个变更 | ||
| - --save 300 10 # RDB 持久化:300秒内至少10个变更 | ||
| - --save 60 10000 # RDB 持久化:60秒内至少10000个变更 | ||
| - --dir /data # 数据存储目录 | ||
| - --dbfilename dump.rdb # RDB 文件名 | ||
| - --appendfilename "appendonly.aof" # AOF 文件名 | ||
| - --auto-aof-rewrite-percentage 100 # AOF 重写触发条件 | ||
| - --auto-aof-rewrite-min-size 64mb # AOF 最小重写大小 | ||
| ports: | ||
| - '6379:6379' | ||
| volumes: | ||
| - redis-data:/data # 使用命名卷 | ||
| restart: unless-stopped | ||
| healthcheck: # 添加健康检查 | ||
| test: ["CMD", "redis-cli", "ping"] | ||
| interval: 30s | ||
| timeout: 10s | ||
| retries: 3 | ||
| volumes: | ||
| redis-data: | ||
| driver: local | ||
| mysql-data: | ||
| driver: local |
@@ -1,9 +0,2 @@ | ||
| import { Attributes, FindOptions, ModelStatic, Model, ModelCtor } from 'sequelize'; | ||
| export declare const initLogPath: () => any; | ||
| export declare const logging: (sql: string) => boolean; | ||
| export declare function findAllValues<M extends Model>(this: ModelStatic<M>, options?: FindOptions<Attributes<M>>): Promise<Attributes<M>[]>; | ||
| export declare function findOneValue<M extends Model>(this: ModelStatic<M>, options?: FindOptions<Attributes<M>>): Promise<Attributes<M>>; | ||
| export declare function findAllCurrentValues<M extends Model>(this: ModelStatic<M>, options?: FindOptions<Attributes<M>>): Promise<Attributes<M>[]>; | ||
| export declare function findOneCurrentValue<M extends Model>(this: ModelStatic<M>, options?: FindOptions<Attributes<M>>): Promise<Attributes<M>>; | ||
| export declare function findOrCreateValue<M extends Model>(this: ModelStatic<M>, options?: FindOptions<Attributes<M>>): Promise<[Attributes<M>, boolean]>; | ||
| export type AttributesModel<T extends ModelCtor<Model>> = T extends ModelCtor<infer M> ? (M extends Model<infer U, object> ? U : never) : never; |
@@ -23,41 +23,3 @@ import { mkdirSync, appendFile } from 'fs'; | ||
| }; | ||
| function findAllValues(options) { | ||
| return this.findAll({ | ||
| ...options, | ||
| raw: true | ||
| }); | ||
| } | ||
| function findOneValue(options = {}) { | ||
| return this.findOne({ | ||
| ...options, | ||
| raw: true | ||
| }); | ||
| } | ||
| function findAllCurrentValues(options = {}) { | ||
| return this.findAll({ | ||
| ...options, | ||
| where: { | ||
| ...(options?.where || {}), | ||
| deleted_at: null | ||
| }, | ||
| raw: true | ||
| }); | ||
| } | ||
| function findOneCurrentValue(options = {}) { | ||
| return this.findOne({ | ||
| ...options, | ||
| where: { | ||
| ...(options?.where || {}), | ||
| deleted_at: null | ||
| }, | ||
| raw: true | ||
| }); | ||
| } | ||
| function findOrCreateValue(options = {}) { | ||
| return this.findOrCreate({ | ||
| ...options, | ||
| raw: true | ||
| }); | ||
| } | ||
| export { findAllCurrentValues, findAllValues, findOneCurrentValue, findOneValue, findOrCreateValue, initLogPath, logging }; | ||
| export { initLogPath, logging }; |
@@ -9,2 +9,5 @@ import { Redis as RedisClient, RedisOptions } from 'ioredis'; | ||
| export declare const getIoRedis: (config?: Config & RedisOptions) => RedisClient; | ||
| export declare const closeRedis: () => Promise<void>; | ||
| export declare const checkRedisHealth: () => Promise<boolean>; | ||
| export declare const manualSave: () => Promise<void>; | ||
| export {}; |
+121
-5
@@ -17,6 +17,6 @@ import redisClient from 'ioredis'; | ||
| const connectConfig = { | ||
| host: host || redis?.host || baseConfig.host, | ||
| port: port || redis?.port || baseConfig.port, | ||
| password: password || redis?.password || baseConfig.password, | ||
| db: db || redis?.db || baseConfig.db | ||
| host: host ?? redis?.host ?? baseConfig.host, | ||
| port: port ?? redis?.port ?? baseConfig.port, | ||
| password: password ?? redis?.password ?? baseConfig.password, | ||
| db: db ?? redis?.db ?? baseConfig.db | ||
| }; | ||
@@ -31,5 +31,121 @@ global.ioRedis = new redisClient({ | ||
| }); | ||
| const persistenceBaseConfig = { | ||
| enabled: true, | ||
| rdb: true, | ||
| aof: true, | ||
| aofFsync: 'everysec' | ||
| }; | ||
| const persistenceConfig = { | ||
| enabled: redis?.persistence?.enabled ?? persistenceBaseConfig.enabled, | ||
| rdb: redis?.persistence?.rdb ?? persistenceBaseConfig.rdb, | ||
| aof: redis?.persistence?.aof ?? persistenceBaseConfig.aof, | ||
| aofFsync: redis?.persistence?.aofFsync ?? persistenceBaseConfig.aofFsync | ||
| }; | ||
| global.ioRedis | ||
| .on('connect', () => { | ||
| logger.debug('Redis client connected'); | ||
| }) | ||
| .on('ready', async () => { | ||
| logger.debug('Redis client ready'); | ||
| try { | ||
| await applyPersistenceConfig(global.ioRedis, persistenceConfig); | ||
| } | ||
| catch (error) { | ||
| logger.warn('Failed to apply persistence configuration:', error); | ||
| } | ||
| }) | ||
| .on('error', (err) => { | ||
| logger.error('Redis client error:', err); | ||
| }) | ||
| .on('close', () => { | ||
| logger.debug('Redis client connection closed'); | ||
| }) | ||
| .on('reconnecting', () => { | ||
| logger.debug('Redis client reconnecting'); | ||
| }); | ||
| return global.ioRedis; | ||
| }; | ||
| async function applyPersistenceConfig(client, persistence) { | ||
| if (!persistence.enabled) { | ||
| logger.info('Redis persistence is disabled'); | ||
| return; | ||
| } | ||
| try { | ||
| if (persistence.rdb) { | ||
| await client.call('CONFIG', 'SET', 'save', '900 1 300 10 60 10000'); | ||
| logger.debug('RDB persistence configured'); | ||
| } | ||
| else { | ||
| await client.call('CONFIG', 'SET', 'save', ''); | ||
| logger.debug('RDB persistence disabled'); | ||
| } | ||
| if (persistence.aof) { | ||
| await client.call('CONFIG', 'SET', 'appendonly', 'yes'); | ||
| await client.call('CONFIG', 'SET', 'appendfsync', persistence.aofFsync); | ||
| logger.debug(`AOF persistence configured with fsync: ${persistence.aofFsync}`); | ||
| } | ||
| else { | ||
| await client.call('CONFIG', 'SET', 'appendonly', 'no'); | ||
| logger.debug('AOF persistence disabled'); | ||
| } | ||
| logger.debug('Redis persistence configuration applied successfully'); | ||
| } | ||
| catch (error) { | ||
| logger.warn('Failed to apply persistence configuration:', error); | ||
| } | ||
| } | ||
| const closeRedis = async () => { | ||
| if (global.ioRedis) { | ||
| try { | ||
| await global.ioRedis.save(); | ||
| logger.debug('Redis data saved before closing'); | ||
| } | ||
| catch (error) { | ||
| logger.warn('Failed to save Redis data before closing:', error); | ||
| } | ||
| await global.ioRedis.quit(); | ||
| global.ioRedis = null; | ||
| logger.debug('Redis connection closed'); | ||
| } | ||
| }; | ||
| const checkRedisHealth = async () => { | ||
| try { | ||
| if (!global.ioRedis) { | ||
| return false; | ||
| } | ||
| await global.ioRedis.ping(); | ||
| return true; | ||
| } | ||
| catch (error) { | ||
| logger.error('Redis health check failed:', error); | ||
| return false; | ||
| } | ||
| }; | ||
| const manualSave = async () => { | ||
| if (!global.ioRedis) { | ||
| throw new Error('Redis client not initialized'); | ||
| } | ||
| try { | ||
| await global.ioRedis.bgsave(); | ||
| logger.debug('Manual background save triggered'); | ||
| } | ||
| catch (error) { | ||
| logger.error('Manual background save failed:', error); | ||
| await global.ioRedis.save(); | ||
| logger.debug('Manual save completed (sync)'); | ||
| } | ||
| }; | ||
| if (typeof process !== 'undefined' && process.on) { | ||
| const cleanup = async () => { | ||
| logger.info('Shutting down Redis connection...'); | ||
| await closeRedis().catch((err) => { | ||
| logger.error('Error closing Redis on exit:', err); | ||
| }); | ||
| }; | ||
| process.on('exit', () => { cleanup().catch((err) => logger.error('Error closing Redis on exit:', err)); }); | ||
| process.on('SIGINT', () => { cleanup().catch((err) => logger.error('Error closing Redis on SIGINT:', err)); }); | ||
| process.on('SIGTERM', () => { cleanup().catch((err) => logger.error('Error closing Redis on SIGTERM:', err)); }); | ||
| process.on('uncaughtException', () => { cleanup().catch((err) => logger.error('Error closing Redis on uncaughtException:', err)); }); | ||
| } | ||
| export { getIoRedis }; | ||
| export { checkRedisHealth, closeRedis, getIoRedis, manualSave }; |
+1
-1
| export * from './db/redis'; | ||
| export * from './db/mysql/connect'; | ||
| export { type AttributesModel, findAllCurrentValues, findAllValues, findOneCurrentValue, findOneValue, findOrCreateValue } from './db/mysql/utils'; | ||
| export * from './db/mysql/pub'; |
+2
-2
@@ -1,3 +0,3 @@ | ||
| export { getIoRedis } from './db/redis.js'; | ||
| export { checkRedisHealth, closeRedis, getIoRedis, manualSave } from './db/redis.js'; | ||
| export { getSequelize } from './db/mysql/connect.js'; | ||
| export { findAllCurrentValues, findAllValues, findOneCurrentValue, findOneValue, findOrCreateValue } from './db/mysql/utils.js'; | ||
| export { BaseModel } from './db/mysql/pub.js'; |
+1
-1
| { | ||
| "name": "@alemonjs/db", | ||
| "version": "0.0.12", | ||
| "version": "0.0.13", | ||
| "description": "数据库连接", | ||
@@ -5,0 +5,0 @@ "author": "lemonade", |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
248805
3.36%21
10.53%1364
18.1%