Browse Source

refactor: Move shared logic to a common directory

Owen Diffey 3 months ago
parent
commit
77f6ab3379

+ 1 - 0
.dockerignore

@@ -4,6 +4,7 @@
 !.git/HEAD
 !.git/refs/
 
+!common/
 !types/
 !backend/
 !frontend/

+ 2 - 1
backend/Dockerfile

@@ -14,10 +14,11 @@ ARG BACKEND_MODE=production
 ENV CONTAINER_MODE=${CONTAINER_MODE}
 ENV BACKEND_MODE=${BACKEND_MODE}
 
-RUN mkdir -p /opt/.git /opt/types /opt/app
+RUN mkdir -p /opt/.git /opt/common /opt/types /opt/app
 WORKDIR /opt/app
 
 COPY .git /opt/.git
+COPY common /opt/common
 COPY types /opt/types
 COPY backend /opt/app
 COPY --from=backend_node_modules /opt/app/node_modules node_modules

+ 1 - 1
backend/src/BaseModule.ts

@@ -1,9 +1,9 @@
 import { readdir } from "fs/promises";
 import path from "path";
+import { forEachIn } from "@common/utils/forEachIn";
 import LogBook, { Log } from "@/LogBook";
 import ModuleManager from "@/ModuleManager";
 import Job from "./Job";
