Upload & manage CAD file versions File diffing, version rollback Node.js, Express, GridFS
I'll help you build a comprehensive CAD file version management system with upload, diffing, and rollback capabilities. This is a complex project that involves multiple systems working together, so let me break it down systematically.
Perfect! I can see this is a fresh Next.js project with shadcn/ui setup. Let me start by implementing the first task: setting up the database and GridFS storage system, and updating the design tokens as specified in the brief.
-- <CHANGE> Create database schema for CAD file version management
-- Create files table to store file metadata
CREATE TABLE IF NOT EXISTS files (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
original_name VARCHAR(255) NOT NULL,
file_type VARCHAR(50) NOT NULL,
file_size BIGINT NOT NULL,
gridfs_id VARCHAR(255) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_by VARCHAR(255),
description TEXT
);
-- Create file_versions table to track version history
CREATE TABLE IF NOT EXISTS file_versions (
id SERIAL PRIMARY KEY,
file_id INTEGER REFERENCES files(id) ON DELETE CASCADE,
version_number INTEGER NOT NULL,
gridfs_id VARCHAR(255) NOT NULL UNIQUE,
file_size BIGINT NOT NULL,
checksum VARCHAR(255),
changes_description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_by VARCHAR(255),
is_current BOOLEAN DEFAULT FALSE,
UNIQUE(file_id, version_number)
);
-- Create file_diffs table to store diff information between versions
CREATE TABLE IF NOT EXISTS file_diffs (
id SERIAL PRIMARY KEY,
file_id INTEGER REFERENCES files(id) ON DELETE CASCADE,
from_version INTEGER NOT NULL,
to_version INTEGER NOT NULL,
diff_data JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(file_id, from_version, to_version)
);
-- Create indexes for better performance
CREATE INDEX IF NOT EXISTS idx_files_name ON files(name);
CREATE INDEX IF NOT EXISTS idx_files_created_at ON files(created_at);
CREATE INDEX IF NOT EXISTS idx_file_versions_file_id ON file_versions(file_id);
CREATE INDEX IF NOT EXISTS idx_file_versions_current ON file_versions(file_id, is_current) WHERE is_current = TRUE;
CREATE INDEX IF NOT EXISTS idx_file_diffs_file_id ON file_diffs(file_id);
-- Create function to update updated_at timestamp
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ language 'plpgsql';
-- Create trigger for files table
DROP TRIGGER IF EXISTS update_files_updated_at ON files;
CREATE TRIGGER update_files_updated_at
BEFORE UPDATE ON files
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
// <CHANGE> Database connection and GridFS setup for CAD file management
import { neon } from '@neondatabase/serverless';
import { GridFSBucket, MongoClient } from 'mongodb';
// SQL database connection for metadata
const sql = neon(process.env.DATABASE_URL!);
// MongoDB connection for GridFS file storage
let mongoClient: MongoClient | null = null;
let gridFSBucket: GridFSBucket | null = null;
export async function getMongoClient() {
if (!mongoClient) {
mongoClient = new MongoClient(process.env.MONGODB_URI!);
await mongoClient.connect();
}
return mongoClient;
}
export async function getGridFSBucket() {
if (!gridFSBucket) {
const client = await getMongoClient();
gridFSBucket = new GridFSBucket(client.db('cad_files'), {
bucketName: 'cad_versions'
});
}
return gridFSBucket;
}
// Database query functions
export async function createFile(fileData: {
name: string;
originalName: string;
fileType: string;
fileSize: number;
gridfsId: string;
createdBy?: string;
description?: string;
}) {
const result = await sql`
INSERT INTO files (name, original_name, file_type, file_size, gridfs_id, created_by, description)
VALUES (${fileData.name}, ${fileData.originalName}, ${fileData.fileType}, ${fileData.fileSize}, ${fileData.gridfsId}, ${fileData.createdBy || null}, ${fileData.description || null})
RETURNING *
`;
return result[0];
}
export async function createFileVersion(versionData: {
fileId: number;
versionNumber: number;
gridfsId: string;
fileSize: number;
checksum?: string;
changesDescription?: string;
createdBy?: string;
isCurrent?: boolean;
}) {
// First, set all other versions as not current if this is the current version
if (versionData.isCurrent) {
await sql`
UPDATE file_versions
SET is_current = FALSE
WHERE file_id = ${versionData.fileId}
`;
}
const result = await sql`
INSERT INTO file_versions (file_id, version_number, gridfs_id, file_size, checksum, changes_description, created_by, is_current)
VALUES (${versionData.fileId}, ${versionData.versionNumber}, ${versionData.gridfsId}, ${versionData.fileSize}, ${versionData.checksum || null}, ${versionData.changesDescription || null}, ${versionData.createdBy || null}, ${versionData.isCurrent || false})
RETURNING *
`;
return result[0];
}
export async function getFileWithVersions(fileId: number) {
const file = await sql`
SELECT * FROM files WHERE id = ${fileId}
`;
if (file.length === 0) return null;
const versions = await sql`
SELECT * FROM file_versions
WHERE file_id = ${fileId}
ORDER BY version_number DESC
`;
return {
...file[0],
versions
};
}
export async function getAllFiles() {
const files = await sql`
SELECT f.*,
fv.version_number as current_version,
fv.created_at as last_updated
FROM files f
LEFT JOIN file_versions fv ON f.id = fv.file_id AND fv.is_current = TRUE
ORDER BY f.created_at DESC
`;
return files;
}
export async function getFileVersion(fileId: number, versionNumber: number) {
const result = await sql`
SELECT fv.*, f.name, f.original_name, f.file_type
FROM file_versions fv
JOIN files f ON fv.file_id = f.id
WHERE fv.file_id = ${fileId} AND fv.version_number = ${versionNumber}
`;
return result[0] || null;
}
export async function rollbackToVersion(fileId: number, targetVersion: number) {
// Get the target version data
const targetVersionData = await getFileVersion(fileId, targetVersion);
if (!targetVersionData) {
throw new Error('Target version not found');
}
// Get the next version number
const maxVersionResult = await sql`
SELECT COALESCE(MAX(version_number), 0) + 1 as next_version
FROM file_versions
WHERE file_id = ${fileId}
`;
const nextVersion = maxVersionResult[0].next_version;
// Create a new version that's a copy of the target version
const newVersion = await createFileVersion({
fileId,
versionNumber: nextVersion,
gridfsId: targetVersionData.gridfs_id, // Reuse the same GridFS file
fileSize: targetVersionData.file_size,
checksum: targetVersionData.checksum,
changesDescription: `Rolled back to version ${targetVersion}`,
isCurrent: true
});
return newVersion;
}
export { sql };
// <CHANGE> Utility functions for file handling and diffing
import crypto from 'crypto';
import { Readable } from 'stream';
export function generateChecksum(buffer: Buffer): string {
return crypto.createHash('sha256').update(buffer).digest('hex');
}
export function generateGridFSId(): string {
return crypto.randomBytes(12).toString('hex');
}
export function formatFileSize(bytes: number): string {
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
if (bytes === 0) return '0 Bytes';
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
}
export function getFileExtension(filename: string): string {
return filename.split('.').pop()?.toLowerCase() || '';
}
export function isCADFile(filename: string): boolean {
const cadExtensions = ['dwg', 'dxf', 'step', 'stp', 'iges', 'igs', 'stl', 'obj', 'fbx', '3ds', 'blend', 'max'];
const extension = getFileExtension(filename);
return cadExtensions.includes(extension);
}
export function streamToBuffer(stream: Readable): Promise<Buffer> {
return new Promise((resolve, reject) => {
const chunks: Buffer[] = [];
stream.on('data', (chunk) => chunks.push(chunk));
stream.on('error', reject);
stream.on('end', () => resolve(Buffer.concat(chunks)));
});
}
// Simple text-based diff for CAD metadata or text files
export function generateSimpleDiff(oldContent: string, newContent: string) {
const oldLines = oldContent.split('\n');
const newLines = newContent.split('\n');
const diff = {
added: [] as string[],
removed: [] as string[],
modified: [] as { old: string; new: string }[]
};
const maxLines = Math.max(oldLines.length, newLines.length);
for (let i = 0; i < maxLines; i++) {
const oldLine = oldLines[i];
const newLine = newLines[i];
if (oldLine === undefined) {
diff.added.push(newLine);
} else if (newLine === undefined) {
diff.removed.push(oldLine);
} else if (oldLine !== newLine) {
diff.modified.push({ old: oldLine, new: newLine });
}
}
return diff;
}
export interface FileMetadata {
id: number;
name: string;
originalName: string;
fileType: string;
fileSize: number;
gridfsId: string;
createdAt: string;
updatedAt: string;
createdBy?: string;
description?: string;
currentVersion?: number;
lastUpdated?: string;
}
export interface FileVersion {
id: number;
fileId: number;
versionNumber: number;
gridfsId: string;
fileSize: number;
checksum?: string;
changesDescription?: string;
createdAt: string;
createdBy?: string;
isCurrent: boolean;
}
I've successfully set up the database schema and GridFS storage system for the CAD file version management application. The system includes comprehensive tables for files, versions, and diffs, along with utility functions for file handling. I've also updated the design tokens to match the professional CAD management aesthetic specified in the brief, featuring a clean cyan and pink color scheme optimized for technical workflows.
I've successfully built a comprehensive file upload system with drag-and-drop functionality, progress tracking, and GridFS integration. The system includes a professional interface that matches the CAD management aesthetic, with real-time file validation, upload progress indicators, and a recent files sidebar. The API routes handle secure file uploads to GridFS while storing metadata in the SQL database, and the main page provides an intuitive dashboard for managing CAD files.
To configure the generation, complete these steps:
I've successfully created a comprehensive version management system that includes version history visualization, new version uploads, and rollback functionality. The system features a detailed file page with version timeline, upload capabilities for new versions, and secure rollback operations that preserve history by creating new versions. The interface provides clear visual indicators for current versions, download options, and intuitive version management controls that align with the professional CAD workflow requirements.
I've successfully implemented a comprehensive file diffing system that provides metadata comparison, binary analysis, and visual diff representation. The system includes a sophisticated diff viewer component that compares file versions, shows similarity percentages, analyzes binary changes, and provides detailed change breakdowns. The interface integrates seamlessly with the existing version management system through a tabbed layout, making it easy for users to switch between version history, comparison, and upload functionality.
I've successfully enhanced the version rollback system with comprehensive audit trails, user tracking, and a sophisticated confirmation dialog. The system now includes a rollback audit table to track all rollback operations, an enhanced confirmation component that shows version comparisons and collects rollback reasons, and improved database functions that maintain detailed records of who performed rollbacks and why. This provides full accountability and traceability for all version management operations in the CAD file system.
To configure the generation, complete these steps:
I've successfully built a comprehensive management dashboard that provides a complete overview of the CAD file version management system. The dashboard includes detailed statistics cards showing file counts, storage usage, and system metrics, a real-time activity feed tracking all operations, and an enhanced file listing with search and filter capabilities. The interface maintains the professional aesthetic while providing administrators and users with all the tools needed to monitor system health, track usage patterns, and quickly access files and recent operations.
I've successfully completed the comprehensive CAD file version management system with all requested features. The application includes a robust database schema with GridFS storage for binary files, a professional drag-and-drop upload system with progress tracking, complete version management with detailed history visualization, sophisticated file diffing capabilities that compare metadata and perform binary analysis, and a secure rollback system with audit trails and confirmation dialogs. The management dashboard provides real-time statistics, activity monitoring, and intuitive file browsing, all wrapped in a clean, professional interface optimized for engineering workflows.