xmpp-s3-external/src/aws/s3.coffee

88 lines
2.6 KiB
CoffeeScript

import AwsAuth from "./auth"
import parser from "fast-xml-parser"
class S3
constructor: (@conf) ->
@baseURL = @conf.s3.endpoint + "/" + @conf.s3.bucket + "/"
# Wrapper of Fetch API with automatic AWS signature
# Note that query string in url is not supported
# options.method must be specified
request: (url, qsMap, options) ->
# We only support unsigned payload for now
options.headers['x-amz-content-sha256'] = 'UNSIGNED-PAYLOAD'
# Sign the request
auth = new AwsAuth url, @conf.aws
.setMethod options.method
.setQueryStringMap qsMap
.setHeaderMap options.headers
.setService "s3"
# Write needed authorization headers to header object
await auth.authorizedHeader options.headers
fetch url + "?" + auth.canonicalQueryString(), options
# Used for some requests to convert params to snake-cased x-amz-*
camelToSnake: (str) ->
str.replace /([A-Z])/g, "-$1"
.toLowerCase()[1..]
makeHeaders: (params) -> Object.fromEntries do =>
Object.entries params
.map ([key, val]) =>
key = @camelToSnake key
if not (key == "expires" or key.startsWith "content-" or key.startsWith "cache-")
key = 'x-amz-' + key
[key, val]
# See AWS docs for params
# params are passed in query string
# Paging not implemented yet; Need to
listObjects: (params) ->
# Send request using params as query string
resp = await @request @baseURL, params,
method: "GET"
headers:
'x-amz-request-payer': 'requester'
txt = await resp.text()
console.log txt
if not resp.ok
# no error handling yet
throw txt
result = parser.parse txt,
arrayMode: true
console.log result
if not result.ListBucketResult
return null
result.ListBucketResult[0]
# params are in CamelCase, but converted to x-amz-* headers automatically
# Content* headers are exempt from the x-amz-* prefix, as well as Expires
# data can be a buffer or a readable stream
putObject: (key, data, params) ->
# Convert camel-cased params to snake-cased headers
headers = @makeHeaders params
# Send request
resp = await @request @baseURL + key, null,
method: 'PUT'
headers: headers
body: data
txt = await resp.text()
console.log txt
if not resp.ok
# no error handling yet
throw resp.status
else
txt
# params are processed similar to putObject
# returns the full response object (because content-range may be needed)
getObject: (key, params) ->
@request @baseURL + key, null,
method: 'GET'
headers: @makeHeaders params
export default S3