134 lines
4.4 KiB
JavaScript
134 lines
4.4 KiB
JavaScript
import path from 'path';
|
|
import { createWriteStream } from 'fs';
|
|
import { pipeline } from 'stream';
|
|
import { promisify } from 'util';
|
|
import multer from 'multer';
|
|
import { promises as fs } from 'fs';
|
|
|
|
export async function getDirectoryContents(dirPath) {
|
|
try {
|
|
await fs.access(dirPath);
|
|
} catch {
|
|
// Create directory if it doesn't exist
|
|
await fs.mkdir(dirPath, { recursive: true });
|
|
}
|
|
|
|
const items = await fs.readdir(dirPath, { withFileTypes: true });
|
|
const files = [];
|
|
|
|
console.log(dirPath, items);
|
|
|
|
for (const item of items) {
|
|
const itemPath = path.join(dirPath, item.name);
|
|
const stats = await fs.stat(itemPath);
|
|
|
|
files.push({
|
|
name: item.name,
|
|
path: itemPath,
|
|
isDirectory: item.isDirectory(),
|
|
size: item.isDirectory() ? 0 : stats.size,
|
|
modified: stats.mtime.toISOString()
|
|
});
|
|
}
|
|
|
|
// Sort: directories first, then files, both alphabetically
|
|
return files.sort((a, b) => {
|
|
if (a.isDirectory && !b.isDirectory) return -1;
|
|
if (!a.isDirectory && b.isDirectory) return 1;
|
|
return a.name.localeCompare(b.name);
|
|
});
|
|
}
|
|
|
|
export function upload(path, zimOnly = false) {
|
|
const storage = multer.diskStorage({
|
|
destination: async (req, file, cb) => {
|
|
const uploadPath = req.body.uploadPath || path;
|
|
try {
|
|
await fs.mkdir(uploadPath, { recursive: true });
|
|
cb(null, uploadPath);
|
|
} catch (err) {
|
|
cb(err);
|
|
}
|
|
},
|
|
filename: (req, file, cb) => {
|
|
let finalFilename = file.originalname;
|
|
if(req.body.filename && req.body.filename.trim() !== '') {
|
|
finalFilename = req.body.filename.trim();
|
|
if(!finalFilename.toLowerCase().endsWith('.zim')) {
|
|
finalFilename += '.zim';
|
|
}
|
|
}
|
|
cb(null, finalFilename);
|
|
}
|
|
});
|
|
|
|
return multer({
|
|
storage,
|
|
fileFilter: (req, file, cb) => {
|
|
if(!zimOnly) cb(null, true);
|
|
else if(/\.zim$/i.test(file.originalname)) cb(null, true);
|
|
else cb(new Error('Only ZIM files allowed'));
|
|
}
|
|
});
|
|
}
|
|
const pipelineAsync = promisify(pipeline);
|
|
|
|
export async function downloadFile(url, destination) {
|
|
if(!url.endsWith('.zim')) throw new Error('Only ZIM files allowed');
|
|
if(!destination.endsWith('.zim')) destination += '.zim';
|
|
const response = await fetch(url);
|
|
if (!response.ok) throw new Error(`HTTP error! ${response.status}`);
|
|
const writer = createWriteStream(destination);
|
|
await pipelineAsync(response.body, writer);
|
|
return destination;
|
|
}
|
|
|
|
export function parseSize(sizeStr) {
|
|
if (sizeStr.toLowerCase() === 'infinity') return Infinity;
|
|
const units = { B: 1, KB: 1024, MB: 1024 ** 2, GB: 1024 ** 3 };
|
|
const match = sizeStr.match(/^(\d+)(B|KB|MB|GB)$/i);
|
|
if (!match) return 100 * 1024 * 1024; // default 100MB
|
|
return parseInt(match[1]) * units[match[2].toUpperCase()];
|
|
}
|
|
|
|
export async function ensureDirectoryExists(dirPath) {
|
|
try {
|
|
await fs.mkdir(dirPath, { recursive: true });
|
|
} catch (err) {
|
|
if (err.code !== 'EEXIST') throw err;
|
|
}
|
|
}
|
|
|
|
export function formatFileSize(bytes) {
|
|
if (bytes === 0) return '0 Bytes';
|
|
const k = 1024;
|
|
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
}
|
|
|
|
export async function getZimFiles(archiveDir) {
|
|
await ensureDirectoryExists(archiveDir);
|
|
const items = await fs.readdir(archiveDir, { withFileTypes: true });
|
|
|
|
const zimFiles = [];
|
|
for (const item of items) {
|
|
if (item.isFile() && path.extname(item.name).toLowerCase() === '.zim') {
|
|
const fullPath = path.join(archiveDir, item.name);
|
|
const stats = await fs.stat(fullPath);
|
|
zimFiles.push({
|
|
name: item.name,
|
|
path: fullPath,
|
|
size: stats.size,
|
|
modified: stats.mtime.toISOString()
|
|
});
|
|
}
|
|
}
|
|
|
|
return zimFiles.sort((a, b) => new Date(b.modified) - new Date(a.modified));
|
|
}
|
|
|
|
export async function deleteFile(filePath) {
|
|
await fs.unlink(filePath);
|
|
}
|