Merge pull request #380 from Irev-Dev/main

Release 25th June 2021
This commit was merged in pull request #380.
This commit is contained in:
Kurt Hutten
2021-06-25 19:48:46 +10:00
committed by GitHub
19 changed files with 1431 additions and 1862 deletions

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

@@ -0,0 +1,5 @@
{
"cSpell.words": [
"Hutten"
]
}

View File

@@ -9,7 +9,7 @@ Let's help Code-CAD reach its [full potential!](https://cadhub.xyz) We're making
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 [Road Map](https://github.com/Irev-Dev/cadhub/discussions/212) and get in touch via, [twitter](https://twitter.com/IrevDev), [discord](https://discord.gg/SD7zFRNjGH) or [discussions](https://github.com/Irev-Dev/cadhub/discussions).
<img src="https://raw.githubusercontent.com/Irev-Dev/repo-images/main/images/fullcadhubshot.jpg"> <img src="https://raw.githubusercontent.com/Irev-Dev/repo-images/main/images/fullcadhubshot.jpg">
<img src="https://raw.githubusercontent.com/Irev-Dev/cadhub/main/docs/static/img/blog/curated-code-cad/CadHubSS.jpg">
## Getting your dev environment setup ## Getting your dev environment setup

View File

@@ -5,7 +5,6 @@
"dependencies": { "dependencies": {
"@redwoodjs/api": "^0.34.1", "@redwoodjs/api": "^0.34.1",
"@sentry/node": "^6.5.1", "@sentry/node": "^6.5.1",
"cloudinary": "^1.23.0", "cloudinary": "^1.23.0"
"graphql-tag": "^2.12.4"
} }
} }

View File

@@ -26,7 +26,7 @@ app.post('/cadquery/stl', async (req, res) => {
console.log('making post request to 5060') console.log('making post request to 5060')
try { try {
const { data } = await axios.post(invocationURL(5060), { const { data } = await axios.post(invocationURL(5060), {
body: req.body body: JSON.stringify(req.body),
}) })
res.status(data.statusCode) res.status(data.statusCode)
res.send(data.body) res.send(data.body)

View File

@@ -36,7 +36,10 @@ RUN npm install
# 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 wget https://github.com/CadQuery/cq-cli/releases/download/v2.2-beta.1/cq-cli-Linux-x86_64.zip
# Comment the entry above out and uncomment the one below to revert to the stable release
# RUN wget https://github.com/CadQuery/cq-cli/releases/download/v2.1.0/cq-cli-Linux-x86_64.zip
RUN unzip cq-cli-Linux-x86_64.zip RUN unzip cq-cli-Linux-x86_64.zip
RUN chmod +x cq-cli/cq-cli RUN chmod +x cq-cli/cq-cli

View File

@@ -144,3 +144,23 @@ functions:
# NewOutput: # NewOutput:
# Description: "Description for the output" # Description: "Description for the output"
# Value: "Some output value" # Value: "Some output value"
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

@@ -17,9 +17,7 @@ export const handler = createGraphQLHandler({
schemas, schemas,
services: makeServices({ services }), services: makeServices({ services }),
}), }),
plugins: [ plugins: [createSentryApolloPlugin()],
createSentryApolloPlugin(),
],
onException: () => { onException: () => {
// Disconnect from your database with an unhandled exception. // Disconnect from your database with an unhandled exception.
db.$disconnect() db.$disconnect()

View File

@@ -12,6 +12,9 @@ export const requireOwnership = async ({ userId, userName, partId } = {}) => {
} }
if (context.currentUser.roles?.includes('admin')) { 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 return
} }

View File

@@ -62,45 +62,44 @@ export const createSentryApolloPlugin: Config['plugins'][number] = () => ({
// If we couldn't parse the operation, don't // If we couldn't parse the operation, don't
// do anything here // do anything here
if (!ctx.operation) { if (!ctx.operation) {
return; return
} }
for (const err of ctx.errors) { for (const err of ctx.errors) {
// Only report internal server errors, // Only report internal server errors,
// all errors extending ApolloError should be user-facing // all errors extending ApolloError should be user-facing
if (err instanceof ApolloError) { if (err instanceof ApolloError) {
continue; continue
} }
// Add scoped report details and send to Sentry // Add scoped report details and send to Sentry
Sentry.withScope(scope => { Sentry.withScope((scope) => {
// Annotate whether failing operation was query/mutation/subscription // Annotate whether failing operation was query/mutation/subscription
scope.setTag("kind", ctx.operation.operation); scope.setTag('kind', ctx.operation.operation)
// Log query and variables as extras (make sure to strip out sensitive data!) // Log query and variables as extras (make sure to strip out sensitive data!)
scope.setExtra("query", ctx.request.query); scope.setExtra('query', ctx.request.query)
scope.setExtra("variables", ctx.request.variables); scope.setExtra('variables', ctx.request.variables)
if (err.path) { if (err.path) {
// We can also add the path as breadcrumb // We can also add the path as breadcrumb
scope.addBreadcrumb({ scope.addBreadcrumb({
category: "query-path", category: 'query-path',
message: err.path.join(" > "), message: err.path.join(' > '),
level: Sentry.Severity.Debug level: Sentry.Severity.Debug,
}); })
} }
const transactionId = ctx.request.http.headers.get( const transactionId =
"x-transaction-id" ctx.request.http.headers.get('x-transaction-id')
);
if (transactionId) { if (transactionId) {
scope.setTransaction(transactionId); scope.setTransaction(transactionId)
} }
Sentry.captureException(err); Sentry.captureException(err)
}); })
} }
} },
} }
} },
}) })

