Compare commits

...

6 Commits

Author SHA1 Message Date
Duncan Phelps
000024426b All endpoints working with AWS 2021-05-06 14:01:18 -05:00
Duncan Phelps
66524e5d79 set up to use AWS. using Serverless 2021-05-03 11:06:05 -05:00
Duncan Phelps
876cd0d84f /save and /file endpoints working with localstack s3 bucket 2021-04-29 19:23:13 -05:00
Duncan Phelps
ace3670339 added /file GET request for reading files stored locally 2021-04-28 16:48:35 -05:00
Duncan Phelps
54f9250e53 added a /save POST that saves a tmp file of the a workbook in user's tmp file directory 2021-04-28 14:58:25 -05:00
Duncan Phelps
2e0f6036b9 added endpoint for updating and downloading a work book 2021-04-24 22:30:09 -05:00
16 changed files with 2747 additions and 4 deletions

2
.gitignore vendored

@ -1,5 +1,7 @@
.now
node_modules
.env
.serverless
# Environment Variables
.env

@ -0,0 +1,82 @@
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "The AWS CloudFormation template for this Serverless application",
"Resources": {
"ServerlessDeploymentBucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketEncryption": {
"ServerSideEncryptionConfiguration": [
{
"ServerSideEncryptionByDefault": {
"SSEAlgorithm": "AES256"
}
}
]
}
}
},
"ServerlessDeploymentBucketPolicy": {
"Type": "AWS::S3::BucketPolicy",
"Properties": {
"Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"PolicyDocument": {
"Statement": [
{
"Action": "s3:*",
"Effect": "Deny",
"Principal": "*",
"Resource": [
{
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":s3:::",
{
"Ref": "ServerlessDeploymentBucket"
},
"/*"
]
]
},
{
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":s3:::",
{
"Ref": "ServerlessDeploymentBucket"
}
]
]
}
],
"Condition": {
"Bool": {
"aws:SecureTransport": false
}
}
}
]
}
}
}
},
"Outputs": {
"ServerlessDeploymentBucketName": {
"Value": {
"Ref": "ServerlessDeploymentBucket"
}
}
}
}

