Support front image resize (#2191)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2022-07-02 16:26:23 +07:00 committed by GitHub
parent 99eb2e01ee
commit fb8685d46d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 309 additions and 17 deletions

View File

@ -271,6 +271,7 @@ specifiers:
mini-css-extract-plugin: ^2.2.0
minio: ^7.0.26
mongodb: ^4.1.1
node-html-parser: ~5.3.3
pdfkit: ~0.13.0
postcss: ^8.3.4
postcss-load-config: ^3.1.0
@ -279,6 +280,7 @@ specifiers:
prettier-plugin-svelte: ^2.7.0
sass: ^1.53.0
sass-loader: ^12.1.0
sharp: ~0.30.7
simplytyped: ^3.3.0
smartcrop: ~2.0.5
style-loader: ^3.2.1
@ -571,6 +573,7 @@ dependencies:
mini-css-extract-plugin: 2.6.1_webpack@5.73.0
minio: 7.0.28
mongodb: 4.7.0
node-html-parser: 5.3.3
pdfkit: 0.13.0
postcss: 8.4.14
postcss-load-config: 3.1.4_postcss@8.4.14+ts-node@10.8.1
@ -579,6 +582,7 @@ dependencies:
prettier-plugin-svelte: 2.7.0_prettier@2.7.1+svelte@3.48.0
sass: 1.53.0
sass-loader: 12.6.0_sass@1.53.0+webpack@5.73.0
sharp: 0.30.7
simplytyped: 3.3.0_typescript@4.7.4
smartcrop: 2.0.5
style-loader: 3.3.1_webpack@5.73.0
@ -2182,6 +2186,12 @@ packages:
'@types/node': 16.11.42
dev: false
/@types/sharp/0.30.4:
resolution: {integrity: sha512-6oJEzKt7wZeS7e+6x9QFEOWGs0T/6of00+0onZGN1zSmcSjcTDZKgIGZ6YWJnHowpaKUCFBPH52mYljWqU32Eg==}
dependencies:
'@types/node': 16.11.42
dev: false
/@types/sockjs/0.3.33:
resolution: {integrity: sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==}
dependencies:
@ -3295,6 +3305,10 @@ packages:
fsevents: 2.3.2
dev: false
/chownr/1.1.4:
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
dev: false
/chrome-trace-event/1.0.3:
resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==}
engines: {node: '>=6.0'}
@ -3381,6 +3395,21 @@ packages:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
dev: false
/color-string/1.9.1:
resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
dependencies:
color-name: 1.1.4
simple-swizzle: 0.2.2
dev: false
/color/4.2.3:
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
engines: {node: '>=12.5.0'}
dependencies:
color-convert: 2.0.1
color-string: 1.9.1
dev: false
/colorette/2.0.19:
resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==}
dev: false
@ -3765,6 +3794,11 @@ packages:
which-typed-array: 1.1.8
dev: false
/deep-extend/0.6.0:
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
engines: {node: '>=4.0.0'}
dev: false
/deep-is/0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
dev: false
@ -3840,6 +3874,11 @@ packages:
engines: {node: '>=8'}
dev: false
/detect-libc/2.0.1:
resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==}
engines: {node: '>=8'}
dev: false
/detect-newline/3.1.0:
resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
engines: {node: '>=8'}
@ -4561,6 +4600,11 @@ packages:
engines: {node: '>= 0.8.0'}
dev: false
/expand-template/2.0.3:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
dev: false
/expect/27.5.1:
resolution: {integrity: sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
@ -4943,6 +4987,10 @@ packages:
get-intrinsic: 1.1.2
dev: false
/github-from-package/0.0.0:
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
dev: false
/glob-escape/0.0.2:
resolution: {integrity: sha512-L/cXYz8x7qer1HAyUQ+mbjcUsJVdpRxpAf7CwqHoNBs9vTpABlGfNN4tzkDxt+u3Z7ZncVyKlCNPtzb0R/7WbA==}
engines: {node: '>= 0.10'}
@ -5379,6 +5427,10 @@ packages:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
dev: false
/ini/1.3.8:
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
dev: false
/internal-slot/1.0.3:
resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==}
engines: {node: '>= 0.4'}
@ -5428,6 +5480,10 @@ packages:
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
dev: false
/is-arrayish/0.3.2:
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
dev: false
/is-bigint/1.0.4:
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
dependencies:
@ -6740,6 +6796,10 @@ packages:
xml2js: 0.4.23
dev: false
/mkdirp-classic/0.5.3:
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
dev: false
/mkdirp/0.5.6:
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
hasBin: true
@ -6810,6 +6870,10 @@ packages:
hasBin: true
dev: false
/napi-build-utils/1.0.2:
resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==}
dev: false
/natural-compare/1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
dev: false
@ -6834,11 +6898,29 @@ packages:
tslib: 2.4.0
dev: false
/node-abi/3.22.0:
resolution: {integrity: sha512-u4uAs/4Zzmp/jjsD9cyFYDXeISfUWaAVWshPmDZOFOv4Xl4SbzTXm53I04C2uRueYJ+0t5PEtLH/owbn2Npf/w==}
engines: {node: '>=10'}
dependencies:
semver: 7.3.7
dev: false
/node-addon-api/5.0.0:
resolution: {integrity: sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==}
dev: false
/node-forge/1.3.1:
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
engines: {node: '>= 6.13.0'}
dev: false
/node-html-parser/5.3.3:
resolution: {integrity: sha512-ncg1033CaX9UexbyA7e1N0aAoAYRDiV8jkTvzEnfd1GDvzFdrsXLzR4p4ik8mwLgnaKP/jyUFWDy9q3jvRT2Jw==}
dependencies:
css-select: 4.3.0
he: 1.2.0
dev: false
/node-int64/0.4.0:
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
dev: false
@ -7344,6 +7426,25 @@ packages:
source-map-js: 1.0.2
dev: false
/prebuild-install/7.1.1:
resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==}
engines: {node: '>=10'}
hasBin: true
dependencies:
detect-libc: 2.0.1
expand-template: 2.0.3
github-from-package: 0.0.0
minimist: 1.2.6
mkdirp-classic: 0.5.3
napi-build-utils: 1.0.2
node-abi: 3.22.0
pump: 3.0.0
rc: 1.2.8
simple-get: 4.0.1
tar-fs: 2.1.1
tunnel-agent: 0.6.0
dev: false
/prelude-ls/1.1.2:
resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==}
engines: {node: '>= 0.8.0'}
@ -7612,6 +7713,16 @@ packages:
unpipe: 1.0.0
dev: false
/rc/1.2.8:
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
hasBin: true
dependencies:
deep-extend: 0.6.0
ini: 1.3.8
minimist: 1.2.6
strip-json-comments: 2.0.1
dev: false
/react-is/17.0.2:
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
dev: false
@ -8056,6 +8167,21 @@ packages:
kind-of: 6.0.3
dev: false
/sharp/0.30.7:
resolution: {integrity: sha512-G+MY2YW33jgflKPTXXptVO28HvNOo9G3j0MybYAHeEmby+QuD2U98dT6ueht9cv/XDqZspSpIhoSW+BAKJ7Hig==}
engines: {node: '>=12.13.0'}
requiresBuild: true
dependencies:
color: 4.2.3
detect-libc: 2.0.1
node-addon-api: 5.0.0
prebuild-install: 7.1.1
semver: 7.3.7
simple-get: 4.0.1
tar-fs: 2.1.1
tunnel-agent: 0.6.0
dev: false
/shebang-command/2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@ -8080,6 +8206,24 @@ packages:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
dev: false
/simple-concat/1.0.1:
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
dev: false
/simple-get/4.0.1:
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
dependencies:
decompress-response: 6.0.0
once: 1.4.0
simple-concat: 1.0.1
dev: false
/simple-swizzle/0.2.2:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
dependencies:
is-arrayish: 0.3.2
dev: false
/simplytyped/3.3.0_typescript@4.7.4:
resolution: {integrity: sha512-mz4RaNdKTZiaKXgi6P1k/cdsxV3gz+y1Wh2NXHWD40dExktLh4Xx/h6MFakmQWODZHj/2rKe59acacpL74ZhQA==}
peerDependencies:
@ -8344,6 +8488,11 @@ packages:
min-indent: 1.0.1
dev: false
/strip-json-comments/2.0.1:
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
engines: {node: '>=0.10.0'}
dev: false
/strip-json-comments/3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
@ -8542,6 +8691,15 @@ packages:
engines: {node: '>=6'}
dev: false
/tar-fs/2.1.1:
resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==}
dependencies:
chownr: 1.1.4
mkdirp-classic: 0.5.3
pump: 3.0.0
tar-stream: 2.2.0
dev: false
/tar-stream/2.2.0:
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
engines: {node: '>=6'}
@ -8784,6 +8942,12 @@ packages:
typescript: 4.7.4
dev: false
/tunnel-agent/0.6.0:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
dependencies:
safe-buffer: 5.2.1
dev: false
/type-check/0.3.2:
resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==}
engines: {node: '>= 0.8.0'}
@ -10284,7 +10448,7 @@ packages:
dev: false
file:projects/front.tgz:
resolution: {integrity: sha512-guStnV89AYnfgSdQuq+YfvZ2uemQ6ezSImVi7JN1WuwelfBmQAzUWS9FP4JndSlYagIhlk5eKrFOIqGFZQwcYA==, tarball: file:projects/front.tgz}
resolution: {integrity: sha512-2e41BziVvBMsiFDvITF0Qan/8+VHucvVUe6dXKlE4AXDktZlRm6lNdL+wvBFOvmni7/94wND5dva/Ukegy51cQ==, tarball: file:projects/front.tgz}
name: '@rush-temp/front'
version: 0.0.0
dependencies:
@ -10297,6 +10461,7 @@ packages:
'@types/heft-jest': 1.0.3
'@types/minio': 7.0.13
'@types/node': 16.11.42
'@types/sharp': 0.30.4
'@types/uuid': 8.3.4
'@typescript-eslint/eslint-plugin': 5.30.3_bd298502bfa44e376686f9e6b29811dd
'@typescript-eslint/parser': 5.30.3_eslint@8.19.0+typescript@4.7.4
@ -10314,6 +10479,7 @@ packages:
express-fileupload: 1.4.0
minio: 7.0.28
prettier: 2.7.1
sharp: 0.30.7
ts-node: 10.8.1_eff44b0165567fe5f2c2cc6c8fd30ef7
typescript: 4.7.4
uuid: 8.3.2
@ -13914,7 +14080,7 @@ packages:
dev: false
file:projects/tool.tgz:
resolution: {integrity: sha512-7BKMRQ8UZ8cgJGDXvMGNoAdjUmEAyMx9GVBmBo3gmW56eLhxamaBqdwkunRIemCR77MJ8JJj+qhgWAzpRXpoRg==, tarball: file:projects/tool.tgz}
resolution: {integrity: sha512-LjCQIq8dqLFEXvLcBmfJKRtkoJublg0r6DWC2RwIlmJyw61xEEBFHdOn3p1l3EyQPg0PjgAaSoEUPF+ViaxlPQ==, tarball: file:projects/tool.tgz}
name: '@rush-temp/tool'
version: 0.0.0
dependencies:
@ -13946,6 +14112,7 @@ packages:
mime-types: 2.1.35
minio: 7.0.28
mongodb: 4.7.0
node-html-parser: 5.3.3
prettier: 2.7.1
ts-node: 10.8.1_eff44b0165567fe5f2c2cc6c8fd30ef7
typescript: 4.7.4

