@things-factory/attachment-base 10.1.111 → 10.1.112

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.
Files changed (40) hide show
  1. package/dist-server/attachment-access.d.ts +21 -0
  2. package/dist-server/attachment-access.js +111 -0
  3. package/dist-server/attachment-access.js.map +1 -0
  4. package/dist-server/attachment-const.d.ts +5 -0
  5. package/dist-server/attachment-const.js +6 -1
  6. package/dist-server/attachment-const.js.map +1 -1
  7. package/dist-server/index.d.ts +1 -0
  8. package/dist-server/index.js +1 -0
  9. package/dist-server/index.js.map +1 -1
  10. package/dist-server/routes.js +59 -17
  11. package/dist-server/routes.js.map +1 -1
  12. package/dist-server/service/attachment/index.d.ts +1 -1
  13. package/dist-server/service/attachment-share/attachment-share-mutation.d.ts +11 -0
  14. package/dist-server/service/attachment-share/attachment-share-mutation.js +87 -0
  15. package/dist-server/service/attachment-share/attachment-share-mutation.js.map +1 -0
  16. package/dist-server/service/attachment-share/attachment-share-rule.d.ts +16 -0
  17. package/dist-server/service/attachment-share/attachment-share-rule.js +41 -0
  18. package/dist-server/service/attachment-share/attachment-share-rule.js.map +1 -0
  19. package/dist-server/service/attachment-share/attachment-share.d.ts +15 -0
  20. package/dist-server/service/attachment-share/attachment-share.js +72 -0
  21. package/dist-server/service/attachment-share/attachment-share.js.map +1 -0
  22. package/dist-server/service/attachment-share/index.d.ts +4 -0
  23. package/dist-server/service/attachment-share/index.js +8 -0
  24. package/dist-server/service/attachment-share/index.js.map +1 -0
  25. package/dist-server/service/index.d.ts +4 -2
  26. package/dist-server/service/index.js +7 -2
  27. package/dist-server/service/index.js.map +1 -1
  28. package/dist-server/storage-azure-blob.js +1 -1
  29. package/dist-server/storage-azure-blob.js.map +1 -1
  30. package/dist-server/storage-s3.js +1 -1
  31. package/dist-server/storage-s3.js.map +1 -1
  32. package/dist-server/tsconfig.tsbuildinfo +1 -1
  33. package/package.json +2 -2
  34. package/tests/attachment-access.test.ts +93 -0
  35. package/tests/attachment-route.test.ts +88 -0
  36. package/translations/en.json +3 -2
  37. package/translations/ja.json +2 -1
  38. package/translations/ko.json +3 -2
  39. package/translations/ms.json +2 -1
  40. package/translations/zh.json +2 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachment-share.js","sourceRoot":"","sources":["../../../server/service/attachment-share/attachment-share.ts"],"names":[],"mappings":";;;;AAAA,qCAAgH;AAChH,+CAAoD;AAEpD,iDAA8C;AAC9C,yDAAgD;AAEhD,yDAAqD;AAErD;;;;;;GAMG;AAII,IAAM,eAAe,GAArB,MAAM,eAAe;CAqC3B,CAAA;AArCY,0CAAe;AAGjB;IAFR,IAAA,gCAAsB,EAAC,MAAM,CAAC;IAC9B,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,EAAE,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;;2CACxD;AAInB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,CAAC;IACzB,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,EAAE,EAAE,WAAW,EAAE,uCAAuC,EAAE,CAAC;sCACxE,cAAM;+CAAA;AAGd;IADC,IAAA,oBAAU,EAAC,CAAC,KAAsB,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC;;iDACrC;AAIhB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,uBAAU,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtD,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,uBAAU,EAAE,EAAE,WAAW,EAAE,gCAAgC,EAAE,CAAC;sCACjE,uBAAU;mDAAA;AAGtB;IADC,IAAA,oBAAU,EAAC,CAAC,KAAsB,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC;;qDACrC;AAIpB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAC;sCACjE,gBAAI;gDAAA;AAGd;IADC,IAAA,oBAAU,EAAC,CAAC,KAAsB,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;;kDACpC;AAIlB;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,8BAA8B,EAAE,CAAC;sCAC5C,IAAI;kDAAA;AAIf;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,0CAA0C,EAAE,CAAC;sCACvE,IAAI;kDAAA;AAIhB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,yBAAyB,EAAE,CAAC;sCACvC,IAAI;kDAAA;0BApCJ,eAAe;IAH3B,IAAA,gBAAM,GAAE;IACR,IAAA,eAAK,EAAC,uBAAuB,EAAE,CAAC,KAAsB,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC5F,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,4EAA4E,EAAE,CAAC;GAC7F,eAAe,CAqC3B","sourcesContent":["import { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId } from 'typeorm'\nimport { Field, ID, ObjectType } from 'type-graphql'\n\nimport { Domain } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\n\nimport { Attachment } from '../attachment/attachment'\n\n/*\n * A link to one attachment handed outside the domain - Label Studio, a mail, a person told by hand (ADR-0106 decision 3).\n *\n * The row is the record and the off switch: who made it, for which attachment, until when, and whether it was taken\n * back. The URL carries this row's id and an HMAC over (id, attachment, domain, expiry); the attachment's own path is\n * not in it.\n */\n@Entity()\n@Index('ix_attachment_share_0', (share: AttachmentShare) => [share.domain, share.attachment])\n@ObjectType({ description: 'A signed, expiring link to one attachment, for someone outside the domain.' })\nexport class AttachmentShare {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier of the share link.' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { description: 'The domain the attachment belongs to.' })\n domain: Domain\n\n @RelationId((share: AttachmentShare) => share.domain)\n domainId: string\n\n @ManyToOne(type => Attachment, { onDelete: 'CASCADE' })\n @Field(type => Attachment, { description: 'The attachment the link opens.' })\n attachment: Attachment\n\n @RelationId((share: AttachmentShare) => share.attachment)\n attachmentId: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who made the link.' })\n creator?: User\n\n @RelationId((share: AttachmentShare) => share.creator)\n creatorId?: string\n\n @Column()\n @Field({ description: 'When the link stops opening.' })\n expiresAt: Date\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When the link was taken back, if it was.' })\n revokedAt?: Date\n\n @CreateDateColumn()\n @Field({ description: 'When the link was made.' })\n createdAt: Date\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import { AttachmentShare } from './attachment-share';
