store-s3-aws 1.3.13 → 1.3.15
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/package.json +1 -1
- package/src/AwsS3Store.js +169 -167
package/package.json
CHANGED
package/src/AwsS3Store.js
CHANGED
|
@@ -1,167 +1,169 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const { createReadStream } = require('fs');
|
|
4
|
-
const { S3Client, HeadObjectCommand, GetObjectCommand, DeleteObjectCommand, CopyObjectCommand } = require('@aws-sdk/client-s3');
|
|
5
|
-
const { Upload } = require('@aws-sdk/lib-storage');
|
|
6
|
-
|
|
7
|
-
class AwsS3Store {
|
|
8
|
-
|
|
9
|
-
#clientS3 = null;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* A class for storing authentication data of Whatsapp-web.js to AWS S3.
|
|
13
|
-
* @example
|
|
14
|
-
* For example usage see `example/index.js`.
|
|
15
|
-
* @param {Object} options Specifies the params pattern.
|
|
16
|
-
* @param {String} options.bucketName Specifies the S3 bucket name.
|
|
17
|
-
* @param {String} options.remoteDataPath Specifies the remote path to save authentication files.
|
|
18
|
-
*/
|
|
19
|
-
constructor({ bucketName, remoteDataPath, clientConfig} = {}) {
|
|
20
|
-
if (!bucketName) throw new Error("A valid bucket name is required for AwsS3Store.");
|
|
21
|
-
if (!remoteDataPath) throw new Error("A valid remote dir path is required for AwsS3Store.");
|
|
22
|
-
this.bucketName = bucketName;
|
|
23
|
-
this.remoteDataPath = remoteDataPath;
|
|
24
|
-
this.clientConfig = clientConfig
|
|
25
|
-
this.debugEnabled = process.env.STORE_DEBUG === 'true';
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async sessionExists(options) {
|
|
29
|
-
this.debugLog('[METHOD: sessionExists] Triggered.');
|
|
30
|
-
|
|
31
|
-
const remoteFilePath = path.join(this.remoteDataPath, `${options.session}.zip`).replace(/\\/g, '/');
|
|
32
|
-
const params = {
|
|
33
|
-
Bucket: this.bucketName,
|
|
34
|
-
Key: remoteFilePath
|
|
35
|
-
};
|
|
36
|
-
try {
|
|
37
|
-
await this.#getClientS3().send(new HeadObjectCommand(params));
|
|
38
|
-
this.debugLog(`[METHOD: sessionExists] File found. PATH='${remoteFilePath}'.`);
|
|
39
|
-
return true;
|
|
40
|
-
} catch (err) {
|
|
41
|
-
if (err.name === 'NoSuchKey' || err.name === 'NotFound') {
|
|
42
|
-
this.debugLog(`[METHOD: sessionExists] File not found. PATH='${remoteFilePath}'.`);
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
this.debugLog(`[METHOD: sessionExists] Error: ${err.message}`);
|
|
46
|
-
// throw err;
|
|
47
|
-
return
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async save(options) {
|
|
52
|
-
this.debugLog('[METHOD: save] Triggered.');
|
|
53
|
-
this.debugLog(`Options: ${JSON.stringify(options, null, 2)}`);
|
|
54
|
-
|
|
55
|
-
const fileName = path.basename(options.session)
|
|
56
|
-
const remoteFilePath = path.join(this.remoteDataPath, `${fileName}.zip`).replace(/\\/g, '/');
|
|
57
|
-
options.remoteFilePath = remoteFilePath;
|
|
58
|
-
//await this.#deletePrevious(options);
|
|
59
|
-
|
|
60
|
-
const fileStream = createReadStream(
|
|
61
|
-
|
|
62
|
-
const upload = new Upload({
|
|
63
|
-
client: this.#getClientS3(),
|
|
64
|
-
params: {
|
|
65
|
-
Bucket: this.bucketName,
|
|
66
|
-
Key: remoteFilePath,
|
|
67
|
-
Body: fileStream,
|
|
68
|
-
},
|
|
69
|
-
leavePartsOnError: false,
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
await upload.done();
|
|
73
|
-
|
|
74
|
-
this.debugLog(`[METHOD: save] File saved. PATH='${remoteFilePath}'.`);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async extract(options) {
|
|
78
|
-
this.debugLog('[METHOD: extract] Triggered.');
|
|
79
|
-
|
|
80
|
-
this.debugLog(`Options: ${JSON.stringify(options, null, 2)}`);
|
|
81
|
-
|
|
82
|
-
const remoteFilePath = path.join(this.remoteDataPath, `${options.session}.zip`).replace(/\\/g, '/');
|
|
83
|
-
const params = {
|
|
84
|
-
Bucket: this.bucketName,
|
|
85
|
-
Key: remoteFilePath
|
|
86
|
-
};
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
this.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
await this.#getClientS3().send(new
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const { createReadStream } = require('fs');
|
|
4
|
+
const { S3Client, HeadObjectCommand, GetObjectCommand, DeleteObjectCommand, CopyObjectCommand } = require('@aws-sdk/client-s3');
|
|
5
|
+
const { Upload } = require('@aws-sdk/lib-storage');
|
|
6
|
+
|
|
7
|
+
class AwsS3Store {
|
|
8
|
+
|
|
9
|
+
#clientS3 = null;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A class for storing authentication data of Whatsapp-web.js to AWS S3.
|
|
13
|
+
* @example
|
|
14
|
+
* For example usage see `example/index.js`.
|
|
15
|
+
* @param {Object} options Specifies the params pattern.
|
|
16
|
+
* @param {String} options.bucketName Specifies the S3 bucket name.
|
|
17
|
+
* @param {String} options.remoteDataPath Specifies the remote path to save authentication files.
|
|
18
|
+
*/
|
|
19
|
+
constructor({ bucketName, remoteDataPath, clientConfig} = {}) {
|
|
20
|
+
if (!bucketName) throw new Error("A valid bucket name is required for AwsS3Store.");
|
|
21
|
+
if (!remoteDataPath) throw new Error("A valid remote dir path is required for AwsS3Store.");
|
|
22
|
+
this.bucketName = bucketName;
|
|
23
|
+
this.remoteDataPath = remoteDataPath;
|
|
24
|
+
this.clientConfig = clientConfig
|
|
25
|
+
this.debugEnabled = process.env.STORE_DEBUG === 'true';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async sessionExists(options) {
|
|
29
|
+
this.debugLog('[METHOD: sessionExists] Triggered.');
|
|
30
|
+
|
|
31
|
+
const remoteFilePath = path.join(this.remoteDataPath, `${options.session}.zip`).replace(/\\/g, '/');
|
|
32
|
+
const params = {
|
|
33
|
+
Bucket: this.bucketName,
|
|
34
|
+
Key: remoteFilePath
|
|
35
|
+
};
|
|
36
|
+
try {
|
|
37
|
+
await this.#getClientS3().send(new HeadObjectCommand(params));
|
|
38
|
+
this.debugLog(`[METHOD: sessionExists] File found. PATH='${remoteFilePath}'.`);
|
|
39
|
+
return true;
|
|
40
|
+
} catch (err) {
|
|
41
|
+
if (err.name === 'NoSuchKey' || err.name === 'NotFound') {
|
|
42
|
+
this.debugLog(`[METHOD: sessionExists] File not found. PATH='${remoteFilePath}'.`);
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
this.debugLog(`[METHOD: sessionExists] Error: ${err.message}`);
|
|
46
|
+
// throw err;
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async save(options) {
|
|
52
|
+
this.debugLog('[METHOD: save] Triggered.');
|
|
53
|
+
this.debugLog(`Options: ${JSON.stringify(options, null, 2)}`);
|
|
54
|
+
|
|
55
|
+
const fileName = path.basename(options.session)
|
|
56
|
+
const remoteFilePath = path.join(this.remoteDataPath, `${fileName}.zip`).replace(/\\/g, '/');
|
|
57
|
+
options.remoteFilePath = remoteFilePath;
|
|
58
|
+
//await this.#deletePrevious(options);
|
|
59
|
+
|
|
60
|
+
const fileStream = createReadStream(`./.wwebjs_auth/${options.session}.zip`);
|
|
61
|
+
|
|
62
|
+
const upload = new Upload({
|
|
63
|
+
client: this.#getClientS3(),
|
|
64
|
+
params: {
|
|
65
|
+
Bucket: this.bucketName,
|
|
66
|
+
Key: remoteFilePath,
|
|
67
|
+
Body: fileStream,
|
|
68
|
+
},
|
|
69
|
+
leavePartsOnError: false,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await upload.done();
|
|
73
|
+
|
|
74
|
+
this.debugLog(`[METHOD: save] File saved. PATH='${remoteFilePath}'.`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async extract(options) {
|
|
78
|
+
this.debugLog('[METHOD: extract] Triggered.');
|
|
79
|
+
|
|
80
|
+
this.debugLog(`Options: ${JSON.stringify(options, null, 2)}`);
|
|
81
|
+
|
|
82
|
+
const remoteFilePath = path.join(this.remoteDataPath, `${options.session}.zip`).replace(/\\/g, '/');
|
|
83
|
+
const params = {
|
|
84
|
+
Bucket: this.bucketName,
|
|
85
|
+
Key: remoteFilePath
|
|
86
|
+
};
|
|
87
|
+
const dir = path.dirname(options.path);
|
|
88
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
89
|
+
const fileStream = fs.createWriteStream(options.path);
|
|
90
|
+
const response = await this.#getClientS3().send(new GetObjectCommand(params));
|
|
91
|
+
await new Promise((resolve, reject) => {
|
|
92
|
+
response.Body.pipe(fileStream)
|
|
93
|
+
.on('error', reject)
|
|
94
|
+
.on('finish', resolve);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
this.debugLog(`[METHOD: extract] File extracted. REMOTE_PATH='${remoteFilePath}', LOCAL_PATH='${options.path}'.`);
|
|
98
|
+
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async delete(options) {
|
|
102
|
+
this.debugLog('[METHOD: delete] Triggered.');
|
|
103
|
+
|
|
104
|
+
const remoteFilePath = path.join(this.remoteDataPath, `${options.session}.zip`).replace(/\\/g, '/');
|
|
105
|
+
const params = {
|
|
106
|
+
Bucket: this.bucketName,
|
|
107
|
+
Key: remoteFilePath
|
|
108
|
+
};
|
|
109
|
+
try {
|
|
110
|
+
await this.#getClientS3().send(new HeadObjectCommand(params));
|
|
111
|
+
await this.#getClientS3().send(new DeleteObjectCommand(params));
|
|
112
|
+
this.debugLog(`[METHOD: delete] File deleted. PATH='${remoteFilePath}'.`);
|
|
113
|
+
} catch (err) {
|
|
114
|
+
if (err.name === 'NoSuchKey' || err.name === 'NotFound') {
|
|
115
|
+
this.debugLog(`[METHOD: delete] File not found. PATH='${remoteFilePath}'.`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
this.debugLog(`[METHOD: delete] Error: ${err.message}`);
|
|
119
|
+
// throw err;
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async #deletePrevious(options) {
|
|
125
|
+
this.debugLog('[METHOD: #deletePrevious] Triggered.');
|
|
126
|
+
|
|
127
|
+
const params = {
|
|
128
|
+
Bucket: this.bucketName,
|
|
129
|
+
Key: options.remoteFilePath
|
|
130
|
+
};
|
|
131
|
+
try {
|
|
132
|
+
await this.#getClientS3().send(new HeadObjectCommand(params));
|
|
133
|
+
const backupKey = `${options.remoteFilePath}.backup`;
|
|
134
|
+
await this.#getClientS3().send(new CopyObjectCommand({
|
|
135
|
+
Bucket: this.bucketName,
|
|
136
|
+
CopySource: `${this.bucketName}/${options.remoteFilePath}`,
|
|
137
|
+
Key: backupKey
|
|
138
|
+
}));
|
|
139
|
+
await this.#getClientS3().send(new DeleteObjectCommand(params));
|
|
140
|
+
this.debugLog(`[METHOD: #deletePrevious] File deleted. PATH='${options.remoteFilePath}'.`);
|
|
141
|
+
} catch (err) {
|
|
142
|
+
if (err.name === 'NoSuchKey' || err.name === 'NotFound') {
|
|
143
|
+
this.debugLog(`[METHOD: #deletePrevious] File not found. PATH='${options.remoteFilePath}'.`);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
this.debugLog(`[METHOD: #deletePrevious] Error: ${err.message}`);
|
|
147
|
+
// throw err;
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
#getClientS3() {
|
|
153
|
+
if (this.#clientS3 === null) {
|
|
154
|
+
this.#clientS3 = new S3Client({
|
|
155
|
+
region: this.clientConfig.region
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
return this.#clientS3;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
debugLog(msg) {
|
|
162
|
+
if (this.debugEnabled) {
|
|
163
|
+
const timestamp = new Date().toISOString();
|
|
164
|
+
console.log(`${timestamp} [STORE_DEBUG] ${msg}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = AwsS3Store;
|