Compare commits

...

7 Commits

Author SHA1 Message Date
Nicolas Raynaud
f3c5efdd38 avoid parsing URL by hand 2021-07-06 15:06:54 +02:00
Nicolas Raynaud
e00270e916 remote S3 HTTP protocol from UI 2021-07-05 11:50:40 +02:00
Nicolas Raynaud
d17fd2dc11 try using minio for S3 backup 2021-06-30 16:03:53 +02:00
Nicolas Raynaud
a69d32de75 try using minio for S3 backup 2021-06-23 13:32:15 +02:00
Nicolas Raynaud
5040ce87e1 Merge branch 'master' into nr-fix-S3-upload 2021-05-21 11:43:50 +02:00
Nicolas Raynaud
b7481f42cd remove AWS managed upload system for a simpler one 2021-05-14 16:11:42 +02:00
Nicolas Raynaud
2329705ad7 remove AWS managed upload system for a simpler one 2021-05-14 16:07:14 +02:00
5 changed files with 84 additions and 30 deletions

View File

@@ -29,6 +29,7 @@
"get-stream": "^6.0.0",
"limit-concurrency-decorator": "^0.5.0",
"lodash": "^4.17.4",
"minio": "^7.0.18",
"promise-toolbox": "^0.19.2",
"proper-lockfile": "^4.1.2",
"readable-stream": "^3.0.6",

View File

