473 Commits

Author SHA1 Message Date
Kurt Hutten
41798682b0 Tweaks 2021-11-21 09:05:30 +11:00
Kurt Hutten
bca9c531a6 Add backend drawing 2021-11-21 07:23:37 +11:00
Kurt Hutten
9e0f1eee60 Get Cad Function working along side other app functions
with serverless
2021-11-20 20:51:36 +11:00
Kurt Hutten
53a6639fd1 Initial rw serverless setup 2021-11-20 17:36:06 +11:00
Kurt Hutten
e9859d85b8 Kurt/upgrade rw 38 (#575)
* Upgrade to rw 38

* Prisma migrate after V3 upgrade

* rw 0.38.1
2021-11-13 11:53:42 +11:00
Kurt Hutten
a62c5bce03 Update OpenSCAD userGuide 2021-11-12 07:29:44 +11:00
Kurt Hutten
149b2b6360 bounding sphere fix (#572) 2021-11-06 13:36:53 +11:00
Kurt Hutten
43fc897bf9 Zoom to fit for openscad (#569)
* Add viewall flag to openscad cli in prep for zoom to fit for scad previews

* Fix remaining issues with social image capture
2021-11-06 09:46:55 +11:00
Kurt Hutten
a909188f15 Increment contributor count 2021-10-24 07:12:04 +11:00
Jaakko Mäntylä
e8da05be8c Use project title as stl name (#570) 2021-10-24 07:09:46 +11:00
Kurt Hutten
dd0178d554 Add expressing intent blog post 2021-10-22 20:56:57 +11:00
Kurt Hutten
e3efb1a3dd Improve types in menuConfig 2021-10-21 07:08:50 +11:00
Kurt Hutten
cd90c3ce49 Update curated code cad 2021-10-20 15:31:08 +11:00
Kurt Hutten
1ea4f9bdd5 Remove the glitch effect on the home page. (#567)
I three people mention to me "what is getting all distorted. Obviously
it's not clear that it's a stylistic effect. plus the home page
animation is busy enough as it is.
2021-10-20 14:25:34 +11:00
Jay Clark
e5f2552fc9 Fix customizer input size (#565)
Signed-off-by: Jay Clark <jay@jayeclark.dev>
2021-10-20 14:19:39 +11:00
Kurt Hutten
219f341972 Kurt/rw 37 upgrade (#566)
* Update readme

* Upgrade redwood to 0.37.x
2021-10-20 14:10:19 +11:00
Kurt Hutten
e26beda598 Fix zoom to fit bug 2021-10-18 07:42:00 +11:00
Kurt Hutten
c402b051e2 Fix stl download bug 2021-10-18 07:25:38 +11:00
Lucas Barros
f7172be68b Pass current code as input for fork mutation (#563) 2021-10-17 11:41:26 +11:00
Kurt Hutten
da0a4d6f1c Add initial code to seed script 2021-10-17 10:23:49 +11:00
Kurt Hutten
0bc759cf9e Lint project 2021-10-17 05:42:25 +11:00
Leonel Jara
a4cfc37576 Add zoom to fit on first load (#561)
* get the bounding box of assets and get controls Ref

* Add zoom to fit on first load

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2021-10-17 05:40:58 +11:00
Kurt Hutten
bc1f12971d Make-cq-customizer-more-resilient (#560)
Was failing when initial values were missing
2021-10-16 03:59:19 +11:00
Kurt Hutten
50cd44cd76 Linting 2021-10-16 03:37:59 +11:00
Kurt Hutten
55917395b4 Refactor recent projects into it's own cell (#558) 2021-10-16 03:35:44 +11:00
Frank Noirot
dc92920481 Merge pull request #557 from Irev-Dev/franknoirot/add-recent-projects
Added recent projects list to logged-in nav
2021-10-15 09:24:28 -04:00
Kurt Hutten
434eb0ef86 Release CQ customizer (#559)
* Switched to Miniconda image

* Update cad endpoint url

and some minor tweaks

Co-authored-by: Jeremy Wright <wrightjmf@gmail.com>

Co-authored-by: Jeremy Wright <wrightjmf@gmail.com>
2021-10-15 18:06:31 +11:00
Jeremy Wright
96ee9c4aa4 Add CadQuery customizer (#547)
* Rough changes to make the CadQuery integration work with the customizer

* Tweak runCQ

* Switched to Anaconda

* Cleaned up code

* Update CadHub after anaconda

Related to #547

* Add final tweaks to CQ customizer

* Separated out customizer.json from params.json

* Changes after discussing CadHub integration

* linting runCQ

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2021-10-15 02:39:03 +11:00
Frank Johnson
77014f0d36 Added recent projects list to logged-in nav 2021-10-13 17:03:44 -04:00
Kurt Hutten
3df903ffc6 Linting 2021-10-13 20:22:49 +11:00
Kurt Hutten
68fa10437e Remove dead code 2021-10-13 20:15:37 +11:00
Kurt Hutten
342953b25f Update contributor count 2021-10-13 18:45:58 +11:00
Kurt Hutten
421ceee88d Format and tweak 554 2021-10-13 05:56:17 +11:00
Javier González Bodas
ae05a79e58 Add modal for alerting that is neccesary to fork the project. Remove unused import component (#554) 2021-10-13 05:55:23 +11:00
Davor Hrg
549217e953 Hide console option in view menu (#545)
* Update menuConfig.tsx

* Revert "Update menuConfig.tsx"

This reverts commit 7be28e2a76.

* second attempt

* Update mosaic tree to remove and add the console.

* Added Toggle UI component

* Remove console noise from Toggle component

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
Co-authored-by: Frank Johnson <frankjohnson1993@gmail.com>
2021-10-12 20:44:16 +11:00
Kurt Hutten
4804c3bfe9 Put social media save popover into editor tab (#541)
and make them live
2021-10-12 06:09:56 +11:00
Lucas Barros
6c093e65bf Add project fork to seed file (#552) 2021-10-12 05:54:08 +11:00
Kurt Hutten
4b9a8591ab Some changes to the side tray to help make writing an issue for #540 (#551) 2021-10-11 07:46:42 +11:00
Kurt Hutten
9f769d6a61 Improve preprender docs and format 2021-10-08 17:04:00 +11:00
Kurt Hutten
32d6ef27ad Attempt 2 at fixing prerendering error 2021-10-07 21:25:07 +11:00
Kurt Hutten
c4c195074b Update cad lambda docs 2021-10-07 20:22:51 +11:00
Kurt Hutten
3f6d919f22 Add jscad to metadata 2021-10-05 19:04:47 +11:00
Kurt Hutten
aabe682782 Fix netify's prerendering service (#546)
If there's an error in netlify's prerendering service,
we don't have access to the log so we have to spin it up locally to
check.

textures was causing a issue on the home page resulting in "Fatal Error"
as the social preview text, not good.

As a bonus I thing I fix FE sentry logging too.
2021-10-05 18:52:24 +11:00
Kurt Hutten
5efaec4df0 Update contributor count
maybe this should be dynamic, but not too hard to update as well
2021-10-02 10:03:51 +10:00
Kurt Hutten
6d0c832f6f Add glitch to homepage (#542) 2021-10-01 21:48:13 +10:00
Kurt Hutten
66dc04d98e Add sign up toast message 2021-10-01 21:22:02 +10:00
Kurt Hutten
3aa3254e48 Add more verification to sign up 2021-09-30 20:28:13 +10:00
Kurt Hutten
879f24b08b Delete project properly as it's not causing problems with forking logic (#539) 2021-09-29 19:04:35 +10:00
Kurt Hutten
b80ea7f813 Make choose your character section only link to draft if not signed in 2021-09-29 18:03:00 +10:00
Kurt Hutten
e9ad7180a7 Fixing linting problem from running yarn rw lint (#537)
✖ 118 problems (65 errors, 53 warnings) currently
2021-09-29 17:35:07 +10:00
Kurt Hutten
0ce7ce4e76 fix homepage pre-rendering broken in #531 (#538)
import form project cell which in tern had dependencies that are not
pre render friendly was causing issues
2021-09-29 17:34:53 +10:00
Hendrie Bosch
088cfa4f2d Typo in integrations.mdx (#536) 2021-09-29 07:05:27 +10:00
Kurt Hutten
ab92894a2d Add id to project query 2021-09-29 05:10:39 +10:00
Frank Noirot
911744a071 Merge pull request #533 from Irev-Dev/frank/add-project-forking
Add project forking
2021-09-28 08:51:36 -04:00
Frank Noirot
d4bfcb4eb8 Merge pull request #534 from Irev-Dev/kurt/add-project-forking
Refactor IdeHeader to take middle buttons as children
2021-09-28 08:49:26 -04:00
Kurt Hutten
965e5b0f54 Update TopNav in UserProfile to remove unneeded props 2021-09-28 20:35:56 +10:00
Kurt Hutten
77799a5870 Refactor IdeHeader to take middle buttons as children 2021-09-28 20:26:15 +10:00
Frank Johnson
7540c908e7 Added link in ProjectProfile and fork count to ProjectCard 2021-09-28 06:18:41 +10:00
Kurt Hutten
dd152709ff Add forking graphQL resolvers 2021-09-28 06:18:41 +10:00
Frank Johnson
2d7fb91f92 added navigation to new project on fork 2021-09-28 06:18:41 +10:00
Kurt Hutten
02463db741 Start project fork feature
Updated schema, project service and UI
Still some polish to go.

Co-authored-by: Frank Noirot <franknoirot@users.noreply.github.com>
2021-09-28 06:18:41 +10:00
Kurt Hutten
38b905e180 Change how customizer params are applied (#529)
* Only send customizer params when it's open

* Add customizer reset button and have two modes of customizer vs not

depending of if the customizer is open.

* Remove re-render on customizer open/close in project profile
2021-09-28 06:05:22 +10:00
Kurt Hutten
cc50c984e4 Move create project into plus button (#531)
The draft page used to automatically create a new project and route
the user to the new project if the user was signed in. Problem arose
if the user use the back button as they would end up creating more
project. resolved my moving this logic into the plus button itself

Resolves #511
2021-09-27 20:43:30 +10:00
Kurt Hutten
9aee4ae725 Add comment 2021-09-26 21:05:35 +10:00
Kurt Hutten
6e45ce96d7 Update hero model (#532) 2021-09-26 05:39:29 +10:00
Kurt Hutten
892b1d3809 Patch for customizer bug causing CQ projects to fail 2021-09-26 05:01:37 +10:00
Kurt Hutten
83d327ad20 Z-index tweak for hinge 2021-09-25 19:51:09 +10:00
Kurt Hutten
c77169cf21 format project 2021-09-25 17:59:34 +10:00
Kurt Hutten
0ba4ec4e21 fix plus button z-index (#530)
The preserve-3d fix for the FF parallax broke the plus button as the
popover was always beneath the main body.

normal z-index fixes didn't apply since it was 3d z-index problem.
2021-09-25 17:25:08 +10:00
Kurt Hutten
06dbc35cf8 Fix JSCAD download again
Small problem where downloading the mesh would make it disappear
from the viewer. Fixed by cloning the geometry before downloading
2021-09-25 06:20:05 +10:00
Kurt Hutten
8170da854d Fix JSCAD download (#528) 2021-09-24 17:59:09 +10:00
Davor Hrg
d255a78cd1 fix parameters retention (#512) 2021-09-24 17:58:58 +10:00
Kurt Hutten
c19658b7f8 Enforce name and username with length 2021-09-24 05:26:51 +10:00
Frank Noirot
335dac8677 Show empty KeyValue's if editing (#526)
* Show empty KeyValue's if editing

* Add verification to name and user name, enforce length

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2021-09-23 18:51:44 +10:00
Kurt Hutten
18732e27fc Add parallax to floaty homepage cards (#524)
* Add parallax to floaty homepage cards

* Add firefox fix and restore model mouse tracking

* Add overflow-x hidden for safety
2021-09-23 18:16:41 +10:00
Kurt Hutten
64624b9c3e Add code backdrops to homepage (#522) 2021-09-23 18:14:06 +10:00
Kurt Hutten
a89f2e7992 Update README.md 2021-09-23 17:22:18 +10:00
Kurt Hutten
6d7f6fb4bf Create CONTRIBUTING.md 2021-09-23 17:20:32 +10:00
Kurt Hutten
b621d78eb4 Add integration info to docs 2021-09-23 17:13:43 +10:00
Kurt Hutten
1dcae6057c Update README.md 2021-09-22 07:58:07 +10:00
Davor Hrg
648f174bd8 Update README.md (#523) 2021-09-22 06:15:16 +10:00
Kurt Hutten
f20fe9a075 Add new design to readme 2021-09-21 20:37:54 +10:00
Kurt Hutten
023b4862eb rename scad again 2021-09-20 19:28:18 +10:00
Kurt Hutten
a2d278fa4d Rename scad 2021-09-20 19:27:47 +10:00
Kurt Hutten
f6df9d1988 upgrade rw + lint (#521)
* Various linting fixes

* Fix component name

* Upgrade to redwood 0.36.4
2021-09-20 19:08:03 +10:00
Kurt Hutten
39ce35b219 Merge pull request #519 from Irev-Dev/editor-tabs
Initial editor tabs implementation with CAD package guides
2021-09-20 17:55:30 +10:00
Kurt Hutten
33c08119ec format 2021-09-20 17:53:00 +10:00
Kurt Hutten
1475fa24d1 tweak uerGuide metadata 2021-09-20 17:52:19 +10:00
Frank Johnson
348d2e0a01 Made CadPackage component support button or div 2021-09-19 17:02:21 -04:00
Frank Johnson
65fc526220 fixed my broken merge with kurt's branch commit, updated OpenSCAD contributors 2021-09-19 14:10:23 -04:00
Frank Johnson
4c4f5643f4 Merge branch 'editor-tabs' of https://github.com/Irev-Dev/cadhub into editor-tabs 2021-09-19 11:07:32 -04:00
Frank Johnson
634304dfce Added cadPackage to ProjectsOfUser, other cleanup/linting 2021-09-19 11:06:45 -04:00
Frank Noirot
70980afab0 Update app/web/src/helpers/markdown.ts
Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2021-09-19 09:48:17 -04:00
Kurt Hutten
bb4659a2dd Make raw-loader more specific and use .js extension for jscad
suggestion for #519
2021-09-19 20:13:28 +10:00
Frank Johnson
59df7fdc25 Added globals.d.ts 2021-09-19 01:07:46 -04:00
Frank Johnson
2d4977ba8f Fixed TS errors with file 2021-09-19 01:05:51 -04:00
Frank Johnson
b27bcd2d35 Completed initial CAD package guides, tweaked initial code import 2021-09-18 23:16:43 -04:00
Frank Johnson
2f006d3e3b Added test tabs, got closing and switching working 2021-09-18 19:54:54 -04:00
Kurt Hutten
d71eec6a5e Merge pull request #516 from Irev-Dev/kurt/user-profile-path-515
user profile patch
2021-09-18 17:54:14 +10:00
Kurt Hutten
3a977ded02 user profile patch
Resolved #515
2021-09-18 17:53:24 +10:00
Kurt Hutten
b14587cdf1 Merge pull request #514 from Irev-Dev/side-tray-styles-patch
side-tray-styles-patch
2021-09-18 17:33:41 +10:00
Kurt Hutten
5f8862b4d2 side-tray-styles-patch 2021-09-18 17:27:36 +10:00
Kurt Hutten
f3201cfd97 format 2021-09-18 16:47:17 +10:00
Kurt Hutten
d94645d381 Merge pull request #507 from Irev-Dev/sidebar-tray
Sidebar tray
2021-09-18 16:22:13 +10:00
Kurt Hutten
cd1cecd774 Remove double icon 2021-09-18 16:19:31 +10:00
Kurt Hutten
a87c1ae9f4 Merge branch 'main' into sidebar-tray 2021-09-18 15:41:52 +10:00
Kurt Hutten
c271600432 Merge pull request #510 from Irev-Dev/profile-page
Profile page redesign
2021-09-18 15:16:33 +10:00
Kurt Hutten
1fb14db6f3 Don't use https for social image 2021-09-15 19:47:15 +10:00
Frank Johnson
7b2be01430 Fix up KeyValue component, fix save issue of Bio, simplify UserProfile 2021-09-15 01:53:44 -04:00
Kurt Hutten
9d9e3c4957 Update homepage meta tag 2021-09-15 05:38:08 +10:00
Frank Johnson
fc6cded59e Merge issues trying to look at a stashed commit 2021-09-12 17:47:59 -04:00
Frank Johnson
2ec3a0b202 Sorted out using <details> element, got ancestor clicks closing out to their level 2021-09-12 17:13:30 -04:00
Frank Johnson
88326ed573 Style tweaks to ImageUploader no-image state 2021-09-12 13:40:47 -04:00
Frank Johnson
58fc8866f1 Minor tweak to Sign In/Up in IDE 2021-09-12 13:11:12 -04:00
Frank Johnson
74a5f9bf2c Linting fixes 2021-09-12 12:42:22 -04:00
Frank Johnson
690d45ff9a Merge branch 'main' of https://github.com/Irev-Dev/cadhub into profile-page 2021-09-12 12:41:27 -04:00
Frank Johnson
34757cf535 Finished fixing nav, tweaked KeyValue edit btn 2021-09-12 12:38:16 -04:00
Kurt Hutten
69c83d33b1 State controlled tray mvp 2021-09-12 19:54:31 +10:00
Frank Johnson
55d48057da Initial profile refactor of layout and config 2021-09-12 05:03:58 -04:00
Kurt Hutten
e7031e9c0d Merge branch 'main' into sidebar-tray 2021-09-12 17:25:52 +10:00
Kurt Hutten
e99f0c07ba Merge pull request #506 from Irev-Dev/kurt/three-perf-n-tweaks-rebase
Improve three scene performance and add JSCAD
2021-09-12 14:52:08 +10:00
Kurt Hutten
e526fa812e Improve three scene performance and add JSCAD
- smoothed follow mouse animation
- made mobile friendlier down to about 330px ish
- added default social image
- used smaller hero asset
2021-09-12 14:40:10 +10:00
Frank Johnson
3dbb963e4e Updated signed in user menu in IDE 2021-09-11 22:14:18 -04:00
Frank Johnson
126b60f5dd Style tweaks to signed-in user modal in top nav 2021-09-11 22:05:35 -04:00
Frank Johnson
6a69a1c1bf Updated styles on Pwd Recovery page 2021-09-11 21:19:34 -04:00
Kurt Hutten
750d10c01d Merge pull request #505 from Irev-Dev/popover-style-tweaks
Tweaked styles on  login modal, sign up button, and new project popover
2021-09-12 08:51:25 +10:00
Frank Johnson
a51991ef0d Tweaked styles on login modal, sign up button, and new project popover 2021-09-11 17:57:00 -04:00
Frank Johnson
011baad9d0 Broke out SettingsMenu as a standalone component 2021-09-11 13:49:26 -04:00
Kurt Hutten
ec9f9d241e Merge pull request #502 from Irev-Dev/kurt/homepage-redo-rebase
Redo homepage to @franknoirot 's new designs
2021-09-11 23:17:51 +10:00
Kurt Hutten
b8fa22eede Redo homepage to @franknoirot 's new designs
Not finished but enough for a mvp
designs; https://www.figma.com/file/VUh53RdncjZ7NuFYj0RGB9/CadHub?node-id=1652%3A4224
2021-09-11 23:15:28 +10:00
Frank Johnson
a8c05a3d27 Merge origin/main 2021-09-11 06:05:17 -04:00
Frank Johnson
39f9a02c0a Broke out into a config file 2021-09-11 05:57:00 -04:00
Frank Johnson
e47ad59003 Added Settings tray with initial sections as details elements 2021-09-11 05:43:29 -04:00
Frank Johnson
0d7e958505 got tsome dummy text in the Files and GitHub menus, improved styling 2021-09-11 05:02:15 -04:00
Frank Johnson
9812db5cd6 got working toggle tabs! I don't like HeadlessUI's Tabs, they don't appear to support programmatic opening 2021-09-11 04:34:13 -04:00
Kurt Hutten
12ab456446 format 2021-09-10 18:45:03 +10:00
Frank Johnson
206ec7fdab aupdated headless-ui, got started on naive implementation 2021-09-10 02:29:13 -04:00
Frank Noirot
da557a5c16 Merge pull request #496 from Irev-Dev/keyboard-shortcuts
Initial keyboard shortcuts configuration implementation
2021-09-09 18:12:59 -04:00
Frank Johnson
fba971b419 remove dependencies from /app/package.json 2021-09-09 07:24:53 -04:00
Kurt Hutten
2e2e7be633 Fix console error messages 2021-09-09 18:36:11 +10:00
Frank Noirot
8a54a88b0a Merge branch 'main' into keyboard-shortcuts 2021-09-08 17:52:58 -04:00
Frank Noirot
5d128c6cbd Merge pull request #497 from Irev-Dev/franknoirot/style-tweaks
minor style tweaks to editor
2021-09-08 17:23:29 -04:00
Frank Johnson
b09733175e removed unused lines in AllShortcutsModal.tsx 2021-09-08 11:46:55 -04:00
Frank Johnson
d3d4b5a632 Added AllShortcutsModal into View menu, fixed visual bug with border-radius 2021-09-08 11:35:17 -04:00
Frank Johnson
5b85eec64c removed big-gear 2021-09-08 10:44:44 -04:00
Frank Johnson
0cf599bbe2 Fixed NPM/Yarn mixup and ran linter, updated AllShortcutsModal shortcut 2021-09-08 10:36:21 -04:00
Kurt Hutten
3f1947a4d9 Merge pull request #495 from Irev-Dev/kurt/494-move-worker-into-webpack-build
Move worker into webpack build
2021-09-08 18:14:25 +10:00
Kurt Hutten
3e26e3d420 Fix pre-render fail 2021-09-08 17:44:31 +10:00
Frank Johnson
51bc32aad0 minor style tweaks to editor 2021-09-08 02:52:45 -04:00
Frank Johnson
70cbe9d11e found a solution for the menu items not rendering within HeadlessUI docs 2021-09-07 21:51:47 -04:00
Frank Johnson
c95bfc400b tweaked DropdownItem styling and removed dev process fluff 2021-09-07 21:25:54 -04:00
Frank Johnson
678754d251 Adding @irev-dev's solution to register all shortcuts 2021-09-07 21:19:29 -04:00
Kurt Hutten
58b618cf5f format jscad worker 2021-09-08 06:18:11 +10:00
Kurt Hutten
22da074965 Move worker into webpack build
The jscad worker code was hosted as a static asset, making it odd
javascript where we have to be conscious of what javascript features we
can use and if it will work on older browsers, plus it can't be
typescript like the rest of the codebase.

Since redwood 0.36 we using webpack 5 should make loading workers easy
https://webpack.js.org/guides/web-workers/
But I had trouble with this (see:
https://community.redwoodjs.com/t/has-anyone-tried-workers-with-webpack-5-rw0-36-x/2394)
and instead used the webpack 4 loader without any issues

This issue relates to #411 , and is a checklist item on #444
Resolves #494
2021-09-08 06:16:52 +10:00
Kurt Hutten
9ae1cd4aff Merge pull request #493 from Irev-Dev/kurt/492-project-card
Make new project card
2021-09-08 06:10:16 +10:00
Frank Johnson
9887eb4804 Another attempt using a component property within the config 2021-09-07 13:07:35 -04:00
Kurt Hutten
7f4eb85106 Make new project card
designs
https://www.figma.com/file/VUh53RdncjZ7NuFYj0RGB9/CadHub?node-id=1150%3A1619
Resolves #492
2021-09-07 20:05:51 +10:00
Frank Johnson
896baf08d1 added an All Shortcuts dialog 2021-09-06 14:20:46 -04:00
Frank Johnson
e1d429877c initial attempt at shortcut system 2021-09-06 13:01:38 -04:00
Kurt Hutten
b9f3955767 Merge pull request #491 from Irev-Dev/kurt/462
Make social card more robust
2021-09-06 20:49:12 +10:00
Kurt Hutten
f9a43e53e2 Make social card more robust
Resolves #462
2021-09-06 20:48:39 +10:00
Kurt Hutten
442da1ffc6 Merge pull request #490 from Irev-Dev/franknoirot/details-styles
style tweaks to description area within the Project details view.
2021-09-06 20:45:53 +10:00
Kurt Hutten
57970465b1 Update docs 2021-09-06 20:11:44 +10:00
Frank Johnson
d203fe7e57 style tweaks to description area within the Project details view. 2021-09-05 21:23:09 -04:00
Kurt Hutten
abdebfccad Fix typo 2021-09-06 08:06:35 +10:00
Kurt Hutten
eb238b6902 Add openscad fonts documentation. 2021-09-06 07:33:03 +10:00
Kurt Hutten
edfde1aa9f format project 2021-09-05 21:08:57 +10:00
Kurt Hutten
912135877c Merge pull request #489 from Irev-Dev/kurt/temp-fix-project-profile-load
Misc tweaks
2021-09-05 21:07:34 +10:00
Kurt Hutten
597bf89135 Add banner explaining static openscad images 2021-09-05 20:57:57 +10:00
Kurt Hutten
e4c95cb396 Allow click through of loading animation 2021-09-05 18:05:51 +10:00
Kurt Hutten
867bc0ca29 Fix left hand side background disappearing when loading project profile 2021-09-05 16:28:49 +10:00
Kurt Hutten
35198b6cc3 Merge branch 'main' of github.com:Irev-Dev/cadhub 2021-09-04 23:53:36 +10:00
Kurt Hutten
5b2ebac15e Merge pull request #485 from Irev-Dev/kurt/484-rebase
Remove s3
2021-09-04 23:53:21 +10:00
Kurt Hutten
4a3144d360 Remove s3
but also upgrade the cad lamdbas to use built javascript files,
allowing us to use typescript, and patching redwood
2021-09-04 23:52:44 +10:00
Kurt Hutten
25bee7ab95 Upgrade redwood patch to 0.36.3 2021-09-04 06:27:25 +10:00
Kurt Hutten
1c13a38ccb Fix font imports after tailwind upgrade 2021-09-03 07:02:19 +10:00
Kurt Hutten
bbf2a2eb55 Merge pull request #483 from Irev-Dev/kurt/482
Upgrade redwood to v0.36
2021-08-31 20:14:09 +10:00
Kurt Hutten
01a28f4d53 upgrade redwood to v 0.36 2021-08-31 20:12:18 +10:00
Kurt Hutten
f5113da9c2 Upgrade redwood to v 0.35 2021-08-31 18:35:51 +10:00
Kurt Hutten
db9270b7ce Fix doc build 2021-08-31 17:19:14 +10:00
Kurt Hutten
a4a92c18cb Merge pull request #480 from sgenoud/fix/url-for-rss
Fix generic URL for blog rss generation
2021-08-31 17:17:28 +10:00
Steve Genoud
eb5d5616bb Fix generic URL for blog rss generation 2021-08-30 08:30:37 +02:00
Kurt Hutten
04261355b7 Up date prod cad endpoint 2021-08-28 07:50:59 +10:00
Kurt Hutten
0bb106028b Merge pull request #477 from Irev-Dev/kurt/320-openscad-parms-demo-rebase
Initial support for OpenSCAD's customizer
2021-08-27 20:47:09 +10:00
Kurt Hutten
431cd2e867 Make sure number respects initial value 2021-08-27 20:21:43 +10:00
Kurt Hutten
cdbf6ed6b4 Fix select styling 2021-08-27 18:21:51 +10:00
Kurt Hutten
87f132a684 Add customizer support for OpenSCAD
This also includes sending metadata and part of the concatenated gzip,
not in the s3 metadata as that has a 2kb limit.

Resolves #320
2021-08-27 06:52:04 +10:00
Davor Hrg
5d79efbf15 choice input 2021-08-27 06:50:46 +10:00
Davor Hrg
118c68c9da types and converter for choice input 2021-08-27 06:49:48 +10:00
Kurt Hutten
8ee4c112cf Merge pull request #475 from Irev-Dev/simplify-jscad-default-script
simplify default jscad script
2021-08-25 05:25:38 +10:00
Davor Hrg
2bc4d904c6 simplify default jscad script 2021-08-24 17:03:41 +02:00
Kurt Hutten
a690265f70 Merge pull request #473 from Irev-Dev/customizer-size
1/3 for customizer is plenty
2021-08-24 18:37:22 +10:00
Kurt Hutten
2b4bc7aa43 Merge pull request #471 from Irev-Dev/jscad-new-default-script
better default script
2021-08-24 18:36:58 +10:00
Davor Hrg
9041301642 Update Customizer.tsx 2021-08-24 10:06:55 +02:00
Kurt Hutten
9f088ba463 Merge pull request #474 from Irev-Dev/franknoirot/style-tweaks
Share popover color tweaks
2021-08-24 17:17:22 +10:00
Frank Johnson
e7b9059958 realized I had missed the bottom border-radius on Encoded Script button! 2021-08-23 22:32:38 -04:00
Frank Johnson
e407a3c002 reverted deletion of .env.example 2021-08-23 22:23:25 -04:00
Frank Johnson
6be2ced06f style tweaks to make Share popover match Figma a bit closer. 2021-08-23 22:16:47 -04:00
Davor Hrg
95bdb570f2 1/3 for customizer is plenty 2021-08-23 14:02:27 +02:00
Davor Hrg
9aa686b4a4 better default script 2021-08-23 11:54:45 +02:00
Kurt Hutten
b4cdd3e1ef Tweak welcome message 2021-08-22 13:53:54 +10:00
Kurt Hutten
96fa776bd9 Merge pull request #470 from Irev-Dev/kurt/469
Highlight OpenSCAD echo
2021-08-22 13:40:49 +10:00
Kurt Hutten
aa43a848a1 Format project 2021-08-22 12:26:06 +10:00
Kurt Hutten
335a1abf41 Highlight OpenSCAD echo
As suggested by @OutwardBuckle in #464
2021-08-22 10:45:02 +10:00
Kurt Hutten
b0647171d8 r3f-ify jsCadController 2021-08-21 11:04:01 +10:00
Kurt Hutten
ac233a5920 Merge pull request #461 from Irev-Dev/kurt/cadhub-customizer-test-code-437
CadHub Customizer
2021-08-21 11:02:56 +10:00
Kurt Hutten
0cc335ea9b Add demo for cadhub customizer
relates to #437
2021-08-21 11:02:18 +10:00
Kurt Hutten
1336ffc437 Fix editing project without social image. 2021-08-21 08:44:51 +10:00
Kurt Hutten
9186e457d9 Fix JSCAD, useEdgeSplit issue 2021-08-18 05:18:46 +10:00
Kurt Hutten
8dbb0468f8 Tweak SEO copy 2021-08-17 19:47:19 +10:00
Kurt Hutten
246a677517 Merge pull request #460 from Irev-Dev/kurt/459
EdgeSplit not flushing out old geometry
2021-08-17 05:14:57 +10:00
Kurt Hutten
644ef8d189 EdgeSplit not flushing out old geometry
Mostly effecting CQ, changing the script and updating leaves a ghost of
the old geometry in the viewer

Resolves #459
2021-08-17 05:13:30 +10:00
Kurt Hutten
daa0d788af Merge pull request #458 from Irev-Dev/kurt/97-clean-up
social card clean up
2021-08-17 05:10:26 +10:00
Kurt Hutten
93a1c7a242 Invalidate social card on title or description change 2021-08-17 05:05:13 +10:00
Kurt Hutten
31583104bd Stop SEO from overrideding preiveous meta tags with falsy content 2021-08-17 04:32:36 +10:00
Kurt Hutten
bb8a3f4dfc Update popup layout 2021-08-17 03:55:38 +10:00
Kurt Hutten
150ab45748 minimize social cards sdl 2021-08-17 03:35:15 +10:00
Kurt Hutten
b902713847 remove ".public_id" since we're not uploading directly to cloudinary 2021-08-16 20:33:31 +10:00
Kurt Hutten
fd2fc92b72 Merge pull request #457 from Irev-Dev/kurt/454
Make sure bio updates after saving the profile.
2021-08-16 20:32:40 +10:00
Kurt Hutten
0d6c8aa261 Make sure bio updates after saving the profile.
resolves #454
2021-08-16 20:11:30 +10:00
Kurt Hutten
580dbb88e6 Some clean up 2021-08-16 20:06:17 +10:00
Kurt Hutten
9fa22a0469 got something working thats only a little hacky 2021-08-16 18:45:33 +10:00
Kurt Hutten
180cbb9503 format project 2021-08-14 21:03:05 +10:00
Kurt Hutten
567da606f3 Bump SocialCard font sizes 2021-08-14 18:16:28 +10:00
Kurt Hutten
182ec78f79 Fix prerender 2021-08-14 16:04:49 +10:00
Kurt Hutten
4cfacb2581 Merge pull request #452 from Irev-Dev/fis-jscad-params-text
parse as number even for text if numeric string
2021-08-14 15:51:53 +10:00
Kurt Hutten
badcb96c6b Merge pull request #453 from Irev-Dev/kurt/social-card-new-approach-97
social card new approach 97
2021-08-14 15:50:24 +10:00
Kurt Hutten
224eb1d3ba Give some TLC the meta tags 2021-08-14 15:49:35 +10:00
Kurt Hutten
709c653afb add card to ide too 2021-08-14 15:31:04 +10:00
Kurt Hutten
32155ba98c Get image upload to cloudinary with the same public id
This means we can put a consistent url in the head for the card image
2021-08-14 15:19:48 +10:00
Davor Hrg
075779f107 parse as number even for text if numeric string 2021-08-13 22:43:45 +02:00
Kurt Hutten
50e9ac61f8 formatting and helpers 2021-08-13 06:38:15 +10:00
Kurt Hutten
7bd3cb44f8 Fix typo 2021-08-12 07:07:56 +10:00
Kurt Hutten
876dc94cc9 Merge branch 'main' of github.com:Irev-Dev/cadhub 2021-08-12 06:54:20 +10:00
Kurt Hutten
74677c89a2 Add more scene tweaks 2021-08-12 06:49:30 +10:00
Kurt Hutten
484c50c921 Adjust scene lighting, attach light to camera for consistent lighting 2021-08-12 06:15:23 +10:00
Kurt Hutten
0ae5065aaf Add welcome message 2021-08-12 06:14:40 +10:00
Kurt Hutten
5677855a43 Merge pull request #449 from Irev-Dev/kurt/safari-flex-gap-435
Fix safari flexbox/gap issue with grid
2021-08-12 05:38:12 +10:00
Kurt Hutten
0376d48bb9 Merge pull request #451 from Irev-Dev/franknoirot/435
Fix safari z-index issue with MaterialUI model style tweak.
2021-08-12 05:14:10 +10:00
Kurt Hutten
63ce7e9fa0 Merge pull request #450 from Irev-Dev/kurt/434-rename-projects-safari
Fix project renaming in safari
2021-08-12 05:12:50 +10:00
Kurt Hutten
d89e080b36 Fix project renaming in safari
Resolves #434
2021-08-11 20:43:19 +10:00
Kurt Hutten
000bf4c027 fix hight issue on details page 2021-08-11 20:17:36 +10:00
Kurt Hutten
1d1f62e38e Fix safari flexbox/gap issue with grid
related to #435
2021-08-11 19:27:08 +10:00
Kurt Hutten
b255af5f0f Merge pull request #448 from Irev-Dev/kurt/383-add-gizmo-and-edges
Add gizmo and edges
2021-08-11 07:05:01 +10:00
Kurt Hutten
799a32544b Use forked useEdegeSplit to allow updating geomerty 2021-08-11 07:01:09 +10:00
Kurt Hutten
5afa5d953c Add edges to models 2021-08-10 20:36:30 +10:00
Kurt Hutten
d2ee8be7e1 some progress on getting the gizmo work but with jank. 2021-08-10 17:37:28 +10:00
Frank Noirot
68346e075a fixed safari z-index issue with MaterialUI model style tweak. 2021-08-10 00:24:02 -04:00
Kurt Hutten
b5470c873b Merge pull request #446 from Irev-Dev/kurt/make-z-axis-up-444
make z axis up and other viewer improvements
2021-08-10 07:31:07 +10:00
Kurt Hutten
81cdeea761 Fix split edge issue 2021-08-10 06:40:27 +10:00
Kurt Hutten
3d02143422 Bunch of teaks to IdeViewer
- add grid
- slit normals
- resync openscad camera with three camera
- fix stl download for CQ and openscad
- clean up failed preview image attempt
2021-08-09 18:12:14 +10:00
Kurt Hutten
283bdcc56d Merge remote-tracking branch 'origin/main' into kurt/make-z-axis-up-444 2021-08-09 09:20:44 +10:00
Kurt Hutten
01977170f1 Merge pull request #445 from Irev-Dev/fix-default-jscad-script
update default script to be smaller
2021-08-08 20:14:01 +10:00
Kurt Hutten
b88ace2117 Change Z axis to up for Three.json
Openscad camera still needs to be fixed
2021-08-08 19:56:53 +10:00
Kurt Hutten
078c79d8ee format project 2021-08-08 19:55:05 +10:00
Davor Hrg
696441c39b Update useIdeState.ts 2021-08-08 11:52:35 +02:00
Kurt Hutten
802ea61639 Merge pull request #440 from Irev-Dev/kurt/customizer-start-320
jscad customizer with non-react component
2021-08-08 07:09:27 +10:00
Kurt Hutten
2ea15df9d1 defer ping animation 2021-08-08 07:06:20 +10:00
Davor Hrg
ff492fc1c7 Update jscadParams.ts 2021-08-07 16:03:59 +02:00
Kurt Hutten
dcd9d42d32 deeply compare old and new params 2021-08-07 21:26:11 +10:00
Davor Hrg
454995304a don't allow group collapse/exp to triger render, only store values 2021-08-07 11:32:03 +02:00
Kurt Hutten
8dd8e2e749 Tweak customizer styles and hide on non-jscad screens 2021-08-07 19:24:49 +10:00
Kurt Hutten
02160e1e8e store latest params in the store 2021-08-07 17:09:56 +10:00
Kurt Hutten
2d7df96ad9 Add a few type improvements to jscadParams.ts 2021-08-07 15:49:53 +10:00
Kurt Hutten
f83d1b395f Fix param panel collapse issue 2021-08-07 13:53:17 +10:00
Kurt Hutten
53da1e49a0 Remove console logs 2021-08-06 20:26:21 +10:00
Kurt Hutten
7cc989014a format project 2021-08-06 20:17:55 +10:00
Davor Hrg
725e877f1b Update useIdeState.ts 2021-08-06 11:26:44 +02:00
Davor Hrg
e856f8208e Update useIdeState.ts 2021-08-06 11:26:00 +02:00
Davor Hrg
e851593c12 cleanup, few fixes 2021-08-06 11:22:09 +02:00
Davor Hrg
80b12a6e3a Merge branch 'kurt/customizer-start-320' of https://github.com/Irev-Dev/cadhub into kurt/customizer-start-320 2021-08-04 23:59:42 +02:00
Davor Hrg
00c0ae801a ... 2021-08-04 23:54:45 +02:00
Davor Hrg
9dca2eb1c1 Merge pull request #438 from Irev-Dev/kurt/checkbox-320
Add live update checkbox to customizer
2021-08-04 23:54:32 +02:00
Kurt Hutten
c9e3d3a708 Add live update checkbox to customizer 2021-08-05 07:44:21 +10:00
Davor Hrg
2b2080c232 more param types to check and style 2021-08-04 00:11:55 +02:00
Davor Hrg
fd7954815a Merge branch 'main' into kurt/customizer-start-320 2021-08-03 21:34:56 +02:00
Kurt Hutten
879e63157d Merge pull request #433 from revolter/patch-2
Fix incorrect docs and blog edit links
2021-08-04 05:26:15 +10:00
Kurt Hutten
d8c80928b0 Merge pull request #432 from revolter/patch-1
Fix typo in docs page
2021-08-04 05:24:55 +10:00
Iulian Onofrei
b6cb22ed2d Fix incorrect docs and blog edit links 2021-08-03 16:58:34 +03:00
Iulian Onofrei
a06e71291f Fix typo in docs page 2021-08-03 16:53:27 +03:00
Davor Hrg
77ee959c43 working somewhat ok 2021-08-02 23:24:26 +02:00
Kurt Hutten
cd9a4794ac Merge pull request #431 from Irev-Dev/fix-stl-export
fix stl export for jscad
2021-08-03 05:15:37 +10:00
Kurt Hutten
336501aaff add customizerParams to state 2021-08-02 23:19:57 +10:00
Kurt Hutten
8883df3445 Add the beginnings of the customizer UI
Relates to #320
2021-08-02 22:48:59 +10:00
Davor Hrg
7178313c2e Update helpers.ts 2021-08-02 13:39:18 +02:00
Kurt Hutten
5e14ad0829 fix constructor bug 2021-08-01 21:13:19 +10:00
Kurt Hutten
39cbdc749b get deployments working again 2021-08-01 20:39:26 +10:00
Kurt Hutten
cedad90513 update import style 2021-08-01 19:53:05 +10:00
Kurt Hutten
7f29fd5125 Merge pull request #428 from Irev-Dev/kurt/411-demo-branch
initial jscad integration
2021-08-01 19:35:56 +10:00
Davor Hrg
67d5d76d67 Delete CSGToBuffers.js 2021-08-01 10:47:58 +02:00
Kurt Hutten
43477d33cc useEntities directly 2021-08-01 17:15:07 +10:00
Kurt Hutten
d8998a73b3 format project 2021-08-01 17:09:47 +10:00
Kurt Hutten
625db5e26b Merge branch 'main' into kurt/411-demo-branch 2021-08-01 09:46:41 +10:00
Kurt Hutten
ad0e9c1d4d format project 2021-08-01 09:44:10 +10:00
Kurt Hutten
caf944716b Move og-image-gen out of functions folder 2021-08-01 09:42:15 +10:00
Davor Hrg
8162a182d7 Update jsCadController.ts 2021-07-31 21:03:44 +02:00
Kurt Hutten
22f3fb6e3e Remove og image types 2021-08-01 04:45:43 +10:00
Kurt Hutten
6d68b939be try moving things into dev dependencies 2021-07-31 17:26:10 +10:00
Kurt Hutten
5c673a6a01 attempt to got functions to deploy 2021-07-31 17:04:20 +10:00
Kurt Hutten
2497627c1d Merge pull request #423 from Irev-Dev/kurt/97-preview-image-rebase
preview image
2021-07-31 16:27:14 +10:00
Kurt Hutten
cbaa79b697 add netlify builder to capture social images
plus also added an invalidator that should invalidate the image each
month
2021-07-31 16:06:22 +10:00
Kurt Hutten
e4bf8f5e81 Add social preview page 2021-07-31 15:34:03 +10:00
Davor Hrg
73cbb9dbb3 stl export for jscad 2021-07-28 15:23:52 +02:00
Davor Hrg
2a3fb0fd84 fix for react-fiber not properly cleaning the scene 2021-07-28 14:27:50 +02:00
Davor Hrg
8d0f2fca51 light tweaks 2021-07-27 12:37:29 +02:00
Davor Hrg
6e88e7030d Update jsCadController.ts 2021-07-27 10:47:29 +02:00
Davor Hrg
0b769a6524 something works 2021-07-27 10:36:02 +02:00
Kurt Hutten
3b1bb23563 getting some geometry into the 3d view 2021-07-27 07:48:56 +10:00
Davor Hrg
1479832b51 stupid typo 2021-07-26 23:01:51 +02:00
Davor Hrg
438a9135e4 unhealthy on err 2021-07-26 22:57:22 +02:00
Davor Hrg
1bfba591ea initial scrappy integration 2021-07-26 22:43:51 +02:00
Kurt Hutten
de71b8f67c Demo branch adding JSCAD type, without implementing the render
Related to #411
2021-07-25 20:59:53 +10:00
Kurt Hutten
3f310a9aaf Merge pull request #421 from Irev-Dev/kurt/420
Fix subscriber name When signing up to newsletter
2021-07-25 20:18:34 +10:00
Kurt Hutten
68da3ce2c1 Fix subscriber name When signing up to newsletter
Resolves #420
2021-07-25 20:16:55 +10:00
Kurt Hutten
3e952e3b36 Update README.md 2021-07-19 05:48:43 +10:00
Kurt Hutten
3819e4313d Add aspect-ratio to docs tailwind 2021-07-18 20:16:29 +10:00
Kurt Hutten
76542a8032 Merge pull request #415 from Irev-Dev/kurt/409-update-title-and-more
Kurt/409 update title and more
2021-07-18 20:10:40 +10:00
Kurt Hutten
90e4d84865 Enforce 25 character titles on front end 2021-07-18 20:08:05 +10:00
Kurt Hutten
754436c79d Make sure old links still work in the dev-IdeWrapper
by making it case-insensitive, forcing it to lower case.
2021-07-18 19:50:39 +10:00
Kurt Hutten
b0bdb2c6f6 Fix canvas jumping on screen cap 2021-07-18 19:28:03 +10:00
Kurt Hutten
5a045fd89d solve weird issues where seperate query would fire each others oncempleted 2021-07-18 18:55:55 +10:00
Kurt Hutten
0100836e31 Fix renaming project title 2021-07-18 18:32:58 +10:00
Kurt Hutten
53eaa2e0f4 Remove console noise 2021-07-18 18:16:28 +10:00
Kurt Hutten
9e968479fc Formatting after #411 2021-07-18 08:37:48 +10:00
Kurt Hutten
23b75f56b5 Merge pull request #412 from Irev-Dev/kurt/cad-kernal-demo-411
Add demo CAD kernel Controller, and typing to suit
2021-07-18 08:26:12 +10:00
Kurt Hutten
21608b740a Add demo CAD kernel Controller, and typing to suit
We might be adding JSCAD soon and adding some guidance on the happy
path with good typing for the CadKernels is a good idea

related to #411
2021-07-18 08:24:32 +10:00
Kurt Hutten
62ec8159b8 Merge pull request #407 from Irev-Dev/kurt/400-rebase
massive refactor  toDrop cascadeStudio and add CadQuery + OpenSCAD
2021-07-17 17:52:33 +10:00
Kurt Hutten
8e558d2342 massive refactor toDrop cascadeStudio and add CadQuery + OpenSCAD
resolves #400
2021-07-17 17:47:29 +10:00
Kurt Hutten
477a557eb8 Merge pull request #406 from Irev-Dev/release
Release
2021-07-17 17:34:00 +10:00
Kurt Hutten
78a901f2f6 Merge pull request #405 from Irev-Dev/main
Release 13th July 2021
2021-07-13 05:24:13 +10:00
Kurt Hutten
6a1730bd2b Merge pull request #401 from Irev-Dev/franknoirot/blog-post
UX case studies intro post.
2021-07-13 05:15:30 +10:00
Kurt Hutten
7507b90561 Update README.md 2021-07-12 19:19:52 +10:00
Kurt Hutten
75cd72d028 Update README.md 2021-07-12 19:19:16 +10:00
Kurt Hutten
066c451f0c Update homepages 2021-07-12 19:17:23 +10:00
Frank Noirot
9ab234f6d1 Lengthened end paragraph with "stay tuned" message. 2021-07-11 17:40:23 -04:00
Kurt Hutten
92c7871659 Merge pull request #403 from Irev-Dev/main
Release 7th July 2021
2021-07-07 20:00:28 +10:00
Kurt Hutten
ff4a453ea9 Merge pull request #402 from Irev-Dev/kurt/fix-small-screens
Fix small screens
2021-07-07 19:59:16 +10:00
Kurt Hutten
6ed315a7ae Fix small screens 2021-07-07 19:54:45 +10:00
Frank Noirot
005dbdebec Wording tweak from 'we believe' to question 2021-07-06 10:19:33 -04:00
Frank Noirot
50a00e1028 rough draft of UX case studies intro post. 2021-07-06 02:14:58 -04:00
Kurt Hutten
17dfdac0c1 Merge pull request #399 from Irev-Dev/main
Release 4th July 2021
2021-07-04 20:50:36 +10:00
Kurt Hutten
e30f870e92 Merge pull request #396 from Irev-Dev/kurt/gzip-391
Gzip files before upload to s3 + high quality CadQuery downloads + TS conversions
2021-07-04 20:49:21 +10:00
Kurt Hutten
f176bbe090 Use high quality render for CadQuery download 2021-07-04 20:48:11 +10:00
Kurt Hutten
70e55a039d Implement gzip compression for cad artifacts
The stls from CadQuery and OpenSCAD are not compressed and so we're
throwing away bandwidth and taking a performance hit by not gziping.

Gzip for s3 basically needs to be gziped before upload and than have

'content-type'      :  'text/stl'
'content-encoding'  :  'gzip'
set.

https://stackoverflow.com/questions/8080824/how-to-serve-gzipped-assets-from-amazon-s3

The obvious part that needs to change is putObject in
app/api/src/docker/common/utils.js but there might be a few more
nuances.

resolves #391
2021-07-03 08:25:20 +10:00
Kurt Hutten
207eb6790a Update curated-code-cad to add live editor links for OpenSCAD & CadQuery 2021-07-02 21:07:16 +10:00
Kurt Hutten
2e3f9b9e25 Add deprecation warning for CascadeStudio
https://github.com/Irev-Dev/cadhub/discussions/261
2021-07-02 20:38:05 +10:00
Kurt Hutten
b01c08b0d2 Remove email route after all of the sillyness 2021-07-02 20:18:17 +10:00
Kurt Hutten
5def8dfd42 Merge pull request #395 from Irev-Dev/main
Emergency fix for cad base url
2021-07-01 20:59:29 +10:00
Kurt Hutten
5a4588ac7f Emergency fix for cad base url 2021-07-01 20:58:30 +10:00
Kurt Hutten
744016972e Merge pull request #394 from Irev-Dev/main
Release 1st July 2021
2021-07-01 20:30:15 +10:00
Kurt Hutten
0531e2cb54 Remove insult on home-page and add github to footer 2021-07-01 20:24:02 +10:00
Kurt Hutten
9671505a49 Merge pull request #393 from Irev-Dev/kurt/email-388
Update email code to send an email per EditUserCell
2021-07-01 20:15:36 +10:00
Kurt Hutten
084c4afdc3 Update email code to send an email per EditUserCell
using multiple recipients is break of privacy since user will be able
to see each other's emails.
2021-07-01 20:14:46 +10:00
Kurt Hutten
43376b8214 Add build path back for openscad 2021-07-01 07:08:48 +10:00
Kurt Hutten
426c3549c6 Merge pull request #392 from Irev-Dev/kurt/latest-scad-321
Upgrade openscad to 2021.01
2021-07-01 07:02:17 +10:00
Kurt Hutten
fc7c74f3e1 Upgrade openscad to 2021.01
resolves #321
2021-07-01 06:59:53 +10:00
Kurt Hutten
37dc0278fa Merge pull request #390 from Irev-Dev/main
release 29th June 2021
2021-06-29 18:57:26 +10:00
Kurt Hutten
8f8dae70c2 Deploy optimized CadQuery stls 2021-06-29 18:43:43 +10:00
Kurt Hutten
8fec470e9f Improve aws/docker docs a little. 2021-06-29 17:34:03 +10:00
Kurt Hutten
7417499d4b Formatting 2021-06-29 17:17:21 +10:00
Kurt Hutten
726945c2ab Merge pull request #389 from Irev-Dev/kurt/388-email
Add emails
2021-06-29 17:07:31 +10:00
Kurt Hutten
98d1b0643d Add admin email notifications 2021-06-29 16:21:28 +10:00
Kurt Hutten
0da15443cb Add email integration to be able to broadcast emails to all users.
Resolves #388
2021-06-29 06:37:04 +10:00
Kurt Hutten
b1dcd46a33 Merge pull request #387 from Irev-Dev/main
Release 28 June 2021
2021-06-28 20:51:55 +10:00
Kurt Hutten
7ef8d8d1ff Merge branch 'main' of github.com:Irev-Dev/cadhub 2021-06-28 20:49:30 +10:00
Kurt Hutten
f9c3f5ed25 Add blog to footer 2021-06-28 20:48:29 +10:00
Kurt Hutten
fc912279b6 Merge pull request #386 from t-paul/patch-1
Update variable name for lambda base URL.
2021-06-27 14:22:42 +10:00
Torsten Paul
c0b961ee39 Update variable name for lambda base URL. 2021-06-27 02:19:55 +02:00
Kurt Hutten
1ed1f3a2b6 Add more details to the docker readme 2021-06-27 07:38:19 +10:00
Kurt Hutten
2baf11fc11 Add material 2021-06-27 06:35:58 +10:00
Kurt Hutten
f93f5e30d8 Merge pull request #384 from Irev-Dev/kurt/383-lighting
Improve three.js scene lighting
2021-06-27 06:32:52 +10:00
Kurt Hutten
c56cf6f4ce Improve three.js scene lighting
Related to #383
2021-06-27 06:23:28 +10:00
Kurt Hutten
e95d47cfdf Update default cq stl quality 2021-06-27 05:52:37 +10:00
Kurt Hutten
bae5ba9d20 Merge pull request #382 from jmwright/main
Updated for cq-cli beta 2 of v2.2, which adds the ability to adjust STL quality
2021-06-26 20:11:06 +10:00
Jeremy Wright
f84d4cacee Updated for cq-cli beta 2 of v2.2, which adds the ability to adjust STL quality 2021-06-25 20:14:10 -04:00
Kurt Hutten
e91a264768 lint project 2021-06-26 06:14:49 +10:00
Kurt Hutten
ce210f8805 Merge pull request #380 from Irev-Dev/main
Release 25th June 2021
2021-06-25 19:48:46 +10:00
Kurt Hutten
0845575680 Merge pull request #379 from Irev-Dev/kurt/378
Fix 502 lambda cors issue. deploy new cq-cli and images tweaks
2021-06-25 19:46:39 +10:00
Kurt Hutten
29999bc2ce Allow admins to edit parts
Needed for moderation
2021-06-25 19:43:34 +10:00
Kurt Hutten
87e43ab7ce Add Cors headers to lamda timeout so they can be detected on the FE 2021-06-25 19:26:33 +10:00
Kurt Hutten
0773915fbc Update cq-cli to latest (beta) 2021-06-25 19:26:12 +10:00
Kurt Hutten
41862257ac Merge pull request #377 from jmwright/main
Added libglew 2.1 to Dockerfile install line
2021-06-25 08:43:37 +10:00
Jeremy Wright
3cebb100c7 Added libglew 2.1 to Dockerfile install line 2021-06-24 17:35:07 -04:00
Kurt Hutten
7a98afa8df Merge pull request #374 from jmwright/main
Updated Dockerfile for newer version of CadQuery and cq-cli
2021-06-25 05:43:04 +10:00
Kurt Hutten
bbab35e05b Merge pull request #376 from Irev-Dev/kurt/375-missing-images
missing images
2021-06-25 05:36:49 +10:00
Kurt Hutten
473f8d667c Fix images being deleted when they shouldn't
Resolves #375
2021-06-25 05:34:17 +10:00
Jeremy Wright
8fa779bb32 Updated Dockerfile for newer version of CadQuery and cq-cli 2021-06-24 10:09:19 -04:00
Kurt Hutten
52bf8922c4 Lint project 2021-06-24 20:36:56 +10:00
Kurt Hutten
df5fc0a100 regenerate lock file 2021-06-24 20:25:51 +10:00
Kurt Hutten
b7ee282238 Add IDE screen shot 2021-06-24 19:44:59 +10:00
Kurt Hutten
2e6afa86f6 Update landing page links 2021-06-23 19:37:23 +10:00
Kurt Hutten
69a34301e7 Add blog posts 2021-06-23 07:16:21 +10:00
Kurt Hutten
8887092606 3d-diffs 2021-06-20 16:38:35 +10:00
Kurt Hutten
0e20a06731 Merge pull request #371 from Irev-Dev/release
sync main with release after debugging email verification
2021-06-19 15:36:05 +10:00
Kurt Hutten
ec6919d499 import graphql for reasons 2021-06-19 11:23:42 +10:00
Kurt Hutten
3e0f17ee3c Upgrade redwood 34.1 2021-06-19 11:09:13 +10:00
Kurt Hutten
13f769af5e Add api logs back in 2021-06-19 10:30:12 +10:00
Kurt Hutten
35a1d84951 Explicitly add graphql-tag as api dependency 2021-06-19 08:51:42 +10:00
Kurt Hutten
13b5d9c108 Remove all logs from api 2021-06-19 07:54:21 +10:00
Kurt Hutten
7f588ebedf Try removing logs 2021-06-19 05:56:34 +10:00
Kurt Hutten
142bf2c890 Start adding ide colors, includes adding custom theme to openscad
Resolves #365
2021-06-16 21:49:24 +10:00
Kurt Hutten
51589f6c0a Merge pull request #362 from Irev-Dev/kurt/update-ide-panel-toolbar-360
IDE redesign, initial implementation
2021-06-15 18:05:02 +10:00
Kurt Hutten
155923b2e7 Add reset view to view dropdown in EditorMenu
Related to #360
2021-06-14 09:21:47 +10:00
Kurt Hutten
b65c4530b2 Tweak panel toolbars
See this thread:
https://discord.com/channels/775984095250612234/775984095250612240/853751444703543326

Related to #360
2021-06-14 08:49:52 +10:00
Kurt Hutten
d3cb93b218 Add docs for how to use external links
related to #360
2021-06-14 06:29:26 +10:00
Kurt Hutten
5083d8e7f8 Add external resource flow
related to #360
2021-06-13 17:08:37 +10:00
Kurt Hutten
da81942adc Run linting
related to #360
2021-06-13 06:33:03 +10:00
Kurt Hutten
d781c94027 Convert to UseIdeContext
related to #360
2021-06-13 06:23:36 +10:00
Kurt Hutten
c142860433 Move encoded script logic together 2021-06-13 06:17:01 +10:00
Kurt Hutten
e591eb8ff8 Rename IdeToolbarNew to IdeWrapper
related to #360
2021-06-12 20:57:04 +10:00
Kurt Hutten
f032dd1e90 Clean up IdeToolbarNew 2021-06-12 20:39:33 +10:00
Kurt Hutten
e9583166f4 Move context provide up one layer to the DevIdePage
Related to #360
2021-06-12 20:35:13 +10:00
Kurt Hutten
cd94f11374 Disable buttons that can't be used yet
Also make construction message hidable.
related to #360
2021-06-12 20:10:30 +10:00
Kurt Hutten
6b4ff7aa61 Make new favicon into a homepage link
related to #360
2021-06-12 19:24:42 +10:00
Kurt Hutten
3c18a24cb6 Add proper menu
related to #360
2021-06-12 19:20:24 +10:00
Kurt Hutten
6ad731d158 finish ide lay out
added side bar and editor toolbar
related to #360
2021-06-11 19:47:25 +10:00
Kurt Hutten
81f7659bbc Convert Svg component to typescript
related to #360
2021-06-11 19:31:46 +10:00
Kurt Hutten
ab084e0d5a Add rough IdeHeader
relates to #360
2021-06-09 06:43:12 +10:00
Kurt Hutten
02460544bf Add new viewer and console toolbar (handle + settings)
Related to #360
2021-06-08 19:27:56 +10:00
Kurt Hutten
927ba29e04 Merge pull request #359 from Irev-Dev/kurt/typo
Fix typo
2021-06-08 06:05:13 +10:00
Kurt Hutten
be2eb8215a Fix typo 2021-06-08 06:04:05 +10:00
Kurt Hutten
11660a852f Merge pull request #358 from Irev-Dev/kurt/polyround-line-highlighting
Add light highlighting to round anything docs
2021-06-07 20:38:23 +10:00
Kurt Hutten
ec68b8827c Add light highlighting to round anything docs 2021-06-07 20:37:38 +10:00
Kurt Hutten
d93326f672 Merge pull request #357 from Irev-Dev/kurt/update-docs-version-356
Update docusaurus to 2.0.0-beta.0
2021-06-07 20:23:48 +10:00
Kurt Hutten
6fcbf58af5 Update docusaurus to 2.0.0-beta.0 2021-06-07 20:22:29 +10:00
Kurt Hutten
8f60a29fc7 Merge pull request #354 from Irev-Dev/kurt/round-anything-docs-351
Add openscad review as blog past
2021-06-07 20:07:23 +10:00
Kurt Hutten
eeb8071fec Add openscad review as blog past 2021-06-07 20:06:08 +10:00
Kurt Hutten
9e4527245d Merge pull request #352 from Irev-Dev/kurt/round-anything-docs-351
Initial migration of round-anything docs into CadHub
2021-06-07 17:36:13 +10:00
Kurt Hutten
18f78a463d Initial migration of round-anything docs into CadHub
Resolves 351.
2021-06-07 17:32:27 +10:00
Kurt Hutten
15732a9ed7 Merge pull request #337 from Irev-Dev/kurt/docs-update-336
Update read me to reflect up and coming OpenSCAD and CadQuery integrations
2021-06-06 20:06:07 +10:00
Kurt Hutten
8c8bd22664 Fix sentry org and project details
Still not sure though
Related to #343
2021-06-06 06:56:22 +10:00
Kurt Hutten
e67eba61aa Put netlify plugin info in the right toml file
related to #343
2021-06-06 06:36:43 +10:00
Kurt Hutten
7cf4f801f4 Merge pull request #347 from Irev-Dev/kurt/toaster-346
Replace redwood flash with toaster
2021-06-06 06:14:21 +10:00
Kurt Hutten
a0b588000a Replace redwood flash with toaster
https://community.redwoodjs.com/t/redwood-flash-is-being-replaced-with-react-hot-toast-how-to-update-your-project-v0-27-0/1921
Resolves #326.
2021-06-06 06:12:26 +10:00
Kurt Hutten
7caf857c98 Merge pull request #344 from Irev-Dev/kurt/sentry-343
Add initial sentry setup
2021-06-05 20:35:17 +10:00
Kurt Hutten
2e91c74baf Add initial sentry setup
Related to #343 but will probably need a few more changes
2021-06-05 20:32:56 +10:00
Kurt Hutten
c38f94558a Disable esbuild
Related to #341
2021-06-05 13:06:55 +10:00
Kurt Hutten
c86a9d39f2 Merge pull request #342 from Irev-Dev/kurt/341-upgade-redwood
Upgrade redwood
2021-06-05 12:53:58 +10:00
Kurt Hutten
563700d995 Upgrade redwood to 0.33
Resolves #341.
2021-06-05 12:52:39 +10:00
Kurt Hutten
e433648fe6 Upgrade redwood to 0.32.2 2021-06-05 12:32:13 +10:00
Kurt Hutten
4a351385e1 Merge pull request #339 from Irev-Dev/kurt/338-Round-anything-library
Add experimental support for OpenSCAD libraries
2021-06-05 12:22:11 +10:00
Kurt Hutten
b868ab292b Add experimental support for OpenSCAD libraries
I've been able to get a proof of concept of downloading a openscad
library when the docker image builds
https://twitter.com/IrevDev/status/1400785325509660678
Since its experimental atm I'll leave it with just the one for now.

I've also got a local dev working again for the cad lambdas.

Resolves #338
2021-06-05 12:20:53 +10:00
Kurt Hutten
77d0fcb336 Update read me to reflect openscad and cadquery changes
Resolves #336.
2021-06-04 18:24:14 +10:00
Kurt Hutten
a2327b6169 Merge pull request #335 from Irev-Dev/kurt/improve-caching-334
Improve browser caching with cache control header
2021-05-31 17:00:28 +10:00
Kurt Hutten
913045fe12 Improve browser caching with cache control header
Not only does the header need to be added, but the signed URL needs to
have it's expiry rounded so that the return url is the same for a given
window, say 10minutes

I followed this https://advancedweb.hu/cacheable-s3-signed-urls/
basically what this means is that because we're caching the assets
themselves, if as user asks for a part that already exists we'll return
a url for the existing part instead of regenerating it, however if it
was them that generated the part less than 10 minutes ago, they'll still
have to download the asset again. This way it will save us costs and
will be quicker for them.

Resolves #334
2021-05-31 16:56:21 +10:00
Kurt Hutten
c049a1d3db Merge pull request #333 from Irev-Dev/kurt/fix-init-code-bug
Make sure the code-init is robust when local storage is empty
2021-05-30 14:01:52 +10:00
Kurt Hutten
bd7aa4cc4e Make sure the code-init is robust when local storage is empty
Plus fix local storage for old code
2021-05-30 13:59:16 +10:00
Kurt Hutten
d7aaeda187 Merge pull request #331 from Irev-Dev/kurt/330-stl-download
Add stl download for OpenSCAD and CadQuery IDEs
2021-05-30 08:47:31 +10:00
Kurt Hutten
bd58e6c7cb Add stl download for OpenSCAD and CadQuery IDEs
Resolves #330.
2021-05-30 08:11:46 +10:00
Kurt Hutten
32fa22efcd Merge pull request #328 from Irev-Dev/kurt/327
Add the ability to link to text resource for IDE
2021-05-28 06:59:52 +10:00
Kurt Hutten
f629833229 Add the ability to link to text resource for IDE
Very basic feature, puts the url of the resource in the url for the ide
to fetch when it loads. I haven't added the ability to create these
links as it probably needs some consideration with how it fits into the
IDE.

Should work with any url that returns text but obviously has to CORS
enable since it's in the browser, of which gitlab raw content is not
which is kinda bizzare.

works with github raw content like so:
`#fetch_text_v1=https://raw.githubusercontent.com/aaevan/openscad_objects/main/fire_tablet_bottom_corner.scad`
however I would recommend url encoding it with `encodeURIComponent` in
case there are special characters in the path
`#fetch_text_v1=https%3A%2F%2Fraw.githubusercontent.com%2Faaevan%2Fopenscad_objects%2Fmain%2Ffire_tablet_bottom_corner.scad`

In the case of github, linking to raw is the safest, however it will try
and get to the raw content from the web-app url i.e. the following still
works:
`#fetch_text_v1=https://github.com/aaevan/openscad_objects/blob/main/fire_tablet_bottom_corner.scad`

Resolves #327
2021-05-28 06:45:39 +10:00
Kurt Hutten
d3f7b40a9b Merge pull request #325 from Irev-Dev/kurt/blog-why-i-started-cadhub
Add "Why I started cadhub" blog post
2021-05-22 06:45:00 +10:00
Kurt Hutten
8ed814ac9f Add "Why I started cadhub" blog post 2021-05-22 06:41:55 +10:00
Kurt Hutten
90f6808663 Merge pull request #322 from Irev-Dev/kurt/link-to-docs
Link to docs
2021-05-18 21:38:50 +10:00
Kurt Hutten
644e431eba Link to docs
Not much connection between the main website and the docs website atm
2021-05-18 21:36:33 +10:00
Kurt Hutten
31b1eb594e Merge pull request #317 from Irev-Dev/kurt/s3-integration-316-rebase
Add s3 integration
2021-05-18 07:19:48 +10:00
Kurt Hutten
315492a08a Add s3 integration
Doing so has a number of benefits
- Overcome the 10Mb limit of the API gateway the lambdas have to go
through
- By storing the key as the hash of the code we can return previous
generated assets, i.e. caching
- cost, transfering assets into the bucket within the AWS ecosystem
is faster than return, and there fore the lambdas execute for less time
- Sets us up for the future as when generating artifacts for repos when
there is a change to master etc we want to store these assets somewhere
and s3 is an obvious choice
- Solved a weird CORS issue where I couldn't get CORS working with
binaryMediaTypes enabled, don't need binary types when dumping in s3

Resolves #316
2021-05-18 07:13:08 +10:00
Kurt Hutten
027b45e6c3 Merge pull request #314 from Irev-Dev/kurt/add-live-demo-links-313
Add live demo links to OpenSCAD tutorial
2021-05-08 22:32:55 +10:00
Kurt Hutten
8a5728508c Add live demo links to OpenSCAD tutorial
Resolves #313
2021-05-08 22:31:43 +10:00
Kurt Hutten
a08426e078 Merge pull request #312 from Irev-Dev/kurt/improve-script-url-encoding-311
Improve script url encoding
2021-05-08 09:15:49 +10:00
Kurt Hutten
7749a14da3 All apostrophes in jsx 2021-05-08 09:09:07 +10:00
Kurt Hutten
053b1d642c Improve script URL ecoding
added some magic to get scripts to efficiently encoded into the URL.
We're using pako to compress the script, but this outputs to a 8bit
array. Stringifying this array adds a lot of overhead, because "125"
has three characters in it.
Instead we're using the character codes to turn these a bit numbers
into single characters base64 is used as well because not all of the
characters are allowed in a url (and b64 is better than
encodeURIComponent).
2021-05-08 09:07:54 +10:00
Kurt Hutten
53985dd250 Merge pull request #307 from Tobbe/patch-1
the-basics.mdx: Make text match example code
2021-05-08 05:20:33 +10:00
Kurt Hutten
cc8166183e Merge branch 'main' into patch-1 2021-05-08 05:20:00 +10:00
Kurt Hutten
9bca10b380 Merge pull request #308 from Tobbe/patch-2
the-basics.mdx: Fix punctuation formatting
2021-05-08 05:17:18 +10:00
Kurt Hutten
c50ffc5c8b Merge pull request #309 from Tobbe/tobbe-basics-translate
the-basics.mdx: Small updates to the Translate section
2021-05-08 05:16:44 +10:00
Tobbe Lundberg
277dc6c008 the-basics.mdx: Small updates to the Translate section 2021-05-07 15:08:59 +02:00
Tobbe Lundberg
81ee147f51 the-basics.mdx: Fix punctuation formatting 2021-05-07 15:03:03 +02:00
Tobbe Lundberg
f0d32d7754 the-basics.mdx: Fix punctuation formatting 2021-05-07 15:00:37 +02:00
Kurt Hutten
aba9b2e19e Merge pull request #305 from Irev-Dev/kurt/docs-polish-304
docs polish
2021-05-07 20:45:00 +10:00
Kurt Hutten
67324f0d0c Remove reference to primer 2021-05-07 20:38:26 +10:00
Kurt Hutten
7070ac21d4 Proof read docs 2021-05-07 20:38:26 +10:00
Kurt Hutten
2d828d7739 Add rounding and spacing at the bottom of images 2021-05-07 20:38:04 +10:00
393 changed files with 26496 additions and 13365 deletions

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "web/src/cascade"]
path = app/web/src/cascade
url = https://github.com/Irev-Dev/CascadeStudio.git

11
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"cSpell.words": [
"Cadhub",
"Customizer",
"Hutten",
"cadquery",
"jscad",
"openscad",
"sendmail"
]
}

67
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,67 @@
Hello 👋
Really happy you're checking out how to contribute.
Here you'll find a break down of the tech we're using,
If you'd like to get involved one of the best ways is to drop by the [discord](https://discord.gg/SD7zFRNjGH), say hi and let us know you're interested in contributing. All are welcome.
## Tech used
### Redwood
CadHub is a [RedWood app](https://redwoodjs.com/). Simplistically this means it's a React frontend, using a serverless graphQL backend with Prisma.
We are also using [Tailwind](https://tailwindcss.com/) to style the app.
To learn more about Redwood, here are some useful links:
- [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.
### Cad Packages
Because Each CadPackage is it's own beast we opted to use Docker in order to give us lots of flexibility for setting up the environment to run there packages. The containers are run using AWS's container lambda and deployed using the serverless framework (JSCAD is an exception since it runs client-side). See [our docs](https://learn.cadhub.xyz/docs/general-cadhub/integrations) for more information of how this is setup.
## Getting your dev environment setup
Clone the repo and `cd` in the app directory (the docs directory is for [learn.cadhub](https://learn.cadhub.xyz/))
```
cd app
```
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).
Run the following
``` terminal
yarn rw prisma migrate dev
yarn rw prisma db seed
```
p.s. `yarn rw prisma studio` spins up an app to inspect the db
### 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/*`.
If you want to access the websight on your phone use `yarn redwood dev --fwd="--host <ip-address-on-your-network-i.e.-192.168.0.5>"`
you can sign in to the following accounts locally
localUser1@kurthutten.com: `abc123`
localUser2@kurthutten.com: `abc123`
localAdmin@kurthutten.com: `abc123`
## Designs
In progress, though can be [seen on Figma](https://www.figma.com/file/VUh53RdncjZ7NuFYj0RGB9/CadHub?node-id=0%3A1)
## Docs
Docs are hosted at [learn.cadhub.xyz](http://learn.cadhub.xyz/). It includes a OpenSCAD tutorial at this point, and more is coming. The docs can be found in this repo at [docs](https://github.com/Irev-Dev/cadhub/tree/main/docs)

View File

@@ -1,78 +1,17 @@
![CadHub banner](https://raw.githubusercontent.com/Irev-Dev/repo-images/main/images/gear%20donutbanner.png) ![Screen Recording 2021-09-21 at 8](https://user-images.githubusercontent.com/29681384/134154332-65491787-7b36-4ad9-ba7a-bac0f2874051.gif)
![scrch2](https://user-images.githubusercontent.com/29681384/134156021-6b55c301-a77a-4851-b67b-b656875123e5.jpg)
# [C a d H u b](https://cadhub.xyz) # [C a d H u b](https://cadhub.xyz)
[![Netlify Status](https://api.netlify.com/api/v1/badges/77f37543-e54a-4723-8136-157c0221ec27/deploy-status)](https://app.netlify.com/sites/cadhubxyz/deploys) <!-- [![Netlify Status](https://api.netlify.com/api/v1/badges/77f37543-e54a-4723-8136-157c0221ec27/deploy-status)](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). 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 integrations in progress for [OpenSCAD](https://cadhub.xyz/dev-ide/openscad) and [CadQuery](https://cadhub.xyz/dev-ide/cadquery) 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). If you want to be involved in anyway, checkout the [contributing.md](https://github.com/Irev-Dev/cadhub/blob/main/CONTRIBUTING.md).
<img src="https://raw.githubusercontent.com/Irev-Dev/repo-images/main/images/fullcadhubshot.jpg"> you might also be interested in the [Road Map](https://github.com/Irev-Dev/cadhub/discussions/212) and getting 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/Part%20IDE%20-%20export%20expand%20state.jpg"> ## Who is CadHub
## Getting Started [Kurt](https://github.com/Irev-Dev) and [Frank](https://github.com/franknoirot) make up the Core-team and [Jeremy](https://github.com/jmwright), [Torsten](https://github.com/t-paul) and [Hrg](https://github.com/hrgdavor) are a major contributors. Plus a number smaller contributors.
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">

View File

@@ -17,3 +17,14 @@ CLOUDINARY_API_KEY=476712943135152
# See: https://redwoodjs.com/docs/logger for level options: # See: https://redwoodjs.com/docs/logger for level options:
# trace | info | debug | warn | error | silent # trace | info | debug | warn | error | silent
# LOG_LEVEL=debug # LOG_LEVEL=debug
# EMAIL_PASSWORD=abc123
# CAD_LAMBDA_BASE_URL="http://localhost:8080"
# sentry
GITHUB_ASSIST_APP_ID=23342
GITHUB_ASSIST_SECRET=abc
GITHUB_ASSIST_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nabcdefg\n-----END RSA PRIVATE KEY-----"

View File

@@ -1 +1 @@
/web/src/cascade/*

7
app/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
dist
web/types/graphql.d.ts
api/types/graphql.d.ts
# Deployment
.serverless

View File

@@ -1 +0,0 @@
module.exports = { extends: "../babel.config.js" }

2547
app/api/backend.tldr Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
-- CreateTable
CREATE TABLE "RW_DataMigration" (
"version" TEXT NOT NULL,
"name" TEXT NOT NULL,
"startedAt" TIMESTAMP(3) NOT NULL,
"finishedAt" TIMESTAMP(3) NOT NULL,
PRIMARY KEY ("version")
);

View File

@@ -0,0 +1,31 @@
/*
Warnings:
- You are about to drop the `Comment` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `Part` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `PartReaction` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropForeignKey
ALTER TABLE "Comment" DROP CONSTRAINT "Comment_partId_fkey";
-- DropForeignKey
ALTER TABLE "Comment" DROP CONSTRAINT "Comment_userId_fkey";
-- DropForeignKey
ALTER TABLE "Part" DROP CONSTRAINT "Part_userId_fkey";
-- DropForeignKey
ALTER TABLE "PartReaction" DROP CONSTRAINT "PartReaction_partId_fkey";
-- DropForeignKey
ALTER TABLE "PartReaction" DROP CONSTRAINT "PartReaction_userId_fkey";
-- DropTable
DROP TABLE "Comment";
-- DropTable
DROP TABLE "Part";
-- DropTable
DROP TABLE "PartReaction";

View File

@@ -0,0 +1,59 @@
-- CreateTable
CREATE TABLE "Project" (
"id" TEXT NOT NULL,
"title" VARCHAR(25) 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 "ProjectReaction" (
"id" TEXT NOT NULL,
"emote" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"projectId" 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,
"projectId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Project.title_userId_unique" ON "Project"("title", "userId");
-- CreateIndex
CREATE UNIQUE INDEX "ProjectReaction.emote_userId_projectId_unique" ON "ProjectReaction"("emote", "userId", "projectId");
-- AddForeignKey
ALTER TABLE "Project" ADD FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProjectReaction" ADD FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProjectReaction" ADD FOREIGN KEY ("projectId") REFERENCES "Project"("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 ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,5 @@
-- CreateEnum
CREATE TYPE "CadPackage" AS ENUM ('openscad', 'cadquery');
-- AlterTable
ALTER TABLE "Project" ADD COLUMN "cadPackage" "CadPackage" NOT NULL DEFAULT E'openscad';

View File

@@ -0,0 +1,17 @@
-- CreateTable
CREATE TABLE "SocialCard" (
"id" TEXT NOT NULL,
"projectId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"url" TEXT,
"outOfDate" BOOLEAN NOT NULL DEFAULT true,
PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "SocialCard_projectId_unique" ON "SocialCard"("projectId");
-- AddForeignKey
ALTER TABLE "SocialCard" ADD FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,2 @@
-- AlterIndex
ALTER INDEX "SocialCard_projectId_unique" RENAME TO "SocialCard.projectId_unique";

View File

@@ -0,0 +1,2 @@
-- AlterEnum
ALTER TYPE "CadPackage" ADD VALUE 'jscad';

View File

@@ -0,0 +1,5 @@
-- AlterTable
ALTER TABLE "Project" ADD COLUMN "forkedFromId" TEXT;
-- AddForeignKey
ALTER TABLE "Project" ADD FOREIGN KEY ("forkedFromId") REFERENCES "Project"("id") ON DELETE SET NULL ON UPDATE CASCADE;

View File

@@ -0,0 +1,56 @@
-- DropForeignKey
ALTER TABLE "Comment" DROP CONSTRAINT "Comment_projectId_fkey";
-- DropForeignKey
ALTER TABLE "Comment" DROP CONSTRAINT "Comment_userId_fkey";
-- DropForeignKey
ALTER TABLE "Project" DROP CONSTRAINT "Project_userId_fkey";
-- DropForeignKey
ALTER TABLE "ProjectReaction" DROP CONSTRAINT "ProjectReaction_projectId_fkey";
-- DropForeignKey
ALTER TABLE "ProjectReaction" DROP CONSTRAINT "ProjectReaction_userId_fkey";
-- DropForeignKey
ALTER TABLE "SocialCard" DROP CONSTRAINT "SocialCard_projectId_fkey";
-- DropForeignKey
ALTER TABLE "SubjectAccessRequest" DROP CONSTRAINT "SubjectAccessRequest_userId_fkey";
-- AddForeignKey
ALTER TABLE "Project" ADD CONSTRAINT "Project_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "SocialCard" ADD CONSTRAINT "SocialCard_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProjectReaction" ADD CONSTRAINT "ProjectReaction_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProjectReaction" ADD CONSTRAINT "ProjectReaction_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Comment" ADD CONSTRAINT "Comment_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Comment" ADD CONSTRAINT "Comment_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "SubjectAccessRequest" ADD CONSTRAINT "SubjectAccessRequest_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- RenameIndex
ALTER INDEX "Project.title_userId_unique" RENAME TO "Project_title_userId_key";
-- RenameIndex
ALTER INDEX "ProjectReaction.emote_userId_projectId_unique" RENAME TO "ProjectReaction_emote_userId_projectId_key";
-- RenameIndex
ALTER INDEX "SocialCard.projectId_unique" RENAME TO "SocialCard_projectId_key";
-- RenameIndex
ALTER INDEX "User.email_unique" RENAME TO "User_email_key";
-- RenameIndex
ALTER INDEX "User.userName_unique" RENAME TO "User_userName_key";

View File

@@ -1,2 +1,3 @@
# Please do not edit this file manually # Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql" provider = "postgresql"

View File

@@ -1,11 +1,11 @@
datasource DS { datasource db {
provider = "postgresql" provider = "postgresql"
url = env("DATABASE_URL") url = env("DATABASE_URL")
} }
generator client { generator client {
provider = "prisma-client-js" provider = "prisma-client-js"
binaryTargets = "native" binaryTargets = ["native", "rhel-openssl-1.0.x"]
} }
// sqlLight does not suport enums so we can't use enums until we set up postgresql in dev mode // sqlLight does not suport enums so we can't use enums until we set up postgresql in dev mode
@@ -14,11 +14,6 @@ generator client {
// ADMIN // ADMIN
// } // }
// enum PartType {
// CASCADESTUDIO
// JSCAD
// }
model User { model User {
id String @id @default(uuid()) id String @id @default(uuid())
userName String @unique // reffered to as userId in @relations userName String @unique // reffered to as userId in @relations
@@ -33,15 +28,21 @@ model User {
image String? // url maybe id or file storage service? cloudinary? image String? // url maybe id or file storage service? cloudinary?
bio String? //mark down bio String? //mark down
Part Part[] Project Project[]
Reaction PartReaction[] Reaction ProjectReaction[]
Comment Comment[] Comment Comment[]
SubjectAccessRequest SubjectAccessRequest[] SubjectAccessRequest SubjectAccessRequest[]
} }
model Part { enum CadPackage {
openscad
cadquery
jscad // TODO #422, add jscad to db schema when were ready to enable saving of jscad projects
}
model Project {
id String @id @default(uuid()) id String @id @default(uuid())
title String title String @db.VarChar(25)
description String? // markdown string description String? // markdown string
code String? code String?
mainImage String? // link to cloudinary mainImage String? // link to cloudinary
@@ -50,23 +51,39 @@ model Part {
user User @relation(fields: [userId], references: [id]) user User @relation(fields: [userId], references: [id])
userId String userId String
deleted Boolean @default(false) deleted Boolean @default(false)
cadPackage CadPackage @default(openscad)
socialCard SocialCard?
forkedFromId String?
forkedFrom Project? @relation("Fork", fields: [forkedFromId], references: [id])
Comment Comment[] childForks Project[] @relation("Fork")
Reaction PartReaction[] Comment Comment[]
Reaction ProjectReaction[]
@@unique([title, userId]) @@unique([title, userId])
} }
model PartReaction { model SocialCard {
id String @id @default(uuid())
projectId String @unique
project Project @relation(fields: [projectId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
url String? // link to cloudinary
outOfDate Boolean @default(true)
}
model ProjectReaction {
id String @id @default(uuid()) id String @id @default(uuid())
emote String // an emoji emote String // an emoji
user User @relation(fields: [userId], references: [id]) user User @relation(fields: [userId], references: [id])
userId String userId String
part Part @relation(fields: [partId], references: [id]) project Project @relation(fields: [projectId], references: [id])
partId String projectId String
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
@@unique([emote, userId, partId]) @@unique([emote, userId, projectId])
} }
model Comment { model Comment {
@@ -74,8 +91,8 @@ model Comment {
text String // the comment, should I allow mark down? text String // the comment, should I allow mark down?
user User @relation(fields: [userId], references: [id]) user User @relation(fields: [userId], references: [id])
userId String userId String
part Part @relation(fields: [partId], references: [id]) project Project @relation(fields: [projectId], references: [id])
partId String projectId String
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
@@ -91,3 +108,10 @@ model SubjectAccessRequest {
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
} }
model RW_DataMigration {
version String @id
name String
startedAt DateTime
finishedAt DateTime
}

View File

@@ -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()
})

View File

@@ -1,6 +1 @@
const { getConfig } = require('@redwoodjs/core') module.exports = require('@redwoodjs/testing/config/jest/api')
const config = getConfig({ type: 'jest', target: 'node' })
config.displayName.name = 'api'
module.exports = config

View File

@@ -3,8 +3,25 @@
"version": "0.0.0", "version": "0.0.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@redwoodjs/api": "^0.31.0", "@redwoodjs/api": "^0.38.1",
"@redwoodjs/api-server": "^0.31.0", "@redwoodjs/graphql-server": "^0.38.1",
"cloudinary": "^1.23.0" "@sentry/node": "^6.5.1",
"axios": "^0.21.1",
"cloudinary": "^1.23.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"human-id": "^2.0.1",
"middy": "^0.36.0",
"nanoid": "^3.1.20",
"nodemailer": "^6.6.2",
"serverless-binary-cors": "^0.0.1"
},
"devDependencies": {
"@netlify/zip-it-and-ship-it": "^4.30.0",
"@types/nodemailer": "^6.4.2",
"concurrently": "^6.0.0",
"nodemon": "^2.0.7",
"serverless-dotenv-plugin": "^3.10.0",
"serverless-plugin-git-variables": "^5.1.0"
} }
} }

View File

@@ -0,0 +1,18 @@
import { mockRedwoodDirective, getDirectiveName } from '@redwoodjs/testing/api'
import requireAuth from './requireAuth'
describe('requireAuth directive', () => {
it('declares the directive sdl as schema, with the correct name', () => {
expect(requireAuth.schema).toBeTruthy()
expect(getDirectiveName(requireAuth.schema)).toBe('requireAuth')
})
it('requireAuth has stub implementation. Should not throw when current user', () => {
// If you want to set values in context, pass it through e.g.
// mockRedwoodDirective(requireAuth, { context: { currentUser: { id: 1, name: 'Lebron McGretzky' } }})
const mockExecution = mockRedwoodDirective(requireAuth, { context: {} })
expect(mockExecution).not.toThrowError()
})
})

View File

@@ -0,0 +1,22 @@
import gql from 'graphql-tag'
import { createValidatorDirective } from '@redwoodjs/graphql-server'
import { requireAuth as applicationRequireAuth } from 'src/lib/auth'
export const schema = gql`
"""
Use to check whether or not a user is authenticated and is associated
with an optional set of roles.
"""
directive @requireAuth(roles: [String]) on FIELD_DEFINITION
`
const validate = ({ directiveArgs }) => {
const { roles } = directiveArgs
applicationRequireAuth({ roles })
}
const requireAuth = createValidatorDirective(schema, validate)
export default requireAuth

View File

@@ -0,0 +1,10 @@
import { getDirectiveName } from '@redwoodjs/testing/api'
import skipAuth from './skipAuth'
describe('skipAuth directive', () => {
it('declares the directive sdl as schema, with the correct name', () => {
expect(skipAuth.schema).toBeTruthy()
expect(getDirectiveName(skipAuth.schema)).toBe('skipAuth')
})
})

View File

@@ -0,0 +1,16 @@
import gql from 'graphql-tag'
import { createValidatorDirective } from '@redwoodjs/graphql-server'
export const schema = gql`
"""
Use to skip authentication checks and allow public access.
"""
directive @skipAuth on FIELD_DEFINITION
`
const skipAuth = createValidatorDirective(schema, () => {
return
})
export default skipAuth

View File

@@ -0,0 +1,5 @@
# The following are the env vars you need run the cad lamdas locally
# The still connect to s3 so some secrets are needed, ask Kurt and he'll set things up for you
DEV_AWS_SECRET_ACCESS_KEY=""
DEV_AWS_ACCESS_KEY_ID=""
DEV_BUCKET="cad-preview-bucket-dev-001"

View File

@@ -1,11 +1,12 @@
# Serverless # Serverless
We're using the serverless from work for deployment We're using the serverless framework for deployment
``` ```
sls deploy yarn rw build api && sls deploy --stage <stagename>
``` ```
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. 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.
Deploying has `yarn rw build` first because the image uses built js files
## Testing changes locally ## Testing changes locally
@@ -14,20 +15,21 @@ 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 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: The docker build relies on a git ignored file, the aws-lambda-rie. [Download it](https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/download/v1.0/aws-lambda-rie), then put it into `app/api/src/docker/common/`. Alternatively you can put this download into the DockerFiles by reading the instructions at around line 29 of the DockerFiles (`app/api/src/docker/openscad/Dockerfile` & `app/api/src/docker/cadquery/Dockerfile`). However this will mean slower build times as it will need download this 14mb file every build.
Run
```bash ```bash
docker-compose up --build yarn cad
``` ```
The first time you run this, it has to build the main image it will take some time, but launching again will be quicker. 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). 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 aws-emulate
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`) You can now add CAD_LAMBDA_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).
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) If you change anything in the `api/src/docker/openscad` directory, you will need to stop the docker process and restart it (will be fairly quick if you're only changing the js)

View File

@@ -1,7 +1,6 @@
const express = require('express') const express = require('express')
var cors = require('cors') var cors = require('cors')
const axios = require('axios') const axios = require('axios')
const { restart } = require('nodemon')
const app = express() const app = express()
const port = 8080 const port = 8080
app.use(express.json()) app.use(express.json())
@@ -10,31 +9,32 @@ app.use(cors())
const invocationURL = (port) => const invocationURL = (port) =>
`http://localhost:${port}/2015-03-31/functions/function/invocations` `http://localhost:${port}/2015-03-31/functions/function/invocations`
app.post('/openscad/preview', async (req, res) => { const makeRequest = (route, port) => [
try { route,
const { data } = await axios.post(invocationURL(5052), { async (req, res) => {
body: Buffer.from(JSON.stringify(req.body)).toString('base64'), console.log(`making post request to ${port}, ${route}`)
}) try {
res.status(data.statusCode) const { data } = await axios.post(invocationURL(port), {
res.send(data.body) body: Buffer.from(JSON.stringify(req.body)).toString('base64'),
} catch (e) { })
res.status(500) res.status(data.statusCode)
res.send() res.setHeader('Content-Type', 'application/javascript')
} if (data.headers && data.headers['Content-Encoding'] === 'gzip') {
}) res.setHeader('Content-Encoding', 'gzip')
app.post('/cadquery/stl', async (req, res) => { res.send(Buffer.from(data.body, 'base64'))
console.log('making post request to 5060') } else {
try { res.send(Buffer.from(data.body, 'base64'))
const { data } = await axios.post(invocationURL(5060), { }
body: Buffer.from(JSON.stringify(req.body)).toString('base64'), } catch (e) {
}) res.status(500)
res.status(data.statusCode) res.send()
res.send(data.body) }
} catch (e) { },
res.status(500) ]
res.send()
} app.post(...makeRequest('/openscad/preview', 5052))
}) app.post(...makeRequest('/openscad/stl', 5053))
app.post(...makeRequest('/cadquery/stl', 5060))
app.listen(port, () => { app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`) console.log(`Example app listening at http://localhost:${port}`)

View File

@@ -1,14 +1,15 @@
FROM public.ecr.aws/lts/ubuntu:20.04_stable FROM continuumio/miniconda3
ENV PATH="/root/miniconda3/bin:${PATH}"
ARG PATH="/root/miniconda3/bin:${PATH}"
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update -qq RUN apt-get update --fix-missing -qq
RUN apt-get -y -qq install software-properties-common dirmngr apt-transport-https lsb-release ca-certificates xvfb 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 update -qq
RUN apt-get install -y wget RUN apt-get install -y wget
# install node14, see comment at the to of node14source_setup.sh # install node14, see comment at the to of node14source_setup.sh
ADD common/node14source_setup.sh /nodesource_setup.sh ADD api/src/docker/common/node14source_setup.sh /nodesource_setup.sh
RUN ["chmod", "+x", "/nodesource_setup.sh"] RUN ["chmod", "+x", "/nodesource_setup.sh"]
RUN bash nodesource_setup.sh RUN bash nodesource_setup.sh
RUN apt-get install -y nodejs RUN apt-get install -y nodejs
@@ -21,29 +22,45 @@ RUN apt-get update && \
cmake \ cmake \
unzip \ unzip \
automake autoconf libtool \ automake autoconf libtool \
libcurl4-openssl-dev libcurl4-openssl-dev \
curl \
git
# Add the lambda emulator for local dev, (see entrypoint.sh for where it's used), # 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), # 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. # 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 api/src/docker/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 # 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"] RUN ["chmod", "+x", "/usr/local/bin/aws-lambda-rie"]
WORKDIR /var/task/ WORKDIR /var/task/
COPY cadquery/package*.json /var/task/ # aws-lambda-ric does not play nice with yarn, so installing it seperately,
RUN npm install # circle back to this later for a proper solution
COPY package*.json /var/task/
RUN npm install aws-lambda-ric@1.0.0
RUN conda --version
# Install CadQuery
RUN conda install -c cadquery -c conda-forge cadquery=master ocp=7.5.2 python=3.8
RUN conda info
# Get a copy of cq-cli from GitHub
RUN git clone https://github.com/CadQuery/cq-cli.git
# Get the distribution copy of cq-cli # 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 apt-get install -y libglew2.1
RUN unzip cq-cli-Linux-x86_64.zip
RUN chmod +x cq-cli/cq-cli RUN echo "cadhub-concat-split" > /var/task/cadhub-concat-split
# using built javascript from dist
# run `yarn rw build` and $(npm bin)/zip-it-and-ship-it api/dist/functions/ api/dist/zipball before bulding this image
COPY api/dist/zipball/cadquery.zip /var/task/
# -n stops aws-lamda-ric from being overridden.
RUN unzip -n /var/task/cadquery.zip
COPY api/src/docker/common/entrypoint.sh /entrypoint.sh
COPY cadquery/*.js /var/task/
COPY common/*.js /var/common/
COPY common/entrypoint.sh /entrypoint.sh
RUN ["chmod", "+x", "/entrypoint.sh"] RUN ["chmod", "+x", "/entrypoint.sh"]
ENTRYPOINT ["sh", "/entrypoint.sh"] ENTRYPOINT ["sh", "/entrypoint.sh"]
CMD [ "cadquery.stl" ] CMD [ "cadquery.stl" ]

View File

@@ -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,
}

View File

@@ -0,0 +1,21 @@
import { runCQ } from './runCQ'
import middy from 'middy'
import { cors } from 'middy/middlewares'
import { loggerWrap, storeAssetAndReturnUrl } from '../common/utils'
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, consoleMessage, fullPath } = await runCQ({ file, settings })
await storeAssetAndReturnUrl({
error,
callback,
fullPath,
consoleMessage,
})
}
export const stl = middy(loggerWrap(_stl)).use(cors())

View File

@@ -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"
}
}

View File

@@ -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 }
}
}

View File

@@ -0,0 +1,60 @@
import { writeFiles, runCommand } from '../common/utils'
import { nanoid } from 'nanoid'
import { readFile } from 'fs/promises'
export const runCQ = async ({
file,
settings: { deflection = 0.3, parameters } = {},
} = {}) => {
const tempFile = await writeFiles(
[
{ file, fileName: 'main.py' },
{
file: JSON.stringify(parameters),
fileName: 'params.json',
},
],
'a' + nanoid() // 'a' ensure nothing funny happens if it start with a bad character like "-", maybe I should pick a safer id generator :shrug:
)
const fullPath = `/tmp/${tempFile}/output.gz`
const stlPath = `/tmp/${tempFile}/output.stl`
const customizerPath = `/tmp/${tempFile}/customizer.json`
const command = [
`/var/task/cq-cli/cq-cli.py`,
`--codec stl`,
`--infile /tmp/${tempFile}/main.py`,
`--outfile ${stlPath}`,
`--outputopts "deflection:${deflection};angularDeflection:${deflection};"`,
`--params /tmp/${tempFile}/params.json`,
`--getparams ${customizerPath}`,
].join(' ')
console.log('command', command)
let consoleMessage = ''
try {
consoleMessage = await runCommand(command, 30000)
const params = JSON.parse(
await readFile(customizerPath, { encoding: 'ascii' })
)
await writeFiles(
[
{
file: JSON.stringify({
customizerParams: params,
consoleMessage,
type: 'stl',
}),
fileName: 'metadata.json',
},
],
tempFile
)
await runCommand(
`cat ${stlPath} /var/task/cadhub-concat-split /tmp/${tempFile}/metadata.json | gzip > ${fullPath}`,
15000,
true
)
return { consoleMessage, fullPath }
} catch (error) {
return { error: consoleMessage || error, fullPath }
}
}

View File

@@ -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=

View File

@@ -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,
}

View File

@@ -0,0 +1,151 @@
const { exec } = require('child_process')
const { promises } = require('fs')
const { writeFile } = promises
const { createHash } = require('crypto')
import { readFile } from 'fs/promises'
export async function writeFiles(
files: { file: string; fileName: string }[] = [],
tempFile: string
): Promise<string> {
console.log(`file to write: ${files.length}`)
try {
await runCommand(`mkdir /tmp/${tempFile}`)
} catch (e) {
//
}
await Promise.all(
files.map(({ file, fileName }) =>
writeFile(`/tmp/${tempFile}/${fileName}`, file)
)
)
return tempFile
}
export async function runCommand(
command,
timeout = 5000,
shouldRejectStdErr = false
): Promise<string> {
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}`)
if (shouldRejectStdErr) {
reject(stderr)
return
}
resolve(stderr)
return
}
console.log(`stdout: ${stdout}`)
resolve(stdout)
})
setTimeout(() => {
reject('timeout')
}, timeout)
})
}
function makeHash(script) {
return createHash('sha256').update(script).digest('hex')
}
async function checkIfAlreadyExists(params, s3) {
try {
await s3.headObject(params).promise()
return { isAlreadyInBucket: true }
} catch (e) {
console.log("couldn't find it", e)
return { isAlreadyInBucket: false }
}
}
function getObjectUrl(params, s3, tk) {
const getTruncatedTime = () => {
const currentTime = new Date()
const d = new Date(currentTime)
d.setMinutes(Math.floor(d.getMinutes() / 10) * 10)
d.setSeconds(0)
d.setMilliseconds(0)
return d
}
const HALF_HOUR = 1800
return tk.withFreeze(getTruncatedTime(), () =>
s3.getSignedUrl('getObject', {
...params,
Expires: HALF_HOUR,
})
)
}
export function loggerWrap(handler) {
return (req, _context, callback) => {
try {
return handler(req, _context, callback)
} catch (e) {
console.log('error in handler', e)
}
}
}
export async function storeAssetAndReturnUrl({
error,
callback,
fullPath,
consoleMessage,
}: {
error: string
callback: Function
fullPath: string
consoleMessage: string
}) {
if (error) {
const response = {
statusCode: 400,
body: Buffer.from(JSON.stringify({ error, fullPath })).toString('base64'),
isBase64Encoded: true,
}
callback(null, response)
return
} else {
console.log(`got result in route: ${consoleMessage}, file is: ${fullPath}`)
let buffer = ''
try {
buffer = await readFile(fullPath, { encoding: 'base64' })
} catch (e) {
console.log('read file error', e)
const response = {
statusCode: 400,
body: Buffer.from(
JSON.stringify({ error: consoleMessage, fullPath })
).toString('base64'),
isBase64Encoded: true,
}
callback(null, response)
return
}
const response = {
statusCode: 200,
body: buffer,
isBase64Encoded: true,
headers: {
'Content-Type': 'application/javascript',
'Content-Encoding': 'gzip',
},
}
callback(null, response)
return
}
}

View File

@@ -1,44 +1,47 @@
services: 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: openscad-preview:
build:
context: ../../../
dockerfile: ./api/src/docker/openscad/Dockerfile
image: openscad image: openscad
# build: ./openscad/.
command: openscad.preview command: openscad.preview
# networks: # Adding volumes so that the containers can be restarted for js only changes in local dev
# - awsland volumes:
- ../dist/docker/openscad:/var/task/js/
- ../dist/docker/common:/var/task/common/
ports: ports:
- "5052:8080" - "5052:8080"
environment:
AWS_SECRET_ACCESS_KEY: "${DEV_AWS_SECRET_ACCESS_KEY}"
AWS_ACCESS_KEY_ID: "${DEV_AWS_ACCESS_KEY_ID}"
BUCKET: "${DEV_BUCKET}"
openscad-stl: openscad-stl:
image: openscad image: openscad
# build: ./openscad/. volumes:
- ../dist/docker/openscad:/var/task/js/
- ../dist/docker/common:/var/task/common/
command: openscad.stl command: openscad.stl
ports: ports:
- "5053:8080" - "5053:8080"
environment:
AWS_SECRET_ACCESS_KEY: "${DEV_AWS_SECRET_ACCESS_KEY}"
AWS_ACCESS_KEY_ID: "${DEV_AWS_ACCESS_KEY_ID}"
BUCKET: "${DEV_BUCKET}"
cadquery-stl: cadquery-stl:
build: build:
context: ./ context: ../../../
dockerfile: ./cadquery/. dockerfile: ./api/src/docker/cadquery/Dockerfile
volumes:
- ../dist/docker/cadquery:/var/task/js/
- ../dist/docker/common:/var/task/common/
command: cadquery.stl command: cadquery.stl
ports: ports:
- 5060:8080 - 5060:8080
environment:
AWS_SECRET_ACCESS_KEY: "${DEV_AWS_SECRET_ACCESS_KEY}"
AWS_ACCESS_KEY_ID: "${DEV_AWS_ACCESS_KEY_ID}"
BUCKET: "${DEV_BUCKET}"
# networks:
# awsland:
# name: awsland

View File

@@ -3,15 +3,18 @@ FROM public.ecr.aws/lts/ubuntu:20.04_stable
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
## install things needed to run openscad (xvfb is an important one) ## install things needed to run openscad (xvfb is an important one)
RUN apt-get update -qq RUN apt-get update --fix-missing -qq
# double check this below, I'm not sure we need inkscape etc # 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 -y -qq install software-properties-common dirmngr apt-transport-https lsb-release ca-certificates xvfb imagemagick unzip inkscape
RUN apt-get install -y curl wget
RUN touch /etc/apt/sources.list.d/openscad.list
RUN echo "deb https://download.opensuse.org/repositories/home:/t-paul/xUbuntu_20.04/ ./" >> /etc/apt/sources.list.d/openscad.list
RUN wget -qO - https://files.openscad.org/OBS-Repository-Key.pub | apt-key add -
RUN apt-get update -qq RUN apt-get update -qq
RUN apt-get install -y -qq openscad RUN apt-get install -y openscad-nightly
RUN apt-get install -y curl
# install node14, see comment at the to of node14source_setup.sh # install node14, see comment at the to of node14source_setup.sh
ADD common/node14source_setup.sh /nodesource_setup.sh ADD api/src/docker/common/node14source_setup.sh /nodesource_setup.sh
RUN ["chmod", "+x", "/nodesource_setup.sh"] RUN ["chmod", "+x", "/nodesource_setup.sh"]
RUN bash nodesource_setup.sh RUN bash nodesource_setup.sh
RUN apt-get install -y nodejs RUN apt-get install -y nodejs
@@ -29,18 +32,35 @@ RUN apt-get update && \
# Add the lambda emulator for local dev, (see entrypoint.sh for where it's used), # 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), # 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. # 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 api/src/docker/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 # 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"] RUN ["chmod", "+x", "/usr/local/bin/aws-lambda-rie"]
WORKDIR /var/task/ WORKDIR /var/task/
COPY openscad/package*.json /var/task/ # aws-lambda-ric does not play nice with yarn, so installing it seperately,
RUN npm install # circle back to this later for a proper solution
COPY package*.json /var/task/
RUN npm install aws-lambda-ric@1.0.0
# Install OpenSCAD libraries
# It's experimental, so only adding latest Round-Anything for now
RUN echo "OPENSCADPATH=/var/task/openscad" >>/etc/profile && \
wget -P /var/task/openscad/ https://github.com/Irev-Dev/Round-Anything/archive/refs/tags/1.0.4.zip && \
unzip /var/task/openscad/1.0.4
# Add our own theming (based on DeepOcean with a different "background" and "opencsg-face-back")
COPY api/src/docker/openscad/cadhubtheme.json /usr/share/openscad-nightly/color-schemes/render/
RUN echo "cadhub-concat-split" > /var/task/cadhub-concat-split
# using built javascript from dist
# run `yarn rw build` and $(npm bin)/zip-it-and-ship-it api/dist/functions/ api/dist/zipball before bulding this image
COPY api/dist/zipball/openscad.zip /var/task/
# -n stops aws-lamda-ric from being overridden.
RUN unzip -n /var/task/openscad.zip
COPY api/src/docker/common/entrypoint.sh /entrypoint.sh
COPY openscad/*.js /var/task/
COPY common/*.js /var/common/
COPY common/entrypoint.sh /entrypoint.sh
RUN ["chmod", "+x", "/entrypoint.sh"] RUN ["chmod", "+x", "/entrypoint.sh"]
ENTRYPOINT ["sh", "/entrypoint.sh"] ENTRYPOINT ["sh", "/entrypoint.sh"]
CMD [ "openscad.render" ] CMD [ "openscad.preview" ]

View File

@@ -0,0 +1,19 @@
{
"name" : "CadHub",
"index" : 1600,
"show-in-gui" : true,
"colors" : {
"background" : "#1A1A1D",
"axes-color" : "#c1c1c1",
"opencsg-face-front" : "#eeeeee",
"opencsg-face-back" : "#8732F2",
"cgal-face-front" : "#eeeeee",
"cgal-face-back" : "#0babc8",
"cgal-face-2d" : "#9370db",
"cgal-edge-front" : "#0000ff",
"cgal-edge-back" : "#0000ff",
"cgal-edge-2d" : "#ff00ff",
"crosshair" : "#f0f0f0"
}
}

View File

@@ -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,
}

View File

@@ -0,0 +1,44 @@
import { runScad, stlExport } from './runScad'
import middy from 'middy'
import { cors } from 'middy/middlewares'
import { loggerWrap, storeAssetAndReturnUrl } from '../common/utils'
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, consoleMessage, fullPath } = await runScad({
file,
settings,
})
await storeAssetAndReturnUrl({
error,
callback,
fullPath,
consoleMessage,
})
}
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, consoleMessage, fullPath } = await stlExport({
file,
settings,
})
await storeAssetAndReturnUrl({
error,
callback,
fullPath,
consoleMessage,
})
}
export const stl = middy(loggerWrap(_stl)).use(cors())
export const preview = middy(loggerWrap(_preview)).use(cors())

View File

@@ -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"
}
}

View File

@@ -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 }
}
}

View File

@@ -0,0 +1,150 @@
import { writeFiles, runCommand } from '../common/utils'
import { nanoid } from 'nanoid'
const { readFile } = require('fs/promises')
const OPENSCAD_COMMON = `xvfb-run --auto-servernum --server-args "-screen 0 1024x768x24" openscad-nightly`
/** Removes our generated/hash filename with just "main.scad", so that it's a nice message in the IDE */
const cleanOpenScadError = (error) =>
error.replace(/["|']\/tmp\/.+\/main.scad["|']/g, "'main.scad'")
export const runScad = async ({
file,
settings: {
viewAll = false,
size: { x = 500, y = 500 } = {},
parameters,
camera: {
position = { x: 40, y: 40, z: 40 },
rotation = { x: 55, y: 0, z: 25 },
dist = 200,
} = {},
} = {}, // TODO add view settings
} = {}): Promise<{
error?: string
consoleMessage?: string
fullPath?: string
customizerPath?: string
}> => {
const tempFile = await writeFiles(
[
{ file, fileName: 'main.scad' },
{
file: JSON.stringify({
parameterSets: { default: parameters },
fileFormatVersion: '1',
}),
fileName: 'params.json',
},
],
'a' + nanoid() // 'a' ensure nothing funny happens if it start with a bad character like "-", maybe I should pick a safer id generator :shrug:
)
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 fullPath = `/tmp/${tempFile}/output.gz`
const imPath = `/tmp/${tempFile}/output.png`
const customizerPath = `/tmp/${tempFile}/customizer.param`
const summaryPath = `/tmp/${tempFile}/summary.json` // contains camera info
const command = [
OPENSCAD_COMMON,
`-o ${customizerPath}`,
`-o ${imPath}`,
`--summary camera --summary-file ${summaryPath}`,
viewAll ? '--viewall' : '',
`-p /tmp/${tempFile}/params.json -P default`,
cameraArg,
`--imgsize=${x},${y}`,
`--colorscheme CadHub`,
`/tmp/${tempFile}/main.scad`,
].join(' ')
console.log('command', command)
try {
const consoleMessage = await runCommand(command, 15000)
const files: string[] = await Promise.all(
[customizerPath, summaryPath].map((path) =>
readFile(path, { encoding: 'ascii' })
)
)
const [params, cameraInfo] = files.map((fileStr: string) =>
JSON.parse(fileStr)
)
await writeFiles(
[
{
file: JSON.stringify({
cameraInfo: viewAll ? cameraInfo.camera : undefined,
customizerParams: params.parameters,
consoleMessage,
type: 'png',
}),
fileName: 'metadata.json',
},
],
tempFile
)
await runCommand(
`cat ${imPath} /var/task/cadhub-concat-split /tmp/${tempFile}/metadata.json | gzip > ${fullPath}`,
15000
)
return { consoleMessage, fullPath, customizerPath }
} catch (dirtyError) {
return { error: cleanOpenScadError(dirtyError) }
}
}
export const stlExport = async ({ file, settings: { parameters } } = {}) => {
const tempFile = await writeFiles(
[
{ file, fileName: 'main.scad' },
{
file: JSON.stringify({
parameterSets: { default: parameters },
fileFormatVersion: '1',
}),
fileName: 'params.json',
},
],
'a' + nanoid() // 'a' ensure nothing funny happens if it start with a bad character like "-", maybe I should pick a safer id generator :shrug:
)
const fullPath = `/tmp/${tempFile}/output.gz`
const stlPath = `/tmp/${tempFile}/output.stl`
const customizerPath = `/tmp/${tempFile}/customizer.param`
const command = [
OPENSCAD_COMMON,
// `--export-format=binstl`,
`-o ${customizerPath}`,
`-o ${stlPath}`,
`-p /tmp/${tempFile}/params.json -P default`,
`/tmp/${tempFile}/main.scad`,
].join(' ')
try {
// lambda will time out before this, we might need to look at background jobs if we do git integration stl generation
const consoleMessage = await runCommand(command, 60000)
const params = JSON.parse(
await readFile(customizerPath, { encoding: 'ascii' })
).parameters
await writeFiles(
[
{
file: JSON.stringify({
customizerParams: params,
consoleMessage,
type: 'stl',
}),
fileName: 'metadata.json',
},
],
tempFile
)
await runCommand(
`cat ${stlPath} /var/task/cadhub-concat-split /tmp/${tempFile}/metadata.json | gzip > ${fullPath}`,
15000
)
return { consoleMessage, fullPath, customizerPath }
} catch (error) {
return { error, fullPath }
}
}

View File

@@ -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=

View File

@@ -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"
}
}

View File

@@ -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"

View File

@@ -0,0 +1,3 @@
import { stl } from 'src/docker/cadquery/cadquery'
export { stl }

View File

@@ -0,0 +1,40 @@
import type { APIGatewayEvent /*, Context*/ } from 'aws-lambda'
import { logger } from 'src/lib/logger'
import { db } from 'src/lib/db'
/**
* The handler function is your code that processes http request events.
* You can use return and throw to send a response or error, respectively.
*
* Important: When deployed, a custom serverless function is an open API endpoint and
* is your responsibility to secure appropriately.
*
* @see {@link https://redwoodjs.com/docs/serverless-functions#security-considerations|Serverless Function Considerations}
* in the RedwoodJS documentation for more information.
*
* @typedef { import('aws-lambda').APIGatewayEvent } APIGatewayEvent
* @typedef { import('aws-lambda').Context } Context
* @param { APIGatewayEvent } event - an object which contains information from the invoker.
* @param { Context } context - contains information about the invocation,
* function, and execution environment.
*/
export const handler = async (event: APIGatewayEvent /*context: Context*/) => {
logger.info('Invoked checkUserName function')
const userName = event.queryStringParameters.username
let isUserNameAvailable = false
try {
const user = await db.user.findUnique({ where: { userName } })
isUserNameAvailable = !user
} catch (error) {
isUserNameAvailable = false
}
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
isUserNameAvailable,
}),
}
}

View File

@@ -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()
},
})

View File

@@ -0,0 +1,28 @@
import { createGraphQLHandler } from '@redwoodjs/graphql-server'
import { createSentryApolloPlugin } from 'src/lib/sentry'
import { logger } from 'src/lib/logger'
import directives from 'src/directives/**/*.{js,ts}'
import sdls from 'src/graphql/**/*.sdl.{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({
loggerConfig: { logger, options: {} },
getCurrentUser,
directives,
sdls,
services,
plugins: [createSentryApolloPlugin()],
cors: {
origin: '*',
credentials: true,
},
onException: () => {
// Disconnect from your database with an unhandled exception.
db.$disconnect()
},
})

View File

@@ -1,8 +1,11 @@
import { createUserInsecure } from 'src/services/users/users.js' import { createUserInsecure } from 'src/services/users/users'
import { db } from 'src/lib/db' import { db } from 'src/lib/db'
import { sentryWrapper } from 'src/lib/sentry'
import { enforceAlphaNumeric, generateUniqueString } from 'src/services/helpers' import { enforceAlphaNumeric, generateUniqueString } from 'src/services/helpers'
import 'graphql-tag'
import { sendMail } from 'src/lib/sendmail'
export const handler = async (req, _context) => { const unWrappedHandler = async (req, _context) => {
const body = JSON.parse(req.body) const body = JSON.parse(req.body)
console.log(body) console.log(body)
console.log(_context) console.log(_context)
@@ -54,7 +57,7 @@ export const handler = async (req, _context) => {
const user = body.user const user = body.user
const email = user.email const email = user.email
let roles = [] const roles = []
if (eventType === 'signup') { if (eventType === 'signup') {
roles.push('user') roles.push('user')
@@ -64,13 +67,53 @@ export const handler = async (req, _context) => {
}) })
const userNameSeed = enforceAlphaNumeric(user?.user_metadata?.userName) const userNameSeed = enforceAlphaNumeric(user?.user_metadata?.userName)
const userName = await generateUniqueString(userNameSeed, isUniqueCallback) // TODO maybe come up with a better default userName? const userName = await generateUniqueString(userNameSeed, isUniqueCallback) // TODO maybe come up with a better default userName?
const name = user?.user_metadata?.full_name
const input = { const input = {
email, email,
userName, userName,
name: user?.user_metadata?.full_name, name,
id: user.id, id: user.id,
} }
await createUserInsecure({ input }) await createUserInsecure({ input })
const kurtNotification = sendMail({
to: 'k.hutten@protonmail.ch',
from: {
address: 'news@mail.cadhub.xyz',
name: 'CadHub',
},
subject: `New Cadhub User`,
text: JSON.stringify(input, null, 2),
})
const welcomeMsg = sendMail({
to: email,
from: {
address: 'news@mail.cadhub.xyz',
name: 'CadHub',
},
subject: `${name} - Some things you should know about CadHub`,
text: `Hi, My name's Kurt.
I started CadHub because I wanted a community hub for people who like CodeCAD as much of I do, you should know that the development of CadHub is very much a community effort as well and if you want get involved the discord is the best place to start https://discord.gg/SD7zFRNjGH.
Long term I hope that CadHub will help push CodeCad as a paradigm forward, as there are clear benefits such as: CI/CD for parts, GIT based workflow and CodeCAD parts are normally much more robust to changes to parametric variables because the author can add logic to accommodate big changes where as GUI-CAD usually relies on blackbox heuristics and is more brittle. Sorry I'm getting into the weeds, if you want to read more on the paradigm see our blog https://learn.cadhub.xyz/.
One very easy way to help out is to give the repo a star (https://github.com/Irev-Dev/cadhub), or simply add any OpenSCAD or CadQuery models you have to the website, building out the library of parts atm is very important.
Hit me up anytime for questions or concerns.
Cheers,
Kurt.
k.hutten@protonmail.ch
https://twitter.com/IrevDev
irevdev#1888 - discord
`,
})
try {
await Promise.all([kurtNotification, welcomeMsg])
} catch (e) {
console.log('Problem sending emails', e)
}
return { return {
statusCode: 200, statusCode: 200,
@@ -82,3 +125,5 @@ export const handler = async (req, _context) => {
} }
} }
} }
export const handler = sentryWrapper(unWrappedHandler)

View File

@@ -0,0 +1,3 @@
import { stl, preview } from 'src/docker/openscad/openscad'
export { stl, preview }

View File

@@ -0,0 +1,41 @@
export const schema = gql`
type ProjectReaction {
id: String!
emote: String!
user: User!
userId: String!
project: Project!
projectId: String!
createdAt: DateTime!
updatedAt: DateTime!
}
type Query {
projectReactions: [ProjectReaction!]! @skipAuth
projectReaction(id: String!): ProjectReaction @skipAuth
projectReactionsByProjectId(projectId: String!): [ProjectReaction!]!
@skipAuth
}
input ToggleProjectReactionInput {
emote: String!
userId: String!
projectId: String!
}
input UpdateProjectReactionInput {
emote: String
userId: String
projectId: String
}
type Mutation {
toggleProjectReaction(input: ToggleProjectReactionInput!): ProjectReaction!
@requireAuth
updateProjectReaction(
id: String!
input: UpdateProjectReactionInput!
): ProjectReaction! @requireAuth
deleteProjectReaction(id: String!): ProjectReaction! @requireAuth
}
`

View File

@@ -4,32 +4,33 @@ export const schema = gql`
text: String! text: String!
user: User! user: User!
userId: String! userId: String!
part: Part! project: Project!
partId: String! projectId: String!
createdAt: DateTime! createdAt: DateTime!
updatedAt: DateTime! updatedAt: DateTime!
} }
type Query { type Query {
comments: [Comment!]! comments: [Comment!]! @skipAuth
comment(id: String!): Comment comment(id: String!): Comment @skipAuth
} }
input CreateCommentInput { input CreateCommentInput {
text: String! text: String!
userId: String! userId: String!
partId: String! projectId: String!
} }
input UpdateCommentInput { input UpdateCommentInput {
text: String text: String
userId: String userId: String
partId: String projectId: String
} }
type Mutation { type Mutation {
createComment(input: CreateCommentInput!): Comment! createComment(input: CreateCommentInput!): Comment! @requireAuth
updateComment(id: String!, input: UpdateCommentInput!): Comment! updateComment(id: String!, input: UpdateCommentInput!): Comment!
deleteComment(id: String!): Comment! @requireAuth
deleteComment(id: String!): Comment! @requireAuth
} }
` `

View File

@@ -0,0 +1,20 @@
export const schema = gql`
type Envelope {
from: String
to: [String!]!
}
type EmailResponse {
accepted: [String!]!
rejected: [String!]!
}
input Email {
subject: String!
body: String!
}
type Mutation {
sendAllUsersEmail(input: Email!): EmailResponse! @requireAuth
}
`

View File

@@ -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!
}
`

View File

@@ -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!
}
`

View File

@@ -0,0 +1,70 @@
export const schema = gql`
type Project {
id: String!
title: String!
description: String
code: String
mainImage: String
createdAt: DateTime!
updatedAt: DateTime!
user: User!
userId: String!
deleted: Boolean!
cadPackage: CadPackage!
socialCard: SocialCard
Comment: [Comment]!
Reaction(userId: String): [ProjectReaction]!
forkedFromId: String
forkedFrom: Project
childForks: [Project]!
}
enum CadPackage {
openscad
cadquery
jscad
}
type Query {
projects(userName: String): [Project!]! @skipAuth
project(id: String!): Project @skipAuth
projectByUserAndTitle(userName: String!, projectTitle: String!): Project
@skipAuth
}
input CreateProjectInput {
title: String
description: String
code: String
mainImage: String
userId: String!
cadPackage: CadPackage!
}
input ForkProjectInput {
userId: String!
forkedFromId: String
code: String
}
input UpdateProjectInput {
title: String
description: String
code: String
mainImage: String
userId: String
}
type Mutation {
createProject(input: CreateProjectInput!): Project! @requireAuth
forkProject(input: ForkProjectInput!): Project! @requireAuth
updateProject(id: String!, input: UpdateProjectInput!): Project!
@requireAuth
updateProjectImages(
id: String!
mainImage64: String
socialCard64: String
): Project! @requireAuth
deleteProject(id: String!): Project! @requireAuth
}
`

View File

@@ -0,0 +1,16 @@
export const schema = gql`
type SocialCard {
id: String!
projectId: String!
project: Project!
createdAt: DateTime!
updatedAt: DateTime!
url: String
outOfDate: Boolean!
}
type Query {
socialCards: [SocialCard!]! @skipAuth
socialCard(id: String!): SocialCard @skipAuth
}
`

View File

@@ -10,8 +10,8 @@ export const schema = gql`
} }
type Query { type Query {
subjectAccessRequests: [SubjectAccessRequest!]! subjectAccessRequests: [SubjectAccessRequest!]! @requireAuth
subjectAccessRequest(id: String!): SubjectAccessRequest subjectAccessRequest(id: String!): SubjectAccessRequest @requireAuth
} }
input CreateSubjectAccessRequestInput { input CreateSubjectAccessRequestInput {
@@ -29,11 +29,11 @@ export const schema = gql`
type Mutation { type Mutation {
createSubjectAccessRequest( createSubjectAccessRequest(
input: CreateSubjectAccessRequestInput! input: CreateSubjectAccessRequestInput!
): SubjectAccessRequest! ): SubjectAccessRequest! @requireAuth
updateSubjectAccessRequest( updateSubjectAccessRequest(
id: String! id: String!
input: UpdateSubjectAccessRequestInput! input: UpdateSubjectAccessRequestInput!
): SubjectAccessRequest! ): SubjectAccessRequest! @requireAuth
deleteSubjectAccessRequest(id: String!): SubjectAccessRequest! deleteSubjectAccessRequest(id: String!): SubjectAccessRequest! @requireAuth
} }
` `

View File

@@ -8,17 +8,17 @@ export const schema = gql`
updatedAt: DateTime! updatedAt: DateTime!
image: String image: String
bio: String bio: String
Parts: [Part]! Projects: [Project]!
Part(partTitle: String): Part Project(projectTitle: String): Project
Reaction: [PartReaction]! Reaction: [ProjectReaction]!
Comment: [Comment]! Comment: [Comment]!
SubjectAccessRequest: [SubjectAccessRequest]! SubjectAccessRequest: [SubjectAccessRequest]!
} }
type Query { type Query {
users: [User!]! users: [User!]! @requireAuth
user(id: String!): User user(id: String!): User @skipAuth
userName(userName: String!): User userName(userName: String!): User @skipAuth
} }
input CreateUserInput { input CreateUserInput {
@@ -38,9 +38,10 @@ export const schema = gql`
} }
type Mutation { type Mutation {
createUser(input: CreateUserInput!): User! createUser(input: CreateUserInput!): User! @requireAuth
updateUser(id: String!, input: UpdateUserInput!): User! updateUser(id: String!, input: UpdateUserInput!): User! @requireAuth
updateUserByUserName(userName: String!, input: UpdateUserInput!): User! updateUserByUserName(userName: String!, input: UpdateUserInput!): User!
deleteUser(id: String!): User! @requireAuth
deleteUser(id: String!): User! @requireAuth
} }
` `

View File

@@ -1,61 +1,5 @@
// Define what you want `currentUser` to return throughout your app. For example, import { AuthenticationError, ForbiddenError } from '@redwoodjs/graphql-server'
// to return a real user from your database, you could do something like: import { parseJWT } from '@redwoodjs/api'
//
// 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, * Use requireAuth in your services to check that a user is logged in,
@@ -97,8 +41,24 @@ import { AuthenticationError, ForbiddenError, parseJWT } from '@redwoodjs/api'
* } * }
* } * }
*/ */
export const getCurrentUser = async (decoded, { _token, _type }) => { export const getCurrentUser = async (
return { ...decoded, roles: parseJWT({ decoded }).roles } decoded,
{ _token, _type },
{ _event, _context }
) => {
if (!decoded) {
// if no decoded, then never set currentUser
return null
}
const { roles } = parseJWT({ decoded }) // extract and check roles separately
if (roles) {
return { ...decoded, roles }
}
return { ...decoded } // only return when certain you have
// the currentUser properties
} }
/** /**
@@ -121,7 +81,8 @@ export const getCurrentUser = async (decoded, { _token, _type }) => {
* requireAuth({ role: ['editor', 'author'] }) * requireAuth({ role: ['editor', 'author'] })
* requireAuth({ role: ['publisher'] }) * requireAuth({ role: ['publisher'] })
*/ */
export const requireAuth = ({ role } = {}) => { export const requireAuth = ({ role }: { role?: string | string[] } = {}) => {
console.log(context.currentUser)
if (!context.currentUser) { if (!context.currentUser) {
throw new AuthenticationError("You don't have permission to do that.") throw new AuthenticationError("You don't have permission to do that.")
} }

View File

@@ -0,0 +1,45 @@
import {
v2 as cloudinary,
UploadApiResponse,
UpdateApiOptions,
} from 'cloudinary'
cloudinary.config({
cloud_name: 'irevdev',
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
})
interface UploadImageArgs {
image64: string
uploadPreset?: string
publicId?: string
invalidate: boolean
}
export const uploadImage = async ({
image64,
uploadPreset = 'CadHub_project_images',
publicId,
invalidate = true,
}: UploadImageArgs): Promise<UploadApiResponse> => {
const options: UpdateApiOptions = { upload_preset: uploadPreset, invalidate }
if (publicId) {
options.public_id = publicId
}
return new Promise((resolve, reject) => {
cloudinary.uploader.upload(image64, options, (error, result) => {
if (error) {
reject(error)
return
}
resolve(result)
})
})
}
export const makeSocialPublicIdServer = (
userName: string,
projectTitle: string
): string => `u-${userName}-slash-p-${projectTitle}`

View File

@@ -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.")
}
}
}

94
app/api/src/lib/owner.ts Normal file
View File

@@ -0,0 +1,94 @@
import { AuthenticationError, ForbiddenError } from '@redwoodjs/graphql-server'
import type { Project } from '@prisma/client'
import { db } from 'src/lib/db'
export const requireOwnership = async ({
userId,
userName,
projectId,
sub,
}: {
userId?: string
userName?: string
projectId?: string
sub?: string
} = {}) => {
// 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 || sub)) {
throw new AuthenticationError("You don't have permission to do that.")
}
if (!userId && !userName && !projectId) {
throw new ForbiddenError("You don't have access to do that.")
}
if (context.currentUser.roles?.includes('admin')) {
if (context.currentUser?.sub === '5cea3906-1e8e-4673-8f0d-89e6a963c096') {
throw new ForbiddenError("That's a local admin ONLY.")
}
return
}
const netlifyUserId = context?.currentUser?.sub || 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 (projectId) {
const user = await db.project
.findUnique({
where: { id: projectId },
})
.user()
if (!user || user.id !== netlifyUserId) {
throw new ForbiddenError("You don't own this resource.")
}
}
}
export const requireProjectOwnership = async ({
projectId,
}: {
userId?: string
userName?: string
projectId?: string
sub?: string
} = {}): Promise<Project> => {
// 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 (!projectId) {
throw new ForbiddenError("You don't have access to do that.")
}
const netlifyUserId = context?.currentUser?.sub
if (projectId || context.currentUser.roles?.includes('admin')) {
if (context.currentUser?.sub === '5cea3906-1e8e-4673-8f0d-89e6a963c096') {
throw new ForbiddenError("That's a local admin ONLY.")
}
const project = await db.project.findUnique({
where: { id: projectId },
})
const hasPermission =
(project && project?.userId === netlifyUserId) ||
context.currentUser.roles?.includes('admin')
if (!hasPermission) {
throw new ForbiddenError("You don't own this resource.")
}
return project
}
}

View File

@@ -0,0 +1,63 @@
import nodemailer, { SendMailOptions } from 'nodemailer'
export interface SendMailArgs {
to: string
from: SendMailOptions['from']
subject: string
text: string
}
interface SuccessResult {
accepted: string[]
rejected: string[]
envelopeTime: number
messageTime: number
messageSize: number
response: string
envelope: {
from: string | false
to: string[]
}
messageId: string
}
export function sendMail({
to,
from,
subject,
text,
}: SendMailArgs): Promise<SuccessResult> {
const transporter = nodemailer.createTransport({
host: 'smtp.mailgun.org',
port: 587,
secure: false,
tls: {
ciphers: 'SSLv3',
},
auth: {
user: 'postmaster@mail.cadhub.xyz',
pass: process.env.EMAIL_PASSWORD,
},
})
console.log({ to, from, subject, text })
const emailPromise = new Promise((resolve, reject) => {
transporter.sendMail(
{
from,
to,
subject,
text,
},
(error, info) => {
if (error) {
reject(error)
} else {
resolve(info)
}
}
)
}) as any as Promise<SuccessResult>
return emailPromise
}

105
app/api/src/lib/sentry.ts Normal file
View File

@@ -0,0 +1,105 @@
import { Config, ApolloError } from '@redwoodjs/graphql-server'
import * as Sentry from '@sentry/node'
let sentryInitialized = false
if (process.env.SENTRY_DSN && !sentryInitialized) {
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.CONTEXT,
release: process.env.COMMIT_REF,
})
sentryInitialized = true
}
async function reportError(error) {
if (!sentryInitialized) return
// If you do have authentication set up, we can add
// some user data to help debug issues
// if (context.currentUser) {
// Sentry.configureScope((scope) => {
// scope.setUser({
// id: context?.currentUser?.id,
// email: context?.currentUser?.email,
// })
// })
// }
if (typeof error === 'string') {
Sentry.captureMessage(error)
} else {
Sentry.captureException(error)
}
await Sentry.flush()
}
export const sentryWrapper = (handler) => async (event, lambdaContext) => {
lambdaContext.callbackWaitsForEmptyEventLoop = false
try {
return await new Promise((resolve, reject) => {
const callback = (err, result) => {
if (err) {
reject(err)
} else {
resolve(result)
}
}
const resp = handler(event, lambdaContext, callback)
if (resp?.then) {
resp.then(resolve, reject)
}
})
} catch (e) {
// This catches both sync errors & promise
// rejections, because we 'await' on the handler
await reportError(e)
throw e
}
}
export const createSentryApolloPlugin: Config['plugins'][number] = () => ({
requestDidStart: () => {
return {
didEncounterErrors(ctx) {
// If we couldn't parse the operation, don't
// do anything here
if (!ctx.operation) {
return
}
for (const err of ctx.errors) {
// Only report internal server errors,
// all errors extending ApolloError should be user-facing
if (err instanceof ApolloError) {
continue
}
// Add scoped report details and send to Sentry
Sentry.withScope((scope) => {
// Annotate whether failing operation was query/mutation/subscription
scope.setTag('kind', ctx.operation.operation)
// Log query and variables as extras (make sure to strip out sensitive data!)
scope.setExtra('query', ctx.request.query)
scope.setExtra('variables', ctx.request.variables)
if (err.path) {
// We can also add the path as breadcrumb
scope.addBreadcrumb({
category: 'query-path',
message: err.path.join(' > '),
level: Sentry.Severity.Debug,
})
}
const transactionId =
ctx.request.http.headers.get('x-transaction-id')
if (transactionId) {
scope.setTransaction(transactionId)
}
Sentry.captureException(err)
})
}
},
}
},
})

View File

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

View File

@@ -33,6 +33,6 @@ export const deleteComment = ({ id }) => {
export const Comment = { export const Comment = {
user: (_obj, { root }) => user: (_obj, { root }) =>
db.comment.findUnique({ where: { id: root.id } }).user(), db.comment.findUnique({ where: { id: root.id } }).user(),
part: (_obj, { root }) => project: (_obj, { root }) =>
db.comment.findUnique({ where: { id: root.id } }).part(), db.comment.findUnique({ where: { id: root.id } }).project(),
} }

View File

@@ -0,0 +1,45 @@
import { requireAuth } from 'src/lib/auth'
import { sendMail } from 'src/lib/sendmail'
import type { SendMailArgs } from 'src/lib/sendmail'
import { users } from 'src/services/users/users'
export const sendAllUsersEmail = async ({ input: { body, subject } }) => {
requireAuth({ role: 'admin' })
const from = {
address: 'news@mail.cadhub.xyz',
name: 'CadHub',
}
const emails: SendMailArgs[] = (await users()).map(({ email }) => ({
to: email,
from,
subject,
text: body,
}))
const emailPromises = emails.map((email) => sendMail(email))
const accepted = []
const rejected = []
const result = await Promise.allSettled(emailPromises)
result.forEach((result) => {
if (result.status === 'fulfilled') {
accepted.push(result.value.accepted[0])
} else {
rejected.push(result.reason)
}
})
await sendMail({
to: 'k.hutten@protonmail.ch',
from,
subject: `All users email report`,
text: JSON.stringify(
{
accepted,
rejected,
originalEmailList: emails,
},
null,
2
),
})
return { accepted, rejected }
}

View File

@@ -1,4 +1,6 @@
import { v2 as cloudinary } from 'cloudinary' import { v2 as cloudinary } from 'cloudinary'
import humanId from 'human-id'
cloudinary.config({ cloudinary.config({
cloud_name: 'irevdev', cloud_name: 'irevdev',
api_key: process.env.CLOUDINARY_API_KEY, api_key: process.env.CLOUDINARY_API_KEY,
@@ -20,7 +22,7 @@ export const foreignKeyReplacement = (input) => {
} }
export const enforceAlphaNumeric = (string) => export const enforceAlphaNumeric = (string) =>
string.replace(/([^a-zA-Z\d_:])/g, '-') (string || '').replace(/([^a-zA-Z\d_:])/g, '-')
export const generateUniqueString = async ( export const generateUniqueString = async (
seed, seed,
@@ -36,6 +38,26 @@ export const generateUniqueString = async (
return generateUniqueString(newSeed, isUniqueCallback, count) return generateUniqueString(newSeed, isUniqueCallback, count)
} }
export const generateUniqueStringWithoutSeed = async (
isUniqueCallback: (seed: string) => Promise<boolean>,
count = 0
) => {
const seed = humanId({
separator: '-',
capitalize: false,
})
const isUnique = !(await isUniqueCallback(seed))
if (isUnique) {
return seed
}
count += 1
if (count > 100) {
console.log('trouble finding unique')
return `very-unique-${seed}`.slice(0, 10)
}
return generateUniqueStringWithoutSeed(isUniqueCallback, count)
}
export const destroyImage = ({ publicId }) => export const destroyImage = ({ publicId }) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
cloudinary.uploader.destroy(publicId, (error, result) => { cloudinary.uploader.destroy(publicId, (error, result) => {

View File

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

View File

@@ -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 } }),
}

View File

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

View File

@@ -1,28 +1,28 @@
import { UserInputError } from '@redwoodjs/api' import { UserInputError } from '@redwoodjs/graphql-server'
import { requireAuth } from 'src/lib/auth' import { requireAuth } from 'src/lib/auth'
import { requireOwnership } from 'src/lib/owner' import { requireOwnership } from 'src/lib/owner'
import { db } from 'src/lib/db' import { db } from 'src/lib/db'
import { foreignKeyReplacement } from 'src/services/helpers' import { foreignKeyReplacement } from 'src/services/helpers'
export const partReactions = () => { export const projectReactions = () => {
return db.partReaction.findMany() return db.projectReaction.findMany()
} }
export const partReaction = ({ id }) => { export const projectReaction = ({ id }) => {
return db.partReaction.findUnique({ return db.projectReaction.findUnique({
where: { id }, where: { id },
}) })
} }
export const partReactionsByPartId = ({ partId }) => { export const projectReactionsByProjectId = ({ projectId }) => {
return db.partReaction.findMany({ return db.projectReaction.findMany({
where: { partId: partId }, where: { projectId },
}) })
} }
export const togglePartReaction = async ({ input }) => { export const toggleProjectReaction = async ({ input }) => {
// if write fails emote_userId_partId @@unique constraint, then delete it instead // if write fails emote_userId_projectId @@unique constraint, then delete it instead
requireAuth() requireAuth()
await requireOwnership({ userId: input?.userId }) 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 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
@@ -36,33 +36,33 @@ export const togglePartReaction = async ({ input }) => {
let dbPromise let dbPromise
const inputClone = { ...input } // TODO foreignKeyReplacement mutates input, which I should fix but am lazy right now const inputClone = { ...input } // TODO foreignKeyReplacement mutates input, which I should fix but am lazy right now
try { try {
dbPromise = await db.partReaction.create({ dbPromise = await db.projectReaction.create({
data: foreignKeyReplacement(input), data: foreignKeyReplacement(input),
}) })
} catch (e) { } catch (e) {
dbPromise = db.partReaction.delete({ dbPromise = db.projectReaction.delete({
where: { emote_userId_partId: inputClone }, where: { emote_userId_projectId: inputClone },
}) })
} }
return dbPromise return dbPromise
} }
export const updatePartReaction = ({ id, input }) => { export const updateProjectReaction = ({ id, input }) => {
return db.partReaction.update({ return db.projectReaction.update({
data: foreignKeyReplacement(input), data: foreignKeyReplacement(input),
where: { id }, where: { id },
}) })
} }
export const deletePartReaction = ({ id }) => { export const deleteProjectReaction = ({ id }) => {
return db.partReaction.delete({ return db.projectReaction.delete({
where: { id }, where: { id },
}) })
} }
export const PartReaction = { export const ProjectReaction = {
user: (_obj, { root }) => user: (_obj, { root }) =>
db.partReaction.findUnique({ where: { id: root.id } }).user(), db.projectReaction.findUnique({ where: { id: root.id } }).user(),
part: (_obj, { root }) => project: (_obj, { root }) =>
db.partReaction.findUnique({ where: { id: root.id } }).part(), db.projectReaction.findUnique({ where: { id: root.id } }).project(),
} }

View File

@@ -0,0 +1,294 @@
import { ResolverArgs } from '@redwoodjs/graphql-server'
import type { Prisma, Project as ProjectType } from '@prisma/client'
import { uploadImage, makeSocialPublicIdServer } from 'src/lib/cloudinary'
import { db } from 'src/lib/db'
import {
foreignKeyReplacement,
enforceAlphaNumeric,
generateUniqueString,
generateUniqueStringWithoutSeed,
destroyImage,
} from 'src/services/helpers'
import { requireAuth } from 'src/lib/auth'
import { requireOwnership, requireProjectOwnership } from 'src/lib/owner'
export const projects = ({ userName }) => {
if (!userName) {
return db.project.findMany({ where: { deleted: false } })
}
return db.project.findMany({
where: {
deleted: false,
user: {
userName,
},
},
})
}
export const project = ({ id }: Prisma.ProjectWhereUniqueInput) => {
return db.project.findUnique({
where: { id },
})
}
export const projectByUserAndTitle = async ({ userName, projectTitle }) => {
const user = await db.user.findUnique({
where: {
userName,
},
})
return db.project.findUnique({
where: {
title_userId: {
title: projectTitle,
userId: user.id,
},
},
})
}
const isUniqueProjectTitle =
(userId: string) =>
async (seed: string): Promise<boolean> =>
!!(await db.project.findUnique({
where: {
title_userId: {
title: seed,
userId,
},
},
}))
interface CreateProjectArgs {
input: Prisma.ProjectCreateArgs['data']
}
export const createProject = async ({ input }: CreateProjectArgs) => {
requireAuth()
console.log(input.userId)
const isUniqueCallback = isUniqueProjectTitle(input.userId)
let title = input.title
if (!title) {
title = await generateUniqueStringWithoutSeed(isUniqueCallback)
}
return db.project.create({
data: foreignKeyReplacement({
...input,
title,
}),
})
}
export const forkProject = async ({ input }) => {
requireAuth()
const projectData = await db.project.findUnique({
where: {
id: input.forkedFromId,
},
})
const isUniqueCallback = isUniqueProjectTitle(input.userId)
let title = projectData.title
title = await generateUniqueString(title, isUniqueCallback)
const { code, description, cadPackage } = projectData
return db.project.create({
data: foreignKeyReplacement({
...input,
title,
code: input.code || code,
description,
cadPackage,
}),
})
}
interface UpdateProjectArgs extends Prisma.ProjectWhereUniqueInput {
input: Prisma.ProjectUpdateInput
}
export const updateProject = async ({ id, input }: UpdateProjectArgs) => {
const checkSocialCardValidity = async (
projectId: string,
input: UpdateProjectArgs['input'],
oldProject: ProjectType
) => {
const titleChange = input.title && input.title !== oldProject.title
const descriptionChange =
input.description && input.description !== oldProject.description
if (titleChange || descriptionChange) {
const socialCard = await db.socialCard.findUnique({
where: { projectId },
})
if (socialCard) {
return db.socialCard.update({
data: { outOfDate: true },
where: { id: socialCard.id },
})
}
}
}
requireAuth()
const originalProject = await requireProjectOwnership({ projectId: id })
if (input.title) {
input.title = enforceAlphaNumeric(input.title)
}
const socialCardPromise = checkSocialCardValidity(id, input, originalProject)
const imageToDestroy =
originalProject.mainImage !== input.mainImage &&
input.mainImage &&
originalProject.mainImage
const update = await db.project.update({
data: foreignKeyReplacement(input),
where: { id },
})
if (imageToDestroy) {
console.log(
`image destroyed, publicId: ${imageToDestroy}, projectId: ${id}, replacing image is ${input.mainImage}`
)
// destroy after the db has been updated
await destroyImage({ publicId: imageToDestroy })
}
await socialCardPromise
return update
}
export const updateProjectImages = async ({
id,
mainImage64,
socialCard64,
}: {
id: string
mainImage64?: string
socialCard64?: string
}): Promise<ProjectType> => {
requireAuth()
const project = await requireProjectOwnership({ projectId: id })
const replaceSocialCard = async () => {
if (!socialCard64) {
return
}
let publicId = ''
let socialCardId = ''
try {
;({ id: socialCardId, url: publicId } = await db.socialCard.findUnique({
where: { projectId: id },
}))
} catch (e) {
const { userName } = await db.user.findUnique({
where: { id: project.userId },
})
publicId = makeSocialPublicIdServer(userName, project.title)
}
const imagePromise = uploadImage({
image64: socialCard64,
uploadPreset: 'CadHub_project_images',
publicId,
invalidate: true,
})
const saveOrUpdateSocialCard = () => {
const data = {
outOfDate: false,
url: publicId,
}
if (socialCardId) {
return db.socialCard.update({
data,
where: { projectId: id },
})
}
return db.socialCard.create({
data: {
...data,
project: {
connect: {
id: id,
},
},
},
})
}
const socialCardUpdatePromise = saveOrUpdateSocialCard()
const [socialCard] = await Promise.all([
socialCardUpdatePromise,
imagePromise,
])
return socialCard
}
const updateMainImage = async (): Promise<ProjectType> => {
if (!mainImage64) {
return project
}
const { public_id: mainImage } = await uploadImage({
image64: mainImage64,
uploadPreset: 'CadHub_project_images',
invalidate: true,
})
const projectPromise = db.project.update({
data: {
mainImage,
},
where: { id },
})
let imageDestroyPromise = new Promise((r) => r(null))
if (project.mainImage) {
console.log(
`image destroyed, publicId: ${project.mainImage}, projectId: ${id}, replacing image is ${mainImage}`
)
// destroy after the db has been updated
imageDestroyPromise = destroyImage({ publicId: project.mainImage })
}
const [updatedProject] = await Promise.all([
projectPromise,
imageDestroyPromise,
])
return updatedProject
}
const [updatedProject] = await Promise.all([
updateMainImage(),
replaceSocialCard(),
])
return updatedProject
}
export const deleteProject = async ({ id }: Prisma.ProjectWhereUniqueInput) => {
requireAuth()
await requireOwnership({ projectId: id })
const project = await db.project.findUnique({
where: { id },
})
const childrenDeletePromises = [
db.comment.deleteMany({ where: { projectId: project.id } }),
db.projectReaction.deleteMany({ where: { projectId: project.id } }),
db.socialCard.deleteMany({ where: { projectId: project.id } }),
]
await Promise.all(childrenDeletePromises)
await db.project.delete({
where: { id },
})
return project
}
export const Project = {
forkedFrom: (_obj, { root }) =>
root.forkedFromId &&
db.project.findUnique({ where: { id: root.forkedFromId } }),
childForks: (_obj, { root }) =>
db.project.findMany({ where: { forkedFromId: root.id } }),
user: (_obj, { root }: ResolverArgs<ReturnType<typeof project>>) =>
db.user.findUnique({ where: { id: root.userId } }),
socialCard: (_obj, { root }: ResolverArgs<ReturnType<typeof project>>) =>
db.project.findUnique({ where: { id: root.id } }).socialCard(),
Comment: (_obj, { root }: ResolverArgs<ReturnType<typeof project>>) =>
db.project
.findUnique({ where: { id: root.id } })
.Comment({ orderBy: { createdAt: 'desc' } }),
Reaction: (_obj, { root }: ResolverArgs<ReturnType<typeof project>>) =>
db.project
.findUnique({ where: { id: root.id } })
.Reaction({ where: { userId: _obj.userId } }),
}

View File

@@ -0,0 +1,25 @@
import { ResolverArgs, BeforeResolverSpecType } from '@redwoodjs/graphql-server'
import type { Prisma } from '@prisma/client'
import { db } from 'src/lib/db'
import { requireAuth } from 'src/lib/auth'
// Used when the environment variable REDWOOD_SECURE_SERVICES=1
export const beforeResolver = (rules: BeforeResolverSpecType) => {
rules.add(requireAuth)
}
export const socialCards = () => {
return db.socialCard.findMany()
}
export const socialCard = ({ id }: Prisma.SocialCardWhereUniqueInput) => {
return db.socialCard.findUnique({
where: { id },
})
}
export const SocialCard = {
project: (_obj, { root }: ResolverArgs<ReturnType<typeof socialCard>>) =>
db.socialCard.findUnique({ where: { id: root.id } }).project(),
}

View File

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

View File

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

View File

@@ -1,8 +1,30 @@
import { UserInputError, ForbiddenError } from '@redwoodjs/graphql-server'
import { db } from 'src/lib/db' import { db } from 'src/lib/db'
import { requireAuth } from 'src/lib/auth' import { requireAuth } from 'src/lib/auth'
import { requireOwnership } from 'src/lib/owner' import { requireOwnership } from 'src/lib/owner'
import { UserInputError } from '@redwoodjs/api'
import { enforceAlphaNumeric, destroyImage } from 'src/services/helpers' import { enforceAlphaNumeric, destroyImage } from 'src/services/helpers'
import type { Prisma } from '@prisma/client'
function userNameVerification(userName: string): string {
if (userName.length < 5) {
throw new ForbiddenError('userName too short')
}
if (userName && ['new', 'edit', 'update'].includes(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 `
)
}
if (userName) {
return enforceAlphaNumeric(userName)
}
}
function nameVerification(name: string) {
if (typeof name === 'string' && name.length < 3) {
throw new ForbiddenError('name too short')
}
}
export const users = () => { export const users = () => {
requireAuth({ role: 'admin' }) requireAuth({ role: 'admin' })
@@ -25,35 +47,54 @@ export const createUser = ({ input }) => {
requireAuth({ role: 'admin' }) requireAuth({ role: 'admin' })
createUserInsecure({ input }) createUserInsecure({ input })
} }
export const createUserInsecure = ({ input }) => { export const createUserInsecure = ({
input,
}: {
input: Prisma.UserUncheckedCreateInput
}) => {
if (typeof input.userName === 'string') {
input.userName = userNameVerification(input.userName)
}
nameVerification(input.name)
return db.user.create({ return db.user.create({
data: input, data: input,
}) })
} }
export const updateUser = ({ id, input }) => { export const updateUser = ({
id,
input,
}: {
id: string
input: Prisma.UserUncheckedCreateInput
}) => {
requireAuth() requireAuth()
if (typeof input.userName === 'string') {
input.userName = userNameVerification(input.userName)
}
nameVerification(input.name)
return db.user.update({ return db.user.update({
data: input, data: input,
where: { id }, where: { id },
}) })
} }
export const updateUserByUserName = async ({ userName, input }) => { export const updateUserByUserName = async ({
userName,
input,
}: {
userName: string
input: Prisma.UserUncheckedCreateInput
}) => {
requireAuth() requireAuth()
await requireOwnership({ userName }) await requireOwnership({ userName })
if (input.userName) { if (typeof input.userName === 'string') {
input.userName = enforceAlphaNumeric(input.userName) input.userName = userNameVerification(input.userName)
} }
if (input.userName && ['new', 'edit', 'update'].includes(input.userName)) { nameVerification(input.name)
//TODO complete this and use a regexp so that it's not case sensitive, don't want someone with the userName eDiT const originalProject = await db.user.findUnique({ where: { userName } })
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 = const imageToDestroy =
originalPart.image !== input.image && originalPart.image originalProject.image !== input.image && originalProject.image
const update = await db.user.update({ const update = await db.user.update({
data: input, data: input,
where: { userName }, where: { userName },
@@ -73,14 +114,14 @@ export const deleteUser = ({ id }) => {
} }
export const User = { export const User = {
Parts: (_obj, { root }) => Projects: (_obj, { root }) =>
db.user.findUnique({ where: { id: root.id } }).Part(), db.user.findUnique({ where: { id: root.id } }).Project(),
Part: (_obj, { root }) => Project: (_obj, { root }) =>
_obj.partTitle && _obj.projectTitle &&
db.part.findUnique({ db.project.findUnique({
where: { where: {
title_userId: { title_userId: {
title: _obj.partTitle, title: _obj.projectTitle,
userId: root.id, userId: root.id,
}, },
}, },

17
app/api/tsconfig.json Normal file
View File

@@ -0,0 +1,17 @@
{
"compilerOptions": {
"noEmit": true,
"allowJs": true,
"esModuleInterop": true,
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"baseUrl": "./",
"paths": {
"src/*": ["./src/*"]
},
"typeRoots": ["../node_modules/@types", "./node_modules/@types"],
"types": ["jest"]
},
"include": ["src", "../.redwood/**/*"]
}

View File

@@ -1,7 +1,5 @@
const { getConfig } = require('@redwoodjs/internal') const { getPaths } = require('@redwoodjs/internal')
const config = getConfig()
module.exports = { module.exports = {
schema: `http://${config.api.host}:${config.api.port}/graphql`, schema: getPaths().generated.schema,
} }

View File

@@ -4,7 +4,16 @@ publish = "web/dist"
functions = "api/dist/functions" functions = "api/dist/functions"
[dev] [dev]
command = "yarn rw dev" # To use [Netlify Dev](https://www.netlify.com/products/dev/),
# install netlify-cli from https://docs.netlify.com/cli/get-started/#installation
# and then use netlify link https://docs.netlify.com/cli/get-started/#link-and-unlink-sites
# to connect your local project to a site already on Netlify
# then run netlify dev and our app will be accessible on the port specified below
framework = "redwoodjs"
# Set targetPort to the [web] side port as defined in redwood.toml
targetPort = 8910
# Point your browser to this port to access your RedwoodJS app
port = 8888
[[redirects]] [[redirects]]
from = "/*" from = "/*"
@@ -13,3 +22,10 @@ functions = "api/dist/functions"
[context.deploy-preview.environment] [context.deploy-preview.environment]
CAD_LAMBDA_BASE_URL = "https://t7wdlz8ztf.execute-api.us-east-1.amazonaws.com/dev2" CAD_LAMBDA_BASE_URL = "https://t7wdlz8ztf.execute-api.us-east-1.amazonaws.com/dev2"
[[plugins]]
package = "@sentry/netlify-build-plugin"
[plugins.inputs]
sentryOrg = "kurt"
sentryProject = "kurt"

View File

@@ -6,19 +6,34 @@
"web" "web"
] ]
}, },
"scripts": {}, "scripts": {
"cad": "yarn rw build api && zip-it-and-ship-it api/dist/functions/ api/dist/zipball && docker-compose --file ./api/src/docker/docker-compose.yml up --build",
"cad-r": "yarn rw build api && zip-it-and-ship-it api/dist/functions/ api/dist/zipball && docker-compose --file ./api/src/docker/docker-compose.yml restart",
"aws-emulate": "nodemon ./api/src/docker/aws-emulator.js"
},
"devDependencies": { "devDependencies": {
"@redwoodjs/core": "^0.31.0" "@redwoodjs/core": "^0.38.1"
}, },
"eslintConfig": { "eslintConfig": {
"extends": "@redwoodjs/eslint-config" "extends": "@redwoodjs/eslint-config",
"rules": {
"react/no-unescaped-entities": [
"error",
{
"forbid": [
">",
"}",
"\""
]
}
]
}
}, },
"engines": { "engines": {
"node": ">=14", "node": ">=14.x <=16.x",
"yarn": ">=1.15" "yarn": ">=1.15"
}, },
"resolutions": { "prisma": {
"react": "17.0.1", "seed": "yarn rw exec seed"
"react-dom": "17.0.1"
} }
} }

View File

@@ -7,11 +7,27 @@
[web] [web]
port = 8910 port = 8910
apiProxyPath = "/.netlify/functions" title = 'CadHub'
includeEnvironmentVariables = ['GOOGLE_ANALYTICS_ID', 'CLOUDINARY_API_KEY', 'CLOUDINARY_API_SECRET', 'CAD_LAMBDA_BASE_URL'] # apiUrl = "/.netlify/functions"
apiUrl = "https://uk5gegwopd.execute-api.us-east-2.amazonaws.com/.netlify/functions"
includeEnvironmentVariables = [
'GOOGLE_ANALYTICS_ID',
'CLOUDINARY_API_KEY',
# 'CLOUDINARY_API_SECRET',
'CAD_LAMBDA_BASE_URL',
'SENTRY_DSN',
'SENTRY_AUTH_TOKEN',
'SENTRY_ORG',
'SENTRY_PROJECT',
# 'EMAIL_PASSWORD'
]
# experimentalFastRefresh = true # this seems to break cascadeStudio # experimentalFastRefresh = true # this seems to break cascadeStudio
[api] [api]
port = 8911 port = 8911
schemaPath = "./api/db/schema.prisma" schemaPath = "./api/db/schema.prisma"
[browser] [browser]
open = true open = true
[experimental]
esbuild = true

235
app/scripts/seed.ts Normal file
View File

@@ -0,0 +1,235 @@
import type { Prisma } from '@prisma/client'
import { db } from '$api/src/lib/db'
export default async () => {
try {
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 projects = [
{
title: 'demo-project1',
description: '# can be markdown',
mainImage: 'CadHub/kjdlgjnu0xmwksia7xox',
code: getOpenScadHingeCode(),
cadPackage: 'openscad',
user: {
connect: {
id: users[0].id,
},
},
},
{
title: 'demo-project2',
description: '## [hey](www.google.com)',
user: {
connect: {
id: users[1].id,
},
},
},
]
existing = await db.project.findMany({where: { title: projects[0].title}})
if(!existing.length) {
await db.project.create({
data: projects[0],
})
}
existing = await db.project.findMany({where: { title: projects[1].title}})
if(!existing.length) {
const result = await db.project.create({
data: projects[1],
})
await db.project.create({
data: {
...projects[1],
title: `${projects[1].title}-fork`,
forkedFrom: {
connect: {
id: result.id,
},
},
},
})
}
const aProject = await db.project.findUnique({where: {
title_userId: {
title: projects[0].title,
userId: users[0].id,
}
}})
await db.comment.create({
data: {
text: "nice project, I like it",
userId: users[0].id,
projectId: aProject.id,
// user: {connect: { id: users[0].id}},
// project: {connect: { id: aProject.id}},
}
})
await db.projectReaction.create({
data: {
emote: "❤️",
userId: users[0].id,
projectId: aProject.id,
// user: {connect: { id: users[0].id}},
// project: {connect: { id: aProject.id}},
}
})
} catch (error) {
console.warn('Please define your seed data.')
console.error(error)
}
}
function getOpenScadHingeCode () {
return `
baseWidth=15; // [0.1:0.1:50]
hingeLength=30; // [0.1:0.1:50]
// Hole mant mounting holes per half.
mountingHoleCount=3; // [1:20]
baseThickness=3; // [0.1:0.1:20]
pivotRadius=5; // [0.1:0.1:20]
// Pin that the hinge pivots on.
pinRadius=2; // [0.1:0.1:20]
mountingHoleRadius=1.5; // [0.1:0.1:10]
// How far away the hole is from the egde.
mountingHoleEdgeOffset=4; // [0:50]
// Depending on the accuracy of your printer this may need to be increased in order for print in place to work.
clearance=0.2; // [0.05:0.01:1]
// Radius difference in the ivot taper to stop the hinge from falling apart. Should be increased with large clearance values.
pinTaper=0.25; // [0.1:0.1:2]
// calculated values
hingeHalfExtrudeLength=hingeLength/2-clearance/2;
mountingHoleMoveIncrement=(hingeLength-2*mountingHoleEdgeOffset)/
(mountingHoleCount-1);
module costomizerEnd() {}
$fn=30;
tiny=0.005;
// modules
module hingeBaseProfile() {
translate([pivotRadius,0,0]){
square([baseWidth,baseThickness]);
}
}
module hingeBodyHalf() {
difference() {
union() {
linear_extrude(hingeHalfExtrudeLength){
offset(1)offset(-2)offset(1){
translate([0,pivotRadius,0]){
circle(pivotRadius);
}
square([pivotRadius,pivotRadius]);
hingeBaseProfile();
}
}
linear_extrude(hingeLength){
offset(1)offset(-1)hingeBaseProfile();
}
}
plateHoles();
}
}
module pin(rotateY, radiusOffset) {
translate([0,pivotRadius,hingeHalfExtrudeLength+tiny]){
rotate([0,rotateY,0]) {
cylinder(
h=hingeLength/2+clearance/2,
r1=pinRadius+radiusOffset,
r2=pinRadius+pinTaper+radiusOffset
);
}
}
}
module hingeHalfFemale() {
difference() {
hingeBodyHalf();
pin(rotateY=180, radiusOffset=clearance);
}
}
module hingeHalfMale() {
translate([0,0,hingeLength]) {
rotate([0,180,0]) {
hingeBodyHalf();
pin(rotateY=0, radiusOffset=0);
}
}
}
module plateHoles() {
for(i=[0:mountingHoleCount-1]){
translate([
baseWidth/2+pivotRadius,
-baseThickness,
i*mountingHoleMoveIncrement+mountingHoleEdgeOffset
]){
rotate([-90,0,0]){
cylinder(r=mountingHoleRadius,h=baseThickness*4);
}
}
}
}
// using high-level modules
translate([0,0,-15]) {
hingeHalfFemale();
hingeHalfMale();
}
`
}

188
app/serverless.yml Normal file
View File

@@ -0,0 +1,188 @@
# See the full yml reference at https://www.serverless.com/framework/docs/providers/aws/guide/serverless.yml/
service: cadhubapi
# Uncomment org and app if you want to integrate your deployment with the Serverless dashboard. See https://www.serverless.com/framework/docs/dashboard/ for more details.
# org: your-org
# app: your-app
plugins:
- serverless-dotenv-plugin
- serverless-binary-cors
- serverless-plugin-git-variables
custom:
dotenv:
include:
- DATABASE_URL_PROD
- CLOUDINARY_API_KEY
- CLOUDINARY_API_SECRET
- EMAIL_PASSWORD
- SENTRY_DSN
# - # List the environment variables you want to include from your .env file here.
provider:
name: aws
lambdaHashingVersion: 20201221
runtime: nodejs14.x
region: us-east-2 # This is the AWS region where the service will be deployed.
httpApi: # HTTP API is used by default. To learn about the available options in API Gateway, see https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html
cors: true
payload: '1.0'
stackTags: # Add CloudFormation stack tags here
source: serverless
name: Redwood Lambda API with HTTP API Gateway
tags: # Add service wide tags here
name: Redwood Lambda API with HTTP API Gateway
ecr:
images:
# this image is built locally and push to ECR
openscadimage:
path: ./
file: api/src/docker/openscad/Dockerfile
cadqueryimage:
path: ./
file: api/src/docker/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 */* :(
- '*/*'
package:
individually: true
functions:
check-user-name:
description: check-user-name function deployed on AWS Lambda
package:
artifact: api/dist/zipball/check-user-name.zip # This is the default location of the zip file generated during the deploy command.
memorySize: 1024 # mb
timeout: 25 # seconds (max: 29)
tags: # Tags for this specific lambda function
endpoint: /.netlify/functions/check-user-name
# Uncomment this section to add environment variables either from the Serverless dotenv plugin or using Serverless params
environment:
SENTRY_DSN: ${env:SENTRY_DSN}
DATABASE_URL: ${env:DATABASE_URL_PROD}
COMMIT_REF: ${git:sha1}
CONTEXT: TODO
handler: check-user-name.handler
events:
- httpApi:
path: /.netlify/functions/check-user-name
method: GET
# cors: true
- httpApi:
path: /.netlify/functions/check-user-name
method: POST
# cors: true
graphql:
description: graphql function deployed on AWS Lambda
package:
artifact: api/dist/zipball/graphql.zip # This is the default location of the zip file generated during the deploy command.
memorySize: 1024 # mb
timeout: 25 # seconds (max: 29)
tags: # Tags for this specific lambda function
endpoint: /.netlify/functions/graphql
# Uncomment this section to add environment variables either from the Serverless dotenv plugin or using Serverless params
environment:
CLOUDINARY_API_KEY: ${env:CLOUDINARY_API_KEY}
CLOUDINARY_API_SECRET: ${env:CLOUDINARY_API_SECRET}
EMAIL_PASSWORD: ${env:EMAIL_PASSWORD}
SENTRY_DSN: ${env:SENTRY_DSN}
DATABASE_URL: ${env:DATABASE_URL_PROD}
COMMIT_REF: ${git:sha1}
CONTEXT: TODO
# YOUR_FIRST_ENV_VARIABLE: ${env:YOUR_FIRST_ENV_VARIABLE}
handler: graphql.handler
events:
- httpApi:
path: /.netlify/functions/graphql
method: GET
# cors: true
- httpApi:
path: /.netlify/functions/graphql
method: POST
# cors: true
# identity-signup: # this is netlify specific and is related to go true auth, so we'll continue having that deployed on netlify
# description: identity-signup function deployed on AWS Lambda
# package:
# artifact: api/dist/zipball/identity-signup.zip # This is the default location of the zip file generated during the deploy command.
# memorySize: 1024 # mb
# timeout: 25 # seconds (max: 29)
# tags: # Tags for this specific lambda function
# endpoint: /.netlify/functions/identity-signup
# # Uncomment this section to add environment variables either from the Serverless dotenv plugin or using Serverless params
# # environment:
# # YOUR_FIRST_ENV_VARIABLE: ${env:YOUR_FIRST_ENV_VARIABLE}
# handler: identity-signup.handler
# events:
# - httpApi:
# path: /.netlify/functions/identity-signup
# method: GET
# - httpApi:
# path: /.netlify/functions/identity-signup
# method: POST
openscadpreview:
image:
name: openscadimage
command:
- openscad.preview
entryPoint:
- '/entrypoint.sh'
events:
- http:
path: openscad/preview
method: post
cors: true
timeout: 25
openscadstl:
image:
name: openscadimage
command:
- openscad.stl
entryPoint:
- '/entrypoint.sh'
events:
- http:
path: openscad/stl
method: post
cors: true
timeout: 30
cadquerystl:
image:
name: cadqueryimage
command:
- cadquery.stl
entryPoint:
- '/entrypoint.sh'
events:
- http:
path: cadquery/stl
method: post
cors: true
timeout: 30
# this allows browsers to see error responses.
resources:
Resources:
GatewayResponseDefault4XX:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
ResponseType: DEFAULT_4XX
RestApiId:
Ref: 'ApiGatewayRestApi'
GatewayResponseDefault5XX:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
ResponseType: DEFAULT_5XX
RestApiId:
Ref: 'ApiGatewayRestApi'

View File

@@ -2,7 +2,8 @@ const path = require('path')
module.exports = { module.exports = {
plugins: [ plugins: [
require('tailwindcss')(path.resolve(__dirname, '../tailwind.config.js')), require('postcss-import'),
require('tailwindcss')(path.resolve(__dirname, 'tailwind.config.js')),
require('autoprefixer'), require('autoprefixer'),
], ],
} }

View File

@@ -1,15 +1,75 @@
module.exports = { module.exports = {
purge: ['./src/**/*.html', './src/**/*.js', './src/**/*.ts', './src/**/*.tsx'], purge: ['src/**/*.{js,jsx,ts,tsx}'],
darkMode: false, // or 'media' or 'class' darkMode: false, // or 'media' or 'class'
theme: { theme: {
extend: { extend: {
backgroundImage: () => ({
texture: `url("data:image/svg+xml,%3Csvg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M11 18c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm48 25c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm-43-7c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm63 31c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM34 90c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm56-76c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM12 86c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm28-65c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm23-11c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-6 60c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm29 22c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zM32 63c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm57-13c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-9-21c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM60 91c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM35 41c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM12 60c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2z' fill='%236a49c3' fill-opacity='0.47' fill-rule='evenodd'/%3E%3C/svg%3E");`,
}),
animation: { animation: {
'bounce-sm-slow': 'bounce-sm 5s linear infinite', 'bounce-sm-slow': 'bounce-sm 5s linear infinite',
'twist-sm-slow': 'twist-sm 10s infinite', 'twist-sm-slow': 'twist-sm 10s infinite',
}, },
backgroundImage: () => ({
texture: `url("data:image/svg+xml,%3Csvg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M11 18c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm48 25c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm-43-7c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm63 31c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM34 90c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm56-76c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM12 86c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm28-65c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm23-11c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-6 60c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm29 22c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zM32 63c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm57-13c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-9-21c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM60 91c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM35 41c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM12 60c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2z' fill='%236a49c3' fill-opacity='0.47' fill-rule='evenodd'/%3E%3C/svg%3E");`,
}),
borderRadius: {
half: '50%',
},
boxShadow: {
ch: '0 4px 4px 0 rgba(0, 0, 0, 0.25), 0 4px 4px 0 rgba(13, 13, 19, 0.15)',
},
colors: {
'ch-gray': {
900: '#0D0D13',
800: '#1A1A1D',
750: '#222222',
760: '#232532',
710: '#2B303C', // TODO: Use HSL so I stop adding grays to fix the warm/cool problem
700: '#2A3038',
600: '#3B3E4B',
550: '#63636A',
500: '#9F9FB4',
400: '#A4A4B0',
300: '#CFCFD8',
},
'ch-purple': {
400: '#3B0480',
450: '#671BC6',
500: '#8732F2',
600: '#A663FA',
200: '#C99DFF',
},
'ch-purple-gray': {
200: '#DBDBEC',
},
'ch-blue': {
700: '#08466F',
650: '#0958BA',
640: '#0A57B5',
630: '#3285EB',
500: '#5098F1',
400: '#79B2F8',
300: '#9BC8FF',
},
'ch-pink': {
800: '#93064F',
500: '#DF5CA0',
300: '#F98CC5',
},
},
cursor: {
grab: 'grab'
},
fontFamily: {
'ropa-sans': ['"Ropa Sans"', 'Arial', 'sans-serif'],
roboto: ['Roboto', 'Arial', 'sans-serif'],
'fira-code': ['"Fira Code"', 'monospace'],
'fira-sans': ['"Fira Sans"', 'sans-serif'],
},
gridAutoColumns: {
'preview-layout': 'minmax(30rem, 1fr) minmax(auto, 2fr)',
},
gridTemplateColumns: {
'profile-layout': 'minmax(32rem, 1fr) 2fr',
},
keyframes: { keyframes: {
'bounce-sm': { 'bounce-sm': {
'0%, 100%': { '0%, 100%': {
@@ -38,18 +98,16 @@ module.exports = {
minHeight: { minHeight: {
md: '28rem', md: '28rem',
}, },
fontFamily: { outline: {
'ropa-sans': ['Ropa Sans', 'Arial', 'sans-serif'], gray: ['2px solid #3B3E4B', '8px'],
roboto: ['Roboto', 'Arial', 'sans-serif'],
}, },
skew: { skew: {
'-20': '-20deg', '-20': '-20deg',
}, },
borderRadius: {
half: '50%',
},
}, },
}, },
variants: {}, variants: {},
plugins: [], plugins: [
require('@tailwindcss/aspect-ratio'),
],
} }

View File

@@ -1,92 +1,13 @@
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
module.exports = (config, { env }) => { module.exports = (config, { env }) => {
config.plugins.forEach((plugin) => { config.plugins.forEach((plugin) => {
if (plugin.constructor.name === 'HtmlWebpackPlugin') { if (plugin.constructor.name === 'HtmlWebpackPlugin') {
plugin.options.favicon = './src/favicon.svg' plugin.userOptions.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( config.module.rules.push({
new MonacoWebpackPlugin({ test: /\.(md|jscad\.js|py|scad)$/i,
languages: ['typescript'], use: 'raw-loader',
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 return config
} }

10
app/web/config/worker-loader.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
declare module "worker-loader!*" {
// You need to change `Worker`, if you specified a different value for the `workerType` option
class WebpackWorker extends Worker {
constructor();
}
// Uncomment this if you set the `esModule` option to `false`
// export = WebpackWorker;
export default WebpackWorker;
}

View File

@@ -1,6 +1 @@
const { getConfig } = require('@redwoodjs/core') module.exports = require('@redwoodjs/testing/config/jest/api')
const config = getConfig({ type: 'jest', target: 'browser' })
config.displayName.name = 'web'
module.exports = config

View File

@@ -13,43 +13,53 @@
] ]
}, },
"dependencies": { "dependencies": {
"@headlessui/react": "^1.0.0", "@headlessui/react": "^1.4.1",
"@heroicons/react": "^1.0.4",
"@material-ui/core": "^4.11.0", "@material-ui/core": "^4.11.0",
"@monaco-editor/react": "^4.0.11", "@monaco-editor/react": "^4.0.11",
"@redwoodjs/auth": "^0.31.0", "@react-three/drei": "^7.3.1",
"@redwoodjs/forms": "^0.31.0", "@react-three/fiber": "^7.0.5",
"@redwoodjs/router": "^0.31.0", "@react-three/postprocessing": "^2.0.5",
"@redwoodjs/web": "^0.31.0", "@redwoodjs/auth": "^0.38.1",
"@redwoodjs/forms": "^0.38.1",
"@redwoodjs/router": "^0.38.1",
"@redwoodjs/web": "^0.38.1",
"@sentry/browser": "^6.5.1",
"@tailwindcss/aspect-ratio": "0.2.1",
"axios": "^0.21.1",
"browser-fs-access": "^0.17.2",
"cloudinary-react": "^1.6.7", "cloudinary-react": "^1.6.7",
"controlkit": "^0.1.9",
"get-active-classes": "^0.0.11", "get-active-classes": "^0.0.11",
"golden-layout": "^1.5.9",
"gotrue-js": "^0.9.27", "gotrue-js": "^0.9.27",
"jquery": "^3.5.1", "hotkeys-js": "^3.8.7",
"monaco-editor": "^0.20.0", "html-to-image": "^1.7.0",
"monaco-editor-webpack-plugin": "^1.9.1", "lodash": "^4.17.21",
"netlify-identity-widget": "^1.9.1", "netlify-identity-widget": "^1.9.1",
"opencascade.js": "^0.1.15", "pako": "^2.0.3",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react": "^17.0.1", "react": "^17.0.2",
"react-dom": "^17.0.1", "react-dom": "^17.0.2",
"react-dropzone": "^11.2.1", "react-dropzone": "^11.2.1",
"react-ga": "^3.3.0", "react-ga": "^3.3.0",
"react-helmet": "^6.1.0", "react-helmet": "^6.1.0",
"react-hotkeys-hook": "^3.4.0",
"react-image-crop": "^8.6.6", "react-image-crop": "^8.6.6",
"react-mosaic-component": "^4.1.1", "react-intersection-observer": "^8.32.1",
"react-three-fiber": "^5.3.19", "react-mosaic-component": "^5.0.0",
"react-tabs": "^3.2.2",
"rich-markdown-editor": "^11.0.2", "rich-markdown-editor": "^11.0.2",
"styled-components": "^5.2.0", "styled-components": "^5.2.0",
"three": "^0.118.3" "three": "^0.130.1",
"worker-loader": "^3.0.8"
}, },
"devDependencies": { "devDependencies": {
"postcss": "^8.2.13", "@types/lodash": "^4.14.170",
"autoprefixer": "^10.2.5", "autoprefixer": "^10.3.1",
"html-webpack-plugin": "^4.5.0", "html-webpack-plugin": "^4.5.0",
"opentype.js": "^1.3.3", "postcss": "^8.3.6",
"postcss-loader": "4.0.2", "postcss-import": "^14.0.2",
"tailwindcss": "^2.1.2", "postcss-loader": "^6.1.1",
"worker-loader": "^3.0.7" "raw-loader": "^4.0.2",
"tailwindcss": "^2.2.7"
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
app/web/public/hinge.stl Normal file

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More