UNPKG

lemon-core

Version:
431 lines 21 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.BlobService = void 0; /** * `s3s-service.js` * - common S3 services. * * * @author Ian Kim <ian@lemoncloud.io> * @date 2023-09-18 initial azure blob service * * @copyright (C) lemoncloud.io 2023 - All Rights Reserved. */ /** **************************************************************************************************************** * Common Headers ** ****************************************************************************************************************/ // eslint-disable-next-line @typescript-eslint/no-unused-vars const engine_1 = require("../../engine"); const uuid_1 = require("uuid"); const test_helper_1 = require("../../common/test-helper"); // import { KeyVaultService } from './azure-keyvault-service'; require("dotenv/config"); const NS = engine_1.$U.NS('BLOB', 'blue'); const instance = () => { return BlobService.instance(); }; /** **************************************************************************************************************** * Public Instance Exported. ** ****************************************************************************************************************/ const region = () => engine_1.$engine.environ('REGION', 'koreacentral'); /** * use `target` as value or environment value. * environ('abc') => string 'abc' * environ('ABC') => use `env.ABC` */ const environ = (target, defEnvName, defEnvValue) => { const isUpperStr = target && /^[A-Z][A-Z0-9_]+$/.test(target); defEnvName = isUpperStr ? target : defEnvName; const val = defEnvName ? engine_1.$engine.environ(defEnvName, defEnvValue) : defEnvValue; target = isUpperStr ? '' : target; return `${target || val}`; }; /** * main service implement. */ class BlobService { constructor() { // protected $kv: KeyVaultService; // constructor() { // this.$kv = new KeyVaultService(); // } /** * get name of this */ this.name = () => `BLOB`; /** * hello */ this.hello = () => `azure-blob-service:${this.bucket()}`; /** * get target endpoint by name. */ this.bucket = (target) => environ(target, BlobService.ENV_BLOB_NAME, BlobService.DEF_BLOB_BUCKET); /** * get azure sdk for blob */ // public static $kv: KeyVaultService = new KeyVaultService(); this.instance = () => __awaiter(this, void 0, void 0, function* () { // eslint-disable-next-line @typescript-eslint/no-var-requires const { BlobServiceClient, StorageSharedKeyCredential, BlobItem, Metadata } = require('@azure/storage-blob'); // eslint-disable-next-line @typescript-eslint/no-var-requires const { StorageManagementClient } = require('@azure/arm-storage'); // eslint-disable-next-line @typescript-eslint/no-var-requires const { DefaultAzureCredential } = require('@azure/identity'); const account = process.env.STORAGE_ACCOUNT_RESOURCE; const accountKey = process.env.STORAGE_ACCOUNT_ACCESS_KEY; // const account = await BlobService.$kv.decrypt(process.env.STORAGE_ACCOUNT_RESOURCE); // const accountKey = await BlobService.$kv.decrypt(process.env.STORAGE_ACCOUNT_ACCESS_KEY); const subscriptionId = process.env.SUBSCRIPTION_ID; const resourceGroupName = process.env.RESOURCE_GROUP; const sharedKeyCredential = new StorageSharedKeyCredential(account, accountKey); const blobServiceClient = new BlobServiceClient(`https://${account}.blob.core.windows.net`, sharedKeyCredential); const storageClient = new StorageManagementClient(new DefaultAzureCredential(), subscriptionId); return { storageClient, blobServiceClient, resourceGroupName, Metadata, BlobItem }; }); /** * retrieve metadata without returning the object * * @param {string} key * @return metadata object / null if not exists */ this.headObject = (key) => __awaiter(this, void 0, void 0, function* () { if (!key) throw new Error(`@key (string) is required - headObject(${key !== null && key !== void 0 ? key : ''})`); const { blobServiceClient } = yield this.instance(); const Bucket = this.bucket(); const params = { Bucket, Key: key }; const parts = key.split('/'); const fileName = parts[parts.length - 1]; const containerClient = blobServiceClient.getContainerClient(Bucket); const blobClient = containerClient.getBlobClient(fileName); try { const data = yield blobClient.getProperties(); (0, engine_1._log)(NS, '> data =', engine_1.$U.json(Object.assign(Object.assign({}, data), { Contents: undefined }))); const result = { ContentType: data.contentType, ContentLength: data.contentLength, Metadata: data.metadata, ETag: data.etag, LastModified: engine_1.$U.ts(data.lastModified), }; return result; } catch (e) { if (e.statusCode == 404) return null; (0, engine_1._err)(NS, '! err=', e); throw e; } }); /** * get a file from Blob Container * * @param {string} key */ this.getObject = (key) => __awaiter(this, void 0, void 0, function* () { if (!key) throw new Error(`@key (string) is required - getObject(${key !== null && key !== void 0 ? key : ''})`); const { blobServiceClient, Metadata } = yield this.instance(); const Bucket = this.bucket(); const params = { Bucket, Key: key }; const parts = key.split('/'); const fileName = parts[parts.length - 1]; const containerClient = blobServiceClient.getContainerClient(Bucket); const blobClient = containerClient.getBlobClient(fileName); const blockBlobClient = containerClient.getBlockBlobClient(fileName); function getJsonData(blobResponse) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { const chunks = []; blobResponse.blobDownloadStream.on('data', (chunk) => { chunks.push(chunk); }); blobResponse.blobDownloadStream.on('end', () => { try { const jsonData = JSON.parse(Buffer.concat(chunks).toString('utf8')); resolve(jsonData); } catch (error) { reject(error); } }); blobResponse.blobDownloadStream.on('error', (error) => { reject(error); }); }); }); } try { const _data = yield blobClient.download(); const data = yield getJsonData(_data); const properties = yield blobClient.getProperties(); const ContentType = properties.contentType; const ContentLength = properties.contentLength; const Metadata = properties.metadata; const ETag = properties.etag; const tagResponse = yield blockBlobClient.getTags(); (0, engine_1._log)(NS, '> data.type =', typeof data); const Body = JSON.stringify(data); const result = { ContentLength, ContentType, Body, ETag, Metadata }; if (tagResponse) result.TagCount = Object.keys(tagResponse.tags).length; return result; } catch (e) { (0, engine_1._err)(NS, '! err=', e); throw e; } }); /** * return decoded Object from Blob Container file. * * @param {string} key ex) 'hello-0001.json' , 'dist/hello-0001.json */ this.getDecodedObject = (key) => __awaiter(this, void 0, void 0, function* () { if (!key) throw new Error(`@key (string) is required - getDecodedObject(${key !== null && key !== void 0 ? key : ''})`); const Bucket = this.bucket(); const params = { Bucket, Key: key }; try { const data = yield this.getObject(key).catch(e => { (0, engine_1._log)(NS, '> data.type =', typeof data); (0, engine_1._err)(NS, '! err=', e); throw e; }); if (!data) { throw new Error('Data not found'); } const content = data.Body.toString(); return JSON.parse(content); } catch (e) { (0, engine_1._err)(NS, '! err=', e); throw e; } }); /** * get tag-set of object * * @param {string} key */ this.getObjectTagging = (key) => __awaiter(this, void 0, void 0, function* () { if (!key) throw new Error(`@key (string) is required - getObjectTagging(${key !== null && key !== void 0 ? key : ''})`); const { blobServiceClient } = yield this.instance(); const Bucket = this.bucket(); const params = { Bucket, Key: key }; const parts = key.split('/'); const fileName = parts[parts.length - 1]; const containerClient = blobServiceClient.getContainerClient(Bucket); const blobClient = containerClient.getBlobClient(fileName); try { const data = yield blobClient.getTags(); (0, engine_1._log)(NS, `> data =`, engine_1.$U.json(data)); return data === null || data === void 0 ? void 0 : data.tags; } catch (e) { (0, engine_1._err)(NS, '! err=', e); throw e; } }); /** * delete object from Blob Container * * @param {string} key */ this.deleteObject = (key) => __awaiter(this, void 0, void 0, function* () { if (!key) throw new Error(`@key (string) is required - deleteObject(${key !== null && key !== void 0 ? key : ''})`); const { blobServiceClient } = yield this.instance(); const Bucket = this.bucket(); const params = { Bucket, Key: key }; const parts = key.split('/'); const fileName = parts[parts.length - 1]; const containerClient = blobServiceClient.getContainerClient(Bucket); const blobClient = containerClient.getBlobClient(fileName); try { const data = yield blobClient.delete(); (0, engine_1._log)(NS, '> data =', engine_1.$U.json(data)); } catch (e) { (0, engine_1._err)(NS, '! err=', e); throw e; } }); /** * list objects in Blob Container */ this.listObjects = (options) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k; // if (!key) throw new Error('@key is required!'); const Prefix = (_a = options === null || options === void 0 ? void 0 : options.prefix) !== null && _a !== void 0 ? _a : ''; const Delimiter = (_b = options === null || options === void 0 ? void 0 : options.delimiter) !== null && _b !== void 0 ? _b : '/'; const MaxKeys = Math.min((_c = options === null || options === void 0 ? void 0 : options.limit) !== null && _c !== void 0 ? _c : 10, 1000); const unlimited = (_d = options === null || options === void 0 ? void 0 : options.unlimited) !== null && _d !== void 0 ? _d : false; const nextToken = options === null || options === void 0 ? void 0 : options.nextToken; const throwable = (_e = options === null || options === void 0 ? void 0 : options.throwable) !== null && _e !== void 0 ? _e : true; //* build the req-params. const Bucket = this.bucket(); const { blobServiceClient } = yield this.instance(); const containerClient = blobServiceClient.getContainerClient(Bucket); const result = { Contents: [], MaxKeys, KeyCount: 0, IsTruncated: false, }; try { let count = 0; result.MaxKeys = MaxKeys; let iterator = containerClient.listBlobsFlat().byPage(Object.assign({ maxPageSize: MaxKeys }, (nextToken ? { continuationToken: nextToken } : {}))); let response = (yield iterator.next()).value; if (response !== undefined && (response === null || response === void 0 ? void 0 : response.segment) !== undefined && ((_f = response === null || response === void 0 ? void 0 : response.segment) === null || _f === void 0 ? void 0 : _f.blobItems) !== undefined) { for (const blob of (_g = response === null || response === void 0 ? void 0 : response.segment) === null || _g === void 0 ? void 0 : _g.blobItems) { result.Contents.push({ Key: blob.name, Size: blob.properties.contentLength, }); } count++; result.NextContinuationToken = response === null || response === void 0 ? void 0 : response.continuationToken; if (!unlimited) { result.KeyCount = count; result.IsTruncated = true; return result; } } while (response !== undefined && (response === null || response === void 0 ? void 0 : response.segment) !== undefined && ((_h = response === null || response === void 0 ? void 0 : response.segment) === null || _h === void 0 ? void 0 : _h.blobItems) !== undefined) { iterator = containerClient.listBlobsFlat().byPage({ maxPageSize: MaxKeys, continuationToken: result.NextContinuationToken, }); response = (yield iterator.next()).value; result.NextContinuationToken = response === null || response === void 0 ? void 0 : response.continuationToken; if (((_j = response === null || response === void 0 ? void 0 : response.segment) === null || _j === void 0 ? void 0 : _j.blobItems.length) > 0 && response !== undefined && (response === null || response === void 0 ? void 0 : response.segment) !== undefined && ((_k = response === null || response === void 0 ? void 0 : response.segment) === null || _k === void 0 ? void 0 : _k.blobItems) !== undefined) { for (const blob of response.segment.blobItems) { result.Contents.push({ Key: blob.name, Size: blob.properties.contentLength, }); } count++; } } result.KeyCount = count; } catch (e) { (0, engine_1._err)(NS, '! err=', e); if (throwable) throw e; result.error = (0, test_helper_1.GETERR)(e); } return result; }); /** * upload a file to Blob Container * * * @param {string|Buffer} content content body * @param {string} key (optional) S3 key to put * @param {Metadata} metadata (optional) metadata to store * @param {object} tags (optional) tag set */ this.putObject = (content, key, metadata, tags) => __awaiter(this, void 0, void 0, function* () { if (!content) throw new Error(`@content (buffer) is required - putObject()`); function generateBlobName() { const uuid = (0, uuid_1.v4)(); return `${uuid}.json`; } if (!key) key = generateBlobName(); const { blobServiceClient, storageClient, resourceGroupName } = yield this.instance(); const Bucket = this.bucket(); const parts = key.split('/'); const fileName = parts[parts.length - 1]; const containerClient = blobServiceClient.getContainerClient(Bucket); const blobClient = containerClient.getBlobClient(fileName); //* upsert const blobExists = yield blobClient.exists(); if (blobExists) { } else { const blockBlobClient = containerClient.getBlockBlobClient(fileName); yield blockBlobClient.upload(content, content.length, { blobHTTPHeaders: { blobContentType: 'application/json; charset=utf-8', }, }); } //* metadata has ContentType if (metadata && metadata.hasOwnProperty('ContentType')) { yield blobClient.setHTTPHeaders({ blobContentType: metadata.ContentType, }); } const properties = yield blobClient.getProperties(); const contentType = properties.contentType; const contentLength = properties.contentLength; const eTag = properties.etag; (0, engine_1._log)(NS, `> params.ContentType =`, contentType); (0, engine_1._log)(NS, `> params.ContentLength =`, contentLength); (0, engine_1._log)(NS, `> params.Metadata =`, metadata); (0, engine_1._log)(NS, `> params.Tagging =`, eTag); try { const blockBlobClient = containerClient.getBlockBlobClient(fileName); yield blockBlobClient.upload(content, content.length, { blobHTTPHeaders: { blobContentType: 'application/json; charset=utf-8', }, }); const storageAccount = yield storageClient.storageAccounts.getProperties(resourceGroupName, blobClient.accountName); if (metadata) yield blobClient.setMetadata(metadata); if (tags) yield blobClient.setTags(tags); const result = { Bucket: Bucket, Location: storageAccount.location, Key: key, ETag: eTag, ContentType: contentType, ContentLength: contentLength, Metadata: metadata, }; return result; } catch (e) { (0, engine_1._err)(NS, `! err[${Bucket}] =`, e); throw e; } }); } } exports.BlobService = BlobService; /** * environ name to use `bucket` */ BlobService.ENV_BLOB_NAME = 'my-blob-container'; /** * default `bucket` name */ BlobService.DEF_BLOB_BUCKET = (_a = process.env.BLOB_CONTAINER) !== null && _a !== void 0 ? _a : 'blob-container'; //# sourceMappingURL=azure-blob-service.js.map