Sync your own workout data from your "Strong" app
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

added readme for lib and update .env example in the project readme #7

+91 -2
+3 -2
README.MD
··· 29 29 STRONG_USER=your_strong_user 30 30 STRONG_PASS=your_strong_pass 31 31 32 - CLICKHOUSE_HOST=clickhouse 32 + CLICKHOUSE_URL=http://clickhouse-server:8123 33 33 CLICKHOUSE_USER=default 34 34 CLICKHOUSE_PASS= 35 - CLICKHOUSE_TABLE=strong 35 + CLICKHOUSE_DATABASE=workouts 36 + CLICKHOUSE_TABLE=workout_sets 36 37 ``` 37 38 3. Run with `cargo run` 38 39
+88
strong-api-lib/README.md
··· 1 + # strong-api-lib 2 + 3 + Rust client library for the [Strong App](https://www.strong.app/) backend API. Fetches workout logs and exercise definitions, then transforms them into a clean `Workout → Exercise → Set` model. 4 + 5 + ## Getting Started 6 + 7 + ```toml 8 + [dependencies] 9 + strong-api-lib = { path = "../strong-api-lib" } 10 + tokio = { version = "1", features = ["full"] } 11 + ``` 12 + 13 + ## Usage 14 + 15 + ```rust 16 + use reqwest::Url; 17 + use strong_api_lib::strong_api::{StrongApi, Includes}; 18 + use strong_api_lib::data_transformer::DataTransformer; 19 + 20 + let url = Url::parse("https://your-strong-backend.example.com").unwrap(); 21 + let mut api = StrongApi::new(url); 22 + 23 + // Authenticate 24 + api.login("user@example.com", "password").await?; 25 + 26 + // Refresh token (uses internally stored tokens) 27 + api.refresh().await?; 28 + 29 + // With the "full" feature, you can pass tokens explicitly: 30 + // api.refresh_by_tokens(access_token, refresh_token).await?; 31 + 32 + // Fetch exercise definitions (paginated) 33 + let page1 = api.get_measurements(1).await?; 34 + let page2 = api.get_measurements(2).await?; 35 + let measurements = page1.merge(page2); 36 + 37 + // Fetch user data with workout logs 38 + let user = api.get_user("", 500, vec![Includes::Log]).await?; 39 + 40 + // Transform into domain model 41 + let transformer = DataTransformer::new() 42 + .with_measurements_response(measurements); 43 + 44 + let workouts = transformer 45 + .get_measurements_from_logs(&user.embedded.log)?; 46 + 47 + for workout in &workouts { 48 + for exercise in &workout.exercises { 49 + for set in &exercise.sets { 50 + println!("{} — {} × {:.1} kg", 51 + exercise.name, set.reps, set.weight.unwrap_or(0.0)); 52 + } 53 + } 54 + } 55 + ``` 56 + 57 + ## `Includes` Variants 58 + 59 + Used with `get_user()` to select which embedded resources to return: 60 + 61 + `Log`, `Measurement`, `Tag`, `Widget`, `Template`, `Folder`, `MeasuredValue` 62 + 63 + ## Domain Model 64 + 65 + ``` 66 + Workout 67 + ├── id, name, timezone, start_date, end_date 68 + └── exercises: Vec<Exercise> 69 + ├── id, name 70 + └── sets: Vec<Set> 71 + └── id, weight, reps, rpe 72 + ``` 73 + 74 + ## Features 75 + 76 + | Feature | Description | 77 + |---|---| 78 + | `full` | Enables `refresh_by_tokens()` — refresh auth with externally persisted tokens | 79 + 80 + ## API Endpoints 81 + 82 + | Method | Path | Function | 83 + |---|---|---| 84 + | `POST` | `auth/login` | `login()` | 85 + | `POST` | `auth/login/refresh` | `refresh()` / `refresh_by_tokens()` | 86 + | `GET` | `api/users/{user_id}` | `get_user()` | 87 + | `GET` | `api/measurements?page={n}` | `get_measurements()` | 88 + | `GET` | `api/logs/{user_id}` | `get_logs_raw()` |