-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathindex.js
140 lines (131 loc) · 3.79 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
const assert = require('assert');
const helper = require('think-helper');
const mysql = require('think-mysql');
const gc = require('think-gc');
const initSessionData = Symbol('think-session-mysql-init');
/*
CREATE TABLE `think_session` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`cookie` varchar(255) NOT NULL DEFAULT '',
`data` text,
`expire` bigint(11) NOT NULL DEFAULT '0',
`maxage` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `cookie` (`cookie`),
KEY `expire` (`expire`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
*/
/**
* use mysql to store session
*
*/
class MysqlSession {
constructor(options = {}, ctx, cookieOptions) {
assert(options.cookie, '.cookie required');
this.options = options;
this.cookieOptions = cookieOptions;
this.mysql = mysql.getInstance(helper.omit(options, 'cookie'));
this.ctx = ctx;
this.data = {};
this.tableName = (this.options.prefix || '') + 'session';
this.status = 0;
this.maxAge = this.options.maxAge || 0;
this.expire = 0;
this.gcType = `session_mysql`;
gc(this, this.options.gcInterval);
// flush session when request finish
this.ctx.res.once('finish', () => {
this.flush();
});
}
[initSessionData]() {
if (this.initPromise) {
return this.initPromise;
}
if (this.options.fresh || this.status === -1) {
this.initPromise = Promise.resolve();
return this.initPromise;
}
this.initPromise = this.mysql.query({
sql: `SELECT * FROM \`${this.tableName}\` WHERE cookie = ? `,
values: [this.options.cookie]
}).then(row => {
if (row.length === 0) return;
// session data is expired
if (row[0].expire < Date.now()) {
return this.delete();
}
const content = row[0].data;
this.data = JSON.parse(content) || {};
if (row[0].maxAge) {
this.maxAge = row[0].maxAge;
}
this.expire = row[0].expire;
this.autoUpdate();
}).catch(err => {
console.error(err);
});
return this.initPromise;
}
autoUpdate() {
if (this.maxAge && this.expire) {
const rate = (this.expire - Date.now()) / this.maxAge;
if (rate < this.cookieOptions.autoUpdateRate) {
this.status = 1;
this.cookieOptions.maxAge = this.maxAge;
// update cookie maxAge
this.ctx.cookie(this.cookieOptions.name, this.options.cookie, this.cookieOptions);
}
}
}
get(name) {
return this[initSessionData]().then(() => {
if (this.options.autoUpdate) {
this.status = 1;
}
return name ? this.data[name] : this.data;
});
}
set(name, value) {
return this[initSessionData]().then(() => {
this.status = 1;
if (value === null) {
delete this.data[name];
} else {
this.data[name] = value;
}
});
}
'delete'() {
this.status = -1;
this.data = {};
return Promise.resolve();
}
flush() {
if (this.status === -1) {
this.status = 0;
// delete data
this.mysql.execute({
sql: `DELETE FROM \`${this.tableName}\` WHERE cookie=?`,
values: [this.options.cookie]
});
} else if (this.status === 1) {
this.status = 0;
// insert or update data
const expire = Date.now() + this.maxAge;
const fields = [this.options.cookie, JSON.stringify(this.data), expire, this.maxAge];
this.mysql.execute({
sql: `INSERT INTO \`${this.tableName}\` (cookie, data, expire, maxage) VALUES(?, ?, ?, ?)
ON DUPLICATE KEY UPDATE data=?, expire=?, maxage=?`,
values: [...fields, fields[1], fields[2], fields[3]]
});
}
return Promise.resolve();
}
gc() {
return this.mysql.execute({
sql: `DELETE FROM \`${this.tableName}\` WHERE expire < ${Date.now()}`
});
}
}
module.exports = MysqlSession;