Skip to content

Commit

Permalink
Request tracking, middleware changes, migration folder bug fix (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
levkk authored Oct 29, 2024
1 parent aa462f2 commit 65774ef
Show file tree
Hide file tree
Showing 35 changed files with 505 additions and 69 deletions.
20 changes: 20 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ members = [
"rwf-macros",
"rwf-tests",
"examples/django",
"examples/request-tracking",
]
4 changes: 2 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Rwf is brand new, but web development is ancient. Many features are missing or a

## Dynanic templates

- [ ] Better error messages, e.g. syntax errors, undefined variables, functions, etc.
- [x] Better error messages, e.g. syntax errors, undefined variables, functions, etc.
- [ ] More data types support, e.g. UUIDs, timestampts, whatever Rust data types we forgot to add
- [ ] More tests
- [ ] Allow for extending template syntax with user-defined functions (defined at startup)
Expand Down Expand Up @@ -56,7 +56,7 @@ Rwf is brand new, but web development is ancient. Many features are missing or a
## Built-ins

- [ ] Feature flags and experiments
- [ ] Tracking (user requests)
- [x] Tracking (user requests)

## More?

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/controllers/middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl Middleware for RequiredHeaders {
let header = request.headers().get(header);

if header.is_none() {
return Ok(Outcome::Stop(Response::bad_request()));
return Ok(Outcome::Stop(request, Response::bad_request()));
}
}

Expand Down
4 changes: 2 additions & 2 deletions examples/middleware/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ impl Middleware for OnlyLinuxBrowsers {
}
}

