Spaces:
Running
Running
const express = require("express"); | |
const cors = require("cors"); | |
const multer = require("multer"); | |
const fs = require("fs"); | |
const path = require("path"); | |
const os = require("os"); | |
const { execSync } = require("child_process"); | |
const { formatDate, formatSize} = require("./function.js"); | |
const FILE_DIR = path.join(__dirname, "files"); | |
const EXPIRATION_TIME = 24 * 60 * 60 * 1000; // 24 jam dalam milidetik | |
const META_FILE = path.join(__dirname, "list.json"); | |
// Load metadata file (kalau ada) | |
let fileData = {}; | |
if (fs.existsSync(META_FILE)) { | |
fileData = JSON.parse(fs.readFileSync(META_FILE)); | |
} | |
function formatRemainingTime(expiresAt) { | |
const remainingMs = expiresAt - Date.now(); | |
if (remainingMs <= 0) return "Expired"; | |
const hours = Math.floor(remainingMs / (1000 * 60 * 60)); | |
const minutes = Math.floor((remainingMs % (1000 * 60 * 60)) / (1000 * 60)); | |
const seconds = Math.floor((remainingMs % (1000 * 60)) / 1000); | |
return `${hours}h ${minutes}m ${seconds}s`; | |
} | |
const app = express(); | |
const port = process.env.PORT || 7860; | |
const uploadDir = path.join(__dirname, "files"); | |
if (!fs.existsSync(uploadDir)) { | |
fs.mkdirSync(uploadDir, { recursive: true }); | |
} | |
app.use(cors()); | |
app.use("/files", express.static(uploadDir)); | |
const storage = multer.diskStorage({ | |
destination: (req, file, cb) => { | |
cb(null, FILE_DIR); | |
}, | |
filename: (req, file, cb) => { | |
const uniqueName = `${file.originalname}`; | |
cb(null, uniqueName); | |
} | |
}); | |
const upload = multer({ storage }); | |
app.post("/upload", upload.single("file"), (req, res) => { | |
if (!req.file) { | |
return res.status(400).json({ success: false, message: "No file uploaded" }); | |
} | |
const filePath = req.file.filename; | |
const customExpiration = req.body.expires_in ? parseInt(req.body.expires_in) * 1000 : req.params.expires_in ? parseInt(req.params.expires_in) * 1000 : EXPIRATION_TIME; | |
const expiresAt = Date.now() + customExpiration; | |
fileData[filePath] = { | |
isPrivate: false, | |
uploader: "Anonymous", | |
file_name: req.file.originalname, | |
file_url: `${req.protocol}://${req.get("host")}/files/${filePath}`, | |
file_size: req.file.size, | |
fileSize: formatSize(req.file.size), | |
file_type: req.file.mimetype, | |
uploadTime: Date.now(), | |
uploadTimeFormat: formatDate(Date.now()), | |
expiredAt: expiresAt, | |
expired: formatDate(expiresAt) | |
}; | |
fs.writeFile(META_FILE, JSON.stringify(fileData, null, 2), (err) => { | |
if (err) console.error("Gagal menulis ke api.json:", err); | |
}); | |
res.json({ | |
status: true, | |
file_name: req.file.originalname, | |
file_size: req.file.size, | |
file_type: req.file.mimetype, | |
uploadTime: Date.now(), | |
file_url: `${req.protocol}://${req.get("host")}/files/${filePath}`, | |
expires_in: fileData[filePath] ? formatRemainingTime(fileData[filePath].expiredAt) : "Unknown" | |
// Format sesuai sisa waktu | |
}); | |
}); | |
app.get("/files/browse", (req, res) => { | |
const page = parseInt(req.query.page) || 1; | |
const limit = 5; | |
// Filter hanya file yang isPrivate = false | |
const publicFiles = Object.keys(fileData).filter(filename => fileData[filename]?.isPrivate === false); | |
const startIndex = (page - 1) * limit; | |
const endIndex = startIndex + limit; | |
const paginatedFiles = publicFiles.slice(startIndex, endIndex).map(filename => ({ | |
filename, | |
file_url: `${req.protocol}://${req.get("host")}/files/${filename}`, | |
expires_in: formatRemainingTime(fileData[filename]?.expiredAt), | |
file_size: fileData[filename]?.fileSize, | |
file_type: fileData[filename]?.file_type, | |
uploadTime: fileData[filename]?.uploadTimeFormat | |
})); | |
res.json({ | |
success: true, | |
current_page: page, | |
total_pages: Math.ceil(publicFiles.length / limit), | |
total_files: publicFiles.length, | |
files: paginatedFiles | |
}); | |
}); | |
app.delete("/files/:filename", (req, res) => { | |
const filePath = path.join(uploadDir, req.params.filename); | |
fs.unlink(filePath, err => { | |
if (err) return res.status(404).json({ success: false, message: "File not found" }); | |
res.json({ success: true, message: "File deleted successfully!" }); | |
}); | |
}); | |
app.get("/", (req, res) => { | |
res.json({ | |
success: true, | |
message: "Welcome to Temporary Cloud File API ๐", | |
usage: { | |
upload_file: { | |
method: "POST", | |
endpoint: "/upload", | |
description: "Upload a file", | |
body: "FormData (key: file)", | |
params: "expires_in: number", | |
response: { | |
success: true, | |
file_url: "string", | |
expires_in: "string" | |
} | |
}, | |
list_files: { | |
method: "GET", | |
endpoint: "/files/browse", | |
description: "Get all uploaded files", | |
response: { | |
success: true, | |
current_page: "number", | |
total_pages: "number", | |
total_files: "number", | |
files: [{ | |
filename: "string", | |
file_url: "string", | |
expires_in: "number", | |
file_size: "number", | |
file_type: "number", | |
uploadTime: "number", | |
}] | |
} | |
}, | |
delete_file: { | |
method: "DELETE", | |
endpoint: "/files/:filename", | |
description: "Delete a file", | |
response: { | |
success: true, | |
message: "File deleted successfully!" | |
} | |
}, | |
check_server: { | |
method: "GET", | |
endpoint: "/status", | |
description: "Check server status (RAM, CPU, uptime, etc.)" | |
} | |
}, | |
author: "rizxyu", | |
github: "https://github.com/rizxyu" | |
}); | |
}); | |
app.get("/status", (req, res) => { | |
const uptime = process.uptime(); | |
const memoryUsage = process.memoryUsage(); | |
const cpuLoad = os.loadavg(); | |
let diskUsage = "Not available"; | |
try { | |
diskUsage = execSync("df -h / | tail -1 | awk '{print $3 \" used / \" $2 \" total (\" $5 \" used)\"}'").toString().trim(); | |
} catch (err) { | |
diskUsage = "Error fetching disk usage"; | |
} | |
res.json({ | |
success: true, | |
server_time: new Date().toISOString(), | |
uptime: `${Math.floor(uptime / 3600)}h ${Math.floor((uptime % 3600) / 60)}m ${Math.floor(uptime % 60)}s`, | |
memory: { | |
total: `${(os.totalmem() / 1024 / 1024).toFixed(2)} MB`, | |
used: `${(memoryUsage.rss / 1024 / 1024).toFixed(2)} MB` | |
}, | |
cpu: { | |
cores: os.cpus().length, | |
load_avg: cpuLoad.map(load => load.toFixed(2)) | |
}, | |
disk: diskUsage, | |
node_version: process.version | |
}); | |
}); | |
setInterval(() => { | |
const now = Date.now(); | |
let updatedFileData = {}; | |
Object.keys(fileData).forEach(filename => { | |
const filePath = path.join(FILE_DIR, filename); | |
if (fileData[filename].expiredAt <= now) { | |
if (fs.existsSync(filePath)) { | |
fs.unlinkSync(filePath); | |
console.log(`๐๏ธ File expired dihapus: ${filename}`); | |
} | |
} else { | |
updatedFileData[filename] = fileData[filename]; | |
} | |
}); | |
// Update metadata file hanya jika ada perubahan | |
if (Object.keys(updatedFileData).length !== Object.keys(fileData).length) { | |
fileData = updatedFileData; | |
fs.writeFileSync(META_FILE, JSON.stringify(fileData, null, 2)); | |
} | |
}, 60 * 60 * 1000); // Cek tiap 1 jam | |
app.listen(port, () => { | |
console.log("Listening on http://localhost:" + port); | |
}); |