lamix 4.2.20 → 4.2.22
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.
- package/lib/{index.d.ts → @types/index.d.ts} +3 -17
- package/lib/index.js +103 -200
- package/package.json +4 -4
|
@@ -501,26 +501,12 @@ export class LamixSessionStore {
|
|
|
501
501
|
constructor(options?: {});
|
|
502
502
|
ttl: any;
|
|
503
503
|
cleanupInterval: any;
|
|
504
|
-
|
|
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<
|
|
507
|
+
touch(sid: any, sessionData: any, cb: any): Promise<any>;
|
|
522
508
|
_startCleanup(): void;
|
|
523
|
-
_cleanupTimer:
|
|
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
|
-
/*
|
|
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
|
|
4126
|
-
? zlib.inflateSync(Buffer.from(raw, 'base64')).toString()
|
|
4127
|
-
: raw;
|
|
4070
|
+
const now = Date.now();
|
|
4128
4071
|
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
}
|
|
4072
|
+
const row = await Session
|
|
4073
|
+
.query()
|
|
4074
|
+
.where('sid', sid)
|
|
4075
|
+
.where('expires', '>', now)
|
|
4076
|
+
.first();
|
|
4135
4077
|
|
|
4136
|
-
|
|
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
|
-
|
|
4155
|
-
async get(sid, cb) {
|
|
4156
|
-
let sessionData = null;
|
|
4080
|
+
let sessionJSON;
|
|
4157
4081
|
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
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
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4190
|
-
|
|
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
|
-
/*
|
|
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
|
|
4203
|
-
if (!data) return this._safeCb(cb, null);
|
|
4125
|
+
const json = JSON.stringify(sessionData);
|
|
4204
4126
|
|
|
4205
|
-
|
|
4127
|
+
// Async compression (non-blocking)
|
|
4128
|
+
const compressed = await gzip(json);
|
|
4129
|
+
const base64Data = compressed.toString('base64');
|
|
4206
4130
|
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
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
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
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
|
-
|
|
4149
|
+
cb(null);
|
|
4233
4150
|
} catch (err) {
|
|
4234
|
-
|
|
4235
|
-
|
|
4151
|
+
cb(new DBError('Failed to persist session', {
|
|
4152
|
+
sid,
|
|
4153
|
+
operation: 'set',
|
|
4154
|
+
err
|
|
4155
|
+
}));
|
|
4236
4156
|
}
|
|
4237
4157
|
}
|
|
4238
4158
|
|
|
4239
|
-
/*
|
|
4159
|
+
/* ---------- Destroy ---------- */
|
|
4160
|
+
|
|
4240
4161
|
async destroy(sid, cb) {
|
|
4241
4162
|
try {
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
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
|
-
|
|
4168
|
+
cb(null);
|
|
4262
4169
|
} catch (err) {
|
|
4263
|
-
|
|
4264
|
-
|
|
4170
|
+
cb(new DBError('Failed to destroy session', {
|
|
4171
|
+
sid,
|
|
4172
|
+
operation: 'destroy',
|
|
4173
|
+
err
|
|
4174
|
+
}));
|
|
4265
4175
|
}
|
|
4266
4176
|
}
|
|
4267
4177
|
|
|
4268
|
-
/*
|
|
4178
|
+
/* ---------- Touch ---------- */
|
|
4179
|
+
|
|
4269
4180
|
async touch(sid, sessionData, cb) {
|
|
4270
|
-
if (!sessionData) return
|
|
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
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4300
|
-
|
|
4196
|
+
cb(new DBError('Failed to touch session', {
|
|
4197
|
+
sid,
|
|
4198
|
+
operation: 'touch',
|
|
4199
|
+
err
|
|
4200
|
+
}));
|
|
4301
4201
|
}
|
|
4302
4202
|
}
|
|
4303
4203
|
|
|
4304
|
-
/*
|
|
4305
|
-
_startCleanup() {
|
|
4306
|
-
if (!this._hasSessionModel()) return;
|
|
4204
|
+
/* ---------- Cleanup ---------- */
|
|
4307
4205
|
|
|
4206
|
+
_startCleanup() {
|
|
4308
4207
|
this._cleanupTimer = setInterval(async () => {
|
|
4309
4208
|
try {
|
|
4310
|
-
await
|
|
4209
|
+
await Session
|
|
4210
|
+
.query()
|
|
4311
4211
|
.where('expires', '<', Date.now())
|
|
4312
4212
|
.delete();
|
|
4313
4213
|
} catch (err) {
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
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,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lamix",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.22",
|
|
4
4
|
"description": "lamix - ORM for Node-express js",
|
|
5
|
-
"main": "./lib",
|
|
5
|
+
"main": "./lib/index.js",
|
|
6
|
+
"types": "./lib/@types/index.d.ts",
|
|
6
7
|
"type": "commonjs",
|
|
7
8
|
"exports": {
|
|
8
9
|
".": {
|
|
9
10
|
"require": "./lib/index.js",
|
|
10
|
-
"types": "./lib/index.d.ts"
|
|
11
|
+
"types": "./lib/@types/index.d.ts"
|
|
11
12
|
}
|
|
12
13
|
},
|
|
13
14
|
"files": [
|
|
@@ -32,7 +33,6 @@
|
|
|
32
33
|
"bcrypt": "^6.0.0",
|
|
33
34
|
"express-session": "^1.19.0",
|
|
34
35
|
"lru-cache": "^11.2.5",
|
|
35
|
-
"ioredis": "^5.9.2",
|
|
36
36
|
"chalk": "^4.1.2",
|
|
37
37
|
"dotenv": "^17.2.4 "
|
|
38
38
|
},
|