return Ok(Outcome::Stop(Response::redirect("https://archlinux.org")))
return Ok(Outcome::Stop(request, Response::redirect("https://archlinux.org")))
}
}
```
Expand Down Expand Up @@ -64,4 +64,4 @@ Middleware is evaluated in the order it's added to the middleware set. The middl

## Modifying responses

To modify responses, implement the `handle_response` method on the `Middleware` trait. See the included [request rate limiter](rwf/src/controller/middleware/rate_limiter.rs) middleware for complete example.
To modify responses, implement the `handle_response` method on the `Middleware` trait. See the included [request rate limiter](/rwf/src/controller/middleware/rate_limiter.rs) middleware for complete example.
2 changes: 1 addition & 1 deletion examples/middleware/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl Middleware for BlockBadHeader {
}
}

Ok(Outcome::Stop(Response::bad_request()))
Ok(Outcome::Stop(request, Response::bad_request()))
}
}

Expand Down
11 changes: 11 additions & 0 deletions examples/request-tracking/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "request-tracking"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rwf = { path = "../../rwf" }
tokio = { version = "1", features = ["full"] }
rand = "0.8"
Empty file.
6 changes: 6 additions & 0 deletions examples/request-tracking/rwf.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[general]
track_requests = true
log_queries = true

[database]
name = "rwf_request_tracking"
1 change: 1 addition & 0 deletions examples/request-tracking/src/controllers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

37 changes: 37 additions & 0 deletions examples/request-tracking/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
mod controllers;
mod models;

use rand::Rng;
use rwf::{http, prelude::*, Server};

#[derive(Default)]
struct Index;

#[async_trait]
impl Controller for Index {
async fn handle(&self, _request: &Request) -> Result<Response, Error> {
let ok = rand::thread_rng().gen::<bool>();

if ok {
// This is tracked.
Ok(Response::new().html("
<h2>All requests are tracked</h2>
<p>To view requests, connect to the <code>rwf_request_tracking</code> database and run:</p>
<code>SELECT * FROM rwf_requests ORDER BY id</code>
"))
} else {
// This is tracked also.
Err(Error::HttpError(Box::new(http::Error::MissingParameter)))
}
}
}

#[tokio::main]
async fn main() -> Result<(), http::Error> {
Logger::init();
Migrations::migrate().await?;

Server::new(vec![route!("/" => Index)])
.launch("0.0.0.0:8000")
.await
}
1 change: 1 addition & 0 deletions examples/request-tracking/src/models/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Empty file.
2 changes: 1 addition & 1 deletion examples/turbo/rwf.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ log_queries = true
cache_templates = true

[database]
name = "rwf_turbo"
name = "rwf_turbo"
2 changes: 1 addition & 1 deletion examples/turbo/src/controllers/signup/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ impl Middleware for LoggedInCheck {
async fn handle_request(&self, request: Request) -> Result<Outcome, Error> {
if let Some(session) = request.session() {
if session.authenticated() {
return Ok(Outcome::Stop(Response::new().redirect("/chat")));
return Ok(Outcome::Stop(request, Response::new().redirect("/chat")));
}
}

Expand Down
18 changes: 18 additions & 0 deletions rwf-cli/src/setup.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::path::Path;
use tokio::fs::{create_dir_all, read_to_string, File};
use tokio::process::Command;

use crate::logging::created;
use rwf::colors::MaybeColorize;
Expand Down Expand Up @@ -62,4 +63,21 @@ pub async fn setup() {
break;
}
}

// Add rwf dependencies
Command::new("cargo")
.arg("add")
.arg("tokio@1")
.arg("--features")
.arg("full")
.status()
.await
.unwrap();

Command::new("cargo")
.arg("add")
.arg("rwf")
.status()
.await
.unwrap();
}
2 changes: 2 additions & 0 deletions rwf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ time = { version = "0.3", features = ["formatting", "serde", "parsing"] }
tokio-postgres = { version = "0.7", features = [
"with-time-0_3",
"with-serde_json-1",
"with-uuid-1",
] }
bytes = "1"
tokio = { version = "1", features = ["full"] }
Expand All @@ -38,6 +39,7 @@ sha1 = "0.10"
toml = "0.8"
pyo3 = { version = "0.22", features = ["auto-initialize"], optional = true }
rayon = { version = "1", optional = true }
uuid = { version = "1", features = ["v4"] }

[dev-dependencies]
tempdir = "0.3"
3 changes: 3 additions & 0 deletions rwf/src/analytics/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod requests;

pub use requests::Request;
69 changes: 69 additions & 0 deletions rwf/src/analytics/requests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use std::net::IpAddr;

use crate::model::{Error, FromRow, Model, ToValue, Value};
use time::OffsetDateTime;

#[derive(Clone)]
pub struct Request {
id: Option<i64>,
path: String,
method: String,
query: serde_json::Value,
code: i32,
client_ip: Option<IpAddr>,
created_at: OffsetDateTime,
duration: f32,
}

impl FromRow for Request {
fn from_row(row: tokio_postgres::Row) -> Result<Self, Error> {
Ok(Self {
id: row.try_get("id")?,
path: row.try_get("path")?,
method: row.try_get("method")?,
query: row.try_get("query")?,
code: row.try_get("code")?,
client_ip: row.try_get("client")?,
created_at: row.try_get("created_at")?,
duration: row.try_get("duration")?,
})
}
}

impl Model for Request {
fn id(&self) -> Value {
self.id.to_value()
}

fn table_name() -> &'static str {
"rwf_requests"
}

fn foreign_key() -> &'static str {
"rwf_request_id"
}

fn column_names() -> &'static [&'static str] {
&[
"path",
"method",
"query",
"code",
"client_ip",
"created_at",
"duration",
]
}

fn values(&self) -> Vec<Value> {
vec![
self.path.to_value(),
self.method.to_value(),
self.query.to_value(),
self.code.to_value(),
self.client_ip.to_value(),
self.created_at.to_value(),
self.duration.to_value(),
]
}
}
19 changes: 18 additions & 1 deletion rwf/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::io::IsTerminal;
use std::path::{Path, PathBuf};
use time::Duration;

use crate::controller::middleware::{request_tracker::RequestTracker, Middleware};
use crate::controller::{AllowAll, AuthHandler, MiddlewareSet};
use rand::{rngs::OsRng, RngCore};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -168,7 +169,9 @@ impl Default for Config {
tty: std::io::stderr().is_terminal(),
default_auth: AuthHandler::new(AllowAll {}),
session_duration: Duration::days(4),
default_middleware: MiddlewareSet::default(),
default_middleware: MiddlewareSet::without_default(vec![
RequestTracker::new().middleware()
]),
cache_templates,
websocket: Websocket::default(),
log_queries: var("RWF_LOG_QUERIES").is_ok(),
Expand Down Expand Up @@ -216,6 +219,14 @@ impl Config {
.database
.from_config_file(&config_file.database.unwrap_or_default());

let mut middelware = vec![];

if config_file.general.track_requests {
middelware.push(RequestTracker::new().middleware());
}

config.default_middleware = MiddlewareSet::without_default(middelware);

Ok(config)
}

Expand Down Expand Up @@ -251,6 +262,8 @@ struct General {
log_queries: bool,
#[serde(default = "General::default_cache_templates")]
cache_templates: bool,
#[serde(default = "General::default_track_requests")]
track_requests: bool,
}

impl General {
Expand Down Expand Up @@ -284,6 +297,10 @@ impl General {
#[cfg(not(debug_assertions))]
return true;
}

fn default_track_requests() -> bool {
false
}
}

#[derive(Serialize, Deserialize, Default)]
Expand Down
9 changes: 9 additions & 0 deletions rwf/src/controller/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,15 @@ impl SessionId {
}
}

impl std::fmt::Display for SessionId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SessionId::Authenticated(id) => write!(f, "{}", id),
SessionId::Guest(id) => write!(f, "{}", id),
}
}
}

impl Default for SessionId {
fn default() -> Self {
use rand::{distributions::Alphanumeric, thread_rng, Rng};
Expand Down
Loading

0 comments on commit 65774ef

Please sign in to comment.