@ -0,0 +1,404 @@
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "The AWS CloudFormation template for this Serverless application",
"Resources": {
"ServerlessDeploymentBucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketEncryption": {
"ServerSideEncryptionConfiguration": [
{
"ServerSideEncryptionByDefault": {
"SSEAlgorithm": "AES256"
}
}
]
}
}
},
"ServerlessDeploymentBucketPolicy": {
"Type": "AWS::S3::BucketPolicy",
"Properties": {
"Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"PolicyDocument": {
"Statement": [
{
"Action": "s3:*",
"Effect": "Deny",
"Principal": "*",
"Resource": [
{
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":s3:::",
{
"Ref": "ServerlessDeploymentBucket"
},
"/*"
]
]
},
{
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":s3:::",
{
"Ref": "ServerlessDeploymentBucket"
}
]
]
}
],
"Condition": {
"Bool": {
"aws:SecureTransport": false
}
}
}
]
}
}
},
"AppLogGroup": {
"Type": "AWS::Logs::LogGroup",
"Properties": {
"LogGroupName": "/aws/lambda/localstack-lambda-dev-app"
}
},
"IamRoleLambdaExecution": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Policies": [
{
"PolicyName": {
"Fn::Join": [
"-",
[
"localstack-lambda",
"dev",
"lambda"
]
]
},
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:CreateLogGroup"
],
"Resource": [
{
"Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/localstack-lambda-dev*:*"
}
]
},
{
"Effect": "Allow",
"Action": [
"logs:PutLogEvents"
],
"Resource": [
{
"Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/localstack-lambda-dev*:*:*"
}
]
}
]
}
}
],
"Path": "/",
"RoleName": {
"Fn::Join": [
"-",
[
"localstack-lambda",
"dev",
{
"Ref": "AWS::Region"
},
"lambdaRole"
]
]
}
}
},
"AppLambdaFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"S3Key": "serverless/localstack-lambda/dev/1620092918879-2021-05-04T01:48:38.879Z/localstack-lambda.zip"
},
"Handler": "api/server.handler",
"Runtime": "nodejs12.x",
"FunctionName": "localstack-lambda-dev-app",
"MemorySize": 512,
"Timeout": 6,
"Role": {
"Fn::GetAtt": [
"IamRoleLambdaExecution",
"Arn"
]
}
},
"DependsOn": [
"AppLogGroup"
]
},
"AppLambdaVersionuvewJZYompWGhR7PreRGORUZWWNhF3zGn5TtfgsWIs": {
"Type": "AWS::Lambda::Version",
"DeletionPolicy": "Retain",
"Properties": {
"FunctionName": {
"Ref": "AppLambdaFunction"
},
"CodeSha256": "3b8ZgXd14AZAi3l2N89aVQ4LE9Ty39V8tHZhL6Q1t3w="
}
},
"ApiGatewayRestApi": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": "dev-localstack-lambda",
"EndpointConfiguration": {
"Types": [
"EDGE"
]
},
"Policy": ""
}
},
"ApiGatewayResourceProxyVar": {
"Type": "AWS::ApiGateway::Resource",
"Properties": {
"ParentId": {
"Fn::GetAtt": [
"ApiGatewayRestApi",
"RootResourceId"
]
},
"PathPart": "{proxy+}",
"RestApiId": {
"Ref": "ApiGatewayRestApi"
}
}
},
"ApiGatewayMethodAny": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"HttpMethod": "ANY",
"RequestParameters": {},
"ResourceId": {
"Fn::GetAtt": [
"ApiGatewayRestApi",
"RootResourceId"
]
},
"RestApiId": {
"Ref": "ApiGatewayRestApi"
},
"ApiKeyRequired": false,
"AuthorizationType": "NONE",
"Integration": {
"IntegrationHttpMethod": "POST",
"Type": "AWS_PROXY",
"Uri": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [
"AppLambdaFunction",
"Arn"
]
},
"/invocations"
]
]
}
},
"MethodResponses": []
}
},
"ApiGatewayMethodProxyVarAny": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"HttpMethod": "ANY",
"RequestParameters": {},
"ResourceId": {
"Ref": "ApiGatewayResourceProxyVar"
},
"RestApiId": {
"Ref": "ApiGatewayRestApi"
},
"ApiKeyRequired": false,
"AuthorizationType": "NONE",
"Integration": {
"IntegrationHttpMethod": "POST",
"Type": "AWS_PROXY",
"Uri": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [
"AppLambdaFunction",
"Arn"
]
},
"/invocations"
]
]
}
},
"MethodResponses": []
}
},
"ApiGatewayDeployment1620092914902": {
"Type": "AWS::ApiGateway::Deployment",
"Properties": {
"RestApiId": {
"Ref": "ApiGatewayRestApi"
},
"StageName": "dev"
},
"DependsOn": [
"ApiGatewayMethodAny",
"ApiGatewayMethodProxyVarAny"
]
},
"AppLambdaPermissionApiGateway": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Fn::GetAtt": [
"AppLambdaFunction",
"Arn"
]
},
"Action": "lambda:InvokeFunction",
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":execute-api:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":",
{
"Ref": "ApiGatewayRestApi"
},
"/*/*"
]
]
}
}
}
},
"Outputs": {
"ServerlessDeploymentBucketName": {
"Value": {
"Ref": "ServerlessDeploymentBucket"
},
"Export": {
"Name": "sls-localstack-lambda-dev-ServerlessDeploymentBucketName"
}
},
"AppLambdaFunctionQualifiedArn": {
"Description": "Current Lambda function version",
"Value": {
"Ref": "AppLambdaVersionuvewJZYompWGhR7PreRGORUZWWNhF3zGn5TtfgsWIs"
},
"Export": {
"Name": "sls-localstack-lambda-dev-AppLambdaFunctionQualifiedArn"
}
},
"ServiceEndpoint": {
"Description": "URL of the service endpoint",
"Value": {
"Fn::Join": [
"",
[
"https://",
{
"Ref": "ApiGatewayRestApi"
},
".execute-api.",
{
"Ref": "AWS::Region"
},
".",
{
"Ref": "AWS::URLSuffix"
},
"/dev"
]
]
},
"Export": {
"Name": "sls-localstack-lambda-dev-ServiceEndpoint"
}
}
}
}

