diff --git a/package.json b/package.json index 405025b..0d295f0 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,6 @@ "@japa/runner": "3.0.0-6", "@poppinss/dev-utils": "^2.0.3", "@swc/core": "^1.3.70", - "@types/fs-extra": "^11.0.1", "@types/node": "^20.4.2", "@types/set-cookie-parser": "^2.4.3", "@types/supertest": "^2.0.12", @@ -71,8 +70,7 @@ "typescript": "^5.1.6" }, "dependencies": { - "@poppinss/utils": "6.5.0-3", - "fs-extra": "^11.1.1" + "@poppinss/utils": "6.5.0-3" }, "peerDependencies": { "@adonisjs/core": "6.1.5-10", diff --git a/src/drivers/file.ts b/src/drivers/file.ts index 0a55763..4583212 100644 --- a/src/drivers/file.ts +++ b/src/drivers/file.ts @@ -7,11 +7,10 @@ * file that was distributed with this source code. */ -import { join } from 'node:path' +import { dirname, join } from 'node:path' +import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises' import { Exception } from '@poppinss/utils' import { MessageBuilder } from '@poppinss/utils' -import { ensureFile, outputFile, remove } from 'fs-extra/esm' -import { readFile } from 'node:fs/promises' import { SessionConfig, SessionDriverContract } from '../types.js' /** @@ -38,13 +37,52 @@ export class FileDriver implements SessionDriverContract { return join(this.#config.file!.location, `${sessionId}.txt`) } + /** + * Check if the given path exists or not + */ + async #pathExists(path: string) { + try { + await access(path) + return true + } catch { + return false + } + } + + /** + * Output file with contents to the given path + */ + async #outputFile(path: string, content: string) { + const pathDirname = dirname(path) + const dirExists = await this.#pathExists(pathDirname) + + if (!dirExists) { + await mkdir(pathDirname, { recursive: true }) + } + + await writeFile(path, content, 'utf-8') + } + + /** + * Ensure the file exists. Create it if missing + */ + async #ensureFile(path: string) { + const pathDirname = dirname(path) + const dirExists = await this.#pathExists(pathDirname) + + if (!dirExists) { + await mkdir(pathDirname, { recursive: true }) + await writeFile(path, '', 'utf-8') + } + } + /** * Returns file contents. A new file will be created if it's * missing. */ async read(sessionId: string): Promise<{ [key: string]: any } | null> { const filePath = this.#getFilePath(sessionId) - await ensureFile(filePath) + await this.#ensureFile(filePath) const contents = await readFile(filePath, 'utf-8') if (!contents.trim()) { @@ -71,14 +109,14 @@ export class FileDriver implements SessionDriverContract { } const message = new MessageBuilder().build(values, undefined, sessionId) - await outputFile(this.#getFilePath(sessionId), message) + await this.#outputFile(this.#getFilePath(sessionId), message) } /** * Cleanup session file by removing it */ async destroy(sessionId: string): Promise { - await remove(this.#getFilePath(sessionId)) + await rm(this.#getFilePath(sessionId), { force: true }) } /** diff --git a/test/file_driver.spec.ts b/test/file_driver.spec.ts index 3027f50..68591b1 100644 --- a/test/file_driver.spec.ts +++ b/test/file_driver.spec.ts @@ -28,6 +28,40 @@ test.group('File driver', () => { ) }) + test('read() should create file when missing', async ({ assert }) => { + const sessionId = '1234' + const session = new FileDriver(config) + const value = await session.read(sessionId) + + assert.isNull(value) + await assert.fileExists('1234.txt') + }) + + test('should create intermediate directories when missing', async ({ assert }) => { + const sessionId = '1234' + const session = new FileDriver({ + driver: 'file', + file: { location: join(fileURLToPath(BASE_URL), 'foo/bar') }, + age: 1000, + clearWithBrowser: false, + cookieName: 'adonis-session', + enabled: true, + cookie: {}, + }) + + const value = await session.read(sessionId) + assert.isNull(value) + await assert.fileExists('foo/bar/1234.txt') + + await session.write(sessionId, { message: 'hello-world' }) + await assert.fileExists('foo/bar/1234.txt') + + await assert.fileEquals( + 'foo/bar/1234.txt', + JSON.stringify({ message: { message: 'hello-world' }, purpose: '1234' }) + ) + }) + test('return null when file is missing', async ({ assert }) => { const sessionId = '1234' const session = new FileDriver(config) @@ -63,6 +97,15 @@ test.group('File driver', () => { await assert.fileNotExists('1234.txt') }) + test('shouldnt file when trying to remove non-existing file', async ({ assert }) => { + const sessionId = '1234' + const session = new FileDriver(config) + + await assert.fileNotExists('1234.txt') + await session.destroy(sessionId) + await assert.fileNotExists('1234.txt') + }) + test('update session expiry', async ({ assert, fs }) => { const sessionId = '1234'