forked from neri/datatrash
show limits explicitly, improve code
This commit is contained in:
parent
31a429861d
commit
c372db6446
File diff suppressed because it is too large
Load Diff
|
@ -9,7 +9,7 @@ edition = "2018"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = "3.3.2"
|
actix-web = "3.3.2"
|
||||||
sqlx = { version = "0.5.1", default-features = false, features = [ "runtime-async-std-rustls", "postgres", "chrono" ] }
|
sqlx = { version = "0.5.1", default-features = false, features = [ "runtime-async-std-rustls", "postgres", "chrono" ] }
|
||||||
env_logger = "0.8.3"
|
env_logger = "0.9.0"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
actix-files = "0.5.0"
|
actix-files = "0.5.0"
|
||||||
async-std = "1.9.0"
|
async-std = "1.9.0"
|
||||||
|
@ -18,6 +18,6 @@ futures = "0.3.13"
|
||||||
rand = "0.8.3"
|
rand = "0.8.3"
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
htmlescape = "0.3.1"
|
htmlescape = "0.3.1"
|
||||||
urlencoding = "1.1.1"
|
urlencoding = "2.1.0"
|
||||||
tree_magic_mini = "1.0.1"
|
tree_magic_mini = "3.0.0"
|
||||||
mime = "0.3.16"
|
mime = "0.3.16"
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
<div id="password-input">
|
||||||
|
<label for="password">
|
||||||
|
passwort benötigt (>{auth_time} oder datei >{auth_large_size} und >{auth_large_time})
|
||||||
|
</label>
|
||||||
|
<br />
|
||||||
|
<input id="password" name="password" type="password" />
|
||||||
|
</div>
|
||||||
|
<script src="/assets/auth-hide.js" lang="javascript"></script>
|
|
@ -0,0 +1 @@
|
||||||
|
(maximal {max_size})
|
|
@ -1,6 +1,7 @@
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
use async_std::{fs, path::PathBuf};
|
use async_std::{fs, path::PathBuf};
|
||||||
|
use chrono::Duration;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
@ -12,8 +13,8 @@ pub struct Config {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct NoAuthLimits {
|
pub struct NoAuthLimits {
|
||||||
pub auth_password: String,
|
pub auth_password: String,
|
||||||
pub max_time: u64,
|
pub max_time: Duration,
|
||||||
pub large_file_max_time: u64,
|
pub large_file_max_time: Duration,
|
||||||
pub large_file_size: u64,
|
pub large_file_size: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,22 +30,7 @@ pub async fn get_config() -> Config {
|
||||||
.await
|
.await
|
||||||
.expect("could not create directory for storing files");
|
.expect("could not create directory for storing files");
|
||||||
|
|
||||||
let no_auth_limits = match (
|
let no_auth_limits = get_no_auth_limits();
|
||||||
env::var("AUTH_PASSWORD").ok(),
|
|
||||||
env_number("NO_AUTH_MAX_TIME"),
|
|
||||||
env_number("NO_AUTH_LARGE_FILE_MAX_TIME"),
|
|
||||||
env_number("NO_AUTH_LARGE_FILE_SIZE"),
|
|
||||||
) {
|
|
||||||
(Some(auth_password), Some(max_time), Some(large_file_max_time), Some(large_file_size)) => {
|
|
||||||
Some(NoAuthLimits {
|
|
||||||
auth_password,
|
|
||||||
max_time,
|
|
||||||
large_file_max_time,
|
|
||||||
large_file_size,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
Config {
|
Config {
|
||||||
files_dir,
|
files_dir,
|
||||||
|
@ -53,6 +39,26 @@ pub async fn get_config() -> Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_no_auth_limits() -> Option<NoAuthLimits> {
|
||||||
|
match (
|
||||||
|
env::var("AUTH_PASSWORD").ok(),
|
||||||
|
env_number("NO_AUTH_MAX_TIME"),
|
||||||
|
env_number("NO_AUTH_LARGE_FILE_MAX_TIME"),
|
||||||
|
env_number("NO_AUTH_LARGE_FILE_SIZE"),
|
||||||
|
) {
|
||||||
|
(Some(auth_password), Some(max_time), Some(large_file_max_time), Some(large_file_size)) => {
|
||||||
|
Some(NoAuthLimits {
|
||||||
|
auth_password,
|
||||||
|
max_time: Duration::seconds(max_time as i64),
|
||||||
|
large_file_max_time: Duration::seconds(large_file_max_time as i64),
|
||||||
|
large_file_size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(None, None, None, None) => None,
|
||||||
|
_ => panic!("Incomplete NO_AUTH configuration: All environment variables must be specified")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn env_number(variable: &str) -> Option<u64> {
|
fn env_number(variable: &str) -> Option<u64> {
|
||||||
env::var(variable).ok().and_then(|n| n.parse::<u64>().ok())
|
env::var(variable).ok().and_then(|n| n.parse::<u64>().ok())
|
||||||
}
|
}
|
||||||
|
|
18
src/main.rs
18
src/main.rs
|
@ -7,7 +7,7 @@ mod multipart;
|
||||||
mod upload;
|
mod upload;
|
||||||
|
|
||||||
use actix_files::Files;
|
use actix_files::Files;
|
||||||
use actix_web::{middleware, web, App, Error, HttpResponse, HttpServer};
|
use actix_web::{App, Error, HttpResponse, HttpServer, middleware::Logger, web};
|
||||||
use async_std::{channel, task};
|
use async_std::{channel, task};
|
||||||
use env_logger::Env;
|
use env_logger::Env;
|
||||||
use sqlx::postgres::PgPool;
|
use sqlx::postgres::PgPool;
|
||||||
|
@ -29,20 +29,20 @@ async fn main() -> std::io::Result<()> {
|
||||||
|
|
||||||
log::info!("omnomnom");
|
log::info!("omnomnom");
|
||||||
|
|
||||||
task::spawn(deleter::delete_old_files(
|
let db = web::Data::new(pool.clone());
|
||||||
receiver,
|
|
||||||
pool.clone(),
|
|
||||||
config.files_dir.clone(),
|
|
||||||
));
|
|
||||||
|
|
||||||
let db = web::Data::new(pool);
|
|
||||||
let expiry_watch_sender = web::Data::new(sender);
|
let expiry_watch_sender = web::Data::new(sender);
|
||||||
let bind_address = env::var("BIND_ADDRESS").unwrap_or_else(|_| "0.0.0.0:8000".to_owned());
|
let bind_address = env::var("BIND_ADDRESS").unwrap_or_else(|_| "0.0.0.0:8000".to_owned());
|
||||||
|
|
||||||
|
task::spawn(deleter::delete_old_files(
|
||||||
|
receiver,
|
||||||
|
pool,
|
||||||
|
config.files_dir.clone(),
|
||||||
|
));
|
||||||
|
|
||||||
HttpServer::new({
|
HttpServer::new({
|
||||||
move || {
|
move || {
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(middleware::Logger::new(r#"%{r}a "%r" =%s %bbytes %Tsec"#))
|
.wrap(Logger::new(r#"%{r}a "%r" =%s %bbytes %Tsec"#))
|
||||||
.app_data(db.clone())
|
.app_data(db.clone())
|
||||||
.app_data(expiry_watch_sender.clone())
|
.app_data(expiry_watch_sender.clone())
|
||||||
.data(config.clone())
|
.data(config.clone())
|
||||||
|
|
|
@ -5,6 +5,9 @@ use async_std::{fs, fs::File, path::Path, prelude::*};
|
||||||
use chrono::{prelude::*, Duration};
|
use chrono::{prelude::*, Duration};
|
||||||
use futures::{StreamExt, TryStreamExt};
|
use futures::{StreamExt, TryStreamExt};
|
||||||
|
|
||||||
|
const MAX_UPLOAD_SECONDS: u64 = 31 * 24 * 60 * 60;
|
||||||
|
const DEFAULT_UPLOAD_SECONDS: u64 = 30 * 60;
|
||||||
|
|
||||||
pub(crate) struct UploadConfig {
|
pub(crate) struct UploadConfig {
|
||||||
pub original_name: String,
|
pub original_name: String,
|
||||||
pub valid_till: DateTime<Local>,
|
pub valid_till: DateTime<Local>,
|
||||||
|
@ -77,20 +80,20 @@ pub(crate) async fn parse_multipart(
|
||||||
let seconds = keep_for.parse().map_err(|e| {
|
let seconds = keep_for.parse().map_err(|e| {
|
||||||
error::ErrorBadRequest(format!("field keep_for is not a number: {}", e))
|
error::ErrorBadRequest(format!("field keep_for is not a number: {}", e))
|
||||||
})?;
|
})?;
|
||||||
let max_keep_for = Duration::days(31).num_seconds() as u64;
|
if seconds > MAX_UPLOAD_SECONDS {
|
||||||
if seconds > max_keep_for {
|
|
||||||
return Err(error::ErrorBadRequest(format!(
|
return Err(error::ErrorBadRequest(format!(
|
||||||
"maximum allowed validity is {} seconds, but you specified {} seconds",
|
"maximum allowed validity is {} seconds, but you specified {} seconds",
|
||||||
max_keep_for, seconds
|
MAX_UPLOAD_SECONDS, seconds
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
seconds
|
seconds
|
||||||
} else {
|
} else {
|
||||||
1800
|
DEFAULT_UPLOAD_SECONDS
|
||||||
};
|
};
|
||||||
let valid_till = Local::now() + Duration::seconds(validated_keep_for as i64);
|
let valid_duration = Duration::seconds(validated_keep_for as i64);
|
||||||
|
let valid_till = Local::now() + valid_duration;
|
||||||
|
|
||||||
check_auth_requirements(size, validated_keep_for, password, config)?;
|
check_auth_requirements(size, valid_duration, password, config)?;
|
||||||
|
|
||||||
Ok(UploadConfig {
|
Ok(UploadConfig {
|
||||||
original_name,
|
original_name,
|
||||||
|
@ -102,7 +105,7 @@ pub(crate) async fn parse_multipart(
|
||||||
|
|
||||||
fn check_auth_requirements(
|
fn check_auth_requirements(
|
||||||
size: u64,
|
size: u64,
|
||||||
validated_keep_for: u64,
|
validated_keep_for: Duration,
|
||||||
password: Option<String>,
|
password: Option<String>,
|
||||||
config: &config::Config,
|
config: &config::Config,
|
||||||
) -> Result<(), error::Error> {
|
) -> Result<(), error::Error> {
|
||||||
|
|
101
src/upload.rs
101
src/upload.rs
|
@ -1,3 +1,5 @@
|
||||||
|
use std::{cmp, vec};
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::file_kind::FileKind;
|
use crate::file_kind::FileKind;
|
||||||
use crate::multipart;
|
use crate::multipart;
|
||||||
|
@ -5,13 +7,15 @@ use crate::multipart::UploadConfig;
|
||||||
use actix_multipart::Multipart;
|
use actix_multipart::Multipart;
|
||||||
use actix_web::{error, web, Error, HttpResponse};
|
use actix_web::{error, web, Error, HttpResponse};
|
||||||
use async_std::{channel::Sender, fs};
|
use async_std::{channel::Sender, fs};
|
||||||
|
use chrono::Duration;
|
||||||
use rand::prelude::SliceRandom;
|
use rand::prelude::SliceRandom;
|
||||||
use sqlx::postgres::PgPool;
|
use sqlx::postgres::PgPool;
|
||||||
|
|
||||||
const INDEX_HTML: &str = include_str!("../template/index.html");
|
const INDEX_HTML: &str = include_str!("../template/index.html");
|
||||||
const INDEX_AUTH_HTML: &str = include_str!("../template/index-auth.html");
|
|
||||||
const AUTH_HIDE_JS: &str = include_str!("../template/auth-hide.js");
|
const AUTH_HIDE_JS: &str = include_str!("../template/auth-hide.js");
|
||||||
const UPLOAD_HTML: &str = include_str!("../template/upload.html");
|
const UPLOAD_HTML: &str = include_str!("../template/upload.html");
|
||||||
|
const AUTH_SNIPPET_HTML: &str = include_str!("../snippet/auth.html.snippet");
|
||||||
|
const MAX_SIZE_SNIPPET_HTML: &str = include_str!("../snippet/max_size.html.snippet");
|
||||||
|
|
||||||
const ID_CHARS: &[char] = &[
|
const ID_CHARS: &[char] = &[
|
||||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||||
|
@ -22,25 +26,102 @@ pub async fn index(
|
||||||
req: web::HttpRequest,
|
req: web::HttpRequest,
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let upload_url = format!("{}/upload", get_host_url(&req));
|
let filled_index_html = fill_index_html(req, config);
|
||||||
let index_html = if config.no_auth_limits.is_some() {
|
|
||||||
INDEX_AUTH_HTML
|
|
||||||
} else {
|
|
||||||
INDEX_HTML
|
|
||||||
};
|
|
||||||
let filled_index_html = index_html.replace("{upload_url}", upload_url.as_str());
|
|
||||||
Ok(HttpResponse::Ok()
|
Ok(HttpResponse::Ok()
|
||||||
.content_type("text/html")
|
.content_type("text/html")
|
||||||
.body(filled_index_html))
|
.body(filled_index_html))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fill_index_html(req: web::HttpRequest, config: web::Data<Config>) -> String {
|
||||||
|
let upload_url = format!("{}/upload", get_host_url(&req));
|
||||||
|
let auth_snippet = config
|
||||||
|
.no_auth_limits
|
||||||
|
.as_ref()
|
||||||
|
.map_or("", |_| AUTH_SNIPPET_HTML);
|
||||||
|
let max_size_snippet = config
|
||||||
|
.max_file_size
|
||||||
|
.as_ref()
|
||||||
|
.map_or("", |_| MAX_SIZE_SNIPPET_HTML);
|
||||||
|
INDEX_HTML
|
||||||
|
.replace("{max_size_snippet}", max_size_snippet)
|
||||||
|
.replace(
|
||||||
|
"{max_size}",
|
||||||
|
&render_file_size(config.max_file_size.unwrap_or(0)),
|
||||||
|
)
|
||||||
|
.replace("{auth_snippet}", auth_snippet)
|
||||||
|
.replace(
|
||||||
|
"{auth_time}",
|
||||||
|
&config
|
||||||
|
.no_auth_limits
|
||||||
|
.as_ref()
|
||||||
|
.map(|limit| limit.max_time)
|
||||||
|
.map_or("".into(), render_duration),
|
||||||
|
)
|
||||||
|
.replace(
|
||||||
|
"{auth_large_time}",
|
||||||
|
&config
|
||||||
|
.no_auth_limits
|
||||||
|
.as_ref()
|
||||||
|
.map(|limit| limit.large_file_max_time)
|
||||||
|
.map_or("".into(), render_duration),
|
||||||
|
)
|
||||||
|
.replace(
|
||||||
|
"{auth_large_size}",
|
||||||
|
&config
|
||||||
|
.no_auth_limits
|
||||||
|
.as_ref()
|
||||||
|
.map(|limit| limit.large_file_size)
|
||||||
|
.map_or("".into(), render_file_size),
|
||||||
|
)
|
||||||
|
.replace("{upload_url}", upload_url.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_file_size(size: u64) -> String {
|
||||||
|
let magnitude = cmp::min((size as f64).log(1024.0) as u32, 5);
|
||||||
|
let prefix = ["", "ki", "Mi", "Gi", "Ti", "Pi"][magnitude as usize];
|
||||||
|
let value = size / (1024_u64.pow(magnitude));
|
||||||
|
format!("{}{}B", value, prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_duration(duration: Duration) -> String {
|
||||||
|
let days = duration.num_days();
|
||||||
|
let hours = duration.num_hours() % 24;
|
||||||
|
let minutes = duration.num_minutes() % 60;
|
||||||
|
let seconds = duration.num_seconds() % 60;
|
||||||
|
let mut elements = vec![];
|
||||||
|
if let Some(name) = pluralize(days, "tag", "e") {
|
||||||
|
elements.push(name);
|
||||||
|
}
|
||||||
|
if let Some(name) = pluralize(hours, "stunde", "n") {
|
||||||
|
elements.push(name);
|
||||||
|
}
|
||||||
|
if let Some(name) = pluralize(minutes, "minute", "n") {
|
||||||
|
elements.push(name);
|
||||||
|
}
|
||||||
|
if let Some(name) = pluralize(seconds, "sekunde", "n") {
|
||||||
|
elements.push(name);
|
||||||
|
}
|
||||||
|
elements.join("+")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pluralize(number: i64, word: &str, suffix: &str) -> Option<String> {
|
||||||
|
match number {
|
||||||
|
0 => None,
|
||||||
|
1 => Some(format!("{} {}", number, word)),
|
||||||
|
_ => Some(format!("{} {}{}", number, word, suffix)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn auth_hide(config: web::Data<Config>) -> Result<HttpResponse, Error> {
|
pub async fn auth_hide(config: web::Data<Config>) -> Result<HttpResponse, Error> {
|
||||||
if let Some(no_auth_limits) = &config.no_auth_limits {
|
if let Some(no_auth_limits) = &config.no_auth_limits {
|
||||||
let auth_hide_js = AUTH_HIDE_JS
|
let auth_hide_js = AUTH_HIDE_JS
|
||||||
.replace("{no_auth_max_time}", &no_auth_limits.max_time.to_string())
|
.replace(
|
||||||
|
"{no_auth_max_time}",
|
||||||
|
&no_auth_limits.max_time.num_seconds().to_string(),
|
||||||
|
)
|
||||||
.replace(
|
.replace(
|
||||||
"{no_auth_large_file_max_time}",
|
"{no_auth_large_file_max_time}",
|
||||||
&no_auth_limits.large_file_max_time.to_string(),
|
&no_auth_limits.large_file_max_time.num_seconds().to_string(),
|
||||||
)
|
)
|
||||||
.replace(
|
.replace(
|
||||||
"{no_auth_large_file_size}",
|
"{no_auth_large_file_size}",
|
||||||
|
|
|
@ -69,6 +69,8 @@ input[type="checkbox"] {
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
font-family: sans;
|
||||||
|
font-weight: normal;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,30 +6,32 @@ const passwordInput = document.getElementById("password-input");
|
||||||
const maxTime = Number("{no_auth_max_time}");
|
const maxTime = Number("{no_auth_max_time}");
|
||||||
const largeFileMaxTime = Number("{no_auth_large_file_max_time}");
|
const largeFileMaxTime = Number("{no_auth_large_file_max_time}");
|
||||||
const largeFileSize = Number("{no_auth_large_file_size}");
|
const largeFileSize = Number("{no_auth_large_file_size}");
|
||||||
const updatePasswordInput = () => {
|
|
||||||
const requirePassword = keep > maxTime || (size > largeFileSize && keep > largeFileMaxTime);
|
|
||||||
passwordInput.className = requirePassword ? "" : "hidden";
|
|
||||||
};
|
|
||||||
|
|
||||||
let keep = Number(keepFor.value);
|
let keep = Number(keepFor.value);
|
||||||
let size = fileUpload.files[0]
|
let size = fileUpload.files[0]
|
||||||
? fileUpload.files[0].size
|
? fileUpload.files[0].size
|
||||||
: textUpload.value.length;
|
: textUpload.value.length;
|
||||||
|
|
||||||
|
const updatePasswordInput = () => {
|
||||||
|
const requirePassword = keep > maxTime || (size > largeFileSize && keep > largeFileMaxTime);
|
||||||
|
passwordInput.className = requirePassword ? "" : "hidden";
|
||||||
|
};
|
||||||
|
|
||||||
updatePasswordInput();
|
updatePasswordInput();
|
||||||
|
|
||||||
fileUpload.addEventListener("change", (e) => {
|
fileUpload.addEventListener("change", () => {
|
||||||
size = fileUpload.files[0]
|
size = fileUpload.files[0]
|
||||||
? fileUpload.files[0].size
|
? fileUpload.files[0].size
|
||||||
: textUpload.value.length;
|
: textUpload.value.length;
|
||||||
updatePasswordInput();
|
updatePasswordInput();
|
||||||
});
|
});
|
||||||
textUpload.addEventListener("input", (e) => {
|
textUpload.addEventListener("input", () => {
|
||||||
if (!fileUpload.files[0]) {
|
if (!fileUpload.files[0]) {
|
||||||
size = textUpload.value.length;
|
size = textUpload.value.length;
|
||||||
updatePasswordInput();
|
updatePasswordInput();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
keepFor.addEventListener("change", (e) => {
|
keepFor.addEventListener("change", () => {
|
||||||
keep = Number(keepFor.value);
|
keep = Number(keepFor.value);
|
||||||
updatePasswordInput();
|
updatePasswordInput();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="de-DE">
|
|
||||||
<head>
|
|
||||||
<title>datatrash</title>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<meta name="description" content="Temporärer Dateiaustausch" />
|
|
||||||
<link href="/static/index.css" rel="stylesheet" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main>
|
|
||||||
<h1>datatrash</h1>
|
|
||||||
<form action="/upload" method="POST" enctype="multipart/form-data">
|
|
||||||
<label for="file-upload">datei</label>
|
|
||||||
<br />
|
|
||||||
<input id="file-upload" type="file" name="file" />
|
|
||||||
<br />
|
|
||||||
<label for="text-upload">oder asciitrash</label>
|
|
||||||
<br />
|
|
||||||
<textarea id="text-upload" name="text" rows="20" cols="120"></textarea>
|
|
||||||
<br />
|
|
||||||
<label for="keep_for">gültig für</label>
|
|
||||||
<select id="keep_for" name="keep_for">
|
|
||||||
<option value="1800">30 minuten</option>
|
|
||||||
<option value="3600">60 minuten</option>
|
|
||||||
<option value="43200">12 stunden</option>
|
|
||||||
<option value="86400">24 stunden</option>
|
|
||||||
<option value="604800">eine woche</option>
|
|
||||||
<option value="2678400">einen monat</option>
|
|
||||||
</select>
|
|
||||||
<br />
|
|
||||||
<input
|
|
||||||
id="delete_on_download"
|
|
||||||
type="checkbox"
|
|
||||||
name="delete_on_download"
|
|
||||||
/>
|
|
||||||
<label for="delete_on_download">nach einem download löschen</label>
|
|
||||||
<br />
|
|
||||||
<div id="password-input">
|
|
||||||
<label for="password">
|
|
||||||
authentifizierung für große, oder lang gültige uploads
|
|
||||||
</label>
|
|
||||||
<br />
|
|
||||||
<input id="password" name="password" type="password" />
|
|
||||||
</div>
|
|
||||||
<input class="main button" type="submit" value="hochladen" />
|
|
||||||
</form>
|
|
||||||
<details class="usage">
|
|
||||||
<summary>nutzung als api</summary>
|
|
||||||
<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}
|
|
||||||
limit to one download
|
|
||||||
curl -F 'text=your text' -F 'delete_on_download=true' {upload_url}</pre
|
|
||||||
>
|
|
||||||
</details>
|
|
||||||
</main>
|
|
||||||
<script src="/assets/auth-hide.js" lang="javascript"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -11,7 +11,7 @@
|
||||||
<main>
|
<main>
|
||||||
<h1>datatrash</h1>
|
<h1>datatrash</h1>
|
||||||
<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{max_size_snippet}</label>
|
||||||
<br />
|
<br />
|
||||||
<input id="file-upload" type="file" name="file" />
|
<input id="file-upload" type="file" name="file" />
|
||||||
<br />
|
<br />
|
||||||
|
@ -35,20 +35,26 @@
|
||||||
name="delete_on_download"
|
name="delete_on_download"
|
||||||
/>
|
/>
|
||||||
<label for="delete_on_download">nach einem download löschen</label>
|
<label for="delete_on_download">nach einem download löschen</label>
|
||||||
<br />
|
<br />{auth_snippet}
|
||||||
<input class="main button" type="submit" value="hochladen" />
|
<input class="main button" type="submit" value="hochladen" />
|
||||||
</form>
|
</form>
|
||||||
<details class="usage">
|
<details class="usage">
|
||||||
<summary>nutzung als api</summary>
|
<summary>nutzung als api</summary>
|
||||||
<pre>
|
<pre>
|
||||||
file upload
|
datei hochladen
|
||||||
curl -F 'file=@yourfile.rs' {upload_url}
|
curl -F 'file=@yourfile.rs' {upload_url}
|
||||||
text upload
|
|
||||||
|
text hochladen
|
||||||
curl -F 'text=your text' {upload_url}
|
curl -F 'text=your text' {upload_url}
|
||||||
including time
|
|
||||||
|
zeitbegrenzung setzen
|
||||||
curl -F 'text=your text' -F 'keep_for=1800' {upload_url}
|
curl -F 'text=your text' -F 'keep_for=1800' {upload_url}
|
||||||
limit to one download
|
|
||||||
curl -F 'text=your text' -F 'delete_on_download=true' {upload_url}</pre
|
nach einem download löschen
|
||||||
|
curl -F 'text=your text' -F 'delete_on_download=true' {upload_url}
|
||||||
|
|
||||||
|
authentifizieren
|
||||||
|
curl -F 'text=your text' -F 'password=…' {upload_url}</pre
|
||||||
>
|
>
|
||||||
</details>
|
</details>
|
||||||
</main>
|
</main>
|
||||||
|
|
Loading…
Reference in New Issue