@enfyra/cloud-backup-storage 0.1.0
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 +27 -0
- package/index.js +119 -0
- package/package.json +21 -0
package/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# @enfyra/cloud-backup-storage
|
|
2
|
+
|
|
3
|
+
Small S3-compatible helpers for Enfyra dynamic handlers that need to stream Cloud backup objects.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
const backupStorage = @PKGS["@enfyra/cloud-backup-storage"];
|
|
9
|
+
|
|
10
|
+
const stream = await backupStorage.createReadStream(
|
|
11
|
+
{
|
|
12
|
+
type: "Cloudflare R2",
|
|
13
|
+
accountId: settings.backup_storage_config.accountId,
|
|
14
|
+
bucket: settings.backup_storage_config.bucket,
|
|
15
|
+
accessKeyId: settings.backup_storage_config.accessKeyId,
|
|
16
|
+
secretAccessKey: settings.backup_storage_config.secretAccessKey,
|
|
17
|
+
},
|
|
18
|
+
backup.object_key,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return @RES.stream(stream, {
|
|
22
|
+
mimetype: backup.content_type || "application/gzip",
|
|
23
|
+
filename: `project-${project.id}-backup-${backup.id}.sql.gz`,
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`createReadStream()` returns a Node readable stream with `.pipe()`. It does not buffer the object and does not know about Enfyra routes, users, projects, or backup records.
|
package/index.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DeleteObjectCommand,
|
|
3
|
+
GetObjectCommand,
|
|
4
|
+
HeadObjectCommand,
|
|
5
|
+
S3Client,
|
|
6
|
+
} from "@aws-sdk/client-s3";
|
|
7
|
+
|
|
8
|
+
function normalizeConfig(config = {}) {
|
|
9
|
+
const provider = String(config.provider || config.type || "s3").toLowerCase();
|
|
10
|
+
const bucket = config.bucket || config.bucketName;
|
|
11
|
+
const accountId = config.accountId || config.cloudflareAccountId;
|
|
12
|
+
const accessKeyId = config.accessKeyId;
|
|
13
|
+
const secretAccessKey = config.secretAccessKey;
|
|
14
|
+
const region = config.region || (provider.includes("r2") ? "auto" : undefined);
|
|
15
|
+
const endpoint =
|
|
16
|
+
config.endpoint ||
|
|
17
|
+
(accountId ? `https://${accountId}.r2.cloudflarestorage.com` : undefined);
|
|
18
|
+
|
|
19
|
+
if (!bucket) throw new Error("bucket is required");
|
|
20
|
+
if (!accessKeyId) throw new Error("accessKeyId is required");
|
|
21
|
+
if (!secretAccessKey) throw new Error("secretAccessKey is required");
|
|
22
|
+
if (!region) throw new Error("region is required for S3-compatible storage");
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
bucket,
|
|
26
|
+
region,
|
|
27
|
+
endpoint,
|
|
28
|
+
forcePathStyle:
|
|
29
|
+
typeof config.forcePathStyle === "boolean"
|
|
30
|
+
? config.forcePathStyle
|
|
31
|
+
: Boolean(endpoint && !provider.includes("r2")),
|
|
32
|
+
credentials: {
|
|
33
|
+
accessKeyId,
|
|
34
|
+
secretAccessKey,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function createClient(config) {
|
|
40
|
+
const normalized = normalizeConfig(config);
|
|
41
|
+
return new S3Client({
|
|
42
|
+
region: normalized.region,
|
|
43
|
+
endpoint: normalized.endpoint,
|
|
44
|
+
forcePathStyle: normalized.forcePathStyle,
|
|
45
|
+
credentials: normalized.credentials,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function createReadStream(config, key, options = {}) {
|
|
50
|
+
if (!key) throw new Error("key is required");
|
|
51
|
+
const normalized = normalizeConfig(config);
|
|
52
|
+
const client = createClient(config);
|
|
53
|
+
const response = await client.send(
|
|
54
|
+
new GetObjectCommand({
|
|
55
|
+
Bucket: normalized.bucket,
|
|
56
|
+
Key: key,
|
|
57
|
+
Range: options.range,
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
if (!response.Body || typeof response.Body.pipe !== "function") {
|
|
62
|
+
throw new Error(`Object stream is not available for key: ${key}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
response.Body.contentLength = response.ContentLength;
|
|
66
|
+
response.Body.contentType = response.ContentType;
|
|
67
|
+
response.Body.etag = response.ETag;
|
|
68
|
+
response.Body.lastModified = response.LastModified;
|
|
69
|
+
const destroyClient = () => client.destroy();
|
|
70
|
+
response.Body.once("end", destroyClient);
|
|
71
|
+
response.Body.once("close", destroyClient);
|
|
72
|
+
response.Body.once("error", destroyClient);
|
|
73
|
+
return response.Body;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function headObject(config, key) {
|
|
77
|
+
if (!key) throw new Error("key is required");
|
|
78
|
+
const normalized = normalizeConfig(config);
|
|
79
|
+
const client = createClient(config);
|
|
80
|
+
try {
|
|
81
|
+
const response = await client.send(
|
|
82
|
+
new HeadObjectCommand({
|
|
83
|
+
Bucket: normalized.bucket,
|
|
84
|
+
Key: key,
|
|
85
|
+
}),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
contentLength: response.ContentLength,
|
|
90
|
+
contentType: response.ContentType,
|
|
91
|
+
etag: response.ETag,
|
|
92
|
+
lastModified: response.LastModified,
|
|
93
|
+
};
|
|
94
|
+
} finally {
|
|
95
|
+
client.destroy();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function deleteObject(config, key) {
|
|
100
|
+
if (!key) throw new Error("key is required");
|
|
101
|
+
const normalized = normalizeConfig(config);
|
|
102
|
+
const client = createClient(config);
|
|
103
|
+
try {
|
|
104
|
+
await client.send(
|
|
105
|
+
new DeleteObjectCommand({
|
|
106
|
+
Bucket: normalized.bucket,
|
|
107
|
+
Key: key,
|
|
108
|
+
}),
|
|
109
|
+
);
|
|
110
|
+
} finally {
|
|
111
|
+
client.destroy();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export default {
|
|
116
|
+
createReadStream,
|
|
117
|
+
deleteObject,
|
|
118
|
+
headObject,
|
|
119
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@enfyra/cloud-backup-storage",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Small S3-compatible stream helpers for Enfyra Cloud backup handlers",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"index.js",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@aws-sdk/client-s3": "^3.1024.0"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
}
|
|
21
|
+
}
|