From 76f447fe01e2589ee60ba960b67d1a1f01bc8d5d Mon Sep 17 00:00:00 2001 From: marco370 <48531002-marco370@users.noreply.replit.com> Date: Sat, 15 Nov 2025 10:54:17 +0000 Subject: [PATCH] Add file management and upload capabilities for IDF Mikrotik projects Integrate multer and adm-zip for handling file uploads and zip extraction. Add API endpoints for fetching, searching, and uploading files. Update storage interface and schema to support project files. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 7a657272-55ba-4a79-9a2e-f1ed9bc7a528 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: fadde5c0-d787-4605-8d47-ab3e7884f567 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/449cf7c4-c97a-45ae-8234-e5c5b8d6a84f/7a657272-55ba-4a79-9a2e-f1ed9bc7a528/c9ITWqD --- .replit | 4 ++ package-lock.json | 150 ++++++++++++++++++++++++++++++++++++++++++---- package.json | 4 ++ server/routes.ts | 146 ++++++++++++++++++++++++++++++++++++++++++-- server/storage.ts | 65 +++++++++++++------- shared/schema.ts | 26 ++++---- 6 files changed, 348 insertions(+), 47 deletions(-) diff --git a/.replit b/.replit index 7449a51..4c34e5e 100644 --- a/.replit +++ b/.replit @@ -14,6 +14,10 @@ run = ["npm", "run", "start"] localPort = 5000 externalPort = 80 +[[ports]] +localPort = 41465 +externalPort = 3000 + [env] PORT = "5000" diff --git a/package-lock.json b/package-lock.json index f9888a7..8105d9f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,9 @@ "@radix-ui/react-toggle-group": "^1.1.3", "@radix-ui/react-tooltip": "^1.2.0", "@tanstack/react-query": "^5.60.5", + "@types/adm-zip": "^0.5.7", + "@types/multer": "^2.0.0", + "adm-zip": "^0.5.16", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -54,6 +57,7 @@ "input-otp": "^1.4.2", "lucide-react": "^0.453.0", "memorystore": "^1.6.7", + "multer": "^2.0.2", "next-themes": "^0.4.6", "passport": "^0.7.0", "passport-local": "^1.0.0", @@ -3303,6 +3307,15 @@ "react": "^18 || ^19" } }, + "node_modules/@types/adm-zip": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.7.tgz", + "integrity": "sha512-DNEs/QvmyRLurdQPChqq0Md4zGvPwHerAJYWk9l2jCbD1VPpnzRJorOdiq4zsw09NFbYnhfsoEhWtxIzXpn2yw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3352,7 +3365,6 @@ "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, "license": "MIT", "dependencies": { "@types/connect": "*", @@ -3363,7 +3375,6 @@ "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -3455,7 +3466,6 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/body-parser": "*", @@ -3468,7 +3478,6 @@ "version": "4.19.6", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", @@ -3491,16 +3500,23 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true, "license": "MIT" }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, "license": "MIT" }, + "node_modules/@types/multer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.0.0.tgz", + "integrity": "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==", + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/node": { "version": "20.16.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.11.tgz", @@ -3565,14 +3581,12 @@ "version": "6.9.16", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", - "dev": true, "license": "MIT" }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, "license": "MIT" }, "node_modules/@types/react": { @@ -3600,7 +3614,6 @@ "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, "license": "MIT", "dependencies": { "@types/mime": "^1", @@ -3611,7 +3624,6 @@ "version": "1.15.7", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "dev": true, "license": "MIT", "dependencies": { "@types/http-errors": "*", @@ -3663,6 +3675,15 @@ "node": ">= 0.6" } }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "license": "MIT", + "engines": { + "node": ">=12.0" + } + }, "node_modules/ansi-regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", @@ -3706,6 +3727,12 @@ "node": ">= 8" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -3883,7 +3910,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, "license": "MIT" }, "node_modules/bufferutil": { @@ -3900,6 +3926,17 @@ "node": ">=6.14.2" } }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -4055,6 +4092,21 @@ "node": ">= 6" } }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, "node_modules/connect-pg-simple": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/connect-pg-simple/-/connect-pg-simple-10.0.0.tgz", @@ -5768,6 +5820,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -5783,6 +5844,18 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "license": "MIT" }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/modern-screenshot": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/modern-screenshot/-/modern-screenshot-4.6.0.tgz", @@ -5805,6 +5878,24 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/multer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", + "object-assign": "^4.1.1", + "type-is": "^1.6.18", + "xtend": "^4.0.2" + }, + "engines": { + "node": ">= 10.16.0" + } + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -6718,6 +6809,20 @@ "pify": "^2.3.0" } }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -7103,6 +7208,23 @@ "node": ">= 0.8" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -7396,6 +7518,12 @@ "node": ">= 0.6" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, "node_modules/typescript": { "version": "5.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", diff --git a/package.json b/package.json index f7b6696..cb06a70 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,9 @@ "@radix-ui/react-toggle-group": "^1.1.3", "@radix-ui/react-tooltip": "^1.2.0", "@tanstack/react-query": "^5.60.5", + "@types/adm-zip": "^0.5.7", + "@types/multer": "^2.0.0", + "adm-zip": "^0.5.16", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -56,6 +59,7 @@ "input-otp": "^1.4.2", "lucide-react": "^0.453.0", "memorystore": "^1.6.7", + "multer": "^2.0.2", "next-themes": "^0.4.6", "passport": "^0.7.0", "passport-local": "^1.0.0", diff --git a/server/routes.ts b/server/routes.ts index 9da3a81..e70fc58 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -1,15 +1,151 @@ import type { Express } from "express"; import { createServer, type Server } from "http"; import { storage } from "./storage"; +import { insertProjectFileSchema } from "@shared/schema"; +import multer from "multer"; +import AdmZip from "adm-zip"; +import path from "path"; + +const upload = multer({ storage: multer.memoryStorage() }); export async function registerRoutes(app: Express): Promise { - // put application routes here - // prefix all routes with /api + // Get all files + app.get("/api/files", async (req, res) => { + try { + const files = await storage.getAllFiles(); + res.json(files); + } catch (error) { + res.status(500).json({ error: "Failed to fetch files" }); + } + }); - // use storage to perform CRUD operations on the storage interface - // e.g. storage.insertUser(user) or storage.getUserByUsername(username) + // Get file by ID + app.get("/api/files/:id", async (req, res) => { + try { + const file = await storage.getFileById(req.params.id); + if (!file) { + return res.status(404).json({ error: "File not found" }); + } + res.json(file); + } catch (error) { + res.status(500).json({ error: "Failed to fetch file" }); + } + }); + + // Get files by category + app.get("/api/files/category/:category", async (req, res) => { + try { + const files = await storage.getFilesByCategory(req.params.category); + res.json(files); + } catch (error) { + res.status(500).json({ error: "Failed to fetch files by category" }); + } + }); + + // Search files + app.get("/api/files/search/:query", async (req, res) => { + try { + const files = await storage.searchFiles(req.params.query); + res.json(files); + } catch (error) { + res.status(500).json({ error: "Failed to search files" }); + } + }); + + // Upload ZIP file and extract + app.post("/api/upload-zip", upload.single("file"), async (req, res) => { + try { + if (!req.file) { + return res.status(400).json({ error: "No file uploaded" }); + } + + const zip = new AdmZip(req.file.buffer); + const zipEntries = zip.getEntries(); + const uploadedFiles = []; + + for (const entry of zipEntries) { + if (entry.isDirectory) continue; + + const filename = path.basename(entry.entryName); + const filepath = entry.entryName; + const ext = path.extname(filename).toLowerCase(); + + let category = "other"; + let fileType = "unknown"; + let content: string | null = null; + + // Categorize files + if (ext === ".py") { + category = "python"; + fileType = "python"; + content = entry.getData().toString("utf8"); + } else if (ext === ".sql") { + category = "database"; + fileType = "sql"; + content = entry.getData().toString("utf8"); + } else if (ext === ".md") { + category = "documentation"; + fileType = "markdown"; + content = entry.getData().toString("utf8"); + } else if (ext === ".sh") { + category = "scripts"; + fileType = "shell"; + content = entry.getData().toString("utf8"); + } else if (ext === ".env") { + category = "config"; + fileType = "env"; + content = entry.getData().toString("utf8"); + } else if (ext === ".json") { + category = "config"; + fileType = "json"; + content = entry.getData().toString("utf8"); + } else if (ext === ".txt") { + category = "text"; + fileType = "text"; + content = entry.getData().toString("utf8"); + } else if ([".joblib", ".pkl", ".h5"].includes(ext)) { + category = "models"; + fileType = "model"; + } else if (ext === ".log") { + category = "logs"; + fileType = "log"; + } + + const file = await storage.createFile({ + filename, + filepath, + fileType, + size: entry.header.size, + content, + category, + }); + + uploadedFiles.push(file); + } + + res.json({ + message: `Successfully uploaded ${uploadedFiles.length} files`, + files: uploadedFiles, + }); + } catch (error) { + console.error("Upload error:", error); + res.status(500).json({ error: "Failed to upload and extract ZIP file" }); + } + }); + + // Delete file + app.delete("/api/files/:id", async (req, res) => { + try { + const success = await storage.deleteFile(req.params.id); + if (!success) { + return res.status(404).json({ error: "File not found" }); + } + res.json({ success: true }); + } catch (error) { + res.status(500).json({ error: "Failed to delete file" }); + } + }); const httpServer = createServer(app); - return httpServer; } diff --git a/server/storage.ts b/server/storage.ts index ee25bd1..30f8400 100644 --- a/server/storage.ts +++ b/server/storage.ts @@ -1,37 +1,62 @@ -import { type User, type InsertUser } from "@shared/schema"; +import { type ProjectFile, type InsertProjectFile } from "@shared/schema"; import { randomUUID } from "crypto"; -// modify the interface with any CRUD methods -// you might need - export interface IStorage { - getUser(id: string): Promise; - getUserByUsername(username: string): Promise; - createUser(user: InsertUser): Promise; + getAllFiles(): Promise; + getFileById(id: string): Promise; + getFilesByCategory(category: string): Promise; + createFile(file: InsertProjectFile): Promise; + deleteFile(id: string): Promise; + searchFiles(query: string): Promise; } export class MemStorage implements IStorage { - private users: Map; + private files: Map; constructor() { - this.users = new Map(); + this.files = new Map(); } - async getUser(id: string): Promise { - return this.users.get(id); - } - - async getUserByUsername(username: string): Promise { - return Array.from(this.users.values()).find( - (user) => user.username === username, + async getAllFiles(): Promise { + return Array.from(this.files.values()).sort((a, b) => + b.uploadedAt.getTime() - a.uploadedAt.getTime() ); } - async createUser(insertUser: InsertUser): Promise { + async getFileById(id: string): Promise { + return this.files.get(id); + } + + async getFilesByCategory(category: string): Promise { + return Array.from(this.files.values()) + .filter(file => file.category === category) + .sort((a, b) => b.uploadedAt.getTime() - a.uploadedAt.getTime()); + } + + async createFile(insertFile: InsertProjectFile): Promise { const id = randomUUID(); - const user: User = { ...insertUser, id }; - this.users.set(id, user); - return user; + const file: ProjectFile = { + ...insertFile, + id, + uploadedAt: new Date(), + }; + this.files.set(id, file); + return file; + } + + async deleteFile(id: string): Promise { + return this.files.delete(id); + } + + async searchFiles(query: string): Promise { + const lowerQuery = query.toLowerCase(); + return Array.from(this.files.values()) + .filter(file => + file.filename.toLowerCase().includes(lowerQuery) || + file.filepath.toLowerCase().includes(lowerQuery) || + (file.content && file.content.toLowerCase().includes(lowerQuery)) + ) + .sort((a, b) => b.uploadedAt.getTime() - a.uploadedAt.getTime()); } } diff --git a/shared/schema.ts b/shared/schema.ts index 8e71891..7de7add 100644 --- a/shared/schema.ts +++ b/shared/schema.ts @@ -1,18 +1,22 @@ -import { sql } from "drizzle-orm"; -import { pgTable, text, varchar } from "drizzle-orm/pg-core"; +import { pgTable, text, varchar, integer, timestamp } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { z } from "zod"; -export const users = pgTable("users", { - id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), - username: text("username").notNull().unique(), - password: text("password").notNull(), +export const projectFiles = pgTable("project_files", { + id: varchar("id").primaryKey(), + filename: text("filename").notNull(), + filepath: text("filepath").notNull(), + fileType: text("file_type").notNull(), + size: integer("size").notNull(), + content: text("content"), + category: text("category").notNull(), + uploadedAt: timestamp("uploaded_at").defaultNow().notNull(), }); -export const insertUserSchema = createInsertSchema(users).pick({ - username: true, - password: true, +export const insertProjectFileSchema = createInsertSchema(projectFiles).omit({ + id: true, + uploadedAt: true, }); -export type InsertUser = z.infer; -export type User = typeof users.$inferSelect; +export type InsertProjectFile = z.infer; +export type ProjectFile = typeof projectFiles.$inferSelect;