2
+ import { AttachmentShareMutation } from './attachment-share-mutation';
3
+ export declare const entities: (typeof AttachmentShare)[];
4
+ export declare const resolvers: (typeof AttachmentShareMutation)[];
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolvers = exports.entities = void 0;
4
+ const attachment_share_1 = require("./attachment-share");
5
+ const attachment_share_mutation_1 = require("./attachment-share-mutation");
6
+ exports.entities = [attachment_share_1.AttachmentShare];
7
+ exports.resolvers = [attachment_share_mutation_1.AttachmentShareMutation];
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/attachment-share/index.ts"],"names":[],"mappings":";;;AAAA,yDAAoD;AACpD,2EAAqE;AAExD,QAAA,QAAQ,GAAG,CAAC,kCAAe,CAAC,CAAA;AAC5B,QAAA,SAAS,GAAG,CAAC,mDAAuB,CAAC,CAAA","sourcesContent":["import { AttachmentShare } from './attachment-share'\nimport { AttachmentShareMutation } from './attachment-share-mutation'\n\nexport const entities = [AttachmentShare]\nexport const resolvers = [AttachmentShareMutation]\n"]}
@@ -1,6 +1,8 @@
1
1
  export * from './attachment/attachment';
2
+ export * from './attachment-share/attachment-share';
3
+ export * from './attachment-share/attachment-share-rule';
2
4
  export * from './attachment/attachment-types';
3
- export declare const entities: typeof import("./attachment/attachment").Attachment[];
5
+ export declare const entities: (typeof import("./attachment-share/attachment-share").AttachmentShare | typeof import("./attachment/attachment").Attachment)[];
4
6
  export declare const schema: {
5
- resolverClasses: (typeof import("./attachment/attachment-mutation").AttachmentMutation | typeof import("./attachment/attachment-query").AttachmentQuery)[];
7
+ resolverClasses: (typeof import("./attachment-share/attachment-share-mutation").AttachmentShareMutation | typeof import("./attachment/attachment-query").AttachmentQuery | typeof import("./attachment/attachment-mutation").AttachmentMutation)[];
6
8
  };
