@alepha/bucket-azure 0.9.3 → 0.9.4
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/dist/index.cjs +13 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -6
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +4 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -11
- package/dist/index.js.map +1 -1
- package/package.json +11 -8
- package/src/providers/AzureFileStorageProvider.ts +18 -15
package/dist/index.cjs
CHANGED
|
@@ -27,6 +27,7 @@ const node_crypto = __toESM(require("node:crypto"));
|
|
|
27
27
|
const node_stream = __toESM(require("node:stream"));
|
|
28
28
|
const __alepha_datetime = __toESM(require("@alepha/datetime"));
|
|
29
29
|
const __alepha_file = __toESM(require("@alepha/file"));
|
|
30
|
+
const __alepha_logger = __toESM(require("@alepha/logger"));
|
|
30
31
|
const __azure_storage_blob = __toESM(require("@azure/storage-blob"));
|
|
31
32
|
|
|
32
33
|
//#region src/providers/AzureFileStorageProvider.ts
|
|
@@ -35,7 +36,7 @@ const envSchema = __alepha_core.t.object({ AZ_STORAGE_CONNECTION_STRING: __aleph
|
|
|
35
36
|
* Azure Blog Storage implementation of File Storage Provider.
|
|
36
37
|
*/
|
|
37
38
|
var AzureFileStorageProvider = class {
|
|
38
|
-
log = (0,
|
|
39
|
+
log = (0, __alepha_logger.$logger)();
|
|
39
40
|
env = (0, __alepha_core.$env)(envSchema);
|
|
40
41
|
alepha = (0, __alepha_core.$inject)(__alepha_core.Alepha);
|
|
41
42
|
time = (0, __alepha_core.$inject)(__alepha_datetime.DateTimeProvider);
|
|
@@ -50,18 +51,15 @@ var AzureFileStorageProvider = class {
|
|
|
50
51
|
handler: async () => {
|
|
51
52
|
for (const bucket of this.alepha.descriptors(__alepha_bucket.$bucket)) {
|
|
52
53
|
if (bucket.provider !== this) continue;
|
|
53
|
-
const containerName = bucket.name
|
|
54
|
-
this.log.debug(`Prepare container ${containerName}...`);
|
|
54
|
+
const containerName = this.convertName(bucket.name);
|
|
55
|
+
this.log.debug(`Prepare container '${containerName}' ...`);
|
|
55
56
|
if (!this.containers[containerName]) this.containers[containerName] = await this.createContainerClient(containerName);
|
|
56
|
-
this.log.info(`Container ${bucket} OK`);
|
|
57
|
+
this.log.info(`Container '${bucket.name}' OK`);
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
});
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const container = await this.createContainerClient(containerName);
|
|
63
|
-
this.containers[containerName] = container;
|
|
64
|
-
return container;
|
|
61
|
+
convertName(name) {
|
|
62
|
+
return name.replaceAll("/", "-").toLowerCase();
|
|
65
63
|
}
|
|
66
64
|
async upload(bucketName, file, fileId) {
|
|
67
65
|
fileId ??= this.createId();
|
|
@@ -85,6 +83,7 @@ var AzureFileStorageProvider = class {
|
|
|
85
83
|
return fileId;
|
|
86
84
|
}
|
|
87
85
|
async download(bucketName, fileId) {
|
|
86
|
+
this.log.trace(`Downloading file '${fileId}' from bucket '${bucketName}'...`);
|
|
88
87
|
const block = this.getBlock(bucketName, fileId);
|
|
89
88
|
const blob = await block.download().catch((error) => {
|
|
90
89
|
if (error instanceof Error) throw new __alepha_bucket.FileNotFoundError("Error downloading file", { cause: error });
|
|
@@ -97,9 +96,11 @@ var AzureFileStorageProvider = class {
|
|
|
97
96
|
});
|
|
98
97
|
}
|
|
99
98
|
async exists(bucketName, fileId) {
|
|
99
|
+
this.log.trace(`Checking existence of file '${fileId}' in bucket '${bucketName}'...`);
|
|
100
100
|
return await this.getBlock(bucketName, fileId).exists();
|
|
101
101
|
}
|
|
102
102
|
async delete(bucketName, fileId) {
|
|
103
|
+
this.log.trace(`Deleting file '${fileId}' from bucket '${bucketName}'...`);
|
|
103
104
|
try {
|
|
104
105
|
await this.getBlock(bucketName, fileId).delete();
|
|
105
106
|
} catch (error) {
|
|
@@ -108,8 +109,9 @@ var AzureFileStorageProvider = class {
|
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
getBlock(container, fileId) {
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
const containerName = this.convertName(container);
|
|
113
|
+
if (!this.containers[containerName]) throw new __alepha_bucket.FileNotFoundError(`File '${fileId}' not found - container '${container}' does not exists`);
|
|
114
|
+
return this.containers[containerName].getBlockBlobClient(fileId);
|
|
113
115
|
}
|
|
114
116
|
async createContainerClient(name) {
|
|
115
117
|
const container = this.blobServiceClient.getContainerClient(name);
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["Alepha","DateTimeProvider","$bucket","
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["t","Alepha","DateTimeProvider","BlobServiceClient","$bucket","name: string","bucketName: string","file: FileLike","fileId?: string","Readable","fileId: string","FileNotFoundError","container: string","FileStorageProvider","AlephaBucket"],"sources":["../src/providers/AzureFileStorageProvider.ts","../src/index.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { Readable } from \"node:stream\";\nimport {\n\t$bucket,\n\tFileNotFoundError,\n\ttype FileStorageProvider,\n} from \"@alepha/bucket\";\nimport {\n\t$env,\n\t$hook,\n\t$inject,\n\tAlepha,\n\ttype FileLike,\n\ttype Static,\n\tt,\n} from \"@alepha/core\";\nimport { DateTimeProvider } from \"@alepha/datetime\";\nimport { createFile } from \"@alepha/file\";\nimport { $logger } from \"@alepha/logger\";\nimport {\n\tBlobServiceClient,\n\ttype BlockBlobClient,\n\ttype ContainerClient,\n\ttype StoragePipelineOptions,\n} from \"@azure/storage-blob\";\n\nconst envSchema = t.object({\n\tAZ_STORAGE_CONNECTION_STRING: t.string({\n\t\tsize: \"long\",\n\t}),\n});\n\ndeclare module \"@alepha/core\" {\n\tinterface Env extends Partial<Static<typeof envSchema>> {}\n}\n\n/**\n * Azure Blog Storage implementation of File Storage Provider.\n */\nexport class AzureFileStorageProvider implements FileStorageProvider {\n\tprotected readonly log = $logger();\n\tprotected readonly env = $env(envSchema);\n\tprotected readonly alepha = $inject(Alepha);\n\tprotected readonly time = $inject(DateTimeProvider);\n\tprotected readonly containers: Record<string, ContainerClient> = {};\n\tprotected readonly blobServiceClient: BlobServiceClient;\n\n\tpublic readonly options: StoragePipelineOptions = {};\n\n\tconstructor() {\n\t\tthis.blobServiceClient = BlobServiceClient.fromConnectionString(\n\t\t\tthis.env.AZ_STORAGE_CONNECTION_STRING,\n\t\t\tthis.options,\n\t\t);\n\t}\n\n\tprotected readonly onStart = $hook({\n\t\ton: \"start\",\n\t\thandler: async () => {\n\t\t\tfor (const bucket of this.alepha.descriptors($bucket)) {\n\t\t\t\tif (bucket.provider !== this) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst containerName = this.convertName(bucket.name);\n\t\t\t\tthis.log.debug(`Prepare container '${containerName}' ...`);\n\n\t\t\t\tif (!this.containers[containerName]) {\n\t\t\t\t\tthis.containers[containerName] =\n\t\t\t\t\t\tawait this.createContainerClient(containerName);\n\t\t\t\t}\n\n\t\t\t\tthis.log.info(`Container '${bucket.name}' OK`);\n\t\t\t}\n\t\t},\n\t});\n\n\tpublic convertName(name: string): string {\n\t\t// Azure Blob Storage does not allow uppercase letters in container names\n\t\treturn name.replaceAll(\"/\", \"-\").toLowerCase();\n\t}\n\n\tpublic async upload(\n\t\tbucketName: string,\n\t\tfile: FileLike,\n\t\tfileId?: string,\n\t): Promise<string> {\n\t\tfileId ??= this.createId();\n\t\tconst block = this.getBlock(bucketName, fileId);\n\n\t\tconst metadata = {\n\t\t\tname: file.name,\n\t\t\ttype: file.type,\n\t\t};\n\n\t\tif (file.filepath) {\n\t\t\tawait block.uploadFile(file.filepath, {\n\t\t\t\tmetadata,\n\t\t\t\tblobHTTPHeaders: {\n\t\t\t\t\tblobContentType: file.type,\n\t\t\t\t},\n\t\t\t});\n\t\t} else if (file.size > 0) {\n\t\t\tawait block.uploadData(await file.arrayBuffer(), {\n\t\t\t\tmetadata,\n\t\t\t\tblobHTTPHeaders: {\n\t\t\t\t\tblobContentType: file.type,\n\t\t\t\t},\n\t\t\t});\n\t\t} else {\n\t\t\tawait block.uploadStream(\n\t\t\t\tReadable.from(file.stream()),\n\t\t\t\tfile.size || undefined,\n\t\t\t\t5,\n\t\t\t\t{\n\t\t\t\t\tmetadata,\n\t\t\t\t\tblobHTTPHeaders: {\n\t\t\t\t\t\tblobContentType: file.type,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\treturn fileId;\n\t}\n\n\tpublic async download(bucketName: string, fileId: string): Promise<FileLike> {\n\t\tthis.log.trace(\n\t\t\t`Downloading file '${fileId}' from bucket '${bucketName}'...`,\n\t\t);\n\t\tconst block = this.getBlock(bucketName, fileId);\n\n\t\tconst blob = await block.download().catch((error) => {\n\t\t\tif (error instanceof Error) {\n\t\t\t\tthrow new FileNotFoundError(\"Error downloading file\", { cause: error });\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t});\n\n\t\tif (!blob.readableStreamBody) {\n\t\t\tthrow new FileNotFoundError(\"File not found - empty stream body\");\n\t\t}\n\n\t\treturn createFile(blob.readableStreamBody, {\n\t\t\t...blob.metadata,\n\t\t\tsize: blob.contentLength,\n\t\t});\n\t}\n\n\tpublic async exists(bucketName: string, fileId: string): Promise<boolean> {\n\t\tthis.log.trace(\n\t\t\t`Checking existence of file '${fileId}' in bucket '${bucketName}'...`,\n\t\t);\n\t\treturn await this.getBlock(bucketName, fileId).exists();\n\t}\n\n\tpublic async delete(bucketName: string, fileId: string): Promise<void> {\n\t\tthis.log.trace(`Deleting file '${fileId}' from bucket '${bucketName}'...`);\n\t\ttry {\n\t\t\tawait this.getBlock(bucketName, fileId).delete();\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) {\n\t\t\t\tthrow new FileNotFoundError(\"Error deleting file\", { cause: error });\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tpublic getBlock(container: string, fileId: string): BlockBlobClient {\n\t\tconst containerName = this.convertName(container);\n\n\t\tif (!this.containers[containerName]) {\n\t\t\tthrow new FileNotFoundError(\n\t\t\t\t`File '${fileId}' not found - container '${container}' does not exists`,\n\t\t\t);\n\t\t}\n\n\t\treturn this.containers[containerName].getBlockBlobClient(fileId);\n\t}\n\n\tprotected async createContainerClient(\n\t\tname: string,\n\t): Promise<ContainerClient> {\n\t\tconst container = this.blobServiceClient.getContainerClient(name);\n\n\t\tawait this.time.deadline(\n\t\t\t(abortSignal) => container.createIfNotExists({ abortSignal }),\n\t\t\t[5, \"seconds\"],\n\t\t);\n\n\t\treturn container;\n\t}\n\n\tprotected createId(): string {\n\t\treturn randomUUID();\n\t}\n}\n","import { AlephaBucket, FileStorageProvider } from \"@alepha/bucket\";\nimport { $module } from \"@alepha/core\";\nimport { AzureFileStorageProvider } from \"./providers/AzureFileStorageProvider.ts\";\n\nexport * from \"./providers/AzureFileStorageProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for Alepha Bucket that provides Azure Blob Storage capabilities.\n *\n * @see {@link AzureFileStorageProvider}\n * @module alepha.bucket.azure\n */\nexport const AlephaBucketAzure = $module({\n\tname: \"alepha.bucket.azure\",\n\tservices: [AzureFileStorageProvider],\n\tregister: (alepha) =>\n\t\talepha\n\t\t\t.with({\n\t\t\t\toptional: true,\n\t\t\t\tprovide: FileStorageProvider,\n\t\t\t\tuse: AzureFileStorageProvider,\n\t\t\t})\n\t\t\t.with(AlephaBucket),\n});\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as _alepha_core1 from "@alepha/core";
|
|
2
|
-
import * as _alepha_core0 from "@alepha/core";
|
|
3
2
|
import { Alepha, FileLike, Static } from "@alepha/core";
|
|
3
|
+
import * as _alepha_logger0 from "@alepha/logger";
|
|
4
4
|
import { FileStorageProvider } from "@alepha/bucket";
|
|
5
5
|
import { DateTimeProvider } from "@alepha/datetime";
|
|
6
6
|
import { BlobServiceClient, BlockBlobClient, ContainerClient, StoragePipelineOptions } from "@azure/storage-blob";
|
|
@@ -16,7 +16,7 @@ declare module "@alepha/core" {
|
|
|
16
16
|
* Azure Blog Storage implementation of File Storage Provider.
|
|
17
17
|
*/
|
|
18
18
|
declare class AzureFileStorageProvider implements FileStorageProvider {
|
|
19
|
-
protected readonly log:
|
|
19
|
+
protected readonly log: _alepha_logger0.Logger;
|
|
20
20
|
protected readonly env: {
|
|
21
21
|
AZ_STORAGE_CONNECTION_STRING: string;
|
|
22
22
|
};
|
|
@@ -27,7 +27,7 @@ declare class AzureFileStorageProvider implements FileStorageProvider {
|
|
|
27
27
|
readonly options: StoragePipelineOptions;
|
|
28
28
|
constructor();
|
|
29
29
|
protected readonly onStart: _alepha_core1.HookDescriptor<"start">;
|
|
30
|
-
|
|
30
|
+
convertName(name: string): string;
|
|
31
31
|
upload(bucketName: string, file: FileLike, fileId?: string): Promise<string>;
|
|
32
32
|
download(bucketName: string, fileId: string): Promise<FileLike>;
|
|
33
33
|
exists(bucketName: string, fileId: string): Promise<boolean>;
|
|
@@ -44,9 +44,7 @@ declare class AzureFileStorageProvider implements FileStorageProvider {
|
|
|
44
44
|
* @see {@link AzureFileStorageProvider}
|
|
45
45
|
* @module alepha.bucket.azure
|
|
46
46
|
*/
|
|
47
|
-
declare const AlephaBucketAzure:
|
|
48
|
-
//# sourceMappingURL=index.d.ts.map
|
|
49
|
-
|
|
47
|
+
declare const AlephaBucketAzure: _alepha_core1.Service<_alepha_core1.Module>;
|
|
50
48
|
//#endregion
|
|
51
49
|
export { AlephaBucketAzure, AzureFileStorageProvider };
|
|
52
50
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/providers/AzureFileStorageProvider.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;cA0BM,yBAAS;gCAIb,aAAA,CAAA;;;EAJI,UAAA,GAIJ,SAGqB,OAHrB,CAG6B,MAH7B,CAAA,OAG2C,SAH3C,CAAA,CAAA,CAAA,CAAA;;;AAJa;AAAA;AAAA,cAaF,wBAAA,YAAoC,mBAblC,CAAA;EAAA,mBAO8B,GAAA,EAMP,
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/providers/AzureFileStorageProvider.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;cA0BM,yBAAS;gCAIb,aAAA,CAAA;;;EAJI,UAAA,GAIJ,SAGqB,OAHrB,CAG6B,MAH7B,CAAA,OAG2C,SAH3C,CAAA,CAAA,CAAA,CAAA;;;AAJa;AAAA;AAAA,cAaF,wBAAA,YAAoC,mBAblC,CAAA;EAAA,mBAO8B,GAAA,EAMP,eAAA,CACf,MAPsB;EAAS,mBAAvB,GAAA,EAAA;IAAR,4BAAA,EAAA,MAAA;EAAO,CAAA;EAAA,mBAAA,MAAA,EASJ,MATI;EAMjB,mBAAA,IAAA,EAIW,gBAJc;EAAA,mBAAA,UAAA,EAKN,MALM,CAAA,MAAA,EAKS,eALT,CAAA;EAAA,mBACf,iBAAA,EAKgB,iBALhB;EAAA,SAEG,OAAA,EAKA,sBALA;EAAA,WACF,CAAA;EAAA,mBACuB,OAAA,EAGC,aAAA,CASrB,cAZoB,CAAA,OAAA,CAAA;EAAe,WAA9B,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA;EAAM,MACC,CAAA,UAAA,EAAA,MAAA,EAAA,IAAA,EAuC/B,QAvC+B,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EAyCnC,OAzCmC,CAAA,MAAA,CAAA;EAAiB,QAE9B,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EA+EkC,OA/ElC,CA+E0C,QA/E1C,CAAA;EAAsB,MAAA,CAAA,UASrB,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EA8F+B,OA9F/B,CAAA,OAAA,CAAA;EAAA,MA4BnB,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAyEkD,OAzElD,CAAA,IAAA,CAAA;EAAQ,QAEZ,CAAA,SAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAmFiD,eAnFjD;EAAO,UAwCyD,qBAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EAyDhE,OAzDgE,CAyDxD,eAzDwD,CAAA;EAAQ,UAAhB,QAAA,CAAA,CAAA,EAAA,MAAA;;;;;;;;;;cChH/C,mBAAiB,aAAA,CAAA,QAW5B,aAAA,CAX4B,MAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { FileStorageProvider } from "@alepha/bucket";
|
|
2
2
|
import * as _alepha_core1 from "@alepha/core";
|
|
3
|
-
import * as _alepha_core0 from "@alepha/core";
|
|
4
3
|
import { Alepha, FileLike, Static } from "@alepha/core";
|
|
5
4
|
import { DateTimeProvider } from "@alepha/datetime";
|
|
5
|
+
import * as _alepha_logger0 from "@alepha/logger";
|
|
6
6
|
import { BlobServiceClient, BlockBlobClient, ContainerClient, StoragePipelineOptions } from "@azure/storage-blob";
|
|
7
7
|
|
|
8
8
|
//#region src/providers/AzureFileStorageProvider.d.ts
|
|
@@ -16,7 +16,7 @@ declare module "@alepha/core" {
|
|
|
16
16
|
* Azure Blog Storage implementation of File Storage Provider.
|
|
17
17
|
*/
|
|
18
18
|
declare class AzureFileStorageProvider implements FileStorageProvider {
|
|
19
|
-
protected readonly log:
|
|
19
|
+
protected readonly log: _alepha_logger0.Logger;
|
|
20
20
|
protected readonly env: {
|
|
21
21
|
AZ_STORAGE_CONNECTION_STRING: string;
|
|
22
22
|
};
|
|
@@ -27,7 +27,7 @@ declare class AzureFileStorageProvider implements FileStorageProvider {
|
|
|
27
27
|
readonly options: StoragePipelineOptions;
|
|
28
28
|
constructor();
|
|
29
29
|
protected readonly onStart: _alepha_core1.HookDescriptor<"start">;
|
|
30
|
-
|
|
30
|
+
convertName(name: string): string;
|
|
31
31
|
upload(bucketName: string, file: FileLike, fileId?: string): Promise<string>;
|
|
32
32
|
download(bucketName: string, fileId: string): Promise<FileLike>;
|
|
33
33
|
exists(bucketName: string, fileId: string): Promise<boolean>;
|
|
@@ -44,9 +44,7 @@ declare class AzureFileStorageProvider implements FileStorageProvider {
|
|
|
44
44
|
* @see {@link AzureFileStorageProvider}
|
|
45
45
|
* @module alepha.bucket.azure
|
|
46
46
|
*/
|
|
47
|
-
declare const AlephaBucketAzure:
|
|
48
|
-
//# sourceMappingURL=index.d.ts.map
|
|
49
|
-
|
|
47
|
+
declare const AlephaBucketAzure: _alepha_core1.Service<_alepha_core1.Module>;
|
|
50
48
|
//#endregion
|
|
51
49
|
export { AlephaBucketAzure, AzureFileStorageProvider };
|
|
52
50
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/providers/AzureFileStorageProvider.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;cA0BM,yBAAS;gCAIb,aAAA,CAAA;;;EAJI,UAAA,GAIJ,SAGqB,OAHrB,CAG6B,MAH7B,CAAA,OAG2C,SAH3C,CAAA,CAAA,CAAA,CAAA;;;AAJa;AAAA;AAAA,cAaF,wBAAA,YAAoC,mBAblC,CAAA;EAAA,mBAO8B,GAAA,EAMP,
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/providers/AzureFileStorageProvider.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;cA0BM,yBAAS;gCAIb,aAAA,CAAA;;;EAJI,UAAA,GAIJ,SAGqB,OAHrB,CAG6B,MAH7B,CAAA,OAG2C,SAH3C,CAAA,CAAA,CAAA,CAAA;;;AAJa;AAAA;AAAA,cAaF,wBAAA,YAAoC,mBAblC,CAAA;EAAA,mBAO8B,GAAA,EAMP,eAAA,CACf,MAPsB;EAAS,mBAAvB,GAAA,EAAA;IAAR,4BAAA,EAAA,MAAA;EAAO,CAAA;EAAA,mBAAA,MAAA,EASJ,MATI;EAMjB,mBAAA,IAAA,EAIW,gBAJc;EAAA,mBAAA,UAAA,EAKN,MALM,CAAA,MAAA,EAKS,eALT,CAAA;EAAA,mBACf,iBAAA,EAKgB,iBALhB;EAAA,SAEG,OAAA,EAKA,sBALA;EAAA,WACF,CAAA;EAAA,mBACuB,OAAA,EAGC,aAAA,CASrB,cAZoB,CAAA,OAAA,CAAA;EAAe,WAA9B,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA;EAAM,MACC,CAAA,UAAA,EAAA,MAAA,EAAA,IAAA,EAuC/B,QAvC+B,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EAyCnC,OAzCmC,CAAA,MAAA,CAAA;EAAiB,QAE9B,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EA+EkC,OA/ElC,CA+E0C,QA/E1C,CAAA;EAAsB,MAAA,CAAA,UASrB,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EA8F+B,OA9F/B,CAAA,OAAA,CAAA;EAAA,MA4BnB,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAyEkD,OAzElD,CAAA,IAAA,CAAA;EAAQ,QAEZ,CAAA,SAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAmFiD,eAnFjD;EAAO,UAwCyD,qBAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EAyDhE,OAzDgE,CAyDxD,eAzDwD,CAAA;EAAQ,UAAhB,QAAA,CAAA,CAAA,EAAA,MAAA;;;;;;;;;;cChH/C,mBAAiB,aAAA,CAAA,QAW5B,aAAA,CAX4B,MAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { $bucket, AlephaBucket, FileNotFoundError, FileStorageProvider } from "@alepha/bucket";
|
|
2
|
-
import { $env, $hook, $inject, $
|
|
2
|
+
import { $env, $hook, $inject, $module, Alepha, t } from "@alepha/core";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { Readable } from "node:stream";
|
|
5
5
|
import { DateTimeProvider } from "@alepha/datetime";
|
|
6
6
|
import { createFile } from "@alepha/file";
|
|
7
|
+
import { $logger } from "@alepha/logger";
|
|
7
8
|
import { BlobServiceClient } from "@azure/storage-blob";
|
|
8
9
|
|
|
9
10
|
//#region src/providers/AzureFileStorageProvider.ts
|
|
@@ -27,18 +28,15 @@ var AzureFileStorageProvider = class {
|
|
|
27
28
|
handler: async () => {
|
|
28
29
|
for (const bucket of this.alepha.descriptors($bucket)) {
|
|
29
30
|
if (bucket.provider !== this) continue;
|
|
30
|
-
const containerName = bucket.name
|
|
31
|
-
this.log.debug(`Prepare container ${containerName}...`);
|
|
31
|
+
const containerName = this.convertName(bucket.name);
|
|
32
|
+
this.log.debug(`Prepare container '${containerName}' ...`);
|
|
32
33
|
if (!this.containers[containerName]) this.containers[containerName] = await this.createContainerClient(containerName);
|
|
33
|
-
this.log.info(`Container ${bucket} OK`);
|
|
34
|
+
this.log.info(`Container '${bucket.name}' OK`);
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
});
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const container = await this.createContainerClient(containerName);
|
|
40
|
-
this.containers[containerName] = container;
|
|
41
|
-
return container;
|
|
38
|
+
convertName(name) {
|
|
39
|
+
return name.replaceAll("/", "-").toLowerCase();
|
|
42
40
|
}
|
|
43
41
|
async upload(bucketName, file, fileId) {
|
|
44
42
|
fileId ??= this.createId();
|
|
@@ -62,6 +60,7 @@ var AzureFileStorageProvider = class {
|
|
|
62
60
|
return fileId;
|
|
63
61
|
}
|
|
64
62
|
async download(bucketName, fileId) {
|
|
63
|
+
this.log.trace(`Downloading file '${fileId}' from bucket '${bucketName}'...`);
|
|
65
64
|
const block = this.getBlock(bucketName, fileId);
|
|
66
65
|
const blob = await block.download().catch((error) => {
|
|
67
66
|
if (error instanceof Error) throw new FileNotFoundError("Error downloading file", { cause: error });
|
|
@@ -74,9 +73,11 @@ var AzureFileStorageProvider = class {
|
|
|
74
73
|
});
|
|
75
74
|
}
|
|
76
75
|
async exists(bucketName, fileId) {
|
|
76
|
+
this.log.trace(`Checking existence of file '${fileId}' in bucket '${bucketName}'...`);
|
|
77
77
|
return await this.getBlock(bucketName, fileId).exists();
|
|
78
78
|
}
|
|
79
79
|
async delete(bucketName, fileId) {
|
|
80
|
+
this.log.trace(`Deleting file '${fileId}' from bucket '${bucketName}'...`);
|
|
80
81
|
try {
|
|
81
82
|
await this.getBlock(bucketName, fileId).delete();
|
|
82
83
|
} catch (error) {
|
|
@@ -85,8 +86,9 @@ var AzureFileStorageProvider = class {
|
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
88
|
getBlock(container, fileId) {
|
|
88
|
-
|
|
89
|
-
|
|
89
|
+
const containerName = this.convertName(container);
|
|
90
|
+
if (!this.containers[containerName]) throw new FileNotFoundError(`File '${fileId}' not found - container '${container}' does not exists`);
|
|
91
|
+
return this.containers[containerName].getBlockBlobClient(fileId);
|
|
90
92
|
}
|
|
91
93
|
async createContainerClient(name) {
|
|
92
94
|
const container = this.blobServiceClient.getContainerClient(name);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["
|
|
1
|
+
{"version":3,"file":"index.js","names":["name: string","bucketName: string","file: FileLike","fileId?: string","fileId: string","container: string"],"sources":["../src/providers/AzureFileStorageProvider.ts","../src/index.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { Readable } from \"node:stream\";\nimport {\n\t$bucket,\n\tFileNotFoundError,\n\ttype FileStorageProvider,\n} from \"@alepha/bucket\";\nimport {\n\t$env,\n\t$hook,\n\t$inject,\n\tAlepha,\n\ttype FileLike,\n\ttype Static,\n\tt,\n} from \"@alepha/core\";\nimport { DateTimeProvider } from \"@alepha/datetime\";\nimport { createFile } from \"@alepha/file\";\nimport { $logger } from \"@alepha/logger\";\nimport {\n\tBlobServiceClient,\n\ttype BlockBlobClient,\n\ttype ContainerClient,\n\ttype StoragePipelineOptions,\n} from \"@azure/storage-blob\";\n\nconst envSchema = t.object({\n\tAZ_STORAGE_CONNECTION_STRING: t.string({\n\t\tsize: \"long\",\n\t}),\n});\n\ndeclare module \"@alepha/core\" {\n\tinterface Env extends Partial<Static<typeof envSchema>> {}\n}\n\n/**\n * Azure Blog Storage implementation of File Storage Provider.\n */\nexport class AzureFileStorageProvider implements FileStorageProvider {\n\tprotected readonly log = $logger();\n\tprotected readonly env = $env(envSchema);\n\tprotected readonly alepha = $inject(Alepha);\n\tprotected readonly time = $inject(DateTimeProvider);\n\tprotected readonly containers: Record<string, ContainerClient> = {};\n\tprotected readonly blobServiceClient: BlobServiceClient;\n\n\tpublic readonly options: StoragePipelineOptions = {};\n\n\tconstructor() {\n\t\tthis.blobServiceClient = BlobServiceClient.fromConnectionString(\n\t\t\tthis.env.AZ_STORAGE_CONNECTION_STRING,\n\t\t\tthis.options,\n\t\t);\n\t}\n\n\tprotected readonly onStart = $hook({\n\t\ton: \"start\",\n\t\thandler: async () => {\n\t\t\tfor (const bucket of this.alepha.descriptors($bucket)) {\n\t\t\t\tif (bucket.provider !== this) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst containerName = this.convertName(bucket.name);\n\t\t\t\tthis.log.debug(`Prepare container '${containerName}' ...`);\n\n\t\t\t\tif (!this.containers[containerName]) {\n\t\t\t\t\tthis.containers[containerName] =\n\t\t\t\t\t\tawait this.createContainerClient(containerName);\n\t\t\t\t}\n\n\t\t\t\tthis.log.info(`Container '${bucket.name}' OK`);\n\t\t\t}\n\t\t},\n\t});\n\n\tpublic convertName(name: string): string {\n\t\t// Azure Blob Storage does not allow uppercase letters in container names\n\t\treturn name.replaceAll(\"/\", \"-\").toLowerCase();\n\t}\n\n\tpublic async upload(\n\t\tbucketName: string,\n\t\tfile: FileLike,\n\t\tfileId?: string,\n\t): Promise<string> {\n\t\tfileId ??= this.createId();\n\t\tconst block = this.getBlock(bucketName, fileId);\n\n\t\tconst metadata = {\n\t\t\tname: file.name,\n\t\t\ttype: file.type,\n\t\t};\n\n\t\tif (file.filepath) {\n\t\t\tawait block.uploadFile(file.filepath, {\n\t\t\t\tmetadata,\n\t\t\t\tblobHTTPHeaders: {\n\t\t\t\t\tblobContentType: file.type,\n\t\t\t\t},\n\t\t\t});\n\t\t} else if (file.size > 0) {\n\t\t\tawait block.uploadData(await file.arrayBuffer(), {\n\t\t\t\tmetadata,\n\t\t\t\tblobHTTPHeaders: {\n\t\t\t\t\tblobContentType: file.type,\n\t\t\t\t},\n\t\t\t});\n\t\t} else {\n\t\t\tawait block.uploadStream(\n\t\t\t\tReadable.from(file.stream()),\n\t\t\t\tfile.size || undefined,\n\t\t\t\t5,\n\t\t\t\t{\n\t\t\t\t\tmetadata,\n\t\t\t\t\tblobHTTPHeaders: {\n\t\t\t\t\t\tblobContentType: file.type,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\treturn fileId;\n\t}\n\n\tpublic async download(bucketName: string, fileId: string): Promise<FileLike> {\n\t\tthis.log.trace(\n\t\t\t`Downloading file '${fileId}' from bucket '${bucketName}'...`,\n\t\t);\n\t\tconst block = this.getBlock(bucketName, fileId);\n\n\t\tconst blob = await block.download().catch((error) => {\n\t\t\tif (error instanceof Error) {\n\t\t\t\tthrow new FileNotFoundError(\"Error downloading file\", { cause: error });\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t});\n\n\t\tif (!blob.readableStreamBody) {\n\t\t\tthrow new FileNotFoundError(\"File not found - empty stream body\");\n\t\t}\n\n\t\treturn createFile(blob.readableStreamBody, {\n\t\t\t...blob.metadata,\n\t\t\tsize: blob.contentLength,\n\t\t});\n\t}\n\n\tpublic async exists(bucketName: string, fileId: string): Promise<boolean> {\n\t\tthis.log.trace(\n\t\t\t`Checking existence of file '${fileId}' in bucket '${bucketName}'...`,\n\t\t);\n\t\treturn await this.getBlock(bucketName, fileId).exists();\n\t}\n\n\tpublic async delete(bucketName: string, fileId: string): Promise<void> {\n\t\tthis.log.trace(`Deleting file '${fileId}' from bucket '${bucketName}'...`);\n\t\ttry {\n\t\t\tawait this.getBlock(bucketName, fileId).delete();\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) {\n\t\t\t\tthrow new FileNotFoundError(\"Error deleting file\", { cause: error });\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tpublic getBlock(container: string, fileId: string): BlockBlobClient {\n\t\tconst containerName = this.convertName(container);\n\n\t\tif (!this.containers[containerName]) {\n\t\t\tthrow new FileNotFoundError(\n\t\t\t\t`File '${fileId}' not found - container '${container}' does not exists`,\n\t\t\t);\n\t\t}\n\n\t\treturn this.containers[containerName].getBlockBlobClient(fileId);\n\t}\n\n\tprotected async createContainerClient(\n\t\tname: string,\n\t): Promise<ContainerClient> {\n\t\tconst container = this.blobServiceClient.getContainerClient(name);\n\n\t\tawait this.time.deadline(\n\t\t\t(abortSignal) => container.createIfNotExists({ abortSignal }),\n\t\t\t[5, \"seconds\"],\n\t\t);\n\n\t\treturn container;\n\t}\n\n\tprotected createId(): string {\n\t\treturn randomUUID();\n\t}\n}\n","import { AlephaBucket, FileStorageProvider } from \"@alepha/bucket\";\nimport { $module } from \"@alepha/core\";\nimport { AzureFileStorageProvider } from \"./providers/AzureFileStorageProvider.ts\";\n\nexport * from \"./providers/AzureFileStorageProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for Alepha Bucket that provides Azure Blob Storage capabilities.\n *\n * @see {@link AzureFileStorageProvider}\n * @module alepha.bucket.azure\n */\nexport const AlephaBucketAzure = $module({\n\tname: \"alepha.bucket.azure\",\n\tservices: [AzureFileStorageProvider],\n\tregister: (alepha) =>\n\t\talepha\n\t\t\t.with({\n\t\t\t\toptional: true,\n\t\t\t\tprovide: FileStorageProvider,\n\t\t\t\tuse: AzureFileStorageProvider,\n\t\t\t})\n\t\t\t.with(AlephaBucket),\n});\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"storage-blob"
|
|
11
11
|
],
|
|
12
12
|
"author": "Feunard",
|
|
13
|
-
"version": "0.9.
|
|
13
|
+
"version": "0.9.4",
|
|
14
14
|
"type": "module",
|
|
15
15
|
"engines": {
|
|
16
16
|
"node": ">=22.0.0"
|
|
@@ -23,19 +23,22 @@
|
|
|
23
23
|
"src"
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@alepha/bucket": "0.9.
|
|
27
|
-
"@alepha/core": "0.9.
|
|
28
|
-
"@alepha/datetime": "0.9.
|
|
29
|
-
"@alepha/file": "0.9.
|
|
30
|
-
"@
|
|
26
|
+
"@alepha/bucket": "0.9.4",
|
|
27
|
+
"@alepha/core": "0.9.4",
|
|
28
|
+
"@alepha/datetime": "0.9.4",
|
|
29
|
+
"@alepha/file": "0.9.4",
|
|
30
|
+
"@alepha/logger": "0.9.4",
|
|
31
|
+
"@azure/storage-blob": "^12.28.0"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
|
-
"tsdown": "^0.
|
|
34
|
+
"tsdown": "^0.14.1",
|
|
35
|
+
"typescript": "^5.9.2",
|
|
34
36
|
"vitest": "^3.2.4"
|
|
35
37
|
},
|
|
36
38
|
"scripts": {
|
|
37
39
|
"test": "vitest run",
|
|
38
|
-
"build": "tsdown -c ../../tsdown.config.ts"
|
|
40
|
+
"build": "tsdown -c ../../tsdown.config.ts",
|
|
41
|
+
"check": "tsc"
|
|
39
42
|
},
|
|
40
43
|
"repository": {
|
|
41
44
|
"type": "git",
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
$env,
|
|
10
10
|
$hook,
|
|
11
11
|
$inject,
|
|
12
|
-
$logger,
|
|
13
12
|
Alepha,
|
|
14
13
|
type FileLike,
|
|
15
14
|
type Static,
|
|
@@ -17,6 +16,7 @@ import {
|
|
|
17
16
|
} from "@alepha/core";
|
|
18
17
|
import { DateTimeProvider } from "@alepha/datetime";
|
|
19
18
|
import { createFile } from "@alepha/file";
|
|
19
|
+
import { $logger } from "@alepha/logger";
|
|
20
20
|
import {
|
|
21
21
|
BlobServiceClient,
|
|
22
22
|
type BlockBlobClient,
|
|
@@ -62,28 +62,22 @@ export class AzureFileStorageProvider implements FileStorageProvider {
|
|
|
62
62
|
continue;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
const containerName = bucket.name
|
|
66
|
-
this.log.debug(`Prepare container ${containerName}...`);
|
|
65
|
+
const containerName = this.convertName(bucket.name);
|
|
66
|
+
this.log.debug(`Prepare container '${containerName}' ...`);
|
|
67
67
|
|
|
68
68
|
if (!this.containers[containerName]) {
|
|
69
69
|
this.containers[containerName] =
|
|
70
70
|
await this.createContainerClient(containerName);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
this.log.info(`Container ${bucket} OK`);
|
|
73
|
+
this.log.info(`Container '${bucket.name}' OK`);
|
|
74
74
|
}
|
|
75
75
|
},
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
public
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (this.containers[containerName]) {
|
|
82
|
-
return this.containers[containerName];
|
|
83
|
-
}
|
|
84
|
-
const container = await this.createContainerClient(containerName);
|
|
85
|
-
this.containers[containerName] = container;
|
|
86
|
-
return container;
|
|
78
|
+
public convertName(name: string): string {
|
|
79
|
+
// Azure Blob Storage does not allow uppercase letters in container names
|
|
80
|
+
return name.replaceAll("/", "-").toLowerCase();
|
|
87
81
|
}
|
|
88
82
|
|
|
89
83
|
public async upload(
|
|
@@ -131,6 +125,9 @@ export class AzureFileStorageProvider implements FileStorageProvider {
|
|
|
131
125
|
}
|
|
132
126
|
|
|
133
127
|
public async download(bucketName: string, fileId: string): Promise<FileLike> {
|
|
128
|
+
this.log.trace(
|
|
129
|
+
`Downloading file '${fileId}' from bucket '${bucketName}'...`,
|
|
130
|
+
);
|
|
134
131
|
const block = this.getBlock(bucketName, fileId);
|
|
135
132
|
|
|
136
133
|
const blob = await block.download().catch((error) => {
|
|
@@ -152,10 +149,14 @@ export class AzureFileStorageProvider implements FileStorageProvider {
|
|
|
152
149
|
}
|
|
153
150
|
|
|
154
151
|
public async exists(bucketName: string, fileId: string): Promise<boolean> {
|
|
152
|
+
this.log.trace(
|
|
153
|
+
`Checking existence of file '${fileId}' in bucket '${bucketName}'...`,
|
|
154
|
+
);
|
|
155
155
|
return await this.getBlock(bucketName, fileId).exists();
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
public async delete(bucketName: string, fileId: string): Promise<void> {
|
|
159
|
+
this.log.trace(`Deleting file '${fileId}' from bucket '${bucketName}'...`);
|
|
159
160
|
try {
|
|
160
161
|
await this.getBlock(bucketName, fileId).delete();
|
|
161
162
|
} catch (error) {
|
|
@@ -167,13 +168,15 @@ export class AzureFileStorageProvider implements FileStorageProvider {
|
|
|
167
168
|
}
|
|
168
169
|
|
|
169
170
|
public getBlock(container: string, fileId: string): BlockBlobClient {
|
|
170
|
-
|
|
171
|
+
const containerName = this.convertName(container);
|
|
172
|
+
|
|
173
|
+
if (!this.containers[containerName]) {
|
|
171
174
|
throw new FileNotFoundError(
|
|
172
175
|
`File '${fileId}' not found - container '${container}' does not exists`,
|
|
173
176
|
);
|
|
174
177
|
}
|
|
175
178
|
|
|
176
|
-
return this.containers[
|
|
179
|
+
return this.containers[containerName].getBlockBlobClient(fileId);
|
|
177
180
|
}
|
|
178
181
|
|
|
179
182
|
protected async createContainerClient(
|