lamix 4.2.20 → 4.2.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/lib/index.d.ts +3 -17
  2. package/lib/index.js +103 -200
  3. package/package.json +2 -3
package/lib/index.d.ts CHANGED
@@ -501,26 +501,12 @@ export class LamixSessionStore {
501
501
  constructor(options?: {});
502
502
  ttl: any;
503
503
  cleanupInterval: any;
504
- logger: any;
505
- compress: any;
506
- cache: LRUCache<{}, {}, unknown>;
507
- redisEnabled: boolean;
508
- redisRetryAt: number;
509
- redisCooldown: any;
510
- redis: any;
511
- _safeCb(cb: any, err: any, result?: any): void;
512
- _hasSessionModel(): boolean;
513
- _canUseRedis(): boolean;
514
- _markRedisFailure(err: any): void;
515
- _serialize(data: any): string;
516
- _deserialize(raw: any): any;
517
- _checkRedisHealth(): Promise<void>;
518
- get(sid: any, cb: any): Promise<void>;
504
+ get(sid: any, cb: any): Promise<any>;
519
505
  set(sid: any, sessionData: any, cb: any): Promise<void>;
520
506
  destroy(sid: any, cb: any): Promise<void>;
521
- touch(sid: any, sessionData: any, cb: any): Promise<void>;
507
+ touch(sid: any, sessionData: any, cb: any): Promise<any>;
522
508
  _startCleanup(): void;
523
- _cleanupTimer: any;
509
+ _cleanupTimer: NodeJS.Timeout;
524
510
  }