@@ -4,18 +4,23 @@ exports.schema = exports.entities = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  /* IMPORT ENTITIES AND RESOLVERS */
6
6
  const attachment_1 = require("./attachment");
7
+ const attachment_share_1 = require("./attachment-share");
7
8
  /* EXPORT ENTITY TYPES */
8
9
  tslib_1.__exportStar(require("./attachment/attachment"), exports);
10
+ tslib_1.__exportStar(require("./attachment-share/attachment-share"), exports);
11
+ tslib_1.__exportStar(require("./attachment-share/attachment-share-rule"), exports);
9
12
  /* EXPORT TYPES */
10
13
  tslib_1.__exportStar(require("./attachment/attachment-types"), exports);
11
14
  exports.entities = [
12
15
  /* ENTITIES */
13
- ...attachment_1.entities
16
+ ...attachment_1.entities,
17
+ ...attachment_share_1.entities
14
18
  ];
15
19
  exports.schema = {
16
20
  resolverClasses: [
17
21
  /* RESOLVER CLASSES */
18
- ...attachment_1.resolvers
22
+ ...attachment_1.resolvers,
23
+ ...attachment_share_1.resolvers
19
24
  ]
20
25
  };
21
26
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,mCAAmC;AACnC,6CAA6F;AAE7F,yBAAyB;AACzB,kEAAuC;AACvC,kBAAkB;AAClB,wEAA6C;AAEhC,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,GAAG,qBAAgB;CACpB,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,eAAe,EAAE;QACf,sBAAsB;QACtB,GAAG,sBAAmB;KACvB;CACF,CAAA","sourcesContent":["/* IMPORT ENTITIES AND RESOLVERS */\nimport { entities as AttachmentEntity, resolvers as AttachmentResolvers } from './attachment'\n\n/* EXPORT ENTITY TYPES */\nexport * from './attachment/attachment'\n/* EXPORT TYPES */\nexport * from './attachment/attachment-types'\n\nexport const entities = [\n /* ENTITIES */\n ...AttachmentEntity\n]\n\nexport const schema = {\n resolverClasses: [\n /* RESOLVER CLASSES */\n ...AttachmentResolvers\n ]\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,mCAAmC;AACnC,6CAA6F;AAC7F,yDAA6G;AAE7G,yBAAyB;AACzB,kEAAuC;AACvC,8EAAmD;AACnD,mFAAwD;AACxD,kBAAkB;AAClB,wEAA6C;AAEhC,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,GAAG,qBAAgB;IACnB,GAAG,2BAAqB;CACzB,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,eAAe,EAAE;QACf,sBAAsB;QACtB,GAAG,sBAAmB;QACtB,GAAG,4BAAwB;KAC5B;CACF,CAAA","sourcesContent":["/* IMPORT ENTITIES AND RESOLVERS */\nimport { entities as AttachmentEntity, resolvers as AttachmentResolvers } from './attachment'\nimport { entities as AttachmentShareEntity, resolvers as AttachmentShareResolvers } from './attachment-share'\n\n/* EXPORT ENTITY TYPES */\nexport * from './attachment/attachment'\nexport * from './attachment-share/attachment-share'\nexport * from './attachment-share/attachment-share-rule'\n/* EXPORT TYPES */\nexport * from './attachment/attachment-types'\n\nexport const entities = [\n /* ENTITIES */\n ...AttachmentEntity,\n ...AttachmentShareEntity\n]\n\nexport const schema = {\n resolverClasses: [\n /* RESOLVER CLASSES */\n ...AttachmentResolvers,\n ...AttachmentShareResolvers\n ]\n}\n"]}
@@ -54,7 +54,7 @@ if (attachment_const_1.STORAGE && attachment_const_1.STORAGE.type == 'azureblob'
54
54
  'Content-Type': mime.getType(attachment),
55
55
  'Last-Modified': result.lastModified.toUTCString(),
56
56
  ETag: result.etag,
57
- 'Cache-Control': 'public, max-age=31556926'
57
+ 'Cache-Control': attachment_const_1.ATTACHMENT_CACHE_CONTROL
58
58
  });
59
59
  context.body = body;
60
60
  };
