Attempt to at move app into app sub dir

This commit is contained in:
Kurt Hutten
2021-05-01 07:32:21 +10:00
parent 9db76458d1
commit 78677a99f8
220 changed files with 1 additions and 1 deletions

View File

@@ -0,0 +1,11 @@
FROM node:14
COPY package.json ./
COPY yarn.lock ./
COPY aws-emulator.js ./
RUN npm install
EXPOSE 8080
CMD ["node", "./aws-emulator.js"]

View File

@@ -0,0 +1,34 @@
# Serverless
We're using the serverless from work for deployment
```
sls deploy
```
But [Kurt Hutten](https://github.com/Irev-Dev) is the only one with credentials for deployment atm, though if you wanted to set your own account you could deploy to that if you wanted to test.
## Testing changes locally
You'll need to have Docker installed
Because of the way the docker containers to be deployed as lambdas on aws are somewhat specialised for the purpose we're using `docker-compose` to spin one up for each function/endpoint. So we've added a aws-emulation layer
Then cd into this folder `cd api/src/docker` and:
```bash
docker-compose up --build
```
The first time you run this, it has to build the main image it will take some time, but launching again will be quicker.
After which we'll also spin up a light express server to act as an emulator to transform some the request from the front end into how the lambda's expect them (This emulates the aws-api-gateway which changes tranforms the inbound requests somewhat).
```
yarn install
yarn emulate
```
You can now add OPENSCAD_BASE_URL="http://localhost:8080" to you .env file and restart your main dev process (`yarn rw dev`)
comment that line out if you want to go back to using the aws endpoint (and restart the dev process).
If you change anything in the `api/src/docker/openscad` directory, you will need to stop the docker process and restart it (will be fairly quick if you're only changing the js)

View File

@@ -0,0 +1,41 @@
const express = require('express')
var cors = require('cors')
const axios = require('axios')
const { restart } = require('nodemon')
const app = express()
const port = 8080
app.use(express.json())
app.use(cors())
const invocationURL = (port) =>
`http://localhost:${port}/2015-03-31/functions/function/invocations`
app.post('/openscad/preview', async (req, res) => {
try {
const { data } = await axios.post(invocationURL(5052), {
body: Buffer.from(JSON.stringify(req.body)).toString('base64'),
})
res.status(data.statusCode)
res.send(data.body)
} catch (e) {
res.status(500)
res.send()
}
})
app.post('/cadquery/stl', async (req, res) => {
console.log('making post request to 5060')
try {
const { data } = await axios.post(invocationURL(5060), {
body: Buffer.from(JSON.stringify(req.body)).toString('base64'),
})
res.status(data.statusCode)
res.send(data.body)
} catch (e) {
res.status(500)
res.send()
}
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})

View File

@@ -0,0 +1,49 @@
FROM public.ecr.aws/lts/ubuntu:20.04_stable
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update -qq
RUN apt-get -y -qq install software-properties-common dirmngr apt-transport-https lsb-release ca-certificates xvfb
RUN apt-get update -qq
RUN apt-get install -y wget
# install node14, see comment at the to of node14source_setup.sh
ADD common/node14source_setup.sh /nodesource_setup.sh
RUN ["chmod", "+x", "/nodesource_setup.sh"]
RUN bash nodesource_setup.sh
RUN apt-get install -y nodejs
# Install aws-lambda-cpp build dependencies, this is for the post install script in aws-lambda-ric (in package.json)
RUN apt-get update && \
apt-get install -y \
g++ \
make \
cmake \
unzip \
automake autoconf libtool \
libcurl4-openssl-dev
# Add the lambda emulator for local dev, (see entrypoint.sh for where it's used),
# I have the file locally (gitignored) to speed up build times (as it downloads everytime),
# but you can use the http version of the below ADD command or download it yourself from that url.
ADD common/aws-lambda-rie /usr/local/bin/aws-lambda-rie
# ADD https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/download/v1.0/aws-lambda-rie /usr/local/bin/aws-lambda-rie
RUN ["chmod", "+x", "/usr/local/bin/aws-lambda-rie"]
WORKDIR /var/task/
COPY cadquery/package*.json /var/task/
RUN npm install
# Get the distribution copy of cq-cli
RUN wget https://github.com/CadQuery/cq-cli/releases/download/v2.1.0/cq-cli-Linux-x86_64.zip
RUN unzip cq-cli-Linux-x86_64.zip
RUN chmod +x cq-cli/cq-cli
COPY cadquery/*.js /var/task/
COPY common/*.js /var/common/
COPY common/entrypoint.sh /entrypoint.sh
RUN ["chmod", "+x", "/entrypoint.sh"]
ENTRYPOINT ["sh", "/entrypoint.sh"]
CMD [ "cadquery.stl" ]

View File

@@ -0,0 +1,53 @@
const { runCQ } = require('./runCQ')
const middy = require('middy')
const { cors } = require('middy/middlewares')
// cors true does not seem to work in serverless.yml, perhaps docker lambdas aren't covered by that config
// special lambda just for responding to options requests
const preflightOptions = (req, _context, callback) => {
const response = {
statusCode: 204,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST',
'Access-Control-Allow-Headers': '*',
},
}
callback(null, response)
}
const stl = async (req, _context, callback) => {
_context.callbackWaitsForEmptyEventLoop = false
const eventBody = Buffer.from(req.body, 'base64').toString('ascii')
console.log(eventBody, 'eventBody')
const { file, settings } = JSON.parse(eventBody)
const { error, result, tempFile } = await runCQ({ file, settings })
if (error) {
const response = {
statusCode: 400,
body: JSON.stringify({ error, tempFile }),
}
callback(null, response)
} else {
console.log(`got result in route: ${result}, file is: ${tempFile}`)
const fs = require('fs')
const image = fs.readFileSync(`/tmp/${tempFile}/output.stl`, {
encoding: 'base64',
})
console.log(image, 'encoded image')
const response = {
statusCode: 200,
body: JSON.stringify({
imageBase64: image,
result,
tempFile,
}),
}
callback(null, response)
}
}
module.exports = {
stl: middy(stl).use(cors()),
preflightOptions,
}

View File

@@ -0,0 +1,16 @@
{
"name": "openscad-endpoint",
"version": "0.0.1",
"description": "endpoint for openscad",
"main": "index.js",
"author": "Kurt Hutten <kurt@kurthutten.com>",
"license": "",
"dependencies": {
"cors": "^2.8.5",
"middy": "^0.36.0",
"nanoid": "^3.1.20"
},
"devDependencies": {
"aws-lambda-ric": "^1.0.0"
}
}

View File

@@ -0,0 +1,15 @@
const { makeFile, runCommand } = require('../common/utils')
const { nanoid } = require('nanoid')
module.exports.runCQ = async ({ file, settings = {} } = {}) => {
const tempFile = await makeFile(file, '.py', nanoid)
const command = `cq-cli/cq-cli --codec stl --infile /tmp/${tempFile}/main.py --outfile /tmp/${tempFile}/output.stl`
console.log('command', command)
try {
const result = await runCommand(command, 30000)
return { result, tempFile }
} catch (error) {
return { error, tempFile }
}
}

View File

@@ -0,0 +1,386 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
accepts@~1.3.7:
version "1.3.7"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
dependencies:
mime-types "~2.1.24"
negotiator "0.6.2"
array-flatten@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
body-parser@1.19.0:
version "1.19.0"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
dependencies:
bytes "3.1.0"
content-type "~1.0.4"
debug "2.6.9"
depd "~1.1.2"
http-errors "1.7.2"
iconv-lite "0.4.24"
on-finished "~2.3.0"
qs "6.7.0"
raw-body "2.4.0"
type-is "~1.6.17"
bytes@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
content-disposition@0.5.3:
version "0.5.3"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd"
integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==
dependencies:
safe-buffer "5.1.2"
content-type@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
cookie-signature@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
cookie@0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
cors@^2.8.5:
version "2.8.5"
resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
dependencies:
object-assign "^4"
vary "^1"
debug@2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
depd@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
destroy@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
express@^4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
dependencies:
accepts "~1.3.7"
array-flatten "1.1.1"
body-parser "1.19.0"
content-disposition "0.5.3"
content-type "~1.0.4"
cookie "0.4.0"
cookie-signature "1.0.6"
debug "2.6.9"
depd "~1.1.2"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
finalhandler "~1.1.2"
fresh "0.5.2"
merge-descriptors "1.0.1"
methods "~1.1.2"
on-finished "~2.3.0"
parseurl "~1.3.3"
path-to-regexp "0.1.7"
proxy-addr "~2.0.5"
qs "6.7.0"
range-parser "~1.2.1"
safe-buffer "5.1.2"
send "0.17.1"
serve-static "1.14.1"
setprototypeof "1.1.1"
statuses "~1.5.0"
type-is "~1.6.18"
utils-merge "1.0.1"
vary "~1.1.2"
finalhandler@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
dependencies:
debug "2.6.9"
encodeurl "~1.0.2"
escape-html "~1.0.3"
on-finished "~2.3.0"
parseurl "~1.3.3"
statuses "~1.5.0"
unpipe "~1.0.0"
forwarded@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=
fresh@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
http-errors@1.7.2:
version "1.7.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
dependencies:
depd "~1.1.2"
inherits "2.0.3"
setprototypeof "1.1.1"
statuses ">= 1.5.0 < 2"
toidentifier "1.0.0"
http-errors@~1.7.2:
version "1.7.3"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
dependencies:
depd "~1.1.2"
inherits "2.0.4"
setprototypeof "1.1.1"
statuses ">= 1.5.0 < 2"
toidentifier "1.0.0"
iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
dependencies:
safer-buffer ">= 2.1.2 < 3"
inherits@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
inherits@2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ipaddr.js@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
mime-db@1.46.0:
version "1.46.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.46.0.tgz#6267748a7f799594de3cbc8cde91def349661cee"
integrity sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==
mime-types@~2.1.24:
version "2.1.29"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.29.tgz#1d4ab77da64b91f5f72489df29236563754bb1b2"
integrity sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==
dependencies:
mime-db "1.46.0"
mime@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
ms@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
nanoid@^3.1.20:
version "3.1.20"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788"
integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==
negotiator@0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
object-assign@^4:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
on-finished@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
dependencies:
ee-first "1.1.1"
parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
path-to-regexp@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
proxy-addr@~2.0.5:
version "2.0.6"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf"
integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==
dependencies:
forwarded "~0.1.2"
ipaddr.js "1.9.1"
qs@6.7.0:
version "6.7.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332"
integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==
dependencies:
bytes "3.1.0"
http-errors "1.7.2"
iconv-lite "0.4.24"
unpipe "1.0.0"
safe-buffer@5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
"safer-buffer@>= 2.1.2 < 3":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
send@0.17.1:
version "0.17.1"
resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==
dependencies:
debug "2.6.9"
depd "~1.1.2"
destroy "~1.0.4"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
http-errors "~1.7.2"
mime "1.6.0"
ms "2.1.1"
on-finished "~2.3.0"
range-parser "~1.2.1"
statuses "~1.5.0"
serve-static@1.14.1:
version "1.14.1"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
dependencies:
encodeurl "~1.0.2"
escape-html "~1.0.3"
parseurl "~1.3.3"
send "0.17.1"
setprototypeof@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
"statuses@>= 1.5.0 < 2", statuses@~1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
toidentifier@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
type-is@~1.6.17, type-is@~1.6.18:
version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
dependencies:
media-typer "0.3.0"
mime-types "~2.1.24"
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
utils-merge@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
vary@^1, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=

View File

@@ -0,0 +1,6 @@
#!/bin/sh
if [ -z "${AWS_LAMBDA_RUNTIME_API}" ]; then
/usr/local/bin/aws-lambda-rie /usr/bin/npx aws-lambda-ric $1
else
/usr/bin/npx aws-lambda-ric $1
fi

View File

@@ -0,0 +1,349 @@
#!/bin/bash
# CADHUB COMMENT
# This was fetch on the fly in the dockerfile "RUN curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh"
# Because we rely on the bash script to install node 14 it make sense to have it in version control
# incase it disapears, plus will speed up build time a little.
# If we upgrade node versions for the lambdas we replace this file
# Discussion, issues and change requests at:
# https://github.com/nodesource/distributions
#
# Script to install the NodeSource Node.js 14.x repo onto a
# Debian or Ubuntu system.
#
# Run as root or insert `sudo -E` before `bash`:
#
# curl -sL https://deb.nodesource.com/setup_14.x | bash -
# or
# wget -qO- https://deb.nodesource.com/setup_14.x | bash -
#
# CONTRIBUTIONS TO THIS SCRIPT
#
# This script is built from a template in
# https://github.com/nodesource/distributions/tree/master/deb/src
# please don't submit pull requests against the built scripts.
#
export DEBIAN_FRONTEND=noninteractive
SCRSUFFIX="_14.x"
NODENAME="Node.js 14.x"
NODEREPO="node_14.x"
NODEPKG="nodejs"
print_status() {
echo
echo "## $1"
echo
}
if test -t 1; then # if terminal
ncolors=$(which tput > /dev/null && tput colors) # supports color
if test -n "$ncolors" && test $ncolors -ge 8; then
termcols=$(tput cols)
bold="$(tput bold)"
underline="$(tput smul)"
standout="$(tput smso)"
normal="$(tput sgr0)"
black="$(tput setaf 0)"
red="$(tput setaf 1)"
green="$(tput setaf 2)"
yellow="$(tput setaf 3)"
blue="$(tput setaf 4)"
magenta="$(tput setaf 5)"
cyan="$(tput setaf 6)"
white="$(tput setaf 7)"
fi
fi
print_bold() {
title="$1"
text="$2"
echo
echo "${red}================================================================================${normal}"
echo "${red}================================================================================${normal}"
echo
echo -e " ${bold}${yellow}${title}${normal}"
echo
echo -en " ${text}"
echo
echo "${red}================================================================================${normal}"
echo "${red}================================================================================${normal}"
}
bail() {
echo 'Error executing command, exiting'
exit 1
}
exec_cmd_nobail() {
echo "+ $1"
bash -c "$1"
}
exec_cmd() {
exec_cmd_nobail "$1" || bail
}
node_deprecation_warning() {
if [[ "X${NODENAME}" == "Xio.js 1.x" ||
"X${NODENAME}" == "Xio.js 2.x" ||
"X${NODENAME}" == "Xio.js 3.x" ||
"X${NODENAME}" == "XNode.js 0.10" ||
"X${NODENAME}" == "XNode.js 0.12" ||
"X${NODENAME}" == "XNode.js 4.x LTS Argon" ||
"X${NODENAME}" == "XNode.js 5.x" ||
"X${NODENAME}" == "XNode.js 6.x LTS Boron" ||
"X${NODENAME}" == "XNode.js 7.x" ||
"X${NODENAME}" == "XNode.js 8.x LTS Carbon" ||
"X${NODENAME}" == "XNode.js 9.x" ||
"X${NODENAME}" == "XNode.js 11.x" ||
"X${NODENAME}" == "XNode.js 13.x" ]]; then
print_bold \
" DEPRECATION WARNING " "\
${bold}${NODENAME} is no longer actively supported!${normal}
${bold}You will not receive security or critical stability updates${normal} for this version.
You should migrate to a supported version of Node.js as soon as possible.
Use the installation script that corresponds to the version of Node.js you
wish to install. e.g.
* ${green}https://deb.nodesource.com/setup_10.x — Node.js 10 LTS \"Dubnium\"${normal}
* ${green}https://deb.nodesource.com/setup_12.x — Node.js 12 LTS \"Erbium\"${normal} (recommended)
* ${green}https://deb.nodesource.com/setup_14.x — Node.js 14 LTS \"Fermium\"${normal}
* ${green}https://deb.nodesource.com/setup_15.x — Node.js 15 \"Fifteen\"${normal}
Please see ${bold}https://github.com/nodejs/Release${normal} for details about which
version may be appropriate for you.
The ${bold}NodeSource${normal} Node.js distributions repository contains
information both about supported versions of Node.js and supported Linux
distributions. To learn more about usage, see the repository:
${bold}https://github.com/nodesource/distributions${normal}
"
echo
echo "Continuing in 20 seconds ..."
echo
sleep 20
fi
}
script_deprecation_warning() {
if [ "X${SCRSUFFIX}" == "X" ]; then
print_bold \
" SCRIPT DEPRECATION WARNING " "\
This script, located at ${bold}https://deb.nodesource.com/setup${normal}, used to
install Node.js 0.10, is deprecated and will eventually be made inactive.
You should use the script that corresponds to the version of Node.js you
wish to install. e.g.
* ${green}https://deb.nodesource.com/setup_10.x — Node.js 10 LTS \"Dubnium\"${normal}
* ${green}https://deb.nodesource.com/setup_12.x — Node.js 12 LTS \"Erbium\"${normal} (recommended)
* ${green}https://deb.nodesource.com/setup_14.x — Node.js 14 LTS \"Fermium\"${normal}
* ${green}https://deb.nodesource.com/setup_15.x — Node.js 15 \"Fifteen\"${normal}
Please see ${bold}https://github.com/nodejs/Release${normal} for details about which
version may be appropriate for you.
The ${bold}NodeSource${normal} Node.js Linux distributions GitHub repository contains
information about which versions of Node.js and which Linux distributions
are supported and how to use the install scripts.
${bold}https://github.com/nodesource/distributions${normal}
"
echo
echo "Continuing in 20 seconds (press Ctrl-C to abort) ..."
echo
sleep 20
fi
}
setup() {
script_deprecation_warning
node_deprecation_warning
print_status "Installing the NodeSource ${NODENAME} repo..."
if $(uname -m | grep -Eq ^armv6); then
print_status "You appear to be running on ARMv6 hardware. Unfortunately this is not currently supported by the NodeSource Linux distributions. Please use the 'linux-armv6l' binary tarballs available directly from nodejs.org for Node.js 4 and later."
exit 1
fi
PRE_INSTALL_PKGS=""
# Check that HTTPS transport is available to APT
# (Check snaked from: https://get.docker.io/ubuntu/)
if [ ! -e /usr/lib/apt/methods/https ]; then
PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} apt-transport-https"
fi
if [ ! -x /usr/bin/lsb_release ]; then
PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} lsb-release"
fi
if [ ! -x /usr/bin/curl ] && [ ! -x /usr/bin/wget ]; then
PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} curl"
fi
# Used by apt-key to add new keys
if [ ! -x /usr/bin/gpg ]; then
PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} gnupg"
fi
# Populating Cache
print_status "Populating apt-get cache..."
exec_cmd 'apt-get update'
if [ "X${PRE_INSTALL_PKGS}" != "X" ]; then
print_status "Installing packages required for setup:${PRE_INSTALL_PKGS}..."
# This next command needs to be redirected to /dev/null or the script will bork
# in some environments
exec_cmd "apt-get install -y${PRE_INSTALL_PKGS} > /dev/null 2>&1"
fi
IS_PRERELEASE=$(lsb_release -d | grep 'Ubuntu .*development' >& /dev/null; echo $?)
if [[ $IS_PRERELEASE -eq 0 ]]; then
print_status "Your distribution, identified as \"$(lsb_release -d -s)\", is a pre-release version of Ubuntu. NodeSource does not maintain official support for Ubuntu versions until they are formally released. You can try using the manual installation instructions available at https://github.com/nodesource/distributions and use the latest supported Ubuntu version name as the distribution identifier, although this is not guaranteed to work."
exit 1
fi
DISTRO=$(lsb_release -c -s)
check_alt() {
if [ "X${DISTRO}" == "X${2}" ]; then
echo
echo "## You seem to be using ${1} version ${DISTRO}."
echo "## This maps to ${3} \"${4}\"... Adjusting for you..."
DISTRO="${4}"
fi
}
check_alt "SolydXK" "solydxk-9" "Debian" "stretch"
check_alt "Kali" "sana" "Debian" "jessie"
check_alt "Kali" "kali-rolling" "Debian" "bullseye"
check_alt "Sparky Linux" "Tyche" "Debian" "stretch"
check_alt "Sparky Linux" "Nibiru" "Debian" "buster"
check_alt "MX Linux 17" "Horizon" "Debian" "stretch"
check_alt "MX Linux 18" "Continuum" "Debian" "stretch"
check_alt "MX Linux 19" "patito feo" "Debian" "buster"
check_alt "Linux Mint" "maya" "Ubuntu" "precise"
check_alt "Linux Mint" "qiana" "Ubuntu" "trusty"
check_alt "Linux Mint" "rafaela" "Ubuntu" "trusty"
check_alt "Linux Mint" "rebecca" "Ubuntu" "trusty"
check_alt "Linux Mint" "rosa" "Ubuntu" "trusty"
check_alt "Linux Mint" "sarah" "Ubuntu" "xenial"
check_alt "Linux Mint" "serena" "Ubuntu" "xenial"
check_alt "Linux Mint" "sonya" "Ubuntu" "xenial"
check_alt "Linux Mint" "sylvia" "Ubuntu" "xenial"
check_alt "Linux Mint" "tara" "Ubuntu" "bionic"
check_alt "Linux Mint" "tessa" "Ubuntu" "bionic"
check_alt "Linux Mint" "tina" "Ubuntu" "bionic"
check_alt "Linux Mint" "tricia" "Ubuntu" "bionic"
check_alt "Linux Mint" "ulyana" "Ubuntu" "focal"
check_alt "Linux Mint" "ulyssa" "Ubuntu" "focal"
check_alt "LMDE" "betsy" "Debian" "jessie"
check_alt "LMDE" "cindy" "Debian" "stretch"
check_alt "LMDE" "debbie" "Debian" "buster"
check_alt "elementaryOS" "luna" "Ubuntu" "precise"
check_alt "elementaryOS" "freya" "Ubuntu" "trusty"
check_alt "elementaryOS" "loki" "Ubuntu" "xenial"
check_alt "elementaryOS" "juno" "Ubuntu" "bionic"
check_alt "elementaryOS" "hera" "Ubuntu" "bionic"
check_alt "elementaryOS" "odin" "Ubuntu" "focal"
check_alt "Trisquel" "toutatis" "Ubuntu" "precise"
check_alt "Trisquel" "belenos" "Ubuntu" "trusty"
check_alt "Trisquel" "flidas" "Ubuntu" "xenial"
check_alt "Trisquel" "etiona" "Ubuntu" "bionic"
check_alt "Uruk GNU/Linux" "lugalbanda" "Ubuntu" "xenial"
check_alt "BOSS" "anokha" "Debian" "wheezy"
check_alt "BOSS" "anoop" "Debian" "jessie"
check_alt "BOSS" "drishti" "Debian" "stretch"
check_alt "BOSS" "unnati" "Debian" "buster"
check_alt "bunsenlabs" "bunsen-hydrogen" "Debian" "jessie"
check_alt "bunsenlabs" "helium" "Debian" "stretch"
check_alt "bunsenlabs" "lithium" "Debian" "buster"
check_alt "Tanglu" "chromodoris" "Debian" "jessie"
check_alt "PureOS" "green" "Debian" "sid"
check_alt "PureOS" "amber" "Debian" "buster"
check_alt "Devuan" "jessie" "Debian" "jessie"
check_alt "Devuan" "ascii" "Debian" "stretch"
check_alt "Devuan" "beowulf" "Debian" "buster"
check_alt "Devuan" "ceres" "Debian" "sid"
check_alt "Deepin" "panda" "Debian" "sid"
check_alt "Deepin" "unstable" "Debian" "sid"
check_alt "Deepin" "stable" "Debian" "buster"
check_alt "Pardus" "onyedi" "Debian" "stretch"
check_alt "Liquid Lemur" "lemur-3" "Debian" "stretch"
check_alt "Astra Linux" "orel" "Debian" "stretch"
check_alt "Ubilinux" "dolcetto" "Debian" "stretch"
if [ "X${DISTRO}" == "Xdebian" ]; then
print_status "Unknown Debian-based distribution, checking /etc/debian_version..."
NEWDISTRO=$([ -e /etc/debian_version ] && cut -d/ -f1 < /etc/debian_version)
if [ "X${NEWDISTRO}" == "X" ]; then
print_status "Could not determine distribution from /etc/debian_version..."
else
DISTRO=$NEWDISTRO
print_status "Found \"${DISTRO}\" in /etc/debian_version..."
fi
fi
print_status "Confirming \"${DISTRO}\" is supported..."
if [ -x /usr/bin/curl ]; then
exec_cmd_nobail "curl -sLf -o /dev/null 'https://deb.nodesource.com/${NODEREPO}/dists/${DISTRO}/Release'"
RC=$?
else
exec_cmd_nobail "wget -qO /dev/null -o /dev/null 'https://deb.nodesource.com/${NODEREPO}/dists/${DISTRO}/Release'"
RC=$?
fi
if [[ $RC != 0 ]]; then
print_status "Your distribution, identified as \"${DISTRO}\", is not currently supported, please contact NodeSource at https://github.com/nodesource/distributions/issues if you think this is incorrect or would like your distribution to be considered for support"
exit 1
fi
if [ -f "/etc/apt/sources.list.d/chris-lea-node_js-$DISTRO.list" ]; then
print_status 'Removing Launchpad PPA Repository for NodeJS...'
exec_cmd_nobail 'add-apt-repository -y -r ppa:chris-lea/node.js'
exec_cmd "rm -f /etc/apt/sources.list.d/chris-lea-node_js-${DISTRO}.list"
fi
print_status 'Adding the NodeSource signing key to your keyring...'
if [ -x /usr/bin/curl ]; then
exec_cmd 'curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -'
else
exec_cmd 'wget -qO- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -'
fi
print_status "Creating apt sources list file for the NodeSource ${NODENAME} repo..."
exec_cmd "echo 'deb https://deb.nodesource.com/${NODEREPO} ${DISTRO} main' > /etc/apt/sources.list.d/nodesource.list"
exec_cmd "echo 'deb-src https://deb.nodesource.com/${NODEREPO} ${DISTRO} main' >> /etc/apt/sources.list.d/nodesource.list"
print_status 'Running `apt-get update` for you...'
exec_cmd 'apt-get update'
print_status """Run \`${bold}sudo apt-get install -y ${NODEPKG}${normal}\` to install ${NODENAME} and npm
## You may also need development tools to build native addons:
sudo apt-get install gcc g++ make
## To install the Yarn package manager, run:
curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo \"deb https://dl.yarnpkg.com/debian/ stable main\" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get install yarn
"""
}
## Defer setup until we have the complete script
setup

View File

@@ -0,0 +1,41 @@
const { exec } = require('child_process')
const { promises } = require('fs')
const { writeFile } = promises
async function makeFile(file, extension = '.scad', makeHash) {
const tempFile = 'a' + makeHash() // 'a' ensure nothing funny happens if it start with a bad character like "-", maybe I should pick a safer id generator :shrug:
console.log(`file to write: ${file}`)
await runCommand(`mkdir /tmp/${tempFile}`)
await writeFile(`/tmp/${tempFile}/main${extension}`, file)
return tempFile
}
async function runCommand(command, timeout = 5000) {
return new Promise((resolve, reject) => {
exec(command, (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`)
console.log(`stderr: ${stderr}`)
console.log(`stdout: ${stdout}`)
reject(stdout || stderr) // it seems random if the message is in stdout or stderr, but not normally both
return
}
if (stderr) {
console.log(`stderr: ${stderr}`)
resolve(stderr)
return
}
console.log(`stdout: ${stdout}`)
resolve(stdout)
})
setTimeout(() => {
reject('timeout')
}, timeout)
})
}
module.exports = {
runCommand,
makeFile,
}

View File

@@ -0,0 +1,44 @@
services:
# aws-emulator:
# build: .
# networks:
# - awsland
# ports:
# - "5050:8080"
openscad-health:
build:
context: ./
dockerfile: ./openscad/.
image: openscad
command: openscad.health
ports:
- "5051:8080"
openscad-preview:
image: openscad
# build: ./openscad/.
command: openscad.preview
# networks:
# - awsland
ports:
- "5052:8080"
openscad-stl:
image: openscad
# build: ./openscad/.
command: openscad.stl
ports:
- "5053:8080"
cadquery-stl:
build:
context: ./
dockerfile: ./cadquery/.
command: cadquery.stl
ports:
- 5060:8080
# networks:
# awsland:
# name: awsland

View File

@@ -0,0 +1,46 @@
FROM public.ecr.aws/lts/ubuntu:20.04_stable
ARG DEBIAN_FRONTEND=noninteractive
## install things needed to run openscad (xvfb is an important one)
RUN apt-get update -qq
# double check this below, I'm not sure we need inkscape etc
RUN apt-get -y -qq install software-properties-common dirmngr apt-transport-https lsb-release ca-certificates xvfb imagemagick unzip inkscape
RUN apt-get update -qq
RUN apt-get install -y -qq openscad
RUN apt-get install -y curl
# install node14, see comment at the to of node14source_setup.sh
ADD common/node14source_setup.sh /nodesource_setup.sh
RUN ["chmod", "+x", "/nodesource_setup.sh"]
RUN bash nodesource_setup.sh
RUN apt-get install -y nodejs
# Install aws-lambda-cpp build dependencies, this is for the post install script in aws-lambda-ric (in package.json)
RUN apt-get update && \
apt-get install -y \
g++ \
make \
cmake \
unzip \
automake autoconf libtool \
libcurl4-openssl-dev
# Add the lambda emulator for local dev, (see entrypoint.sh for where it's used),
# I have the file locally (gitignored) to speed up build times (as it downloads everytime),
# but you can use the http version of the below ADD command or download it yourself from that url.
ADD common/aws-lambda-rie /usr/local/bin/aws-lambda-rie
# ADD https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/download/v1.0/aws-lambda-rie /usr/local/bin/aws-lambda-rie
RUN ["chmod", "+x", "/usr/local/bin/aws-lambda-rie"]
WORKDIR /var/task/
COPY openscad/package*.json /var/task/
RUN npm install
COPY openscad/*.js /var/task/
COPY common/*.js /var/common/
COPY common/entrypoint.sh /entrypoint.sh
RUN ["chmod", "+x", "/entrypoint.sh"]
ENTRYPOINT ["sh", "/entrypoint.sh"]
CMD [ "openscad.render" ]

View File

@@ -0,0 +1,95 @@
const { runScad, stlExport } = require('./runScad')
const middy = require('middy')
const { cors } = require('middy/middlewares')
const health = async () => {
console.log('Health endpoint')
return {
statusCode: 200,
body: 'ok',
}
}
// cors true does not seem to work in serverless.yml, perhaps docker lambdas aren't covered by that config
// special lambda just for responding to options requests
const preflightOptions = (req, _context, callback) => {
const response = {
statusCode: 204,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST',
'Access-Control-Allow-Headers': '*',
},
}
callback(null, response)
}
const preview = async (req, _context, callback) => {
_context.callbackWaitsForEmptyEventLoop = false
const eventBody = Buffer.from(req.body, 'base64').toString('ascii')
console.log(eventBody, 'eventBody')
const { file, settings } = JSON.parse(eventBody)
const { error, result, tempFile } = await runScad({ file, settings })
if (error) {
const response = {
statusCode: 400,
body: JSON.stringify({ error, tempFile }),
}
callback(null, response)
} else {
console.log(`got result in route: ${result}, file is: ${tempFile}`)
const fs = require('fs')
const image = fs.readFileSync(`/tmp/${tempFile}/output.png`, {
encoding: 'base64',
})
console.log(image, 'encoded image')
const response = {
statusCode: 200,
body: JSON.stringify({
imageBase64: image,
result,
tempFile,
}),
}
callback(null, response)
}
}
const stl = async (req, _context, callback) => {
_context.callbackWaitsForEmptyEventLoop = false
const eventBody = Buffer.from(req.body, 'base64').toString('ascii')
console.log(eventBody, 'eventBody')
const { file } = JSON.parse(eventBody)
const { error, result, tempFile } = await stlExport({ file })
if (error) {
const response = {
statusCode: 400,
body: { error, tempFile },
}
callback(null, response)
} else {
console.log(`got result in route: ${result}, file is: ${tempFile}`)
const fs = require('fs')
const stl = fs.readFileSync(`/tmp/${tempFile}/output.stl`, {
encoding: 'base64',
})
console.log('encoded stl', stl)
const response = {
statusCode: 200,
headers: {
'content-type': 'application/stl',
},
body: stl,
isBase64Encoded: true,
}
console.log('callback fired')
callback(null, response)
}
}
module.exports = {
health: middy(health).use(cors()),
stl: middy(stl).use(cors()),
preview: middy(preview).use(cors()),
preflightOptions,
}

View File

@@ -0,0 +1,16 @@
{
"name": "openscad-endpoint",
"version": "0.0.1",
"description": "endpoint for openscad",
"main": "index.js",
"author": "Kurt Hutten <kurt@kurthutten.com>",
"license": "",
"dependencies": {
"cors": "^2.8.5",
"middy": "^0.36.0",
"nanoid": "^3.1.20"
},
"devDependencies": {
"aws-lambda-ric": "^1.0.0"
}
}

View File

@@ -0,0 +1,42 @@
const { makeFile, runCommand } = require('../common/utils')
const { nanoid } = require('nanoid')
module.exports.runScad = async ({
file,
settings: {
size: { x = 500, y = 500 } = {},
camera: {
position = { x: 40, y: 40, z: 40 },
rotation = { x: 55, y: 0, z: 25 },
dist = 200,
} = {},
} = {}, // TODO add view settings
} = {}) => {
const tempFile = await makeFile(file, '.scad', nanoid)
const { x: rx, y: ry, z: rz } = rotation
const { x: px, y: py, z: pz } = position
const cameraArg = `--camera=${px},${py},${pz},${rx},${ry},${rz},${dist}`
const command = `xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" openscad -o /tmp/${tempFile}/output.png ${cameraArg} --imgsize=${x},${y} --colorscheme DeepOcean /tmp/${tempFile}/main.scad`
console.log('command', command)
try {
const result = await runCommand(command, 15000)
return { result, tempFile }
} catch (error) {
return { error, tempFile }
}
}
module.exports.stlExport = async ({ file } = {}) => {
const tempFile = await makeFile(file, '.scad', nanoid)
try {
const result = await runCommand(
`openscad -o /tmp/${tempFile}/output.stl /tmp/${tempFile}/main.scad`,
300000 // lambda will time out before this, we might need to look at background jobs if we do git integration stl generation
)
return { result, tempFile }
} catch (error) {
return { error, tempFile }
}
}

View File

@@ -0,0 +1,386 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
accepts@~1.3.7:
version "1.3.7"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
dependencies:
mime-types "~2.1.24"
negotiator "0.6.2"
array-flatten@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
body-parser@1.19.0:
version "1.19.0"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
dependencies:
bytes "3.1.0"
content-type "~1.0.4"
debug "2.6.9"
depd "~1.1.2"
http-errors "1.7.2"
iconv-lite "0.4.24"
on-finished "~2.3.0"
qs "6.7.0"
raw-body "2.4.0"
type-is "~1.6.17"
bytes@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
content-disposition@0.5.3:
version "0.5.3"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd"
integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==
dependencies:
safe-buffer "5.1.2"
content-type@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
cookie-signature@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
cookie@0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
cors@^2.8.5:
version "2.8.5"
resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
dependencies:
object-assign "^4"
vary "^1"
debug@2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
depd@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
destroy@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
express@^4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
dependencies:
accepts "~1.3.7"
array-flatten "1.1.1"
body-parser "1.19.0"
content-disposition "0.5.3"
content-type "~1.0.4"
cookie "0.4.0"
cookie-signature "1.0.6"
debug "2.6.9"
depd "~1.1.2"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
finalhandler "~1.1.2"
fresh "0.5.2"
merge-descriptors "1.0.1"
methods "~1.1.2"
on-finished "~2.3.0"
parseurl "~1.3.3"
path-to-regexp "0.1.7"
proxy-addr "~2.0.5"
qs "6.7.0"
range-parser "~1.2.1"
safe-buffer "5.1.2"
send "0.17.1"
serve-static "1.14.1"
setprototypeof "1.1.1"
statuses "~1.5.0"
type-is "~1.6.18"
utils-merge "1.0.1"
vary "~1.1.2"
finalhandler@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
dependencies:
debug "2.6.9"
encodeurl "~1.0.2"
escape-html "~1.0.3"
on-finished "~2.3.0"
parseurl "~1.3.3"
statuses "~1.5.0"
unpipe "~1.0.0"
forwarded@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=
fresh@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
http-errors@1.7.2:
version "1.7.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
dependencies:
depd "~1.1.2"
inherits "2.0.3"
setprototypeof "1.1.1"
statuses ">= 1.5.0 < 2"
toidentifier "1.0.0"
http-errors@~1.7.2:
version "1.7.3"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
dependencies:
depd "~1.1.2"
inherits "2.0.4"
setprototypeof "1.1.1"
statuses ">= 1.5.0 < 2"
toidentifier "1.0.0"
iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
dependencies:
safer-buffer ">= 2.1.2 < 3"
inherits@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
inherits@2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ipaddr.js@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
mime-db@1.46.0:
version "1.46.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.46.0.tgz#6267748a7f799594de3cbc8cde91def349661cee"
integrity sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==
mime-types@~2.1.24:
version "2.1.29"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.29.tgz#1d4ab77da64b91f5f72489df29236563754bb1b2"
integrity sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==
dependencies:
mime-db "1.46.0"
mime@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
ms@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
nanoid@^3.1.20:
version "3.1.20"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788"
integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==
negotiator@0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
object-assign@^4:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
on-finished@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
dependencies:
ee-first "1.1.1"
parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
path-to-regexp@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
proxy-addr@~2.0.5:
version "2.0.6"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf"
integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==
dependencies:
forwarded "~0.1.2"
ipaddr.js "1.9.1"
qs@6.7.0:
version "6.7.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332"
integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==
dependencies:
bytes "3.1.0"
http-errors "1.7.2"
iconv-lite "0.4.24"
unpipe "1.0.0"
safe-buffer@5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
"safer-buffer@>= 2.1.2 < 3":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
send@0.17.1:
version "0.17.1"
resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==
dependencies:
debug "2.6.9"
depd "~1.1.2"
destroy "~1.0.4"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
http-errors "~1.7.2"
mime "1.6.0"
ms "2.1.1"
on-finished "~2.3.0"
range-parser "~1.2.1"
statuses "~1.5.0"
serve-static@1.14.1:
version "1.14.1"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
dependencies:
encodeurl "~1.0.2"
escape-html "~1.0.3"
parseurl "~1.3.3"
send "0.17.1"
setprototypeof@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
"statuses@>= 1.5.0 < 2", statuses@~1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
toidentifier@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
type-is@~1.6.17, type-is@~1.6.18:
version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
dependencies:
media-typer "0.3.0"
mime-types "~2.1.24"
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
utils-merge@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
vary@^1, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=

View File

@@ -0,0 +1,20 @@
{
"name": "aws-emulator",
"version": "1.0.0",
"description": "thin layer so that we can use docker lambdas locally",
"scripts": {
"lambdas": "docker-compose up --build",
"emulate": "nodemon ./aws-emulator.js",
"watch": "concurrently \"yarn lambdas\" \"yarn emulate\""
},
"main": "aws-emulator.js",
"dependencies": {
"axios": "^0.21.1",
"cors": "^2.8.5",
"express": "^4.17.1"
},
"devDependencies": {
"concurrently": "^6.0.0",
"nodemon": "^2.0.7"
}
}

View File

@@ -0,0 +1,179 @@
service: cad-lambdas
# app and org for use with dashboard.serverless.com
#app: your-app-name
#org: your-org-name
# plugins:
# - serverless-offline
# You can pin your service to only deploy with a specific Serverless version
# Check out our docs for more details
frameworkVersion: '2'
provider:
name: aws
lambdaHashingVersion: 20201221
ecr:
images:
# this image is built locally and push to ECR
openscadimage:
path: ./
file: ./openscad/Dockerfile
cadqueryimage:
path: ./
file: ./cadquery/Dockerfile
apiGateway:
metrics: true
binaryMediaTypes:
# we need to allow binary types to be able to send back images and stls, but it would be better to be more specific
# ie image/png etc. as */* treats everything as binary including the json body as the input the lambdas
# which mean we need to decode the input bode from base64, but the images break with anything other than */* :(
- '*/*'
# you can overwrite defaults here
# stage: dev
# region: us-east-1
# you can add statements to the Lambda function's IAM Role here
# iamRoleStatements:
# - Effect: "Allow"
# Action:
# - "s3:ListBucket"
# Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ] }
# - Effect: "Allow"
# Action:
# - "s3:PutObject"
# Resource:
# Fn::Join:
# - ""
# - - "arn:aws:s3:::"
# - "Ref" : "ServerlessDeploymentBucket"
# - "/*"
# you can define service wide environment variables here
# environment:
# variable1: value1
functions:
# see preflightoptions comment in openscad.js
preflightopenscadpreview:
image:
name: openscadimage
command:
- openscad.preflightOptions
entryPoint:
- '/entrypoint.sh'
events:
- http:
path: openscad/preview
method: options
preflightopenscadstl:
image:
name: openscadimage
command:
- openscad.preflightOptions
entryPoint:
- '/entrypoint.sh'
events:
- http:
path: openscad/stl
method: options
openscadpreview:
image:
name: openscadimage
command:
- openscad.preview
entryPoint:
- '/entrypoint.sh'
events:
- http:
path: openscad/preview
method: post
timeout: 15
openscadstl:
image:
name: openscadimage
command:
- openscad.stl
entryPoint:
- '/entrypoint.sh'
events:
- http:
path: openscad/stl
method: post
timeout: 30
preflightcadquerystl:
image:
name: cadqueryimage
command:
- cadquery.preflightOptions
entryPoint:
- '/entrypoint.sh'
events:
- http:
path: cadquery/stl
method: options
cadquerystl:
image:
name: cadqueryimage
command:
- cadquery.stl
entryPoint:
- '/entrypoint.sh'
events:
- http:
path: cadquery/stl
method: post
timeout: 30
# The following are a few example events you can configure
# NOTE: Please make sure to change your handler code to work with those events
# Check the event documentation for details
# events:
# - httpApi:
# path: /users/create
# method: get
# - websocket: $connect
# - s3: ${env:BUCKET}
# - schedule: rate(10 minutes)
# - sns: greeter-topic
# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx
# - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
# - iot:
# sql: "SELECT * FROM 'some_topic'"
# - cloudwatchEvent:
# event:
# source:
# - "aws.ec2"
# detail-type:
# - "EC2 Instance State-change Notification"
# detail:
# state:
# - pending
# - cloudwatchLog: '/aws/lambda/hello'
# - cognitoUserPool:
# pool: MyUserPool
# trigger: PreSignUp
# - alb:
# listenerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:listener/app/my-load-balancer/50dc6c495c0c9188/
# priority: 1
# conditions:
# host: example.com
# path: /hello
# Define function environment variables here
# environment:
# variable2: value2
# you can add CloudFormation resource templates here
#resources:
# Resources:
# NewResource:
# Type: AWS::S3::Bucket
# Properties:
# BucketName: my-new-bucket
# Outputs:
# NewOutput:
# Description: "Description for the output"
# Value: "Some output value"

1479
app/api/src/docker/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
import {
createGraphQLHandler,
makeMergedSchema,
makeServices,
} from '@redwoodjs/api'
import schemas from 'src/graphql/**/*.{js,ts}'
import services from 'src/services/**/*.{js,ts}'
import { getCurrentUser } from 'src/lib/auth'
import { db } from 'src/lib/db'
export const handler = createGraphQLHandler({
getCurrentUser,
schema: makeMergedSchema({
schemas,
services: makeServices({ services }),
}),
onException: () => {
// Disconnect from your database with an unhandled exception.
db.$disconnect()
},
})

View File

@@ -0,0 +1,84 @@
import { createUserInsecure } from 'src/services/users/users.js'
import { db } from 'src/lib/db'
import { enforceAlphaNumeric, generateUniqueString } from 'src/services/helpers'
export const handler = async (req, _context) => {
const body = JSON.parse(req.body)
console.log(body)
console.log(_context)
// DUMP FROM THE LOGS ABOVE
/*
5:09:30 AM: 2020-10-19T18:09:30.011Z 9da27e24-b6ec-404e-8e7d-25b5d323b67a INFO {
event: 'signup',
instance_id: '403b7d63-17f9-48f1-a85f-3d6b41c7dad1',
user: {
id: '641222ee-3e61-4253-8c11-9f764779bcc5',
aud: '',
role: '',
email: 'k.hutten@protonmail.ch',
confirmation_sent_at: '2020-10-19T18:09:01Z',
app_metadata: { provider: 'email' },
user_metadata: { full_name: 'sick_dog', userName: 'hi bob' },
created_at: '2020-10-19T18:09:01Z',
updated_at: '2020-10-19T18:09:01Z'
}
}
5:09:30 AM: 2020-10-19T18:09:30.011Z 9da27e24-b6ec-404e-8e7d-25b5d323b67a INFO {
callbackWaitsForEmptyEventLoop: [Getter/Setter],
succeed: [Function],
fail: [Function],
done: [Function],
functionVersion: '$LATEST',
functionName: 'ba7eb4948d1313283ebb91472c689d38444f07ae2f4278da925d3ce7f1d94e3c',
memoryLimitInMB: '1024',
logGroupName: '/aws/lambda/ba7eb4948d1313283ebb91472c689d38444f07ae2f4278da925d3ce7f1d94e3c',
logStreamName: '2020/10/19/[$LATEST]af6ff2c067da44268b4a0c9d1e4ca1ea',
clientContext: {
custom: {
netlify: 'eyJpZGVudGl0eSI6eyJ1cmwiOiJodHRwczovL2FuZ3J5LWRpamtzdHJhLTAzMWExMC5uZXRsaWZ5LmFwcC8ubmV0bGlmeS9pZGVudGl0eSIsInRva2VuIjoiZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SmxlSEFpT2pFMk1ETXhNekV3TWprc0luTjFZaUk2SWpBaWZRLk54Q0hmb0I2aDRpc0V6NnpJREhWbThLTU5hcEZrb3g0dTFXS2dTemhzUncifSwic2l0ZV91cmwiOiJodHRwczovL2FuZ3J5LWRpamtzdHJhLTAzMWExMC5uZXRsaWZ5LmFwcCJ9'
},
identity: {
url: 'https://angry-dijkstra-031a10.netlify.app/.netlify/identity',
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MDMxMzEwMjksInN1YiI6IjAifQ.NxCHfoB6h4isEz6zIDHVm8KMNapFkox4u1WKgSzhsRw'
}
},
identity: undefined,
invokedFunctionArn: 'arn:aws:lambda:us-east-1:012533533302:function:ba7eb4948d1313283ebb91472c689d38444f07ae2f4278da925d3ce7f1d94e3c',
awsRequestId: '9da27e24-b6ec-404e-8e7d-25b5d323b67a',
getRemainingTimeInMillis: [Function: getRemainingTimeInMillis]
}
5:09:30 AM: Duration: 5.78 ms Memory Usage: 69 MB Init Duration: 199.35 ms
*/
const eventType = body.event
const user = body.user
const email = user.email
let roles = []
if (eventType === 'signup') {
roles.push('user')
const isUniqueCallback = async (seed) =>
db.user.findUnique({
where: { userName: seed },
})
const userNameSeed = enforceAlphaNumeric(user?.user_metadata?.userName)
const userName = await generateUniqueString(userNameSeed, isUniqueCallback) // TODO maybe come up with a better default userName?
const input = {
email,
userName,
name: user?.user_metadata?.full_name,
id: user.id,
}
await createUserInsecure({ input })
return {
statusCode: 200,
body: JSON.stringify({ app_metadata: { roles: roles } }),
}
} else {
return {
statusCode: 200,
}
}
}

View File

View File

@@ -0,0 +1,35 @@
export const schema = gql`
type Comment {
id: String!
text: String!
user: User!
userId: String!
part: Part!
partId: String!
createdAt: DateTime!
updatedAt: DateTime!
}
type Query {
comments: [Comment!]!
comment(id: String!): Comment
}
input CreateCommentInput {
text: String!
userId: String!
partId: String!
}
input UpdateCommentInput {
text: String
userId: String
partId: String
}
type Mutation {
createComment(input: CreateCommentInput!): Comment!
updateComment(id: String!, input: UpdateCommentInput!): Comment!
deleteComment(id: String!): Comment!
}
`

View File

@@ -0,0 +1,39 @@
export const schema = gql`
type PartReaction {
id: String!
emote: String!
user: User!
userId: String!
part: Part!
partId: String!
createdAt: DateTime!
updatedAt: DateTime!
}
type Query {
partReactions: [PartReaction!]!
partReaction(id: String!): PartReaction
partReactionsByPartId(partId: String!): [PartReaction!]!
}
input TogglePartReactionInput {
emote: String!
userId: String!
partId: String!
}
input UpdatePartReactionInput {
emote: String
userId: String
partId: String
}
type Mutation {
togglePartReaction(input: TogglePartReactionInput!): PartReaction!
updatePartReaction(
id: String!
input: UpdatePartReactionInput!
): PartReaction!
deletePartReaction(id: String!): PartReaction!
}
`

View File

@@ -0,0 +1,45 @@
export const schema = gql`
type Part {
id: String!
title: String!
description: String
code: String
mainImage: String
createdAt: DateTime!
updatedAt: DateTime!
deleted: Boolean!
user: User!
userId: String!
Comment: [Comment]!
Reaction(userId: String): [PartReaction]!
}
type Query {
parts(userName: String): [Part!]!
part(id: String!): Part
partByUserAndTitle(userName: String!, partTitle: String!): Part
}
input CreatePartInput {
title: String!
description: String
code: String
mainImage: String
userId: String!
}
input UpdatePartInput {
title: String
description: String
code: String
mainImage: String
userId: String
}
type Mutation {
createPart(input: CreatePartInput!): Part!
forkPart(input: CreatePartInput!): Part!
updatePart(id: String!, input: UpdatePartInput!): Part!
deletePart(id: String!): Part!
}
`

View File

@@ -0,0 +1,39 @@
export const schema = gql`
type SubjectAccessRequest {
id: String!
comment: String!
payload: String!
user: User!
userId: String!
createdAt: DateTime!
updatedAt: DateTime!
}
type Query {
subjectAccessRequests: [SubjectAccessRequest!]!
subjectAccessRequest(id: String!): SubjectAccessRequest
}
input CreateSubjectAccessRequestInput {
comment: String!
payload: String!
userId: String!
}
input UpdateSubjectAccessRequestInput {
comment: String
payload: String
userId: String
}
type Mutation {
createSubjectAccessRequest(
input: CreateSubjectAccessRequestInput!
): SubjectAccessRequest!
updateSubjectAccessRequest(
id: String!
input: UpdateSubjectAccessRequestInput!
): SubjectAccessRequest!
deleteSubjectAccessRequest(id: String!): SubjectAccessRequest!
}
`

View File

@@ -0,0 +1,46 @@
export const schema = gql`
type User {
id: String!
userName: String!
email: String!
name: String
createdAt: DateTime!
updatedAt: DateTime!
image: String
bio: String
Parts: [Part]!
Part(partTitle: String): Part
Reaction: [PartReaction]!
Comment: [Comment]!
SubjectAccessRequest: [SubjectAccessRequest]!
}
type Query {
users: [User!]!
user(id: String!): User
userName(userName: String!): User
}
input CreateUserInput {
userName: String!
email: String!
name: String
image: String
bio: String
}
input UpdateUserInput {
userName: String
email: String
name: String
image: String
bio: String
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: String!, input: UpdateUserInput!): User!
updateUserByUserName(userName: String!, input: UpdateUserInput!): User!
deleteUser(id: String!): User!
}
`

148
app/api/src/lib/auth.js Normal file
View File

@@ -0,0 +1,148 @@
// Define what you want `currentUser` to return throughout your app. For example,
// to return a real user from your database, you could do something like:
//
// export const getCurrentUser = async ({ email }) => {
// return await db.user.findUnique({ where: { email } })
// }
//
// If you want to enforce role-based access ...
//
// You'll need to set the currentUser's roles attributes to the
// collection of roles as defined by your app.
//
// This allows requireAuth() on the api side and hasRole() in the useAuth() hook on the web side
// to check if the user is assigned a given role or not.
//
// How you set the currentUser's roles depends on your auth provider and its implementation.
//
// For example, your decoded JWT may store `roles` in it namespaced `app_metadata`:
//
// {
// 'https://example.com/app_metadata': { authorization: { roles: ['admin'] } },
// 'https://example.com/user_metadata': {},
// iss: 'https://app.us.auth0.com/',
// sub: 'email|1234',
// aud: [
// 'https://example.com',
// 'https://app.us.auth0.com/userinfo'
// ],
// iat: 1596481520,
// exp: 1596567920,
// azp: '1l0w6JXXXXL880T',
// scope: 'openid profile email'
// }
//
// The parseJWT utility will extract the roles from decoded token.
//
// The app_medata claim may or may not be namespaced based on the auth provider.
// Note: Auth0 requires namespacing custom JWT claims
//
// Some providers, such as with Auth0, will set roles an authorization
// attribute in app_metadata (namespaced or not):
//
// 'app_metadata': { authorization: { roles: ['publisher'] } }
// 'https://example.com/app_metadata': { authorization: { roles: ['publisher'] } }
//
// Other providers may include roles simply within app_metadata:
//
// 'app_metadata': { roles: ['author'] }
// 'https://example.com/app_metadata': { roles: ['author'] }
//
// And yet other may define roles as a custom claim at the root of the decoded token:
//
// roles: ['admin']
//
// The function `getCurrentUser` should return the user information
// together with a collection of roles to check for role assignment:
import { AuthenticationError, ForbiddenError, parseJWT } from '@redwoodjs/api'
/**
* Use requireAuth in your services to check that a user is logged in,
* whether or not they are assigned a role, and optionally raise an
* error if they're not.
*
* @param {string=, string[]=} role - An optional role
*
* @example - No role-based access control.
*
* export const getCurrentUser = async (decoded) => {
* return await db.user.findUnique({ where: { decoded.email } })
* }
*
* @example - User info is contained in the decoded token and roles extracted
*
* export const getCurrentUser = async (decoded, { _token, _type }) => {
* return { ...decoded, roles: parseJWT({ decoded }).roles }
* }
*
* @example - User record query by email with namespaced app_metadata roles
*
* export const getCurrentUser = async (decoded) => {
* const currentUser = await db.user.findUnique({ where: { email: decoded.email } })
*
* return {
* ...currentUser,
* roles: parseJWT({ decoded: decoded, namespace: NAMESPACE }).roles,
* }
* }
*
* @example - User record query by an identity with app_metadata roles
*
* const getCurrentUser = async (decoded) => {
* const currentUser = await db.user.findUnique({ where: { userIdentity: decoded.sub } })
* return {
* ...currentUser,
* roles: parseJWT({ decoded: decoded }).roles,
* }
* }
*/
export const getCurrentUser = async (decoded, { _token, _type }) => {
return { ...decoded, roles: parseJWT({ decoded }).roles }
}
/**
* Use requireAuth in your services to check that a user is logged in,
* whether or not they are assigned a role, and optionally raise an
* error if they're not.
*
* @param {string=} roles - An optional role or list of roles
* @param {string[]=} roles - An optional list of roles
* @example
*
* // checks if currentUser is authenticated
* requireAuth()
*
* @example
*
* // checks if currentUser is authenticated and assigned one of the given roles
* requireAuth({ role: 'admin' })
* requireAuth({ role: ['editor', 'author'] })
* requireAuth({ role: ['publisher'] })
*/
export const requireAuth = ({ role } = {}) => {
if (!context.currentUser) {
throw new AuthenticationError("You don't have permission to do that.")
}
if (
typeof role !== 'undefined' &&
typeof role === 'string' &&
!context.currentUser.roles?.includes(role)
) {
throw new ForbiddenError("You don't have access to do that.")
}
if (
typeof role !== 'undefined' &&
Array.isArray(role) &&
!context.currentUser.roles?.some((r) => role.includes(r))
) {
throw new ForbiddenError("You don't have access to do that.")
}
if (context.currentUser?.sub === '5cea3906-1e8e-4673-8f0d-89e6a963c096') {
throw new ForbiddenError("That's a local admin ONLY.")
}
}

21
app/api/src/lib/db.js Normal file
View File

@@ -0,0 +1,21 @@
// See https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client/constructor
// for options.
import { PrismaClient } from '@prisma/client'
import { emitLogLevels, handlePrismaLogging } from '@redwoodjs/api/logger'
import { logger } from './logger'
/*
* Instance of the Prisma Client
*/
export const db = new PrismaClient({
log: emitLogLevels(['info', 'warn', 'error']),
})
handlePrismaLogging({
db,
logger,
logLevels: ['info', 'warn', 'error'],
})

17
app/api/src/lib/logger.ts Normal file
View File

@@ -0,0 +1,17 @@
import { createLogger } from '@redwoodjs/api/logger'
/**
* Creates a logger with RedwoodLoggerOptions
*
* These extend and override default LoggerOptions,
* can define a destination like a file or other supported pin log transport stream,
* and sets where or not to show the logger configuration settings (defaults to false)
*
* @param RedwoodLoggerOptions
*
* RedwoodLoggerOptions have
* @param {options} LoggerOptions - defines how to log, such as pretty printing, redaction, and format
* @param {string | DestinationStream} destination - defines where to log, such as a transport stream or file
* @param {boolean} showConfig - whether to display logger configuration on initialization
*/
export const logger = createLogger({})

44
app/api/src/lib/owner.js Normal file
View File

@@ -0,0 +1,44 @@
import { AuthenticationError, ForbiddenError } from '@redwoodjs/api'
import { db } from 'src/lib/db'
export const requireOwnership = async ({ userId, userName, partId } = {}) => {
// IMPORTANT, don't forget to await this function, as it will only block
// unwanted db actions if it has time to look up resources in the db.
if (!context.currentUser) {
throw new AuthenticationError("You don't have permission to do that.")
}
if (!userId && !userName && !partId) {
throw new ForbiddenError("You don't have access to do that.")
}
if (context.currentUser.roles?.includes('admin')) {
return
}
const netlifyUserId = context.currentUser?.sub
if (userId && userId !== netlifyUserId) {
throw new ForbiddenError("You don't own this resource.")
}
if (userName) {
const user = await db.user.findUnique({
where: { userName },
})
if (!user || user.id !== netlifyUserId) {
throw new ForbiddenError("You don't own this resource.")
}
}
if (partId) {
const user = await db.part
.findUnique({
where: { id: partId },
})
.user()
if (!user || user.id !== netlifyUserId) {
throw new ForbiddenError("You don't own this resource.")
}
}
}

View File

View File

@@ -0,0 +1,38 @@
import { db } from 'src/lib/db'
import { foreignKeyReplacement } from 'src/services/helpers'
export const comments = () => {
return db.comment.findMany()
}
export const comment = ({ id }) => {
return db.comment.findUnique({
where: { id },
})
}
export const createComment = ({ input }) => {
return db.comment.create({
data: foreignKeyReplacement(input),
})
}
export const updateComment = ({ id, input }) => {
return db.comment.update({
data: foreignKeyReplacement(input),
where: { id },
})
}
export const deleteComment = ({ id }) => {
return db.comment.delete({
where: { id },
})
}
export const Comment = {
user: (_obj, { root }) =>
db.comment.findUnique({ where: { id: root.id } }).user(),
part: (_obj, { root }) =>
db.comment.findUnique({ where: { id: root.id } }).part(),
}

View File

@@ -0,0 +1,9 @@
/*
import { comments } from './comments'
*/
describe('comments', () => {
it('returns true', () => {
expect(true).toBe(true)
})
})

View File

@@ -0,0 +1,48 @@
import { v2 as cloudinary } from 'cloudinary'
cloudinary.config({
cloud_name: 'irevdev',
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
})
export const foreignKeyReplacement = (input) => {
let output = input
const foreignKeys = Object.keys(input).filter((k) => k.match(/Id$/))
foreignKeys.forEach((key) => {
const modelName = key.replace(/Id$/, '')
const value = input[key]
delete output[key]
output = Object.assign(output, {
[modelName]: { connect: { id: value } },
})
})
return output
}
export const enforceAlphaNumeric = (string) =>
string.replace(/([^a-zA-Z\d_:])/g, '-')
export const generateUniqueString = async (
seed,
isUniqueCallback,
count = 0
) => {
const isUnique = !(await isUniqueCallback(seed))
if (isUnique) {
return seed
}
count += 1
const newSeed = count === 1 ? `${seed}_${count}` : seed.slice(0, -1) + count
return generateUniqueString(newSeed, isUniqueCallback, count)
}
export const destroyImage = ({ publicId }) =>
new Promise((resolve, reject) => {
cloudinary.uploader.destroy(publicId, (error, result) => {
if (error) {
reject(error)
return
}
resolve(result)
})
})

View File

@@ -0,0 +1,68 @@
import { UserInputError } from '@redwoodjs/api'
import { requireAuth } from 'src/lib/auth'
import { requireOwnership } from 'src/lib/owner'
import { db } from 'src/lib/db'
import { foreignKeyReplacement } from 'src/services/helpers'
export const partReactions = () => {
return db.partReaction.findMany()
}
export const partReaction = ({ id }) => {
return db.partReaction.findUnique({
where: { id },
})
}
export const partReactionsByPartId = ({ partId }) => {
return db.partReaction.findMany({
where: { partId: partId },
})
}
export const togglePartReaction = async ({ input }) => {
// if write fails emote_userId_partId @@unique constraint, then delete it instead
requireAuth()
await requireOwnership({ userId: input?.userId })
const legalReactions = ['❤️', '👍', '😄', '🙌'] // TODO figure out a way of sharing code between FE and BE, so this is consistent with web/src/components/EmojiReaction/EmojiReaction.js
if (!legalReactions.includes(input.emote)) {
throw new UserInputError(
`You can't react with '${
input.emote
}', only the following are allowed: ${legalReactions.join(', ')}`
)
}
let dbPromise
const inputClone = { ...input } // TODO foreignKeyReplacement mutates input, which I should fix but am lazy right now
try {
dbPromise = await db.partReaction.create({
data: foreignKeyReplacement(input),
})
} catch (e) {
dbPromise = db.partReaction.delete({
where: { emote_userId_partId: inputClone },
})
}
return dbPromise
}
export const updatePartReaction = ({ id, input }) => {
return db.partReaction.update({
data: foreignKeyReplacement(input),
where: { id },
})
}
export const deletePartReaction = ({ id }) => {
return db.partReaction.delete({
where: { id },
})
}
export const PartReaction = {
user: (_obj, { root }) =>
db.partReaction.findUnique({ where: { id: root.id } }).user(),
part: (_obj, { root }) =>
db.partReaction.findUnique({ where: { id: root.id } }).part(),
}

View File

@@ -0,0 +1,9 @@
/*
import { partReactions } from './partReactions'
*/
describe('partReactions', () => {
it('returns true', () => {
expect(true).toBe(true)
})
})

View File

@@ -0,0 +1,113 @@
import { db } from 'src/lib/db'
import {
foreignKeyReplacement,
enforceAlphaNumeric,
generateUniqueString,
destroyImage,
} from 'src/services/helpers'
import { requireAuth } from 'src/lib/auth'
import { requireOwnership } from 'src/lib/owner'
export const parts = ({ userName }) => {
if (!userName) {
return db.part.findMany({ where: { deleted: false } })
}
return db.part.findMany({
where: {
deleted: false,
user: {
userName,
},
},
})
}
export const part = ({ id }) => {
return db.part.findUnique({
where: { id },
})
}
export const partByUserAndTitle = async ({ userName, partTitle }) => {
const user = await db.user.findUnique({
where: {
userName,
},
})
return db.part.findUnique({
where: {
title_userId: {
title: partTitle,
userId: user.id,
},
},
})
}
export const createPart = async ({ input }) => {
requireAuth()
return db.part.create({
data: foreignKeyReplacement(input),
})
}
export const forkPart = async ({ input }) => {
// Only difference between create and fork part is that fork part will generate a unique title
// (for the user) if there is a conflict
const isUniqueCallback = async (seed) =>
db.part.findUnique({
where: {
title_userId: {
title: seed,
userId: input.userId,
},
},
})
const title = await generateUniqueString(input.title, isUniqueCallback)
// TODO change the description to `forked from userName/partName ${rest of description}`
return db.part.create({
data: foreignKeyReplacement({ ...input, title }),
})
}
export const updatePart = async ({ id, input }) => {
requireAuth()
await requireOwnership({ partId: id })
if (input.title) {
input.title = enforceAlphaNumeric(input.title)
}
const originalPart = await db.part.findUnique({ where: { id } })
const imageToDestroy =
originalPart.mainImage !== input.mainImage && originalPart.mainImage
const update = await db.part.update({
data: foreignKeyReplacement(input),
where: { id },
})
if (imageToDestroy) {
console.log(`image destroyed, publicId: ${imageToDestroy}, partId: ${id}`)
// destroy after the db has been updated
destroyImage({ publicId: imageToDestroy })
}
return update
}
export const deletePart = async ({ id }) => {
requireAuth()
await requireOwnership({ partId: id })
return db.part.update({
data: {
deleted: true,
},
where: { id },
})
}
export const Part = {
user: (_obj, { root }) =>
db.part.findUnique({ where: { id: root.id } }).user(),
Comment: (_obj, { root }) =>
db.part.findUnique({ where: { id: root.id } }).Comment(),
Reaction: (_obj, { root }) =>
db.part
.findUnique({ where: { id: root.id } })
.Reaction({ where: { userId: _obj.userId } }),
}

View File

@@ -0,0 +1,9 @@
/*
import { parts } from './parts'
*/
describe('parts', () => {
it('returns true', () => {
expect(true).toBe(true)
})
})

View File

@@ -0,0 +1,42 @@
import { db } from 'src/lib/db'
import { requireAuth } from 'src/lib/auth'
import { foreignKeyReplacement } from 'src/services/helpers'
export const subjectAccessRequests = () => {
requireAuth({ role: 'admin' })
return db.subjectAccessRequest.findMany()
}
export const subjectAccessRequest = ({ id }) => {
requireAuth({ role: 'admin' })
return db.subjectAccessRequest.findUnique({
where: { id },
})
}
export const createSubjectAccessRequest = ({ input }) => {
requireAuth({ role: 'admin' })
return db.subjectAccessRequest.create({
data: foreignKeyReplacement(input),
})
}
export const updateSubjectAccessRequest = ({ id, input }) => {
requireAuth({ role: 'admin' })
return db.subjectAccessRequest.update({
data: foreignKeyReplacement(input),
where: { id },
})
}
export const deleteSubjectAccessRequest = ({ id }) => {
requireAuth({ role: 'admin' })
return db.subjectAccessRequest.delete({
where: { id },
})
}
export const SubjectAccessRequest = {
user: (_obj, { root }) =>
db.subjectAccessRequest.findUnique({ where: { id: root.id } }).user(),
}

View File

@@ -0,0 +1,9 @@
/*
import { subjectAccessRequests } from './subjectAccessRequests'
*/
describe('subjectAccessRequests', () => {
it('returns true', () => {
expect(true).toBe(true)
})
})

View File

@@ -0,0 +1,94 @@
import { db } from 'src/lib/db'
import { requireAuth } from 'src/lib/auth'
import { requireOwnership } from 'src/lib/owner'
import { UserInputError } from '@redwoodjs/api'
import { enforceAlphaNumeric, destroyImage } from 'src/services/helpers'
export const users = () => {
requireAuth({ role: 'admin' })
return db.user.findMany()
}
export const user = ({ id }) => {
return db.user.findUnique({
where: { id },
})
}
export const userName = ({ userName }) => {
return db.user.findUnique({
where: { userName },
})
}
export const createUser = ({ input }) => {
requireAuth({ role: 'admin' })
createUserInsecure({ input })
}
export const createUserInsecure = ({ input }) => {
return db.user.create({
data: input,
})
}
export const updateUser = ({ id, input }) => {
requireAuth()
return db.user.update({
data: input,
where: { id },
})
}
export const updateUserByUserName = async ({ userName, input }) => {
requireAuth()
await requireOwnership({ userName })
if (input.userName) {
input.userName = enforceAlphaNumeric(input.userName)
}
if (input.userName && ['new', 'edit', 'update'].includes(input.userName)) {
//TODO complete this and use a regexp so that it's not case sensitive, don't want someone with the userName eDiT
throw new UserInputError(
`You've tried to used a protected word as you userName, try something other than `
)
}
const originalPart = await db.user.findUnique({ where: { userName } })
const imageToDestroy =
originalPart.image !== input.image && originalPart.image
const update = await db.user.update({
data: input,
where: { userName },
})
if (imageToDestroy) {
// destroy after the db has been updated
destroyImage({ publicId: imageToDestroy })
}
return update
}
export const deleteUser = ({ id }) => {
requireAuth({ role: 'admin' })
return db.user.delete({
where: { id },
})
}
export const User = {
Parts: (_obj, { root }) =>
db.user.findUnique({ where: { id: root.id } }).Part(),
Part: (_obj, { root }) =>
_obj.partTitle &&
db.part.findUnique({
where: {
title_userId: {
title: _obj.partTitle,
userId: root.id,
},
},
}),
Reaction: (_obj, { root }) =>
db.user.findUnique({ where: { id: root.id } }).Reaction(),
Comment: (_obj, { root }) =>
db.user.findUnique({ where: { id: root.id } }).Comment(),
SubjectAccessRequest: (_obj, { root }) =>
db.user.findUnique({ where: { id: root.id } }).SubjectAccessRequest(),
}

View File

@@ -0,0 +1,9 @@
/*
import { users } from './users'
*/
describe('users', () => {
it('returns true', () => {
expect(true).toBe(true)
})
})