Binary file not shown.

@ -0,0 +1,546 @@
{
"service": {
"service": "localstack-lambda",
"serviceObject": {
"name": "localstack-lambda"
},
"provider": {
"name": "aws",
"runtime": "nodejs12.x",
"stage": "dev",
"region": "us-east-1",
"memorySize": 512,
"variableSyntax": "\\${([^{}:]+?(?:\\(|:)(?:[^:{}][^{}]*?)?)}",
"versionFunctions": true,
"compiledCloudFormationTemplate": {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "The AWS CloudFormation template for this Serverless application",
"Resources": {
"ServerlessDeploymentBucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketEncryption": {
"ServerSideEncryptionConfiguration": [
{
"ServerSideEncryptionByDefault": {
"SSEAlgorithm": "AES256"
}
}
]
}
}
},
"ServerlessDeploymentBucketPolicy": {
"Type": "AWS::S3::BucketPolicy",
"Properties": {
"Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"PolicyDocument": {
"Statement": [
{
"Action": "s3:*",
"Effect": "Deny",
"Principal": "*",
"Resource": [
{
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":s3:::",
{
"Ref": "ServerlessDeploymentBucket"
},
"/*"
]
]
},
{
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":s3:::",
{
"Ref": "ServerlessDeploymentBucket"
}
]
]
}
],
"Condition": {
"Bool": {
"aws:SecureTransport": false
}
}
}
]
}
}
},
"AppLogGroup": {
"Type": "AWS::Logs::LogGroup",
"Properties": {
"LogGroupName": "/aws/lambda/localstack-lambda-dev-app"
}
},
"IamRoleLambdaExecution": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Policies": [
{
"PolicyName": {
"Fn::Join": [
"-",
[
"localstack-lambda",
"dev",
"lambda"
]
]
},
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:CreateLogGroup"
],
"Resource": [
{
"Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/localstack-lambda-dev*:*"
}
]
},
{
"Effect": "Allow",
"Action": [
"logs:PutLogEvents"
],
"Resource": [
{
"Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/localstack-lambda-dev*:*:*"
}
]
}
]
}
}
],
"Path": "/",
"RoleName": {
"Fn::Join": [
"-",
[
"localstack-lambda",
"dev",
{
"Ref": "AWS::Region"
},
"lambdaRole"
]
]
}
}
},
"AppLambdaFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"S3Key": "serverless/localstack-lambda/dev/1620092918879-2021-05-04T01:48:38.879Z/localstack-lambda.zip"
},
"Handler": "api/server.handler",
"Runtime": "nodejs12.x",
"FunctionName": "localstack-lambda-dev-app",
"MemorySize": 512,
"Timeout": 6,
"Role": {
"Fn::GetAtt": [
"IamRoleLambdaExecution",
"Arn"
]
}
},
"DependsOn": [
"AppLogGroup"
]
},
"AppLambdaVersionuvewJZYompWGhR7PreRGORUZWWNhF3zGn5TtfgsWIs": {
"Type": "AWS::Lambda::Version",
"DeletionPolicy": "Retain",
"Properties": {
"FunctionName": {
"Ref": "AppLambdaFunction"
},
"CodeSha256": "3b8ZgXd14AZAi3l2N89aVQ4LE9Ty39V8tHZhL6Q1t3w="
}
},
"ApiGatewayRestApi": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": "dev-localstack-lambda",
"EndpointConfiguration": {
"Types": [
"EDGE"
]
},
"Policy": ""
}
},
"ApiGatewayResourceProxyVar": {
"Type": "AWS::ApiGateway::Resource",
"Properties": {
"ParentId": {
"Fn::GetAtt": [
"ApiGatewayRestApi",
"RootResourceId"
]
},
"PathPart": "{proxy+}",
"RestApiId": {
"Ref": "ApiGatewayRestApi"
}
}
},
"ApiGatewayMethodAny": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"HttpMethod": "ANY",
"RequestParameters": {},
"ResourceId": {
"Fn::GetAtt": [
"ApiGatewayRestApi",
"RootResourceId"
]
},
"RestApiId": {
"Ref": "ApiGatewayRestApi"
},
"ApiKeyRequired": false,
"AuthorizationType": "NONE",
"Integration": {
"IntegrationHttpMethod": "POST",
"Type": "AWS_PROXY",
"Uri": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [
"AppLambdaFunction",
"Arn"
]
},
"/invocations"
]
]
}
},
"MethodResponses": []
}
},
"ApiGatewayMethodProxyVarAny": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"HttpMethod": "ANY",
"RequestParameters": {},
"ResourceId": {
"Ref": "ApiGatewayResourceProxyVar"
},
"RestApiId": {
"Ref": "ApiGatewayRestApi"
},
"ApiKeyRequired": false,
"AuthorizationType": "NONE",
"Integration": {
"IntegrationHttpMethod": "POST",
"Type": "AWS_PROXY",
"Uri": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [
"AppLambdaFunction",
"Arn"
]
},
"/invocations"
]
]
}
},
"MethodResponses": []
}
},
"ApiGatewayDeployment1620092914902": {
"Type": "AWS::ApiGateway::Deployment",
"Properties": {
"RestApiId": {
"Ref": "ApiGatewayRestApi"
},
"StageName": "dev"
},
"DependsOn": [
"ApiGatewayMethodAny",
"ApiGatewayMethodProxyVarAny"
]
},
"AppLambdaPermissionApiGateway": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Fn::GetAtt": [
"AppLambdaFunction",
"Arn"
]
},
"Action": "lambda:InvokeFunction",
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":execute-api:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":",
{
"Ref": "ApiGatewayRestApi"
},
"/*/*"
]
]
}
}
}
},
"Outputs": {
"ServerlessDeploymentBucketName": {
"Value": {
"Ref": "ServerlessDeploymentBucket"
},
"Export": {
"Name": "sls-localstack-lambda-dev-ServerlessDeploymentBucketName"
}
},
"AppLambdaFunctionQualifiedArn": {
"Description": "Current Lambda function version",
"Value": {
"Ref": "AppLambdaVersionuvewJZYompWGhR7PreRGORUZWWNhF3zGn5TtfgsWIs"
},
"Export": {
"Name": "sls-localstack-lambda-dev-AppLambdaFunctionQualifiedArn"
}
},
"ServiceEndpoint": {
"Description": "URL of the service endpoint",
"Value": {
"Fn::Join": [
"",
[
"https://",
{
"Ref": "ApiGatewayRestApi"
},
".execute-api.",
{
"Ref": "AWS::Region"
},
".",
{
"Ref": "AWS::URLSuffix"
},
"/dev"
]
]
},
"Export": {
"Name": "sls-localstack-lambda-dev-ServiceEndpoint"
}
}
}
},
"coreCloudFormationTemplate": {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "The AWS CloudFormation template for this Serverless application",
"Resources": {
"ServerlessDeploymentBucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketEncryption": {
"ServerSideEncryptionConfiguration": [
{
"ServerSideEncryptionByDefault": {
"SSEAlgorithm": "AES256"
}
}
]
}
}
},
"ServerlessDeploymentBucketPolicy": {
"Type": "AWS::S3::BucketPolicy",
"Properties": {
"Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"PolicyDocument": {
"Statement": [
{
"Action": "s3:*",
"Effect": "Deny",
"Principal": "*",
"Resource": [
{
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":s3:::",
{
"Ref": "ServerlessDeploymentBucket"
},
"/*"
]
]
},
{
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":s3:::",
{
"Ref": "ServerlessDeploymentBucket"
}
]
]
}
],
"Condition": {
"Bool": {
"aws:SecureTransport": false
}
}
}
]
}
}
}
},
"Outputs": {
"ServerlessDeploymentBucketName": {
"Value": {
"Ref": "ServerlessDeploymentBucket"
}
}
}
},
"vpc": {}
},
"pluginsData": {},
"functions": {
"app": {
"handler": "api/server.handler",
"events": [
{
"http": "ANY /"
},
{
"http": "ANY {proxy+}"
}
],
"name": "localstack-lambda-dev-app",
"package": {},
"memory": 512,
"timeout": 6,
"runtime": "nodejs12.x",
"vpc": {},
"versionLogicalId": "AppLambdaVersionuvewJZYompWGhR7PreRGORUZWWNhF3zGn5TtfgsWIs"
}
},
"configValidationMode": "warn",
"serviceFilename": "serverless.yml",
"layers": {},
"initialServerlessConfig": {
"service": {
"$ref": "$[\"service\"][\"serviceObject\"]"
},
"frameworkVersion": "2",
"provider": {
"$ref": "$[\"service\"][\"provider\"]"
},
"functions": {
"$ref": "$[\"service\"][\"functions\"]"
}
},
"isDashboardMonitoringPreconfigured": false,
"artifact": "/Users/duncanphelps/Documents/Learning/Javascript/sheetaki/sheetaki/.serverless/localstack-lambda.zip"
},
"package": {
"artifactDirectoryName": "serverless/localstack-lambda/dev/1620092918879-2021-05-04T01:48:38.879Z",
"artifact": "localstack-lambda.zip"
}
}

