Add curl instructions, extract copy into own file

This commit is contained in:
neri 2021-02-13 16:47:04 +01:00
parent 83ea1a15c7
commit 12544034af
7 changed files with 84 additions and 73 deletions

View File

@ -6,7 +6,7 @@ use actix_files::{Files, NamedFile};
use actix_multipart::Multipart; use actix_multipart::Multipart;
use actix_web::{ use actix_web::{
error, error,
http::header::{ContentDisposition, DispositionParam, DispositionType}, http::header::{ContentDisposition, DispositionParam, DispositionType, ACCEPT},
middleware, middleware,
web::{self, Bytes}, web::{self, Bytes},
App, Error, FromRequest, HttpRequest, HttpResponse, HttpServer, App, Error, FromRequest, HttpRequest, HttpResponse, HttpServer,
@ -24,16 +24,23 @@ use sqlx::{
}; };
use std::env; use std::env;
const INDEX_HTML: &str = include_str!("../template/index.html");
const UPLOAD_HTML: &str = include_str!("../template/upload.html"); const UPLOAD_HTML: &str = include_str!("../template/upload.html");
const VIEW_HTML: &str = include_str!("../template/view.html"); const VIEW_HTML: &str = include_str!("../template/view.html");
async fn index() -> Result<NamedFile, Error> { async fn index(req: web::HttpRequest) -> Result<HttpResponse, Error> {
Ok(NamedFile::open("./static/index.html") let upload_url = format!("{}/upload", get_host_url(&req));
.map_err(|_| error::ErrorNotFound(""))? let index_html = INDEX_HTML.replace("{upload_url}", upload_url.as_str());
.disable_content_disposition()) Ok(HttpResponse::Ok()
.content_type("text/html")
.body(index_html))
} }
// multipart data
// required: either 'file' or 'text'
// optional: 'keep_for' default to 30 minutes
async fn upload( async fn upload(
req: web::HttpRequest,
payload: Multipart, payload: Multipart,
db: web::Data<PgPool>, db: web::Data<PgPool>,
expiry_watch_sender: web::Data<Sender<()>>, expiry_watch_sender: web::Data<Sender<()>>,
@ -88,24 +95,34 @@ async fn upload(
expiry_watch_sender.send(()).await; expiry_watch_sender.send(()).await;
let redirect = if kind == FileKind::BINARY && original_name.is_some() { let redirect = if kind == FileKind::BINARY && original_name.is_some() {
format!("/upload/{}/{}", file_id, original_name.unwrap()) format!("/upload/{}/{}", file_id, original_name.as_ref().unwrap())
} else { } else {
format!("/upload/{}", file_id) format!("/upload/{}", file_id)
}; };
let url = get_file_url(&req, &file_id, original_name.as_deref());
Ok(HttpResponse::SeeOther() Ok(HttpResponse::SeeOther()
.header("location", redirect) .header("location", redirect)
.finish()) .body(format!("{}\n", url)))
}
fn get_host_url(req: &web::HttpRequest) -> String {
let conn = req.connection_info();
format!("{}://{}", conn.scheme(), conn.host())
}
fn get_file_url(req: &web::HttpRequest, id: &str, name: Option<&str>) -> String {
if let Some(name) = name {
format!("{}/file/{}/{}", get_host_url(req), id, name)
} else {
format!("{}/file/{}", get_host_url(req), id)
}
} }
async fn uploaded(req: web::HttpRequest) -> Result<HttpResponse, Error> { async fn uploaded(req: web::HttpRequest) -> Result<HttpResponse, Error> {
let id = req.match_info().query("id"); let id = req.match_info().query("id");
let name = req.match_info().get("name"); let name = req.match_info().get("name");
let conn = req.connection_info(); let url = get_file_url(&req, id, name);
let url = if let Some(name) = name {
format!("{}://{}/file/{}/{}", conn.scheme(), conn.host(), id, name)
} else {
format!("{}://{}/file/{}", conn.scheme(), conn.host(), id)
};
let upload_html = UPLOAD_HTML.replace("{url}", url.as_str()); let upload_html = UPLOAD_HTML.replace("{url}", url.as_str());
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
.content_type("text/html") .content_type("text/html")

View File

@ -11,17 +11,17 @@ pub(crate) async fn parse_multipart(
filename: &Path, filename: &Path,
) -> Result<(Option<String>, DateTime<Local>, FileKind), error::Error> { ) -> Result<(Option<String>, DateTime<Local>, FileKind), error::Error> {
let mut original_name: Option<String> = None; let mut original_name: Option<String> = None;
let mut timeout: Option<String> = None; let mut keep_for: Option<String> = None;
let mut kind: Option<FileKind> = None; let mut kind: Option<FileKind> = None;
while let Ok(Some(field)) = payload.try_next().await { while let Ok(Some(field)) = payload.try_next().await {
let name = get_field_name(&field)?; let name = get_field_name(&field)?;
let name = name.as_str(); let name = name.as_str();
match name { match name {
"validity_secs" => { "keep_for" => {
timeout = Some(parse_string(name, field).await?); keep_for = Some(parse_string(name, field).await?);
} }
"content" => { "file" => {
let file_original_name = get_original_filename(&field); let file_original_name = get_original_filename(&field);
if file_original_name == None || file_original_name.as_deref() == Some("") { if file_original_name == None || file_original_name.as_deref() == Some("") {
continue; continue;
@ -35,7 +35,7 @@ pub(crate) async fn parse_multipart(
.await .await
.map_err(|_| error::ErrorInternalServerError("could not write file"))?; .map_err(|_| error::ErrorInternalServerError("could not write file"))?;
} }
"text_content" => { "text" => {
if original_name.is_some() { if original_name.is_some() {
continue; continue;
} }
@ -58,12 +58,11 @@ pub(crate) async fn parse_multipart(
} }
} }
let validity_secs = timeout let validity_secs = keep_for
.ok_or_else(|| error::ErrorBadRequest("field validity_secs not set"))? .map(|timeout| timeout.parse())
.parse() .transpose()
.map_err(|e| { .map_err(|e| error::ErrorBadRequest(format!("field validity_secs is not a number: {}", e)))?
error::ErrorBadRequest(format!("field validity_secs is not a number: {}", e)) .unwrap_or(1800); // default to 30 minutes
})?;
let max_validity_secs = Duration::days(31).num_seconds(); let max_validity_secs = Duration::days(31).num_seconds();
if validity_secs > max_validity_secs { if validity_secs > max_validity_secs {
return Err(error::ErrorBadRequest(format!( return Err(error::ErrorBadRequest(format!(

16
static/copy.js Normal file
View File

@ -0,0 +1,16 @@
const button = document.getElementById("copy");
button.onclick = () => {
if (!navigator.clipboard) {
button.innerText = "nicht unterstützt";
return;
}
const content = document.getElementsByClassName("copy-content")[0];
navigator.clipboard.writeText(content.textContent).then(
(_) => {
button.innerText = "kopiert!";
},
(_) => {
button.innerText = "nicht unterstützt";
}
);
};

View File

@ -71,3 +71,7 @@ a.button:visited {
.button.main:hover { .button.main:hover {
background-color: forestgreen; background-color: forestgreen;
} }
.usage {
margin-top: 2em;
}

View File

@ -13,19 +13,14 @@
<form action="/upload" method="POST" enctype="multipart/form-data"> <form action="/upload" method="POST" enctype="multipart/form-data">
<label for="file-upload">datei</label> <label for="file-upload">datei</label>
<br /> <br />
<input id="file-upload" type="file" name="content" /> <input id="file-upload" type="file" name="file" />
<br /> <br />
<label for="text-upload">oder asciitrash</label> <label for="text-upload">oder asciitrash</label>
<br /> <br />
<textarea <textarea id="text-upload" name="text" rows="20" cols="120"></textarea>
id="text-upload"
name="text_content"
rows="20"
cols="120"
></textarea>
<br /> <br />
<label for="validity_secs">gültig für</label> <label for="keep_for">gültig für</label>
<select id="validity_secs" name="validity_secs"> <select id="keep_for" name="keep_for">
<option value="1800">30 minuten</option> <option value="1800">30 minuten</option>
<option value="3600">60 minuten</option> <option value="3600">60 minuten</option>
<option value="43200">12 stunden</option> <option value="43200">12 stunden</option>
@ -36,6 +31,16 @@
<br /> <br />
<input class="main button" type="submit" value="hochladen" /> <input class="main button" type="submit" value="hochladen" />
</form> </form>
<section class="usage">
<pre>
file upload
curl -F 'file=@yourfile.rs' {upload_url}
text upload
curl -F 'text=your text' {upload_url}
including time
curl -F 'text=your text' -F 'keep_for=1800' {upload_url}
</pre>
</section>
</main> </main>
</body> </body>
</html> </html>

View File

@ -11,27 +11,10 @@
<h1><a href="/">datatrash</a></h1> <h1><a href="/">datatrash</a></h1>
<p> <p>
datei-link: datei-link:
<a id="link" href="{url}"> <a id="link" class="copy-content" href="{url}">{url}</a>
{url}
</a>
</p> </p>
<button id="copy" class="main button" onclick="copyToClipboard()"> <button id="copy" class="main button">link kopieren</button>
link kopieren
</button>
</main> </main>
<script lang="javascript"> <script src="/static/copy.js" lang="javascript"></script>
function copyToClipboard() {
const button = document.getElementById("copy");
if (!navigator.clipboard) {
button.innerText = "nicht unterstützt";
return;
}
const anchor = document.getElementById("link");
navigator.clipboard.writeText(anchor.href).then(
_ => { button.innerText = "kopiert!"; },
_ => { button.innerText = "nicht unterstützt"; },
);
}
</script>
</body> </body>
</html> </html>

View File

@ -9,26 +9,13 @@
<body> <body>
<main> <main>
<h1><a href="/">datatrash</a></h1> <h1><a href="/">datatrash</a></h1>
<textarea id="text" rows="20" cols="120" readonly>{text}</textarea> <textarea id="text" rows="20" cols="120" class="copy-content" readonly>
{text}</textarea
>
<br /> <br />
<a class="main button" href="?raw">herunterladen</a> <a class="main button" href="?raw">herunterladen</a>
<button id="copy" class="button" onclick="copyToClipboard()"> <button id="copy" class="button">text kopieren</button>
text kopieren
</button>
</main> </main>
<script lang="javascript"> <script src="/static/copy.js" lang="javascript"></script>
function copyToClipboard() {
const button = document.getElementById("copy");
if (!navigator.clipboard) {
button.innerText = "nicht unterstützt";
return;
}
const textarea = document.getElementById("text");
navigator.clipboard.writeText(textarea.value).then(
_ => { button.innerText = "kopiert!"; },
_ => { button.innerText = "nicht unterstützt"; },
);
}
</script>
</body> </body>
</html> </html>