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:

======================================================
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:
    cp .env.example .env
    
  2. Build Tailwind CSS styling:
    npm install
    npx tailwindcss -i src/input.css -o static/tailwind.css
    
  3. Run the development server:
    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:
    docker build -t stick .
    
  2. Start the container (assumes MongoDB is running on the host machine):
    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:
    pub fn router<S>() -> Router<S>
    where
        Config: axum::extract::FromRef<S>,
        MongoUserRepository: axum::extract::FromRef<S>,
        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:
    let app = Router::new()
        .merge(main_view::router())
        .merge(auth::router())
        .merge(projects::router()) // Merged domain router
        .with_state(state);
    
S
Description
No description provided
Readme 37 MiB
Languages
HTML 67%
Rust 21.4%
JavaScript 10%
CSS 1.1%
Dockerfile 0.5%