Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot] 5d29cd919d Bump lodash from 4.17.21 to 4.17.23 in /frontend
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.17.23
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-23 23:44:41 +00:00
3 changed files with 57 additions and 85 deletions
+40 -55
View File
@@ -48,14 +48,12 @@ const resolveDatabaseUrl = (rawUrl?: string) => {
const prismaDir = path.resolve(backendRoot, "prisma");
const normalizedRelative = filePath.replace(/^\.\/?/, "");
const hasLeadingPrismaDir =
normalizedRelative === "prisma" || normalizedRelative.startsWith("prisma/");
normalizedRelative === "prisma" ||
normalizedRelative.startsWith("prisma/");
const absolutePath = path.isAbsolute(filePath)
? filePath
: path.resolve(
hasLeadingPrismaDir ? backendRoot : prismaDir,
normalizedRelative,
);
: path.resolve(hasLeadingPrismaDir ? backendRoot : prismaDir, normalizedRelative);
return `file:${absolutePath}`;
};
@@ -148,7 +146,7 @@ const io = new Server(httpServer, {
const prisma = new PrismaClient();
const parseJsonField = <T>(
rawValue: string | null | undefined,
fallback: T,
fallback: T
): T => {
if (!rawValue) return fallback;
try {
@@ -242,7 +240,7 @@ app.use(
credentials: true,
allowedHeaders: ["Content-Type", "Authorization", "x-csrf-token"],
exposedHeaders: ["x-csrf-token"],
}),
})
);
app.use(express.json({ limit: "50mb" }));
app.use(express.urlencoded({ extended: true, limit: "50mb" }));
@@ -254,8 +252,8 @@ app.use((req, res, next) => {
if (sizeInMB > 10) {
console.log(
`[LARGE REQUEST] ${req.method} ${req.path} - ${sizeInMB.toFixed(
2,
)}MB - Content-Length: ${contentLength} bytes`,
2
)}MB - Content-Length: ${contentLength} bytes`
);
}
}
@@ -269,18 +267,18 @@ app.use((req, res, next) => {
res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
res.setHeader(
"Permissions-Policy",
"geolocation=(), microphone=(), camera=()",
"geolocation=(), microphone=(), camera=()"
);
res.setHeader(
"Content-Security-Policy",
"default-src 'self'; " +
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://unpkg.com; " +
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; " +
"font-src 'self' https://fonts.gstatic.com; " +
"img-src 'self' data: blob: https:; " +
"connect-src 'self' ws: wss:; " +
"frame-ancestors 'none';",
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://unpkg.com; " +
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; " +
"font-src 'self' https://fonts.gstatic.com; " +
"img-src 'self' data: blob: https:; " +
"connect-src 'self' ws: wss:; " +
"frame-ancestors 'none';"
);
next();
@@ -289,17 +287,14 @@ app.use((req, res, next) => {
const requestCounts = new Map<string, { count: number; resetTime: number }>();
const RATE_LIMIT_WINDOW = 15 * 60 * 1000;
setInterval(
() => {
const now = Date.now();
for (const [ip, data] of requestCounts.entries()) {
if (now > data.resetTime) {
requestCounts.delete(ip);
}
setInterval(() => {
const now = Date.now();
for (const [ip, data] of requestCounts.entries()) {
if (now > data.resetTime) {
requestCounts.delete(ip);
}
},
5 * 60 * 1000,
).unref();
}
}, 5 * 60 * 1000).unref();
const RATE_LIMIT_MAX_REQUESTS = (() => {
const parsed = Number(process.env.RATE_LIMIT_MAX_REQUESTS);
@@ -366,10 +361,7 @@ app.get("/csrf-token", (req, res) => {
}
clientLimit.count++;
} else {
csrfRateLimit.set(ip, {
count: 1,
resetTime: now + CSRF_RATE_LIMIT_WINDOW,
});
csrfRateLimit.set(ip, { count: 1, resetTime: now + CSRF_RATE_LIMIT_WINDOW });
}
// Cleanup old rate limit entries occasionally
@@ -384,7 +376,7 @@ app.get("/csrf-token", (req, res) => {
res.json({
token,
header: getCsrfTokenHeader(),
header: getCsrfTokenHeader()
});
});
@@ -392,7 +384,7 @@ app.get("/csrf-token", (req, res) => {
const csrfProtectionMiddleware = (
req: express.Request,
res: express.Response,
next: express.NextFunction,
next: express.NextFunction
) => {
// Skip CSRF validation for safe methods (GET, HEAD, OPTIONS)
// Note: /csrf-token is a GET endpoint, so it's automatically exempt
@@ -485,7 +477,7 @@ const drawingCreateSchema = drawingBaseSchema
},
{
message: "Invalid or malicious drawing data detected",
},
}
);
const drawingUpdateSchema = drawingBaseSchema
@@ -535,12 +527,12 @@ const drawingUpdateSchema = drawingBaseSchema
},
{
message: "Invalid or malicious drawing data detected",
},
}
);
const respondWithValidationErrors = (
res: express.Response,
issues: z.ZodIssue[],
issues: z.ZodIssue[]
) => {
res.status(400).json({
error: "Invalid drawing payload",
@@ -590,7 +582,7 @@ const verifyDatabaseIntegrityAsync = (filePath: string): Promise<boolean> => {
path.resolve(__dirname, "./workers/db-verify.js"),
{
workerData: { filePath },
},
}
);
let timeoutHandle: NodeJS.Timeout;
let settled = false;
@@ -665,7 +657,7 @@ io.on("connection", (socket) => {
roomUsers.set(roomId, filteredUsers);
io.to(roomId).emit("presence-update", filteredUsers);
},
}
);
socket.on("cursor-move", (data) => {
@@ -690,7 +682,7 @@ io.on("connection", (socket) => {
io.to(roomId).emit("presence-update", users);
}
}
},
}
);
socket.on("disconnect", () => {
@@ -1081,9 +1073,8 @@ app.get("/export", async (req, res) => {
res.setHeader("Content-Type", "application/octet-stream");
res.setHeader(
"Content-Disposition",
`attachment; filename="excalidash-db-${
new Date().toISOString().split("T")[0]
}.${extension}"`,
`attachment; filename="excalidash-db-${new Date().toISOString().split("T")[0]
}.${extension}"`
);
const fileStream = fs.createReadStream(dbPath);
@@ -1105,9 +1096,8 @@ app.get("/export/json", async (req, res) => {
res.setHeader("Content-Type", "application/zip");
res.setHeader(
"Content-Disposition",
`attachment; filename="excalidraw-drawings-${
new Date().toISOString().split("T")[0]
}.zip"`,
`attachment; filename="excalidraw-drawings-${new Date().toISOString().split("T")[0]
}.zip"`
);
const archive = archiver("zip", { zlib: { level: 9 } });
@@ -1121,8 +1111,6 @@ app.get("/export/json", async (req, res) => {
const drawingsByCollection: { [key: string]: any[] } = {};
const exportSource = `${req.protocol}://${req.get("host")}`;
drawings.forEach((drawing: any) => {
const collectionName = drawing.collection?.name || "Unorganized";
if (!drawingsByCollection[collectionName]) {
@@ -1130,9 +1118,6 @@ app.get("/export/json", async (req, res) => {
}
const drawingData = {
type: "excalidraw",
version: 2,
source: exportSource,
elements: JSON.parse(drawing.elements),
appState: JSON.parse(drawing.appState),
files: JSON.parse(drawing.files || "{}"),
@@ -1150,7 +1135,7 @@ app.get("/export/json", async (req, res) => {
collectionDrawings.forEach((drawing, index) => {
const fileName = `${drawing.name.replace(
/[<>:"/\\|?*]/g,
"_",
"_"
)}.excalidraw`;
const filePath = `${folderName}/${fileName}`;
@@ -1158,7 +1143,7 @@ app.get("/export/json", async (req, res) => {
name: filePath,
});
});
},
}
);
const readmeContent = `ExcaliDash Export
@@ -1176,8 +1161,8 @@ Total Drawings: ${drawings.length}
Collections:
${Object.entries(drawingsByCollection)
.map(([name, drawings]) => `- ${name}: ${drawings.length} drawings`)
.join("\n")}
.map(([name, drawings]) => `- ${name}: ${drawings.length} drawings`)
.join("\n")}
`;
archive.append(readmeContent, { name: "README.txt" });
@@ -1222,7 +1207,7 @@ app.post("/import/sqlite", upload.single("db"), async (req, res) => {
const originalPath = req.file.path;
const stagedPath = path.join(
uploadDir,
`temp-${Date.now()}-${Math.random().toString(36).slice(2)}.db`,
`temp-${Date.now()}-${Math.random().toString(36).slice(2)}.db`
);
try {
@@ -1249,7 +1234,7 @@ app.post("/import/sqlite", upload.single("db"), async (req, res) => {
try {
await fsPromises.access(dbPath);
await fsPromises.copyFile(dbPath, backupPath);
} catch {}
} catch { }
await moveFile(stagedPath, dbPath);
} catch (error) {
+16 -29
View File
@@ -1,12 +1,12 @@
{
"name": "frontend",
"version": "0.1.8",
"version": "0.3.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "frontend",
"version": "0.1.8",
"version": "0.3.1",
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/utilities": "^3.2.2",
@@ -15,7 +15,7 @@
"axios": "^1.13.2",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"lodash": "^4.17.21",
"lodash": "^4.17.23",
"lucide-react": "^0.554.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@@ -162,7 +162,6 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
@@ -517,7 +516,6 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18"
},
@@ -561,7 +559,6 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18"
}
@@ -2612,7 +2609,8 @@
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
"dev": true,
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
@@ -2777,7 +2775,6 @@
"integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
@@ -2789,7 +2786,6 @@
"integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/react": "*"
}
@@ -2860,7 +2856,6 @@
"integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.47.0",
"@typescript-eslint/types": "8.47.0",
@@ -3224,7 +3219,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -3275,6 +3269,7 @@
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=8"
}
@@ -3504,7 +3499,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.8.25",
"caniuse-lite": "^1.0.30001754",
@@ -3836,7 +3830,6 @@
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz",
"integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10"
}
@@ -4210,7 +4203,6 @@
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
"license": "ISC",
"peer": true,
"engines": {
"node": ">=12"
}
@@ -4450,7 +4442,8 @@
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
"dev": true,
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/dompurify": {
"version": "3.1.6",
@@ -4669,7 +4662,6 @@
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -5503,7 +5495,6 @@
"resolved": "https://registry.npmjs.org/jotai/-/jotai-2.11.0.tgz",
"integrity": "sha512-zKfoBBD1uDw3rljwHkt0fWuja1B76R7CjznuBO+mSX6jpsO1EBeWNRKpeaQho9yPI/pvCv4recGfgOXGxwPZvQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12.20.0"
},
@@ -5784,9 +5775,9 @@
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"license": "MIT"
},
"node_modules/lodash-es": {
@@ -5851,6 +5842,7 @@
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"lz-string": "bin/bin.js"
}
@@ -6880,7 +6872,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
@@ -7046,6 +7037,7 @@
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"ansi-regex": "^5.0.1",
"ansi-styles": "^5.0.0",
@@ -7061,6 +7053,7 @@
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10"
},
@@ -7116,7 +7109,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0"
},
@@ -7129,7 +7121,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.2"
@@ -7143,7 +7134,8 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true,
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/react-refresh": {
"version": "0.18.0",
@@ -7858,7 +7850,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -7997,7 +7988,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -8187,7 +8177,6 @@
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@@ -8281,7 +8270,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -8608,7 +8596,6 @@
"integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==",
"dev": true,
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
+1 -1
View File
@@ -22,7 +22,7 @@
"axios": "^1.13.2",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"lodash": "^4.17.21",
"lodash": "^4.17.23",
"lucide-react": "^0.554.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",