diff --git a/web/bun.lockb b/web/bun.lockb
new file mode 100755
index 00000000..e2076107
Binary files /dev/null and b/web/bun.lockb differ
diff --git a/web/package.json b/web/package.json
index 9e1ee802..54db4c34 100644
--- a/web/package.json
+++ b/web/package.json
@@ -91,6 +91,7 @@
"sql-formatter": "^15.6.6",
"tailwind-merge": "^2.5.5",
"tailwindcss-animate": "^1.0.7",
+ "uuid": "^13.0.0",
"vaul": "^1.1.2",
"vega-embed": "^7.0.2",
"vega-lite": "^6.2.0",
diff --git a/web/src/components/dataset/unified-uploader.tsx b/web/src/components/dataset/unified-uploader.tsx
index f28b8868..b192de47 100644
--- a/web/src/components/dataset/unified-uploader.tsx
+++ b/web/src/components/dataset/unified-uploader.tsx
@@ -15,6 +15,7 @@ import Dashboard from "@uppy/dashboard";
import GoogleDrive from "@uppy/google-drive";
import Url from "@uppy/url";
import AwsS3Multipart from "@uppy/aws-s3";
+import { sanitizeAndUniquifyFilename } from "@/lib/utils/sanitize-filename";
// Import Uppy styles and our custom theme
import "@uppy/core/dist/style.min.css";
@@ -210,6 +211,22 @@ export const UnifiedUploader = forwardRef<
],
},
debug: process.env.NODE_ENV === "development",
+ onBeforeFileAdded: (currentFile) => {
+ setOriginalFileNameRef.current(currentFile.name || "");
+ const originalName = currentFile?.name || '';
+ const modifiedName = sanitizeAndUniquifyFilename(originalName);
+
+ const modifiedFile = {
+ ...currentFile,
+ name: modifiedName, // Update the primary name
+ meta: {
+ ...currentFile.meta,
+ name: modifiedName, // Also update meta name if needed by plugins
+ originalName: originalName, // Store original name in meta if needed later
+ },
+ };
+ return modifiedFile;
+ }
});
// Add Dashboard plugin with proper theme
@@ -260,7 +277,6 @@ export const UnifiedUploader = forwardRef<
try {
setSelectedFile(file);
setUploadError(null);
- setOriginalFileNameRef.current(file.name || "");
// Detect file format
const format = detectFileFormat(
diff --git a/web/src/components/project/inline-project-editor.tsx b/web/src/components/project/inline-project-editor.tsx
index 813eb5d6..7fa42c3f 100644
--- a/web/src/components/project/inline-project-editor.tsx
+++ b/web/src/components/project/inline-project-editor.tsx
@@ -232,21 +232,21 @@ export function InlineProjectEditor({ project }: InlineProjectEditorProps) {
Created:
- {formatDate(project.createdAt)}
+ {formatDate(project.created_at)}
Updated:
- {formatDate(project.updatedAt)}
+ {formatDate(project.updated_at)}
Created by:
- {project.createdBy}
+ {project.created_by}
diff --git a/web/src/components/project/project-card.tsx b/web/src/components/project/project-card.tsx
index 35e57e66..e91d2f29 100644
--- a/web/src/components/project/project-card.tsx
+++ b/web/src/components/project/project-card.tsx
@@ -231,11 +231,11 @@ export function ProjectCard({ project, onUpdate, onDelete }: ProjectCardProps) {
- {project.createdAt && (
+ {project.created_at && (
- {format(new Date(project.createdAt), "MMM d, yyyy")}
+ {format(new Date(project.created_at), "MMM d, yyyy")}
)}
diff --git a/web/src/hooks/use-chat-session.ts b/web/src/hooks/use-chat-session.ts
index 2ba27aa3..936c7c52 100644
--- a/web/src/hooks/use-chat-session.ts
+++ b/web/src/hooks/use-chat-session.ts
@@ -13,6 +13,7 @@ import { useDatasetSql } from "@/lib/mutations/dataset/sql";
import { parseSqlError } from "@/lib/sql-error-utils";
import { useResultsPanelStore } from "@/lib/stores/results-panel-store";
import type { GoPieUIMessage } from "@/types/chat-message";
+import { useAuthStore } from "@/lib/stores/auth-store";
// Constants
const QUERY_INVALIDATION_DELAY_MS = 100; // Delay before invalidating queries to ensure smooth transition
@@ -31,6 +32,8 @@ export function useChatSession({
isNewChat = false,
}: UseChatSessionProps) {
const queryClient = useQueryClient();
+ const { organizationId } = useAuthStore();
+ const isAuthDisabled = String(process.env.NEXT_PUBLIC_ENABLE_AUTH).trim() !== "true";
const { selectChatForDataset } = useChatStore();
const { setIsOpen: setSqlPanelOpen, setResults: setSqlResults, setIsLoading: setSqlLoading, resetExecutedQueries } = useSqlStore();
const { clearPaths, setPaths: setVisualizationPaths } = useVisualizationStore();
@@ -64,7 +67,14 @@ export function useChatSession({
// Create a new chat when needed (for new chats)
useEffect(() => {
- if (isNewChat && !selectedChatId && !chatId && !isPreparingChat && selectedContexts.length > 0) {
+ if (
+ isNewChat &&
+ (isAuthDisabled || organizationId) &&
+ !selectedChatId &&
+ !chatId &&
+ !isPreparingChat &&
+ selectedContexts.length > 0
+ ) {
setIsPreparingChat(true);
// Clear any previous SQL results and visualizations when starting a new chat
@@ -115,7 +125,23 @@ export function useChatSession({
}
);
}
- }, [isNewChat, selectedChatId, chatId, isPreparingChat, selectedContexts, createInitialChatMutation, selectChatForDataset, updateUrlWithChatId, resetExecutedQueries, clearPaths, setSqlResults, setSqlPanelOpen, setActiveTab]);
+ }, [
+ isNewChat,
+ organizationId,
+ selectedChatId,
+ chatId,
+ isPreparingChat,
+ selectedContexts,
+ createInitialChatMutation,
+ selectChatForDataset,
+ updateUrlWithChatId,
+ resetExecutedQueries,
+ clearPaths,
+ setSqlResults,
+ setSqlPanelOpen,
+ setActiveTab,
+ isAuthDisabled
+ ]);
// Update chat ID when selectedChatId changes
useEffect(() => {
diff --git a/web/src/lib/api-client.ts b/web/src/lib/api-client.ts
index 02228be8..f3f5bf56 100644
--- a/web/src/lib/api-client.ts
+++ b/web/src/lib/api-client.ts
@@ -93,11 +93,11 @@ export interface ProjectInput {
export interface Project extends ProjectInput {
id: string;
- createdAt: string;
- updatedAt: string;
+ created_at: string;
+ updated_at: string;
dataset_count: number;
- createdBy: string;
- updatedBy: string;
+ created_by: string;
+ updated_by: string;
custom_prompt?: string;
}
diff --git a/web/src/lib/utils/sanitize-filename.ts b/web/src/lib/utils/sanitize-filename.ts
new file mode 100644
index 00000000..f38a302d
--- /dev/null
+++ b/web/src/lib/utils/sanitize-filename.ts
@@ -0,0 +1,17 @@
+import { v4 as uuidv4 } from 'uuid';
+/**
+ * Generates a unique filename using UUID while preserving the original extension.
+ * This prevents filename collisions and ensures safe storage.
+ *
+ * @param originalName - The original filename
+ * @returns A UUID-based filename in format: "uuid.extension"
+ * @example
+ * sanitizeAndUniquifyFilename("my-file.csv")
+ * // Returns: "550e8400-e29b-41d4-a716-446655440000.csv"
+ */
+export function sanitizeAndUniquifyFilename(originalName: string): string {
+ const uniqueId = uuidv4();
+ const extensionMatch = originalName.match(/\.[^.]+$/);
+ const extension = extensionMatch ? extensionMatch[0] : '';
+ return `${uniqueId}${extension}`;
+}
\ No newline at end of file