525
511
  export class BaseModel extends Model {
526
512
  static passwordField: string;
package/lib/index.js CHANGED
@@ -5,7 +5,6 @@ const { performance } = require('perf_hooks');
5
5
  const { AsyncLocalStorage } = require('async_hooks');
6
6
  require('dotenv').config();
7
7
  const session = require('express-session');
8
- const Redis = require('ioredis');
9
8
  const { LRUCache } = require('lru-cache');
10
9
  const zlib = require('zlib');
11
10
 
@@ -4043,155 +4042,79 @@ class Session extends Model {
4043
4042
  let SessionModel = null;
4044
4043
 
4045
4044
  try {
4046
- // If this throws, DB-backed sessions are simply disabled
4047
4045
  SessionModel = Session;
4048
4046
  } catch (err) {
4049
4047
  SessionModel = null;
4050
4048
  }
4051
4049
 
4052
4050
  /* -------------------- Store -------------------- */
4051
+ const { promisify } = require('util');
4052
+
4053
+ const gzip = promisify(zlib.gzip);
4054
+ const gunzip = promisify(zlib.gunzip);
4055
+
4053
4056
  class LamixSessionStore extends session.Store {
4054
4057
  constructor(options = {}) {
4055
4058
  super();
4056
4059
 
4057
- this.ttl = options.ttl || 86400;
4060
+ this.ttl = options.ttl || 86400; // seconds
4058
4061
  this.cleanupInterval = options.cleanupInterval || 60000;
4059
- this.logger = options.logger || console;
4060
- this.compress = options.compress ?? true;
4061
-
4062
- /* -------- In-memory cache -------- */
4063
- this.cache = new LRUCache({
4064
- max: options.cacheSize || 1000,
4065
- ttl: options.cacheTTL || 60000,
4066
- });
4067
-
4068
- /* -------- Redis -------- */
4069
- this.redisEnabled = false;
4070
- this.redisRetryAt = 0;
4071
- this.redisCooldown = options.redisCooldown || 30000;
4072
-
4073
- if (options.redis) {
4074
- this.redis =
4075
- options.redis instanceof Redis
4076
- ? options.redis
4077
- : new Redis(options.redis);
4078
-
4079
- this._checkRedisHealth();
4080
- }
4081
4062
 
4082
4063
  this._startCleanup();
4083
4064
  }
4084
4065
 
4085
- /* -------------------- Safety Helpers -------------------- */
4086
- _safeCb(cb, err, result = null) {
4087
- try {
4088
- cb(err, result);
4089
- } catch (e) {
4090
- this.logger.error('Session callback threw', e);
4091
- }
4092
- }
4093
-
4094
- _hasSessionModel() {
4095
- return !!SessionModel;
4096
- }
4097
-
4098
- _canUseRedis() {
4099
- return this.redisEnabled && Date.now() > this.redisRetryAt;
4100
- }
4101
-
4102
- _markRedisFailure(err) {
4103
- this.redisEnabled = false;
4104
- this.redisRetryAt = Date.now() + this.redisCooldown;
4105
- this.logger.warn('Redis disabled temporarily', err);
4106
- }
4107
-
4108
- /* -------------------- Serialization -------------------- */
4109
- _serialize(data) {
4110
- try {
4111
- const json = JSON.stringify(data);
4112
- return this.compress
4113
- ? zlib.deflateSync(Buffer.from(json)).toString('base64')
4114
- : json;
4115
- } catch (err) {
4116
- this.logger.warn('Serialize failed, skipping persistence', err);
4117
- return null;
4118
- }
4119
- }
4120
-
4121
- _deserialize(raw) {
4122
- if (!raw) return null;
4066
+ /* ---------- Get ---------- */
4123
4067
 
4068
+ async get(sid, cb) {
4124
4069
  try {
4125
- const str = this.compress
4126
- ? zlib.inflateSync(Buffer.from(raw, 'base64')).toString()
4127
- : raw;
4070
+ const now = Date.now();
4128
4071
 
4129
- return JSON.parse(str);
4130
- } catch (err) {
4131
- this.logger.warn('Deserialize failed, dropping session', err);
4132
- return null;
4133
- }
4134
- }
4072
+ const row = await Session
4073
+ .query()
4074
+ .where('sid', sid)
4075
+ .where('expires', '>', now)
4076
+ .first();
4135
4077
 
4136
- /* -------------------- Redis Health -------------------- */
4137
- async _checkRedisHealth() {
4138
- try {
4139
- await this.redis.ping();
4140
- this.redisEnabled = true;
4141
- this.logger.info('Redis session store active');
4142
-
4143
- this.redis.on('error', (err) => this._markRedisFailure(err));
4144
- this.redis.on('connect', () => {
4145
- this.redisEnabled = true;
4146
- this.logger.info('Redis reconnected');
4147
- });
4148
- } catch (err) {
4149
- this.redisEnabled = false;
4150
- this.logger.warn('Redis unavailable', err);
4151
- }
4152
- }
4078
+ if (!row) return cb(null, null);
4153
4079
 
4154
- /* -------------------- GET -------------------- */
4155
- async get(sid, cb) {
4156
- let sessionData = null;
4080
+ let sessionJSON;
4157
4081
 
4158
- try {
4159
- if (this.cache.has(sid)) {
4160
- return this._safeCb(cb, null, this.cache.get(sid));
4161
- }
4162
-
4163
- if (this._canUseRedis()) {
4164
- try {
4165
- const raw = await this.redis.get(`sess:${sid}`);
4166
- sessionData = this._deserialize(raw);
4167
- } catch (err) {
4168
- this._markRedisFailure(err);
4169
- }
4082
+ // ---------- Step 1: Decompress ----------
4083
+ try {
4084
+ const buffer = Buffer.from(row.data, 'base64');
4085
+ const decompressed = await gunzip(buffer);
4086
+ sessionJSON = decompressed.toString('utf8');
4087
+ } catch {
4088
+ // fallback to legacy plain JSON
4089
+ sessionJSON = row.data;
4170
4090
  }
4171
4091
 
4172
- if (!sessionData && this._hasSessionModel()) {
4173
- try {
4174
- const row = await SessionModel.query()
4175
- .where('sid', sid)
4176
- .where('expires', '>', Date.now())
4177
- .first();
4092
+ // ---------- Step 2: Parse JSON safely ----------
4093
+ try {
4094
+ const parsed = JSON.parse(sessionJSON);
4095
+ return cb(null, parsed);
4096
+ } catch (parseErr) {
4097
+ console.warn('Corrupted session detected. Destroying:', sid);
4098
+
4099
+ // destroy corrupted session
4100
+ await Session
4101
+ .query()
4102
+ .where('sid', sid)
4103
+ .delete();
4178
4104
 
4179
- if (row) sessionData = this._deserialize(row.data);
4180
- } catch (err) {
4181
- this.logger.warn('DB GET failed, disabling DB sessions', err);
4182
- SessionModel = null;
4183
- }
4105
+ return cb(null, null); // treat as no session
4184
4106
  }
4185
4107
 
4186
- if (sessionData) this.cache.set(sid, sessionData);
4187
- this._safeCb(cb, null, sessionData);
4188
4108
  } catch (err) {
4189
- this.logger.error('GET failed safely', err);
4190
- this._safeCb(cb, null, null);
4109
+ console.error('Session GET failed:', err);
4110
+
4111
+ // IMPORTANT: never pass fatal error to express-session
4112
+ return cb(null, null);
4191
4113
  }
4192
4114
  }
4193
4115
 
4194
- /* -------------------- SET -------------------- */
4116
+ /* ---------- Set ---------- */
4117
+
4195
4118
  async set(sid, sessionData, cb) {
4196
4119
  try {
4197
4120
  const expires =
@@ -4199,75 +4122,63 @@ class LamixSessionStore extends session.Store {
4199
4122
  ? new Date(sessionData.cookie.expires).getTime()
4200
4123
  : Date.now() + this.ttl * 1000;
4201
4124
 
4202
- const data = this._serialize(sessionData);
4203
- if (!data) return this._safeCb(cb, null);
4125
+ const json = JSON.stringify(sessionData);
4204
4126
 
4205
- this.cache.set(sid, sessionData);
4127
+ // Async compression (non-blocking)
4128
+ const compressed = await gzip(json);
4129
+ const base64Data = compressed.toString('base64');
4206
4130
 
4207
- if (this._hasSessionModel()) {
4208
- try {
4209
- const existing = await SessionModel.query()
4210
- .where('sid', sid)
4211
- .first();
4212
-
4213
- if (existing) {
4214
- await existing.update({ data, expires });
4215
- } else {
4216
- await new SessionModel({ sid, data, expires }, false).saveNew();
4217
- }
4218
- } catch (err) {
4219
- this.logger.warn('DB SET failed, disabling DB sessions', err);
4220
- SessionModel = null;
4221
- }
4222
- }
4131
+ const payload = {
4132
+ sid,
4133
+ data: base64Data,
4134
+ expires
4135
+ };
4223
4136
 
4224
- if (this._canUseRedis()) {
4225
- try {
4226
- await this.redis.set(`sess:${sid}`, data, 'PX', expires - Date.now());
4227
- } catch (err) {
4228
- this._markRedisFailure(err);
4229
- }
4137
+ const existing = await Session
4138
+ .query()
4139
+ .where('sid', sid)
4140
+ .first();
4141
+
4142
+ if (existing) {
4143
+ await existing.update(payload);
4144
+ } else {
4145
+ const session = new Session(payload, false);
4146
+ await session.saveNew(payload);
4230
4147
  }
4231
4148
 
4232
- this._safeCb(cb, null);
4149
+ cb(null);
4233
4150
  } catch (err) {
4234
- this.logger.error('SET failed safely', err);
4235
- this._safeCb(cb, null);
4151
+ cb(new DBError('Failed to persist session', {
4152
+ sid,
4153
+ operation: 'set',
4154
+ err
4155
+ }));
4236
4156
  }
4237
4157
  }
4238
4158
 
4239
- /* -------------------- DESTROY -------------------- */
4159
+ /* ---------- Destroy ---------- */
4160
+
4240
4161
  async destroy(sid, cb) {
4241
4162
  try {
4242
- this.cache.delete(sid);
4243
-
4244
- if (this._hasSessionModel()) {
4245
- try {
4246
- await SessionModel.query().where('sid', sid).delete();
4247
- } catch (err) {
4248
- this.logger.warn('DB DESTROY failed, disabling DB sessions', err);
4249
- SessionModel = null;
4250
- }
4251
- }
4252
-
4253
- if (this._canUseRedis()) {
4254
- try {
4255
- await this.redis.del(`sess:${sid}`);
4256
- } catch (err) {
4257
- this._markRedisFailure(err);
4258
- }
4259
- }
4163
+ await Session
4164
+ .query()
4165
+ .where('sid', sid)
4166
+ .delete();
4260
4167
 
4261
- this._safeCb(cb, null);
4168
+ cb(null);
4262
4169
  } catch (err) {
4263
- this.logger.error('DESTROY failed safely', err);
4264
- this._safeCb(cb, null);
4170
+ cb(new DBError('Failed to destroy session', {
4171
+ sid,
4172
+ operation: 'destroy',
4173
+ err
4174
+ }));
4265
4175
  }
4266
4176
  }
4267
4177
 
4268
- /* -------------------- TOUCH -------------------- */
4178
+ /* ---------- Touch ---------- */
4179
+
4269
4180
  async touch(sid, sessionData, cb) {
4270
- if (!sessionData) return this._safeCb(cb, null);
4181
+ if (!sessionData) return cb(null);
4271
4182
 
4272
4183
  try {
4273
4184
  const expires =
@@ -4275,45 +4186,37 @@ class LamixSessionStore extends session.Store {
4275
4186
  ? new Date(sessionData.cookie.expires).getTime()
4276
4187
  : Date.now() + this.ttl * 1000;
4277
4188
 
4278
- this.cache.set(sid, sessionData);
4279
-
4280
- if (this._hasSessionModel()) {
4281
- try {
4282
- await SessionModel.query().where('sid', sid).update({ expires });
4283
- } catch (err) {
4284
- this.logger.warn('DB TOUCH failed, disabling DB sessions', err);
4285
- SessionModel = null;
4286
- }
4287
- }
4189
+ await Session
4190
+ .query()
4191
+ .where('sid', sid)
4192
+ .update({ expires });
4288
4193
 
4289
- if (this._canUseRedis()) {
4290
- try {
4291
- await this.redis.pexpire(`sess:${sid}`, expires - Date.now());
4292
- } catch (err) {
4293
- this._markRedisFailure(err);
4294
- }
4295
- }
4296
-
4297
- this._safeCb(cb, null);
4194
+ cb();
4298
4195
  } catch (err) {
4299
- this.logger.error('TOUCH failed safely', err);
4300
- this._safeCb(cb, null);
4196
+ cb(new DBError('Failed to touch session', {
4197
+ sid,
4198
+ operation: 'touch',
4199
+ err
4200
+ }));
4301
4201
  }
4302
4202
  }
4303
4203
 
4304
- /* -------------------- Cleanup -------------------- */
4305
- _startCleanup() {
4306
- if (!this._hasSessionModel()) return;
4204
+ /* ---------- Cleanup ---------- */
4307
4205
 
4206
+ _startCleanup() {
4308
4207
  this._cleanupTimer = setInterval(async () => {
4309
4208
  try {
4310
- await SessionModel.query()
4209
+ await Session
4210
+ .query()
4311
4211
  .where('expires', '<', Date.now())
4312
4212
  .delete();
4313
4213
  } catch (err) {
4314
- this.logger.warn('Cleanup failed, disabling DB sessions', err);
4315
- SessionModel = null;
4316
- clearInterval(this._cleanupTimer);
4214
+ console.error(
4215
+ new DBError('Session cleanup failed', {
4216
+ operation: 'cleanup',
4217
+ err
4218
+ })
4219
+ );
4317
4220
  }
4318
4221
  }, this.cleanupInterval);
4319
4222
 
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "lamix",
3
- "version": "4.2.20",
3
+ "version": "4.2.21",
4
4
  "description": "lamix - ORM for Node-express js",
5
- "main": "./lib",
5
+ "main": "./lib/index.js",
6
6
  "type": "commonjs",
7
7
  "exports": {
8
8
  ".": {
@@ -32,7 +32,6 @@
32
32
  "bcrypt": "^6.0.0",
33
33
  "express-session": "^1.19.0",
34
34
  "lru-cache": "^11.2.5",
35
- "ioredis": "^5.9.2",
36
35
  "chalk": "^4.1.2",
37
36
  "dotenv": "^17.2.4 "
38
37
  },