forked from HowlingEverett/koa-generic-session-file
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
108 lines (97 loc) · 3.16 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
"use strict";
let path = require("path");
let fs = require("fs");
let EventEmitter = require("events");
let promisify = require("es6-promisify");
// glob.glob here is just exploiting a hidden property of glob to allow
// me to stub this for testing. I know it's an old API compatibility
// feature.
let glob = promisify(require("glob").glob);
let mkdirp = promisify(require("mkdirp"));
let debug = require("debug")("koa-session-file-store");
let readFile = promisify(fs.readFile);
let writeFile = promisify(fs.writeFile);
let stat = promisify(fs.stat);
let unlink = promisify(fs.unlink);
class FileStore extends EventEmitter {
constructor(options) {
super();
this.emit("connect");
this.options = options || {};
this.options.sessionDirectory = path.resolve(
this.options.sessionDirectory || "./sessions");
}
/**
* Load a session file from the sessionDirectory called <sid>.json, return
* parsed session data
* @param {string} sid Unique sessionID (generated by koa-generic-session)
* @return {object} Parsed session object, or null if no session file exists
*/
get(sid) {
let sessionGlob = path.join(this.options.sessionDirectory,
`${sid}__*.json`);
let sessionPath;
return glob(sessionGlob, {nonull: false}).then(files => {
if (files.length === 0) {
debug("No session available for user");
return true;
}
sessionPath = files[0];
return hasSessionExpired(sessionPath, this.options.sessionDirectory);
}).then(sessionExpired => {
if (!sessionPath) {
return null;
}
let sessionFilePath = path.resolve(this.options.sessionDirectory,
sessionPath);
if (sessionExpired) {
debug("Session expired, removing session file");
return unlink(sessionFilePath);
}
return readFile(sessionFilePath, "utf8");
}).then(content => {
if (!content) {
return null;
}
try {
return JSON.parse(content);
} catch (err) {
// Ignore parsing error
debug("Parse session error: %s", err.message);
return null;
}
});
}
set(sid, session, ttl) {
let sessionFilePath = path.resolve(this.options.sessionDirectory,
`${sid}__${ttl}.json`);
return mkdirp(this.options.sessionDirectory).then(() => {
return writeFile(sessionFilePath, JSON.stringify(session));
});
}
destroy(sid) {
let sessionGlob = path.join(this.options.sessionDirectory,
`${sid}__*.json`);
return glob(sessionGlob, {nonull: false}).then((files) => {
if (files.length === 0) {
return null;
}
return unlink(files[0]);
});
}
}
/*
* Will return true if the number of milliseconds between the current time and
* the last modified time of the session file is greater than the TTL value
* kludgily stored in the session's filename.
*/
function hasSessionExpired(sessionPath, sessionsDirectory) {
let ttl = parseInt(path.basename(sessionPath).split("__")[1]);
return stat(path.resolve(sessionsDirectory, sessionPath))
.then(stats => {
return Date.now() - stats.mtime > ttl;
}).catch(() => {
return true;
});
}
module.exports = FileStore;