Compare commits

..

9 commits

7 changed files with 71 additions and 29 deletions

26
Cargo.lock generated
View file

@ -290,6 +290,21 @@ dependencies = [
"syn",
]
[[package]]
name = "axum-range"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c30398a7f716ebdd7f3c8a4f7a7a6df48a30e002007fd57b2a7a00fac864bd"
dependencies = [
"axum",
"axum-extra",
"bytes",
"futures",
"http-body",
"pin-project",
"tokio",
]
[[package]]
name = "backtrace"
version = "0.3.71"
@ -1457,13 +1472,14 @@ dependencies = [
[[package]]
name = "nyazoom"
version = "0.2.4"
version = "0.3.2"
dependencies = [
"async-bincode",
"async-trait",
"async_zip",
"axum",
"axum-extra",
"axum-range",
"bincode",
"chrono",
"clap",
@ -1483,7 +1499,7 @@ dependencies = [
"sqlx",
"tokio",
"tokio-util",
"tower 0.5.0",
"tower 0.5.2",
"tower-http",
"tracing",
"tracing-subscriber",
@ -2656,14 +2672,14 @@ dependencies = [
[[package]]
name = "tower"
version = "0.5.0"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36b837f86b25d7c0d7988f00a54e74739be6477f2aac6201b8f429a7569991b7"
checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
dependencies = [
"futures-core",
"futures-util",
"pin-project-lite",
"sync_wrapper 0.1.2",
"sync_wrapper 1.0.1",
"tower-layer",
"tower-service",
]

View file

@ -1,6 +1,6 @@
[package]
name = "nyazoom"
version = "0.2.4"
version = "0.3.2"
edition = "2021"
authors = ["Zynh Ludwig <Zynh0722@gmail.com>"]
readme = "README.md"
@ -46,6 +46,7 @@ tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
color-eyre = "0.6.3"
eyre = "0.6.12"
clap = "4.5.26"
axum-range = "0.4.0"
# I want to use askama's block feature, this requires unreleased 0.13
[dependencies.sqlx]

View file

@ -7,8 +7,9 @@ README is a supreme work in progress, terribly sorry
<!-- markdownlint-disable MD013 -->
| variable | description | default |
| ----------- | --------------------------------------------- | ------------------------ |
| `CACHE_DIR` | the location for storing all downloaded files | "/var/lib/nyazoom/cache" |
| `DIST_DIR` | location of the folder to be static served | "./dist" |
| -------------- | --------------------------------------------- | ------------------------ |
| `NZ_CACHE_DIR` | the location for storing all downloaded files | "/var/lib/nyazoom/cache" |
| `NZ_DIST_DIR` | location of the folder to be static served | "./dist" |
| `DATABASE_URL` | sqlite database connection url | "./dist" |
<!-- markdownlint-restore -->

5
build.rs Normal file
View file

@ -0,0 +1,5 @@
// generated by `sqlx migrate build-script`
fn main() {
// trigger recompilation when a new migration is added
println!("cargo:rerun-if-changed=migrations");
}

View file

@ -6,13 +6,15 @@ use axum::{
Router,
};
use axum_extra::TypedHeader;
use eyre::Context;
use tower_http::{services::ServeDir, trace::TraceLayer};
use views::templates::WelcomeTemplate;
use std::{net::SocketAddr, sync::OnceLock};
use std::net::SocketAddr;
use nyazoom::{config::Settings, *};
use nyazoom::*;
use config::Settings;
use util::{headers::ForwardedFor, logging, sweeper};
#[tokio::main]
@ -20,16 +22,26 @@ async fn main() -> eyre::Result<()> {
color_eyre::install()?;
logging::init_tracing();
let config = Settings::try_new()?;
tracing::info!(?config, "config generated");
// Generate settings from figments and create initial AppState
let state = AppState::new_with_settings(Settings::try_new()?);
// uses create_dir_all to create both .cache and serve inside it in one go
util::make_dir(&config.cache.dir).await?;
tracing::debug!(
chache_dir=?state.settings.cache.dir,
dist_dir=?state.settings.dist.dir,
db_url=?state.settings.database.url,
"config generated"
);
let fallback_service = ServeDir::new(&config.dist.dir);
// uses create_dir_all to ensure a valid directory structure
util::make_dir(&state.settings.cache.dir).await?;
let state = AppState::new_with_settings(config);
// Use the sqlite pool to run the db migrations
sqlx::migrate!()
.run(&state.pool)
.await
.context("running database migrations")?;
// Spawn the cleanup task
sweeper::spawn(state.clone());
// Router Setup
@ -39,10 +51,10 @@ async fn main() -> eyre::Result<()> {
.nest("/upload", get_upload_router())
.nest("/records", get_records_router())
.nest("/link", get_link_router())
.with_state(state)
.fallback_service(fallback_service)
.fallback_service(ServeDir::new(&state.settings.dist.dir))
.layer(TraceLayer::new_for_http())
.layer(middleware::from_fn(log_source));
.layer(middleware::from_fn(log_source))
.with_state(state);
serve(app).await;

View file

@ -1,12 +1,12 @@
use axum::{
body::Body,
extract::State,
response::{IntoResponse, Redirect},
routing::get,
Router,
};
use axum_extra::{headers::Range, TypedHeader};
use axum_range::{KnownSize, Ranged};
use reqwest::StatusCode;
use tokio_util::io::ReaderStream;
use crate::AppState;
@ -16,6 +16,7 @@ pub fn get_download_router() -> Router<AppState> {
async fn download(
axum::extract::Path(id): axum::extract::Path<String>,
range: Option<TypedHeader<Range>>,
State(state): State<AppState>,
) -> Result<axum::response::Response, (StatusCode, String)> {
let mut conn = state.pool.acquire().await.unwrap();
@ -33,10 +34,11 @@ async fn download(
.await
.map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()))?;
return Ok(axum::response::Response::builder()
.header("Content-Type", "application/zip")
.body(Body::from_stream(ReaderStream::new(file)))
.unwrap());
let body = KnownSize::file(file).await.unwrap();
let range = range.map(|TypedHeader(range)| range);
let ranged_body = Ranged::new(range, body);
return Ok(([("Content-Type", "application/zip")], ranged_body).into_response());
}
Ok(Redirect::to("/404.html").into_response())

View file

@ -1,6 +1,9 @@
use std::str::FromStr;
use sqlx::{sqlite::SqliteConnectOptions, SqlitePool};
use sqlx::{
sqlite::{SqliteConnectOptions, SqliteJournalMode},
SqlitePool,
};
use crate::config::Settings;
@ -16,7 +19,9 @@ impl AppState {
Self {
pool: SqlitePool::connect_lazy_with(
SqliteConnectOptions::from_str(&settings.database.url)
.expect("Invalid Database String"),
.expect("Invalid Database String")
.journal_mode(SqliteJournalMode::Wal)
.create_if_missing(true),
),
settings,
}