13
api/file/index.js Normal file

@ -0,0 +1,13 @@
const URL = require('url');
fs = require('fs');
const { getFile } = require('../s3');
module.exports = function (req, res) {
res.setHeader('Access-Control-Allow-Origin', '*');
const url = URL.parse(req.url, true);
if (!url.query.filename) return res.status(400).send("Must specify filename");
if (url.query.filename){
getFile(req, res, url, url.query.filename);
}
};

38
api/s3.js Normal file

@ -0,0 +1,38 @@
require('dotenv').config();
const S3 = require('aws-sdk/clients/s3');
const do_wb = require('../src/util');
const bucketName = process.env.AWS_BUCKET_NAME
const region = process.env.AWS_BUCKET_REGION
const secretAccessKey = process.env.AWS_SECRET_KEY
const accessKeyId = process.env.AWS_ACCESS_KEY
const s3 = new S3({
region,
accessKeyId,
secretAccessKey
})
function uploadFile(filename, file){
filename = filename.split("/");
filename = filename[filename.length - 1]
const uploadParams = {
Bucket: bucketName,
Key: filename,
Body: file
};
return s3.upload(uploadParams).promise()
}
exports.uploadFile = uploadFile;
function getFile(req, res, url, filename){
const params = {
Key: filename,
Bucket: bucketName,
}
s3.getObject(params, function (err, data) {
if (err) return res.status(500).send(err.message || err);
do_wb(req, data.Body, url, res);
});
}
exports.getFile = getFile;

