diff --git a/Cargo.lock b/Cargo.lock index 302e8dc..de3cad8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -612,9 +612,13 @@ dependencies = [ "clap", "dotenvy", "env_logger", + "lazy_static", "log", + "rand", + "ring", "sqlx", "tokio", + "tower-http", "url", ] @@ -1124,6 +1128,21 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rsa" version = "0.9.6" @@ -1666,6 +1685,22 @@ dependencies = [ "tower-service", ] +[[package]] +name = "tower-http" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +dependencies = [ + "bitflags 2.4.2", + "bytes", + "http", + "http-body", + "http-body-util", + "pin-project-lite", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.2" @@ -1764,6 +1799,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.0" diff --git a/Cargo.toml b/Cargo.toml index 96daf6b..c3c5168 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,11 @@ chrono = { version = "0.4", default-features = false, features = ["now"] } clap = { version = "4.5", default-features = false, features = ["std", "derive", "unicode", "help", "usage"] } dotenvy = { version = "0.15", default-features = false } env_logger = { version = "0.11", default-features = false, features = ["humantime"] } +lazy_static = "1.4" log = { version = "0.4", default-features = false, features = ["std"] } +rand = { version = "0.8", default-features = false, features = ["std", "std_rng"] } +ring = { version = "0.17", features = ["std"] } sqlx = { version = "0.7", default-features = false, features = ["macros", "sqlite", "migrate", "tls-none", "runtime-tokio"] } tokio = { version = "1", default-features = false, features = ["signal", "rt-multi-thread"] } -url = { version = "2.5.0", default-features = false } +tower-http = { version = "0.5", default-features = false, features = ["cors"] } +url = { version = "2.5", default-features = false } diff --git a/env.example b/env.example index a755e4b..96e0380 100644 --- a/env.example +++ b/env.example @@ -1,4 +1,5 @@ DATABASE_URL=sqlite:///${HOME}/.hitman.db -DATABASE_FILE=file:///${HOME}/.hitman.db -LISTENING_ADDR=127.0.0.1 +DATABASE_FILE=${HOME}/.hitman.db +LISTENING_ADDR=0.0.0.0 LISTENING_PORT=5000 +HITMAN_ORIGIN=http://localhost:3000 diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/index.html b/index.html new file mode 100644 index 0000000..721e54a --- /dev/null +++ b/index.html @@ -0,0 +1,26 @@ + + + +
+ + + + + + + + + + + + diff --git a/migrations/20240317182405_hits.up.sql b/migrations/20240317182405_hits.up.sql index 9e377ff..a5853c2 100644 --- a/migrations/20240317182405_hits.up.sql +++ b/migrations/20240317182405_hits.up.sql @@ -1,7 +1,8 @@ create table if not exists hits ( id integer primary key, - page text not null, - accessed text not null + page text not null, -- the slug from the page + hit_key blob not null unique, + accessed timestamp not null default CURRENT_TIMESTAMP ); create index if not exists hits_page_dex on hits(page); diff --git a/src/main.rs b/src/main.rs index 4126f9a..4c8ad56 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,25 +6,41 @@ use std::{ }; use axum::{ + debug_handler, extract::{Path, State}, - http::{header::REFERER, HeaderMap}, + http::{header::REFERER, method::Method, HeaderMap, HeaderValue}, routing::get, Router, }; use clap::Parser; +use lazy_static::lazy_static; +use ring::digest::{Context, SHA256}; use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions}; use tokio::net::TcpListener; +use tower_http::cors::{self, CorsLayer}; use url::Url; +lazy_static! { + static ref HITMAN_ORIGIN: String = + std::env::var("HITMAN_ORIGIN").expect("could not get origin for service"); + static ref SESSION_SALT: u64 = rand::random(); +} + #[tokio::main] async fn main() { init(); let pool = db().await; + let origin = HeaderValue::from_str(&HITMAN_ORIGIN).unwrap(); + let cors_layer = CorsLayer::new() + .allow_origin(origin) + .allow_methods(cors::AllowMethods::exact(Method::GET)); + let app = Router::new() - .route("/hit", get(register_hit)) - .route("/hits", get(get_hits)) - .route("/hits/:period", get(get_hits)) + .route("/hit/:slug", get(register_hit)) + .route("/hits/:slug", get(get_hits)) + .route("/hits/:slug/:period", get(get_hits)) + .layer(cors_layer) .with_state(pool.clone()) .into_make_service(); let listener = mklistener().await; @@ -36,25 +52,29 @@ async fn main() { pool.close().await; } -async fn register_hit(State(db): State