From 0fad65d7b2a0f6c2d9d3d681d9d060e1d8103955 Mon Sep 17 00:00:00 2001
From: Yeicor <4929005+Yeicor@users.noreply.github.com>
Date: Sun, 3 Mar 2024 13:16:51 +0100
Subject: [PATCH] solving lots of production issues
---
.gitignore | 1 +
build.py | 7 +++++++
package.json | 2 +-
pyproject.toml | 10 +++++++++-
src/index.ts | 7 +------
src/misc/network.ts | 12 +++++-------
src/misc/scene.ts | 3 ++-
src/misc/settings.ts | 13 +++++++------
src/viewer/ModelViewerWrapper.vue | 8 ++++----
yacv_server/__init__.py | 7 ++++---
yacv_server/server.py | 11 ++++++++++-
11 files changed, 51 insertions(+), 30 deletions(-)
create mode 100644 build.py
diff --git a/.gitignore b/.gitignore
index 10b49b1..b2604fb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
# Useful .gitignore templates: https://github.com/github/gitignore
/node_modules/
/dist/
+/yacv_server/frontend/
/.cache/
/.parcel-cache/
/.idea/
diff --git a/build.py b/build.py
new file mode 100644
index 0000000..4586cfa
--- /dev/null
+++ b/build.py
@@ -0,0 +1,7 @@
+import os
+import subprocess
+
+if __name__ == "__main__":
+ # When building the backend, make sure the frontend is built first
+ subprocess.run(['yarn', 'install'], check=True)
+ subprocess.run(['yarn', 'build', '--dist-dir', 'yacv_server/frontend'], check=True)
diff --git a/package.json b/package.json
index e89f76d..2279c41 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
"author": "Yeicor",
"scripts": {
"start": "parcel src/index.html",
- "build": "yarn update-licenses && parcel build src/index.html --reporter @parcel/reporter-bundle-analyzer --detailed-report --public-url ./",
+ "build": "yarn update-licenses && parcel build src/index.html --reporter @parcel/reporter-bundle-analyzer --detailed-report --public-url ./ --no-scope-hoist",
"update-licenses": "generate-license-file --input package.json --output assets/licenses.txt --overwrite"
},
"dependencies": {
diff --git a/pyproject.toml b/pyproject.toml
index 5a8ad1a..265d039 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,10 +1,14 @@
[tool.poetry]
name = "yacv-server"
-version = "0.1.0"
+version = "0.1.0" # TODO: Update automatically by CI on release
description = "Yet Another CAD Viewer (server)"
authors = ["Yeicor <4929005+Yeicor@users.noreply.github.com>"]
license = "MIT"
readme = "README.md"
+include = [
+ { path = 'yacv_server/frontend/*', format = 'wheel' },
+ { path = 'yacv_server/frontend/*', format = 'sdist' },
+]
[tool.poetry.dependencies]
python = "^3.9"
@@ -22,6 +26,10 @@ aiohttp-devtools = "^1.1.2"
pygltflib = "^1.16.1"
pillow = "^10.2.0"
+[tool.poetry.build]
+generate-setup-file = false
+script = "build.py"
+
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
diff --git a/src/index.ts b/src/index.ts
index 2ad02cd..54728f2 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -2,13 +2,12 @@ globalThis.__VUE_OPTIONS_API__ = process.env.NODE_ENV === "development"
globalThis.__VUE_PROD_DEVTOOLS__ = process.env.NODE_ENV === "development"
globalThis.__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ = process.env.NODE_ENV === "development"
-// import {createApp} from 'vue/dist/vue.esm-browser.prod.js'
-// import {createApp} from 'vue/dist/vue.esm-browser.js'
import {createApp} from 'vue'
import App from './App.vue'
import {createVuetify} from 'vuetify';
import * as directives from 'vuetify/lib/directives';
+import 'vuetify/dist/vuetify.css';
const vuetify = createVuetify({
directives,
@@ -20,7 +19,3 @@ const vuetify = createVuetify({
const app = createApp(App)
app.use(vuetify)
app.mount('body')
-
-// Start non-blocking loading of Vuetify styles
-// @ts-ignore
-import('vuetify/dist/vuetify.css');
\ No newline at end of file
diff --git a/src/misc/network.ts b/src/misc/network.ts
index f0d7ce0..7fe2a6b 100644
--- a/src/misc/network.ts
+++ b/src/misc/network.ts
@@ -39,6 +39,7 @@ export class NetworkManager extends EventTarget {
}
private monitorWebSocket(url: string) {
+ // WARNING: This will spam the console logs with failed requests when the server is down
let ws = new WebSocket(url);
ws.onmessage = (event) => {
let data = JSON.parse(event.data);
@@ -48,13 +49,10 @@ export class NetworkManager extends EventTarget {
urlObj.searchParams.set("api_object", data.name);
this.foundModel(data.name, data.hash, urlObj.toString());
};
- ws.onerror = (event) => {
- console.error("WebSocket error", event);
- }
- ws.onclose = () => {
- console.debug("WebSocket closed, reconnecting very soon");
- setTimeout(() => this.monitorWebSocket(url), settings.checkServerEveryMs);
- }
+ ws.onerror = () => ws.close();
+ ws.onclose = () => setTimeout(() => this.monitorWebSocket(url), settings.monitorEveryMs);
+ let timeoutFaster = setTimeout(() => ws.close(), settings.monitorOpenTimeoutMs);
+ ws.onopen = () => clearTimeout(timeoutFaster);
}
private foundModel(name: string, hash: string, url: string) {
diff --git a/src/misc/scene.ts b/src/misc/scene.ts
index aca86c9..b9f9a29 100644
--- a/src/misc/scene.ts
+++ b/src/misc/scene.ts
@@ -1,4 +1,4 @@
-import {Ref, ShallowRef} from 'vue';
+import {Ref} from 'vue';
import {Document} from '@gltf-transform/core';
import {extrasNameKey, extrasNameValueHelpers, mergeFinalize, mergePartial, removeModel, toBuffer} from "./gltf";
import {newAxes, newGridBox} from "./helpers";
@@ -19,6 +19,7 @@ export class SceneMgr {
if (name !== extrasNameValueHelpers) {
// Reload the helpers to fit the new model
+ // TODO: Only reload the helpers after a few milliseconds of no more models being added/removed
document = await this.reloadHelpers(sceneUrl, document);
} else {
// Display the final fully loaded model
diff --git a/src/misc/settings.ts b/src/misc/settings.ts
index 32a5790..9ffd27e 100644
--- a/src/misc/settings.ts
+++ b/src/misc/settings.ts
@@ -2,18 +2,19 @@
export const settings = {
preloadModels: [
// @ts-ignore
- new URL('../../assets/fox.glb', import.meta.url).href,
+ // new URL('../../assets/fox.glb', import.meta.url).href,
// @ts-ignore
- new URL('../../assets/logo_build/base.glb', import.meta.url).href,
+ // new URL('../../assets/logo_build/base.glb', import.meta.url).href,
// @ts-ignore
- new URL('../../assets/logo_build/location.glb', import.meta.url).href,
+ // new URL('../../assets/logo_build/location.glb', import.meta.url).href,
// @ts-ignore
- new URL('../../assets/logo_build/img.jpg.glb', import.meta.url).href,
+ // new URL('../../assets/logo_build/img.jpg.glb', import.meta.url).href,
// Websocket URLs automatically listen for new models from the python backend
- // "ws://192.168.1.132:32323/"
+ "ws://127.0.0.1:32323/"
],
displayLoadingEveryMs: 1000, /* How often to display partially loaded models */
- checkServerEveryMs: 100, /* How often to check for a new server */
+ monitorEveryMs: 100,
+ monitorOpenTimeoutMs: 10000,
// ModelViewer settings
autoplay: true,
arModes: 'webxr scene-viewer quick-look',
diff --git a/src/viewer/ModelViewerWrapper.vue b/src/viewer/ModelViewerWrapper.vue
index 526c0d9..4970c75 100644
--- a/src/viewer/ModelViewerWrapper.vue
+++ b/src/viewer/ModelViewerWrapper.vue
@@ -28,7 +28,7 @@ onMounted(() => {
// Delete the initial load banner
let banner = elem.value.querySelector('.initial-load-banner');
if (banner) banner.remove();
- // Set the scene
+ // Set the scene and renderer
scene.value = elem.value[$scene] as ModelScene;
renderer.value = elem.value[$renderer] as Renderer;
// Emit the load event
@@ -162,11 +162,11 @@ watch(disableTap, (value) => {
{{ line.centerText }}
diff --git a/yacv_server/__init__.py b/yacv_server/__init__.py
index 9ce7175..c687eb7 100644
--- a/yacv_server/__init__.py
+++ b/yacv_server/__init__.py
@@ -26,11 +26,12 @@ show_all = server.show_cad_all
def _get_app() -> web.Application:
"""Required by aiohttp-devtools"""
logging.basicConfig(level=logging.DEBUG)
- from logo import build_logo
+ from logo import build_logo, ASSETS_DIR
logo, img_location, img_path = build_logo()
- server.show_cad(logo, 'Logo')
- server.show_cad(img_location, 'Location')
+ server.show_cad(logo, 'logo')
+ server.show_cad(img_location, 'location')
server.show_image(img_path, img_location, 20)
+ server.show_gltf(open(os.path.join(ASSETS_DIR, 'fox.glb'), 'rb').read(), 'fox')
return server.app
diff --git a/yacv_server/server.py b/yacv_server/server.py
index 23727fe..36c97fe 100644
--- a/yacv_server/server.py
+++ b/yacv_server/server.py
@@ -20,7 +20,16 @@ from mylogger import logger
from pubsub import BufferedPubSub
from tessellate import _hashcode, tessellate
-FRONTEND_BASE_PATH = os.getenv('FRONTEND_BASE_PATH', '../dist')
+# Find the frontend folder (optional, but recommended)
+FILE_DIR = os.path.dirname(__file__)
+FRONTEND_BASE_PATH = os.getenv('FRONTEND_BASE_PATH', os.path.join(FILE_DIR, 'frontend'))
+if not os.path.exists(FRONTEND_BASE_PATH):
+ if os.path.exists(os.path.join(FILE_DIR, '..', 'dist')): # Fallback to dev build
+ FRONTEND_BASE_PATH = os.path.join(FILE_DIR, '..', 'dist')
+ else:
+ logger.warning('Frontend not found at %s', FRONTEND_BASE_PATH)
+
+# Define the API paths (also available at the root path for simplicity)
UPDATES_API_PATH = '/api/updates'
OBJECTS_API_PATH = '/api/object' # /{name}