Skip to content

Commit

Permalink
Merge pull request #36 from oo-bldrs/refactor-server-endpoints
Browse files Browse the repository at this point in the history
Refactor server endpoints
  • Loading branch information
pablo-mayrgundter committed Nov 16, 2023
2 parents 4481042 + 1638d8b commit 1b56e37
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 71 deletions.
2 changes: 1 addition & 1 deletion esbuild/build.esb.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const externalPackages = [
// TODO(pablo): this builds for me, but the bundle isn't running yet.
esbuild
.build({
entryPoints: ['./src/server.js'],
entryPoints: ['./src/server/index.js'],
outfile: './build/server-bundle.js',
//outdir: 'build',
bundle: true,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@babel/core": "^7.18.10",
"@babel/preset-env": "^7.22.10",
"@babel/preset-typescript": "^7.23.2",
"@jest-mock/express": "^2.0.2",
"babel-jest": "^28.1.3",
"esbuild": "^0.18.16",
"jest": "^29.6.2",
Expand Down
5 changes: 5 additions & 0 deletions src/server/healthcheck.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const healthcheckHandler = (req, res) => {
res.status(200).send()
}

export default healthcheckHandler
13 changes: 13 additions & 0 deletions src/server/healthcheck.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import healthcheckHandler from './healthcheck.js';
import { getMockRes } from '@jest-mock/express';

describe('/healthcheck', () => {
it('should always return a 200 ok', () => {
const req = {}
const { res } = getMockRes()

healthcheckHandler(req, res)

expect(res.status).toHaveBeenLastCalledWith(200)
})
})
76 changes: 6 additions & 70 deletions src/server.js → src/server/index.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import express from 'express'
import * as Sentry from '@sentry/node'
import {
fitModelToFrame,
initThree,
render,
captureScreenshot,
parseCamera,
} from "./lib.js"
import {parseUrl} from './urls.js'
import {load} from './Loader.js'
import debug, {INFO} from './debug.js'
import {createTaggedLogger} from './logging'
import debug, {INFO} from '../debug.js'
import {createTaggedLogger} from '../logging.js'
import renderHandler from './render.js'
import healthcheckHandler from './healthcheck.js'


const renderLogger = createTaggedLogger('/render')
Expand Down Expand Up @@ -81,69 +74,12 @@ function loggingHandler(req, res, next) {
app.use(loggingHandler)


app.post('/render', handler)
app.get('/healthcheck', (req, res) => {
res.status(200).send()
})
app.get('/healthcheck', healthcheckHandler)
app.post('/render', renderHandler)
// Install Sentry error handler after all routes but before any other error handlers
app.use(Sentry.Handlers.errorHandler())


app.listen(port, () => {
debug(INFO).log(`Listening on 0.0.0.0:${port}`)
})


async function handler(req, res) {
const [glCtx, renderer, scene, camera] = initThree()
const modelUrl = new URL(req.body.url)
const parsedUrl = parseUrl(modelUrl)
renderLogger.log('debug', 'server#post, parsedUrl:', parsedUrl)
if (parsedUrl.target === undefined) {
renderLogger.warn(msg)
res.status(404).send(`Cannot parse URL: ${modelUrl}`).end()
return
}
const [px, py, pz, tx, ty, tz] = parsedUrl.params.c ? parseCamera(parsedUrl.params.c) : [0,0,0,0,0,0]
const targetUrl = parsedUrl.target.url
let model
try {
model = await load(targetUrl)
} catch (e) {
const msg = `Internal server error ${e}`
renderLogger.error(msg)
res.status(500).send(msg)
return
}
if (model === undefined) {
const msg = `Could not load model for unknown reason`
renderLogger.error(msg)
res.status(500).send(msg)
return
}

// renderLogger.log('server#post, model:', model)
scene.add(model)

if (parsedUrl.params.c) {
const [px, py, pz, tx, ty, tz] = parseCamera(parsedUrl.params.c) || [0,0,0,0,0,0]
renderLogger.log('debug', `headless#camera setting: camera.pos(${px}, ${py}, ${pz}) target.pos(${tx}, ${ty}, ${tz})`)
if (isFinite(px) && isFinite(py) && isFinite(pz)) {
camera.position.set(px, py, pz)
}
if (isFinite(tx) && isFinite(ty) && isFinite(tz)) {
renderLogger.log('debug', `server#post, camera.pos(${px}, ${py}, ${pz}) target.pos(${tx}, ${ty}, ${tz})`)
camera.lookAt(tx, ty, tz)
} else {
renderLogger.log('debug', `server#post, camera.pos(${px}, ${py}, ${pz}) target.pos(0, 0, 0)`)
camera.lookAt(0, 0, 0)
}
} else {
fitModelToFrame(renderer.domElement, scene, model, camera)
}

const useSsaa = false
render(renderer, scene, camera, useSsaa)
res.setHeader('content-type', 'image/png')
captureScreenshot(glCtx).pipe(res)
}
59 changes: 59 additions & 0 deletions src/server/render.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { captureScreenshot, fitModelToFrame, initThree, parseCamera, render } from '../lib.js';
import { parseUrl } from '../urls.js';
import { load } from '../Loader.js';

const renderHandler = async (req, res) => {
const [glCtx, renderer, scene, camera] = initThree()
const modelUrl = new URL(req.body.url)
const parsedUrl = parseUrl(modelUrl)
renderLogger.log('debug', 'server#post, parsedUrl:', parsedUrl)
if (parsedUrl.target === undefined) {
renderLogger.warn(msg)
res.status(404).send(`Cannot parse URL: ${modelUrl}`).end()
return
}
const [px, py, pz, tx, ty, tz] = parsedUrl.params.c ? parseCamera(parsedUrl.params.c) : [0,0,0,0,0,0]
const targetUrl = parsedUrl.target.url
let model
try {
model = await load(targetUrl)
} catch (e) {
const msg = `Internal server error ${e}`
renderLogger.error(msg)
res.status(500).send(msg)
return
}
if (model === undefined) {
const msg = `Could not load model for unknown reason`
renderLogger.error(msg)
res.status(500).send(msg)
return
}

// renderLogger.log('server#post, model:', model)
scene.add(model)

if (parsedUrl.params.c) {
const [px, py, pz, tx, ty, tz] = parseCamera(parsedUrl.params.c) || [0,0,0,0,0,0]
renderLogger.log('debug', `headless#camera setting: camera.pos(${px}, ${py}, ${pz}) target.pos(${tx}, ${ty}, ${tz})`)
if (isFinite(px) && isFinite(py) && isFinite(pz)) {
camera.position.set(px, py, pz)
}
if (isFinite(tx) && isFinite(ty) && isFinite(tz)) {
renderLogger.log('debug', `server#post, camera.pos(${px}, ${py}, ${pz}) target.pos(${tx}, ${ty}, ${tz})`)
camera.lookAt(tx, ty, tz)
} else {
renderLogger.log('debug', `server#post, camera.pos(${px}, ${py}, ${pz}) target.pos(0, 0, 0)`)
camera.lookAt(0, 0, 0)
}
} else {
fitModelToFrame(renderer.domElement, scene, model, camera)
}

const useSsaa = false
render(renderer, scene, camera, useSsaa)
res.setHeader('content-type', 'image/png')
captureScreenshot(glCtx).pipe(res)
}

export default renderHandler
84 changes: 84 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1133,6 +1133,13 @@
resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz"
integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==

"@jest-mock/express@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@jest-mock/express/-/express-2.0.2.tgz#e4f61c30b45e517c14b35ea5d67d89a7e52908f7"
integrity sha512-B1mjh5Tgm/HDd3BLC9s2jZNqRIxiJJD5rMWm48gEeK0K2hfUE66QZ+AxHxHlb/uaqL9H+PFJzCSjJPl46oNzDg==
dependencies:
"@types/express" "^4.17.17"

"@jest/console@^29.6.2":
version "29.6.2"
resolved "https://registry.npmjs.org/@jest/console/-/console-29.6.2.tgz"
Expand Down Expand Up @@ -1544,6 +1551,21 @@
dependencies:
"@babel/types" "^7.20.7"

"@types/body-parser@*":
version "1.19.5"
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4"
integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==
dependencies:
"@types/connect" "*"
"@types/node" "*"

"@types/connect@*":
version "3.4.38"
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858"
integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==
dependencies:
"@types/node" "*"

"@types/cookie@^0.4.1":
version "0.4.1"
resolved "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz"
Expand All @@ -1556,13 +1578,38 @@
dependencies:
"@types/ms" "*"

"@types/express-serve-static-core@^4.17.33":
version "4.17.41"
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz#5077defa630c2e8d28aa9ffc2c01c157c305bef6"
integrity sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==
dependencies:
"@types/node" "*"
"@types/qs" "*"
"@types/range-parser" "*"
"@types/send" "*"

"@types/express@^4.17.17":
version "4.17.21"
resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d"
integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==
dependencies:
"@types/body-parser" "*"
"@types/express-serve-static-core" "^4.17.33"
"@types/qs" "*"
"@types/serve-static" "*"

"@types/graceful-fs@^4.1.3":
version "4.1.6"
resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz"
integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==
dependencies:
"@types/node" "*"

"@types/http-errors@*":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f"
integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==

"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
version "2.0.4"
resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz"
Expand Down Expand Up @@ -1596,6 +1643,16 @@
"@types/tough-cookie" "*"
parse5 "^7.0.0"

"@types/mime@*":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45"
integrity sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==

"@types/mime@^1":
version "1.3.5"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690"
integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==

"@types/ms@*":
version "0.7.32"
resolved "https://registry.npmjs.org/@types/ms/-/ms-0.7.32.tgz"
Expand All @@ -1606,6 +1663,33 @@
resolved "https://registry.npmjs.org/@types/node/-/node-20.4.8.tgz"
integrity sha512-0mHckf6D2DiIAzh8fM8f3HQCvMKDpK94YQ0DSVkfWTG9BZleYIWudw9cJxX8oCk9bM+vAkDyujDV6dmKHbvQpg==

"@types/qs@*":
version "6.9.10"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.10.tgz#0af26845b5067e1c9a622658a51f60a3934d51e8"
integrity sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==

"@types/range-parser@*":
version "1.2.7"
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb"
integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==

"@types/send@*":
version "0.17.4"
resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a"
integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==
dependencies:
"@types/mime" "^1"
"@types/node" "*"

"@types/serve-static@*":
version "1.15.5"
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.5.tgz#15e67500ec40789a1e8c9defc2d32a896f05b033"
integrity sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==
dependencies:
"@types/http-errors" "*"
"@types/mime" "*"
"@types/node" "*"

"@types/set-cookie-parser@^2.4.0":
version "2.4.4"
resolved "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.4.tgz"
Expand Down

0 comments on commit 1b56e37

Please sign in to comment.