chore: initialized repository
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
weight_tracker.db*
|
||||||
2321
Cargo.lock
generated
Normal file
2321
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "weight_tracker"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
axum = "0.8"
|
||||||
|
askama = "0.12"
|
||||||
|
|
||||||
|
sqlx = { version = "0.7", features = ["runtime-tokio", "sqlite", "macros"] }
|
||||||
|
tokio = { version = "1.0", features = ["full"] }
|
||||||
|
thiserror = "1.0"
|
||||||
|
anyhow = "1.0"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
tower-http = { version = "0.5", features = ["fs", "cors"] }
|
||||||
6
migrations/20260408203423_create_weights_table.sql
Normal file
6
migrations/20260408203423_create_weights_table.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
CREATE TABLE weights (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id TEXT NOT NULL,
|
||||||
|
date TEXT NOT NULL,
|
||||||
|
weight REAL NOT NULL
|
||||||
|
);
|
||||||
41
src/handlers.rs
Normal file
41
src/handlers.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
use axum::response::Html;
|
||||||
|
use askama::Template;
|
||||||
|
use sqlx::SqlitePool;
|
||||||
|
use axum::{extract::State, Form};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "index.html")]
|
||||||
|
pub struct IndexTemplate {
|
||||||
|
pub weights: Vec<super::models::Weight>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "input.html")]
|
||||||
|
pub struct InputTemplate;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct WeightForm {
|
||||||
|
date: String,
|
||||||
|
weight: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn index(State(pool): State<SqlitePool>) -> Html<String> {
|
||||||
|
let weights = super::models::get_all_weights(&pool).await.unwrap_or_default();
|
||||||
|
let template = IndexTemplate { weights };
|
||||||
|
Html(template.render().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn input_get() -> Html<String> {
|
||||||
|
let template = InputTemplate;
|
||||||
|
Html(template.render().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn input_post(
|
||||||
|
State(pool): State<SqlitePool>,
|
||||||
|
Form(form): Form<WeightForm>,
|
||||||
|
) -> Html<String> {
|
||||||
|
let user_id = "test_user"; // TODO: Implement OIDC to get real user_id
|
||||||
|
super::models::insert_weight(&pool, user_id, &form.date, form.weight).await.unwrap();
|
||||||
|
Html("<p>Weight added successfully!</p>".to_string())
|
||||||
|
}
|
||||||
2
src/lib.rs
Normal file
2
src/lib.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod handlers;
|
||||||
|
pub mod models;
|
||||||
27
src/main.rs
Normal file
27
src/main.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use axum::{
|
||||||
|
routing::get,
|
||||||
|
Router,
|
||||||
|
};
|
||||||
|
use tower_http::services::ServeDir;
|
||||||
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
// Set up database
|
||||||
|
let database_url = "sqlite:weight_tracker.db";
|
||||||
|
let pool = SqlitePool::connect(database_url).await.expect("Failed to connect to database");
|
||||||
|
|
||||||
|
// build our application with a route
|
||||||
|
let app = Router::new()
|
||||||
|
.route("/", get(weight_tracker::handlers::index))
|
||||||
|
.route("/input", get(weight_tracker::handlers::input_get).post(weight_tracker::handlers::input_post))
|
||||||
|
.with_state(pool)
|
||||||
|
.nest_service("/static", ServeDir::new("static"));
|
||||||
|
|
||||||
|
// run it
|
||||||
|
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
println!("listening on {}", listener.local_addr().unwrap());
|
||||||
|
axum::serve(listener, app).await.unwrap();
|
||||||
|
}
|
||||||
26
src/models.rs
Normal file
26
src/models.rs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sqlx::FromRow;
|
||||||
|
|
||||||
|
#[derive(Debug, FromRow, Serialize, Deserialize)]
|
||||||
|
pub struct Weight {
|
||||||
|
pub id: i64,
|
||||||
|
pub user_id: String,
|
||||||
|
pub date: String,
|
||||||
|
pub weight: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_all_weights(pool: &sqlx::SqlitePool) -> Result<Vec<Weight>, sqlx::Error> {
|
||||||
|
sqlx::query_as::<_, Weight>("SELECT id, user_id, date, weight FROM weights ORDER BY date DESC")
|
||||||
|
.fetch_all(pool)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn insert_weight(pool: &sqlx::SqlitePool, user_id: &str, date: &str, weight: f64) -> Result<i64, sqlx::Error> {
|
||||||
|
let result = sqlx::query("INSERT INTO weights (user_id, date, weight) VALUES (?, ?, ?)")
|
||||||
|
.bind(user_id)
|
||||||
|
.bind(date)
|
||||||
|
.bind(weight)
|
||||||
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
Ok(result.last_insert_rowid())
|
||||||
|
}
|
||||||
6
static/css/style.css
Normal file
6
static/css/style.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
17
templates/index.html
Normal file
17
templates/index.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Weight Tracker</title>
|
||||||
|
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||||||
|
<link rel="stylesheet" href="/static/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Weight Tracker</h1>
|
||||||
|
<div id="weights">
|
||||||
|
{% for weight in weights %}
|
||||||
|
<p>{{ weight.date }}: {{ weight.weight }} kg by {{ weight.user_id }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<a href="/input">Add Weight</a>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
20
templates/input.html
Normal file
20
templates/input.html
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Add Weight</title>
|
||||||
|
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||||||
|
<link rel="stylesheet" href="/static/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Add Weight</h1>
|
||||||
|
<form hx-post="/input" hx-target="#result" hx-swap="innerHTML">
|
||||||
|
<label for="date">Date:</label>
|
||||||
|
<input type="date" id="date" name="date" required><br>
|
||||||
|
<label for="weight">Weight (kg):</label>
|
||||||
|
<input type="number" step="0.1" id="weight" name="weight" required><br>
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
<div id="result"></div>
|
||||||
|
<a href="/">Back to Tracker</a>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user