S3 storage - amazon server side (no full documentation yet)

This commit is contained in:
Yuriy Puchkov
2020-09-28 16:20:05 +03:00
parent 05cbd9660f
commit e0523acafe
6 changed files with 311 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
[General]
;HTTP_PROXY_HOST=0.0.0.0
;HTTP_PROXY_PORT=3128
; No authentification USER and PASSWORD should be empty
;HTTP_PROXY_USER=
;HTTP_PROXY_PASSWORD=
HTTP_PROXY_TYPE=3
; Proxy Types (3 is default):
; 0 Proxy is determined based on the application proxy set using setApplicationProxy()
; 1 Socks5 proxying is used
; 3 HTTP transparent proxying is used
; 4 Proxying for HTTP requests only
; 5 Proxying for FTP requests only
[S3]
S3_URL=https://api.flameshot.org/
S3_CREDS_URL=https://api.img.flameshot.org/
S3_X_API_KEY=amazon-secret-key

View File

@@ -0,0 +1 @@
tests/*

View File

@@ -0,0 +1,122 @@
'use strict';
const uuid = require('short-uuid');
const AWS = require('aws-sdk');
AWS.config.update({ region: process.env.AWS_REGION || 'us-east-1' });
const s3 = new AWS.S3();
const cloudfront = new AWS.CloudFront();
const allowedImageType = 'png';
// Main Lambda entry point
exports.handler = async (event) => {
console.log('Event: ', event);
let result;
if (event.resource === '/v2/image/{fileName}' && event.httpMethod === 'DELETE') {
let token = (event.headers.Authorization && event.headers.Authorization.split(' ')[1]) || '';
result = await deleteObject(event.pathParameters.fileName, token);
if (result === false) {
return {
statusCode: 400,
isBase64Encoded: false,
headers: {
'Access-Control-Allow-Origin': '*'
},
body: 'Bad request'
};
}
} else {
result = await getUploadDetails(event.resource === '/v2/image' ? 2 : 1);
}
console.log('Result: ', result);
return {
statusCode: 200,
isBase64Encoded: false,
headers: {
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify(result)
};
};
const getUploadDetails = async function(version) {
const actionId = uuid.generate();
const deleteToken = uuid.generate();
const s3Params = {
Bucket: process.env.UploadBucket,
Fields: {
Key: `${actionId}.${allowedImageType}`,
},
Conditions: [
[ 'eq', '$acl', 'private' ],
[ 'eq', '$Content-Type', `image/${allowedImageType}` ],
[ 'content-length-range', 0, 10485760 ], //allows a file size from 0B to 10 MiB
],
Expires: 60,
};
if (version === 2) {
s3Params.Fields.tagging = getDeleteTags(deleteToken);
}
console.log('getUploadURL: ', s3Params);
const signedForm = await createPresignedPost(s3Params);
signedForm.url = `https://${process.env.UploadBucket}.s3-accelerate.amazonaws.com`;
signedForm.fields = Object.assign({
acl: 'private',
'Content-Type': `image/${allowedImageType}`,
}, signedForm.fields);
const uploadDetails = {
formData: signedForm,
resultURL: `${process.env.BasePath}${s3Params.Fields.Key}`
};
if (version === 2) {
uploadDetails.deleteToken = deleteToken;
}
return uploadDetails;
};
const deleteObject = async (key, token) => {
const s3ObjectParams = {Bucket: process.env.UploadBucket, Key: key};
const {TagSet: tags} = await s3.getObjectTagging(s3ObjectParams).promise();
const storedTokenInfo = tags.find(v => v.Key === 'deleteToken');
if (storedTokenInfo === undefined) {
console.log(`Unable to find storedTokenInfo for requested key "${key}"`);
return false;
}
if (storedTokenInfo.Value !== token) {
console.log(`storedTokenInfo != passed token: ${storedTokenInfo.Value} != ${token}`);
return false;
}
await s3.deleteObject(s3ObjectParams).promise();
await cloudfront.createInvalidation({
DistributionId: process.env.DistributionId,
InvalidationBatch: {
CallerReference: uuid.generate(),
Paths: {
Quantity: 1,
Items: [`/${key}`]
}
}
}).promise();
return {status: 'ok'}
};
const getDeleteTags = (token) => `<Tagging><TagSet><Tag><Key>deleteToken</Key><Value>${token}</Value></Tag></TagSet></Tagging>`;
const createPresignedPost = params =>
new Promise((resolve, reject) =>
s3.createPresignedPost( params, (err, data) => err ? reject(err) : resolve(data) )
);

View File

@@ -0,0 +1,125 @@
{
"name": "none",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"any-base": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz",
"integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg=="
},
"aws-sdk": {
"version": "2.700.0",
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.700.0.tgz",
"integrity": "sha512-faBkr/D3IavfL2mwst4/thiKsHkN8YCwU9927Mkiushbe7n4UXxlcNf7LnVxFyjr3WIf4KfziSw4bajRAiAjYA==",
"requires": {
"buffer": "4.9.2",
"events": "1.1.1",
"ieee754": "1.1.13",
"jmespath": "0.15.0",
"querystring": "0.2.0",
"sax": "1.2.1",
"url": "0.10.3",
"uuid": "3.3.2",
"xml2js": "0.4.19"
},
"dependencies": {
"uuid": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
}
}
},
"base64-js": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
},
"buffer": {
"version": "4.9.2",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
"integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
"requires": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4",
"isarray": "^1.0.0"
}
},
"events": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
},
"ieee754": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"jmespath": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
},
"punycode": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
},
"querystring": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
},
"sax": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
"integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
},
"short-uuid": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/short-uuid/-/short-uuid-3.1.1.tgz",
"integrity": "sha512-7dI69xtJYpTIbg44R6JSgrbDtZFuZ9vAwwmnF/L0PinykbFrhQ7V8omKsQcVw1TP0nYJ7uQp1PN6/aVMkzQFGQ==",
"requires": {
"any-base": "^1.1.0",
"uuid": "^3.3.2"
},
"dependencies": {
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
}
}
},
"url": {
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
"integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
"requires": {
"punycode": "1.3.2",
"querystring": "0.2.0"
}
},
"xml2js": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
"requires": {
"sax": ">=0.6.0",
"xmlbuilder": "~9.0.1"
}
},
"xmlbuilder": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
}
}
}

View File

@@ -0,0 +1,18 @@
{
"name": "none",
"private": true,
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"start": "node testHarness.js",
"pack": "zip -r ../.infrastructure/lambda.zip ./",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"license": "UNLICENSED",
"dependencies": {
"aws-sdk": "^2.478.0",
"short-uuid": "^3.1.1"
}
}

View File

@@ -0,0 +1,25 @@
/*
Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// For local testing
const { handler } = require('./app')
const main = async () => {
await handler()
}
main()