44
api/save/index.js Normal file

@ -0,0 +1,44 @@
const URL = require('url'), fs = require('fs');
const do_wb = require('../../src/util');
const formidable = require('formidable-serverless');
const tmp = require('tmp');
const AWS = require('aws-sdk');
const { uploadFile } = require('../s3');
module.exports = function (req, res) {
res.setHeader('Access-Control-Allow-Origin', '*');
const url = URL.parse(req.url, true);
// const s3 = new AWS.S3({ endpoint: 'http://localhost:4566', s3ForcePathStyle: true });
const s3 = new AWS.S3();
/* parse form data */
const form = formidable({ multiples: true, maxFileSize: 2 * 1024 * 1024 });
/*create temp file*/
const newFile = () => {
const tmpobj = tmp.fileSync({postfix: '.xlsx'});
return tmpobj.name;
}
form.parse(req, (err, fields, files) => {
if (err) return res.status(400).send(err.message || err);
if (!url.query) url.query = fields;
["N", "t"].forEach(k => { if (!url.query[k] && fields[k] != null) url.query[k] = fields[k]; });
/* look for first uploaded file entry */
if (!files) return res.status(400).send("Missing file");
const fentries = Object.entries(files);
if (fentries.length == 0) return res.status(400).send("Missing file");
/* read file */
const file = fentries[0][1];
fs.readFile(file.path, async (err, body) => {
if (err) return res.status(500).send(err.message || err);
const tmpFile = newFile();
const result = await uploadFile(tmpFile, body);
console.log(result);
res.send(result.key);
});
});
};

