khamba
Version:
A cli tool for sharing files through local network.
207 lines (206 loc) • 8.6 kB
JavaScript
import express from 'express';
import { useEffect } from 'react';
import fs from 'fs';
import path from 'path';
import { hashFile, hashFolder } from '../functions/useHashCheck.js';
import { log, logError } from '../functions/log.js';
import { SEND_PATH } from '../functions/variables.js';
import { c } from 'tar';
import { initSenderTransfer, updateSenderTransferState, updateSenderTransferProgress, updateSenderTransferError, } from '../stores/senderFileHandlerStore.js';
import { $sendingFiles } from '../stores/baseStore.js';
export const useHttpServer = (MY_IP, TCP_PORT, isSending, sendingFileNames) => {
useEffect(() => {
const app = express();
app.use(express.json());
app.get('/', (req, res) => {
res.json({
msg: 'Hoe!',
});
});
app.get('/get-active-peer-info', (req, res) => {
res.json({
isSending,
sendingFileNames,
});
});
app.get('/get-active-peer', (req, res) => {
setTimeout(() => {
res.json({ active: true });
}, 10000);
});
app.get('/init-sender-transfer/*', (req, res) => {
try {
const peerID = req.params['0'];
if (!peerID) {
return res.status(400).json({ msg: 'receiver peerID required.' });
}
initSenderTransfer(peerID);
res.json({
msg: 'transfer initialized',
});
}
catch (error) {
logError(error);
}
});
app.get('/update-sender-transfer-state/*/*/*', (req, res) => {
try {
const peerID = req.params['0'];
const fileID = req.params['1'];
const state = req.params['2'];
const error = req.query['error'];
if (!peerID) {
return res.status(400).json({ msg: 'receiver peerID required.' });
}
if (!fileID) {
return res.status(400).json({ msg: 'fileID required.' });
}
if (!state) {
return res.status(400).json({ msg: 'transfer state required.' });
}
updateSenderTransferState(peerID, fileID, state);
if (error) {
updateSenderTransferError(peerID, fileID, error);
}
res.json({
msg: 'transfer state change acknowledged.',
});
}
catch (error) {
logError(error);
}
});
app.get('/download/*/*', (req, res) => {
try {
const peerID = req.params['0'];
const fileID = req.params['1'];
if (!peerID) {
return res.status(400).json({ msg: 'receiver peerID required.' });
}
if (!fileID) {
return res.status(400).json({ msg: 'fileID required.' });
}
const filename = $sendingFiles.get()[fileID]?.fileName;
if (!filename) {
return res.status(400).json({ msg: 'filename not found.' });
}
const filePath = path.join(SEND_PATH, filename);
if (!fs.existsSync(filePath)) {
return res.status(404).json({ msg: 'File not found!', filePath });
}
updateSenderTransferState(peerID, fileID, 'TRANSFERRING');
// const stat = fs.statSync(filePath);
// const fileSize = stat.size;
// res.setHeader('Content-Length', fileSize);
res.setHeader('Content-Disposition', `attachment; filename=${filename}`);
const fileStream = fs.createReadStream(filePath);
fileStream.pipe(res);
fileStream.on('data', chunk => {
updateSenderTransferProgress(peerID, fileID, chunk.length);
});
fileStream.on('close', () => { });
fileStream.on('end', () => {
updateSenderTransferState(peerID, fileID, 'TRANSFERRED');
});
fileStream.on('error', (err) => {
logError('fileStream error:', err);
res.status(500).end('Internal Server Error');
});
res.on('error', err => {
logError('Response stream error:', err);
});
req.on('close', () => {
fileStream.destroy();
});
}
catch (error) {
logError(error);
}
});
app.get('/download-tar/*/*', (req, res) => {
try {
const peerID = req.params['0'];
const fileID = req.params['1'];
if (!peerID) {
return res.status(400).json({ msg: 'receiver peerID required.' });
}
if (!fileID) {
return res.status(400).json({ msg: 'fileID required.' });
}
const foldername = $sendingFiles.get()[fileID]?.fileName;
if (!foldername) {
return res.status(400).json({ msg: 'foldername not found.' });
}
const folderPath = path.join(SEND_PATH, foldername);
if (!fs.existsSync(folderPath)) {
return res.status(404).json({ msg: 'Folder not found!', folderPath });
}
updateSenderTransferState(peerID, fileID, 'TRANSFERRING');
// const folderSize = getFolderSize(folderPath);
res.setHeader('Content-Type', 'application/x-tar');
// res.setHeader('Content-Length', folderSize);
res.setHeader('Content-Disposition', `attachment; filename=${foldername}.tar`);
const pack = c({
C: folderPath,
}, ['']);
pack.on('error', (err) => {
logError('Pack stream error:', err);
res.status(500).end('Internal Server Error');
});
pack.on('end', () => {
updateSenderTransferState(peerID, fileID, 'TRANSFERRED');
});
res.on('error', err => {
logError('Response stream error:', err);
});
res.on('close', () => { });
pack.on('data', chunk => {
updateSenderTransferProgress(peerID, fileID, chunk.length);
});
pack.pipe(res);
}
catch (error) {
logError(error);
}
});
app.get('/get-hash/*', async (req, res) => {
try {
const fileID = req.params['0'];
if (!fileID) {
return res.status(400).json({ msg: 'fileID required.' });
}
const filename = $sendingFiles.get()[fileID]?.fileName;
if (!filename) {
return res.status(400).json({ msg: 'filename not found.' });
}
const filePath = path.join(SEND_PATH, filename);
if (!fs.existsSync(filePath)) {
return res.status(404).json({ msg: 'File not found!!' });
}
try {
const stats = fs.statSync(filePath);
const hash = stats.isDirectory()
? await hashFolder(filePath)
: await hashFile(filePath);
log(`Hash of ${filename} is -> ${hash}`);
return res.status(200).json({ msg: 'Hash Successful!', hash });
}
catch (err) {
logError('Error hashing file:', err);
return res.status(400).json({ msg: 'Hash Failed!' });
}
}
catch (error) {
logError(error);
}
});
const server = app.listen(TCP_PORT, MY_IP, () => {
log(`Server is running on http://${MY_IP}:${TCP_PORT}`);
});
return () => {
server.close(() => {
log('Server stopped listening for requests.');
});
};
}, []);
};