fastify-session-better-sqlite3-store 1.0.5

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.
@@ -0,0 +1,37 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+
11
+ tests-ubuntu:
12
+
13
+ runs-on: ubuntu-latest
14
+
15
+ steps:
16
+
17
+ - uses: actions/checkout@v2
18
+
19
+ - name: Install Dependencies
20
+ run: npm install
21
+
22
+ - name: run tests
23
+ run: npm run test
24
+
25
+ tests-windows:
26
+
27
+ runs-on: windows-latest
28
+
29
+ steps:
30
+
31
+ - uses: actions/checkout@v2
32
+
33
+ - name: Install Dependencies
34
+ run: npm install
35
+
36
+ - name: run tests
37
+ run: npm run test
@@ -0,0 +1,23 @@
1
+ name: standard
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+
11
+ check-code-style-standard:
12
+
13
+ runs-on: ubuntu-latest
14
+
15
+ steps:
16
+
17
+ - uses: actions/checkout@v2
18
+
19
+ - name: Install Standard
20
+ run: npm i -D standard
21
+
22
+ - name: Check code style format
23
+ run: npx standard *.js
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Mordecai Velasco
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # fastify-better-sqlite3-session-store
2
+
3
+ ![ci](https://github.com/mrdcvlsc/fastify-better-sqlite3-session-store/actions/workflows/ci.yml/badge.svg)
4
+ ![standard](https://github.com/mrdcvlsc/fastify-better-sqlite3-session-store/actions/workflows/standard.yml/badge.svg)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ Session store for [@fastify/session](https://github.com/fastify/session) using [better-sqlite3](https://github.com/WiseLibs/better-sqlite3). By default [@fastify/session](https://github.com/fastify/session) stores sessions in-memory. Using this package's class you can store sessions on an **SQLite3** database instead.
8
+
9
+ ## Installation
10
+
11
+ ```
12
+ npm install fastify-better-sqlite3-session-store
13
+ ```
14
+
15
+ ## Example
16
+
17
+ Use with `fastify-session`'s `store` property.
18
+
19
+ ```js
20
+ const fastify = require('fastify')({logger:true})
21
+ const fastifyCookie = require('@fastify/cookie')
22
+ const fastifySession = require('@fastify/session')
23
+ const db = require('better-sqlite3')(`./sqlite.db`)
24
+
25
+ // require module
26
+ const SqliteStore = require('fastify-better-sqlite3-session-store')
27
+
28
+ fastify.register(fastifyCookie)
29
+ fastify.register(fastifySession,{
30
+ // ...
31
+ // other session options
32
+ // ...
33
+ store: new SqliteStore(db)
34
+ })
35
+ ```
36
+
37
+ -----
38
+
39
+ ## Package-Status
40
+
41
+ *premature, there are a lot of rooms for improvement*
42
+
43
+ -----
44
+
45
+ ## License
46
+
47
+ [MIT Licence](https://github.com/mrdcvlsc/fastify-better-sqlite3-session-store/blob/main/LICENSE)
package/index.js ADDED
@@ -0,0 +1,81 @@
1
+ 'use strict'
2
+
3
+ const EventEmitter = require('events')
4
+
5
+ class SqliteStore extends EventEmitter {
6
+ /**
7
+ * @param {Object} sqlite3db database instance of better-sqlite3.
8
+ * @param {string} table table name where session data will be stored, defaults to `session`.
9
+ */
10
+ constructor (sqlite3db, table = 'session') {
11
+ try {
12
+ sqlite3db.exec(`
13
+ create table ${table} (
14
+ sid TEXT PRIMARY KEY NOT NULL,
15
+ expires DATETIME NOT NULL,
16
+ session TEXT NOT NULL
17
+ )`
18
+ )
19
+ } catch (err) {
20
+ if (err.toString() !== 'SqliteError: table session already exists') {
21
+ throw Error(err.toString())
22
+ }
23
+ }
24
+ super()
25
+ this.setSession = sqlite3db.prepare(`INSERT INTO ${table} (sid, expires, session) VALUES (?, ?, ?)`)
26
+ this.getSession = sqlite3db.prepare(`SELECT sid, expires, session FROM ${table} WHERE sid = ?`)
27
+ this.destroySession = sqlite3db.prepare(`DELETE FROM ${table} WHERE sid = ?`)
28
+ }
29
+ }
30
+
31
+ SqliteStore.prototype.set = function set (sessionId, session, callback) {
32
+ try {
33
+ this.setSession.run(
34
+ sessionId,
35
+ session.expires.toISOString(),
36
+ JSON.stringify(session)
37
+ )
38
+ callback(null)
39
+ } catch (err) {
40
+ if (err.code === 'SQLITE_CONSTRAINT_PRIMARYKEY') {
41
+ callback(null)
42
+ } else {
43
+ callback(err)
44
+ }
45
+ }
46
+ }
47
+
48
+ SqliteStore.prototype.get = function get (sessionId, callback) {
49
+ try {
50
+ const results = []
51
+ for (const row of this.getSession.iterate(sessionId)) {
52
+ results.push(row)
53
+ }
54
+
55
+ let session = null
56
+ if (results.length === 1) {
57
+ const found = JSON.parse(results[0].session)
58
+ if (found.expires) {
59
+ if (new Date() < new Date(found.expires)) {
60
+ session = found
61
+ }
62
+ } else {
63
+ session = found
64
+ }
65
+ }
66
+ callback(null, session)
67
+ } catch (err) {
68
+ callback(err)
69
+ }
70
+ }
71
+
72
+ SqliteStore.prototype.destroy = function destroy (sessionId, callback) {
73
+ try {
74
+ this.destroySession.run(sessionId)
75
+ callback(null)
76
+ } catch (err) {
77
+ callback(err)
78
+ }
79
+ }
80
+
81
+ module.exports = SqliteStore
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "fastify-session-better-sqlite3-store",
3
+ "version": "1.0.5",
4
+ "description": "A simple session store for fastify-session using better-sqlite3",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "lint": "npx standard *.js",
8
+ "test": "node test/first.test && node test/second.test"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/mrdcvlsc/fastify-session-better-sqlite3-store.git"
13
+ },
14
+ "keywords": [
15
+ "session",
16
+ "fastify",
17
+ "store",
18
+ "better-sqlite3",
19
+ "sqlite",
20
+ "session-store"
21
+ ],
22
+ "author": "mrdcvlsc",
23
+ "license": "MIT",
24
+ "devDependencies": {
25
+ "@fastify/cookie": "^7.2.0",
26
+ "@fastify/session": "^9.0.0",
27
+ "better-sqlite3": "^7.6.2",
28
+ "fastify": "^4.2.1",
29
+ "standard": "^17.0.0"
30
+ }
31
+ }
@@ -0,0 +1,77 @@
1
+ const fs = require('fs')
2
+
3
+ class SmallTest {
4
+ constructor (testName) {
5
+ this.verdicts = []
6
+ this.names = []
7
+ this.passing = true
8
+ this.testName = testName
9
+ }
10
+
11
+ assertEqual (name, a, b) {
12
+ this.names.push(name)
13
+ const result = (a === b)
14
+ this.verdicts.push(result ? 'PASSED' : 'FAILED')
15
+ if (!result) this.passing = false
16
+ }
17
+
18
+ results () {
19
+ console.log(`\n\nSmallTest : ${this.testName}\n`)
20
+ if (this.verdicts.length === 0) {
21
+ console.log('NO TESTS FOUND')
22
+ return false
23
+ }
24
+ for (let i = 0; i < this.verdicts.length; ++i) {
25
+ console.log(` ${this.verdicts[i]} : ${this.names[i]}`)
26
+ }
27
+ return this.passing
28
+ }
29
+ }
30
+
31
+ async function saveJsonFile (filename, json) {
32
+ try {
33
+ await fs.promises.writeFile(filename, JSON.stringify(json))
34
+ return Promise.resolve()
35
+ } catch (err) {
36
+ return Promise.reject(err)
37
+ }
38
+ }
39
+
40
+ async function readJsonFile (filename) {
41
+ try {
42
+ const json = JSON.parse(await fs.promises.readFile(filename))
43
+ return Promise.resolve(json)
44
+ } catch (err) {
45
+ return Promise.reject(err)
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Simulate an http GET request, with session cookies.
51
+ * @param {Object} fastify instance of fastify application.
52
+ * @param {String} endpoint get request url.
53
+ * @param {Object} clientSessionCookie client session cookie key pair value.
54
+ * @returns {Promise<Response>}
55
+ */
56
+ async function httpGetRequest (fastify, endpoint, clientSessionCookie) {
57
+ try {
58
+ const response = await fastify.inject({
59
+ method: 'GET',
60
+ url: endpoint,
61
+ cookies: clientSessionCookie
62
+ })
63
+ return Promise.resolve(response)
64
+ } catch (err) {
65
+ return Promise.reject(err)
66
+ }
67
+ }
68
+
69
+ async function setTimeoutAsync (delay) {
70
+ return new Promise((resolve) => {
71
+ setTimeout(() => {
72
+ return resolve()
73
+ }, delay)
74
+ })
75
+ }
76
+
77
+ module.exports = { httpGetRequest, saveJsonFile, readJsonFile, SmallTest, setTimeoutAsync }
@@ -0,0 +1,99 @@
1
+ const fastify = require('fastify')({ logger: true })
2
+ const fastifyCookie = require('@fastify/cookie')
3
+ const fastifySession = require('@fastify/session')
4
+ const sqlite3db = require('better-sqlite3')('./test/sqlite.db')
5
+
6
+ const SqliteStore = require('../index')
7
+
8
+ fastify.register(fastifyCookie)
9
+ fastify.register(fastifySession, {
10
+ secret: 'a super duper secret session string',
11
+ cookie: {
12
+ secure: false,
13
+ maxAge: 5000
14
+ },
15
+ store: new SqliteStore(sqlite3db),
16
+ saveUninitialized: false
17
+ })
18
+
19
+ fastify.get('/set/:name', (request, reply) => {
20
+ request.session.name = request.params.name
21
+ reply.send(`recorded:${request.session.name}`)
22
+ })
23
+
24
+ fastify.get('/destroy', async (request, reply) => {
25
+ try {
26
+ if (request.session.name) {
27
+ const destroyedUser = request.session.name
28
+ await request.session.destroy()
29
+ return reply.send(`destroyed:${destroyedUser}`)
30
+ } else {
31
+ return reply.send('destroyed:no-user')
32
+ }
33
+ } catch (err) {
34
+ fastify.log.error(err)
35
+ return process.exit(1)
36
+ }
37
+ })
38
+
39
+ fastify.get('/session', (request, reply) => {
40
+ if (request.session.name) {
41
+ reply.send(`session:${request.session.name}`)
42
+ } else {
43
+ reply.send('session:no-user')
44
+ }
45
+ })
46
+
47
+ const { SmallTest, httpGetRequest, saveJsonFile, setTimeoutAsync } = require('./SmallTest')
48
+
49
+ async function start () {
50
+ const t = new SmallTest('TEST SET CASE 1')
51
+ try {
52
+ await fastify.listen({ port: 3000 })
53
+
54
+ const subject1 = await httpGetRequest(fastify, '/session')
55
+ const subject2 = await httpGetRequest(fastify, '/set/subject2')
56
+ const cookie1 = {
57
+ sessionId: subject2.cookies[0].value
58
+ }
59
+ const subject3 = await httpGetRequest(fastify, '/session', cookie1)
60
+ const subject4 = await httpGetRequest(fastify, '/destroy', cookie1)
61
+ const subject5 = await httpGetRequest(fastify, '/session', cookie1)
62
+ const subject6 = await httpGetRequest(fastify, '/set/subject6')
63
+ const cookie2 = {
64
+ sessionId: subject6.cookies[0].value
65
+ }
66
+ const subject7 = await httpGetRequest(fastify, '/session', cookie2)
67
+ await setTimeoutAsync(8000)
68
+ const subject8 = await httpGetRequest(fastify, '/session', cookie2)
69
+ const subject9 = await httpGetRequest(fastify, '/set/subject9')
70
+ const cookie3 = {
71
+ sessionId: subject9.cookies[0].value
72
+ }
73
+
74
+ t.assertEqual('session none', subject1.payload, 'session:no-user')
75
+ t.assertEqual('session recorded', subject2.payload, 'recorded:subject2')
76
+ t.assertEqual('session existing', subject3.payload, 'session:subject2')
77
+ t.assertEqual('session destroyed', subject4.payload, 'destroyed:subject2')
78
+ t.assertEqual('session no more', subject5.payload, 'session:no-user')
79
+ t.assertEqual('new session recorded', subject6.payload, 'recorded:subject6')
80
+ t.assertEqual('new session existing', subject7.payload, 'session:subject6')
81
+ t.assertEqual('new session expired', subject8.payload, 'session:no-user')
82
+ t.assertEqual('session saved then server shutsdown', subject9.payload, 'recorded:subject9')
83
+
84
+ await fastify.close()
85
+ await saveJsonFile('test/cookie.json', cookie3)
86
+
87
+ if (t.results()) {
88
+ console.log(t.testName, '=> PASSED ALL TEST\n')
89
+ } else {
90
+ console.log(t.testName, '=> FAILED SOME TEST\n')
91
+ process.exit(1)
92
+ }
93
+ } catch (err) {
94
+ fastify.log.error(err)
95
+ process.exit(1)
96
+ }
97
+ }
98
+
99
+ start()
@@ -0,0 +1,52 @@
1
+ const fastify = require('fastify')({ logger: true })
2
+ const fastifyCookie = require('@fastify/cookie')
3
+ const fastifySession = require('@fastify/session')
4
+ const sqlite3db = require('better-sqlite3')('./test/sqlite.db')
5
+
6
+ const SqliteStore = require('../index')
7
+
8
+ fastify.register(fastifyCookie)
9
+ fastify.register(fastifySession, {
10
+ secret: 'a super duper secret session string',
11
+ cookie: {
12
+ secure: false,
13
+ maxAge: 5000
14
+ },
15
+ store: new SqliteStore(sqlite3db),
16
+ saveUninitialized: false
17
+ })
18
+
19
+ fastify.get('/session', (request, reply) => {
20
+ if (request.session.name) {
21
+ reply.send(`session:${request.session.name}`)
22
+ } else {
23
+ reply.send('session:no-user')
24
+ }
25
+ })
26
+
27
+ const { SmallTest, httpGetRequest, readJsonFile } = require('./SmallTest')
28
+
29
+ async function start () {
30
+ const t = new SmallTest('TEST SET CASE 2')
31
+ try {
32
+ await fastify.listen({ port: 3001 })
33
+
34
+ const cookie = await readJsonFile('test/cookie.json')
35
+ const subject9 = await httpGetRequest(fastify, '/session', cookie)
36
+ t.assertEqual('session existing after server restart', subject9.payload, 'session:subject9')
37
+
38
+ await fastify.close()
39
+
40
+ if (t.results()) {
41
+ console.log(t.testName, '=> PASSED ALL TEST\n')
42
+ } else {
43
+ console.log(t.testName, '=> FAILED SOME TEST\n')
44
+ process.exit(1)
45
+ }
46
+ } catch (err) {
47
+ fastify.log.error(err)
48
+ process.exit(1)
49
+ }
50
+ }
51
+
52
+ start()