@theshelf/filestore 0.0.1
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/README.md +72 -0
- package/dist/definitions/interfaces.d.ts +10 -0
- package/dist/definitions/interfaces.js +1 -0
- package/dist/errors/FileNotFound.d.ts +4 -0
- package/dist/errors/FileNotFound.js +6 -0
- package/dist/errors/FileSystemError.d.ts +2 -0
- package/dist/errors/FileSystemError.js +2 -0
- package/dist/errors/NotConnected.d.ts +4 -0
- package/dist/errors/NotConnected.js +6 -0
- package/dist/errors/UnknownImplementation.d.ts +4 -0
- package/dist/errors/UnknownImplementation.js +6 -0
- package/dist/implementation.d.ts +3 -0
- package/dist/implementation.js +14 -0
- package/dist/implementations/memory/Memory.d.ts +12 -0
- package/dist/implementations/memory/Memory.js +45 -0
- package/dist/implementations/memory/create.d.ts +2 -0
- package/dist/implementations/memory/create.js +4 -0
- package/dist/implementations/minio/MinioFS.d.ts +14 -0
- package/dist/implementations/minio/MinioFS.js +88 -0
- package/dist/implementations/minio/create.d.ts +2 -0
- package/dist/implementations/minio/create.js +9 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/package.json +24 -0
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
|
|
2
|
+
# File Store | The Shelf
|
|
3
|
+
|
|
4
|
+
The file store package provides a universal interaction layer with an actual file storage solution.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install @theshelf/filestore
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Implementations
|
|
13
|
+
|
|
14
|
+
Currently, there are two implementations:
|
|
15
|
+
|
|
16
|
+
* **Memory** - non-persistent in memory storage (suited for testing).
|
|
17
|
+
* **Minio** - persistent S3 compatible object storage.
|
|
18
|
+
|
|
19
|
+
## Configuration
|
|
20
|
+
|
|
21
|
+
The used implementation needs to be configured in the `.env` file.
|
|
22
|
+
|
|
23
|
+
```env
|
|
24
|
+
FILE_STORE_IMPLEMENTATION="minio" # (memory | minio)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
In case of Minio, additional configuration is required.
|
|
28
|
+
|
|
29
|
+
```env
|
|
30
|
+
MINIO_END_POINT="address"
|
|
31
|
+
MINIO_PORT_NUMBER=9000
|
|
32
|
+
MINIO_USE_SSL=true
|
|
33
|
+
MINIO_ACCESS_KEY="development"
|
|
34
|
+
MINIO_SECRET_KEY="secret"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## How to use
|
|
38
|
+
|
|
39
|
+
An instance of the configured file storage implementation can be imported for performing file operations.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import fileStorage from '@theshelf/filestorage';
|
|
43
|
+
|
|
44
|
+
// Perform operations with the fileStorage instance
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Operations
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import fileStorage from '@theshelf/filestorage';
|
|
51
|
+
|
|
52
|
+
// Open connection
|
|
53
|
+
await fileStorage.connect();
|
|
54
|
+
|
|
55
|
+
// Close connection
|
|
56
|
+
await fileStorage.disconnect();
|
|
57
|
+
|
|
58
|
+
// Check if a file exists
|
|
59
|
+
const exists: boolean = await fileStorage.hasFile('path/to/file.txt');
|
|
60
|
+
|
|
61
|
+
// Write a file to the storage
|
|
62
|
+
const data: Buffer = Buffer.from('Something interesting');
|
|
63
|
+
await fileStorage.writeFile('path/to/file.txt', data);
|
|
64
|
+
|
|
65
|
+
// Read a file from storage
|
|
66
|
+
// Throws FileNotFound if not found
|
|
67
|
+
const data: Buffer = await fileStorage.readFile('path/to/file.txt');
|
|
68
|
+
|
|
69
|
+
// Delete a file from storage
|
|
70
|
+
// Throws FileNotFound if not found
|
|
71
|
+
await fileStorage.deleteFile('path/to/file.txt');
|
|
72
|
+
```
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface FileStore {
|
|
2
|
+
get connected(): boolean;
|
|
3
|
+
connect(): Promise<void>;
|
|
4
|
+
disconnect(): Promise<void>;
|
|
5
|
+
hasFile(path: string): Promise<boolean>;
|
|
6
|
+
writeFile(path: string, data: Buffer): Promise<void>;
|
|
7
|
+
readFile(path: string): Promise<Buffer>;
|
|
8
|
+
deleteFile(path: string): Promise<void>;
|
|
9
|
+
clear(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import UnknownImplementation from './errors/UnknownImplementation';
|
|
2
|
+
import createMemoryFS from './implementations/memory/create';
|
|
3
|
+
import createMinioFS from './implementations/minio/create';
|
|
4
|
+
const implementations = new Map([
|
|
5
|
+
['memory', createMemoryFS],
|
|
6
|
+
['minio', createMinioFS],
|
|
7
|
+
]);
|
|
8
|
+
const DEFAULT_FILE_STORE_IMPLEMENTATION = 'memory';
|
|
9
|
+
const implementationName = process.env.FILE_STORE_IMPLEMENTATION ?? DEFAULT_FILE_STORE_IMPLEMENTATION;
|
|
10
|
+
const creator = implementations.get(implementationName.toLowerCase());
|
|
11
|
+
if (creator === undefined) {
|
|
12
|
+
throw new UnknownImplementation(implementationName);
|
|
13
|
+
}
|
|
14
|
+
export default creator();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FileStore } from '../../definitions/interfaces';
|
|
2
|
+
export default class Memory implements FileStore {
|
|
3
|
+
#private;
|
|
4
|
+
get connected(): boolean;
|
|
5
|
+
connect(): Promise<void>;
|
|
6
|
+
disconnect(): Promise<void>;
|
|
7
|
+
hasFile(path: string): Promise<boolean>;
|
|
8
|
+
writeFile(path: string, data: Buffer): Promise<void>;
|
|
9
|
+
readFile(path: string): Promise<Buffer>;
|
|
10
|
+
deleteFile(path: string): Promise<void>;
|
|
11
|
+
clear(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import FileNotFound from '../../errors/FileNotFound';
|
|
2
|
+
import NotConnected from '../../errors/NotConnected';
|
|
3
|
+
export default class Memory {
|
|
4
|
+
#files = new Map();
|
|
5
|
+
#connected = false;
|
|
6
|
+
get connected() { return this.#connected; }
|
|
7
|
+
async connect() {
|
|
8
|
+
this.#connected = true;
|
|
9
|
+
}
|
|
10
|
+
async disconnect() {
|
|
11
|
+
this.#connected = false;
|
|
12
|
+
}
|
|
13
|
+
async hasFile(path) {
|
|
14
|
+
const files = this.#getFiles();
|
|
15
|
+
return files.has(path);
|
|
16
|
+
}
|
|
17
|
+
async writeFile(path, data) {
|
|
18
|
+
const files = this.#getFiles();
|
|
19
|
+
files.set(path, data);
|
|
20
|
+
}
|
|
21
|
+
async readFile(path) {
|
|
22
|
+
const files = this.#getFiles();
|
|
23
|
+
const data = files.get(path);
|
|
24
|
+
if (data === undefined) {
|
|
25
|
+
throw new FileNotFound(path);
|
|
26
|
+
}
|
|
27
|
+
return data;
|
|
28
|
+
}
|
|
29
|
+
async deleteFile(path) {
|
|
30
|
+
const files = this.#getFiles();
|
|
31
|
+
if (files.has(path) === false) {
|
|
32
|
+
throw new FileNotFound(path);
|
|
33
|
+
}
|
|
34
|
+
files.delete(path);
|
|
35
|
+
}
|
|
36
|
+
#getFiles() {
|
|
37
|
+
if (this.#files === undefined) {
|
|
38
|
+
throw new NotConnected();
|
|
39
|
+
}
|
|
40
|
+
return this.#files;
|
|
41
|
+
}
|
|
42
|
+
async clear() {
|
|
43
|
+
this.#files.clear();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ClientOptions } from 'minio';
|
|
2
|
+
import type { FileStore } from '../../definitions/interfaces';
|
|
3
|
+
export default class MinioFS implements FileStore {
|
|
4
|
+
#private;
|
|
5
|
+
constructor(configuration: ClientOptions);
|
|
6
|
+
get connected(): boolean;
|
|
7
|
+
connect(): Promise<void>;
|
|
8
|
+
disconnect(): Promise<void>;
|
|
9
|
+
hasFile(path: string): Promise<boolean>;
|
|
10
|
+
writeFile(path: string, data: Buffer): Promise<void>;
|
|
11
|
+
readFile(path: string): Promise<Buffer>;
|
|
12
|
+
deleteFile(path: string): Promise<void>;
|
|
13
|
+
clear(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Client } from 'minio';
|
|
2
|
+
import FileNotFound from '../../errors/FileNotFound';
|
|
3
|
+
import NotConnected from '../../errors/NotConnected';
|
|
4
|
+
const BUCKET_NAME = 'comify';
|
|
5
|
+
export default class MinioFS {
|
|
6
|
+
#configuration;
|
|
7
|
+
#client;
|
|
8
|
+
constructor(configuration) {
|
|
9
|
+
this.#configuration = configuration;
|
|
10
|
+
}
|
|
11
|
+
get connected() {
|
|
12
|
+
return this.#client !== undefined;
|
|
13
|
+
}
|
|
14
|
+
async connect() {
|
|
15
|
+
this.#client = new Client(this.#configuration);
|
|
16
|
+
if (await this.#client.bucketExists(BUCKET_NAME) === false) {
|
|
17
|
+
await this.#client.makeBucket(BUCKET_NAME);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async disconnect() {
|
|
21
|
+
if (this.#client === undefined) {
|
|
22
|
+
throw new NotConnected();
|
|
23
|
+
}
|
|
24
|
+
this.#client = undefined;
|
|
25
|
+
}
|
|
26
|
+
async hasFile(path) {
|
|
27
|
+
const client = this.#getClient();
|
|
28
|
+
try {
|
|
29
|
+
await client.statObject(BUCKET_NAME, path);
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
const customError = this.#handleError(error, path);
|
|
34
|
+
if (customError instanceof FileNotFound) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async writeFile(path, data) {
|
|
41
|
+
const client = this.#getClient();
|
|
42
|
+
try {
|
|
43
|
+
await client.putObject(BUCKET_NAME, path, data);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw this.#handleError(error, path);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async readFile(path) {
|
|
50
|
+
const client = this.#getClient();
|
|
51
|
+
try {
|
|
52
|
+
const stream = await client.getObject(BUCKET_NAME, path);
|
|
53
|
+
const chunks = await stream.toArray();
|
|
54
|
+
return Buffer.concat(chunks);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
throw this.#handleError(error, path);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async deleteFile(path) {
|
|
61
|
+
const client = this.#getClient();
|
|
62
|
+
try {
|
|
63
|
+
await client.removeObject(BUCKET_NAME, path);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
throw this.#handleError(error, path);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async clear() {
|
|
70
|
+
return; // Deliberately not implemented
|
|
71
|
+
}
|
|
72
|
+
#getClient() {
|
|
73
|
+
if (this.#client === undefined) {
|
|
74
|
+
throw new NotConnected();
|
|
75
|
+
}
|
|
76
|
+
return this.#client;
|
|
77
|
+
}
|
|
78
|
+
#handleError(error, path) {
|
|
79
|
+
if (error instanceof Error && this.#isNotFoundError(error)) {
|
|
80
|
+
return new FileNotFound(path);
|
|
81
|
+
}
|
|
82
|
+
return error;
|
|
83
|
+
}
|
|
84
|
+
#isNotFoundError(error) {
|
|
85
|
+
return error.message.startsWith('The specified key does not exist')
|
|
86
|
+
|| error.message.startsWith('Not Found');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import MinioFS from './MinioFS';
|
|
2
|
+
export default function create() {
|
|
3
|
+
const endPoint = process.env.MINIO_END_POINT ?? 'undefined';
|
|
4
|
+
const port = Number(process.env.MINIO_PORT_NUMBER ?? 9000);
|
|
5
|
+
const useSSL = process.env.MINIO_USE_SSL === 'true';
|
|
6
|
+
const accessKey = process.env.MINIO_ROOT_USER ?? 'undefined';
|
|
7
|
+
const secretKey = process.env.MINIO_ROOT_PASSWORD ?? 'undefined';
|
|
8
|
+
return new MinioFS({ endPoint, port, useSSL, accessKey, secretKey });
|
|
9
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { FileStore } from './definitions/interfaces';
|
|
2
|
+
export { default as FileNotFound } from './errors/FileNotFound';
|
|
3
|
+
export { default as FileSystemError } from './errors/FileSystemError';
|
|
4
|
+
export { default as NotConnected } from './errors/NotConnected';
|
|
5
|
+
export { default as UnknownImplementation } from './errors/UnknownImplementation';
|
|
6
|
+
export { default } from './implementation';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as FileNotFound } from './errors/FileNotFound';
|
|
2
|
+
export { default as FileSystemError } from './errors/FileSystemError';
|
|
3
|
+
export { default as NotConnected } from './errors/NotConnected';
|
|
4
|
+
export { default as UnknownImplementation } from './errors/UnknownImplementation';
|
|
5
|
+
export { default } from './implementation';
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@theshelf/filestore",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc",
|
|
8
|
+
"clean": "rimraf dist",
|
|
9
|
+
"test": "vitest run",
|
|
10
|
+
"test-coverage": "vitest run --coverage",
|
|
11
|
+
"lint": "eslint",
|
|
12
|
+
"review": "npm run build && npm run lint && npm run test",
|
|
13
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"README.md",
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"types": "dist/index.d.ts",
|
|
20
|
+
"exports": "./dist/index.js",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"minio": "8.0.6"
|
|
23
|
+
}
|
|
24
|
+
}
|