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