View File

@@ -77,13 +77,13 @@ export const updatePart = async ({ id, input }) => {
} }
const originalPart = await db.part.findUnique({ where: { id } }) const originalPart = await db.part.findUnique({ where: { id } })
const imageToDestroy = const imageToDestroy =
originalPart.mainImage !== input.mainImage && originalPart.mainImage originalPart.mainImage !== input.mainImage && input.mainImage && originalPart.mainImage
const update = await db.part.update({ const update = await db.part.update({
data: foreignKeyReplacement(input), data: foreignKeyReplacement(input),
where: { id }, where: { id },
}) })
if (imageToDestroy) { if (imageToDestroy) {
console.log(`image destroyed, publicId: ${imageToDestroy}, partId: ${id}`) console.log(`image destroyed, publicId: ${imageToDestroy}, partId: ${id}, replacing image is ${input.mainImage}`)
// destroy after the db has been updated // destroy after the db has been updated
destroyImage({ publicId: imageToDestroy }) destroyImage({ publicId: imageToDestroy })
} }

View File

@@ -22,7 +22,7 @@ const SmallLoadingPing = (
) )
const BigLoadingPing = () => ( const BigLoadingPing = () => (
<div className="inset-0 absolute flex items-center justify-center"> <div className="inset-0 absolute flex items-center justify-center bg-ch-gray-800">
<div className="h-16 w-16 bg-pink-600 rounded-full animate-ping"></div> <div className="h-16 w-16 bg-pink-600 rounded-full animate-ping"></div>
</div> </div>
) )

View File

@@ -89,7 +89,7 @@ const LandingSection = () => {
<li>Build your own helper functions and abstractions</li> <li>Build your own helper functions and abstractions</li>
<li> <li>
Trigger{' '} Trigger{' '}
<QuickLink to="https://en.wikipedia.org/wiki/Finite_element_method"> <QuickLink to="https://learn.cadhub.xyz/blog/testing-code-cad">
FEM FEM
</QuickLink>{' '} </QuickLink>{' '}
or regenerate tool paths with a{' '} or regenerate tool paths with a{' '}
@@ -100,7 +100,7 @@ const LandingSection = () => {
</li> </li>
<li> <li>
Auto-generate a{' '} Auto-generate a{' '}
<QuickLink to="https://en.wikipedia.org/wiki/Bill_of_materials"> <QuickLink to="https://learn.cadhub.xyz/blog/codecad-artifacts">
BOM BOM
</QuickLink> </QuickLink>
</li> </li>
@@ -111,6 +111,11 @@ const LandingSection = () => {
</QuickLink>{' '} </QuickLink>{' '}
tools tools
</li> </li>
<li>
<QuickLink to="https://learn.cadhub.xyz/blog/3d-diffs">
3d-diffs
</QuickLink>
</li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@@ -13,6 +13,7 @@ const OutBound = ({ className, children, to }) => {
}) })
return true return true
}} }}
rel="noreferrer"
> >
{children} {children}
</a> </a>

View File

