Attempt to at move app into app sub dir
This commit is contained in:
11
app/api/src/docker/Dockerfile
Normal file
11
app/api/src/docker/Dockerfile
Normal 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"]
|
||||
34
app/api/src/docker/README.md
Normal file
34
app/api/src/docker/README.md
Normal 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)
|
||||
|
||||
|
||||
41
app/api/src/docker/aws-emulator.js
Normal file
41
app/api/src/docker/aws-emulator.js
Normal 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}`)
|
||||
})
|
||||
49
app/api/src/docker/cadquery/Dockerfile
Normal file
49
app/api/src/docker/cadquery/Dockerfile
Normal 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" ]
|
||||
53
app/api/src/docker/cadquery/cadquery.js
Normal file
53
app/api/src/docker/cadquery/cadquery.js
Normal 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,
|
||||
}
|
||||
16
app/api/src/docker/cadquery/package.json
Normal file
16
app/api/src/docker/cadquery/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
15
app/api/src/docker/cadquery/runCQ.js
Normal file
15
app/api/src/docker/cadquery/runCQ.js
Normal 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 }
|
||||
}
|
||||
}
|
||||
386
app/api/src/docker/cadquery/yarn.lock
Normal file
386
app/api/src/docker/cadquery/yarn.lock
Normal 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=
|
||||
6
app/api/src/docker/common/entrypoint.sh
Normal file
6
app/api/src/docker/common/entrypoint.sh
Normal 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
|
||||
349
app/api/src/docker/common/node14source_setup.sh
Normal file
349
app/api/src/docker/common/node14source_setup.sh
Normal 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
|
||||
41
app/api/src/docker/common/utils.js
Normal file
41
app/api/src/docker/common/utils.js
Normal 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,
|
||||
}
|
||||
44
app/api/src/docker/docker-compose.yml
Normal file
44
app/api/src/docker/docker-compose.yml
Normal 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
|
||||
46
app/api/src/docker/openscad/Dockerfile
Normal file
46
app/api/src/docker/openscad/Dockerfile
Normal 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" ]
|
||||
95
app/api/src/docker/openscad/openscad.js
Normal file
95
app/api/src/docker/openscad/openscad.js
Normal 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,
|
||||
}
|
||||
16
app/api/src/docker/openscad/package.json
Normal file
16
app/api/src/docker/openscad/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
42
app/api/src/docker/openscad/runScad.js
Normal file
42
app/api/src/docker/openscad/runScad.js
Normal 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 }
|
||||
}
|
||||
}
|
||||
386
app/api/src/docker/openscad/yarn.lock
Normal file
386
app/api/src/docker/openscad/yarn.lock
Normal 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=
|
||||
20
app/api/src/docker/package.json
Normal file
20
app/api/src/docker/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
179
app/api/src/docker/serverless.yml
Normal file
179
app/api/src/docker/serverless.yml
Normal 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
1479
app/api/src/docker/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
23
app/api/src/functions/graphql.js
Normal file
23
app/api/src/functions/graphql.js
Normal 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()
|
||||
},
|
||||
})
|
||||
84
app/api/src/functions/identity-signup.js
Normal file
84
app/api/src/functions/identity-signup.js
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
0
app/api/src/graphql/.keep
Normal file
0
app/api/src/graphql/.keep
Normal file
35
app/api/src/graphql/comments.sdl.js
Normal file
35
app/api/src/graphql/comments.sdl.js
Normal 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!
|
||||
}
|
||||
`
|
||||
39
app/api/src/graphql/partReactions.sdl.js
Normal file
39
app/api/src/graphql/partReactions.sdl.js
Normal 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!
|
||||
}
|
||||
`
|
||||
45
app/api/src/graphql/parts.sdl.js
Normal file
45
app/api/src/graphql/parts.sdl.js
Normal 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!
|
||||
}
|
||||
`
|
||||
39
app/api/src/graphql/subjectAccessRequests.sdl.js
Normal file
39
app/api/src/graphql/subjectAccessRequests.sdl.js
Normal 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!
|
||||
}
|
||||
`
|
||||
46
app/api/src/graphql/users.sdl.js
Normal file
46
app/api/src/graphql/users.sdl.js
Normal 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
148
app/api/src/lib/auth.js
Normal 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
21
app/api/src/lib/db.js
Normal 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
17
app/api/src/lib/logger.ts
Normal 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
44
app/api/src/lib/owner.js
Normal 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.")
|
||||
}
|
||||
}
|
||||
}
|
||||
0
app/api/src/services/.keep
Normal file
0
app/api/src/services/.keep
Normal file
38
app/api/src/services/comments/comments.js
Normal file
38
app/api/src/services/comments/comments.js
Normal 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(),
|
||||
}
|
||||
9
app/api/src/services/comments/comments.test.js
Normal file
9
app/api/src/services/comments/comments.test.js
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
import { comments } from './comments'
|
||||
*/
|
||||
|
||||
describe('comments', () => {
|
||||
it('returns true', () => {
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
})
|
||||
48
app/api/src/services/helpers.js
Normal file
48
app/api/src/services/helpers.js
Normal 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)
|
||||
})
|
||||
})
|
||||
68
app/api/src/services/partReactions/partReactions.js
Normal file
68
app/api/src/services/partReactions/partReactions.js
Normal 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(),
|
||||
}
|
||||
9
app/api/src/services/partReactions/partReactions.test.js
Normal file
9
app/api/src/services/partReactions/partReactions.test.js
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
import { partReactions } from './partReactions'
|
||||
*/
|
||||
|
||||
describe('partReactions', () => {
|
||||
it('returns true', () => {
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
})
|
||||
113
app/api/src/services/parts/parts.js
Normal file
113
app/api/src/services/parts/parts.js
Normal 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 } }),
|
||||
}
|
||||
9
app/api/src/services/parts/parts.test.js
Normal file
9
app/api/src/services/parts/parts.test.js
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
import { parts } from './parts'
|
||||
*/
|
||||
|
||||
describe('parts', () => {
|
||||
it('returns true', () => {
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -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(),
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
import { subjectAccessRequests } from './subjectAccessRequests'
|
||||
*/
|
||||
|
||||
describe('subjectAccessRequests', () => {
|
||||
it('returns true', () => {
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
})
|
||||
94
app/api/src/services/users/users.js
Normal file
94
app/api/src/services/users/users.js
Normal 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(),
|
||||
}
|
||||
9
app/api/src/services/users/users.test.js
Normal file
9
app/api/src/services/users/users.test.js
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
import { users } from './users'
|
||||
*/
|
||||
|
||||
describe('users', () => {
|
||||
it('returns true', () => {
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user