From 9d22fe8413882d5a71efefbdf3474a473cd3122e Mon Sep 17 00:00:00 2001 From: goynov Date: Thu, 5 Feb 2026 13:50:06 +0200 Subject: [PATCH] refactor Utils package --- backend/app/Utils.js | 114 ++++++++++++++++++ backend/app/bl/UserManager.js | 9 +- backend/controllers/api/UsersController.js | 3 +- .../MazeQuizGame/MazeQuizGame.js | 2 +- .../InteractiveObjects/PairMatchingGame.js | 2 +- .../InteractiveObjects/SingleQuestion.js | 2 +- src/components/SceneDesigner/Scene.vue | 2 +- .../SceneDesigner/SceneDesigner.vue | 2 +- src/components/SceneDesigner/SvgRectangle.vue | 2 +- src/components/SceneDesigner/Task.vue | 2 +- src/lib/Dashboard.js | 2 +- vite.config.mjs | 3 +- 12 files changed, 131 insertions(+), 14 deletions(-) create mode 100644 backend/app/Utils.js diff --git a/backend/app/Utils.js b/backend/app/Utils.js new file mode 100644 index 0000000..d28ca2e --- /dev/null +++ b/backend/app/Utils.js @@ -0,0 +1,114 @@ +const epsilon = 0.1; + +const Utils = { + blobToBase64: blob => { + const reader = new FileReader(); + reader.readAsDataURL(blob); + return new Promise(resolve => { + reader.onloadend = () => { + resolve(reader.result); + }; + }); + }, + + adjustMinMax(r){ + return { + x1: Math.min(r.x1, r.x2), x2: Math.max(r.x1, r.x2), + y1: Math.min(r.y1, r.y2), y2: Math.max(r.y1, r.y2) + } + }, + + intersectPointRect(p, r){ + //r = this.adjustMinMax(r); + return p[0] >= r.x1 && p[0] <= r.x2 && p[1] >= r.y1 && p[1] <= r.y2; + }, + + intersectPointLine(p, l){ + //l = this.adjustMinMax(l); + let dx = l.x2 - l.x1, dy = l.y2 - l.y1; + let c = dy / dx; + return this.intersectPointRect(p, l) && c * p[0] - p[1] <= epsilon + }, + + intersectLineRect(l, r){ + return this.intersectPointRect([l.x1, l.y1], r) || + this.intersectPointRect([l.x2, l.y2], r); + }, + + intersectRectRect(r1, r2){ + return this.intersectPointRect([r1.x1, r1.y1], r2) || + this.intersectPointRect([r1.x1, r1.y2], r2) || + this.intersectPointRect([r1.x2, r1.y1], r2) || + this.intersectPointRect([r1.x2, r1.y2], r2); + }, + + round(n, p = 2){ + let pp = Math.pow(10, p); + return Math.round(n*pp)/pp; + }, + + deg2rad(deg){ + return deg * (Math.PI / 180); + }, + + rad2deg(rad){ + return rad * 180 / Math.PI; + }, + + shuffleArray(arr){ + return arr.map(value => ({ value, sort: Math.random() })) + .sort((a, b) => a.sort - b.sort).map(({ value }) => value) + }, + + deepMerge(target, source, transformFn) { + Object.entries(source).forEach(([key, value]) => { + if (transformFn){ + value = transformFn(key, value) + } + if (value && typeof value === 'object') { + let dflt = Array.isArray(value) ? [] : {}; + this.deepMerge(target[key] = target[key] || dflt, value, transformFn); + return; + } + target[key] = value; + }); + return target; + }, + + drawOnCanvas(svg, width, height){ + return new Promise((resolve, reject)=>{ + let url = URL.createObjectURL(new Blob([svg],{ type:"image/svg+xml;charset=utf-8" })); + let img = new Image(); + let canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + let ctx = canvas.getContext('2d'); + + img.addEventListener('load', function () { + ctx.drawImage(this, 0, 0, canvas.width, canvas.height); + URL.revokeObjectURL(url); + resolve(canvas); + }, { once: true }) + + img.src = url; + }) + }, + + async wait(ms){ + await new Promise((resolve, reject)=>{ + setTimeout(resolve, ms) + }) + }, + + async waitFor(expFn){ + while (!expFn()){ + await Utils.wait(200); + } + }, + + escapeRegExp(string) { + return string && string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string + }, +} + +export default Utils; \ No newline at end of file diff --git a/backend/app/bl/UserManager.js b/backend/app/bl/UserManager.js index 2c4bdea..c420781 100644 --- a/backend/app/bl/UserManager.js +++ b/backend/app/bl/UserManager.js @@ -5,6 +5,7 @@ import { v4 as uuidv4 } from 'uuid'; import { Strategy as LocalStrategy } from 'passport-local'; import { Strategy as FacebookStrategy } from 'passport-facebook'; import { Strategy as GoogleStrategy } from 'passport-google-oauth20'; +import Utils from '../Utils.js'; const collection = 'users'; const emailRegexp = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; @@ -31,16 +32,16 @@ class UserManager { passport.use(new LocalStrategy({ usernameField: 'email', passwordField: 'password' }, async function (email, password, done) { - let user = await db.get(collection, { email: { $regex: `^${global.JsUtils.escapeRegExp(email)}$`, $options: 'i' } }); + let user = await db.get(collection, { email: { $regex: `^${Utils.escapeRegExp(email)}$`, $options: 'i' } }); if (user) { if (md5(md5(password) + config.am.salt) == user.password) { return done(null, am.getUserProfile(user)); } else { - await global.JsUtils.wait(3000); + await Utils.wait(3000); return done(null, false, { message: 'invalidPassword' }); } } else { - await global.JsUtils.wait(3000); + await Utils.wait(3000); return done(null, false, { message: 'invalidUsername' }); } } @@ -130,7 +131,7 @@ class UserManager { if (!emailRegexp.test(data.email)) { return reject(new Error('invalidEmail')); } - let exists = await db.get(collection, { email: { $regex: `^${global.JsUtils.escapeRegExp(data.email)}$`, $options: 'i' } }); + let exists = await db.get(collection, { email: { $regex: `^${Utils.escapeRegExp(data.email)}$`, $options: 'i' } }); if (exists) { return reject(new Error('emailExists')) } diff --git a/backend/controllers/api/UsersController.js b/backend/controllers/api/UsersController.js index c24e75d..db370c0 100644 --- a/backend/controllers/api/UsersController.js +++ b/backend/controllers/api/UsersController.js @@ -1,5 +1,6 @@ import express from 'express'; import svgCaptcha from 'svg-captcha'; +import Utils from '../../app/Utils.js'; const collection = 'users'; @@ -138,7 +139,7 @@ class UsersController { limit: req.body.limit || 12, skip: req.body.skip || 0 }; if (req.body.email) { - q.query.email = { $regex: global.JsUtils.escapeRegExp(req.body.email), $options: 'i' } + q.query.email = { $regex: Utils.escapeRegExp(req.body.email), $options: 'i' } } let list = await db.list(collection, q); res.json(list); diff --git a/src/components/InteractiveObjects/MazeQuizGame/MazeQuizGame.js b/src/components/InteractiveObjects/MazeQuizGame/MazeQuizGame.js index 95dd794..9cef755 100644 --- a/src/components/InteractiveObjects/MazeQuizGame/MazeQuizGame.js +++ b/src/components/InteractiveObjects/MazeQuizGame/MazeQuizGame.js @@ -1,5 +1,5 @@ import { MazeObject } from "./MazeObject"; -import Utils from "@/lib/Utils"; +import Utils from "#/app/Utils"; import { EventManager } from '@/lib/EventManager'; const params = { diff --git a/src/components/InteractiveObjects/PairMatchingGame.js b/src/components/InteractiveObjects/PairMatchingGame.js index c12e7bb..ddfd403 100644 --- a/src/components/InteractiveObjects/PairMatchingGame.js +++ b/src/components/InteractiveObjects/PairMatchingGame.js @@ -2,7 +2,7 @@ import { BoxGeometry, Mesh, MeshStandardMaterial, Group, Vector3, CatmullRomCurv import { LineMaterial, LineGeometry, Line2 } from 'three/examples/jsm/Addons.js'; import { centerOrigin } from '@/lib/MeshUtils'; import { EventManager } from '@/lib/EventManager'; -import Utils from '@/lib/Utils'; +import Utils from '#/app/Utils'; class PairMatchingGame extends EventManager { emits = ['finish', 'interaction'] diff --git a/src/components/InteractiveObjects/SingleQuestion.js b/src/components/InteractiveObjects/SingleQuestion.js index 677e440..fcc190f 100644 --- a/src/components/InteractiveObjects/SingleQuestion.js +++ b/src/components/InteractiveObjects/SingleQuestion.js @@ -2,7 +2,7 @@ import { Group, Color } from 'three'; import { centerOrigin } from '@/lib/MeshUtils'; import { EventManager } from '@/lib/EventManager'; import { TextObject } from './TextObject'; -import Utils from '@/lib/Utils'; +import Utils from '#/app/Utils'; class SingleQuestion extends EventManager { emits = ['finish'] diff --git a/src/components/SceneDesigner/Scene.vue b/src/components/SceneDesigner/Scene.vue index d1cc84f..6c77bcc 100644 --- a/src/components/SceneDesigner/Scene.vue +++ b/src/components/SceneDesigner/Scene.vue @@ -46,7 +46,7 @@