Skip to content

Commit

Permalink
BC-7960 - Move config api to tldraw server (#105)
Browse files Browse the repository at this point in the history
  • Loading branch information
SevenWaysDP authored Oct 22, 2024
1 parent a58ee01 commit dba0f3f
Show file tree
Hide file tree
Showing 12 changed files with 142 additions and 109 deletions.
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"
CONFIG_PATH: "{{ '/api/tldraw/config/public' if WITH_TLDRAW2 else '/api/v3/config/public' }}"
2 changes: 1 addition & 1 deletion nginx.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ 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}" }';
return 200 '{ "CONFIG_PATH": "${CONFIG_PATH}" }';
add_header Content-Type application/json;
}

Expand Down
43 changes: 42 additions & 1 deletion src/configuration/api/api.configuration.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,49 @@
// remove this code by BC-7906
// set CONFIG_PATH to /api/tldraw/config/public
// remove line 7 and 8 in ngnix.conf.template
import { HttpStatusCode } from "../../types/StatusCodeEnums";
import { setErrorData } from "../../utils/errorData";
import { redirectToErrorPage } from "../../utils/redirectUtils";

const getConfigOptions = async (): Promise<{
CONFIG_PATH: string;
}> => {
const connectionOptions = {
CONFIG_PATH: configApiUrl(),
};

if (import.meta.env.PROD) {
try {
const response = await fetch("/tldraw-client-runtime.config.json");

if (!response.ok) {
throw new Error(`${response.status} - ${response.statusText}`);
}

const data: { CONFIG_PATH: string } = await response.json();
connectionOptions.CONFIG_PATH = data.CONFIG_PATH;
} catch (error) {
setErrorData(HttpStatusCode.InternalServerError, "error.500");
redirectToErrorPage();
}
}

return connectionOptions;
};

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?parentId=PARENTID",
USER_DATA: `/api/v3/user/me`,
ENV_CONFIG: `/api/v3/config/public`,
CONFIG_PATH: await getConfigOptions().then((options) => options.CONFIG_PATH),
};
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
40 changes: 20 additions & 20 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 @@ -350,21 +350,21 @@ export function useMultiplayerState({
file: File,
id: string,
): Promise<string | false> => {
if (!envs!.TLDRAW__ASSETS_ENABLED) {
if (!envs.TLDRAW__ASSETS_ENABLED) {
toast.info("Asset uploading is disabled");
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;
}

const isMimeTypeDisallowed =
envs!.TLDRAW__ASSETS_ALLOWED_MIME_TYPES_LIST &&
!envs!.TLDRAW__ASSETS_ALLOWED_MIME_TYPES_LIST.includes(file.type);
envs.TLDRAW__ASSETS_ALLOWED_MIME_TYPES_LIST &&
!envs.TLDRAW__ASSETS_ALLOWED_MIME_TYPES_LIST.includes(file.type);

if (isMimeTypeDisallowed) {
toast.info("Asset of this type is not allowed");
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
32 changes: 14 additions & 18 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, getParentId } from "../utils/connectionOptions";
import { getEnvs } from "../utils/envConfig";
import { getUserData } from "../utils/userData";
import { clearErrorData } from "../utils/errorData";
import {
getParentId,
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 parentId = getParentId();
const doc = new Doc();
const provider = new WebsocketProvider(
connectionOptions.websocketUrl,
envs?.TLDRAW__WEBSOCKET_URL,
parentId,
doc,
{
Expand Down Expand Up @@ -67,16 +63,16 @@ const resumeSync = () => {
};

export {
doc,
envs,
user,
parentId,
doc,
pauseSync,
provider,
resumeSync,
room,
yShapes,
yBindings,
yAssets,
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.

34 changes: 27 additions & 7 deletions src/utils/envConfig.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
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);
const response = await fetch(API.CONFIG_PATH);
HttpGuard.checkStatusOk(response);
const responseData = await response.json();

Expand All @@ -18,8 +21,25 @@ 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 {
const configuration: Envs = {
TLDRAW__WEBSOCKET_URL: "ws://localhost:3345",
TLDRAW__ASSETS_ENABLED: true,
TLDRAW__ASSETS_MAX_SIZE_BYTES: 10485760,
TLDRAW__ASSETS_ALLOWED_MIME_TYPES_LIST: [
"image/png",
"image/jpeg",
"image/gif",
"image/svg+xml",
],
FEATURE_TLDRAW_ENABLED: true,
};

return configuration;
}
}
};
Loading

0 comments on commit dba0f3f

Please sign in to comment.