@@ -1,19 +0,0 @@
|
||||
# These environment variables will be used by default if you do not create any
|
||||
# yourself in .env. This file should be safe to check into your version control
|
||||
# system. Any custom values should go in .env and .env should *not* be checked
|
||||
# into version control.
|
||||
|
||||
# schema.prisma defaults
|
||||
DATABASE_URL=file:./dev.db
|
||||
|
||||
# disables Prisma CLI update notifier
|
||||
PRISMA_HIDE_UPDATE_MESSAGE=true
|
||||
|
||||
CLOUDINARY_API_KEY=476712943135152
|
||||
# ask Kurt for help getting set up with a secret
|
||||
# CLOUDINARY_API_SECRET=
|
||||
|
||||
# Option to override the current environment's default api-side log level
|
||||
# See: https://redwoodjs.com/docs/logger for level options:
|
||||
# trace | info | debug | warn | error | silent
|
||||
# LOG_LEVEL=debug
|
||||
@@ -1,2 +0,0 @@
|
||||
# DATABASE_URL=file:./dev.db
|
||||
# BINARY_TARGET=native
|
||||
@@ -1 +0,0 @@
|
||||
/web/src/cascade/*
|
||||
@@ -1 +0,0 @@
|
||||
lts/*
|
||||
@@ -1,78 +0,0 @@
|
||||

|
||||
|
||||
# [C a d H u b](https://cadhub.xyz)
|
||||
|
||||
[](https://app.netlify.com/sites/cadhubxyz/deploys)
|
||||
|
||||
Let's help Code-CAD reach its [full potential!](https://cadhub.xyz) We're making a ~~cad~~hub for the Code-CAD community, think of it as model-repository crossed with a live editor. We have an integration with the excellent [cascadeStudio](https://zalo.github.io/CascadeStudio/) with [more coming soon](https://github.com/Irev-Dev/curated-code-cad).
|
||||
|
||||
If you want to be involved in anyway, checkout the [Road Map](https://github.com/Irev-Dev/cadhub/discussions/212) and get in touch via, [twitter](https://twitter.com/IrevDev), [discord](https://discord.gg/SD7zFRNjGH) or [discussions](https://github.com/Irev-Dev/cadhub/discussions).
|
||||
|
||||
<img src="https://raw.githubusercontent.com/Irev-Dev/repo-images/main/images/fullcadhubshot.jpg">
|
||||
|
||||
<img src="https://raw.githubusercontent.com/Irev-Dev/repo-images/main/images/Part%20IDE%20-%20export%20expand%20state.jpg">
|
||||
|
||||
## Getting Started
|
||||
|
||||
Because we're integrating cascadeStudio, this is done some what crudely for the time being, so you'll need to clone the repo with submodules.
|
||||
|
||||
```terminal
|
||||
git clone --recurse-submodules -j8 git@github.com:Irev-Dev/cadhub.git
|
||||
# or
|
||||
git clone --recurse-submodules -j8 https://github.com/Irev-Dev/cadhub.git
|
||||
```
|
||||
|
||||
Install dependencies
|
||||
```terminal
|
||||
yarn install
|
||||
```
|
||||
|
||||
Setting up the db, you'll need to have a postgres installed locally, you can [follow this guide](https://redwoodjs.com/docs/local-postgres-setup) with a couple of exceptions:
|
||||
- Run `yarn rw prisma migrate dev` instead of `yarn rw db up` in the guide.
|
||||
- Don't worry about changing the `schema.prisma` file.
|
||||
- You will need to add a `DATABASE_URL` and test url to you `.env` file at the root of the project.
|
||||
|
||||
Run the following
|
||||
``` terminal
|
||||
yarn rw prisma migrate dev
|
||||
yarn rw prisma db seed
|
||||
```
|
||||
|
||||
### Fire up dev
|
||||
```terminal
|
||||
yarn rw dev
|
||||
```
|
||||
|
||||
Your browser should open automatically to `http://localhost:8910` to see the web app. Lambda functions run on `http://localhost:8911` and are also proxied to `http://localhost:8910/.redwood/functions/*`.
|
||||
|
||||
you can sign in to the following accounts locally
|
||||
|
||||
localUser1@kurthutten.com: `abc123`
|
||||
|
||||
localUser2@kurthutten.com: `abc123`
|
||||
|
||||
localAdmin@kurthutten.com: `abc123`
|
||||
|
||||
You may need to register a account depending on what issue you are trying to tackle, This can be done by clicking the login button on the top right. This will open up netlify's idenitiy modal asking for the websites url, since it will notice you developing locally. Enter `https://cadhub.xyz/` than use you email, verify your email and you should be set.
|
||||
(some routes are protected, but permissions is a big area that needs a lot of work in the near future, so it's in a very incomplete state atm)
|
||||
|
||||
### Note:
|
||||
We're using [RedwoodJS](https://redwoodjs.com/), this is perhaps unwise since they haven't reached 1.0 yet, however with their aim to release 1.0 by the end of the year, it shouldn't be too difficult to port changes over the coming months.
|
||||
If you not familiar with Redwood, never fear the main bit of tech it uses is React, Graphql(apollo) and serverless/lamdas, depending on what part of the app you want to help with, so long as you know you way around these bits of tech you should be fine with some light referencing of the RedWood docs
|
||||
|
||||
### Extra Redwood docs, i.e. getting familiar with the frame work.
|
||||
- [Tutorial](https://redwoodjs.com/tutorial/welcome-to-redwood): getting started and complete overview guide.
|
||||
- [Docs](https://redwoodjs.com/docs/introduction): using the Redwood Router, handling assets and files, list of command-line tools, and more.
|
||||
- [Redwood Community](https://community.redwoodjs.com): get help, share tips and tricks, and collaborate on everything about RedwoodJS.
|
||||
|
||||
## Styles
|
||||
|
||||
We're using tailwind utility classes so please try and use them as much as possible. Again if you not familiar, the [tailwind search](https://tailwindcss.com/) is fantastic, so searching for the css property you want to use will lead you to the correct class 99% of the time.
|
||||
|
||||
## Designs
|
||||
|
||||
In progress, though can be [seen on Figma](https://www.figma.com/file/VUh53RdncjZ7NuFYj0RGB9/CadHub?node-id=0%3A1)
|
||||
|
||||
<img src="https://raw.githubusercontent.com/Irev-Dev/repo-images/main/images/Part%20Page(1).jpg">
|
||||
|
||||
<img src="https://raw.githubusercontent.com/Irev-Dev/repo-images/main/images/User%20Page%20Edit.jpg">
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = { extends: "../babel.config.js" }
|
||||
@@ -1,94 +0,0 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userName" TEXT NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"name" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"image" TEXT,
|
||||
"bio" TEXT,
|
||||
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Part" (
|
||||
"id" TEXT NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"code" TEXT,
|
||||
"mainImage" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"deleted" BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "PartReaction" (
|
||||
"id" TEXT NOT NULL,
|
||||
"emote" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"partId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Comment" (
|
||||
"id" TEXT NOT NULL,
|
||||
"text" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"partId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "SubjectAccessRequest" (
|
||||
"id" TEXT NOT NULL,
|
||||
"comment" TEXT NOT NULL,
|
||||
"payload" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User.userName_unique" ON "User"("userName");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User.email_unique" ON "User"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Part.title_userId_unique" ON "Part"("title", "userId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "PartReaction.emote_userId_partId_unique" ON "PartReaction"("emote", "userId", "partId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Part" ADD FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "PartReaction" ADD FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "PartReaction" ADD FOREIGN KEY ("partId") REFERENCES "Part"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Comment" ADD FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Comment" ADD FOREIGN KEY ("partId") REFERENCES "Part"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "SubjectAccessRequest" ADD FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -1,2 +0,0 @@
|
||||
# Please do not edit this file manually
|
||||
provider = "postgresql"
|
||||
@@ -1,93 +0,0 @@
|
||||
datasource DS {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
binaryTargets = "native"
|
||||
}
|
||||
|
||||
// sqlLight does not suport enums so we can't use enums until we set up postgresql in dev mode
|
||||
// enum Role {
|
||||
// USER
|
||||
// ADMIN
|
||||
// }
|
||||
|
||||
// enum PartType {
|
||||
// CASCADESTUDIO
|
||||
// JSCAD
|
||||
// }
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
userName String @unique // reffered to as userId in @relations
|
||||
email String @unique
|
||||
name String?
|
||||
// role should probably be a list [] and also use enums, neither are supported by sqllight, so we need to set up postgresql in dev
|
||||
// maybe let netlify handle roles for now.
|
||||
// role String @default("user")
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
image String? // url maybe id or file storage service? cloudinary?
|
||||
bio String? //mark down
|
||||
Part Part[]
|
||||
Reaction PartReaction[]
|
||||
Comment Comment[]
|
||||
SubjectAccessRequest SubjectAccessRequest[]
|
||||
}
|
||||
|
||||
model Part {
|
||||
id String @id @default(uuid())
|
||||
title String
|
||||
description String? // markdown string
|
||||
code String?
|
||||
mainImage String? // link to cloudinary
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId String
|
||||
deleted Boolean @default(false)
|
||||
|
||||
Comment Comment[]
|
||||
Reaction PartReaction[]
|
||||
@@unique([title, userId])
|
||||
}
|
||||
|
||||
model PartReaction {
|
||||
id String @id @default(uuid())
|
||||
emote String // an emoji
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId String
|
||||
part Part @relation(fields: [partId], references: [id])
|
||||
partId String
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@@unique([emote, userId, partId])
|
||||
}
|
||||
|
||||
model Comment {
|
||||
id String @id @default(uuid())
|
||||
text String // the comment, should I allow mark down?
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId String
|
||||
part Part @relation(fields: [partId], references: [id])
|
||||
partId String
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model SubjectAccessRequest {
|
||||
id String @id @default(uuid())
|
||||
comment String
|
||||
payload String // json dump
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId String
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
const { PrismaClient } = require('@prisma/client')
|
||||
const dotenv = require('dotenv')
|
||||
|
||||
dotenv.config()
|
||||
const db = new PrismaClient()
|
||||
|
||||
async function main() {
|
||||
// Seed data is database data that needs to exist for your app to run.
|
||||
// Ideally this file should be idempotent: running it multiple times
|
||||
// will result in the same database state (usually by checking for the
|
||||
// existence of a record before trying to create it). For example:
|
||||
//
|
||||
// const existing = await db.user.findMany({ where: { email: 'admin@email.com' }})
|
||||
// if (!existing.length) {
|
||||
// await db.user.create({ data: { name: 'Admin', email: 'admin@email.com' }})
|
||||
// }
|
||||
const users = [
|
||||
{
|
||||
id: "a2b21ce1-ae57-43a2-b6a3-b6e542fd9e60",
|
||||
userName: "local-user-1",
|
||||
name: "local 1",
|
||||
email: "localUser1@kurthutten.com"
|
||||
},
|
||||
{
|
||||
id: "682ba807-d10e-4caf-bf28-74054e46c9ec",
|
||||
userName: "local-user-2",
|
||||
name: "local 2",
|
||||
email: "localUser2@kurthutten.com"
|
||||
},
|
||||
{
|
||||
id: "5cea3906-1e8e-4673-8f0d-89e6a963c096",
|
||||
userName: "local-admin-2",
|
||||
name: "local admin",
|
||||
email: "localAdmin@kurthutten.com"
|
||||
},
|
||||
]
|
||||
|
||||
let existing
|
||||
existing = await db.user.findMany({ where: { id: users[0].id }})
|
||||
if(!existing.length) {
|
||||
await db.user.create({
|
||||
data: users[0],
|
||||
})
|
||||
}
|
||||
existing = await db.user.findMany({ where: { id: users[1].id }})
|
||||
if(!existing.length) {
|
||||
await db.user.create({
|
||||
data: users[1],
|
||||
})
|
||||
}
|
||||
|
||||
const parts = [
|
||||
{
|
||||
title: 'demo-part1',
|
||||
description: '# can be markdown',
|
||||
mainImage: 'CadHub/kjdlgjnu0xmwksia7xox',
|
||||
user: {
|
||||
connect: {
|
||||
id: users[0].id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'demo-part2',
|
||||
description: '## [hey](www.google.com)',
|
||||
user: {
|
||||
connect: {
|
||||
id: users[1].id,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
existing = await db.part.findMany({where: { title: parts[0].title}})
|
||||
if(!existing.length) {
|
||||
await db.part.create({
|
||||
data: parts[0],
|
||||
})
|
||||
}
|
||||
existing = await db.part.findMany({where: { title: parts[1].title}})
|
||||
if(!existing.length) {
|
||||
await db.part.create({
|
||||
data: parts[1],
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
const aPart = await db.part.findUnique({where: {
|
||||
title_userId: {
|
||||
title: parts[0].title,
|
||||
userId: users[0].id,
|
||||
}
|
||||
}})
|
||||
await db.comment.create({
|
||||
data: {
|
||||
text: "nice part, I like it",
|
||||
user: {connect: { id: users[0].id}},
|
||||
part: {connect: { id: aPart.id}},
|
||||
}
|
||||
})
|
||||
await db.partReaction.create({
|
||||
data: {
|
||||
emote: "❤️",
|
||||
user: {connect: { id: users[0].id}},
|
||||
part: {connect: { id: aPart.id}},
|
||||
}
|
||||
})
|
||||
|
||||
console.info('No data to seed. See api/prisma/seeds.js for info.')
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => console.error(e))
|
||||
.finally(async () => {
|
||||
await db.$disconnect()
|
||||
})
|
||||
@@ -1,6 +0,0 @@
|
||||
const { getConfig } = require('@redwoodjs/core')
|
||||
|
||||
const config = getConfig({ type: 'jest', target: 'node' })
|
||||
config.displayName.name = 'api'
|
||||
|
||||
module.exports = config
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"src/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*", "../.redwood/index.d.ts"]
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "api",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@redwoodjs/api": "^0.31.0",
|
||||
"@redwoodjs/api-server": "^0.31.0",
|
||||
"cloudinary": "^1.23.0"
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
FROM node:14
|
||||
|
||||
COPY package.json ./
|
||||
COPY yarn.lock ./
|
||||
COPY aws-emulator.js ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
CMD ["node", "./aws-emulator.js"]
|
||||
@@ -1,34 +0,0 @@
|
||||
# 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)
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
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}`)
|
||||
})
|
||||
@@ -1,49 +0,0 @@
|
||||
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" ]
|
||||
@@ -1,53 +0,0 @@
|
||||
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,
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
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 }
|
||||
}
|
||||
}
|
||||
@@ -1,386 +0,0 @@
|
||||
# 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=
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,349 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,41 +0,0 @@
|
||||
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,
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
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
|
||||
@@ -1,46 +0,0 @@
|
||||
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" ]
|
||||
@@ -1,95 +0,0 @@
|
||||
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,
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
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 }
|
||||
}
|
||||
}
|
||||
@@ -1,386 +0,0 @@
|
||||
# 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=
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
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"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,23 +0,0 @@
|
||||
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()
|
||||
},
|
||||
})
|
||||
@@ -1,84 +0,0 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
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!
|
||||
}
|
||||
`
|
||||
@@ -1,39 +0,0 @@
|
||||
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!
|
||||
}
|
||||
`
|
||||
@@ -1,45 +0,0 @@
|
||||
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!
|
||||
}
|
||||
`
|
||||
@@ -1,39 +0,0 @@
|
||||
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!
|
||||
}
|
||||
`
|
||||
@@ -1,46 +0,0 @@
|
||||
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!
|
||||
}
|
||||
`
|
||||
@@ -1,148 +0,0 @@
|
||||
// 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.")
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
// 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'],
|
||||
})
|
||||
@@ -1,17 +0,0 @@
|
||||
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({})
|
||||
@@ -1,44 +0,0 @@
|
||||
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.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
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(),
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/*
|
||||
import { comments } from './comments'
|
||||
*/
|
||||
|
||||
describe('comments', () => {
|
||||
it('returns true', () => {
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,48 +0,0 @@
|
||||
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)
|
||||
})
|
||||
})
|
||||
@@ -1,68 +0,0 @@
|
||||
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(),
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/*
|
||||
import { partReactions } from './partReactions'
|
||||
*/
|
||||
|
||||
describe('partReactions', () => {
|
||||
it('returns true', () => {
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,113 +0,0 @@
|
||||
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 } }),
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/*
|
||||
import { parts } from './parts'
|
||||
*/
|
||||
|
||||
describe('parts', () => {
|
||||
it('returns true', () => {
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,42 +0,0 @@
|
||||
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(),
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/*
|
||||
import { subjectAccessRequests } from './subjectAccessRequests'
|
||||
*/
|
||||
|
||||
describe('subjectAccessRequests', () => {
|
||||
it('returns true', () => {
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,94 +0,0 @@
|
||||
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(),
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/*
|
||||
import { users } from './users'
|
||||
*/
|
||||
|
||||
describe('users', () => {
|
||||
it('returns true', () => {
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
presets: ['@redwoodjs/core/config/babel-preset'],
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
const { getConfig } = require('@redwoodjs/internal')
|
||||
|
||||
const config = getConfig()
|
||||
|
||||
module.exports = {
|
||||
schema: `http://${config.api.host}:${config.api.port}/graphql`,
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
[build]
|
||||
command = "yarn rw deploy netlify"
|
||||
publish = "web/dist"
|
||||
functions = "api/dist/functions"
|
||||
|
||||
[dev]
|
||||
command = "yarn rw dev"
|
||||
|
||||
[[redirects]]
|
||||
from = "/*"
|
||||
to = "/index.html"
|
||||
status = 200
|
||||
|
||||
[context.deploy-preview.environment]
|
||||
CAD_LAMBDA_BASE_URL = "https://t7wdlz8ztf.execute-api.us-east-1.amazonaws.com/dev2"
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"private": true,
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
"api",
|
||||
"web"
|
||||
]
|
||||
},
|
||||
"scripts": {},
|
||||
"devDependencies": {
|
||||
"@redwoodjs/core": "^0.31.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "@redwoodjs/eslint-config"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14",
|
||||
"yarn": ">=1.15"
|
||||
},
|
||||
"resolutions": {
|
||||
"react": "17.0.1",
|
||||
"react-dom": "17.0.1"
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// https://prettier.io/docs/en/options.html
|
||||
module.exports = {
|
||||
trailingComma: 'es5',
|
||||
bracketSpacing: true,
|
||||
tabWidth: 2,
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
arrowParens: 'always',
|
||||
overrides: [
|
||||
{
|
||||
files: 'Routes.js',
|
||||
options: {
|
||||
printWidth: 200,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
# This file contains the configuration settings for your Redwood app.
|
||||
# This file is also what makes your Redwood app a Redwood app.
|
||||
# If you remove it and try to run `yarn rw dev`, you'll get an error.
|
||||
#
|
||||
# For the full list of options, see the "App Configuration: redwood.toml" doc:
|
||||
# https://redwoodjs.com/docs/app-configuration-redwood-toml
|
||||
|
||||
[web]
|
||||
port = 8910
|
||||
apiProxyPath = "/.netlify/functions"
|
||||
includeEnvironmentVariables = ['GOOGLE_ANALYTICS_ID', 'CLOUDINARY_API_KEY', 'CLOUDINARY_API_SECRET', 'CAD_LAMBDA_BASE_URL']
|
||||
# experimentalFastRefresh = true # this seems to break cascadeStudio
|
||||
[api]
|
||||
port = 8911
|
||||
schemaPath = "./api/db/schema.prisma"
|
||||
[browser]
|
||||
open = true
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = { extends: "../babel.config.js" }
|
||||
@@ -1,8 +0,0 @@
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('tailwindcss')(path.resolve(__dirname, '../tailwind.config.js')),
|
||||
require('autoprefixer'),
|
||||
],
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
|
||||
|
||||
module.exports = (config, { env }) => {
|
||||
config.plugins.forEach((plugin) => {
|
||||
if (plugin.constructor.name === 'HtmlWebpackPlugin') {
|
||||
plugin.options.favicon = './src/favicon.svg'
|
||||
} else if (plugin.constructor.name === 'CopyPlugin') {
|
||||
plugin.patterns.push({
|
||||
from: './src/cascade/js/StandardLibraryIntellisense.ts',
|
||||
to: 'js/StandardLibraryIntellisense.ts',
|
||||
})
|
||||
plugin.patterns.push({
|
||||
from: './src/cascade/static_node_modules/opencascade.js/dist/oc.d.ts',
|
||||
to: 'opencascade.d.ts',
|
||||
})
|
||||
plugin.patterns.push({
|
||||
from: '../node_modules/three/src/Three.d.ts',
|
||||
to: 'Three.d.ts',
|
||||
})
|
||||
plugin.patterns.push({
|
||||
from: './src/cascade/fonts',
|
||||
to: 'fonts',
|
||||
})
|
||||
plugin.patterns.push({
|
||||
from: './src/cascade/textures',
|
||||
to: 'textures',
|
||||
})
|
||||
}
|
||||
})
|
||||
config.plugins.push(
|
||||
new MonacoWebpackPlugin({
|
||||
languages: ['typescript'],
|
||||
features: [
|
||||
'accessibilityHelp',
|
||||
'anchorSelect',
|
||||
'bracketMatching',
|
||||
'caretOperations',
|
||||
'clipboard',
|
||||
'codeAction',
|
||||
'codelens',
|
||||
'comment',
|
||||
'contextmenu',
|
||||
'coreCommands',
|
||||
'cursorUndo',
|
||||
'documentSymbols',
|
||||
'find',
|
||||
'folding',
|
||||
'fontZoom',
|
||||
'format',
|
||||
'gotoError',
|
||||
'gotoLine',
|
||||
'gotoSymbol',
|
||||
'hover',
|
||||
'inPlaceReplace',
|
||||
'indentation',
|
||||
'inlineHints',
|
||||
'inspectTokens',
|
||||
'linesOperations',
|
||||
'linkedEditing',
|
||||
'links',
|
||||
'multicursor',
|
||||
'parameterHints',
|
||||
'quickCommand',
|
||||
'quickHelp',
|
||||
'quickOutline',
|
||||
'referenceSearch',
|
||||
'rename',
|
||||
'smartSelect',
|
||||
'snippets',
|
||||
'suggest',
|
||||
'toggleHighContrast',
|
||||
'toggleTabFocusMode',
|
||||
'transpose',
|
||||
'unusualLineTerminators',
|
||||
'viewportSemanticTokens',
|
||||
'wordHighlighter',
|
||||
'wordOperations',
|
||||
'wordPartOperations',
|
||||
],
|
||||
})
|
||||
)
|
||||
config.module.rules[0].oneOf.push({
|
||||
test: /opencascade\.wasm\.wasm$/,
|
||||
type: 'javascript/auto',
|
||||
loader: 'file-loader',
|
||||
})
|
||||
config.node = {
|
||||
fs: 'empty',
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"event": "signup",
|
||||
"instance_id": "403b7d63-17f9-48f1-a85f-3d6b41c7dad1",
|
||||
"user": {
|
||||
"id": "641222ee-3e61-4253-8c11-9f764779bcc5",
|
||||
"aud": "",
|
||||
"role": "",
|
||||
"email": "k.hutten2@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"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
const { getConfig } = require('@redwoodjs/core')
|
||||
|
||||
const config = getConfig({ type: 'jest', target: 'browser' })
|
||||
config.displayName.name = 'web'
|
||||
|
||||
module.exports = config
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"src/*": ["./src/*"]
|
||||
},
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": ["src/**/*", "../.redwood/index.d.ts"]
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
{
|
||||
"name": "web",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"browserslist": {
|
||||
"development": [
|
||||
"last 1 version"
|
||||
],
|
||||
"production": [
|
||||
"defaults",
|
||||
"not IE 11",
|
||||
"not IE_Mob 11"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.0.0",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@monaco-editor/react": "^4.0.11",
|
||||
"@redwoodjs/auth": "^0.31.0",
|
||||
"@redwoodjs/forms": "^0.31.0",
|
||||
"@redwoodjs/router": "^0.31.0",
|
||||
"@redwoodjs/web": "^0.31.0",
|
||||
"cloudinary-react": "^1.6.7",
|
||||
"controlkit": "^0.1.9",
|
||||
"get-active-classes": "^0.0.11",
|
||||
"golden-layout": "^1.5.9",
|
||||
"gotrue-js": "^0.9.27",
|
||||
"jquery": "^3.5.1",
|
||||
"monaco-editor": "^0.20.0",
|
||||
"monaco-editor-webpack-plugin": "^1.9.1",
|
||||
"netlify-identity-widget": "^1.9.1",
|
||||
"opencascade.js": "^0.1.15",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-dropzone": "^11.2.1",
|
||||
"react-ga": "^3.3.0",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-image-crop": "^8.6.6",
|
||||
"react-mosaic-component": "^4.1.1",
|
||||
"react-three-fiber": "^5.3.19",
|
||||
"rich-markdown-editor": "^11.0.2",
|
||||
"styled-components": "^5.2.0",
|
||||
"three": "^0.118.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "9.8.6",
|
||||
"html-webpack-plugin": "^4.5.0",
|
||||
"opentype.js": "^1.3.3",
|
||||
"postcss-loader": "4.0.2",
|
||||
"tailwindcss": "^1.9.1",
|
||||
"worker-loader": "^3.0.7"
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
# Static Assets
|
||||
Use this folder to add static files directly to your app. All included files and folders will be copied directly into the `/dist` folder (created when Webpack builds for production). They will also be available during development when you run `yarn rw dev`.
|
||||
>Note: files will *not* hot reload while the development server is running. You'll need to manually stop/start to access file changes.
|
||||
|
||||
### Example Use
|
||||
A file like `favicon.png` will be copied to `/dist/favicon.png`. A folder containing a file such as `static-files/my-logo.jpg` will be copied to `/dist/static-files/my-logo.jpg`. These can be referenced in your code directly without any special handling, e.g.
|
||||
```
|
||||
<link rel="icon" type="image/png" href="/favicon.png" />
|
||||
```
|
||||
and
|
||||
```
|
||||
<img src="/static-files/my-logo.jpg"> alt="Logo" />
|
||||
```
|
||||
|
||||
Behind the scenes, we are using Webpack's ["copy-webpack-plugin"](https://github.com/webpack-contrib/copy-webpack-plugin).
|
||||
|
||||
## Best Practices
|
||||
Because assets in this folder are bypassing the javascript module system, **this folder should be used sparingly** for assets such as favicons, robots.txt, manifests, libraries incompatible with Webpack, etc.
|
||||
|
||||
In general, it's best to import files directly into a template, page, or component. This allows Webpack to include that file in the bundle, which ensures Webpack will correctly process and move assets into the distribution folder, providing error checks and correct paths along the way.
|
||||
|
||||
### Example Asset Import with Webpack
|
||||
Instead of handling our logo image as a static file per the example above, we can do the following:
|
||||
```
|
||||
import React from "react"
|
||||
import logo from "./my-logo.jpg"
|
||||
|
||||
|
||||
function Header() {
|
||||
return <img src={logo} alt="Logo" />
|
||||
}
|
||||
|
||||
export default Header
|
||||
```
|
||||
|
||||
Behind the scenes, we are using Webpack's ["file-loader"](https://webpack.js.org/loaders/file-loader/) and ["url-loader](https://webpack.js.org/loaders/url-loader/) (for files smaller than 10kb).
|
||||
@@ -1,2 +0,0 @@
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -1,31 +0,0 @@
|
||||
import { AuthProvider } from '@redwoodjs/auth'
|
||||
import GoTrue from 'gotrue-js'
|
||||
|
||||
import { FatalErrorBoundary } from '@redwoodjs/web'
|
||||
import { RedwoodApolloProvider } from '@redwoodjs/web/apollo'
|
||||
import FatalErrorPage from 'src/pages/FatalErrorPage'
|
||||
import ReactGA from 'react-ga'
|
||||
|
||||
ReactGA.initialize(process.env.GOOGLE_ANALYTICS_ID)
|
||||
|
||||
import Routes from 'src/Routes'
|
||||
|
||||
import './scaffold.css'
|
||||
import './index.css'
|
||||
|
||||
const goTrueClient = new GoTrue({
|
||||
APIUrl: 'https://cadhub.xyz/.netlify/identity',
|
||||
setCookie: true,
|
||||
})
|
||||
|
||||
const App = () => (
|
||||
<FatalErrorBoundary page={FatalErrorPage}>
|
||||
<AuthProvider client={goTrueClient} type="goTrue">
|
||||
<RedwoodApolloProvider>
|
||||
<Routes />
|
||||
</RedwoodApolloProvider>
|
||||
</AuthProvider>
|
||||
</FatalErrorBoundary>
|
||||
)
|
||||
|
||||
export default App
|
||||
@@ -1,70 +0,0 @@
|
||||
// In this file, all Page components from 'src/pages` are auto-imported. Nested
|
||||
// directories are supported, and should be uppercase. Each subdirectory will be
|
||||
// prepended onto the component name.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// 'src/pages/HomePage/HomePage.js' -> HomePage
|
||||
// 'src/pages/Admin/BooksPage/BooksPage.js' -> AdminBooksPage
|
||||
import { useEffect } from 'react'
|
||||
import { Router, Route, Private } from '@redwoodjs/router'
|
||||
|
||||
const welcomeMessage = `
|
||||
%cHey, 👋.
|
||||
%c______________________________________________________________________________
|
||||
|
||||
%cCadHub is in active development - Want to lend a hand? %chttps://github.com/Irev-Dev/cadhub
|
||||
|
||||
%cOr get a sneak peak of work under construction? %chttps://cadhub.xyz/dev-ide
|
||||
|
||||
`
|
||||
|
||||
const Routes = () => {
|
||||
useEffect(
|
||||
() =>
|
||||
console.log(
|
||||
welcomeMessage,
|
||||
'font-family: Georgia, serif; font-weight:bold; line-height: 2rem; font-size: 32px; color: #3c366b',
|
||||
'font-size: 10px; color:#D3D3D3',
|
||||
'font-family: "Ropa Sans",Georgia, serif; font-size: 16px; line-height:3rem',
|
||||
'font-family: Helvetica Neue, sans-serif; font-size: 16px; line-height: 1.5rem; color:#gray',
|
||||
'font-family: "Ropa Sans",Georgia, serif; font-size: 16px; line-height:3rem',
|
||||
'font-family: Helvetica Neue, sans-serif; font-size: 16px; line-height: 1.5rem'
|
||||
),
|
||||
[]
|
||||
)
|
||||
return (
|
||||
<Router>
|
||||
<Route path="/dev-ide/{cadPackage}" page={DevIdePage} name="devIde" />
|
||||
<Route path="/policies/privacy-policy" page={PrivacyPolicyPage} name="privacyPolicy" />
|
||||
<Route path="/policies/code-of-conduct" page={CodeOfConductPage} name="codeOfConduct" />
|
||||
<Route path="/account-recovery/update-password" page={UpdatePasswordPage} name="updatePassword" />
|
||||
<Route path="/account-recovery" page={AccountRecoveryPage} name="accountRecovery" />
|
||||
<Route path="/" page={HomePage} name="home" prerender />
|
||||
<Route notfound page={NotFoundPage} />
|
||||
|
||||
{/* Ownership enforced routes */}
|
||||
<Route path="/u/{userName}/new" page={NewPartPage} name="newPart" />
|
||||
<Private unauthenticated="home" role="user">
|
||||
<Route path="/u/{userName}/edit" page={EditUserPage} name="editUser" />
|
||||
<Route path="/u/{userName}/{partTitle}/edit" page={EditPartPage} name="editPart" />
|
||||
</Private>
|
||||
{/* End ownership enforced routes */}
|
||||
|
||||
<Route path="/draft" page={DraftPartPage} name="draftPart" />
|
||||
<Route path="/u/{userName}" page={UserPage} name="user" />
|
||||
<Route path="/u/{userName}/{partTitle}" page={PartPage} name="part" />
|
||||
<Route path="/u/{userName}/{partTitle}/ide" page={IdePartPage} name="ide" />
|
||||
|
||||
<Private unauthenticated="home" role="admin">
|
||||
<Route path="/admin/users" page={UsersPage} name="users" />
|
||||
<Route path="/admin/parts" page={AdminPartsPage} name="parts" />
|
||||
<Route path="/admin/subject-access-requests/{id}/edit" page={EditSubjectAccessRequestPage} name="editSubjectAccessRequest" />
|
||||
<Route path="/admin/subject-access-requests/{id}" page={SubjectAccessRequestPage} name="subjectAccessRequest" />
|
||||
<Route path="/admin/subject-access-requests" page={SubjectAccessRequestsPage} name="subjectAccessRequests" />
|
||||
</Private>
|
||||
</Router>
|
||||
)
|
||||
}
|
||||
|
||||
export default Routes
|
||||
Submodule app/web/web/src/cascade deleted from cd23a8e673
@@ -1,128 +0,0 @@
|
||||
import { useMutation, useFlash } from '@redwoodjs/web'
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
|
||||
import { QUERY } from 'src/components/AdminPartsCell'
|
||||
|
||||
const DELETE_PART_MUTATION = gql`
|
||||
mutation DeletePartMutation($id: String!) {
|
||||
deletePart(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const MAX_STRING_LENGTH = 150
|
||||
|
||||
const truncate = (text) => {
|
||||
let output = text
|
||||
if (text && text.length > MAX_STRING_LENGTH) {
|
||||
output = output.substring(0, MAX_STRING_LENGTH) + '...'
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
const jsonTruncate = (obj) => {
|
||||
return truncate(JSON.stringify(obj, null, 2))
|
||||
}
|
||||
|
||||
const timeTag = (datetime) => {
|
||||
return (
|
||||
<time dateTime={datetime} title={datetime}>
|
||||
{new Date(datetime).toUTCString()}
|
||||
</time>
|
||||
)
|
||||
}
|
||||
|
||||
const checkboxInputTag = (checked) => {
|
||||
return <input type="checkbox" checked={checked} disabled />
|
||||
}
|
||||
|
||||
const AdminParts = ({ parts }) => {
|
||||
const { addMessage } = useFlash()
|
||||
const [deletePart] = useMutation(DELETE_PART_MUTATION, {
|
||||
onCompleted: () => {
|
||||
addMessage('Part deleted.', { classes: 'rw-flash-success' })
|
||||
},
|
||||
// This refetches the query on the list page. Read more about other ways to
|
||||
// update the cache over here:
|
||||
// https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates
|
||||
refetchQueries: [{ query: QUERY }],
|
||||
awaitRefetchQueries: true,
|
||||
})
|
||||
|
||||
const onDeleteClick = (id) => {
|
||||
if (confirm('Are you sure you want to delete part ' + id + '?')) {
|
||||
deletePart({ variables: { id } })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rw-segment rw-table-wrapper-responsive">
|
||||
<table className="rw-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Title</th>
|
||||
<th>Description</th>
|
||||
<th>Code</th>
|
||||
<th>Main image</th>
|
||||
<th>Created at</th>
|
||||
<th>Updated at</th>
|
||||
<th>User id</th>
|
||||
<th>Deleted</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{parts.map((part) => (
|
||||
<tr key={part.id}>
|
||||
<td>{truncate(part.id)}</td>
|
||||
<td>{truncate(part.title)}</td>
|
||||
<td>{truncate(part.description)}</td>
|
||||
<td>{truncate(part.code)}</td>
|
||||
<td>{truncate(part.mainImage)}</td>
|
||||
<td>{timeTag(part.createdAt)}</td>
|
||||
<td>{timeTag(part.updatedAt)}</td>
|
||||
<td>{truncate(part.userId)}</td>
|
||||
<td>{checkboxInputTag(part.deleted)}</td>
|
||||
<td>
|
||||
<nav className="rw-table-actions">
|
||||
<Link
|
||||
to={routes.part({
|
||||
userName: part?.user?.userName,
|
||||
partTitle: part?.title,
|
||||
})}
|
||||
title={'Show part ' + part.id + ' detail'}
|
||||
className="rw-button rw-button-small"
|
||||
>
|
||||
Show
|
||||
</Link>
|
||||
<Link
|
||||
to={routes.editPart({
|
||||
userName: part?.user?.userName,
|
||||
partTitle: part?.title,
|
||||
})}
|
||||
title={'Edit part ' + part.id}
|
||||
className="rw-button rw-button-small rw-button-blue"
|
||||
>
|
||||
Edit
|
||||
</Link>
|
||||
<a
|
||||
href="#"
|
||||
title={'Delete part ' + part.id}
|
||||
className="rw-button rw-button-small rw-button-red"
|
||||
onClick={() => onDeleteClick(part.id)}
|
||||
>
|
||||
Delete
|
||||
</a>
|
||||
</nav>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AdminParts
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
|
||||
import AdminParts from 'src/components/AdminParts'
|
||||
|
||||
export const QUERY = gql`
|
||||
query PARTS {
|
||||
parts {
|
||||
id
|
||||
title
|
||||
description
|
||||
code
|
||||
mainImage
|
||||
createdAt
|
||||
updatedAt
|
||||
userId
|
||||
deleted
|
||||
user {
|
||||
userName
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Empty = () => {
|
||||
return (
|
||||
<div className="rw-text-center">
|
||||
{'No parts yet. '}
|
||||
<Link to={routes.newPart()} className="rw-link">
|
||||
{'Create one?'}
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const Success = ({ parts }) => {
|
||||
return <AdminParts parts={parts} />
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { getActiveClasses } from 'get-active-classes'
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
|
||||
import InputText from 'src/components/InputText'
|
||||
|
||||
const Breadcrumb = ({
|
||||
userName,
|
||||
partTitle,
|
||||
onPartTitleChange,
|
||||
className,
|
||||
isInvalid,
|
||||
}) => {
|
||||
return (
|
||||
<h3 className={getActiveClasses('text-2xl font-roboto', className)}>
|
||||
<div className="w-1 inline-block text-indigo-800 bg-indigo-800 mr-2">
|
||||
.
|
||||
</div>
|
||||
<span
|
||||
className={getActiveClasses({
|
||||
'text-gray-500': !onPartTitleChange,
|
||||
'text-gray-400': onPartTitleChange,
|
||||
})}
|
||||
>
|
||||
<Link to={routes.user({ userName })}>{userName}</Link>
|
||||
</span>
|
||||
<div className="w-1 inline-block bg-gray-400 text-gray-400 mx-3 transform -skew-x-20">
|
||||
.
|
||||
</div>
|
||||
<InputText
|
||||
value={partTitle}
|
||||
onChange={onPartTitleChange}
|
||||
isEditable={onPartTitleChange}
|
||||
className={getActiveClasses('text-indigo-800 text-2xl', {
|
||||
'-ml-2': !onPartTitleChange,
|
||||
})}
|
||||
isInvalid={isInvalid}
|
||||
/>
|
||||
</h3>
|
||||
)
|
||||
}
|
||||
|
||||
export default Breadcrumb
|
||||
@@ -1,7 +0,0 @@
|
||||
import Breadcrumb from './Breadcrumb'
|
||||
|
||||
export const generated = () => {
|
||||
return <Breadcrumb />
|
||||
}
|
||||
|
||||
export default { title: 'Components/Breadcrumb' }
|
||||
@@ -1,11 +0,0 @@
|
||||
import { render } from '@redwoodjs/testing'
|
||||
|
||||
import Breadcrumb from './Breadcrumb'
|
||||
|
||||
describe('Breadcrumb', () => {
|
||||
it('renders successfully', () => {
|
||||
expect(() => {
|
||||
render(<Breadcrumb />)
|
||||
}).not.toThrow()
|
||||
})
|
||||
})
|
||||
@@ -1,37 +0,0 @@
|
||||
import { getActiveClasses } from 'get-active-classes'
|
||||
import Svg from 'src/components/Svg'
|
||||
|
||||
const Button = ({
|
||||
onClick,
|
||||
iconName,
|
||||
children,
|
||||
className,
|
||||
shouldAnimateHover,
|
||||
disabled,
|
||||
type,
|
||||
}) => {
|
||||
return (
|
||||
<button
|
||||
disabled={disabled}
|
||||
className={getActiveClasses(
|
||||
{
|
||||
'bg-gray-300 shadow-none hover:shadow-none': disabled,
|
||||
'text-red-600 bg-red-200 border border-red-600': type === 'danger',
|
||||
'text-indigo-600': !type,
|
||||
},
|
||||
'flex items-center bg-opacity-50 rounded-xl p-2 px-6',
|
||||
{
|
||||
'mx-px transform hover:-translate-y-px transition-all duration-150':
|
||||
shouldAnimateHover && !disabled,
|
||||
},
|
||||
className
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
{children}
|
||||
<Svg className="w-6 ml-4" name={iconName} />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
export default Button
|
||||
@@ -1,12 +0,0 @@
|
||||
import Button from './Button'
|
||||
|
||||
export const generated = () => {
|
||||
return (
|
||||
<>
|
||||
button with icon
|
||||
<Button>click Me </Button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default { title: 'Components/Button' }
|
||||
@@ -1,11 +0,0 @@
|
||||
import { render } from '@redwoodjs/testing'
|
||||
|
||||
import Button from './Button'
|
||||
|
||||
describe('Button', () => {
|
||||
it('renders successfully', () => {
|
||||
expect(() => {
|
||||
render(<Button />)
|
||||
}).not.toThrow()
|
||||
})
|
||||
})
|
||||
@@ -1,38 +0,0 @@
|
||||
import Dialog from '@material-ui/core/Dialog'
|
||||
import Button from 'src/components/Button'
|
||||
|
||||
const ConfirmDialog = ({ open, onClose, message, onConfirm }) => {
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose}>
|
||||
<div className="bg-gray-100 max-w-3xl rounded-lg shadow-lg">
|
||||
<div className="p-4">
|
||||
<span className="text-gray-600 text-center">{message}</span>
|
||||
<div className="flex gap-4">
|
||||
<Button
|
||||
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-indigo-200 relative z-20"
|
||||
shouldAnimateHover
|
||||
iconName={'save'}
|
||||
onClick={onClose}
|
||||
>
|
||||
Don't delete
|
||||
</Button>
|
||||
<Button
|
||||
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-red-200 relative z-20"
|
||||
shouldAnimateHover
|
||||
iconName={'trash'}
|
||||
onClick={() => {
|
||||
onClose()
|
||||
onConfirm()
|
||||
}}
|
||||
type="danger"
|
||||
>
|
||||
Yes, Delete
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default ConfirmDialog
|
||||
@@ -1,7 +0,0 @@
|
||||
import ConfirmDialog from './ConfirmDialog'
|
||||
|
||||
export const generated = () => {
|
||||
return <ConfirmDialog />
|
||||
}
|
||||
|
||||
export default { title: 'Components/ConfirmDialog' }
|
||||
@@ -1,11 +0,0 @@
|
||||
import { render } from '@redwoodjs/testing'
|
||||
|
||||
import ConfirmDialog from './ConfirmDialog'
|
||||
|
||||
describe('ConfirmDialog', () => {
|
||||
it('renders successfully', () => {
|
||||
expect(() => {
|
||||
render(<ConfirmDialog />)
|
||||
}).not.toThrow()
|
||||
})
|
||||
})
|
||||
@@ -1,70 +0,0 @@
|
||||
import { useMutation, useFlash } from '@redwoodjs/web'
|
||||
import { navigate, routes } from '@redwoodjs/router'
|
||||
import SubjectAccessRequestForm from 'src/components/SubjectAccessRequestForm'
|
||||
|
||||
export const QUERY = gql`
|
||||
query FIND_SUBJECT_ACCESS_REQUEST_BY_ID($id: String!) {
|
||||
subjectAccessRequest: subjectAccessRequest(id: $id) {
|
||||
id
|
||||
comment
|
||||
payload
|
||||
userId
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`
|
||||
const UPDATE_SUBJECT_ACCESS_REQUEST_MUTATION = gql`
|
||||
mutation UpdateSubjectAccessRequestMutation(
|
||||
$id: String!
|
||||
$input: UpdateSubjectAccessRequestInput!
|
||||
) {
|
||||
updateSubjectAccessRequest(id: $id, input: $input) {
|
||||
id
|
||||
comment
|
||||
payload
|
||||
userId
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Success = ({ subjectAccessRequest }) => {
|
||||
const { addMessage } = useFlash()
|
||||
const [updateSubjectAccessRequest, { loading, error }] = useMutation(
|
||||
UPDATE_SUBJECT_ACCESS_REQUEST_MUTATION,
|
||||
{
|
||||
onCompleted: () => {
|
||||
navigate(routes.subjectAccessRequests())
|
||||
addMessage('SubjectAccessRequest updated.', {
|
||||
classes: 'rw-flash-success',
|
||||
})
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const onSave = (input, id) => {
|
||||
updateSubjectAccessRequest({ variables: { id, input } })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rw-segment">
|
||||
<header className="rw-segment-header">
|
||||
<h2 className="rw-heading rw-heading-secondary">
|
||||
Edit SubjectAccessRequest {subjectAccessRequest.id}
|
||||
</h2>
|
||||
</header>
|
||||
<div className="rw-segment-main">
|
||||
<SubjectAccessRequestForm
|
||||
subjectAccessRequest={subjectAccessRequest}
|
||||
onSave={onSave}
|
||||
error={error}
|
||||
loading={loading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import { useMutation, useFlash } from '@redwoodjs/web'
|
||||
import { navigate, routes } from '@redwoodjs/router'
|
||||
|
||||
import UserProfile from 'src/components/UserProfile'
|
||||
|
||||
export const QUERY = gql`
|
||||
query FIND_USER_BY_ID($userName: String!) {
|
||||
user: userName(userName: $userName) {
|
||||
id
|
||||
userName
|
||||
name
|
||||
createdAt
|
||||
updatedAt
|
||||
image
|
||||
bio
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const UPDATE_USER_MUTATION = gql`
|
||||
mutation UpdateUserMutation($userName: String!, $input: UpdateUserInput!) {
|
||||
updateUserByUserName(userName: $userName, input: $input) {
|
||||
id
|
||||
userName
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div className="h-screen">Loading...</div>
|
||||
|
||||
export const Empty = () => <div className="h-full">Empty</div>
|
||||
|
||||
export const Failure = ({ error }) => <div>Error: {error.message}</div>
|
||||
|
||||
export const Success = ({ user, refetch, variables: { isEditable } }) => {
|
||||
const { addMessage } = useFlash()
|
||||
const [updateUser, { loading, error }] = useMutation(UPDATE_USER_MUTATION, {
|
||||
onCompleted: ({ updateUserByUserName }) => {
|
||||
navigate(routes.user({ userName: updateUserByUserName.userName }))
|
||||
addMessage('User updated.', { classes: 'rw-flash-success' })
|
||||
},
|
||||
})
|
||||
|
||||
const onSave = async (userName, input) => {
|
||||
await updateUser({ variables: { userName, input } })
|
||||
refetch()
|
||||
}
|
||||
|
||||
return (
|
||||
<UserProfile
|
||||
user={user}
|
||||
onSave={onSave}
|
||||
loading={loading}
|
||||
error={error}
|
||||
isEditable={isEditable}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
// Define your own mock data here:
|
||||
export const standard = (/* vars, { ctx, req } */) => ({
|
||||
editUser: {
|
||||
id: 42,
|
||||
},
|
||||
})
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Loading, Empty, Failure, Success } from './EditUserCell'
|
||||
import { standard } from './EditUserCell.mock'
|
||||
|
||||
export const loading = () => {
|
||||
return Loading ? <Loading /> : null
|
||||
}
|
||||
|
||||
export const empty = () => {
|
||||
return Empty ? <Empty /> : null
|
||||
}
|
||||
|
||||
export const failure = () => {
|
||||
return Failure ? <Failure error={new Error('Oh no')} /> : null
|
||||
}
|
||||
|
||||
export const success = () => {
|
||||
return Success ? <Success {...standard()} /> : null
|
||||
}
|
||||
|
||||
export default { title: 'Cells/EditUserCell' }
|
||||
@@ -1,26 +0,0 @@
|
||||
import { render, screen } from '@redwoodjs/testing'
|
||||
import { Loading, Empty, Failure, Success } from './EditUserCell'
|
||||
import { standard } from './EditUserCell.mock'
|
||||
|
||||
describe('EditUserCell', () => {
|
||||
test('Loading renders successfully', () => {
|
||||
render(<Loading />)
|
||||
// Use screen.debug() to see output
|
||||
expect(screen.getByText('Loading...')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('Empty renders successfully', async () => {
|
||||
render(<Empty />)
|
||||
expect(screen.getByText('Empty')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('Failure renders successfully', async () => {
|
||||
render(<Failure error={new Error('Oh no')} />)
|
||||
expect(screen.getByText(/Oh no/i)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('Success renders successfully', async () => {
|
||||
render(<Success editUser={standard().editUser} />)
|
||||
expect(screen.getByText(/42/i)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -1,133 +0,0 @@
|
||||
import { useState } from 'react'
|
||||
import { getActiveClasses } from 'get-active-classes'
|
||||
import Popover from '@material-ui/core/Popover'
|
||||
import { useAuth } from '@redwoodjs/auth'
|
||||
|
||||
import Svg from 'src/components/Svg'
|
||||
|
||||
const emojiMenu = ['❤️', '👍', '😄', '🙌']
|
||||
// const emojiMenu = ['🏆', '❤️', '👍', '😊', '😄', '🚀', '👏', '🙌']
|
||||
const noEmotes = [
|
||||
{
|
||||
emoji: '❤️',
|
||||
count: 0,
|
||||
},
|
||||
]
|
||||
|
||||
const textShadow = { textShadow: '0 4px 6px rgba(0, 0, 0, 0.3)' }
|
||||
|
||||
const EmojiReaction = ({
|
||||
emotes,
|
||||
userEmotes,
|
||||
onEmote = () => {},
|
||||
onShowPartReactions,
|
||||
className,
|
||||
}) => {
|
||||
const { currentUser } = useAuth()
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
const [popoverId, setPopoverId] = useState(undefined)
|
||||
|
||||
const openPopover = (target) => {
|
||||
setAnchorEl(target)
|
||||
setPopoverId('simple-popover')
|
||||
setIsOpen(true)
|
||||
}
|
||||
|
||||
const closePopover = () => {
|
||||
setAnchorEl(null)
|
||||
setPopoverId(undefined)
|
||||
setIsOpen(false)
|
||||
}
|
||||
|
||||
const togglePopover = ({ currentTarget }) => {
|
||||
if (isOpen) {
|
||||
return closePopover()
|
||||
}
|
||||
|
||||
openPopover(currentTarget)
|
||||
}
|
||||
|
||||
const handleEmojiClick = (emoji) => {
|
||||
// TODO handle user not signed in better, maybe open up a modal, I danno think about it.
|
||||
currentUser && onEmote(emoji)
|
||||
closePopover()
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={getActiveClasses(
|
||||
'h-10 relative overflow-hidden py-4',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="absolute left-0 w-8 inset-y-0 z-10 flex items-center bg-gray-100">
|
||||
<div
|
||||
className="h-8 w-8 relative"
|
||||
aria-describedby={popoverId}
|
||||
onClick={togglePopover}
|
||||
>
|
||||
<button className="bg-gray-200 border-2 m-px w-full h-full border-gray-300 rounded-full flex justify-center items-center shadow-md hover:shadow-lg hover:border-indigo-200 transform hover:-translate-y-px transition-all duration-150">
|
||||
<Svg
|
||||
className="h-8 w-8 pt-px mt-px text-gray-500"
|
||||
name="dots-vertical"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="whitespace-no-wrap absolute right-0 inset-y-0 flex items-center flex-row-reverse">
|
||||
{(emotes.length ? emotes : noEmotes).map((emote, i) => (
|
||||
<span
|
||||
className={getActiveClasses(
|
||||
'rounded-full tracking-wide hover:bg-indigo-100 p-1 mx-px transform hover:-translate-y-px transition-all duration-150 border-indigo-400',
|
||||
{ border: currentUser && userEmotes?.includes(emote.emoji) }
|
||||
)}
|
||||
style={textShadow}
|
||||
key={`${emote.emoji}--${i}`}
|
||||
onClick={() => handleEmojiClick(emote.emoji)}
|
||||
>
|
||||
<span className="text-lg pr-1">{emote.emoji}</span>
|
||||
<span className="text-sm font-ropa-sans">{emote.count}</span>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<Popover
|
||||
id={popoverId}
|
||||
open={isOpen}
|
||||
anchorEl={anchorEl}
|
||||
onClose={closePopover}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'left',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'left',
|
||||
}}
|
||||
>
|
||||
<div className="p-2 pr-3 flex flex-col">
|
||||
<div className="inline-flex">
|
||||
{emojiMenu.map((emoji, i) => (
|
||||
<button
|
||||
className="p-2"
|
||||
style={textShadow}
|
||||
key={`${emoji}-${i}}`}
|
||||
onClick={() => handleEmojiClick(emoji)}
|
||||
>
|
||||
{emoji}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<button className="text-gray-700" onClick={onShowPartReactions}>
|
||||
View Reactions
|
||||
</button>
|
||||
</div>
|
||||
</Popover>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default EmojiReaction
|
||||
@@ -1,7 +0,0 @@
|
||||
import EmojiReaction from './EmojiReaction'
|
||||
|
||||
export const generated = () => {
|
||||
return <EmojiReaction />
|
||||
}
|
||||
|
||||
export default { title: 'Components/EmojiReaction' }
|
||||
@@ -1,11 +0,0 @@
|
||||
import { render } from '@redwoodjs/testing'
|
||||
|
||||
import EmojiReaction from './EmojiReaction'
|
||||
|
||||
describe('EmojiReaction', () => {
|
||||
it('renders successfully', () => {
|
||||
expect(() => {
|
||||
render(<EmojiReaction />)
|
||||
}).not.toThrow()
|
||||
})
|
||||
})
|
||||
@@ -1,23 +0,0 @@
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
import OutBound from 'src/components/OutBound'
|
||||
|
||||
const Footer = () => {
|
||||
return (
|
||||
<div className="bg-indigo-900 text-indigo-200 font-roboto mt-20 text-sm">
|
||||
<div className="flex h-16 justify-end items-center mx-16">
|
||||
<OutBound
|
||||
className="mr-8"
|
||||
to="https://github.com/Irev-Dev/cadhub/discussions/212"
|
||||
>
|
||||
Road Map
|
||||
</OutBound>
|
||||
<Link className="mr-8" to={routes.codeOfConduct()}>
|
||||
Code of Conduct
|
||||
</Link>
|
||||
<Link to={routes.privacyPolicy()}>Privacy Policy</Link>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Footer
|
||||
@@ -1,7 +0,0 @@
|
||||
import Footer from './Footer'
|
||||
|
||||
export const generated = () => {
|
||||
return <Footer />
|
||||
}
|
||||
|
||||
export default { title: 'Components/Footer' }
|
||||
@@ -1,11 +0,0 @@
|
||||
import { render } from '@redwoodjs/testing'
|
||||
|
||||
import Footer from './Footer'
|
||||
|
||||
describe('Footer', () => {
|
||||
it('renders successfully', () => {
|
||||
expect(() => {
|
||||
render(<Footer />)
|
||||
}).not.toThrow()
|
||||
})
|
||||
})
|
||||
@@ -1,126 +0,0 @@
|
||||
import { useAuth } from '@redwoodjs/auth'
|
||||
import CascadeController from 'src/helpers/cascadeController'
|
||||
import IdeToolbar from 'src/components/IdeToolbar'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { threejsViewport } from 'src/cascade/js/MainPage/CascadeState'
|
||||
import {
|
||||
uploadToCloudinary,
|
||||
captureAndSaveViewport,
|
||||
} from 'src/helpers/cloudinary'
|
||||
|
||||
const defaultExampleCode = `// Welcome to Cascade Studio! Here are some useful functions:
|
||||
// Translate(), Rotate(), Scale(), Union(), Difference(), Intersection()
|
||||
// Box(), Sphere(), Cylinder(), Cone(), Text3D(), Polygon()
|
||||
// Offset(), Extrude(), RotatedExtrude(), Revolve(), Pipe(), Loft(),
|
||||
// FilletEdges(), ChamferEdges(),
|
||||
// Slider(), Button(), Checkbox()
|
||||
|
||||
let holeRadius = Slider("Radius", 30 , 20 , 40);
|
||||
|
||||
let sphere = Sphere(50);
|
||||
let cylinderZ = Cylinder(holeRadius, 200, true);
|
||||
let cylinderY = Rotate([0,1,0], 90, Cylinder(holeRadius, 200, true));
|
||||
let cylinderX = Rotate([1,0,0], 90, Cylinder(holeRadius, 200, true));
|
||||
|
||||
Translate([0, 0, 50], Difference(sphere, [cylinderX, cylinderY, cylinderZ]));
|
||||
|
||||
Translate([-130, 0, 100], Text3D("Start Hacking"));
|
||||
|
||||
// Don't forget to push imported or oc-defined shapes into sceneShapes to add them to the workspace!`
|
||||
|
||||
const IdeCascadeStudio = ({ part, saveCode, loading }) => {
|
||||
const isDraft = !part
|
||||
const [code, setCode] = useState(isDraft ? defaultExampleCode : part.code)
|
||||
const { currentUser } = useAuth()
|
||||
const canEdit = currentUser?.sub === part?.user?.id
|
||||
useEffect(() => {
|
||||
// Cascade studio attaches "cascade-container" a div outside the react app in 'web/src/index.html', and so we are
|
||||
// "opening" and "closing" it for the ide part of the app by displaying none or block. Which is why this useEffect
|
||||
// returns a clean up function that hides the div again.
|
||||
setCode(part?.code || '')
|
||||
const onCodeChange = (code) => setCode(code)
|
||||
CascadeController.initialise(onCodeChange, code || '')
|
||||
const element = document.getElementById('cascade-container')
|
||||
element.setAttribute('style', 'display: block; opacity: 100%; overflow: hidden; height: calc(100vh - 8rem)') // eslint-disable-line
|
||||
return () => {
|
||||
element.setAttribute('style', 'display: none; overflow: hidden; height: calc(100vh - 8rem)') // eslint-disable-line
|
||||
}
|
||||
}, [part?.code])
|
||||
const isChanges = code !== part?.code
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<IdeToolbar
|
||||
canEdit={canEdit}
|
||||
isChanges={isChanges && !loading}
|
||||
isDraft={isDraft}
|
||||
code={code}
|
||||
onSave={async () => {
|
||||
const input = {
|
||||
code,
|
||||
title: part?.title,
|
||||
userId: currentUser?.sub,
|
||||
description: part?.description,
|
||||
}
|
||||
const isFork = !canEdit
|
||||
if (isFork) {
|
||||
const { publicId } = await captureAndSaveViewport()
|
||||
input.mainImage = publicId
|
||||
}
|
||||
saveCode({
|
||||
input,
|
||||
id: part.id,
|
||||
isFork,
|
||||
})
|
||||
}}
|
||||
onExport={(type) => threejsViewport[`saveShape${type}`]()}
|
||||
userNamePart={{
|
||||
userName: part?.user?.userName,
|
||||
partTitle: part?.title,
|
||||
image: part?.user?.image,
|
||||
}}
|
||||
onCapture={async () => {
|
||||
const config = {
|
||||
currImage: part?.mainImage,
|
||||
callback: uploadAndUpdateImage,
|
||||
cloudinaryImgURL: '',
|
||||
updated: false,
|
||||
}
|
||||
// Get the canvas image as a Data URL
|
||||
config.image = await CascadeController.capture(
|
||||
threejsViewport.environment
|
||||
)
|
||||
config.imageObjectURL = window.URL.createObjectURL(config.image)
|
||||
|
||||
async function uploadAndUpdateImage() {
|
||||
// Upload the image to Cloudinary
|
||||
const cloudinaryImgURL = await uploadToCloudinary(config.image)
|
||||
|
||||
// Save the screenshot as the mainImage
|
||||
saveCode({
|
||||
input: {
|
||||
mainImage: cloudinaryImgURL.public_id,
|
||||
},
|
||||
id: part?.id,
|
||||
isFork: !canEdit,
|
||||
})
|
||||
|
||||
return cloudinaryImgURL
|
||||
}
|
||||
|
||||
// if there isn't a screenshot saved yet, just go ahead and save right away
|
||||
if (!part || !part.mainImage) {
|
||||
config.cloudinaryImgURL = await uploadAndUpdateImage().public_id
|
||||
config.updated = true
|
||||
}
|
||||
|
||||
return config
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default IdeCascadeStudio
|
||||
@@ -1,7 +0,0 @@
|
||||
import IdeCascadeStudio from './IdeCascadeStudio'
|
||||
|
||||
export const generated = () => {
|
||||
return <IdeCascadeStudio />
|
||||
}
|
||||
|
||||
export default { title: 'Components/IdeCascadeStudio' }
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user