-import { forEachIn } from "@/utils/forEachIn";
 
 export enum ModuleStatus {
 	LOADED = "LOADED",

+ 2 - 2
backend/src/Job.ts

@@ -1,11 +1,11 @@
 import { SessionSchema } from "@models/sessions/schema";
+import { getErrorMessage } from "@common/utils/getErrorMessage";
+import { generateUuid } from "@common/utils/generateUuid";
 import JobContext from "@/JobContext";
 import JobStatistics, { JobStatisticsType } from "@/JobStatistics";
 import LogBook, { Log } from "@/LogBook";
 import BaseModule from "./BaseModule";
 import EventsModule from "./modules/EventsModule";
-import { getErrorMessage } from "./utils/getErrorMessage";
-import { generateUuid } from "@/utils/generateUuid";
 
 export enum JobStatus {
 	QUEUED = "QUEUED",

+ 1 - 1
backend/src/modules/CacheModule.ts

@@ -8,8 +8,8 @@ import {
 	RedisScripts,
 	createClient
 } from "redis";
+import { forEachIn } from "@common/utils/forEachIn";
 import BaseModule, { ModuleStatus } from "@/BaseModule";
-import { forEachIn } from "@/utils/forEachIn";
 
 export class CacheModule extends BaseModule {
 	private _redisClient?: RedisClientType<

+ 1 - 1
backend/src/modules/DataModule.ts

@@ -4,6 +4,7 @@ import { patchHistoryPlugin, patchEventEmitter } from "ts-patch-mongoose";
 import { readdir } from "fs/promises";
 import path from "path";
 import updateVersioningPlugin from "mongoose-update-versioning";
+import { forEachIn } from "@common/utils/forEachIn";
 import Migration from "@/modules/DataModule/Migration";
 import documentVersionPlugin from "@/modules/DataModule/plugins/documentVersion";
 import getDataPlugin from "@/modules/DataModule/plugins/getData";
@@ -11,7 +12,6 @@ import BaseModule, { ModuleStatus } from "@/BaseModule";
 import EventsModule from "./EventsModule";
 import DataModuleJob from "./DataModule/DataModuleJob";
 import Job from "@/Job";
-import { forEachIn } from "@/utils/forEachIn";
 
 export class DataModule extends BaseModule {
 	private _models?: Record<string, Model<any>>;

+ 1 - 1
backend/src/modules/DataModule/DataModuleJob.ts

@@ -1,8 +1,8 @@
 import { HydratedDocument, Model, isObjectIdOrHexString } from "mongoose";
+import { forEachIn } from "@common/utils/forEachIn";
 import Job, { JobOptions } from "@/Job";
 import DataModule from "../DataModule";
 import { UserSchema } from "./models/users/schema";
-import { forEachIn } from "@/utils/forEachIn";
 
 export default abstract class DataModuleJob extends Job {
 	protected static _modelName: string;

+ 1 - 1
backend/src/modules/DataModule/models/stations/jobs/Index.ts

@@ -1,11 +1,11 @@
 import { HydratedDocument } from "mongoose";
+import { forEachIn } from "@common/utils/forEachIn";
 import DataModule from "@/modules/DataModule";
 import DataModuleJob from "@/modules/DataModule/DataModuleJob";
 import isDj from "@/modules/DataModule/permissions/isDj";
 import isOwner from "@/modules/DataModule/permissions/isOwner";
 import isPublic from "@/modules/DataModule/permissions/isPublic";
 import { StationModel, StationSchema } from "../schema";
-import { forEachIn } from "@/utils/forEachIn";
 
 export default class Index extends DataModuleJob {
 	protected static _modelName = "stations";

+ 1 - 1
backend/src/modules/DataModule/models/users/jobs/GetModelPermissions.ts

@@ -1,10 +1,10 @@
 import { isObjectIdOrHexString } from "mongoose";
+import { forEachIn } from "@common/utils/forEachIn";
 import CacheModule from "@/modules/CacheModule";
 import DataModule from "@/modules/DataModule";
 import ModuleManager from "@/ModuleManager";
 import GetPermissions, { GetPermissionsResult } from "./GetPermissions";
 import DataModuleJob from "@/modules/DataModule/DataModuleJob";
-import { forEachIn } from "@/utils/forEachIn";
 
 export type GetModelPermissionsResult = Record<string, boolean>;
 

+ 1 - 1
backend/src/modules/EventsModule.ts

@@ -8,9 +8,9 @@ import {
 	RedisScripts
 } from "redis";
 import config from "config";
+import { forEachIn } from "@common/utils/forEachIn";
 import BaseModule, { ModuleStatus } from "@/BaseModule";
 import WebSocketModule from "./WebSocketModule";
-import { forEachIn } from "@/utils/forEachIn";
 
 export class EventsModule extends BaseModule {
 	private _pubClient?: RedisClientType<

+ 1 - 1
backend/src/modules/EventsModule/jobs/SubscribeMany.ts

@@ -1,6 +1,6 @@
+import { forEachIn } from "@common/utils/forEachIn";
 import Job, { JobOptions } from "@/Job";
 import EventsModule from "@/modules/EventsModule";
-import { forEachIn } from "@/utils/forEachIn";
 
 export default class SubscribeMany extends Job {
 	public constructor(payload?: unknown, options?: JobOptions) {

+ 2 - 2
backend/src/modules/WebSocketModule.ts

@@ -3,6 +3,8 @@ import express from "express";
 import http, { Server, IncomingMessage } from "node:http";
 import { RawData, WebSocketServer } from "ws";
 import { Types, isObjectIdOrHexString } from "mongoose";
+import { forEachIn } from "@common/utils/forEachIn";
+import { getErrorMessage } from "@common/utils/getErrorMessage";
 import BaseModule from "@/BaseModule";
 import WebSocket from "@/WebSocket";
 import ModuleManager from "@/ModuleManager";
@@ -11,8 +13,6 @@ import DataModule from "./DataModule";
 import { UserModel } from "./DataModule/models/users/schema";
 import { SessionModel } from "./DataModule/models/sessions/schema";
 import EventsModule from "./EventsModule";
-import { forEachIn } from "@/utils/forEachIn";
-import { getErrorMessage } from "@/utils/getErrorMessage";
 
 export class WebSocketModule extends BaseModule {
 	private _httpServer?: Server;

+ 2 - 1
backend/tsconfig.json

@@ -32,7 +32,8 @@
 		"paths": {
 			/* Specify a set of entries that re-map imports to additional lookup locations. */
 			"@/*": ["src/*"],
-			"@models/*": ["src/modules/DataModule/models/*"]
+			"@models/*": ["src/modules/DataModule/models/*"],
+			"@common/*": ["../common/*"]
 		},
 		// "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
 		// "typeRoots": [],                                  /* Specify multiple folders that act like './node_modules/@types'. */

+ 0 - 0
backend/src/utils/forEachIn.ts → common/utils/forEachIn.ts


+ 0 - 0
backend/src/utils/generateUuid.ts → common/utils/generateUuid.ts


+ 0 - 0
backend/src/utils/getErrorMessage.ts → common/utils/getErrorMessage.ts


+ 2 - 0
docker-compose.dev.yml

@@ -5,6 +5,7 @@ services:
       - "${BACKEND_HOST:-0.0.0.0}:${BACKEND_DEBUG_PORT:-9229}:9229"
     volumes:
       - ./.git:/opt/.git:ro
+      - ./common:/opt/common
       - ./types:/opt/types
       - ./backend:/opt/app
     environment:
@@ -13,6 +14,7 @@ services:
   frontend:
     volumes:
       - ./.git:/opt/.git:ro
+      - ./common:/opt/common
       - ./types:/opt/types
       - ./frontend:/opt/app
 

+ 2 - 1
frontend/Dockerfile

@@ -31,10 +31,11 @@ ENV FRONTEND_MODE=${FRONTEND_MODE} \
 
 RUN apt-get update && apt-get install nginx -y
 
-RUN mkdir -p /opt/.git /opt/types /opt/app /run/nginx
+RUN mkdir -p /opt/.git /opt/common /opt/types /opt/app /run/nginx
 WORKDIR /opt/app
 
 COPY .git /opt/.git
+COPY common /opt/common
 COPY types /opt/types
 COPY frontend /opt/app
 COPY --from=frontend_node_modules /opt/app/node_modules node_modules

+ 2 - 2
frontend/src/aw.ts

@@ -1,5 +1,5 @@
 import Toast from "toasters";
-import utils from "@/utils";
+import { generateUuid } from "@common/utils/generateUuid";
 
 let gotPong = false;
 let pingTries = 0;
@@ -73,7 +73,7 @@ export default {
 
 	enable() {
 		if (!enabled) {
-			uuid = utils.guid();
+			uuid = generateUuid();
 
 			document.addEventListener(
 				"ActivityWatchMusareEvent",

+ 2 - 2
frontend/src/classes/SocketHandler.class.ts

@@ -1,7 +1,7 @@
+import { generateUuid } from "@common/utils/generateUuid";
 import ListenerHandler from "@/classes/ListenerHandler.class";
 import { useConfigStore } from "@/stores/config";
 import { useUserAuthStore } from "@/stores/userAuth";
-import utils from "@/utils";
 
 export default class SocketHandler {
 	socket?: WebSocket;
@@ -165,7 +165,7 @@ export default class SocketHandler {
 		}
 
 		const lastArg = args[args.length - 1];
-		const CB_REF = utils.guid();
+		const CB_REF = generateUuid();
 
 		if (typeof lastArg === "function") {
 			this.CB_REFS[CB_REF] = lastArg;

+ 2 - 2
frontend/src/composables/useSoundcloudPlayer.ts

@@ -1,10 +1,10 @@
 import { ref, watch } from "vue";
-import utils from "@/utils";
+import { generateUuid } from "@common/utils/generateUuid";
 
 const soundcloudDomain = "https://w.soundcloud.com";
 
 export const useSoundcloudPlayer = () => {
-	const uuid = utils.guid();
+	const uuid = generateUuid();
 
 	const soundcloudIframeElement = ref();
 	const widgetId = ref();

+ 2 - 3
frontend/src/stores/modals.ts

@@ -1,7 +1,6 @@
 import { defineAsyncComponent } from "vue";
 import { defineStore } from "pinia";
-import utils from "@/utils";
-
+import { generateUuid } from "@common/utils/generateUuid";
 import { useWebsocketsStore } from "@/stores/websockets";
 import { useConfigStore } from "@/stores/config";
 
@@ -21,7 +20,7 @@ export const useModalsStore = defineStore("modals", {
 		openModal(
 			dataOrModal: string | { modal: string; props?: Record<string, any> }
 		) {
-			const uuid = utils.guid();
+			const uuid = generateUuid();
 			let modal;
 			let props;
 			if (typeof dataOrModal === "string") modal = dataOrModal;

+ 4 - 4
frontend/src/stores/model.ts

@@ -1,7 +1,7 @@
 import { reactive, ref } from "vue";
 import { defineStore } from "pinia";
+import { generateUuid } from "@common/utils/generateUuid";
 import { useWebsocketStore } from "./websocket";
-import utils from "@/utils";
 import Model from "@/Model";
 
 export const useModelStore = defineStore("model", () => {
@@ -92,7 +92,7 @@ export const useModelStore = defineStore("model", () => {
 				data => onCreatedCallback(modelName, data)
 			);
 
-		const uuid = utils.guid();
+		const uuid = generateUuid();
 
 		subscriptions.value.created[modelName] ??= {};
 		subscriptions.value.created[modelName][uuid] = callback;
@@ -104,7 +104,7 @@ export const useModelStore = defineStore("model", () => {
 		modelName: string,
 		callback: (data?: any) => any
 	) => {
-		const uuid = utils.guid();
+		const uuid = generateUuid();
 
 		subscriptions.value.updated[modelName] ??= {};
 		subscriptions.value.updated[modelName][uuid] = callback;
@@ -129,7 +129,7 @@ export const useModelStore = defineStore("model", () => {
 		modelName: string,
 		callback: (data?: any) => any
 	) => {
-		const uuid = utils.guid();
+		const uuid = generateUuid();
 
 		subscriptions.value.deleted[modelName] ??= {};
 		subscriptions.value.deleted[modelName][uuid] = callback;

+ 5 - 5
frontend/src/stores/websocket.ts

@@ -1,8 +1,8 @@
 import { defineStore } from "pinia";
 import { ref } from "vue";
+import { generateUuid } from "@common/utils/generateUuid";
 import { useConfigStore } from "./config";
 import { useUserAuthStore } from "./userAuth";
-import utils from "@/utils";
 import ms from "@/ms";
 
 export const useWebsocketStore = defineStore("websocket", () => {
@@ -20,7 +20,7 @@ export const useWebsocketStore = defineStore("websocket", () => {
 
 	const runJob = async (job: string, payload?: any) =>
 		new Promise((resolve, reject) => {
-			const callbackRef = utils.guid();
+			const callbackRef = generateUuid();
 			const message = JSON.stringify([
 				job,
 				payload ?? {},
@@ -54,7 +54,7 @@ export const useWebsocketStore = defineStore("websocket", () => {
 
 		subscriptions.value[channel].status = "subscribed";
 
-		const uuid = utils.guid();
+		const uuid = generateUuid();
 
 		subscriptions.value[channel].callbacks[uuid] = callback;
 
@@ -90,7 +90,7 @@ export const useWebsocketStore = defineStore("websocket", () => {
 
 		return Object.fromEntries(
 			Object.entries(channels).map(([channel, callback]) => {
-				const uuid = utils.guid();
+				const uuid = generateUuid();
 
 				subscriptions.value[channel].callbacks[uuid] = callback;
 
@@ -162,7 +162,7 @@ export const useWebsocketStore = defineStore("websocket", () => {
 	};
 
 	const onReady = async (callback: () => any) => {
-		const uuid = utils.guid();
+		const uuid = generateUuid();
 
 		readyCallbacks.value[uuid] = callback;
 

+ 0 - 13
frontend/src/utils.ts

@@ -1,17 +1,4 @@
 export default {
-	guid: () =>
-		"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, symbol => {
-			let array;
-
-			if (symbol === "y") {
-				array = ["8", "9", "a", "b"];
-				return array[Math.floor(Math.random() * array.length)];
-			}
-
-			array = new Uint8Array(1);
-			window.crypto.getRandomValues(array);
-			return (array[0] % 16).toString(16);
-		}),
 	formatTime: (originalDuration: number) => {
 		if (originalDuration <= 0) return "0:00";
 

+ 24 - 30
frontend/tsconfig.json

@@ -1,32 +1,26 @@
 {
-  "compilerOptions": {
-    "target": "esnext",
-    "module": "esnext",
-    "moduleResolution": "node",
-    "importHelpers": true,
-    "isolatedModules": true,
-    "resolveJsonModule": true,
-    "noEmit": true,
-    "baseUrl": ".",
-    "paths": {
-      "@musare_types/*": [
-        "../types/*"
-      ],
-      "@/*": [
-        "./src/*"
-      ],
-    },
-    "jsx": "preserve",
-    "types": [
-      "vite/client",
-      "@intlify/unplugin-vue-i18n/messages",
-      "vitest/globals"
-    ],
-    "strict": false,
-    "allowSyntheticDefaultImports": true
-  },
-  "exclude": [
-    "./src/index.html",
-    "./src/coverage"
-  ]
+	"compilerOptions": {
+		"target": "esnext",
+		"module": "esnext",
+		"moduleResolution": "node",
+		"importHelpers": true,
+		"isolatedModules": true,
+		"resolveJsonModule": true,
+		"noEmit": true,
+		"baseUrl": ".",
+		"paths": {
+			"@musare_types/*": ["../types/*"],
+			"@/*": ["./src/*"],
+			"@common/*": ["../common/*"]
+		},
+		"jsx": "preserve",
+		"types": [
+			"vite/client",
+			"@intlify/unplugin-vue-i18n/messages",
+			"vitest/globals"
+		],
+		"strict": false,
+		"allowSyntheticDefaultImports": true
+	},
+	"exclude": ["./src/index.html", "./src/coverage"]
 }

+ 4 - 0
frontend/vite.config.js

@@ -146,6 +146,10 @@ export default {
 			{
 				find: "@",
 				replacement: path.resolve(__dirname, "src")
+			},
+			{
+				find: "@common",
+				replacement: path.resolve(__dirname, "../common")
 			}
 		],
 		extensions: [