View File

@ -29,7 +29,7 @@
url = blobURL
})
} else if (avatar !== undefined && avatar !== null) {
url = getFileUrl(avatar)
url = getFileUrl(avatar, size)
} else {
url = undefined
}

View File

@ -35,7 +35,7 @@
if (direct !== undefined) {
file = direct
} else if (avatar != null) {
const url = getFileUrl(avatar)
const url = getFileUrl(avatar, 'full')
file = await (await fetch(url)).blob()
} else {
return inputRef.click()

View File

@ -38,6 +38,7 @@ import { getMetadata } from '@anticrm/platform'
import { LiveQuery as LQ } from '@anticrm/query'
import { onDestroy } from 'svelte'
import { deepEqual } from 'fast-equals'
import { IconSize } from '@anticrm/ui'
let liveQuery: LQ
let client: TxOperations
@ -131,10 +132,10 @@ export function createQuery (dontDestroy?: boolean): LiveQuery {
return new LiveQuery(dontDestroy)
}
export function getFileUrl (file: string): string {
export function getFileUrl (file: string, size: IconSize = 'full'): string {
const uploadUrl = getMetadata(login.metadata.UploadUrl)
const token = getMetadata(login.metadata.LoginToken)
const url = `${uploadUrl as string}?file=${file}&token=${token as string}`
const url = `${uploadUrl as string}?file=${file}&token=${token as string}&size=${size as string}`
return url
}

View File

@ -15,7 +15,7 @@
//
import core, { Doc, Tx, TxCreateDoc, TxRemoveDoc, TxUpdateDoc } from '@anticrm/core'
import type { TriggerControl } from '@anticrm/server-core'
import type { TriggerControl, MinioClient, BucketItem } from '@anticrm/server-core'
import contact, { Contact, contactId, formatName, Organization, Person } from '@anticrm/contact'
import { getMetadata } from '@anticrm/platform'
import login from '@anticrm/login'
@ -52,6 +52,13 @@ export async function OnContactDelete (tx: Tx, { findAll, hierarchy, storageFx }
storageFx(async (adapter, bucket) => {
await adapter.removeObject(bucket, avatar)
const extra = await listMinioObjects(adapter, bucket, avatar)
if (extra.size > 0) {
for (const e of extra.entries()) {
await adapter.removeObject(bucket, e[1].name)
}
}
})
return []
@ -105,3 +112,17 @@ export default async () => ({
OrganizationTextPresenter: organizationTextPresenter
}
})
async function listMinioObjects (client: MinioClient, db: string, prefix: string): Promise<Map<string, BucketItem>> {
const items = new Map<string, BucketItem>()
const list = await client.listObjects(db, prefix, true)
await new Promise((resolve) => {
list.on('data', (data) => {
items.set(data.name, { ...data })
})
list.on('end', () => {
resolve(null)
})
})
return items
}

View File

@ -34,7 +34,9 @@ import type {
} from '@anticrm/core'
import { Hierarchy, TxFactory } from '@anticrm/core'
import type { Resource } from '@anticrm/platform'
import type { Client as MinioClient } from 'minio'
import type { Client as MinioClient, BucketItem } from 'minio'
export { MinioClient, BucketItem }
/**
* @public

View File

@ -1,9 +1,13 @@
FROM node:16
FROM node:16-alpine
WORKDIR /usr/src/app
RUN apk add dumb-init
ENV NODE_ENV production
WORKDIR /app
RUN npm install --ignore-scripts=false --verbose sharp --unsafe-perm
COPY bundle.js ./
COPY dist/ ./dist/
EXPOSE 8080
CMD [ "node", "bundle.js" ]
CMD [ "dumb-init", "node", "./bundle.js" ]

View File

@ -8,7 +8,7 @@
"build": "heft build",
"build:watch": "tsc",
"lint:fix": "eslint --fix src",
"bundle": "esbuild src/__start.ts --define:process.env.MODEL_VERSION=$(node ../../models/all/lib/__showversion.js) --bundle --minify --platform=node > bundle.js & rm -rf ./dist && cp -r ../../dev/prod/dist . && cp -r ../../dev/prod/public/* ./dist/ && rm ./dist/config.json",
"bundle": "esbuild src/__start.ts --define:process.env.MODEL_VERSION=$(node ../../models/all/lib/__showversion.js) --bundle --minify --platform=node --external:sharp > bundle.js & rm -rf ./dist && cp -r ../../dev/prod/dist . && cp -r ../../dev/prod/public/* ./dist/ && rm ./dist/config.json",
"docker:build": "docker build -t hardcoreeng/front .",
"docker:staging": "../../common/scripts/docker_tag.sh hardcoreeng/front staging",
"docker:push": "../../common/scripts/docker_tag.sh hardcoreeng/front",
@ -39,7 +39,8 @@
"@types/body-parser": "~1.19.2",
"cross-env": "~7.0.3",
"ts-node": "^10.8.0",
"@types/compression": "~1.7.2"
"@types/compression": "~1.7.2",
"@types/sharp": "~0.30.4"
},
"dependencies": {
"@anticrm/core": "~0.6.16",
@ -55,6 +56,7 @@
"@anticrm/contrib": "~0.6.0",
"minio": "^7.0.26",
"body-parser": "~1.19.1",
"compression": "~1.7.4"
"compression": "~1.7.4",
"sharp": "~0.30.7"
}
}

View File

@ -18,16 +18,17 @@ import attachment from '@anticrm/attachment'
import { Account, Doc, Ref, Space } from '@anticrm/core'
import { createElasticAdapter } from '@anticrm/elastic'
import type { IndexedDoc } from '@anticrm/server-core'
import { decodeToken } from '@anticrm/server-token'
import { decodeToken, Token } from '@anticrm/server-token'
import bp from 'body-parser'
import compression from 'compression'
import cors from 'cors'
import express from 'express'
import fileUpload, { UploadedFile } from 'express-fileupload'
import https from 'https'
import { Client, ItemBucketMetadata } from 'minio'
import { BucketItem, Client, ItemBucketMetadata } from 'minio'
import { join, resolve } from 'path'
import { v4 as uuid } from 'uuid'
import sharp from 'sharp'
async function minioUpload (minio: Client, workspace: string, file: UploadedFile): Promise<string> {
const id = uuid()
@ -41,6 +42,26 @@ async function minioUpload (minio: Client, workspace: string, file: UploadedFile
return id
}
async function readMinioData (client: Client, db: string, name: string): Promise<Buffer[]> {
const data = await client.getObject(db, name)
const chunks: Buffer[] = []
await new Promise((resolve) => {
data.on('readable', () => {
let chunk
while ((chunk = data.read()) !== null) {
const b = chunk as Buffer
chunks.push(b)
}
})
data.on('end', () => {
resolve(null)
})
})
return chunks
}
/**
* @public
* @param port -
@ -96,9 +117,13 @@ export function start (
try {
const token = req.query.token as string
const payload = decodeToken(token)
const uuid = req.query.file as string
let uuid = req.query.file as string
const size = req.query.size as 'inline' | 'tiny' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large' | 'full'
uuid = await getResizeID(size, uuid, config, payload)
const stat = await config.minio.statObject(payload.workspace, uuid)
config.minio.getObject(payload.workspace, uuid, function (err, dataStream) {
if (err !== null) {
return console.log(err)
@ -203,6 +228,13 @@ export function start (
await config.minio.removeObject(payload.workspace, uuid)
const extra = await listMinioObjects(config.minio, payload.workspace, uuid)
if (extra.size > 0) {
for (const e of extra.entries()) {
await config.minio.removeObject(payload.workspace, e[1].name)
}
}
res.status(200).send()
} catch (error) {
console.log(error)
@ -399,3 +431,66 @@ export function start (
server.close()
}
}
async function getResizeID (size: string, uuid: string, config: { minio: Client }, payload: Token): Promise<string> {
if (size !== undefined && size !== 'full') {
let width = 64
switch (size) {
case 'inline':
case 'tiny':
case 'x-small':
case 'small':
case 'medium':
width = 64
break
case 'large':
width = 256
break
case 'x-large':
width = 512
break
}
let hasSmall = false
const sizeId = uuid + `%size%${width}`
try {
const d = await config.minio.statObject(payload.workspace, sizeId)
hasSmall = d !== undefined && d.size > 0
} catch (err) {}
if (hasSmall) {
// We have cached small document, let's proceed with it.
uuid = sizeId
} else {
// Let's get data and resize it
const data = Buffer.concat(await readMinioData(config.minio, payload.workspace, uuid))
const dataBuff = await sharp(data)
.resize({
width
})
.jpeg()
.toBuffer()
await config.minio.putObject(payload.workspace, sizeId, dataBuff, {
'Content-Type': 'image/jpeg'
})
uuid = sizeId
}
}
return uuid
}
async function listMinioObjects (
client: Client,
db: string,
prefix: string
): Promise<Map<string, BucketItem & { metaData: ItemBucketMetadata }>> {
const items = new Map<string, BucketItem & { metaData: ItemBucketMetadata }>()
const list = await client.listObjects(db, prefix, true)
await new Promise((resolve) => {
list.on('data', (data) => {
items.set(data.name, { metaData: {}, ...data })
})
list.on('end', () => {
resolve(null)
})
})
return items
}