solving lots of production issues

This commit is contained in:
Yeicor
2024-03-03 13:16:51 +01:00
parent 71887f75e6
commit 0fad65d7b2
11 changed files with 51 additions and 30 deletions

1
.gitignore vendored
View File

@@ -3,6 +3,7 @@
# Useful .gitignore templates: https://github.com/github/gitignore
/node_modules/
/dist/
/yacv_server/frontend/
/.cache/
/.parcel-cache/
/.idea/

7
build.py Normal file
View File

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

View File

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

View File

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

View File

@@ -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');

View File

@@ -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) {

View File

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

View File

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

View File

@@ -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 :x1="line.start2D[0]" :y1="line.start2D[1]" :x2="line.end2D[0]"
:y2="line.end2D[1]" v-bind="line.lineAttrs"/>
<rect :x="(line.start2D[0] + line.end2D[0]) / 2 - line.centerTextSize[0]/2 - 4"
:y="(line.start2D[1] + line.end2D[1]) / 2 - line.centerTextSize[1]/2 - 4"
:width="line.centerTextSize[0] + 8" :height="line.centerTextSize[1] - 4"
:y="(line.start2D[1] + line.end2D[1]) / 2 - line.centerTextSize[1]/2 - 2"
:width="line.centerTextSize[0] + 8" :height="line.centerTextSize[1] + 4"
fill="gray" fill-opacity="0.75" rx="4" ry="4" stroke="black" v-if="line.centerText"/>
<text :x="(line.start2D[0] + line.end2D[0]) / 2" :y="(line.start2D[1] + line.end2D[1]) / 2"
text-anchor="middle" alignment-baseline="middle" font-size="16" fill="black"
text-anchor="middle" dominant-baseline="middle" font-size="16" fill="black"
:class="'line' + lineId + '_text'" v-if="line.centerText">
{{ line.centerText }}
</text>

View File

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

View File

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