add environment variable configuration
This commit is contained in:
parent
82b2bd4075
commit
4af61bfd86
13
README.md
13
README.md
|
@ -19,13 +19,14 @@ To get set up:
|
||||||
|
|
||||||
## running & config
|
## running & config
|
||||||
|
|
||||||
At runtime the environment variable `DATABASE_URL` must be set (e.g. `postgres://localhost`).
|
| environment variable | default value |
|
||||||
A folder named `files` needs to be created next to the application.
|
| -------------------- | --------------------- |
|
||||||
|
| DATABASE_URL | postresql://localhost |
|
||||||
|
| SERVER_URL | http://loalhost:8000 |
|
||||||
|
| FILES_DIR | ./files |
|
||||||
|
| UPLOAD_MAX_BYTES | 8388608 (8MiB) |
|
||||||
|
| BIND_ADDRESS | 0.0.0.0:8000 |
|
||||||
|
|
||||||
Other things are not configurable yet.
|
Other things are not configurable yet.
|
||||||
|
|
||||||
- The application listens on port 8000
|
|
||||||
- The server url is `http://localhost:8000/`
|
|
||||||
- The upload limit is 8MiB
|
|
||||||
- The maximum filename length is 255
|
- The maximum filename length is 255
|
||||||
- The uploaded files are stored in the `files` directory
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use chrono::{prelude::*, Duration};
|
||||||
use futures::future::FutureExt;
|
use futures::future::FutureExt;
|
||||||
use sqlx::postgres::PgPool;
|
use sqlx::postgres::PgPool;
|
||||||
|
|
||||||
pub(crate) async fn delete_old_files(receiver: Receiver<()>, db: PgPool) {
|
pub(crate) async fn delete_old_files(receiver: Receiver<()>, db: PgPool, files_dir: PathBuf) {
|
||||||
loop {
|
loop {
|
||||||
wait_for_file_expiry(&receiver, &db).await;
|
wait_for_file_expiry(&receiver, &db).await;
|
||||||
let now = Local::now().naive_local();
|
let now = Local::now().naive_local();
|
||||||
|
@ -13,7 +13,8 @@ pub(crate) async fn delete_old_files(receiver: Receiver<()>, db: PgPool) {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
for expired_file in expired_files {
|
for expired_file in expired_files {
|
||||||
let path = PathBuf::from(&format!("files/{}", expired_file.file_id));
|
let mut path = files_dir.clone();
|
||||||
|
path.push(&expired_file.file_id);
|
||||||
if path.exists().await {
|
if path.exists().await {
|
||||||
log::info!("delete file {}", expired_file.file_id);
|
log::info!("delete file {}", expired_file.file_id);
|
||||||
fs::remove_file(&path).await.expect("could not delete file");
|
fs::remove_file(&path).await.expect("could not delete file");
|
||||||
|
|
47
src/main.rs
47
src/main.rs
|
@ -30,9 +30,11 @@ async fn upload(
|
||||||
payload: Multipart,
|
payload: Multipart,
|
||||||
db: web::Data<PgPool>,
|
db: web::Data<PgPool>,
|
||||||
sender: web::Data<Sender<()>>,
|
sender: web::Data<Sender<()>>,
|
||||||
|
config: web::Data<Config>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let file_id = format!("{:x?}", rand::random::<u32>());
|
let file_id = format!("{:x?}", rand::random::<u32>());
|
||||||
let filename = PathBuf::from(format!("files/{}", file_id));
|
let mut filename = config.files_dir.clone();
|
||||||
|
filename.push(&file_id);
|
||||||
|
|
||||||
let (original_name, valid_till, kind) =
|
let (original_name, valid_till, kind) =
|
||||||
match multipart::parse_multipart(payload, &file_id, &filename).await {
|
match multipart::parse_multipart(payload, &file_id, &filename).await {
|
||||||
|
@ -72,8 +74,10 @@ async fn upload(
|
||||||
.finish())
|
.finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn uploaded(id: web::Path<String>) -> Result<HttpResponse, Error> {
|
async fn uploaded(id: web::Path<String>, config: web::Data<Config>) -> Result<HttpResponse, Error> {
|
||||||
let upload_html = UPLOAD_HTML.replace("{id}", id.as_ref());
|
let upload_html = UPLOAD_HTML
|
||||||
|
.replace("{id}", id.as_ref())
|
||||||
|
.replace("{server}", &config.server_url);
|
||||||
Ok(HttpResponse::Ok()
|
Ok(HttpResponse::Ok()
|
||||||
.content_type("text/html")
|
.content_type("text/html")
|
||||||
.body(upload_html))
|
.body(upload_html))
|
||||||
|
@ -83,6 +87,7 @@ async fn download(
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
id: web::Path<String>,
|
id: web::Path<String>,
|
||||||
db: web::Data<PgPool>,
|
db: web::Data<PgPool>,
|
||||||
|
config: web::Data<Config>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let row = sqlx::query!(
|
let row = sqlx::query!(
|
||||||
"SELECT file_id, file_name, kind from files WHERE file_id = $1",
|
"SELECT file_id, file_name, kind from files WHERE file_id = $1",
|
||||||
|
@ -91,7 +96,8 @@ async fn download(
|
||||||
.fetch_one(db.as_ref())
|
.fetch_one(db.as_ref())
|
||||||
.await
|
.await
|
||||||
.map_err(|_| error::ErrorNotFound("could not find file"))?;
|
.map_err(|_| error::ErrorNotFound("could not find file"))?;
|
||||||
let path: PathBuf = PathBuf::from(format!("files/{}", row.file_id));
|
let mut path = config.files_dir.clone();
|
||||||
|
path.push(&row.file_id);
|
||||||
|
|
||||||
if row.kind == FileKind::TEXT.to_string() {
|
if row.kind == FileKind::TEXT.to_string() {
|
||||||
let content = fs::read_to_string(path).await?;
|
let content = fs::read_to_string(path).await?;
|
||||||
|
@ -110,7 +116,7 @@ async fn download(
|
||||||
async fn setup_db() -> PgPool {
|
async fn setup_db() -> PgPool {
|
||||||
let pool = PgPool::builder()
|
let pool = PgPool::builder()
|
||||||
.max_size(5)
|
.max_size(5)
|
||||||
.build(&env::var("DATABASE_URL").expect("DATABASE_URL environement variable not set"))
|
.build(&env::var("DATABASE_URL").unwrap_or_else(|_| "postgresql://localhost".to_owned()))
|
||||||
.await
|
.await
|
||||||
.expect("could not create db pool");
|
.expect("could not create db pool");
|
||||||
|
|
||||||
|
@ -122,6 +128,12 @@ async fn setup_db() -> PgPool {
|
||||||
pool
|
pool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Config {
|
||||||
|
server_url: String,
|
||||||
|
files_dir: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
#[actix_rt::main]
|
#[actix_rt::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
std::env::set_var("RUST_LOG", "warn,datatrash=info,actix_web=info");
|
std::env::set_var("RUST_LOG", "warn,datatrash=info,actix_web=info");
|
||||||
|
@ -131,25 +143,42 @@ async fn main() -> std::io::Result<()> {
|
||||||
|
|
||||||
log::info!("omnomnom");
|
log::info!("omnomnom");
|
||||||
|
|
||||||
|
let config = Config {
|
||||||
|
server_url: env::var("SERVER_URL").unwrap_or_else(|_| "http://localhost:8000".to_owned()),
|
||||||
|
files_dir: PathBuf::from(env::var("FILES_DIR").unwrap_or_else(|_| "./files".to_owned())),
|
||||||
|
};
|
||||||
|
|
||||||
let (send, recv) = async_std::sync::channel::<()>(1);
|
let (send, recv) = async_std::sync::channel::<()>(1);
|
||||||
task::spawn(deleter::delete_old_files(recv, pool.clone()));
|
task::spawn(deleter::delete_old_files(
|
||||||
|
recv,
|
||||||
|
pool.clone(),
|
||||||
|
config.files_dir.clone(),
|
||||||
|
));
|
||||||
|
|
||||||
let db = web::Data::new(pool);
|
let db = web::Data::new(pool);
|
||||||
let send = web::Data::new(send);
|
let send = web::Data::new(send);
|
||||||
|
let max_bytes: usize = env::var("UPLOAD_MAX_BYTES")
|
||||||
|
.ok()
|
||||||
|
.and_then(|variable| variable.parse().ok())
|
||||||
|
.unwrap_or(8_388_608);
|
||||||
|
let bind_address = env::var("BIND_ADDRESS").unwrap_or_else(|_| "0.0.0.0:8000".to_owned());
|
||||||
|
|
||||||
HttpServer::new(move || {
|
HttpServer::new({
|
||||||
|
move || {
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(middleware::Logger::default())
|
.wrap(middleware::Logger::default())
|
||||||
.app_data(db.clone())
|
.app_data(db.clone())
|
||||||
.app_data(send.clone())
|
.app_data(send.clone())
|
||||||
.app_data(Bytes::configure(|cfg| cfg.limit(8_388_608)))
|
.app_data(Bytes::configure(|cfg| cfg.limit(max_bytes)))
|
||||||
|
.data(config.clone())
|
||||||
.service(web::resource("/").route(web::get().to(index)))
|
.service(web::resource("/").route(web::get().to(index)))
|
||||||
.service(web::resource("/upload").route(web::post().to(upload)))
|
.service(web::resource("/upload").route(web::post().to(upload)))
|
||||||
.service(web::resource("/upload/{id}").route(web::get().to(uploaded)))
|
.service(web::resource("/upload/{id}").route(web::get().to(uploaded)))
|
||||||
.service(web::resource("/file/{id}").route(web::get().to(download)))
|
.service(web::resource("/file/{id}").route(web::get().to(download)))
|
||||||
.service(Files::new("/static", "static").disable_content_disposition())
|
.service(Files::new("/static", "static").disable_content_disposition())
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.bind("0.0.0.0:8000")?
|
.bind(bind_address)?
|
||||||
.run()
|
.run()
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<h1><a href="/">datatrash</a></h1>
|
<h1><a href="/">datatrash</a></h1>
|
||||||
<p>
|
<p>
|
||||||
Uploaded
|
Uploaded
|
||||||
<a href="http://localhost:8000/file/{id}">
|
<a href="{server}/file/{id}">
|
||||||
http://localhost:8000/files/{id}
|
http://localhost:8000/files/{id}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
Loading…
Reference in New Issue