# Stick Stick is a Rust web application starter template built on Axum, Askama, and MongoDB. Unlike traditional MVC architectures that organize code by technical layers (controllers, models, views), Stick is organized by **vertical slices** (features or use-cases). All files related to a specific domain feature—such as authentication or task management—live together in a single module. This setup is ideal for medium-to-large projects where horizontal layers become hard to navigate, and compiling templates at runtime is too risky. --- ## Technical Architecture ### 1. Vertical Slice Layout Each feature slice is self-contained. For example, the authentication domain has the following structure: * `src/auth/models.rs`: BSON data structures and claims. * `src/auth/repository.rs`: Database operations and query logic. * `src/auth/handlers.rs`: Request/response lifecycle logic. * `src/auth/extractors.rs`: Axum extractors for session state. * `templates/auth/`: HTML markup templates rendered at compile-time. ### 2. Key Stack Decisions * **Axum (v0.8)**: Handles routing, middleware, and request extraction. * **Askama (v0.16)**: Evaluates and compiles HTML templates into Rust code at compile time. If you reference a variable or field that doesn't exist, the project fails to compile, catching UI rendering bugs before deployment. * **MongoDB**: Standard Rust driver configured with BSON serialization. * **Tailwind CSS**: Pre-compiled utility styling using a Node-based wrapper process. * **Authentication**: Managed via JWT (JSON Web Tokens) stored in secure, encrypted `HttpOnly` cookies. --- ## Core Features Included ### Self-Provisioning Administrator On startup, the application checks if the `users` collection in the MongoDB database is empty. If no users are found, it generates a secure, random 16-character alphanumeric password, hashes it with bcrypt, and creates a default `admin` account. The credentials are logged directly to standard output: ```text ====================================================== CREATED INITIAL ADMINISTRATOR ACCOUNT: Username: admin Password: [GeneratedPassword] ====================================================== ``` ### Decoupled Identity vs. Domain Entities Stick strictly separates infrastructure/user models from business domain models: * **Users** (`User`): Manage authentication, roles (`is_admin`), and settings under `/auth`. * **Developers** (`Developer`): Plain domain entities managed under `/developers` that represent team members. ### User Management Panel (Administrators Only) Accessible under `/auth/users` by logged-in administrators. The panel allows: * Viewing all registered users and their administrative permissions. * Editing user profiles, resetting passwords, and toggling administrator roles. * Deleting users (with safeguards preventing administrators from deleting their own active accounts or revoking their own admin permissions). * Registering new users. ### Self-Service Account Settings Any authenticated user can change their own password by clicking their username in the navigation bar, which directs them to `/auth/password`. --- ## Setup and Running ### Prerequisites * **Rust**: Toolchain v1.75+ (for native async traits). * **Node.js & npm**: Required to build Tailwind CSS. * **MongoDB**: Running locally on `mongodb://localhost:27017`. ### Local Setup 1. Copy the environment configuration: ```bash cp .env.example .env ``` 2. Build Tailwind CSS styling: ```bash npm install npx tailwindcss -i src/input.css -o static/tailwind.css ``` 3. Run the development server: ```bash cargo run ``` The server will start listening at `http://127.0.0.1:3000`. ### Running with Docker A multi-stage `Dockerfile` is provided to compile Tailwind, compile the Rust binary, and bundle a lightweight Debian run container. 1. Build the image: ```bash docker build -t stick . ``` 2. Start the container (assumes MongoDB is running on the host machine): ```bash docker run --name stick-app --rm --network="host" \ -e DATABASE_URL="mongodb://127.0.0.1:27017" \ -e DATABASE_NAME="stick_db" \ -e JWT_SECRET="super_secret_template_signing_key_that_is_at_least_32_characters_long" \ -e HOST="127.0.0.1" \ -e PORT="3009" \ stick ``` --- ## Developer Guide: Adding a Feature Slice To add a new feature (e.g. `projects`): 1. Create a module folder: `src/projects/`. 2. Define models in `models.rs` and database access functions in `repository.rs`. 3. Add request handlers in `handlers.rs`. 4. Create a router configuration in `src/projects/mod.rs` exposing a routing module setup: ```rust pub fn router() -> Router where Config: axum::extract::FromRef, MongoUserRepository: axum::extract::FromRef, S: Clone + Send + Sync + 'static, { Router::new() .route("/projects", get(handlers::get_projects)) } ``` 5. Place HTML layouts under `templates/projects/` extending the `base.html` layout. 6. Register and merge the router in `src/main.rs`: ```rust let app = Router::new() .merge(main_view::router()) .merge(auth::router()) .merge(projects::router()) // Merged domain router .with_state(state); ```