Skip to content

Commit fc84462

Browse files
authored
Merge pull request #1602 from FalkorDB/staging
staging
2 parents 3b51f1b + b4cc802 commit fc84462

File tree

18 files changed

+638
-224
lines changed

18 files changed

+638
-224
lines changed

.github/workflows/playwright.yml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ jobs:
4747
~/.npm
4848
node_modules
4949
~/.cache/ms-playwright
50-
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/playwright.config.ts') }}
50+
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/playwright.config.ts') }}-${{ hashFiles('.github/workflows/playwright.yml') }}
5151
restore-keys: |
52+
${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/playwright.config.ts') }}-
5253
${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-
5354
${{ runner.os }}-node-
5455
- name: Install dependencies with enhanced retry logic
@@ -82,7 +83,7 @@ jobs:
8283
fi
8384
done
8485
- name: Install Playwright Browsers
85-
run: npx playwright install --with-deps chromium
86+
run: npx playwright install --with-deps chromium firefox
8687
- name: Build application
8788
run: npm run build
8889
- name: Upload build output
@@ -127,8 +128,9 @@ jobs:
127128
~/.npm
128129
node_modules
129130
~/.cache/ms-playwright
130-
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/playwright.config.ts') }}
131+
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/playwright.config.ts') }}-${{ hashFiles('.github/workflows/playwright.yml') }}
131132
restore-keys: |
133+
${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/playwright.config.ts') }}-
132134
${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-
133135
${{ runner.os }}-node-
134136
- name: Download build output
@@ -204,8 +206,9 @@ jobs:
204206
~/.npm
205207
node_modules
206208
~/.cache/ms-playwright
207-
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/playwright.config.ts') }}
209+
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/playwright.config.ts') }}-${{ hashFiles('.github/workflows/playwright.yml') }}
208210
restore-keys: |
211+
${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/playwright.config.ts') }}-
209212
${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-
210213
${{ runner.os }}-node-
211214
- name: Download build output
@@ -340,8 +343,9 @@ jobs:
340343
~/.npm
341344
node_modules
342345
~/.cache/ms-playwright
343-
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/playwright.config.ts') }}
346+
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/playwright.config.ts') }}-${{ hashFiles('.github/workflows/playwright.yml') }}
344347
restore-keys: |
348+
${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/playwright.config.ts') }}-
345349
${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-
346350
${{ runner.os }}-node-
347351
- name: Download build output

.github/workflows/release-image.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ jobs:
6969
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
7070

7171
- name: Login to DockerHub
72-
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
72+
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
7373
with:
7474
username: ${{ secrets.DOCKER_USERNAME }}
7575
password: ${{ secrets.DOCKER_PASSWORD }}
@@ -106,7 +106,7 @@ jobs:
106106
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
107107

108108
- name: Login to DockerHub
109-
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
109+
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
110110
with:
111111
username: ${{ secrets.DOCKER_USERNAME }}
112112
password: ${{ secrets.DOCKER_PASSWORD }}

app/api/graph/model.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
/* eslint-disable one-var */
33
/* eslint-disable no-param-reassign */
44
/* eslint-disable @typescript-eslint/no-explicit-any */
5-
65
import { Data, getMetaStats, GraphData, InfoLabel, InfoRelationship, Label, Link, LinkCell, MemoryValue, Node, NodeCell, Relationship, ToastFn, Value } from "@/lib/utils";
76

87
// Color palette for node customization

app/api/upload/route.ts

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,53 @@ import { promisify } from "util";
44
import { pipeline } from "stream";
55
import fs from "fs";
66
import { getCorsHeaders } from "../utils";
7+
import { getClient } from "../auth/[...nextauth]/options";
78

89
const pump = promisify(pipeline);
910

1011
// eslint-disable-next-line import/prefer-default-export
1112
export async function POST(request: NextRequest) {
12-
const formData = await request.formData();
13+
try {
14+
const session = await getClient(request);
1315

14-
const file = formData.get("file") as File;
16+
if (session instanceof NextResponse) {
17+
return session;
18+
}
1519

16-
if (!file) {
17-
return NextResponse.json({ error: "No files received." }, { status: 400, headers: getCorsHeaders(request) });
18-
}
20+
const formData = await request.formData();
1921

20-
const filename = file.name.replaceAll(" ", "_");
21-
const filePath = path.join(process.cwd(), `public/assets/${filename}`);
22+
const file = formData.get("file") as File;
2223

23-
try {
24-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
25-
// @ts-ignore
26-
await pump(file.stream(), fs.createWriteStream(filePath));
27-
return NextResponse.json({ path: filePath, status: 200 }, { headers: getCorsHeaders(request) });
28-
} catch (error) {
29-
console.error(error);
24+
if (!file) {
25+
return NextResponse.json({ error: "No files received." }, { status: 400, headers: getCorsHeaders(request) });
26+
}
27+
28+
const filename = path.basename(file.name).replaceAll(" ", "_");
29+
const filePath = path.join(process.cwd(), "public", "assets", filename);
30+
31+
// Guard against path traversal
32+
const assetsDir = path.join(process.cwd(), "public", "assets");
33+
if (!filePath.startsWith(assetsDir)) {
34+
return NextResponse.json({ error: "Invalid file name." }, { status: 400, headers: getCorsHeaders(request) });
35+
}
36+
37+
try {
38+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
39+
// @ts-ignore
40+
await pump(file.stream(), fs.createWriteStream(filePath));
41+
return NextResponse.json({ path: filePath, status: 200 }, { headers: getCorsHeaders(request) });
42+
} catch (error) {
43+
console.error(error);
44+
return NextResponse.json(
45+
{ message: (error as Error).message },
46+
{ status: 400, headers: getCorsHeaders(request) }
47+
);
48+
}
49+
} catch (err) {
50+
console.error(err);
3051
return NextResponse.json(
31-
{ message: (error as Error).message },
32-
{ status: 400, headers: getCorsHeaders(request) }
52+
{ message: (err as Error).message },
53+
{ status: 500, headers: getCorsHeaders(request) }
3354
);
3455
}
3556
}

app/components/provider.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ type BrowserSettingsContextType = {
5454
graphInfo: {
5555
newRefreshInterval: number;
5656
setNewRefreshInterval: Dispatch<SetStateAction<number>>;
57+
newMaxItemsForSearch: number;
58+
setNewMaxItemsForSearch: Dispatch<SetStateAction<number>>;
5759
};
5860
};
5961
settings: {
@@ -109,6 +111,8 @@ type BrowserSettingsContextType = {
109111
showMemoryUsage: boolean;
110112
refreshInterval: number;
111113
setRefreshInterval: Dispatch<SetStateAction<number>>;
114+
maxItemsForSearch: number;
115+
setMaxItemsForSearch: Dispatch<SetStateAction<number>>;
112116
};
113117
};
114118
hasChanges: boolean;
@@ -257,6 +261,8 @@ export const BrowserSettingsContext = createContext<BrowserSettingsContextType>(
257261
graphInfo: {
258262
newRefreshInterval: 0,
259263
setNewRefreshInterval: () => { },
264+
newMaxItemsForSearch: 0,
265+
setNewMaxItemsForSearch: () => { },
260266
},
261267
},
262268
settings: {
@@ -306,6 +312,8 @@ export const BrowserSettingsContext = createContext<BrowserSettingsContextType>(
306312
showMemoryUsage: false,
307313
refreshInterval: 0,
308314
setRefreshInterval: () => { },
315+
maxItemsForSearch: 0,
316+
setMaxItemsForSearch: () => { },
309317
},
310318
},
311319
hasChanges: false,

app/graph/Chat.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { GraphContext, IndicatorContext, QueryLoadingContext, BrowserSettingsCon
1414
import { EventType } from "../api/chat/route";
1515
import ToastButton from "../components/ToastButton";
1616
import { ShineBorder } from "@/components/ui/shine-border";
17+
import { getConnectionItem, setConnectionItem, getConnectionPrefix } from "@/lib/connection-storage";
1718

1819
// Function to get the last maxSavedMessages user messages and all messages in between
1920
const getLastUserMessagesWithContext = (allMessages: Message[], maxUserMessages: number) => {
@@ -108,19 +109,20 @@ export default function Chat({ onClose }: Props) {
108109

109110
// Load messages and cypher only preference for current graph on mount
110111
useEffect(() => {
111-
const savedMessages = localStorage.getItem(`chat-${graphName}`);
112+
if (!getConnectionPrefix()) return;
113+
const savedMessages = getConnectionItem(`chat-${graphName}`);
112114
const currentMessages = JSON.parse(savedMessages || "[]");
113115
setMessages(currentMessages);
114116

115-
const savedCypherOnly = localStorage.getItem(`cypherOnly-${graphName}`);
117+
const savedCypherOnly = getConnectionItem(`cypherOnly-${graphName}`);
116118
setCypherOnly(savedCypherOnly === "true");
117119
}, [graphName, maxSavedMessages]);
118120

119121
useEffect(() => {
120122
let statusGroup: Message[];
121123

122124
if (messages.length > 0) {
123-
localStorage.setItem(`chat-${graphName}`, JSON.stringify(getLastUserMessagesWithContext(messages, maxSavedMessages)));
125+
setConnectionItem(`chat-${graphName}`, JSON.stringify(getLastUserMessagesWithContext(messages, maxSavedMessages)));
124126
}
125127

126128
const newMessagesList = messages.map((message, i): Message | [Message[], boolean] | undefined => {
@@ -536,7 +538,7 @@ export default function Chat({ onClose }: Props) {
536538
onClick={() => {
537539
const next = !cypherOnly;
538540
setCypherOnly(next);
539-
localStorage.setItem(`cypherOnly-${graphName}`, String(next));
541+
setConnectionItem(`cypherOnly-${graphName}`, String(next));
540542
}}
541543
className={cn(
542544
"shrink-0 flex items-center justify-center rounded-md transition-all duration-150 active:scale-[0.96]",

app/graph/GraphView.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { cn, GraphRef, Tab, Label, Link, Node, Relationship, HistoryQuery } from
88
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
99
import { GraphContext, ForceGraphContext } from "@/app/components/provider";
1010
import ForceGraph from "@/app/components/ForceGraph";
11+
import { setConnectionItem } from "@/lib/connection-storage";
1112
import Button from "../components/ui/Button";
1213
import TableView from "./TableView";
1314
import Toolbar from "./toolbar";
@@ -283,7 +284,7 @@ function GraphView({
283284

284285
const newQueries = prev.queries.map(q => q.text === newQuery.text ? newQuery : q);
285286

286-
localStorage.setItem("query history", JSON.stringify(newQueries));
287+
setConnectionItem("query history", JSON.stringify(newQueries));
287288

288289
return {
289290
...prev,

app/graph/Selector.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { useTheme } from "next-themes";
1212
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
1313
import Button from "../components/ui/Button";
1414
import { BrowserSettingsContext, GraphContext, IndicatorContext } from "../components/provider";
15+
import { setConnectionItem, removeConnectionItem } from "@/lib/connection-storage";
1516
import CypherEditor, { CYPHER_LANGUAGE_NAME } from "../components/CypherEditor";
1617
import EditorComponent from "../components/EditorComponent";
1718
import DialogComponent from "../components/DialogComponent";
@@ -319,8 +320,8 @@ export default function Selector<T extends "Graph" | "Schema" = "Graph" | "Schem
319320

320321
const newQueries = historyQuery.queries.filter((_, idx) => !deleteElements.some((removeIndex) => idx === removeIndex));
321322

322-
if (newQueries.length === 0) localStorage.removeItem("query history");
323-
else localStorage.setItem("query history", JSON.stringify(newQueries));
323+
if (newQueries.length === 0) removeConnectionItem("query history");
324+
else setConnectionItem("query history", JSON.stringify(newQueries));
324325

325326
// Check if counter points to a deleted query (counter is 1-indexed, so counter - 1 is the index)
326327
const isCounterDeleted = historyQuery.counter > 0 && deleteElements.includes(historyQuery.counter - 1);
@@ -359,7 +360,7 @@ export default function Selector<T extends "Graph" | "Schema" = "Graph" | "Schem
359360
q.timestamp === item.timestamp ? { ...q, fav: !q.fav, name } : q
360361
);
361362

362-
localStorage.setItem("query history", JSON.stringify(newQueries));
363+
setConnectionItem("query history", JSON.stringify(newQueries));
363364

364365
setHistoryQuery(prev => ({
365366
...prev,
@@ -559,7 +560,7 @@ export default function Selector<T extends "Graph" | "Schema" = "Graph" | "Schem
559560
data-testid="queryHistoryDelete"
560561
title="Remove all queries from history"
561562
onClick={() => {
562-
localStorage.removeItem("query history");
563+
removeConnectionItem("query history");
563564
setHistoryQuery(prev => ({
564565
...prev,
565566
queries: [],
@@ -580,7 +581,7 @@ export default function Selector<T extends "Graph" | "Schema" = "Graph" | "Schem
580581
title="Clear all favorites"
581582
onClick={() => {
582583
const newQueries = historyQuery.queries.map(q => ({ ...q, fav: false, name: undefined }));
583-
localStorage.setItem("query history", JSON.stringify(newQueries));
584+
setConnectionItem("query history", JSON.stringify(newQueries));
584585
setHistoryQuery(prev => ({
585586
...prev,
586587
queries: newQueries,

0 commit comments

Comments
 (0)