@things-factory/attachment-base 8.0.0-alpha.24 → 8.0.0-alpha.26

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.
@@ -14,4 +14,11 @@ Object.defineProperty(exports, "singleUpload", { enumerable: true, get: function
14
14
  require("./routes");
15
15
  tslib_1.__exportStar(require("./attachment-const"), exports);
16
16
  tslib_1.__exportStar(require("./util"), exports);
17
+ const nfc_normalize_1 = require("./nfc-normalize");
18
+ process.on('bootstrap-module-start', async ({ app, config, client }) => {
19
+ /* 이 코드는 기존 첨부파일의 NFC 정규화를 강제로 실행하기 위해서 임시로 작성되었다. */
20
+ (0, nfc_normalize_1.normalizeNamesToNFC)()
21
+ .then(() => console.log('Normalization completed.'))
22
+ .catch(err => console.error('Error during normalization:', err));
23
+ });
17
24
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;;AAAA,6DAAkC;AAClC,oDAAyB;AACzB,gFAOiD;AAN/C,uHAAA,gBAAgB,OAAA;AAChB,wHAAA,iBAAiB,OAAA;AACjB,uHAAA,gBAAgB,OAAA;AAChB,6HAAA,sBAAsB,OAAA;AACtB,qHAAA,cAAc,OAAA;AACd,mHAAA,YAAY,OAAA;AAGd,oBAAiB;AAEjB,6DAAkC;AAClC,iDAAsB","sourcesContent":["export * from './attachment-const'\nexport * from './service'\nexport {\n createAttachment,\n createAttachments,\n deleteAttachment,\n deleteAttachmentsByRef,\n multipleUpload,\n singleUpload\n} from './service/attachment/attachment-mutation'\n\nimport './routes'\n\nexport * from './attachment-const'\nexport * from './util'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;;AAAA,6DAAkC;AAClC,oDAAyB;AACzB,gFAOiD;AAN/C,uHAAA,gBAAgB,OAAA;AAChB,wHAAA,iBAAiB,OAAA;AACjB,uHAAA,gBAAgB,OAAA;AAChB,6HAAA,sBAAsB,OAAA;AACtB,qHAAA,cAAc,OAAA;AACd,mHAAA,YAAY,OAAA;AAGd,oBAAiB;AAEjB,6DAAkC;AAClC,iDAAsB;AAEtB,mDAAqD;AAErD,OAAO,CAAC,EAAE,CAAC,wBAA+B,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAO,EAAE,EAAE;IACjF,qDAAqD;IACrD,IAAA,mCAAmB,GAAE;SAClB,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;SACnD,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC,CAAA;AACpE,CAAC,CAAC,CAAA","sourcesContent":["export * from './attachment-const'\nexport * from './service'\nexport {\n createAttachment,\n createAttachments,\n deleteAttachment,\n deleteAttachmentsByRef,\n multipleUpload,\n singleUpload\n} from './service/attachment/attachment-mutation'\n\nimport './routes'\n\nexport * from './attachment-const'\nexport * from './util'\n\nimport { normalizeNamesToNFC } from './nfc-normalize'\n\nprocess.on('bootstrap-module-start' as any, async ({ app, config, client }: any) => {\n /* 이 코드는 기존 첨부파일의 NFC 정규화를 강제로 실행하기 위해서 임시로 작성되었다. */\n normalizeNamesToNFC()\n .then(() => console.log('Normalization completed.'))\n .catch(err => console.error('Error during normalization:', err))\n})\n"]}
@@ -0,0 +1 @@
1
+ export declare function normalizeNamesToNFC(): Promise<void>;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeNamesToNFC = normalizeNamesToNFC;
4
+ const shell_1 = require("@things-factory/shell");
5
+ const attachment_1 = require("./service/attachment/attachment");
6
+ async function normalizeNamesToNFC() {
7
+ const attachmentRepository = (0, shell_1.getRepository)(attachment_1.Attachment);
8
+ // 모든 Attachment 항목을 조회
9
+ const attachments = await attachmentRepository.find();
10
+ // NFD로 저장된 name을 NFC로 변환하여 업데이트
11
+ for (const attachment of attachments) {
12
+ if (attachment.name) {
13
+ const normalizedName = attachment.name.normalize('NFC');
14
+ // name이 NFD로 저장된 경우만 업데이트
15
+ if (attachment.name !== normalizedName) {
16
+ attachment.name = normalizedName;
17
+ await attachmentRepository.save(attachment);
18
+ console.log(`Updated name for attachment ID ${attachment.id}: ${normalizedName}`);
19
+ }
20
+ }
21
+ }
22
+ console.log('All names have been normalized to NFC.');
23
+ }
24
+ //# sourceMappingURL=nfc-normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nfc-normalize.js","sourceRoot":"","sources":["../server/nfc-normalize.ts"],"names":[],"mappings":";;AAGA,kDAqBC;AAxBD,iDAAqD;AACrD,gEAA4D;AAErD,KAAK,UAAU,mBAAmB;IACvC,MAAM,oBAAoB,GAAG,IAAA,qBAAa,EAAC,uBAAU,CAAC,CAAA;IAEtD,uBAAuB;IACvB,MAAM,WAAW,GAAG,MAAM,oBAAoB,CAAC,IAAI,EAAE,CAAA;IAErD,gCAAgC;IAChC,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,IAAI,UAAU,CAAC,IAAI,EAAE,CAAC;YACpB,MAAM,cAAc,GAAG,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;YAEvD,0BAA0B;YAC1B,IAAI,UAAU,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBACvC,UAAU,CAAC,IAAI,GAAG,cAAc,CAAA;gBAChC,MAAM,oBAAoB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;gBAC3C,OAAO,CAAC,GAAG,CAAC,kCAAkC,UAAU,CAAC,EAAE,KAAK,cAAc,EAAE,CAAC,CAAA;YACnF,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAA;AACvD,CAAC","sourcesContent":["import { getRepository } from '@things-factory/shell'\nimport { Attachment } from './service/attachment/attachment'\n\nexport async function normalizeNamesToNFC() {\n const attachmentRepository = getRepository(Attachment)\n\n // 모든 Attachment 항목을 조회\n const attachments = await attachmentRepository.find()\n\n // NFD로 저장된 name을 NFC로 변환하여 업데이트\n for (const attachment of attachments) {\n if (attachment.name) {\n const normalizedName = attachment.name.normalize('NFC')\n\n // name이 NFD로 저장된 경우만 업데이트\n if (attachment.name !== normalizedName) {\n attachment.name = normalizedName\n await attachmentRepository.save(attachment)\n console.log(`Updated name for attachment ID ${attachment.id}: ${normalizedName}`)\n }\n }\n }\n\n console.log('All names have been normalized to NFC.')\n}\n"]}
@@ -16,7 +16,7 @@ let AttachmentQuery = class AttachmentQuery {
16
16
  params,
17
17
  domain,
18
18
  alias: 'attachment',
19
- searchables: ['name', 'description', 'tags']
19
+ searchables: ['id', 'name', 'description', 'tags']
20
20
  });
21
21
  const [items, total] = await queryBuilder.getManyAndCount();
22
22
  return { items, total };
@@ -1 +1 @@
1
- {"version":3,"file":"attachment-query.js","sourceRoot":"","sources":["../../../server/service/attachment/attachment-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { In } from 'typeorm'\nimport { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { User } from '@things-factory/auth-base'\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'\n\nimport { AttachmentList } from '../'\nimport { Attachment } from './attachment'\n\n@Resolver(Attachment)\nexport class AttachmentQuery {\n @Directive('@privilege(category: \"attachment\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => AttachmentList)\n async attachments(\n @Ctx() context: ResolverContext,\n @Args(type => ListParam) params: ListParam\n ): Promise<AttachmentList> {\n const { domain } = context.state\n\n const queryBuilder = getQueryBuilderFromListParams({\n repository: await getRepository(Attachment),\n params,\n domain,\n alias: 'attachment',\n searchables: ['name', 'description', 'tags']\n })\n\n const [items, total] = await queryBuilder.getManyAndCount()\n\n return { items, total }\n }\n\n @Directive('@privilege(category: \"attachment\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => Attachment)\n async attachment(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Attachment> {\n const { domain } = context.state\n\n return await getRepository(Attachment).findOne({\n select: [\n 'domain',\n 'id',\n 'name',\n 'path',\n 'size',\n 'mimetype',\n 'encoding',\n 'category',\n 'updatedAt',\n 'updater',\n 'createdAt',\n 'creator'\n ],\n where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id },\n relations: ['domain', 'creator', 'updater']\n })\n }\n\n @FieldResolver(type => Domain)\n async domain(@Root() attachment: Attachment) {\n return await getRepository(Domain).findOneBy({\n id: attachment.domainId\n })\n }\n\n @FieldResolver(type => User)\n async updater(@Root() attachment: Attachment): Promise<User> {\n return await getRepository(User).findOneBy({\n id: attachment.updaterId\n })\n }\n\n @FieldResolver(type => User)\n async creator(@Root() attachment: Attachment): Promise<User> {\n return await getRepository(User).findOneBy({\n id: attachment.creatorId\n })\n }\n}\n"]}
1
+ {"version":3,"file":"attachment-query.js","sourceRoot":"","sources":["../../../server/service/attachment/attachment-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { In } from 'typeorm'\nimport { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { User } from '@things-factory/auth-base'\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'\n\nimport { AttachmentList } from '../'\nimport { Attachment } from './attachment'\n\n@Resolver(Attachment)\nexport class AttachmentQuery {\n @Directive('@privilege(category: \"attachment\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => AttachmentList)\n async attachments(\n @Ctx() context: ResolverContext,\n @Args(type => ListParam) params: ListParam\n ): Promise<AttachmentList> {\n const { domain } = context.state\n\n const queryBuilder = getQueryBuilderFromListParams({\n repository: await getRepository(Attachment),\n params,\n domain,\n alias: 'attachment',\n searchables: ['id', 'name', 'description', 'tags']\n })\n\n const [items, total] = await queryBuilder.getManyAndCount()\n\n return { items, total }\n }\n\n @Directive('@privilege(category: \"attachment\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => Attachment)\n async attachment(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Attachment> {\n const { domain } = context.state\n\n return await getRepository(Attachment).findOne({\n select: [\n 'domain',\n 'id',\n 'name',\n 'path',\n 'size',\n 'mimetype',\n 'encoding',\n 'category',\n 'updatedAt',\n 'updater',\n 'createdAt',\n 'creator'\n ],\n where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id },\n relations: ['domain', 'creator', 'updater']\n })\n }\n\n @FieldResolver(type => Domain)\n async domain(@Root() attachment: Attachment) {\n return await getRepository(Domain).findOneBy({\n id: attachment.domainId\n })\n }\n\n @FieldResolver(type => User)\n async updater(@Root() attachment: Attachment): Promise<User> {\n return await getRepository(User).findOneBy({\n id: attachment.updaterId\n })\n }\n\n @FieldResolver(type => User)\n async creator(@Root() attachment: Attachment): Promise<User> {\n return await getRepository(User).findOneBy({\n id: attachment.creatorId\n })\n }\n}\n"]}
@@ -11,7 +11,9 @@ if (attachment_const_1.STORAGE && attachment_const_1.STORAGE.type == 'database')
11
11
  attachment_const_1.STORAGE.uploadFile = async ({ id, file, context }) => {
12
12
  var _a, e_1, _b, _c;
13
13
  var { createReadStream, filename, mimetype, encoding } = await file;
14
- filename = Buffer.from(filename, 'latin1').toString('utf-8'); /* Because busboy uses latin1 encoding */
14
+ filename = Buffer.from(filename, 'latin1')
15
+ .toString('utf-8')
16
+ .normalize('NFC'); /* Because busboy uses latin1 encoding */
15
17
  const stream = createReadStream();
16
18
  const chunks = [];
17
19
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"storage-database.js","sourceRoot":"","sources":["../server/storage-database.ts"],"names":[],"mappings":"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","sourcesContent":["import contentDisposition from 'content-disposition'\n\nimport { logger } from '@things-factory/env'\nimport { getRepository } from '@things-factory/shell'\n\nimport { Attachment } from './service/attachment/attachment'\nimport { ATTACHMENT_PATH, STORAGE } from './attachment-const'\n\nconst crypto = require('crypto')\n\nif (STORAGE && STORAGE.type == 'database') {\n STORAGE.uploadFile = async ({ id, file, context }) => {\n var { createReadStream, filename, mimetype, encoding } = await file\n filename = Buffer.from(filename, 'latin1').toString('utf-8') /* Because busboy uses latin1 encoding */\n\n const stream = createReadStream()\n\n const chunks: Buffer[] = []\n for await (const chunk of stream) {\n if (chunk instanceof Buffer) {\n chunks.push(chunk)\n }\n }\n\n id = id || crypto.randomUUID()\n const ext = filename.split('.').pop()\n const path = ext ? `${id}.${ext}` : id\n\n const contents = Buffer.concat(chunks)\n\n return {\n id,\n filename,\n mimetype,\n encoding,\n contents,\n path,\n size: contents.length\n }\n }\n\n STORAGE.deleteFile = async path => {}\n\n STORAGE.sendFile = async (context, attachment, next) => {\n const id = attachment.split('.')[0]\n\n const entity = await getRepository(Attachment).findOne({\n select: ['name', 'contents'],\n where: { id }\n })\n\n context.set('Content-Disposition', contentDisposition(entity.name))\n context.body = entity.contents\n context.type = entity.mimetype\n }\n\n STORAGE.readFile = async (attachment, encoding) => {\n const id = attachment.split('.')[0]\n\n const entity = await getRepository(Attachment).findOne({\n select: ['name', 'contents'],\n where: { id }\n })\n\n return await entity.contents\n }\n\n STORAGE.generateUploadURL = async (type: string): Promise<{ url: string; fields: { [key: string]: string } }> => {\n const id = crypto.randomUUID()\n\n return await {\n url: `/${ATTACHMENT_PATH}`,\n fields: {}\n }\n }\n\n logger.info('File Storage is Ready.')\n}\n"]}
1
+ {"version":3,"file":"storage-database.js","sourceRoot":"","sources":["../server/storage-database.ts"],"names":[],"mappings":"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","sourcesContent":["import contentDisposition from 'content-disposition'\n\nimport { logger } from '@things-factory/env'\nimport { getRepository } from '@things-factory/shell'\n\nimport { Attachment } from './service/attachment/attachment'\nimport { ATTACHMENT_PATH, STORAGE } from './attachment-const'\n\nconst crypto = require('crypto')\n\nif (STORAGE && STORAGE.type == 'database') {\n STORAGE.uploadFile = async ({ id, file, context }) => {\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\n const chunks: Buffer[] = []\n for await (const chunk of stream) {\n if (chunk instanceof Buffer) {\n chunks.push(chunk)\n }\n }\n\n id = id || crypto.randomUUID()\n const ext = filename.split('.').pop()\n const path = ext ? `${id}.${ext}` : id\n\n const contents = Buffer.concat(chunks)\n\n return {\n id,\n filename,\n mimetype,\n encoding,\n contents,\n path,\n size: contents.length\n }\n }\n\n STORAGE.deleteFile = async path => {}\n\n STORAGE.sendFile = async (context, attachment, next) => {\n const id = attachment.split('.')[0]\n\n const entity = await getRepository(Attachment).findOne({\n select: ['name', 'contents'],\n where: { id }\n })\n\n context.set('Content-Disposition', contentDisposition(entity.name))\n context.body = entity.contents\n context.type = entity.mimetype\n }\n\n STORAGE.readFile = async (attachment, encoding) => {\n const id = attachment.split('.')[0]\n\n const entity = await getRepository(Attachment).findOne({\n select: ['name', 'contents'],\n where: { id }\n })\n\n return await entity.contents\n }\n\n STORAGE.generateUploadURL = async (type: string): Promise<{ url: string; fields: { [key: string]: string } }> => {\n const id = crypto.randomUUID()\n\n return await {\n url: `/${ATTACHMENT_PATH}`,\n fields: {}\n }\n }\n\n logger.info('File Storage is Ready.')\n}\n"]}
@@ -12,7 +12,9 @@ if (attachment_const_1.STORAGE && attachment_const_1.STORAGE.type == 'file') {
12
12
  const uploadDir = env_1.config.getPath(null, attachment_const_1.STORAGE.base || 'attachments');
13
13
  attachment_const_1.STORAGE.uploadFile = async ({ id, file }) => {
14
14
  var { createReadStream, filename, mimetype, encoding } = await file;
15
- filename = Buffer.from(filename, 'latin1').toString('utf-8'); /* Because busboy uses latin1 encoding */
15
+ filename = Buffer.from(filename, 'latin1')
16
+ .toString('utf-8')
17
+ .normalize('NFC'); /* Because busboy uses latin1 encoding */
16
18
  const stream = createReadStream();
17
19
  mkdirp.sync(uploadDir);
18
20
  id = id || crypto.randomUUID();
@@ -1 +1 @@
1
- {"version":3,"file":"storage-file.js","sourceRoot":"","sources":["../server/storage-file.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs'\nimport * as mkdirp from 'mkdirp'\nimport { resolve } from 'path'\n\nimport { config, logger } from '@things-factory/env'\n\nimport { ATTACHMENT_PATH, STORAGE } from './attachment-const'\n\nconst crypto = require('crypto')\nconst send = require('koa-send')\n\nif (STORAGE && STORAGE.type == 'file') {\n const uploadDir = config.getPath(null, STORAGE.base || 'attachments')\n\n STORAGE.uploadFile = async ({ id, file }) => {\n var { createReadStream, filename, mimetype, encoding } = await file\n filename = Buffer.from(filename, 'latin1').toString('utf-8') /* Because busboy uses latin1 encoding */\n\n const stream = createReadStream()\n\n mkdirp.sync(uploadDir)\n\n id = id || crypto.randomUUID()\n const ext = filename.split('.').pop()\n const path = ext ? resolve(uploadDir, `${id}.${ext}`) : resolve(uploadDir, id)\n const relativePath = path.split('\\\\').pop().split('/').pop()\n var size: number = 0\n\n return new Promise<{\n id: string\n filename: string\n path: string\n size: number\n mimetype: string\n encoding: string\n }>((resolve, reject) =>\n stream\n .on('error', error => {\n if (stream.truncated)\n // Delete the truncated file\n fs.unlinkSync(path)\n reject(error)\n })\n .on('data', chunk => {\n size += chunk.length\n })\n .pipe(fs.createWriteStream(path))\n .on('finish', () => resolve({ id, filename, path: relativePath, size, mimetype, encoding }))\n )\n }\n\n STORAGE.deleteFile = async path => {\n const fullpath = resolve(uploadDir, path)\n\n await fs.unlink(fullpath, logger.error)\n }\n\n STORAGE.sendFile = async (context, attachment, next) => {\n await send(context, attachment, { root: uploadDir })\n }\n\n STORAGE.readFile = (attachment, encoding) => {\n const fullpath = resolve(uploadDir, attachment)\n\n return fs.readFileSync(fullpath, encoding)\n }\n\n STORAGE.generateUploadURL = async (type: string): Promise<{ url: string; fields: { [key: string]: string } }> => {\n const id = crypto.randomUUID()\n\n return await {\n url: `/${ATTACHMENT_PATH}`,\n fields: {}\n }\n }\n\n logger.info('File Storage is Ready.')\n}\n"]}
1
+ {"version":3,"file":"storage-file.js","sourceRoot":"","sources":["../server/storage-file.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs'\nimport * as mkdirp from 'mkdirp'\nimport { resolve } from 'path'\n\nimport { config, logger } from '@things-factory/env'\n\nimport { ATTACHMENT_PATH, STORAGE } from './attachment-const'\n\nconst crypto = require('crypto')\nconst send = require('koa-send')\n\nif (STORAGE && STORAGE.type == 'file') {\n const uploadDir = config.getPath(null, STORAGE.base || 'attachments')\n\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\n mkdirp.sync(uploadDir)\n\n id = id || crypto.randomUUID()\n const ext = filename.split('.').pop()\n const path = ext ? resolve(uploadDir, `${id}.${ext}`) : resolve(uploadDir, id)\n const relativePath = path.split('\\\\').pop().split('/').pop()\n var size: number = 0\n\n return new Promise<{\n id: string\n filename: string\n path: string\n size: number\n mimetype: string\n encoding: string\n }>((resolve, reject) =>\n stream\n .on('error', error => {\n if (stream.truncated)\n // Delete the truncated file\n fs.unlinkSync(path)\n reject(error)\n })\n .on('data', chunk => {\n size += chunk.length\n })\n .pipe(fs.createWriteStream(path))\n .on('finish', () => resolve({ id, filename, path: relativePath, size, mimetype, encoding }))\n )\n }\n\n STORAGE.deleteFile = async path => {\n const fullpath = resolve(uploadDir, path)\n\n await fs.unlink(fullpath, logger.error)\n }\n\n STORAGE.sendFile = async (context, attachment, next) => {\n await send(context, attachment, { root: uploadDir })\n }\n\n STORAGE.readFile = (attachment, encoding) => {\n const fullpath = resolve(uploadDir, attachment)\n\n return fs.readFileSync(fullpath, encoding)\n }\n\n STORAGE.generateUploadURL = async (type: string): Promise<{ url: string; fields: { [key: string]: string } }> => {\n const id = crypto.randomUUID()\n\n return await {\n url: `/${ATTACHMENT_PATH}`,\n fields: {}\n }\n }\n\n logger.info('File Storage is Ready.')\n}\n"]}
@@ -24,7 +24,9 @@ if (attachment_const_1.STORAGE && attachment_const_1.STORAGE.type == 's3') {
24
24
  /* upload file */
25
25
  attachment_const_1.STORAGE.uploadFile = async ({ id, file }) => {
26
26
  var { createReadStream, filename, mimetype, encoding } = await file;
27
- filename = Buffer.from(filename, 'latin1').toString('utf-8'); /* Because busboy uses latin1 encoding */
27
+ filename = Buffer.from(filename, 'latin1')
28
+ .toString('utf-8')
29
+ .normalize('NFC'); /* Because busboy uses latin1 encoding */
28
30
  const stream = createReadStream();
29
31
  id = id || crypto.randomUUID();
30
32
  const ext = filename.split('.').pop();
@@ -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').toString('utf-8') /* 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 { 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"]}