mirror of
https://github.com/yeicor-3d/yet-another-cad-viewer.git
synced 2025-12-19 14:14:13 +01:00
solving lots of production issues
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -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
7
build.py
Normal 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)
|
||||
@@ -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": {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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');
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user