@ -1,7 +1,10 @@
const serverless = require('serverless-http');
const express = require('express');
const app = express();
const data = require('./data/index');
const upload = require('./upload/index');
const save = require('./save/index');
const file = require('./file/index');
const bodyParser = require('body-parser');
const port = 3000;
@ -12,7 +15,10 @@ app.use(express.static('public'));
app.use('/api/data', data);
app.use('/api/upload', upload);
app.use('/api/save', save);
app.use('/api/file', file);
app.listen(port, () => {
console.log(`Listening on port: ${port}`);
});
// app.listen(port, () => {
// console.log(`Listening on port: ${port}`);
// });
module.exports.handler = serverless(app);

14
docker-compose.yml Executable file

@ -0,0 +1,14 @@
version: '3.1'
services:
localstack:
image: localstack/localstack:latest
environment:
- AWS_DEFAULT_REGION=us-east-1
- EDGE_PORT=4566
- SERVICES=lambda,s3,cloudformation,sts,apigateway,iam,route53
ports:
- '4566-4597:4566-4597'
volumes:
- "${TEMPDIR:-/tmp/localstack}:/temp/localstack"
- "/var/run/docker.sock:/var/run/docker.sock"

6
localstack_endpoints.json Executable file

@ -0,0 +1,6 @@
{
"CloudFormation" : "http://localhost:4566",
"CloudWatch" : "http://localhost:4566",
"Lambda" : "http://localhost:4566",
"S3" : "http://localhost:4566"
}

1541
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -11,10 +11,15 @@
"excel"
],
"dependencies": {
"aws-sdk": "^2.896.0",
"body-parser": "^1.19.0",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"formidable-serverless": "",
"request": "",
"serverless-http": "^2.7.0",
"serverless-localstack": "^0.4.30",
"tmp": "^0.2.1",
"xlsx": ""
},
"scripts": {

@ -66,6 +66,11 @@ for(var i = 0; i < URLS.length; ++i) {
<input type="submit" value="Click here to upload!">
</form>
<form method="POST" action="api/save" enctype="multipart/form-data"><input type="hidden" name="N" value="0">
<label for="file">Upload a file</label>
<input type="file" name="file" id="file">
<input type="submit" value="Click here to save!">
</form>
<b>This service is powered by <a href="http://sheetjs.com">SheetJS</a></b>

28
serverless.yml Executable file

@ -0,0 +1,28 @@
service: localstack-lambda
# plugins:
# - serverless-localstack
# custom:
# localstack:
# debug: true
# stages:
# - local
# - dev
# host: http://localhost # optional - LocalStack host to connect to
# edgePort: 4566 # optional - LocalStack edge port to connect to
# endpointFile: localstack_endpoints.json
frameworkVersion: '2'
provider:
name: aws
runtime: nodejs12.x
stage: dev
region: us-east-1
memorySize: 512
functions:
app:
handler: api/server.handler
events:
- http: ANY /
- http: 'ANY {proxy+}'

@ -9,7 +9,13 @@ module.exports = function(req, body, url, res) {
/* N parameter specifies worksheet index */
const N = url.query.N ? parseInt(url.query.N,10) : 0;
const addr = url.query.addr || null;
const val = url.query.val || null;
if (addr != null && val != null) {
XLSX.utils.sheet_add_aoa(wb.Sheets[wb.SheetNames[0]], [[val]], { origin: addr });
}
/* -1 -> return sheet names */
if(N < 0) switch(url.query.t || "csv") {
case "json": return res.status(200).send(JSON.stringify(wb.SheetNames.join("\n")));
@ -28,6 +34,11 @@ module.exports = function(req, body, url, res) {
switch(url.query.t) {
case "json": return res.status(200).json(XLSX.utils.sheet_to_json(ws, {header:1, raw:true}));
case "html": return XLSX.stream.to_html(ws).pipe(res);
case "file":
const buf = XLSX.write(wb, { type: "buffer", bookType: "xlsx" });
res.header("Content-Type", "application/vnd.ms-excel");
res.header("Content-Disposition", 'attachment; filename="test2.xlsx"');
return res.status(200).end(buf);
default: return XLSX.stream.to_csv(ws).pipe(res);
}
};