diff --git a/api/src/docker/aws-emulator.js b/api/src/docker/aws-emulator.js index 4c722e2..fae018d 100644 --- a/api/src/docker/aws-emulator.js +++ b/api/src/docker/aws-emulator.js @@ -1,7 +1,7 @@ const express = require('express') var cors = require('cors') const axios = require('axios') -const stream = require('stream') +const { restart } = require('nodemon') const app = express() const port = 8080 app.use(express.json()) @@ -10,12 +10,30 @@ app.use(cors()) const invocationURL = (port) => `http://localhost:${port}/2015-03-31/functions/function/invocations` -app.post('/render', async (req, res) => { - const { data } = await axios.post(invocationURL(5052), { - body: Buffer.from(JSON.stringify(req.body)).toString('base64'), - }) - res.status(data.statusCode) - res.send(data.body) +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, () => { diff --git a/api/src/docker/cadquery/Dockerfile b/api/src/docker/cadquery/Dockerfile new file mode 100644 index 0000000..d44bb13 --- /dev/null +++ b/api/src/docker/cadquery/Dockerfile @@ -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" ] diff --git a/api/src/docker/cadquery/cadquery.js b/api/src/docker/cadquery/cadquery.js new file mode 100644 index 0000000..e5f65fd --- /dev/null +++ b/api/src/docker/cadquery/cadquery.js @@ -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, +} diff --git a/api/src/docker/cadquery/package.json b/api/src/docker/cadquery/package.json new file mode 100644 index 0000000..7bf56d7 --- /dev/null +++ b/api/src/docker/cadquery/package.json @@ -0,0 +1,16 @@ +{ + "name": "openscad-endpoint", + "version": "0.0.1", + "description": "endpoint for openscad", + "main": "index.js", + "author": "Kurt Hutten ", + "license": "", + "dependencies": { + "cors": "^2.8.5", + "middy": "^0.36.0", + "nanoid": "^3.1.20" + }, + "devDependencies": { + "aws-lambda-ric": "^1.0.0" + } +} diff --git a/api/src/docker/cadquery/runCQ.js b/api/src/docker/cadquery/runCQ.js new file mode 100644 index 0000000..8b85930 --- /dev/null +++ b/api/src/docker/cadquery/runCQ.js @@ -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 } + } +} diff --git a/api/src/docker/cadquery/yarn.lock b/api/src/docker/cadquery/yarn.lock new file mode 100644 index 0000000..6f98765 --- /dev/null +++ b/api/src/docker/cadquery/yarn.lock @@ -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= diff --git a/api/src/docker/openscad/entrypoint.sh b/api/src/docker/common/entrypoint.sh similarity index 100% rename from api/src/docker/openscad/entrypoint.sh rename to api/src/docker/common/entrypoint.sh diff --git a/api/src/docker/openscad/node14source_setup.sh b/api/src/docker/common/node14source_setup.sh similarity index 100% rename from api/src/docker/openscad/node14source_setup.sh rename to api/src/docker/common/node14source_setup.sh diff --git a/api/src/docker/common/utils.js b/api/src/docker/common/utils.js new file mode 100644 index 0000000..80f0539 --- /dev/null +++ b/api/src/docker/common/utils.js @@ -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, +} diff --git a/api/src/docker/docker-compose.yml b/api/src/docker/docker-compose.yml index c797a3c..0cad72e 100644 --- a/api/src/docker/docker-compose.yml +++ b/api/src/docker/docker-compose.yml @@ -7,28 +7,38 @@ services: # - "5050:8080" openscad-health: - build: ./openscad/. + build: + context: ./ + dockerfile: ./openscad/. image: openscad command: openscad.health ports: - "5051:8080" - openscad-render: + openscad-preview: image: openscad # build: ./openscad/. - command: openscad.render + command: openscad.preview # networks: # - awsland ports: - "5052:8080" - openscad-export: + openscad-stl: image: openscad # build: ./openscad/. - command: openscad.exportstl + command: openscad.stl ports: - "5053:8080" + cadquery-stl: + build: + context: ./ + dockerfile: ./cadquery/. + command: cadquery.stl + ports: + - 5060:8080 + # networks: # awsland: # name: awsland diff --git a/api/src/docker/openscad/Dockerfile b/api/src/docker/openscad/Dockerfile index e9b685e..27412cc 100644 --- a/api/src/docker/openscad/Dockerfile +++ b/api/src/docker/openscad/Dockerfile @@ -11,7 +11,7 @@ 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 node14source_setup.sh /nodesource_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 @@ -29,16 +29,17 @@ RUN apt-get update && \ # 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 aws-lambda-rie /usr/local/bin/aws-lambda-rie +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 package*.json /var/task/ +COPY openscad/package*.json /var/task/ RUN npm install -COPY *.js /var/task/ -COPY entrypoint.sh /entrypoint.sh +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"] diff --git a/api/src/docker/openscad/openscad.js b/api/src/docker/openscad/openscad.js index 1edb2ef..980b9ad 100644 --- a/api/src/docker/openscad/openscad.js +++ b/api/src/docker/openscad/openscad.js @@ -24,7 +24,7 @@ const preflightOptions = (req, _context, callback) => { callback(null, response) } -const render = async (req, _context, callback) => { +const preview = async (req, _context, callback) => { _context.callbackWaitsForEmptyEventLoop = false const eventBody = Buffer.from(req.body, 'base64').toString('ascii') console.log(eventBody, 'eventBody') @@ -55,7 +55,7 @@ const render = async (req, _context, callback) => { } } -const exportstl = async (req, _context, callback) => { +const stl = async (req, _context, callback) => { _context.callbackWaitsForEmptyEventLoop = false const eventBody = Buffer.from(req.body, 'base64').toString('ascii') console.log(eventBody, 'eventBody') @@ -89,7 +89,7 @@ const exportstl = async (req, _context, callback) => { module.exports = { health: middy(health).use(cors()), - exportstl: middy(exportstl).use(cors()), - render: middy(render).use(cors()), + stl: middy(stl).use(cors()), + preview: middy(preview).use(cors()), preflightOptions, } diff --git a/api/src/docker/openscad/package-lock.json b/api/src/docker/openscad/package-lock.json deleted file mode 100644 index 776fa2d..0000000 --- a/api/src/docker/openscad/package-lock.json +++ /dev/null @@ -1,944 +0,0 @@ -{ - "name": "openscad-endpoint", - "version": "0.0.1", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@types/aws-lambda": { - "version": "8.10.72", - "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.72.tgz", - "integrity": "sha512-jOrTwAhSiUtBIN/QsWNKlI4+4aDtpZ0sr2BRvKW6XQZdspgHUSHPcuzxbzCRiHUiDQ+0026u5TSE38VyIhNnfA==" - }, - "@types/http-errors": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz", - "integrity": "sha512-2aoSC4UUbHDj2uCsCxcG/vRMXey/m17bC7UwitVm5hn22nI8O8Y9iDpA76Orc+DWkQ4zZrOKEshCqR/jSuXAHA==" - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-i18n": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/ajv-i18n/-/ajv-i18n-3.6.0.tgz", - "integrity": "sha512-F21DzmzYq9aVtY8CGGtlnQDy3rFMaFW2KRlMuCQp76KiPIkvqN+mpq5MI9EsgC0VFwj+jeLIsvVCvPOa1sobBQ==" - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "aws-lambda-ric": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/aws-lambda-ric/-/aws-lambda-ric-1.0.0.tgz", - "integrity": "sha512-1a7oCloj2uIui41RMMRRVlf/bIiRQt7UlmfY7+NpuEyal5bnpj4RMooErq3Q8jOBSmX4pqP6KeRScv7uV2Ep1w==", - "dev": true, - "requires": { - "node-addon-api": "3.0.2", - "node-gyp": "7.1.2" - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "busboy": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz", - "integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==", - "requires": { - "dicer": "0.3.0" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "dicer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", - "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", - "requires": { - "streamsearch": "0.1.2" - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "env-paths": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", - "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", - "dev": true - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "dev": true, - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true - }, - "http-errors": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", - "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "json-mask": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/json-mask/-/json-mask-0.3.9.tgz", - "integrity": "sha512-RRu7bf7vzOohKMrU5pD9+fROMltTegWj2trZlPNr7hXekptFGkOZo4S63Jdx2X1GR7IK6rEVvXkQKY+2TPs0PA==" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "middy": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/middy/-/middy-0.36.0.tgz", - "integrity": "sha512-IhIVEZQs8mxcvPHfzAQpwINj4R+aVgeCcUL9KS+OYE5Vy4hKhZtVl/1yfI8dSvDAfRBoRp6N/Gi04pxQM4vAzw==", - "requires": { - "@types/aws-lambda": "^8.10.45", - "@types/http-errors": "^1.6.3", - "ajv": "^6.9.1", - "ajv-i18n": "^3.4.0", - "ajv-keywords": "^3.4.1", - "busboy": "^0.3.1", - "content-type": "^1.0.4", - "http-errors": "^1.7.3", - "json-mask": "^0.3.8", - "negotiator": "^0.6.1", - "once": "^1.4.0", - "qs": "^6.6.0", - "querystring": "^0.2.0" - }, - "dependencies": { - "qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" - } - } - }, - "mime-db": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", - "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==", - "dev": true - }, - "mime-types": { - "version": "2.1.29", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", - "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", - "dev": true, - "requires": { - "mime-db": "1.46.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "nanoid": { - "version": "3.1.20", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", - "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==" - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "node-addon-api": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.2.tgz", - "integrity": "sha512-+D4s2HCnxPd5PjjI0STKwncjXTUKKqm74MDMz9OPXavjsGmjkvwgLtA5yoxJUdmpj52+2u+RrXgPipahKczMKg==", - "dev": true - }, - "node-gyp": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz", - "integrity": "sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ==", - "dev": true, - "requires": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.3", - "nopt": "^5.0.0", - "npmlog": "^4.1.2", - "request": "^2.88.2", - "rimraf": "^3.0.2", - "semver": "^7.3.2", - "tar": "^6.0.2", - "which": "^2.0.2" - } - }, - "nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "querystring": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", - "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", - "dev": true, - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - } - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } -} diff --git a/api/src/docker/openscad/runScad.js b/api/src/docker/openscad/runScad.js index c23e0b6..b3a84f0 100644 --- a/api/src/docker/openscad/runScad.js +++ b/api/src/docker/openscad/runScad.js @@ -1,6 +1,4 @@ -const { exec } = require('child_process') -const { promises } = require('fs') -const { writeFile } = promises +const { makeFile, runCommand } = require('../common/utils') const { nanoid } = require('nanoid') module.exports.runScad = async ({ @@ -14,7 +12,7 @@ module.exports.runScad = async ({ } = {}, } = {}, // TODO add view settings } = {}) => { - const tempFile = await makeFile(file) + 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}` @@ -22,7 +20,7 @@ module.exports.runScad = async ({ console.log('command', command) try { - const result = await runCommand(command, 10000) + const result = await runCommand(command, 15000) return { result, tempFile } } catch (error) { return { error, tempFile } @@ -30,7 +28,7 @@ module.exports.runScad = async ({ } module.exports.stlExport = async ({ file } = {}) => { - const tempFile = await makeFile(file) + const tempFile = await makeFile(file, '.scad', nanoid) try { const result = await runCommand( @@ -42,36 +40,3 @@ module.exports.stlExport = async ({ file } = {}) => { return { error, tempFile } } } - -async function makeFile(file) { - const tempFile = 'a' + nanoid() // '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.scad`, 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) - }) -} diff --git a/api/src/docker/serverless.yml b/api/src/docker/serverless.yml index 62892d0..26c0f65 100644 --- a/api/src/docker/serverless.yml +++ b/api/src/docker/serverless.yml @@ -17,7 +17,11 @@ provider: images: # this image is built locally and push to ECR openscadimage: - path: ./openscad/ + path: ./ + file: ./openscad/Dockerfile + cadqueryimage: + path: ./ + file: ./cadquery/Dockerfile apiGateway: metrics: true binaryMediaTypes: @@ -52,7 +56,7 @@ provider: functions: # see preflightoptions comment in openscad.js - preflightrender: + preflightopenscadpreview: image: name: openscadimage command: @@ -61,9 +65,9 @@ functions: - '/entrypoint.sh' events: - http: - path: render + path: openscad/preview method: options - preflightexport: + preflightopenscadstl: image: name: openscadimage command: @@ -72,30 +76,54 @@ functions: - '/entrypoint.sh' events: - http: - path: export + path: openscad/stl method: options - render: + openscadpreview: image: name: openscadimage command: - - openscad.render + - openscad.preview entryPoint: - '/entrypoint.sh' events: - http: - path: render + path: openscad/preview method: post timeout: 15 - exportStl: + openscadstl: image: name: openscadimage command: - - openscad.exportstl + - openscad.stl entryPoint: - '/entrypoint.sh' events: - http: - path: export + 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 diff --git a/netlify.toml b/netlify.toml index d7baac8..6d9ea29 100644 --- a/netlify.toml +++ b/netlify.toml @@ -10,3 +10,6 @@ functions = "api/dist/functions" from = "/*" to = "/index.html" status = 200 + +[context.deploy-preview.environment] + CAD_LAMBDA_BASE_URL = "https://t7wdlz8ztf.execute-api.us-east-1.amazonaws.com/dev2" diff --git a/redwood.toml b/redwood.toml index c4fe771..6502c5d 100644 --- a/redwood.toml +++ b/redwood.toml @@ -8,7 +8,7 @@ [web] port = 8910 apiProxyPath = "/.netlify/functions" - includeEnvironmentVariables = ['GOOGLE_ANALYTICS_ID', 'CLOUDINARY_API_KEY', 'CLOUDINARY_API_SECRET'] + includeEnvironmentVariables = ['GOOGLE_ANALYTICS_ID', 'CLOUDINARY_API_KEY', 'CLOUDINARY_API_SECRET', 'CAD_LAMBDA_BASE_URL'] # experimentalFastRefresh = true # this seems to break cascadeStudio [api] port = 8911 diff --git a/web/package.json b/web/package.json index 1c627c8..7aa3247 100644 --- a/web/package.json +++ b/web/package.json @@ -13,6 +13,7 @@ ] }, "dependencies": { + "@headlessui/react": "^1.0.0", "@material-ui/core": "^4.11.0", "@monaco-editor/react": "^4.0.11", "@redwoodjs/auth": "^0.30.1", diff --git a/web/src/Routes.js b/web/src/Routes.js index dd9d3dc..e7ec58d 100644 --- a/web/src/Routes.js +++ b/web/src/Routes.js @@ -35,7 +35,7 @@ const Routes = () => { ) return ( - + diff --git a/web/src/components/IdeConsole/IdeConsole.js b/web/src/components/IdeConsole/IdeConsole.js index 0b62f9c..70a94f5 100644 --- a/web/src/components/IdeConsole/IdeConsole.js +++ b/web/src/components/IdeConsole/IdeConsole.js @@ -1,17 +1,34 @@ -import { useContext } from 'react' +import { useContext, useEffect } from 'react' import { IdeContext } from 'src/components/IdeToolbarNew' +import { matchEditorVsDarkTheme } from 'src/components/IdeEditor' const IdeConsole = () => { const { state } = useContext(IdeContext) + useEffect(() => { + const element = document.querySelector('.console-tile .mosaic-window-body') + if (element) { + element.scrollTop = element.scrollHeight - element.clientHeight + } + }, [state.consoleMessages]) + return ( -
+
- {state.consoleMessages?.map(({ type, message }, index) => ( + {state.consoleMessages?.map(({ type, message, time }, index) => (
-            {message}
+            
+ {time?.toLocaleString()} +
+
+ {message} +
))}
diff --git a/web/src/components/IdeContainer/IdeContainer.js b/web/src/components/IdeContainer/IdeContainer.js index e61b426..536b08c 100644 --- a/web/src/components/IdeContainer/IdeContainer.js +++ b/web/src/components/IdeContainer/IdeContainer.js @@ -1,7 +1,8 @@ import { useContext, useRef, useEffect } from 'react' import { Mosaic, MosaicWindow } from 'react-mosaic-component' import { IdeContext } from 'src/components/IdeToolbarNew' -import IdeEditor from 'src/components/IdeEditor' +import { requestRender } from 'src/helpers/hooks/useIdeState' +import IdeEditor, { matchEditorVsDarkTheme } from 'src/components/IdeEditor' import IdeViewer from 'src/components/IdeViewer' import IdeConsole from 'src/components/IdeConsole' import 'react-mosaic-component/react-mosaic-component.css' @@ -13,7 +14,7 @@ const ELEMENT_MAP = { } const IdeContainer = () => { - const { state, dispatch } = useContext(IdeContext) + const { state, thunkDispatch } = useContext(IdeContext) const viewerDOM = useRef(null) const debounceTimeoutId = useRef @@ -22,12 +23,22 @@ const IdeContainer = () => { function handleViewerSizeUpdate() { if (viewerDOM !== null && viewerDOM.current) { const { width, height } = viewerDOM.current.getBoundingClientRect() - dispatch({ - type: 'render', - payload: { - code: state.code, - viewerSize: { width, height }, - }, + thunkDispatch({ + type: 'updateViewerSize', + payload: { viewerSize: { width, height } }, + }) + thunkDispatch((dispatch, getState) => { + const state = getState() + if (state.ideType === 'openScad') { + dispatch({ type: 'setLoading' }) + requestRender({ + state, + dispatch, + code: state.code, + viewerSize: { width, height }, + camera: state.camera, + }) + } }) } } @@ -47,22 +58,36 @@ const IdeContainer = () => { }, []) return ( -
+
( - - {id === 'Viewer' ? ( -
- {ELEMENT_MAP[id]} -
- ) : ( - ELEMENT_MAP[id] - )} -
- )} + renderTile={(id, path) => { + return ( + ( +
+ {id} + {id === 'Editor' && ` (${state.ideType})`} +
+ )} + className={`${id.toLowerCase()} ${id.toLowerCase()}-tile`} + > + {id === 'Viewer' ? ( +
+ {ELEMENT_MAP[id]} +
+ ) : ( + ELEMENT_MAP[id] + )} +
+ ) + }} value={state.layout} onChange={(newLayout) => - dispatch({ type: 'setLayout', payload: { message: newLayout } }) + thunkDispatch({ type: 'setLayout', payload: { message: newLayout } }) } onRelease={handleViewerSizeUpdate} /> diff --git a/web/src/components/IdeEditor/IdeEditor.js b/web/src/components/IdeEditor/IdeEditor.js index 7ad5ad5..20fd750 100644 --- a/web/src/components/IdeEditor/IdeEditor.js +++ b/web/src/components/IdeEditor/IdeEditor.js @@ -1,51 +1,70 @@ -import { useContext, useEffect, Suspense, lazy } from 'react' -import { isBrowser } from '@redwoodjs/prerender/browserUtils' +import { useContext, Suspense, lazy } from 'react' import { IdeContext } from 'src/components/IdeToolbarNew' import { codeStorageKey } from 'src/helpers/hooks/useIdeState' +import { requestRender } from 'src/helpers/hooks/useIdeState' const Editor = lazy(() => import('@monaco-editor/react')) -const IdeEditor = () => { - const { state, dispatch } = useContext(IdeContext) +export const matchEditorVsDarkTheme = { + // Some colors to roughly match the vs-dark editor theme + Bg: { backgroundColor: 'rgb(30,30,30)' }, + lighterBg: { backgroundColor: 'rgb(55,55,55)' }, + Text: { color: 'rgb(212,212,212)' }, + TextBrown: { color: 'rgb(206,144,120)' }, +} - const scriptKey = 'encoded_script' - useEffect(() => { - // load code from hash if it's there - let hash - if (isBrowser) { - hash = window.location.hash - } - const [key, scriptBase64] = hash.slice(1).split('=') - if (key === scriptKey) { - const script = atob(scriptBase64) - dispatch({ type: 'updateCode', payload: script }) - } - }, []) - useEffect(() => { - if (isBrowser) { - window.location.hash = '' - } - }, [state.code]) +const IdeEditor = () => { + const { state, thunkDispatch } = useContext(IdeContext) + const ideTypeToLanguageMap = { + cadQuery: 'python', + openScad: 'cpp', + } function handleCodeChange(value, _event) { - dispatch({ type: 'updateCode', payload: value }) + thunkDispatch({ type: 'updateCode', payload: value }) } function handleSaveHotkey(event) { //ctrl|meta + s is very intuitive for most devs const { key, ctrlKey, metaKey } = event if (key === 's' && (ctrlKey || metaKey)) { event.preventDefault() - dispatch({ type: 'render', payload: { code: state.code } }) + thunkDispatch((dispatch, getState) => { + const state = getState() + dispatch({ type: 'setLoading' }) + requestRender({ + state, + dispatch, + code: state.code, + viewerSize: state.viewerSize, + camera: state.camera, + }) + }) localStorage.setItem(codeStorageKey, state.code) } } - + const loading = ( +
+
+ . . . loading +
+
+ ) return ( -
- . . . loading
}> +
+ diff --git a/web/src/components/IdeToolbarNew/IdeToolbarNew.js b/web/src/components/IdeToolbarNew/IdeToolbarNew.js index 4920515..dc12d73 100644 --- a/web/src/components/IdeToolbarNew/IdeToolbarNew.js +++ b/web/src/components/IdeToolbarNew/IdeToolbarNew.js @@ -1,17 +1,44 @@ -import { createContext } from 'react' +import { createContext, useEffect } from 'react' import IdeContainer from 'src/components/IdeContainer' import { isBrowser } from '@redwoodjs/prerender/browserUtils' import { useIdeState, codeStorageKey } from 'src/helpers/hooks/useIdeState' import { copyTextToClipboard } from 'src/helpers/clipboard' +import { requestRender } from 'src/helpers/hooks/useIdeState' export const IdeContext = createContext() -const IdeToolbarNew = () => { - const [state, dispatch] = useIdeState() - function setIdeType(ide) { - dispatch({ type: 'setIdeType', payload: { message: ide } }) - } +const IdeToolbarNew = ({ cadPackage }) => { + const [state, thunkDispatch] = useIdeState() + const scriptKey = 'encoded_script' + useEffect(() => { + thunkDispatch({ + type: 'initIde', + payload: { cadPackage }, + }) + // load code from hash if it's there + let hash + if (isBrowser) { + hash = window.location.hash + } + const [key, scriptBase64] = hash.slice(1).split('=') + if (key === scriptKey) { + const script = atob(scriptBase64) + thunkDispatch({ type: 'updateCode', payload: script }) + } + window.location.hash = '' + setTimeout(() => handleRender()) // definitely a little hacky, timeout with no delay is just to push it into the next event loop. + }, [cadPackage]) function handleRender() { - dispatch({ type: 'render', payload: { code: state.code } }) + thunkDispatch((dispatch, getState) => { + const state = getState() + dispatch({ type: 'setLoading' }) + requestRender({ + state, + dispatch, + code: state.code, + viewerSize: state.viewerSize, + camera: state.camera, + }) + }) localStorage.setItem(codeStorageKey, state.code) } function handleMakeLink() { @@ -23,25 +50,19 @@ const IdeToolbarNew = () => { } return ( - +
diff --git a/web/src/components/IdeViewer/IdeViewer.js b/web/src/components/IdeViewer/IdeViewer.js index 2790d4d..0362e32 100644 --- a/web/src/components/IdeViewer/IdeViewer.js +++ b/web/src/components/IdeViewer/IdeViewer.js @@ -1,11 +1,41 @@ import { IdeContext } from 'src/components/IdeToolbarNew' import { useRef, useState, useEffect, useContext } from 'react' -import { Canvas, extend, useFrame, useThree } from 'react-three-fiber' +import { + Canvas, + extend, + useFrame, + useThree, + useUpdate, +} from 'react-three-fiber' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' import { Vector3 } from 'three' +import { STLLoader } from 'three/examples/jsm/loaders/STLLoader' +import { requestRender } from 'src/helpers/hooks/useIdeState' extend({ OrbitControls }) +function Asset({ stlData }) { + const [loadedGeometry, setLoadedGeometry] = useState() + const mesh = useRef() + const ref = useUpdate((geometry) => { + geometry.attributes = loadedGeometry.attributes + }) + useEffect(() => { + if (stlData) { + const decoded = atob(stlData) + const loader = new STLLoader() + setLoadedGeometry(loader.parse(decoded)) + } + }, [stlData]) + if (!loadedGeometry) return null + return ( + + + + + ) +} + let debounceTimeoutId function Controls({ onCameraChange, onDragStart }) { const controls = useRef() @@ -85,7 +115,7 @@ function Controls({ onCameraChange, onDragStart }) { } }, []) - useFrame(() => controls.current.update()) + useFrame(() => controls.current?.update()) return ( ) } -let currentCode // I have no idea why this works and using state.code is the dispatch doesn't but it was always stale const IdeViewer = () => { - const { state, dispatch } = useContext(IdeContext) + const { state, thunkDispatch } = useContext(IdeContext) const [isDragging, setIsDragging] = useState(false) const [image, setImage] = useState() @@ -127,8 +156,7 @@ const IdeViewer = () => { 'data:image/png;base64,' + state.objectData?.data ) setIsDragging(false) - }, [state.objectData]) - currentCode = state.code + }, [state.objectData?.type, state.objectData?.data]) const openSCADDeepOceanThemeBackground = '#323232' // the following are tailwind colors in hex, can't use these classes to color three.js meshes. @@ -151,41 +179,68 @@ const IdeViewer = () => { isDragging ? 'opacity-25' : 'opacity-100' }`} > - + code-cad preview
)} - {state.isLoading && ( -
-
-
- )} -
setIsDragging(true)} > setIsDragging(true)} - onCameraChange={(camera) => - dispatch({ - type: 'render', - payload: { - code: currentCode, - camera, - }, + onCameraChange={(camera) => { + thunkDispatch({ + type: 'updateCamera', + payload: { camera }, }) - } + thunkDispatch((dispatch, getState) => { + const state = getState() + if (state.ideType === 'openScad') { + dispatch({ type: 'setLoading' }) + requestRender({ + state, + dispatch, + code: state.code, + viewerSize: state.viewerSize, + camera, + }) + } + }) + }} /> - - - - + {state.ideType === 'openScad' && ( + <> + + + + + + )} + {state.ideType === 'cadQuery' && ( + + )}
+ {state.isLoading && ( +
+
+
+ )}
) } diff --git a/web/src/components/NavPlusButton/NavPlusButton.tsx b/web/src/components/NavPlusButton/NavPlusButton.tsx new file mode 100644 index 0000000..edd27f9 --- /dev/null +++ b/web/src/components/NavPlusButton/NavPlusButton.tsx @@ -0,0 +1,48 @@ +import { Link, routes } from '@redwoodjs/router' +import Svg from 'src/components/Svg/Svg' +import { Popover } from '@headlessui/react' + +const NavPlusButton: React.FC = () => { + return ( + + + + + + +
    + {[ + { + name: 'OpenSCAD', + sub: 'beta', + ideType: 'openScad', + }, + { name: 'CadQuery', sub: 'beta', ideType: 'cadQuery' }, + { + name: 'CascadeStudio', + sub: 'soon to be deprecated', + }, + ].map(({ name, sub, ideType }) => ( +
  • + +
    {name}
    +
    {sub}
    + +
  • + ))} +
+
+
+ ) +} + +export default NavPlusButton diff --git a/web/src/helpers/cadPackages/cadQueryController.js b/web/src/helpers/cadPackages/cadQueryController.js new file mode 100644 index 0000000..379a82c --- /dev/null +++ b/web/src/helpers/cadPackages/cadQueryController.js @@ -0,0 +1,66 @@ +import { lambdaBaseURL } from './common' + +export const render = async ({ code }) => { + const body = JSON.stringify({ + settings: {}, + file: code, + }) + try { + const response = await fetch(lambdaBaseURL + '/cadquery/stl', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body, + }) + if (response.status === 400) { + // TODO add proper error messages for CadQuery + const { error } = await response.json() + const cleanedErrorMessage = error.replace( + /["|']\/tmp\/.+\/main.scad["|']/g, + "'main.scad'" + ) + return { + status: 'error', + message: { + type: 'error', + message: cleanedErrorMessage, + time: new Date(), + }, + } + } + const data = await response.json() + return { + status: 'healthy', + objectData: { + type: 'stl', + data: data.imageBase64, + }, + message: { + type: 'message', + message: data.result || 'Successful Render', + time: new Date(), + }, + } + } catch (e) { + // TODO handle errors better + // I think we should display something overlayed on the viewer window something like "network issue try again" + // and in future I think we need timeouts differently as they maybe from a user trying to render something too complex + // or something with minkowski in it :/ either way something like "render timed out, try again or here are tips to reduce part complexity" with a link talking about $fn and minkowski etc + return { + status: 'error', + message: { + type: 'error', + message: 'network issue', + time: new Date(), + }, + } + } +} + +const openScad = { + render, + // more functions to come +} + +export default openScad diff --git a/web/src/helpers/cadPackages/common.js b/web/src/helpers/cadPackages/common.js new file mode 100644 index 0000000..2187f48 --- /dev/null +++ b/web/src/helpers/cadPackages/common.js @@ -0,0 +1,3 @@ +export const lambdaBaseURL = + process.env.CAD_LAMBDA_BASE_URL || + 'https://wzab9s632b.execute-api.us-east-1.amazonaws.com/prod' diff --git a/web/src/helpers/cadPackages/index.js b/web/src/helpers/cadPackages/index.js index ab938ae..19494ad 100644 --- a/web/src/helpers/cadPackages/index.js +++ b/web/src/helpers/cadPackages/index.js @@ -1,7 +1,7 @@ import openScad from './openScadController' -import openCascade from './newCascadeController' +import cadQuery from './cadQueryController' export const cadPackages = { openScad, - openCascade, -} \ No newline at end of file + cadQuery, +} diff --git a/web/src/helpers/cadPackages/newCascadeController.js b/web/src/helpers/cadPackages/newCascadeController.js deleted file mode 100644 index 5367fc3..0000000 --- a/web/src/helpers/cadPackages/newCascadeController.js +++ /dev/null @@ -1,35 +0,0 @@ -// Rename this file to remove "new" once Cascade integration is complete - -export const render = async ({ code, settings }) => { - return new Promise((resolve, reject) => { - setTimeout(() => { - const shouldReject = Math.random() < 0.7 - if (shouldReject) { - resolve({ - objectData: { - type: 'stl', - data: ((Math.random() * 256 + 1) >>> 0).toString(2), // Randomized 8-bit numbers for funzies - }, - message: { - type: 'message', - message: `bodies rendered by: ${code}`, - }, - }) - } else { - reject({ - message: { - type: 'error', - message: 'unable to parse line: x', - }, - }) - } - }, 700) - }) -} - -const openCascade = { - render, - // More functions to come -} - -export default openCascade \ No newline at end of file diff --git a/web/src/helpers/cadPackages/openScadController.js b/web/src/helpers/cadPackages/openScadController.js index 167174b..da4697a 100644 --- a/web/src/helpers/cadPackages/openScadController.js +++ b/web/src/helpers/cadPackages/openScadController.js @@ -1,39 +1,23 @@ -let openScadBaseURL = - process.env.OPENSCAD_BASE_URL || - 'https://x2wvhihk56.execute-api.us-east-1.amazonaws.com/dev' - -let lastViewPortSize = 'INIT' -let lastCameraSettings = 'INIT' +import { lambdaBaseURL } from './common' export const render = async ({ code, settings }) => { const pixelRatio = window.devicePixelRatio || 1 - const size = settings.viewerSize - ? { - x: Math.round(settings.viewerSize?.width * pixelRatio), - y: Math.round(settings.viewerSize?.height * pixelRatio), - } - : lastViewPortSize - const camera = settings.camera || lastCameraSettings - if (settings.camera) { - lastCameraSettings = settings.camera - } - if (settings.viewerSize) { - lastViewPortSize = size - } - if ([camera, size].includes('INIT')) { - return { - status: 'insufficient-preview-info', - } + const size = { + x: Math.round(settings.viewerSize?.width * pixelRatio), + y: Math.round(settings.viewerSize?.height * pixelRatio), } const body = JSON.stringify({ settings: { size, - camera, + camera: settings.camera, }, file: code, }) + if (!settings.camera.position) { + return + } try { - const response = await fetch(openScadBaseURL + '/render', { + const response = await fetch(lambdaBaseURL + '/openscad/preview', { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -50,7 +34,8 @@ export const render = async ({ code, settings }) => { status: 'error', message: { type: 'error', - message: addDateToLog(cleanedErrorMessage), + message: cleanedErrorMessage, + time: new Date(), }, } } @@ -63,7 +48,8 @@ export const render = async ({ code, settings }) => { }, message: { type: 'message', - message: addDateToLog(data.result), + message: data.result, + time: new Date(), }, } } catch (e) { @@ -75,7 +61,8 @@ export const render = async ({ code, settings }) => { status: 'error', message: { type: 'error', - message: addDateToLog('network issue'), + message: 'network issue', + time: new Date(), }, } } @@ -87,8 +74,3 @@ const openScad = { } export default openScad - -function addDateToLog(message) { - return `-> ${new Date().toLocaleString()} -${message}` -} diff --git a/web/src/helpers/hooks/useIdeState.js b/web/src/helpers/hooks/useIdeState.js index a6e123b..28d0a3b 100644 --- a/web/src/helpers/hooks/useIdeState.js +++ b/web/src/helpers/hooks/useIdeState.js @@ -1,7 +1,15 @@ import { useReducer } from 'react' import { cadPackages } from 'src/helpers/cadPackages' -const donutInitCode = ` +function withThunk(dispatch, getState) { + return (actionOrThunk) => + typeof actionOrThunk === 'function' + ? actionOrThunk(dispatch, getState) + : dispatch(actionOrThunk) +} + +const initCodeMap = { + openScad: ` color(c="DarkGoldenrod")rotate_extrude()translate([20,0])circle(d=30); donut(); module donut() { @@ -14,19 +22,37 @@ module stick(basewid, angl){ sphere(7); translate([0,0,10])sphere(9); } -}` +}`, + cadQuery: `import cadquery as cq +from cadquery import exporters -export const codeStorageKey = 'Last-openscad-code' +diam = 5.0 + +result = (cq.Workplane().circle(diam).extrude(20.0) + .faces(">Z").workplane(invert=True).circle(1.05).cutBlind(8.0) + .faces(" { - const code = localStorage.getItem(codeStorageKey) || donutInitCode + const code = localStorage.getItem(codeStorageKey) || initCodeMap.openscad const initialState = { - ideType: 'openScad', - consoleMessages: [{ type: 'message', message: 'Initialising OpenSCAD' }], + ideType: 'INIT', + consoleMessages: [ + { type: 'message', message: 'Initialising', time: new Date() }, + ], code, objectData: { type: 'stl', - data: 'some binary', + data: null, }, layout: { direction: 'row', @@ -38,10 +64,18 @@ export const useIdeState = () => { splitPercentage: 70, }, }, + camera: {}, + viewerSize: { width: 0, height: 0 }, isLoading: false, } const reducer = (state, { type, payload }) => { switch (type) { + case 'initIde': + return { + ...state, + code: initCodeMap[payload.cadPackage] || initCodeMap.openscad, + ideType: payload.cadPackage, + } case 'updateCode': return { ...state, code: payload } case 'healthyRender': @@ -64,61 +98,71 @@ export const useIdeState = () => { : payload.message, isLoading: false, } - case 'setIdeType': - return { - ...state, - ideType: payload.message, - } case 'setLayout': return { ...state, layout: payload.message, } + case 'updateCamera': + return { + ...state, + camera: payload.camera, + } + case 'updateViewerSize': + return { + ...state, + viewerSize: payload.viewerSize, + } case 'setLoading': return { ...state, isLoading: true, } + case 'resetLoading': + return { + ...state, + isLoading: false, + } default: return state } } - function dispatchMiddleware(dispatch, state) { - return ({ type, payload }) => { - switch (type) { - case 'render': - cadPackages[state.ideType] - .render({ - code: payload.code, - settings: { - camera: payload.camera, - viewerSize: payload.viewerSize, - }, - }) - .then(({ objectData, message, status }) => { - if (status === 'insufficient-preview-info') return - if (status === 'error') { - dispatch({ - type: 'errorRender', - payload: { message }, - }) - } else { - dispatch({ - type: 'healthyRender', - payload: { objectData, message }, - }) - } - }) - dispatch({ type: 'setLoading' }) - break - - default: - return dispatch({ type, payload }) - } - } - } - const [state, dispatch] = useReducer(reducer, initialState) - return [state, dispatchMiddleware(dispatch, state)] + mutableState = state + const getState = () => mutableState + return [state, withThunk(dispatch, getState)] +} + +export const requestRender = ({ + state, + dispatch, + code, + camera, + viewerSize, +}) => { + state.ideType !== 'INIT' && + !state.isLoading && + cadPackages[state.ideType] + .render({ + code, + settings: { + camera, + viewerSize, + }, + }) + .then(({ objectData, message, status }) => { + if (status === 'error') { + dispatch({ + type: 'errorRender', + payload: { message }, + }) + } else { + dispatch({ + type: 'healthyRender', + payload: { objectData, message, lastRunCode: code }, + }) + } + }) + .catch(() => dispatch({ type: 'resetLoading' })) // TODO should probably display something to the user here } diff --git a/web/src/index.css b/web/src/index.css index 650d409..b66a0a6 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -44,6 +44,11 @@ @apply list-disc; } +.mosaic-toolbar-overrides .mosaic-window .mosaic-window-toolbar { + /* makes the height of the toolbar based off the content inside instead of hardcoded to 30px */ + height: unset; +} + /* Used for LandingSection.js, if it's gone or these class isn't used there you can probably delete it */ .svg-shadow { @@ -100,4 +105,4 @@ input.error, textarea.error { /* used in IdeContainer component */ #cadhub-ide .mosaic-window.console .mosaic-window-body { overflow-y: auto; -} \ No newline at end of file +} diff --git a/web/src/layouts/MainLayout/MainLayout.js b/web/src/layouts/MainLayout/MainLayout.js index e75bfe0..2df5967 100644 --- a/web/src/layouts/MainLayout/MainLayout.js +++ b/web/src/layouts/MainLayout/MainLayout.js @@ -8,6 +8,7 @@ import { getActiveClasses } from 'get-active-classes' import Footer from 'src/components/Footer' import { useLocation } from '@redwoodjs/router' import LoginModal from 'src/components/LoginModal' +import NavPlusButton from 'src/components/NavPlusButton' import ReactGA from 'react-ga' import { isBrowser } from '@redwoodjs/prerender/browserUtils' @@ -132,9 +133,7 @@ const MainLayout = ({ children, shouldRemoveFooterInIde }) => { 'mr-8 h-10 w-10 rounded-full border-2 border-indigo-300 flex items-center justify-center' )} > - - - + {isAuthenticated ? (
  • { +const DevIdePage = ({ cadPackage }) => { return (
    @@ -12,10 +12,9 @@ const DevIdePage = () => { description="new ide in development" lang="en-US" /> -
    -
    - Woah, woah. You shouldn't be here! We're still working on this. - Since you've seen it now, have a look what{' '} +
    +
    + We're still working on this. Since you're here, have a look what{' '} {
    - +
    ) diff --git a/yarn.lock b/yarn.lock index ca35b52..40472f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1404,6 +1404,11 @@ resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-2.0.0.tgz#5bb2193eb685c0007540ca61d166d4e1edaf918d" integrity sha512-WEezM1FWztfbzqIUbsDzFRVMxSoLy3HugVcux6KDDtTqzPsLE8NDRHfXvev66aH1i2oOKKar3/XDjbvh/OUBdg== +"@headlessui/react@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.0.0.tgz#661b50ebfd25041abb45d8eedd85e7559056bcaf" + integrity sha512-mjqRJrgkbcHQBfAHnqH0yRxO/y/22jYrdltpE7WkurafREKZ+pj5bPBwYHMt935Sdz/n16yRcVmsSCqDFHee9A== + "@icons/material@^0.2.4": version "0.2.4" resolved "https://registry.yarnpkg.com/@icons/material/-/material-0.2.4.tgz#e90c9f71768b3736e76d7dd6783fc6c2afa88bc8"