Spaces:
Running
Running
| const express = require("express"); | |
| const multer = require("multer"); | |
| const path = require("path"); | |
| const fs = require("fs"); | |
| const cors = require("cors"); | |
| const crypto = require("crypto"); | |
| const { exec } = require("child_process"); | |
| const app = express(); | |
| /* ================= CONFIG ================= */ | |
| app.use(cors()); | |
| app.use(express.json()); | |
| const UPLOADS = path.join(__dirname, "uploads"); | |
| const SHARES = path.join(__dirname, "shares.json"); | |
| if (!fs.existsSync(UPLOADS)) fs.mkdirSync(UPLOADS); | |
| if (!fs.existsSync(SHARES)) fs.writeFileSync(SHARES, "{}"); | |
| /* ================= HELPERS ================= */ | |
| const safe = v => | |
| v.replace(/[^a-zA-Z0-9@._/-]/g, "").replace(/\.\./g, ""); | |
| const readShares = () => JSON.parse(fs.readFileSync(SHARES)); | |
| const writeShares = d => | |
| fs.writeFileSync(SHARES, JSON.stringify(d, null, 2)); | |
| const token = () => Math.random().toString(36).slice(2) + Date.now(); | |
| // Helper to calculate directory size and generate tree | |
| const getDirInfo = (dirPath) => { | |
| let size = 0; | |
| const children = fs.readdirSync(dirPath).map(name => { | |
| const fullPath = path.join(dirPath, name); | |
| const stats = fs.statSync(fullPath); | |
| if (stats.isDirectory()) { | |
| const subDir = getDirInfo(fullPath); | |
| size += subDir.size; | |
| return { name, isFolder: true, children: subDir.children, size: subDir.size }; | |
| } | |
| size += stats.size; | |
| return { name, isFolder: false, size: stats.size }; | |
| }); | |
| return { children, size }; | |
| }; | |
| /* ================= PREVIEW STORE (Persistent Logic) ================= */ | |
| // We now use a hash-based approach so the URL is deterministic for the file | |
| const getPersistentToken = (email, filePath) => { | |
| return crypto.createHash('sha256').update(`${email}-${filePath}`).digest('hex').slice(0, 16); | |
| }; | |
| /* ================= FILE EDITOR ================= */ | |
| app.post("/get-file-content", (req, res) => { | |
| const { email, path: filePath } = req.body; | |
| if (!email || !filePath) | |
| return res.status(400).json({ error: "Missing email or path" }); | |
| const fullPath = path.join(UPLOADS, safe(email), safe(filePath)); | |
| if (!fs.existsSync(fullPath)) | |
| return res.status(404).json({ error: "File not found" }); | |
| res.json({ content: fs.readFileSync(fullPath, "utf8") }); | |
| }); | |
| app.post("/save-file-content", (req, res) => { | |
| const { email, path: filePath, content } = req.body; | |
| if (!email || !filePath || content === undefined) | |
| return res.status(400).json({ error: "Missing data" }); | |
| const fullPath = path.join(UPLOADS, safe(email), safe(filePath)); | |
| fs.writeFileSync(fullPath, content, "utf8"); | |
| res.json({ ok: true }); | |
| }); | |
| // DETERMINISTIC PREVIEW URL | |
| app.post("/preview-html", (req, res) => { | |
| const { email, path: filePath } = req.body; | |
| if (!email || !filePath) | |
| return res.status(400).json({ error: "Missing email or path" }); | |
| const t = getPersistentToken(email, filePath); | |
| res.json({ | |
| url: `https://adamyakhairwal2011-vasuki-cloud.hf.space/preview/${email}/${filePath}` | |
| }); | |
| }); | |
| // SERVE PREVIEW (Directly from Disk for Persistence) | |
| app.get("/preview/:email/*", (req, res) => { | |
| const email = safe(req.params.email); | |
| const filePath = safe(req.params[0]); | |
| const fullPath = path.join(UPLOADS, email, filePath); | |
| if (!fs.existsSync(fullPath)) return res.status(404).send("File not found"); | |
| const content = fs.readFileSync(fullPath, "utf8"); | |
| const baseUrl = `https://adamyakhairwal2011-vasuki-cloud.hf.space/download/${email}/`; | |
| res.send(` | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <base href="${baseUrl}"> | |
| <style> | |
| body { margin: 0; font-family: 'Roboto', sans-serif; } | |
| </style> | |
| </head> | |
| <body> | |
| ${content} | |
| <script> | |
| window.addEventListener("message", (e) => { | |
| if (e.data === "VASUKI_RELOAD") { | |
| location.reload(); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> | |
| `); | |
| }); | |
| /* ================= PYTHON COMPILATION ================= */ | |
| app.post("/run-python", (req, res) => { | |
| const { code } = req.body; | |
| if (!code) return res.status(400).json({ error: "No code provided" }); | |
| const fileName = `exec_${Date.now()}.py`; | |
| const filePath = path.join(__dirname, fileName); | |
| fs.writeFileSync(filePath, code); | |
| // Execute with a 5-second timeout for safety | |
| exec(`python3 ${filePath}`, { timeout: 5000 }, (error, stdout, stderr) => { | |
| // Cleanup | |
| if (fs.existsSync(filePath)) fs.unlinkSync(filePath); | |
| if (error && error.killed) { | |
| return res.json({ output: "", error: "Execution timed out (5s limit)" }); | |
| } | |
| res.json({ | |
| output: stdout, | |
| error: stderr | |
| }); | |
| }); | |
| }); | |
| /* ================= ADMIN OPS ================= */ | |
| // CEO Insight: System Health and Storage Tree | |
| app.get("/admin/system-overview", (req, res) => { | |
| try { | |
| const info = getDirInfo(UPLOADS); | |
| res.json({ | |
| status: "online", | |
| totalSize: `${(info.size / (1024 * 1024)).toFixed(2)} MB`, | |
| tree: info.children | |
| }); | |
| } catch (err) { | |
| res.status(500).json({ error: err.message }); | |
| } | |
| }); | |
| /* ================= FILE OPS ================= */ | |
| app.post("/create-file", (req, res) => { | |
| const { email, folder = "", name, extension } = req.body; | |
| if (!email || !name || !extension) | |
| return res.status(400).json({ error: "Missing data" }); | |
| const fileName = `${safe(name)}.${safe(extension)}`; | |
| const fullPath = path.join(UPLOADS, safe(email), safe(folder), fileName); | |
| if (fs.existsSync(fullPath)) | |
| return res.status(409).json({ error: "File already exists" }); | |
| fs.mkdirSync(path.dirname(fullPath), { recursive: true }); | |
| fs.writeFileSync(fullPath, "", "utf8"); | |
| res.json({ ok: true, file: fileName }); | |
| }); | |
| app.post("/create-folder", (req, res) => { | |
| const dir = path.join( | |
| UPLOADS, | |
| safe(req.body.email), | |
| safe(req.body.folder || ""), | |
| safe(req.body.name) | |
| ); | |
| fs.mkdirSync(dir, { recursive: true }); | |
| res.json({ ok: true }); | |
| }); | |
| app.post("/list-files", (req, res) => { | |
| const dir = path.join( | |
| UPLOADS, | |
| safe(req.body.email), | |
| safe(req.body.folder || "") | |
| ); | |
| if (!fs.existsSync(dir)) return res.json({ files: [] }); | |
| const files = fs.readdirSync(dir).map(n => { | |
| const s = fs.statSync(path.join(dir, n)); | |
| return { name: n, isFolder: s.isDirectory(), size: s.size }; | |
| }); | |
| res.json({ files }); | |
| }); | |
| const storage = multer.diskStorage({ | |
| destination: (req, file, cb) => { | |
| const dir = path.join(UPLOADS, safe(req.body.email), safe(req.body.folder || "")); | |
| fs.mkdirSync(dir, { recursive: true }); | |
| cb(null, dir); | |
| }, | |
| filename: (req, file, cb) => cb(null, file.originalname.trim()) | |
| }); | |
| const upload = multer({ storage }); | |
| app.post("/upload", upload.array("files", 20), (req, res) => | |
| res.json({ ok: true }) | |
| ); | |
| app.post("/delete-file", (req, res) => { | |
| const p = path.join(UPLOADS, safe(req.body.email), safe(req.body.path)); | |
| if (!fs.existsSync(p)) return res.status(404).json({ error: "Not found" }); | |
| fs.lstatSync(p).isDirectory() | |
| ? fs.rmSync(p, { recursive: true, force: true }) | |
| : fs.unlinkSync(p); | |
| res.json({ ok: true }); | |
| }); | |
| app.post("/rename-file", (req, res) => { | |
| const oldP = path.join(UPLOADS, safe(req.body.email), safe(req.body.oldPath)); | |
| const newP = path.join(path.dirname(oldP), safe(req.body.newName)); | |
| fs.renameSync(oldP, newP); | |
| res.json({ ok: true }); | |
| }); | |
| app.get("/download/:email/*", (req, res) => { | |
| const p = path.join(UPLOADS, safe(req.params.email), safe(req.params[0])); | |
| res.download(p); | |
| }); | |
| /* ================= SHARING ================= */ | |
| app.post("/share", (req, res) => { | |
| const { email, files, permission } = req.body; | |
| if (!email || !files?.length) | |
| return res.status(400).json({ error: "Invalid request" }); | |
| const db = readShares(); | |
| const t = token(); | |
| db[t] = { | |
| email, | |
| files: files.map(safe), | |
| permission, | |
| expires: Date.now() + 86400000 | |
| }; | |
| writeShares(db); | |
| res.json({ url: `https://vasuki.cloud/share.html?token=${t}` }); | |
| }); | |
| app.get("/shared-info/:token", (req, res) => { | |
| const db = readShares(); | |
| res.json(db[req.params.token] || {}); | |
| }); | |
| app.get("/shared-file/:token/*", (req, res) => { | |
| const db = readShares(); | |
| const s = db[req.params.token]; | |
| if (!s || Date.now() > s.expires) return res.sendStatus(404); | |
| const rel = safe(req.params[0]); | |
| if (!s.files.includes(rel)) return res.sendStatus(403); | |
| const p = path.join(UPLOADS, safe(s.email), rel); | |
| if (!fs.existsSync(p)) return res.sendStatus(404); | |
| res.sendFile(p); | |
| }); | |
| app.get('/', (req, res) => { | |
| res.status(200).send('Vasuki Backend is Online'); | |
| }); | |
| /* ================= START ================= */ | |
| const PORT = process.env.PORT || 3000; | |
| app.listen(PORT, () => { | |
| console.log(`Vasuki Backend running on port ${PORT}`); | |
| }); |