@things-factory/attachment-base 8.0.0 → 9.0.0-beta.3
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-server/index.js +7 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/nfc-normalize.d.ts +1 -0
- package/dist-server/nfc-normalize.js +24 -0
- package/dist-server/nfc-normalize.js.map +1 -0
- package/dist-server/storage-database.js +3 -1
- package/dist-server/storage-database.js.map +1 -1
- package/dist-server/storage-file.js +3 -1
- package/dist-server/storage-file.js.map +1 -1
- package/dist-server/storage-s3.js +3 -1
- package/dist-server/storage-s3.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/server/attachment-const.ts +0 -5
- package/server/awb-storage-s3.ts +0 -44
- package/server/index.ts +0 -15
- package/server/routes.ts +0 -35
- package/server/service/attachment/attachment-mutation.ts +0 -343
- package/server/service/attachment/attachment-query.ts +0 -78
- package/server/service/attachment/attachment-types.ts +0 -76
- package/server/service/attachment/attachment.ts +0 -129
- package/server/service/attachment/index.ts +0 -6
- package/server/service/index.ts +0 -19
- package/server/storage-azure-blob.ts +0 -111
- package/server/storage-database.ts +0 -78
- package/server/storage-file.ts +0 -78
- package/server/storage-s3.ts +0 -139
- package/server/util/index.ts +0 -1
- package/server/util/upload-awb.ts +0 -11
@@ -1,129 +0,0 @@
|
|
1
|
-
import { Field, ID, ObjectType } from 'type-graphql'
|
2
|
-
import {
|
3
|
-
Column,
|
4
|
-
CreateDateColumn,
|
5
|
-
Entity,
|
6
|
-
Index,
|
7
|
-
ManyToOne,
|
8
|
-
PrimaryGeneratedColumn,
|
9
|
-
RelationId,
|
10
|
-
UpdateDateColumn
|
11
|
-
} from 'typeorm'
|
12
|
-
|
13
|
-
import { User } from '@things-factory/auth-base'
|
14
|
-
import { Domain, ScalarObject } from '@things-factory/shell'
|
15
|
-
|
16
|
-
import { ATTACHMENT_PATH } from '../../attachment-const'
|
17
|
-
import { config } from '@things-factory/env'
|
18
|
-
|
19
|
-
const ORMCONFIG = config.get('ormconfig', {})
|
20
|
-
const DATABASE_TYPE = ORMCONFIG.type
|
21
|
-
|
22
|
-
@Entity()
|
23
|
-
@Index('ix_attachment_0', (attachment: Attachment) => [attachment.domain, attachment.name], { unique: false })
|
24
|
-
@Index('ix_attachment_1', (attachment: Attachment) => [attachment.domain, attachment.category, attachment.name], {
|
25
|
-
unique: false
|
26
|
-
})
|
27
|
-
@Index('ix_attachment_2', (attachment: Attachment) => [attachment.domain, attachment.refBy], {
|
28
|
-
unique: false
|
29
|
-
})
|
30
|
-
@Index('ix_attachment_3', (attachment: Attachment) => [attachment.domain, attachment.refType, attachment.refBy], {
|
31
|
-
unique: false
|
32
|
-
})
|
33
|
-
@ObjectType()
|
34
|
-
export class Attachment {
|
35
|
-
@PrimaryGeneratedColumn('uuid')
|
36
|
-
@Field(type => ID)
|
37
|
-
readonly id?: string
|
38
|
-
|
39
|
-
@ManyToOne(type => Domain, { nullable: false })
|
40
|
-
@Field(type => Domain)
|
41
|
-
domain?: Domain
|
42
|
-
|
43
|
-
@RelationId((attachment: Attachment) => attachment.domain)
|
44
|
-
domainId?: string
|
45
|
-
|
46
|
-
@Column()
|
47
|
-
@Field()
|
48
|
-
name?: string
|
49
|
-
|
50
|
-
@Column({ nullable: true })
|
51
|
-
@Field({ nullable: true })
|
52
|
-
description?: string
|
53
|
-
|
54
|
-
@Column()
|
55
|
-
@Field()
|
56
|
-
mimetype?: string
|
57
|
-
|
58
|
-
@Column()
|
59
|
-
@Field()
|
60
|
-
encoding?: string
|
61
|
-
|
62
|
-
@Column({ nullable: true })
|
63
|
-
@Field({ nullable: true })
|
64
|
-
category?: string
|
65
|
-
|
66
|
-
@Column({ nullable: true, default: '' })
|
67
|
-
@Field({ nullable: true })
|
68
|
-
refType?: string = ''
|
69
|
-
|
70
|
-
@Column({ nullable: true })
|
71
|
-
@Field({ nullable: true })
|
72
|
-
refBy?: string
|
73
|
-
|
74
|
-
@Column()
|
75
|
-
@Field()
|
76
|
-
path?: string
|
77
|
-
|
78
|
-
@Column({
|
79
|
-
nullable: true,
|
80
|
-
type: DATABASE_TYPE == 'mssql' ? 'bigint' : undefined
|
81
|
-
})
|
82
|
-
@Field()
|
83
|
-
size?: string
|
84
|
-
|
85
|
-
@Column({
|
86
|
-
nullable: true,
|
87
|
-
type:
|
88
|
-
DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'
|
89
|
-
? 'longblob'
|
90
|
-
: DATABASE_TYPE == 'postgres'
|
91
|
-
? 'bytea'
|
92
|
-
: DATABASE_TYPE == 'mssql'
|
93
|
-
? 'varbinary'
|
94
|
-
: 'blob',
|
95
|
-
length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined
|
96
|
-
})
|
97
|
-
contents?: Buffer
|
98
|
-
|
99
|
-
@Column('simple-json', { nullable: true, default: null })
|
100
|
-
@Field(type => ScalarObject, { nullable: true })
|
101
|
-
tags?: string[]
|
102
|
-
|
103
|
-
@CreateDateColumn()
|
104
|
-
@Field()
|
105
|
-
createdAt?: Date
|
106
|
-
|
107
|
-
@UpdateDateColumn()
|
108
|
-
@Field()
|
109
|
-
updatedAt?: Date
|
110
|
-
|
111
|
-
@ManyToOne(type => User)
|
112
|
-
@Field(type => User, { nullable: true })
|
113
|
-
creator?: User
|
114
|
-
|
115
|
-
@RelationId((attachment: Attachment) => attachment.creator)
|
116
|
-
creatorId?: string
|
117
|
-
|
118
|
-
@ManyToOne(type => User)
|
119
|
-
@Field(type => User, { nullable: true })
|
120
|
-
updater?: User
|
121
|
-
|
122
|
-
@RelationId((attachment: Attachment) => attachment.updater)
|
123
|
-
updaterId?: string
|
124
|
-
|
125
|
-
@Field()
|
126
|
-
get fullpath(): string {
|
127
|
-
return `/${ATTACHMENT_PATH}/${this.path}`
|
128
|
-
}
|
129
|
-
}
|
package/server/service/index.ts
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
/* IMPORT ENTITIES AND RESOLVERS */
|
2
|
-
import { entities as AttachmentEntity, resolvers as AttachmentResolvers } from './attachment'
|
3
|
-
|
4
|
-
/* EXPORT ENTITY TYPES */
|
5
|
-
export * from './attachment/attachment'
|
6
|
-
/* EXPORT TYPES */
|
7
|
-
export * from './attachment/attachment-types'
|
8
|
-
|
9
|
-
export const entities = [
|
10
|
-
/* ENTITIES */
|
11
|
-
...AttachmentEntity
|
12
|
-
]
|
13
|
-
|
14
|
-
export const schema = {
|
15
|
-
resolverClasses: [
|
16
|
-
/* RESOLVER CLASSES */
|
17
|
-
...AttachmentResolvers
|
18
|
-
]
|
19
|
-
}
|
@@ -1,111 +0,0 @@
|
|
1
|
-
import { BlobServiceClient } from '@azure/storage-blob'
|
2
|
-
import { logger } from '@things-factory/env'
|
3
|
-
|
4
|
-
import { STORAGE } from './attachment-const'
|
5
|
-
|
6
|
-
const crypto = require('crypto')
|
7
|
-
const mime = require('mime')
|
8
|
-
|
9
|
-
if (STORAGE && STORAGE.type == 'azureblob') {
|
10
|
-
const blobServiceClient = BlobServiceClient.fromConnectionString(STORAGE.connectionString)
|
11
|
-
|
12
|
-
/* upload file */
|
13
|
-
STORAGE.uploadFile = async ({ id, file }) => {
|
14
|
-
const { createReadStream, filename, mimetype, encoding } = await file
|
15
|
-
|
16
|
-
const containerClient = blobServiceClient.getContainerClient(STORAGE.containerName)
|
17
|
-
id = id || crypto.randomUUID()
|
18
|
-
const ext = filename.split('.').pop()
|
19
|
-
const key = ext ? `${id}.${ext}` : id
|
20
|
-
|
21
|
-
const blockBlobClient = containerClient.getBlockBlobClient(key)
|
22
|
-
const stream = createReadStream()
|
23
|
-
const buffer = await streamToBuffer(stream)
|
24
|
-
|
25
|
-
await blockBlobClient.upload(buffer, buffer.length, {
|
26
|
-
blobHTTPHeaders: {
|
27
|
-
blobContentType: mimetype
|
28
|
-
}
|
29
|
-
})
|
30
|
-
|
31
|
-
// await blockBlobClient.uploadStream(stream, undefined, undefined, {
|
32
|
-
// blobHTTPHeaders: {
|
33
|
-
// blobContentType: mimetype
|
34
|
-
// }
|
35
|
-
// })
|
36
|
-
|
37
|
-
const url = `${STORAGE.url}/${STORAGE.containerName}/${key}`
|
38
|
-
return {
|
39
|
-
id,
|
40
|
-
path: key,
|
41
|
-
filename,
|
42
|
-
size: buffer.length,
|
43
|
-
mimetype,
|
44
|
-
encoding
|
45
|
-
}
|
46
|
-
}
|
47
|
-
|
48
|
-
STORAGE.deleteFile = async (path: string) => {
|
49
|
-
const containerClient = blobServiceClient.getContainerClient(STORAGE.containerName)
|
50
|
-
const blockBlobClient = containerClient.getBlockBlobClient(path)
|
51
|
-
await blockBlobClient.deleteIfExists()
|
52
|
-
}
|
53
|
-
|
54
|
-
/* TODO Streaming to Streaming 으로 구현하라. */
|
55
|
-
STORAGE.sendFile = async (context, attachment, next) => {
|
56
|
-
const containerClient = blobServiceClient.getContainerClient(STORAGE.containerName)
|
57
|
-
const blockBlobClient = containerClient.getBlockBlobClient(attachment)
|
58
|
-
|
59
|
-
const result = await blockBlobClient.getProperties()
|
60
|
-
const response = await blockBlobClient.download(0)
|
61
|
-
const body = response.readableStreamBody
|
62
|
-
|
63
|
-
context.set({
|
64
|
-
'Content-Length': result.contentLength,
|
65
|
-
'Content-Type': mime.getType(attachment),
|
66
|
-
'Last-Modified': result.lastModified.toUTCString(),
|
67
|
-
ETag: result.etag,
|
68
|
-
'Cache-Control': 'public, max-age=31556926'
|
69
|
-
})
|
70
|
-
|
71
|
-
context.body = body
|
72
|
-
}
|
73
|
-
|
74
|
-
STORAGE.readFile = async (attachment: string, encoding: string) => {
|
75
|
-
const containerClient = blobServiceClient.getContainerClient(STORAGE.containerName)
|
76
|
-
const blockBlobClient = containerClient.getBlockBlobClient(attachment)
|
77
|
-
|
78
|
-
const response = await blockBlobClient.download(0)
|
79
|
-
const body = response.readableStreamBody
|
80
|
-
|
81
|
-
const buffer = Buffer.from(await streamToBuffer(body))
|
82
|
-
|
83
|
-
switch (encoding) {
|
84
|
-
case 'base64':
|
85
|
-
return buffer.toString('base64')
|
86
|
-
default:
|
87
|
-
return buffer
|
88
|
-
}
|
89
|
-
}
|
90
|
-
|
91
|
-
STORAGE.generateUploadURL = async (type: string): Promise<{ url: string; fields: { [key: string]: string } }> => {
|
92
|
-
const expiresInMinutes = 1
|
93
|
-
const id = crypto.randomUUID()
|
94
|
-
|
95
|
-
return {
|
96
|
-
url: `${STORAGE.url}/${STORAGE.containerName}/${id}`,
|
97
|
-
fields: {}
|
98
|
-
}
|
99
|
-
}
|
100
|
-
|
101
|
-
logger.info('Azure Blob Storage is Ready.')
|
102
|
-
}
|
103
|
-
|
104
|
-
async function streamToBuffer(stream): Promise<Buffer> {
|
105
|
-
return new Promise<Buffer>((resolve, reject) => {
|
106
|
-
const chunks = []
|
107
|
-
stream.on('data', chunk => chunks.push(chunk))
|
108
|
-
stream.on('end', () => resolve(Buffer.concat(chunks)))
|
109
|
-
stream.on('error', reject)
|
110
|
-
})
|
111
|
-
}
|
@@ -1,78 +0,0 @@
|
|
1
|
-
import contentDisposition from 'content-disposition'
|
2
|
-
|
3
|
-
import { logger } from '@things-factory/env'
|
4
|
-
import { getRepository } from '@things-factory/shell'
|
5
|
-
|
6
|
-
import { Attachment } from './service/attachment/attachment'
|
7
|
-
import { ATTACHMENT_PATH, STORAGE } from './attachment-const'
|
8
|
-
|
9
|
-
const crypto = require('crypto')
|
10
|
-
|
11
|
-
if (STORAGE && STORAGE.type == 'database') {
|
12
|
-
STORAGE.uploadFile = async ({ id, file, context }) => {
|
13
|
-
var { createReadStream, filename, mimetype, encoding } = await file
|
14
|
-
filename = Buffer.from(filename, 'latin1').toString('utf-8') /* Because busboy uses latin1 encoding */
|
15
|
-
|
16
|
-
const stream = createReadStream()
|
17
|
-
|
18
|
-
const chunks: Buffer[] = []
|
19
|
-
for await (const chunk of stream) {
|
20
|
-
if (chunk instanceof Buffer) {
|
21
|
-
chunks.push(chunk)
|
22
|
-
}
|
23
|
-
}
|
24
|
-
|
25
|
-
id = id || crypto.randomUUID()
|
26
|
-
const ext = filename.split('.').pop()
|
27
|
-
const path = ext ? `${id}.${ext}` : id
|
28
|
-
|
29
|
-
const contents = Buffer.concat(chunks)
|
30
|
-
|
31
|
-
return {
|
32
|
-
id,
|
33
|
-
filename,
|
34
|
-
mimetype,
|
35
|
-
encoding,
|
36
|
-
contents,
|
37
|
-
path,
|
38
|
-
size: contents.length
|
39
|
-
}
|
40
|
-
}
|
41
|
-
|
42
|
-
STORAGE.deleteFile = async path => {}
|
43
|
-
|
44
|
-
STORAGE.sendFile = async (context, attachment, next) => {
|
45
|
-
const id = attachment.split('.')[0]
|
46
|
-
|
47
|
-
const entity = await getRepository(Attachment).findOne({
|
48
|
-
select: ['name', 'contents'],
|
49
|
-
where: { id }
|
50
|
-
})
|
51
|
-
|
52
|
-
context.set('Content-Disposition', contentDisposition(entity.name))
|
53
|
-
context.body = entity.contents
|
54
|
-
context.type = entity.mimetype
|
55
|
-
}
|
56
|
-
|
57
|
-
STORAGE.readFile = async (attachment, encoding) => {
|
58
|
-
const id = attachment.split('.')[0]
|
59
|
-
|
60
|
-
const entity = await getRepository(Attachment).findOne({
|
61
|
-
select: ['name', 'contents'],
|
62
|
-
where: { id }
|
63
|
-
})
|
64
|
-
|
65
|
-
return await entity.contents
|
66
|
-
}
|
67
|
-
|
68
|
-
STORAGE.generateUploadURL = async (type: string): Promise<{ url: string; fields: { [key: string]: string } }> => {
|
69
|
-
const id = crypto.randomUUID()
|
70
|
-
|
71
|
-
return await {
|
72
|
-
url: `/${ATTACHMENT_PATH}`,
|
73
|
-
fields: {}
|
74
|
-
}
|
75
|
-
}
|
76
|
-
|
77
|
-
logger.info('File Storage is Ready.')
|
78
|
-
}
|
package/server/storage-file.ts
DELETED
@@ -1,78 +0,0 @@
|
|
1
|
-
import * as fs from 'fs'
|
2
|
-
import * as mkdirp from 'mkdirp'
|
3
|
-
import { resolve } from 'path'
|
4
|
-
|
5
|
-
import { config, logger } from '@things-factory/env'
|
6
|
-
|
7
|
-
import { ATTACHMENT_PATH, STORAGE } from './attachment-const'
|
8
|
-
|
9
|
-
const crypto = require('crypto')
|
10
|
-
const send = require('koa-send')
|
11
|
-
|
12
|
-
if (STORAGE && STORAGE.type == 'file') {
|
13
|
-
const uploadDir = config.getPath(null, STORAGE.base || 'attachments')
|
14
|
-
|
15
|
-
STORAGE.uploadFile = async ({ id, file }) => {
|
16
|
-
var { createReadStream, filename, mimetype, encoding } = await file
|
17
|
-
filename = Buffer.from(filename, 'latin1').toString('utf-8') /* Because busboy uses latin1 encoding */
|
18
|
-
|
19
|
-
const stream = createReadStream()
|
20
|
-
|
21
|
-
mkdirp.sync(uploadDir)
|
22
|
-
|
23
|
-
id = id || crypto.randomUUID()
|
24
|
-
const ext = filename.split('.').pop()
|
25
|
-
const path = ext ? resolve(uploadDir, `${id}.${ext}`) : resolve(uploadDir, id)
|
26
|
-
const relativePath = path.split('\\').pop().split('/').pop()
|
27
|
-
var size: number = 0
|
28
|
-
|
29
|
-
return new Promise<{
|
30
|
-
id: string
|
31
|
-
filename: string
|
32
|
-
path: string
|
33
|
-
size: number
|
34
|
-
mimetype: string
|
35
|
-
encoding: string
|
36
|
-
}>((resolve, reject) =>
|
37
|
-
stream
|
38
|
-
.on('error', error => {
|
39
|
-
if (stream.truncated)
|
40
|
-
// Delete the truncated file
|
41
|
-
fs.unlinkSync(path)
|
42
|
-
reject(error)
|
43
|
-
})
|
44
|
-
.on('data', chunk => {
|
45
|
-
size += chunk.length
|
46
|
-
})
|
47
|
-
.pipe(fs.createWriteStream(path))
|
48
|
-
.on('finish', () => resolve({ id, filename, path: relativePath, size, mimetype, encoding }))
|
49
|
-
)
|
50
|
-
}
|
51
|
-
|
52
|
-
STORAGE.deleteFile = async path => {
|
53
|
-
const fullpath = resolve(uploadDir, path)
|
54
|
-
|
55
|
-
await fs.unlink(fullpath, logger.error)
|
56
|
-
}
|
57
|
-
|
58
|
-
STORAGE.sendFile = async (context, attachment, next) => {
|
59
|
-
await send(context, attachment, { root: uploadDir })
|
60
|
-
}
|
61
|
-
|
62
|
-
STORAGE.readFile = (attachment, encoding) => {
|
63
|
-
const fullpath = resolve(uploadDir, attachment)
|
64
|
-
|
65
|
-
return fs.readFileSync(fullpath, encoding)
|
66
|
-
}
|
67
|
-
|
68
|
-
STORAGE.generateUploadURL = async (type: string): Promise<{ url: string; fields: { [key: string]: string } }> => {
|
69
|
-
const id = crypto.randomUUID()
|
70
|
-
|
71
|
-
return await {
|
72
|
-
url: `/${ATTACHMENT_PATH}`,
|
73
|
-
fields: {}
|
74
|
-
}
|
75
|
-
}
|
76
|
-
|
77
|
-
logger.info('File Storage is Ready.')
|
78
|
-
}
|
package/server/storage-s3.ts
DELETED
@@ -1,139 +0,0 @@
|
|
1
|
-
import type { Readable } from 'stream'
|
2
|
-
|
3
|
-
import {
|
4
|
-
DeleteObjectCommand,
|
5
|
-
GetObjectCommand,
|
6
|
-
GetObjectCommandInput,
|
7
|
-
HeadObjectCommand,
|
8
|
-
S3Client
|
9
|
-
} from '@aws-sdk/client-s3'
|
10
|
-
import { Upload } from '@aws-sdk/lib-storage'
|
11
|
-
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
|
12
|
-
import { logger } from '@things-factory/env'
|
13
|
-
|
14
|
-
import { STORAGE } from './attachment-const'
|
15
|
-
|
16
|
-
const crypto = require('crypto')
|
17
|
-
const mime = require('mime')
|
18
|
-
|
19
|
-
if (STORAGE && STORAGE.type == 's3') {
|
20
|
-
const client = new S3Client({
|
21
|
-
credentials: {
|
22
|
-
accessKeyId: STORAGE.accessKeyId,
|
23
|
-
secretAccessKey: STORAGE.secretAccessKey
|
24
|
-
},
|
25
|
-
region: STORAGE.region
|
26
|
-
})
|
27
|
-
|
28
|
-
const streamToBuffer = (stream: Readable) =>
|
29
|
-
new Promise<Buffer>((resolve, reject) => {
|
30
|
-
const chunks: Buffer[] = []
|
31
|
-
stream.on('data', chunk => chunks.push(chunk))
|
32
|
-
stream.once('end', () => resolve(Buffer.concat(chunks)))
|
33
|
-
stream.once('error', reject)
|
34
|
-
})
|
35
|
-
|
36
|
-
/* upload file */
|
37
|
-
STORAGE.uploadFile = async ({ id, file }) => {
|
38
|
-
var { createReadStream, filename, mimetype, encoding } = await file
|
39
|
-
filename = Buffer.from(filename, 'latin1').toString('utf-8') /* Because busboy uses latin1 encoding */
|
40
|
-
|
41
|
-
const stream = createReadStream()
|
42
|
-
id = id || crypto.randomUUID()
|
43
|
-
const ext = filename.split('.').pop()
|
44
|
-
const key = ext ? `${id}.${ext}` : id
|
45
|
-
|
46
|
-
const upload = new Upload({
|
47
|
-
client,
|
48
|
-
params: {
|
49
|
-
Bucket: STORAGE.bucketName,
|
50
|
-
Key: key,
|
51
|
-
Body: stream
|
52
|
-
}
|
53
|
-
})
|
54
|
-
|
55
|
-
await upload.done()
|
56
|
-
|
57
|
-
const headObjectCommand = new HeadObjectCommand({
|
58
|
-
Bucket: STORAGE.bucketName,
|
59
|
-
Key: key
|
60
|
-
})
|
61
|
-
|
62
|
-
const { ContentLength } = await client.send(headObjectCommand)
|
63
|
-
|
64
|
-
return {
|
65
|
-
id,
|
66
|
-
path: key,
|
67
|
-
filename,
|
68
|
-
size: ContentLength,
|
69
|
-
mimetype,
|
70
|
-
encoding
|
71
|
-
}
|
72
|
-
}
|
73
|
-
|
74
|
-
STORAGE.deleteFile = async (path: string) => {
|
75
|
-
const command = new DeleteObjectCommand({
|
76
|
-
Bucket: STORAGE.bucketName,
|
77
|
-
Key: path
|
78
|
-
})
|
79
|
-
|
80
|
-
return await client.send(command)
|
81
|
-
}
|
82
|
-
|
83
|
-
/* TODO Streaming to Streaming 으로 구현하라. */
|
84
|
-
STORAGE.sendFile = async (context, attachment, next) => {
|
85
|
-
const result = await client.send(
|
86
|
-
new GetObjectCommand({
|
87
|
-
Bucket: STORAGE.bucketName,
|
88
|
-
Key: attachment
|
89
|
-
} as GetObjectCommandInput)
|
90
|
-
)
|
91
|
-
|
92
|
-
context.set({
|
93
|
-
'Content-Length': result.ContentLength,
|
94
|
-
'Content-Type': mime.getType(attachment),
|
95
|
-
'Last-Modified': result.LastModified.toUTCString(),
|
96
|
-
ETag: result.ETag,
|
97
|
-
'Cache-Control': 'public, max-age=31556926'
|
98
|
-
})
|
99
|
-
|
100
|
-
context.body = result.Body
|
101
|
-
}
|
102
|
-
|
103
|
-
STORAGE.readFile = async (attachment: string, encoding: string) => {
|
104
|
-
/*
|
105
|
-
* refered to
|
106
|
-
* https://transang.me/modern-fetch-and-how-to-get-buffer-output-from-aws-sdk-v3-getobjectcommand/#the-body-type
|
107
|
-
*/
|
108
|
-
const result = await client.send(
|
109
|
-
new GetObjectCommand({
|
110
|
-
Bucket: STORAGE.bucketName,
|
111
|
-
Key: attachment
|
112
|
-
} as GetObjectCommandInput)
|
113
|
-
)
|
114
|
-
|
115
|
-
var body = result.Body as Readable
|
116
|
-
var buffer = await streamToBuffer(body)
|
117
|
-
|
118
|
-
switch (encoding) {
|
119
|
-
case 'base64':
|
120
|
-
return buffer.toString('base64')
|
121
|
-
default:
|
122
|
-
return await buffer
|
123
|
-
}
|
124
|
-
}
|
125
|
-
|
126
|
-
STORAGE.generateUploadURL = async (type: string): Promise<{ url: string; fields: { [key: string]: string } }> => {
|
127
|
-
const expiresInMinutes = 1
|
128
|
-
const id = crypto.randomUUID()
|
129
|
-
|
130
|
-
return await createPresignedPost(client, {
|
131
|
-
Bucket: STORAGE.bucketName,
|
132
|
-
Key: id,
|
133
|
-
Expires: expiresInMinutes * 60,
|
134
|
-
Conditions: [['eq', '$Content-Type', type]]
|
135
|
-
})
|
136
|
-
}
|
137
|
-
|
138
|
-
logger.info('S3 Bucket Storage is Ready.')
|
139
|
-
}
|
package/server/util/index.ts
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
export * from './upload-awb'
|
@@ -1,11 +0,0 @@
|
|
1
|
-
import '../awb-storage-s3'
|
2
|
-
|
3
|
-
import { AWBSTORAGE } from '../attachment-const'
|
4
|
-
|
5
|
-
export async function uploadAwb(param: any) {
|
6
|
-
const { content, title } = param
|
7
|
-
|
8
|
-
let result = await AWBSTORAGE.uploadFile({ stream: content, filename: title })
|
9
|
-
|
10
|
-
return result
|
11
|
-
}
|