@@ -1 +1 @@
1
- {"version":3,"file":"storage-azure-blob.js","sourceRoot":"","sources":["../server/storage-azure-blob.ts"],"names":[],"mappings":"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","sourcesContent":["import { BlobServiceClient } from '@azure/storage-blob'\nimport { logger } from '@things-factory/env'\n\nimport { STORAGE } from './attachment-const'\n\nconst crypto = require('crypto')\nconst mime = require('mime')\n\nif (STORAGE && STORAGE.type == 'azureblob') {\n const blobServiceClient = BlobServiceClient.fromConnectionString(STORAGE.connectionString)\n\n /* upload file */\n STORAGE.uploadFile = async ({ id, file }) => {\n const { createReadStream, filename, mimetype, encoding } = await file\n\n const containerClient = blobServiceClient.getContainerClient(STORAGE.containerName)\n id = id || crypto.randomUUID()\n const ext = filename.split('.').pop()\n const key = ext ? `${id}.${ext}` : id\n\n const blockBlobClient = containerClient.getBlockBlobClient(key)\n const stream = createReadStream()\n const buffer = await streamToBuffer(stream)\n\n await blockBlobClient.upload(buffer, buffer.length, {\n blobHTTPHeaders: {\n blobContentType: mimetype\n }\n })\n\n // await blockBlobClient.uploadStream(stream, undefined, undefined, {\n // blobHTTPHeaders: {\n // blobContentType: mimetype\n // }\n // })\n\n const url = `${STORAGE.url}/${STORAGE.containerName}/${key}`\n return {\n id,\n path: key,\n filename,\n size: buffer.length,\n mimetype,\n encoding\n }\n }\n\n STORAGE.deleteFile = async (path: string) => {\n const containerClient = blobServiceClient.getContainerClient(STORAGE.containerName)\n const blockBlobClient = containerClient.getBlockBlobClient(path)\n await blockBlobClient.deleteIfExists()\n }\n\n /* TODO Streaming to Streaming 으로 구현하라. */\n STORAGE.sendFile = async (context, attachment, next) => {\n const containerClient = blobServiceClient.getContainerClient(STORAGE.containerName)\n const blockBlobClient = containerClient.getBlockBlobClient(attachment)\n\n const result = await blockBlobClient.getProperties()\n const response = await blockBlobClient.download(0)\n const body = response.readableStreamBody\n\n context.set({\n 'Content-Length': result.contentLength,\n 'Content-Type': mime.getType(attachment),\n 'Last-Modified': result.lastModified.toUTCString(),\n ETag: result.etag,\n 'Cache-Control': 'public, max-age=31556926'\n })\n\n context.body = body\n }\n\n STORAGE.readFile = async (attachment: string, encoding: string) => {\n const containerClient = blobServiceClient.getContainerClient(STORAGE.containerName)\n const blockBlobClient = containerClient.getBlockBlobClient(attachment)\n\n const response = await blockBlobClient.download(0)\n const body = response.readableStreamBody\n\n const buffer = Buffer.from(await streamToBuffer(body))\n\n switch (encoding) {\n case 'base64':\n return buffer.toString('base64')\n default:\n return buffer\n }\n }\n\n STORAGE.generateUploadURL = async (type: string): Promise<{ url: string; fields: { [key: string]: string } }> => {\n const expiresInMinutes = 1\n const id = crypto.randomUUID()\n\n return {\n url: `${STORAGE.url}/${STORAGE.containerName}/${id}`,\n fields: {}\n }\n }\n\n logger.info('Azure Blob Storage is Ready.')\n}\n\nasync function streamToBuffer(stream): Promise<Buffer> {\n return new Promise<Buffer>((resolve, reject) => {\n const chunks = []\n stream.on('data', chunk => chunks.push(chunk))\n stream.on('end', () => resolve(Buffer.concat(chunks)))\n stream.on('error', reject)\n })\n}\n"]}
1
+ {"version":3,"file":"storage-azure-blob.js","sourceRoot":"","sources":["../server/storage-azure-blob.ts"],"names":[],"mappings":";;AAAA,sDAAuD;AACvD,6CAA4C;AAE5C,yDAAsE;AAEtE,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;AAChC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AAE5B,IAAI,0BAAO,IAAI,0BAAO,CAAC,IAAI,IAAI,WAAW,EAAE,CAAC;IAC3C,MAAM,iBAAiB,GAAG,gCAAiB,CAAC,oBAAoB,CAAC,0BAAO,CAAC,gBAAgB,CAAC,CAAA;IAE1F,iBAAiB;IACjB,0BAAO,CAAC,UAAU,GAAG,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QAC1C,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAI,CAAA;QAErE,MAAM,eAAe,GAAG,iBAAiB,CAAC,kBAAkB,CAAC,0BAAO,CAAC,aAAa,CAAC,CAAA;QACnF,EAAE,GAAG,EAAE,IAAI,MAAM,CAAC,UAAU,EAAE,CAAA;QAC9B,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAA;QACrC,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QAErC,MAAM,eAAe,GAAG,eAAe,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAA;QAC/D,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAA;QACjC,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,CAAA;QAE3C,MAAM,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;YAClD,eAAe,EAAE;gBACf,eAAe,EAAE,QAAQ;aAC1B;SACF,CAAC,CAAA;QAEF,qEAAqE;QACrE,uBAAuB;QACvB,gCAAgC;QAChC,MAAM;QACN,KAAK;QAEL,MAAM,GAAG,GAAG,GAAG,0BAAO,CAAC,GAAG,IAAI,0BAAO,CAAC,aAAa,IAAI,GAAG,EAAE,CAAA;QAC5D,OAAO;YACL,EAAE;YACF,IAAI,EAAE,GAAG;YACT,QAAQ;YACR,IAAI,EAAE,MAAM,CAAC,MAAM;YACnB,QAAQ;YACR,QAAQ;SACT,CAAA;IACH,CAAC,CAAA;IAED,0BAAO,CAAC,UAAU,GAAG,KAAK,EAAE,IAAY,EAAE,EAAE;QAC1C,MAAM,eAAe,GAAG,iBAAiB,CAAC,kBAAkB,CAAC,0BAAO,CAAC,aAAa,CAAC,CAAA;QACnF,MAAM,eAAe,GAAG,eAAe,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAA;QAChE,MAAM,eAAe,CAAC,cAAc,EAAE,CAAA;IACxC,CAAC,CAAA;IAED,0CAA0C;IAC1C,0BAAO,CAAC,QAAQ,GAAG,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE;QACrD,MAAM,eAAe,GAAG,iBAAiB,CAAC,kBAAkB,CAAC,0BAAO,CAAC,aAAa,CAAC,CAAA;QACnF,MAAM,eAAe,GAAG,eAAe,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAA;QAEtE,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,aAAa,EAAE,CAAA;QACpD,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;QAClD,MAAM,IAAI,GAAG,QAAQ,CAAC,kBAAkB,CAAA;QAExC,OAAO,CAAC,GAAG,CAAC;YACV,gBAAgB,EAAE,MAAM,CAAC,aAAa;YACtC,cAAc,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;YACxC,eAAe,EAAE,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE;YAClD,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,eAAe,EAAE,2CAAwB;SAC1C,CAAC,CAAA;QAEF,OAAO,CAAC,IAAI,GAAG,IAAI,CAAA;IACrB,CAAC,CAAA;IAED,0BAAO,CAAC,QAAQ,GAAG,KAAK,EAAE,UAAkB,EAAE,QAAgB,EAAE,EAAE;QAChE,MAAM,eAAe,GAAG,iBAAiB,CAAC,kBAAkB,CAAC,0BAAO,CAAC,aAAa,CAAC,CAAA;QACnF,MAAM,eAAe,GAAG,eAAe,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAA;QAEtE,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;QAClD,MAAM,IAAI,GAAG,QAAQ,CAAC,kBAAkB,CAAA;QAExC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC,CAAA;QAEtD,QAAQ,QAAQ,EAAE,CAAC;YACjB,KAAK,QAAQ;gBACX,OAAO,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;YAClC;gBACE,OAAO,MAAM,CAAA;QACjB,CAAC;IACH,CAAC,CAAA;IAED,0BAAO,CAAC,iBAAiB,GAAG,KAAK,EAAE,IAAY,EAA+D,EAAE;QAC9G,MAAM,gBAAgB,GAAG,CAAC,CAAA;QAC1B,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,EAAE,CAAA;QAE9B,OAAO;YACL,GAAG,EAAE,GAAG,0BAAO,CAAC,GAAG,IAAI,0BAAO,CAAC,aAAa,IAAI,EAAE,EAAE;YACpD,MAAM,EAAE,EAAE;SACX,CAAA;IACH,CAAC,CAAA;IAED,YAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAA;AAC7C,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,MAAM;IAClC,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC7C,MAAM,MAAM,GAAG,EAAE,CAAA;QACjB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;QAC9C,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;QACtD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IAC5B,CAAC,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import { BlobServiceClient } from '@azure/storage-blob'\nimport { logger } from '@things-factory/env'\n\nimport { ATTACHMENT_CACHE_CONTROL, STORAGE } from './attachment-const'\n\nconst crypto = require('crypto')\nconst mime = require('mime')\n\nif (STORAGE && STORAGE.type == 'azureblob') {\n const blobServiceClient = BlobServiceClient.fromConnectionString(STORAGE.connectionString)\n\n /* upload file */\n STORAGE.uploadFile = async ({ id, file }) => {\n const { createReadStream, filename, mimetype, encoding } = await file\n\n const containerClient = blobServiceClient.getContainerClient(STORAGE.containerName)\n id = id || crypto.randomUUID()\n const ext = filename.split('.').pop()\n const key = ext ? `${id}.${ext}` : id\n\n const blockBlobClient = containerClient.getBlockBlobClient(key)\n const stream = createReadStream()\n const buffer = await streamToBuffer(stream)\n\n await blockBlobClient.upload(buffer, buffer.length, {\n blobHTTPHeaders: {\n blobContentType: mimetype\n }\n })\n\n // await blockBlobClient.uploadStream(stream, undefined, undefined, {\n // blobHTTPHeaders: {\n // blobContentType: mimetype\n // }\n // })\n\n const url = `${STORAGE.url}/${STORAGE.containerName}/${key}`\n return {\n id,\n path: key,\n filename,\n size: buffer.length,\n mimetype,\n encoding\n }\n }\n\n STORAGE.deleteFile = async (path: string) => {\n const containerClient = blobServiceClient.getContainerClient(STORAGE.containerName)\n const blockBlobClient = containerClient.getBlockBlobClient(path)\n await blockBlobClient.deleteIfExists()\n }\n\n /* TODO Streaming to Streaming 으로 구현하라. */\n STORAGE.sendFile = async (context, attachment, next) => {\n const containerClient = blobServiceClient.getContainerClient(STORAGE.containerName)\n const blockBlobClient = containerClient.getBlockBlobClient(attachment)\n\n const result = await blockBlobClient.getProperties()\n const response = await blockBlobClient.download(0)\n const body = response.readableStreamBody\n\n context.set({\n 'Content-Length': result.contentLength,\n 'Content-Type': mime.getType(attachment),\n 'Last-Modified': result.lastModified.toUTCString(),\n ETag: result.etag,\n 'Cache-Control': ATTACHMENT_CACHE_CONTROL\n })\n\n context.body = body\n }\n\n STORAGE.readFile = async (attachment: string, encoding: string) => {\n const containerClient = blobServiceClient.getContainerClient(STORAGE.containerName)\n const blockBlobClient = containerClient.getBlockBlobClient(attachment)\n\n const response = await blockBlobClient.download(0)\n const body = response.readableStreamBody\n\n const buffer = Buffer.from(await streamToBuffer(body))\n\n switch (encoding) {\n case 'base64':\n return buffer.toString('base64')\n default:\n return buffer\n }\n }\n\n STORAGE.generateUploadURL = async (type: string): Promise<{ url: string; fields: { [key: string]: string } }> => {\n const expiresInMinutes = 1\n const id = crypto.randomUUID()\n\n return {\n url: `${STORAGE.url}/${STORAGE.containerName}/${id}`,\n fields: {}\n }\n }\n\n logger.info('Azure Blob Storage is Ready.')\n}\n\nasync function streamToBuffer(stream): Promise<Buffer> {\n return new Promise<Buffer>((resolve, reject) => {\n const chunks = []\n stream.on('data', chunk => chunks.push(chunk))\n stream.on('end', () => resolve(Buffer.concat(chunks)))\n stream.on('error', reject)\n })\n}\n"]}
@@ -72,7 +72,7 @@ if (attachment_const_1.STORAGE && attachment_const_1.STORAGE.type == 's3') {
72
72
  'Content-Type': mime.getType(attachment),
73
73
  'Last-Modified': result.LastModified.toUTCString(),
74
74
  ETag: result.ETag,
75
- 'Cache-Control': 'public, max-age=31556926'
75
+ 'Cache-Control': attachment_const_1.ATTACHMENT_CACHE_CONTROL
76
76
  });