@@ -1,6 +1,7 @@
import aws from '@sullux/aws-sdk'
import assert from 'assert'
import http from 'http'
import https from 'https'
import { Client as Minio } from 'minio'
import { parse } from 'xo-remote-parser'
import RemoteHandlerAbstract from './abstract'
@@ -10,13 +11,10 @@ import RemoteHandlerAbstract from './abstract'
// limits: https://docs.aws.amazon.com/AmazonS3/latest/dev/qfacts.html
const MIN_PART_SIZE = 1024 * 1024 * 5 // 5MB
const MAX_PART_SIZE = 1024 * 1024 * 1024 * 5 // 5GB
const MAX_PARTS_COUNT = 10000
const MAX_OBJECT_SIZE = 1024 * 1024 * 1024 * 1024 * 5 // 5TB
const IDEAL_FRAGMENT_SIZE = Math.ceil(MAX_OBJECT_SIZE / MAX_PARTS_COUNT) // the smallest fragment size that still allows a 5TB upload in 10000 fragments, about 524MB
export default class S3Handler extends RemoteHandlerAbstract {
constructor(remote, _opts) {
super(remote)
const { host, path, username, password, protocol, region } = parse(remote.url)
const { host, hostname, port, path, username, password, protocol, region } = parse(remote.url)
const params = {
accessKeyId: username,
apiVersion: '2006-03-01',
@@ -29,8 +27,12 @@ export default class S3Handler extends RemoteHandlerAbstract {
},
}
if (protocol === 'http') {
params.httpOptions.agent = new http.Agent()
params.sslEnabled = false
throw new Error('HTTP no longer supported, please use HTTPS')
} else {
params.httpOptions.agent = new https.Agent({
// TODO : UI checkbox
rejectUnauthorized: false,
})
}
if (region !== undefined) {
params.region = region
@@ -38,6 +40,15 @@ export default class S3Handler extends RemoteHandlerAbstract {
this._s3 = aws(params).s3
this._minioClient = new Minio({
endPoint: hostname,
port: port !== undefined ? +port : undefined,
useSSL: protocol !== 'http',
accessKey: username,
secretKey: password,
})
// TODO : UI checkbox
this._minioClient.setRequestOptions({ rejectUnauthorized: false })
const splitPath = path.split('/').filter(s => s.length)
this._bucket = splitPath.shift()
this._dir = splitPath.join('/')
@@ -73,13 +84,7 @@ export default class S3Handler extends RemoteHandlerAbstract {
}
async _outputStream(path, input, { validator }) {
await this._s3.upload(
{
...this._createParams(path),
Body: input,
},
{ partSize: IDEAL_FRAGMENT_SIZE, queueSize: 1 }
)
await this._minioClient.putObject(this._bucket, this._dir + path, input)
if (validator !== undefined) {
try {
await validator.call(this, path)

View File

@@ -43,6 +43,8 @@ export const parse = string => {
object.type = 's3'
object.region = parsed.hash.length === 0 ? undefined : parsed.hash.slice(1) // remove '#'
object.host = parsed.host
object.port = parsed.port
object.hostname = parsed.hostname
object.path = parsed.pathname
object.username = parsed.username
object.password = decodeURIComponent(parsed.password)

View File

@@ -3,7 +3,6 @@ import ActionButton from 'action-button'
import decorate from 'apply-decorators'
import Icon from 'icon'
import React from 'react'
import Tooltip from 'tooltip'
import { addSubscriptions, resolveId } from 'utils'
import { alert, confirm } from 'modal'
import { createRemote, editRemote, subscribeRemotes } from 'xo'
@@ -12,7 +11,7 @@ import { format } from 'xo-remote-parser'
import { generateId, linkState } from 'reaclette-utils'
import { injectState, provideState } from 'reaclette'
import { map, some, trimStart } from 'lodash'
import { Password, Number, Toggle } from 'form'
import { Password, Number } from 'form'
import { SelectProxy } from 'select-objects'
const remoteTypes = {
@@ -40,7 +39,6 @@ export default decorate([
username: undefined,
directory: undefined,
bucket: undefined,
protocol: undefined,
region: undefined,
}),
effects: {
@@ -63,7 +61,6 @@ export default decorate([
proxyId = remote.proxy,
type = remote.type,
username = remote.username,
protocol = remote.protocol || 'https',
region = remote.region,
} = state
let { path = remote.path } = state
@@ -81,7 +78,7 @@ export default decorate([
port: port || undefined,
type,
username,
protocol,
protocol: 'https',
region,
}),
options: options !== '' ? options : null,
@@ -140,9 +137,6 @@ export default decorate([
setSecretKey(_, { target: { value } }) {
this.state.password = value
},
setInsecure(_, value) {
this.state.protocol = value ? 'http' : 'https'
},
},
computed: {
formId: generateId,
@@ -159,7 +153,6 @@ export default decorate([
name = remote.name || '',
options = remote.options || '',
password = remote.password || '',
protocol = remote.protocol || 'https',
region = remote.region || '',
parsedPath,
path = parsedPath || '',
@@ -339,11 +332,7 @@ export default decorate([
{type === 's3' && (
<fieldset className='form-group form-group'>
<div className='input-group form-group'>
<span className='input-group-addon'>
<Tooltip content={formatMessage(messages.remoteS3TooltipProtocol)}>
<Toggle iconSize={1} onChange={effects.setInsecure} value={protocol === 'http'} />
</Tooltip>
</span>
<em className='text-warning'>HTTP support has been removed, only HTTPS is supported</em>
<input
className='form-control'
name='host'

View File

@@ -2717,6 +2717,11 @@ async@^2.6.2, async@~2.6.3:
dependencies:
lodash "^4.17.14"
async@^3.1.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@@ -3083,6 +3088,13 @@ bl@^4.0.3:
inherits "^2.0.4"
readable-stream "^3.4.0"
block-stream2@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/block-stream2/-/block-stream2-2.1.0.tgz#ac0c5ef4298b3857796e05be8ebed72196fa054b"
integrity sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==
dependencies:
readable-stream "^3.4.0"
block-stream@*:
version "0.0.9"
resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
@@ -6076,6 +6088,11 @@ es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@
es6-symbol "~3.1.3"
next-tick "~1.0.0"
es6-error@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
es6-iterator@^2.0.1, es6-iterator@^2.0.3, es6-iterator@~2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
@@ -6789,7 +6806,7 @@ fast-safe-stringify@^2.0.7:
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
fast-xml-parser@^3.17.4:
fast-xml-parser@^3.17.4, fast-xml-parser@^3.17.5:
version "3.19.0"
resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz#cb637ec3f3999f51406dd8ff0e6fc4d83e520d01"
integrity sha512-4pXwmBplsCPv8FOY1WRakF970TjNGnGnfbOnLqjlYvMiF1SR3yOHyxMR/YCXpPTOspNF5gwudqktIP4VsWkvBg==
@@ -9881,6 +9898,11 @@ json-stable-stringify@~0.0.0:
dependencies:
jsonify "~0.0.0"
json-stream@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/json-stream/-/json-stream-1.0.0.tgz#1a3854e28d2bbeeab31cc7ddf683d2ddc5652708"
integrity sha1-GjhU4o0rvuqzHMfd9oPS3cVlJwg=
json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
@@ -11112,6 +11134,11 @@ mime-db@1.47.0, "mime-db@>= 1.43.0 < 2":
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c"
integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==
mime-db@1.48.0:
version "1.48.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d"
integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==
mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24:
version "2.1.30"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d"
@@ -11119,6 +11146,13 @@ mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.17, mime-types@~2.1.19,
dependencies:
mime-db "1.47.0"
mime-types@^2.1.14:
version "2.1.31"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b"
integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==
dependencies:
mime-db "1.48.0"
mime@1.6.0, mime@^1.4.1:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
@@ -11195,6 +11229,24 @@ minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
minio@^7.0.18:
version "7.0.18"
resolved "https://registry.yarnpkg.com/minio/-/minio-7.0.18.tgz#a2a6dae52a4dde9e35ed47cdf2accc21df4a512d"
integrity sha512-jVRjkw8A5Spf+ETY5OXQUcQckHriuUA3u2+MAcX36btLT8EytlOVivxIseXvyFf9cNn3dy5w1F1UyjMvHU+nqg==
dependencies:
async "^3.1.0"
block-stream2 "^2.0.0"
es6-error "^4.1.1"
fast-xml-parser "^3.17.5"
json-stream "^1.0.0"
lodash "^4.17.20"
mime-types "^2.1.14"
mkdirp "^0.5.1"
querystring "0.2.0"
through2 "^3.0.1"
xml "^1.0.0"
xml2js "^0.4.15"
minipass@^3.0.0:
version "3.1.3"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd"
@@ -17718,7 +17770,7 @@ xml2js@0.4.19:
sax ">=0.6.0"
xmlbuilder "~9.0.1"
xml2js@^0.4.19, xml2js@^0.4.23:
xml2js@^0.4.15, xml2js@^0.4.19, xml2js@^0.4.23:
version "0.4.23"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
@@ -17726,6 +17778,11 @@ xml2js@^0.4.19, xml2js@^0.4.23:
sax ">=0.6.0"
xmlbuilder "~11.0.0"
xml@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5"
integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=
xmlbuilder@8.2.x:
version "8.2.2"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-8.2.2.tgz#69248673410b4ba42e1a6136551d2922335aa773"