{"id":48147525,"url":"https://github.com/kavinda-100/sprintly","last_synced_at":"2026-04-04T17:01:43.505Z","repository":{"id":343873849,"uuid":"1179006325","full_name":"kavinda-100/Sprintly","owner":"kavinda-100","description":"A production-ready Task Management REST API built with Rust, Axum, and SQLx, designed for team collaboration and task tracking. This project demonstrates clean architecture, async Rust patterns, and scalable backend design.","archived":false,"fork":false,"pushed_at":"2026-03-20T15:16:36.000Z","size":193,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-20T21:55:01.155Z","etag":null,"topics":["axum","postgresql","rest-api","rust","sqlx"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kavinda-100.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-11T15:36:30.000Z","updated_at":"2026-03-20T15:16:41.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kavinda-100/Sprintly","commit_stats":null,"previous_names":["kavinda-100/sprintly"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/kavinda-100/Sprintly","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kavinda-100%2FSprintly","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kavinda-100%2FSprintly/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kavinda-100%2FSprintly/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kavinda-100%2FSprintly/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kavinda-100","download_url":"https://codeload.github.com/kavinda-100/Sprintly/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kavinda-100%2FSprintly/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31407391,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["axum","postgresql","rest-api","rust","sqlx"],"created_at":"2026-04-04T17:01:41.530Z","updated_at":"2026-04-04T17:01:43.442Z","avatar_url":"https://github.com/kavinda-100.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🏃 Sprintly API\n\nA production-ready Task Management REST API built with **Rust**, **Axum**, and **SQLx**, designed for team collaboration and task tracking. This project demonstrates clean architecture, async Rust patterns, and scalable backend design.\n\n[![Rust](https://img.shields.io/badge/rust-1.75%2B-orange.svg)](https://www.rust-lang.org/)  \n[![Axum](https://img.shields.io/badge/axum-0.7-blue.svg)](https://github.com/tokio-rs/axum)  \n[![SQLx](https://img.shields.io/badge/sqlx-0.7-green.svg)](https://github.com/launchbadge/sqlx)  \n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\n---\n\n## ✨ Features\n\n- 🏗️ **Clean Architecture** – Organized into Models, Controllers, Services, Routes, and Middleware\n- 🔄 **API Versioning** – `/api/v1` ready\n- 🔐 **JWT Cookie Authentication (Implemented)** – Register, login, logout, and protected `me` endpoint\n- 🔒 **Google OAuth (Planned/Partial)** – User model and login checks are ready for OAuth users\n- 🗃️ **Database Migrations** – SQLx-powered migrations for PostgreSQL\n- ⚡ **Async/Await** – Powered by Tokio runtime for high-performance APIs\n- 📝 **Full CRUD** – Tasks, Projects, Users, Workspaces\n- 🩺 **Health Check** – `/health` endpoint for monitoring\n- 📦 **Consistent Responses** – Standardized JSON format across all endpoints\n\n---\n\n## 🛠️ Technologies Used\n\n| Technology             | Purpose                                         |\n| ---------------------- | ----------------------------------------------- |\n| **Rust**               | Programming language                            |\n| **Axum**               | Web framework                                   |\n| **SQLx**               | Async SQL toolkit with compile-time query check |\n| **Tokio**              | Async runtime                                   |\n| **PostgreSQL**         | Relational database                             |\n| **Serde**              | Serialization/deserialization                   |\n| **Chrono**             | Date and time handling                          |\n| **dotenvy**            | Environment variable management                 |\n| **JWT**                | Token-based authentication                      |\n| **OAuth2**             | Google authentication                           |\n| **Tower / Tower-HTTP** | Middleware and utilities                        |\n\n---\n\n## 📁 Project Structure (Current)\n\n```bash\nsrc/\n├── controllers/\n│   ├── auth_controller.rs      # register/login/logout/me handlers\n│   └── root_controller.rs      # health check\n├── dtos/\n│   └── auth_dto.rs             # request validation + auth response DTO\n├── middleware/\n│   └── auth.rs                 # JWT cookie auth extractor for protected routes\n├── routes/\n│   ├── auth_routes.rs          # /api/v1/auth/* route definitions\n│   └── mod.rs                  # route composition + static file fallback\n├── utils/\n│   ├── response.rs             # ApiResponse\u003cT\u003e success wrapper\n│   ├── api_error.rs            # ApiError enum -\u003e HTTP error responses\n│   ├── jwt.rs                  # token generation and verification\n│   ├── hash.rs                 # Argon2 password hash + verify\n│   └── mod.rs                  # utility exports + validation error formatter\n├── db/\n│   └── mod.rs                  # DB pool + automatic SQLx migrations at startup\n└── main.rs                     # app bootstrap, middleware layers, state wiring\n```\n\n## 🔐 Auth Module (Implemented So Far)\n\nBase path: `/api/v1/auth`\n\n- `POST /register`\n    - Validates payload (`name`, `email`, `password`, `confirm_password`)\n    - Checks duplicate email\n    - Hashes password with Argon2\n    - Creates user in PostgreSQL\n    - Generates JWT and stores it in an HTTP-only cookie named `token`\n    - Returns normalized `ApiResponse\u003cAuthUserResponse\u003e`\n\n- `POST /login`\n    - Validates payload\n    - Finds user by email\n    - Blocks password login for Google-only users (`google_id` present)\n    - Verifies password against Argon2 hash\n    - Generates JWT and updates `token` cookie\n    - Returns normalized `ApiResponse\u003cAuthUserResponse\u003e`\n\n- `POST /logout`\n    - Clears auth cookie (`token`)\n    - Returns normalized `ApiResponse\u003c()\u003e`\n\n- `GET /me`\n    - Protected route using `AuthUser` extractor (`src/middleware/auth.rs`)\n    - Reads and verifies JWT from cookie\n    - Loads user from DB\n    - Returns current authenticated user profile in `ApiResponse\u003cAuthUserResponse\u003e`\n\n## 🧰 `src/utils` Folder and Use Cases\n\nThis project is educational, so the utility layer is intentionally explicit and reusable for future modules (tasks, projects, workspaces).\n\n| File                     | What it does                                                                                                                         | Why it matters for future features                            |\n| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------- |\n| `src/utils/response.rs`  | Defines `ApiResponse\u003cT\u003e` with `success`, `status_code`, `message`, `data`                                                            | Gives every endpoint a predictable success payload shape      |\n| `src/utils/api_error.rs` | Defines `ApiError` (`BadRequest`, `Unauthorized`, `NotFound`, `Conflict`, `InternalServerError`) and converts it into HTTP responses | Centralizes error-to-status mapping so controllers stay clean |\n| `src/utils/jwt.rs`       | Generates and verifies JWT claims (`sub`, `exp`)                                                                                     | Reusable token logic for any protected resource               |\n| `src/utils/hash.rs`      | Hashes passwords (Argon2 + random salt) and verifies passwords                                                                       | Security best practice reusable for account/password features |\n| `src/utils/mod.rs`       | Exports utility modules and formats validator errors into readable strings                                                           | Keeps validation messages user-friendly and consistent        |\n\n## 🔄 How Response + Error + JWT Work Together\n\n1. **Request enters route** (`src/routes/auth_routes.rs`) and is handled by `auth_controller`.\n2. **DTO validation runs** (`validator` crate in `src/dtos/auth_dto.rs`).\n3. **If validation fails**, controller maps errors using `format_validation_errors(...)` and returns `ApiError::BadRequest(...)`.\n4. **If business/database logic fails**, controller returns another `ApiError` variant.\n5. **`ApiError` implements `IntoResponse`**, so Axum converts it into a standardized HTTP error payload.\n6. **If success**, controller returns `Json(ApiResponse\u003cT\u003e)` from `src/utils/response.rs`.\n7. **For protected routes**, `AuthUser` middleware extractor in `src/middleware/auth.rs` reads `token` from cookies, verifies JWT using `utils/jwt.rs`, fetches the user from DB, and injects authenticated `User` into the handler.\n\n## 📦 Response Format (Current)\n\nSuccess shape:\n\n```json\n{\n\t\"success\": true,\n\t\"status_code\": 200,\n\t\"message\": \"User logged in successfully\",\n\t\"data\": {\n\t\t\"id\": \"uuid\",\n\t\t\"email\": \"user@example.com\",\n\t\t\"name\": \"User\"\n\t}\n}\n```\n\nError shape:\n\n```json\n{\n\t\"success\": false,\n\t\"status_code\": 400,\n\t\"message\": \"email: Invalid email format, password: Password must be between 6 and 12 characters\",\n\t\"data\": null\n}\n```\n\n## 🍪 JWT Cookie Behavior\n\n- Cookie name: `token`\n- `HttpOnly`: enabled (protects token from client-side JS access)\n- `SameSite`: `Lax`\n- `Secure`: enabled when `DEV_MODE != development`\n- Signing secret: `JWT_SECRET` from environment\n\nThis makes auth state server-trusted and easy to consume from browser clients.\n\n## database migrations\n\n1. install sqlx-cli\n\n```bash\ncargo install sqlx-cli --no-default-features --features postgres\n```\n\n2. create a migration\n\n```bash\nsqlx migrate add init_schema\n```\n\n3. run migrations\n\n```bash\nsqlx migrate run\n# this step run is the manual way\n# `src/db/mod.rs` has the code to run migrations automatically when the app starts\n```\n\n## Relationship Diagram\n\n```ascii\n+------------+        +-------------+        +----------+\n|   users    |        | workspaces  |        | projects |\n+------------+        +-------------+        +----------+\n| id         |\u003c-------| owner_id    |        | id       |\n| email      |        | name        |\u003c-------| workspace_id\n| name       |        | created_at  |        | name\n| google_id  |        +-------------+        | description\n+------------+                               +----------+\n       |                                          |\n       |                                          |\n       |                                          |\n       v                                          v\n+-------------+    +----------------+    +----------------+\n|   tasks     |\u003c---| task_assignees |---\u003e|     users      |\n+-------------+    +----------------+    +----------------+\n| id          |    | task_id        |    | id             |\n| project_id  |    | user_id        |    +----------------+\n| title       |    +----------------+\n| description |\n| status_id   |----\u003e task_status\n| priority_id |----\u003e task_priority\n| owner_id    |\n+-------------+\n       |\n       v\n+-------------+\n|  comments   |\n+-------------+\n| id          |\n| task_id     |\n| user_id     |\n| content     |\n+-------------+\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkavinda-100%2Fsprintly","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkavinda-100%2Fsprintly","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkavinda-100%2Fsprintly/lists"}