Compare commits
4 commits
acd58dbaa5
...
827c96daf5
Author | SHA1 | Date | |
---|---|---|---|
xenofem | 827c96daf5 | ||
xenofem | 7d8c4f00fa | ||
xenofem | c78844d8b1 | ||
xenofem | db233b57c1 |
13
README.md
13
README.md
|
@ -14,7 +14,8 @@
|
||||||
sender has gone offline
|
sender has gone offline
|
||||||
- Easy to send multiple files at once - they're bundled into a zip
|
- Easy to send multiple files at once - they're bundled into a zip
|
||||||
file for receivers, with zero compression so extraction is quick
|
file for receivers, with zero compression so extraction is quick
|
||||||
- Sanitizes filenames
|
- Sanitises filenames, using sensible non-obnoxious defaults that
|
||||||
|
should be safe across platforms
|
||||||
- Fires a laser beam that turns you trans
|
- Fires a laser beam that turns you trans
|
||||||
|
|
||||||
## configuration
|
## configuration
|
||||||
|
@ -28,9 +29,7 @@ transbeam is configured with the following environment variables:
|
||||||
|
|
||||||
## todo
|
## todo
|
||||||
|
|
||||||
- [ ] file downloading
|
- uploader auth
|
||||||
- [ ] upload progress bar
|
- downloader auth
|
||||||
- [ ] uploader auth
|
- delete uploads after a while
|
||||||
- [ ] downloader auth
|
- configurable maximum file size
|
||||||
- [ ] delete uploads after a while
|
|
||||||
- [ ] configurable maximum file size
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ use time::OffsetDateTime;
|
||||||
|
|
||||||
use crate::{file::LiveWriter, DownloadableFile, UploadedFile};
|
use crate::{file::LiveWriter, DownloadableFile, UploadedFile};
|
||||||
|
|
||||||
|
const MAX_FILES: usize = 256;
|
||||||
const FILENAME_DATE_FORMAT: &[time::format_description::FormatItem] =
|
const FILENAME_DATE_FORMAT: &[time::format_description::FormatItem] =
|
||||||
time::macros::format_description!("[year]-[month]-[day]-[hour][minute][second]");
|
time::macros::format_description!("[year]-[month]-[day]-[hour][minute][second]");
|
||||||
|
|
||||||
|
@ -28,10 +29,12 @@ enum Error {
|
||||||
UnexpectedMessageType,
|
UnexpectedMessageType,
|
||||||
#[error("Metadata contained an empty list of files")]
|
#[error("Metadata contained an empty list of files")]
|
||||||
NoFiles,
|
NoFiles,
|
||||||
|
#[error("Number of files submitted by client exceeded the maximum limit")]
|
||||||
|
TooManyFiles,
|
||||||
#[error("Websocket was closed by client before completing transfer")]
|
#[error("Websocket was closed by client before completing transfer")]
|
||||||
ClosedEarly(Option<CloseReason>),
|
ClosedEarly(Option<CloseReason>),
|
||||||
#[error("Client sent more data than they were supposed to")]
|
#[error("Client sent more data than they were supposed to")]
|
||||||
TooMuchData,
|
UnexpectedExtraData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
|
@ -43,8 +46,9 @@ impl Error {
|
||||||
Self::DuplicateFilename => CloseCode::Policy,
|
Self::DuplicateFilename => CloseCode::Policy,
|
||||||
Self::UnexpectedMessageType => CloseCode::Invalid,
|
Self::UnexpectedMessageType => CloseCode::Invalid,
|
||||||
Self::NoFiles => CloseCode::Policy,
|
Self::NoFiles => CloseCode::Policy,
|
||||||
|
Self::TooManyFiles => CloseCode::Policy,
|
||||||
Self::ClosedEarly(_) => CloseCode::Invalid,
|
Self::ClosedEarly(_) => CloseCode::Invalid,
|
||||||
Self::TooMuchData => CloseCode::Invalid,
|
Self::UnexpectedExtraData => CloseCode::Invalid,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,7 +109,7 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for Uploader {
|
||||||
let msg = match msg {
|
let msg = match msg {
|
||||||
Ok(m) => m,
|
Ok(m) => m,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Websocket error: {:?}", e);
|
error!("Websocket error: {}", e);
|
||||||
ctx.stop();
|
ctx.stop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -113,7 +117,7 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for Uploader {
|
||||||
|
|
||||||
match self.handle_message(msg, ctx) {
|
match self.handle_message(msg, ctx) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("{:?}", e);
|
error!("{}", e);
|
||||||
ctx.close(Some(ws::CloseReason {
|
ctx.close(Some(ws::CloseReason {
|
||||||
code: e.close_code(),
|
code: e.close_code(),
|
||||||
description: Some(e.to_string()),
|
description: Some(e.to_string()),
|
||||||
|
@ -160,6 +164,9 @@ impl Uploader {
|
||||||
let raw_files: Vec<RawUploadedFile> = serde_json::from_slice(text.as_bytes())?;
|
let raw_files: Vec<RawUploadedFile> = serde_json::from_slice(text.as_bytes())?;
|
||||||
info!("Received file list: {} files", raw_files.len());
|
info!("Received file list: {} files", raw_files.len());
|
||||||
debug!("{:?}", raw_files);
|
debug!("{:?}", raw_files);
|
||||||
|
if raw_files.len() > MAX_FILES {
|
||||||
|
return Err(Error::TooManyFiles);
|
||||||
|
}
|
||||||
let mut filenames: HashSet<String> = HashSet::new();
|
let mut filenames: HashSet<String> = HashSet::new();
|
||||||
let mut files = Vec::new();
|
let mut files = Vec::new();
|
||||||
for raw_file in raw_files.iter() {
|
for raw_file in raw_files.iter() {
|
||||||
|
@ -251,7 +258,7 @@ impl Uploader {
|
||||||
fn handle_data(&mut self, data: Bytes) -> Result<bool, Error> {
|
fn handle_data(&mut self, data: Bytes) -> Result<bool, Error> {
|
||||||
if let Some(ref mut writer) = self.writer {
|
if let Some(ref mut writer) = self.writer {
|
||||||
if (data.len() as u64) > self.bytes_remaining {
|
if (data.len() as u64) > self.bytes_remaining {
|
||||||
return Err(Error::TooMuchData);
|
return Err(Error::UnexpectedExtraData);
|
||||||
}
|
}
|
||||||
self.bytes_remaining -= data.len() as u64;
|
self.bytes_remaining -= data.len() as u64;
|
||||||
writer.write_all(&data)?;
|
writer.write_all(&data)?;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
<link rel="stylesheet" type="text/css" href="transbeam.css"/>
|
<link rel="stylesheet" type="text/css" href="transbeam.css"/>
|
||||||
<script src="util.js"></script>
|
<script src="util.js"></script>
|
||||||
<title>Upload Test</title>
|
<title>transbeam</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="header">
|
<div id="header">
|
||||||
|
@ -39,6 +39,6 @@
|
||||||
<h5>(c) 2022 xenofem, MIT licensed</h5>
|
<h5>(c) 2022 xenofem, MIT licensed</h5>
|
||||||
<h5><a target="_blank" href="https://git.xeno.science/xenofem/transbeam">source</a></h5>
|
<h5><a target="_blank" href="https://git.xeno.science/xenofem/transbeam">source</a></h5>
|
||||||
</div>
|
</div>
|
||||||
<script src="upload.js"></script>
|
<script src="transbeam.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const FILE_CHUNK_SIZE = 16384;
|
const FILE_CHUNK_SIZE = 16384;
|
||||||
|
const MAX_FILES = 256;
|
||||||
|
|
||||||
const fileInputContainer = document.getElementById('file_input_container');
|
const fileInputContainer = document.getElementById('file_input_container');
|
||||||
const fileInput = document.getElementById('file_input');
|
const fileInput = document.getElementById('file_input');
|
||||||
|
@ -86,6 +87,7 @@ function updateFiles() {
|
||||||
uploadButton.textContent = `Upload ${files.length} file${files.length > 1 ? 's' : ''} (${displaySize(totalBytes)})`;
|
uploadButton.textContent = `Upload ${files.length} file${files.length > 1 ? 's' : ''} (${displaySize(totalBytes)})`;
|
||||||
uploadButton.style.display = '';
|
uploadButton.style.display = '';
|
||||||
}
|
}
|
||||||
|
fileInput.disabled = (files.length >= MAX_FILES);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFiles();
|
updateFiles();
|
||||||
|
@ -93,6 +95,7 @@ downloadLinkContainer.style.display = 'none';
|
||||||
progressContainer.style.display = 'none';
|
progressContainer.style.display = 'none';
|
||||||
|
|
||||||
function addFile(newFile) {
|
function addFile(newFile) {
|
||||||
|
if (files.length >= MAX_FILES) { return; }
|
||||||
if (files.some((oldFile) => newFile.name === oldFile.name)) { return; }
|
if (files.some((oldFile) => newFile.name === oldFile.name)) { return; }
|
||||||
|
|
||||||
files.push(newFile);
|
files.push(newFile);
|
Loading…
Reference in a new issue