77
77
  context.body = result.Body;
78
78
  };
@@ -1 +1 @@
1
- {"version":3,"file":"storage-s3.js","sourceRoot":"","sources":["../server/storage-s3.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Readable } from 'stream'\n\nimport {\n DeleteObjectCommand,\n GetObjectCommand,\n GetObjectCommandInput,\n HeadObjectCommand,\n S3Client\n} from '@aws-sdk/client-s3'\nimport { Upload } from '@aws-sdk/lib-storage'\nimport { createPresignedPost } from '@aws-sdk/s3-presigned-post'\nimport { logger } from '@things-factory/env'\n\nimport { STORAGE } from './attachment-const'\n\nconst crypto = require('crypto')\nconst mime = require('mime')\n\nif (STORAGE && STORAGE.type == 's3') {\n const client = new S3Client({\n credentials: {\n accessKeyId: STORAGE.accessKeyId,\n secretAccessKey: STORAGE.secretAccessKey\n },\n region: STORAGE.region\n })\n\n const streamToBuffer = (stream: Readable) =>\n new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = []\n stream.on('data', chunk => chunks.push(chunk))\n stream.once('end', () => resolve(Buffer.concat(chunks)))\n stream.once('error', reject)\n })\n\n /* upload file */\n STORAGE.uploadFile = async ({ id, file }) => {\n var { createReadStream, filename, mimetype, encoding } = await file\n filename = Buffer.from(filename, 'latin1')\n .toString('utf-8')\n .normalize('NFC') /* Because busboy uses latin1 encoding */\n\n const stream = createReadStream()\n id = id || crypto.randomUUID()\n const ext = filename.split('.').pop()\n const key = ext ? `${id}.${ext}` : id\n\n const upload = new Upload({\n client,\n params: {\n Bucket: STORAGE.bucketName,\n Key: key,\n Body: stream\n }\n })\n\n await upload.done()\n\n const headObjectCommand = new HeadObjectCommand({\n Bucket: STORAGE.bucketName,\n Key: key\n })\n\n const { ContentLength } = await client.send(headObjectCommand)\n\n return {\n id,\n path: key,\n filename,\n size: ContentLength,\n mimetype,\n encoding\n }\n }\n\n STORAGE.deleteFile = async (path: string) => {\n const command = new DeleteObjectCommand({\n Bucket: STORAGE.bucketName,\n Key: path\n })\n\n return await client.send(command)\n }\n\n /* TODO Streaming to Streaming 으로 구현하라. */\n STORAGE.sendFile = async (context, attachment, next) => {\n const result = await client.send(\n new GetObjectCommand({\n Bucket: STORAGE.bucketName,\n Key: attachment\n } as GetObjectCommandInput)\n )\n\n context.set({\n 'Content-Length': result.ContentLength,\n 'Content-Type': mime.getType(attachment),\n 'Last-Modified': result.LastModified.toUTCString(),\n ETag: result.ETag,\n 'Cache-Control': 'public, max-age=31556926'\n })\n\n context.body = result.Body\n }\n\n STORAGE.readFile = async (attachment: string, encoding: string) => {\n /*\n * refered to\n * https://transang.me/modern-fetch-and-how-to-get-buffer-output-from-aws-sdk-v3-getobjectcommand/#the-body-type\n */\n const result = await client.send(\n new GetObjectCommand({\n Bucket: STORAGE.bucketName,\n Key: attachment\n } as GetObjectCommandInput)\n )\n\n var body = result.Body as Readable\n var buffer = await streamToBuffer(body)\n\n switch (encoding) {\n case 'base64':\n return buffer.toString('base64')\n default:\n return await buffer\n }\n }\n\n STORAGE.generateUploadURL = async (type: string): Promise<{ url: string; fields: { [key: string]: string } }> => {\n const expiresInMinutes = 1\n const id = crypto.randomUUID()\n\n return await createPresignedPost(client, {\n Bucket: STORAGE.bucketName,\n Key: id,\n Expires: expiresInMinutes * 60,\n Conditions: [['eq', '$Content-Type', type]]\n })\n }\n\n logger.info('S3 Bucket Storage is Ready.')\n}\n"]}
1
+ {"version":3,"file":"storage-s3.js","sourceRoot":"","sources":["../server/storage-s3.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Readable } from 'stream'\n\nimport {\n DeleteObjectCommand,\n GetObjectCommand,\n GetObjectCommandInput,\n HeadObjectCommand,\n S3Client\n} from '@aws-sdk/client-s3'\nimport { Upload } from '@aws-sdk/lib-storage'\nimport { createPresignedPost } from '@aws-sdk/s3-presigned-post'\nimport { logger } from '@things-factory/env'\n\nimport { ATTACHMENT_CACHE_CONTROL, STORAGE } from './attachment-const'\n\nconst crypto = require('crypto')\nconst mime = require('mime')\n\nif (STORAGE && STORAGE.type == 's3') {\n const client = new S3Client({\n credentials: {\n accessKeyId: STORAGE.accessKeyId,\n secretAccessKey: STORAGE.secretAccessKey\n },\n region: STORAGE.region\n })\n\n const streamToBuffer = (stream: Readable) =>\n new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = []\n stream.on('data', chunk => chunks.push(chunk))\n stream.once('end', () => resolve(Buffer.concat(chunks)))\n stream.once('error', reject)\n })\n\n /* upload file */\n STORAGE.uploadFile = async ({ id, file }) => {\n var { createReadStream, filename, mimetype, encoding } = await file\n filename = Buffer.from(filename, 'latin1')\n .toString('utf-8')\n .normalize('NFC') /* Because busboy uses latin1 encoding */\n\n const stream = createReadStream()\n id = id || crypto.randomUUID()\n const ext = filename.split('.').pop()\n const key = ext ? `${id}.${ext}` : id\n\n const upload = new Upload({\n client,\n params: {\n Bucket: STORAGE.bucketName,\n Key: key,\n Body: stream\n }\n })\n\n await upload.done()\n\n const headObjectCommand = new HeadObjectCommand({\n Bucket: STORAGE.bucketName,\n Key: key\n })\n\n const { ContentLength } = await client.send(headObjectCommand)\n\n return {\n id,\n path: key,\n filename,\n size: ContentLength,\n mimetype,\n encoding\n }\n }\n\n STORAGE.deleteFile = async (path: string) => {\n const command = new DeleteObjectCommand({\n Bucket: STORAGE.bucketName,\n Key: path\n })\n\n return await client.send(command)\n }\n\n /* TODO Streaming to Streaming 으로 구현하라. */\n STORAGE.sendFile = async (context, attachment, next) => {\n const result = await client.send(\n new GetObjectCommand({\n Bucket: STORAGE.bucketName,\n Key: attachment\n } as GetObjectCommandInput)\n )\n\n context.set({\n 'Content-Length': result.ContentLength,\n 'Content-Type': mime.getType(attachment),\n 'Last-Modified': result.LastModified.toUTCString(),\n ETag: result.ETag,\n 'Cache-Control': ATTACHMENT_CACHE_CONTROL\n })\n\n context.body = result.Body\n }\n\n STORAGE.readFile = async (attachment: string, encoding: string) => {\n /*\n * refered to\n * https://transang.me/modern-fetch-and-how-to-get-buffer-output-from-aws-sdk-v3-getobjectcommand/#the-body-type\n */\n const result = await client.send(\n new GetObjectCommand({\n Bucket: STORAGE.bucketName,\n Key: attachment\n } as GetObjectCommandInput)\n )\n\n var body = result.Body as Readable\n var buffer = await streamToBuffer(body)\n\n switch (encoding) {\n case 'base64':\n return buffer.toString('base64')\n default:\n return await buffer\n }\n }\n\n STORAGE.generateUploadURL = async (type: string): Promise<{ url: string; fields: { [key: string]: string } }> => {\n const expiresInMinutes = 1\n const id = crypto.randomUUID()\n\n return await createPresignedPost(client, {\n Bucket: STORAGE.bucketName,\n Key: id,\n Expires: expiresInMinutes * 60,\n Conditions: [['eq', '$Content-Type', type]]\n })\n }\n\n logger.info('S3 Bucket Storage is Ready.')\n}\n"]}