@@ -27,7 +27,7 @@ const PartProfile = ({
const [isInvalid, setIsInvalid] = useState(false) const [isInvalid, setIsInvalid] = useState(false)
const { currentUser } = useAuth() const { currentUser } = useAuth()
const editorRef = useRef(null) const editorRef = useRef(null)
const canEdit = currentUser?.sub === userPart.id const canEdit = currentUser?.sub === userPart.id || currentUser?.roles.includes('admin')
const isImageEditable = !isEditable && canEdit // image is editable when not in profile edit mode in order to separate them as it's too hard too to upload an image to cloudinary temporarily until the use saves (and maybe have to clean up) for the time being const isImageEditable = !isEditable && canEdit // image is editable when not in profile edit mode in order to separate them as it's too hard too to upload an image to cloudinary temporarily until the use saves (and maybe have to clean up) for the time being
const part = userPart?.Part const part = userPart?.Part
const emotes = countEmotes(part?.Reaction) const emotes = countEmotes(part?.Reaction)

View File

@@ -3,6 +3,7 @@ import {
stlToGeometry, stlToGeometry,
createHealthyResponse, createHealthyResponse,
createUnhealthyResponse, createUnhealthyResponse,
timeoutErrorMessage,
} from './common' } from './common'
export const render = async ({ code }) => { export const render = async ({ code }) => {
@@ -29,6 +30,9 @@ export const render = async ({ code }) => {
}, },
} }
} }
if (response.status === 502) {
return createUnhealthyResponse(new Date(), timeoutErrorMessage)
}
const data = await response.json() const data = await response.json()
const geometry = await stlToGeometry(data.url) const geometry = await stlToGeometry(data.url)
return createHealthyResponse({ return createHealthyResponse({

View File

@@ -2,7 +2,7 @@ import { STLLoader } from 'three/examples/jsm/loaders/STLLoader'
export const lambdaBaseURL = export const lambdaBaseURL =
process.env.CAD_LAMBDA_BASE_URL || process.env.CAD_LAMBDA_BASE_URL ||
'https://oxt2p7ddgj.execute-api.us-east-1.amazonaws.com/prod' 'https://2inlbple1b.execute-api.us-east-1.amazonaws.com/prod2'
export const stlToGeometry = (url) => export const stlToGeometry = (url) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
@@ -38,3 +38,5 @@ export function createUnhealthyResponse(date, message = 'network issue') {
}, },
} }
} }
export const timeoutErrorMessage = `timeout: We're currently limited a 30s execution time. You can try again, sometimes it works the second time`

View File

@@ -3,6 +3,7 @@ import {
stlToGeometry, stlToGeometry,
createHealthyResponse, createHealthyResponse,
createUnhealthyResponse, createUnhealthyResponse,
timeoutErrorMessage,
} from './common' } from './common'
export const render = async ({ code, settings }) => { export const render = async ({ code, settings }) => {
@@ -49,6 +50,9 @@ export const render = async ({ code, settings }) => {
const cleanedErrorMessage = cleanError(error) const cleanedErrorMessage = cleanError(error)
return createUnhealthyResponse(new Date(), cleanedErrorMessage) return createUnhealthyResponse(new Date(), cleanedErrorMessage)
} }
if (response.status === 502) {
return createUnhealthyResponse(new Date(), timeoutErrorMessage)
}
const data = await response.json() const data = await response.json()
const type = data.type !== 'stl' ? 'png' : 'geometry' const type = data.type !== 'stl' ? 'png' : 'geometry'
const newData = data.type !== 'stl' ? data.url : stlToGeometry(data.url) const newData = data.type !== 'stl' ? data.url : stlToGeometry(data.url)
@@ -81,6 +85,9 @@ export const stl = async ({ code, settings }) => {
const cleanedErrorMessage = cleanError(error) const cleanedErrorMessage = cleanError(error)
return createUnhealthyResponse(new Date(), cleanedErrorMessage) return createUnhealthyResponse(new Date(), cleanedErrorMessage)
} }
if (response.status === 502) {
return createUnhealthyResponse(new Date(), timeoutErrorMessage)
}
const data = await response.json() const data = await response.json()
const geometry = await stlToGeometry(data.url) const geometry = await stlToGeometry(data.url)
return createHealthyResponse({ return createHealthyResponse({

File diff suppressed because it is too large Load Diff

View File

@@ -83,18 +83,14 @@ export default function Home() {
<li>Build your own helper functions and abstractions</li> <li>Build your own helper functions and abstractions</li>
<li> <li>
Trigger{' '} Trigger{' '}
<a href="https://en.wikipedia.org/wiki/Finite_element_method"> <a href="/blog/testing-code-cad">
FEM FEM
</a>{' '} </a>{' '}
or regenerate tool paths with a{' '} or regenerate tool paths with a CI/CD process
<a href="https://www.redhat.com/en/topics/devops/what-is-ci-cd">
CI/CD
</a>{' '}
process
</li> </li>
<li> <li>
Auto-generate a{' '} Auto-generate a{' '}
<a href="https://en.wikipedia.org/wiki/Bill_of_materials"> <a href="/blog/codecad-artifacts">
BOM BOM
</a> </a>
</li> </li>
@@ -103,6 +99,11 @@ export default function Home() {
<a href="https://www.ptc.com/en/technologies/plm">PLM</a>{' '} <a href="https://www.ptc.com/en/technologies/plm">PLM</a>{' '}
tools tools
</li> </li>
<li>
<a href="/blog/3d-diffs">
3d-diffs
</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>