Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BC-7960 - Move config api to tldraw server #105

Merged
merged 13 commits into from
Oct 22, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ data:
NODE_ENV: "production"
TZ: "Europe/Berlin"
PORT: "3046"
TLDRAW_SERVER_URL: "wss://{{ DOMAIN }}/tldraw-server"
VITE_SERVER_TLDRAW_2_ENABLED : {{ WITH_TLDRAW2 }}
5 changes: 0 additions & 5 deletions nginx.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@ server {

set $csp "default-src 'self'; connect-src 'self' data:; base-uri 'self'; script-src 'nonce-$request_id' 'strict-dynamic' https:; object-src 'none'; font-src 'self' data:; img-src 'self' data:; style-src 'self' 'unsafe-inline';";

location /tldraw-client-runtime.config.json {
return 200 '{ "tldrawServerURL" : "${TLDRAW_SERVER_URL}" }';
add_header Content-Type application/json;
}

location /assets {
alias /usr/share/nginx/html/assets/;
}
Expand Down
9 changes: 8 additions & 1 deletion src/configuration/api/api.configuration.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
const configApiUrl = () => {
const configApiUrl = import.meta.env.VITE_SERVER_TLDRAW_2_ENABLED
? `/api/tldraw/config/public`
: `/api/v3/config/public`;

return configApiUrl;
};
export const API = {
FILE_UPLOAD: "/api/v3/file/upload/school/SCHOOLID/boardnodes/CONTEXTID",
FILE_DELETE: "/api/v3/file/delete/FILERECORD_ID",
FILE_RESTORE: "/api/v3/file/restore/FILERECORD_ID",
LOGIN_REDIRECT: "/login?redirect=/tldraw?roomName=ROOMID",
USER_DATA: `/api/v3/user/me`,
ENV_CONFIG: `/api/v3/config/public`,
ENV_CONFIG: configApiUrl(),
};
10 changes: 5 additions & 5 deletions src/hooks/useMultiplayerState.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { renderHook, act } from "@testing-library/react";
import { act, renderHook } from "@testing-library/react";
import * as Tldraw from "@tldraw/tldraw";
import {
TDAsset,
TDBinding,
TDShape,
TDUser,
TldrawApp,
TDUserStatus,
TldrawApp,
} from "@tldraw/tldraw";
import * as Tldraw from "@tldraw/tldraw";
import { useMultiplayerState } from "./useMultiplayerState";
import { doc, room, undoManager } from "../stores/setup";
import { deleteAsset, handleAssets } from "../utils/handleAssets";
import { useMultiplayerState } from "./useMultiplayerState";

vi.mock("@tldraw/tldraw", async () => {
const tldraw = await vi.importActual("@tldraw/tldraw");
Expand Down Expand Up @@ -74,7 +74,7 @@ vi.mock("../stores/setup", () => ({
},
envs: {
TLDRAW__ASSETS_ENABLED: true,
TLDRAW__ASSETS_MAX_SIZE: 1000000,
TLDRAW__ASSETS_MAX_SIZE_BYTES: 1000000,
TLDRAW__ASSETS_ALLOWED_MIME_TYPES_LIST: ["image/png", "image/jpeg"],
},
}));
Expand Down
34 changes: 17 additions & 17 deletions src/hooks/useMultiplayerState.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import lodash from "lodash";
import { Utils } from "@tldraw/core";
import {
TDAsset,
TDAssetType,
Expand All @@ -11,36 +11,36 @@ import {
TldrawApp,
TldrawPatch,
} from "@tldraw/tldraw";
import { Vec } from "@tldraw/vec";
import { User } from "@y-presence/client";
import lodash from "lodash";
import { useCallback, useEffect, useState } from "react";
import { toast } from "react-toastify";
import {
doc,
room,
envs,
pauseSync,
provider,
resumeSync,
room,
undoManager,
user,
yAssets,
yBindings,
yShapes,
user,
envs,
pauseSync,
resumeSync,
} from "../stores/setup";
import { STORAGE_SETTINGS_KEY } from "../utils/userSettings";
import { UserPresence } from "../types/UserPresence";
import {
importAssetsToS3,
openFromFileSystem,
} from "../utils/boardImportUtils";
import {
fileToBase64,
fileToText,
saveToFileSystem,
} from "../utils/boardExportUtils";
import {
importAssetsToS3,
openFromFileSystem,
} from "../utils/boardImportUtils";
import { uploadFileToStorage } from "../utils/fileUpload";
import { getImageBlob } from "../utils/tldrawImageExportUtils";
import { Utils } from "@tldraw/core";
import { deleteAsset, handleAssets } from "../utils/handleAssets";
import {
getImageSizeFromSrc,
getVideoSizeFromSrc,
Expand All @@ -49,8 +49,8 @@ import {
openAssetsFromFileSystem,
VIDEO_EXTENSIONS,
} from "../utils/tldrawFileUploadUtils";
import { Vec } from "@tldraw/vec";
import { deleteAsset, handleAssets } from "../utils/handleAssets";
import { getImageBlob } from "../utils/tldrawImageExportUtils";
import { STORAGE_SETTINGS_KEY } from "../utils/userSettings";

declare const window: Window & { app: TldrawApp };

Expand Down Expand Up @@ -355,9 +355,9 @@ export function useMultiplayerState({
return false;
}

if (file.size > envs!.TLDRAW__ASSETS_MAX_SIZE) {
if (file.size > envs!.TLDRAW__ASSETS_MAX_SIZE_BYTES) {
const bytesInMb = 1048576;
const sizeInMb = envs!.TLDRAW__ASSETS_MAX_SIZE / bytesInMb;
const sizeInMb = envs!.TLDRAW__ASSETS_MAX_SIZE_BYTES / bytesInMb;
toast.info(`Asset is too big - max. ${sizeInMb}MB`);
return false;
}
Expand Down
12 changes: 7 additions & 5 deletions src/mapper/configuration.mapper.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { Envs } from "../types/Envs";
import { TypeGuard } from "../guards/type.guard";
import { Envs } from "../types/Envs";

const checkEnvType = (obj: Record<string, unknown>): void => {
TypeGuard.checkKeyAndValueExists(obj, "TLDRAW__WEBSOCKET_URL");
TypeGuard.checkKeyAndValueExists(obj, "FEATURE_TLDRAW_ENABLED");
TypeGuard.checkKeyAndValueExists(obj, "TLDRAW__ASSETS_ENABLED");
TypeGuard.checkKeyAndValueExists(obj, "TLDRAW__ASSETS_MAX_SIZE");
TypeGuard.checkKeyAndValueExists(obj, "TLDRAW__ASSETS_MAX_SIZE_BYTES");
TypeGuard.checkKeyAndValueExists(
obj,
"TLDRAW__ASSETS_ALLOWED_MIME_TYPES_LIST",
);
TypeGuard.checkBoolean(obj.FEATURE_TLDRAW_ENABLED);
TypeGuard.checkBoolean(obj.FEATURE_TLDRAW_ENABLED);
TypeGuard.checkNumber(obj.TLDRAW__ASSETS_MAX_SIZE);
TypeGuard.checkNumber(obj.TLDRAW__ASSETS_MAX_SIZE_BYTES);
TypeGuard.checkArray(obj.TLDRAW__ASSETS_ALLOWED_MIME_TYPES_LIST);
};

Expand All @@ -27,9 +27,11 @@ export class ConfigurationMapper {
const configuration = castToEnv(obj);

const mappedConfiguration: Envs = {
TLDRAW__WEBSOCKET_URL: configuration.TLDRAW__WEBSOCKET_URL,
FEATURE_TLDRAW_ENABLED: configuration.FEATURE_TLDRAW_ENABLED,
TLDRAW__ASSETS_ENABLED: configuration.TLDRAW__ASSETS_ENABLED,
TLDRAW__ASSETS_MAX_SIZE: configuration.TLDRAW__ASSETS_MAX_SIZE,
TLDRAW__ASSETS_MAX_SIZE_BYTES:
configuration.TLDRAW__ASSETS_MAX_SIZE_BYTES,
TLDRAW__ASSETS_ALLOWED_MIME_TYPES_LIST:
configuration.TLDRAW__ASSETS_ALLOWED_MIME_TYPES_LIST,
};
Expand Down
34 changes: 15 additions & 19 deletions src/stores/setup.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
import { TDAsset, TDBinding, TDShape } from "@tldraw/tldraw";
import { Doc, Map, UndoManager } from "yjs";
import { WebsocketProvider } from "y-websocket";
import { Room } from "@y-presence/client";
import { WebsocketProvider } from "y-websocket";
import { Doc, Map, UndoManager } from "yjs";
import { UserPresence } from "../types/UserPresence";
import { getConnectionOptions, getRoomId } from "../utils/connectionOptions";
import { getEnvs } from "../utils/envConfig";
import { getUserData } from "../utils/userData";
import { clearErrorData } from "../utils/errorData";
import {
getRoomId,
handleRedirectIfNotValid,
redirectToNotFoundErrorPage,
} from "../utils/redirectUtils";
import { clearErrorData } from "../utils/errorData";
import { getUserData } from "../utils/userData";
import { setDefaultState } from "../utils/userSettings";

clearErrorData();

const [connectionOptions, envs, userResult] = await Promise.all([
getConnectionOptions(),
getEnvs(),
getUserData(),
]);
const [envs, userResult] = await Promise.all([getEnvs(), getUserData()]);

handleRedirectIfNotValid(userResult, envs);

Expand All @@ -29,7 +25,7 @@ const user = userResult.user;
const roomId = getRoomId();
const doc = new Doc();
const provider = new WebsocketProvider(
connectionOptions.websocketUrl,
envs?.TLDRAW__WEBSOCKET_URL,
roomId,
doc,
{
Expand Down Expand Up @@ -67,16 +63,16 @@ const resumeSync = () => {
};

export {
envs,
user,
roomId,
doc,
envs,
pauseSync,
provider,
resumeSync,
room,
yShapes,
yBindings,
yAssets,
roomId,
undoManager,
pauseSync,
resumeSync,
user,
yAssets,
yBindings,
yShapes,
};
3 changes: 2 additions & 1 deletion src/types/Envs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export type Envs = {
FEATURE_TLDRAW_ENABLED: boolean;
TLDRAW__ASSETS_ENABLED: boolean;
TLDRAW__ASSETS_MAX_SIZE: number;
TLDRAW__ASSETS_MAX_SIZE_BYTES: number;
TLDRAW__ASSETS_ALLOWED_MIME_TYPES_LIST: string[];
TLDRAW__WEBSOCKET_URL: string;
};
42 changes: 0 additions & 42 deletions src/utils/connectionOptions.ts

This file was deleted.

30 changes: 24 additions & 6 deletions src/utils/envConfig.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Envs } from "../types/Envs";
import { API } from "../configuration/api/api.configuration";
import { ConfigurationMapper } from "../mapper/configuration.mapper";
import { HttpGuard } from "../guards/http.guard";
import { ConfigurationMapper } from "../mapper/configuration.mapper";
import { Envs } from "../types/Envs";
import { HttpStatusCode } from "../types/StatusCodeEnums";
import { setErrorData } from "./errorData";
import { redirectToErrorPage } from "./redirectUtils";

// the try catch should not part of getEnvs, the place that use it must handle the errors
// should be part of a store
// Without loading the config the Promise.all should not be finished and proceed.
export const getEnvs = async (): Promise<Envs | undefined> => {
export const getEnvs = async (): Promise<Envs> => {
try {
// TODO: check order..
const response = await fetch(API.ENV_CONFIG);
Expand All @@ -18,8 +21,23 @@ export const getEnvs = async (): Promise<Envs | undefined> => {

return configuration;
} catch (error) {
// It should exists one place that execute the console.error in the application. A errorHandler.
// If we want to collect this informations to send it back to us, then we have currently no possibility to implement it.
console.error("Error fetching env config:", error);
if (import.meta.env.PROD) {
setErrorData(HttpStatusCode.InternalServerError, "error.500");
redirectToErrorPage();
throw error;
} else {
return {
TLDRAW__WEBSOCKET_URL: "ws://localhost:3345",
ASSETS_ENABLED: true,
bergatco marked this conversation as resolved.
Show resolved Hide resolved
ASSETS_MAX_SIZE_BYTES: 10485760,
ASSETS_ALLOWED_MIME_TYPES_LIST: [
"image/png",
"image/jpeg",
"image/gif",
"image/svg+xml",
],
FEATURE_TLDRAW_ENABLED: true,
} as unknown as Envs;
}
}
};
22 changes: 16 additions & 6 deletions src/utils/redirectUtils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import { getRoomId } from "./connectionOptions";
import { UserResult } from "../types/User";
import { API } from "../configuration/api/api.configuration";
import { Envs } from "../types/Envs";
import { setErrorData } from "./errorData";
import { HttpStatusCode } from "../types/StatusCodeEnums";
import { API } from "../configuration/api/api.configuration";
import { UserResult } from "../types/User";
import { setErrorData } from "./errorData";
import { validateId } from "./validator";

const getRoomId = () => {
const urlParams = new URLSearchParams(window.location.search);
const roomId = urlParams.get("roomName") ?? "";

validateId(roomId);

return roomId;
};

const redirectToLoginPage = () => {
const roomId = getRoomId();
Expand Down Expand Up @@ -50,8 +59,9 @@ const handleRedirectIfNotValid = (userResult: UserResult, envs?: Envs) => {
};

export {
redirectToLoginPage,
getRoomId,
handleRedirectIfNotValid,
redirectToErrorPage,
redirectToLoginPage,
redirectToNotFoundErrorPage,
handleRedirectIfNotValid,
};
7 changes: 6 additions & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="vitest" />
import { defineConfig, PluginOption } from "vite";
import react from "@vitejs/plugin-react";
import { defineConfig, PluginOption } from "vite";
import topLevelAwait from "vite-plugin-top-level-await";

const noncePlugin = (placeholderName = "**CSP_NONCE**"): PluginOption => ({
Expand Down Expand Up @@ -35,6 +35,11 @@ export default defineConfig({
changeOrigin: true,
secure: false,
},
"/api/tldraw": {
target: "http://localhost:3349",
changeOrigin: true,
secure: false,
},
},
},
test: {
Expand Down
Loading