vasuki-cloud / server.js
adamyakhairwal2011's picture
Update server.js
265e155 verified
Raw
History Blame Contribute Delete
8.61 kB
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}`);
});