init
This commit is contained in:
133
src/services/files.mjs
Normal file
133
src/services/files.mjs
Normal file
@@ -0,0 +1,133 @@
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user