Compare commits
13 Commits
da2129344d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
594e15a768 | ||
|
|
13918f65ad | ||
|
|
97c8b6a38d | ||
|
|
97854359bf | ||
|
|
bf394f7dbc | ||
|
|
9096d6a3e9 | ||
| ffc5b946c2 | |||
|
|
2c807d62bc | ||
|
|
2efaa86440 | ||
|
|
ea5a23ac23 | ||
|
|
8415a4e503 | ||
|
|
0e0da24caf | ||
|
|
e0f91c6c80 |
20
.github/workflows/dependency-check.yml
vendored
20
.github/workflows/dependency-check.yml
vendored
@@ -1,9 +1,6 @@
|
|||||||
name: Dependency Check
|
name: Dependency Check
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
|
||||||
# Run every Monday at 9:00 AM UTC
|
|
||||||
- cron: "0 9 * * 1"
|
|
||||||
workflow_dispatch: # Allow manual triggering
|
workflow_dispatch: # Allow manual triggering
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@@ -73,10 +70,12 @@ jobs:
|
|||||||
working-directory: dep-check-test
|
working-directory: dep-check-test
|
||||||
|
|
||||||
- name: Check with upgraded dependencies
|
- name: Check with upgraded dependencies
|
||||||
|
if: steps.upgrade.outputs.has_updates == 'true'
|
||||||
run: cargo check
|
run: cargo check
|
||||||
working-directory: dep-check-test
|
working-directory: dep-check-test
|
||||||
|
|
||||||
- name: Build with upgraded dependencies
|
- name: Build with upgraded dependencies
|
||||||
|
if: steps.upgrade.outputs.has_updates == 'true'
|
||||||
run: cargo build
|
run: cargo build
|
||||||
working-directory: dep-check-test
|
working-directory: dep-check-test
|
||||||
|
|
||||||
@@ -96,11 +95,6 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
echo "Changes to template:"
|
echo "Changes to template:"
|
||||||
git diff Cargo.toml.liquid
|
git diff Cargo.toml.liquid
|
||||||
git checkout -b deps/auto-upgrade-${{ github.run_number }}
|
|
||||||
git add Cargo.toml.liquid
|
|
||||||
git config user.email "bot@gitea.actions"
|
|
||||||
git config user.name "Dependency Bot"
|
|
||||||
git commit -m "chore: upgrade dependencies"
|
|
||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
|
|
||||||
- name: Create issue on failure
|
- name: Create issue on failure
|
||||||
@@ -129,6 +123,7 @@ jobs:
|
|||||||
}"
|
}"
|
||||||
|
|
||||||
- name: Generate test project without aide
|
- name: Generate test project without aide
|
||||||
|
if: steps.upgrade.outputs.has_updates == 'true'
|
||||||
run: |
|
run: |
|
||||||
mkdir -p dep-check-no-aide && cd dep-check-no-aide
|
mkdir -p dep-check-no-aide && cd dep-check-no-aide
|
||||||
cargo generate --path .. --name dep-check-no-aide --vcs none --init \
|
cargo generate --path .. --name dep-check-no-aide --vcs none --init \
|
||||||
@@ -140,18 +135,22 @@ jobs:
|
|||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
|
|
||||||
- name: Check without aide (current dependencies)
|
- name: Check without aide (current dependencies)
|
||||||
|
if: steps.upgrade.outputs.has_updates == 'true'
|
||||||
run: cargo check
|
run: cargo check
|
||||||
working-directory: dep-check-no-aide
|
working-directory: dep-check-no-aide
|
||||||
|
|
||||||
- name: Upgrade dependencies (no aide)
|
- name: Upgrade dependencies (no aide)
|
||||||
|
if: steps.upgrade.outputs.has_updates == 'true'
|
||||||
run: cargo upgrade --incompatible
|
run: cargo upgrade --incompatible
|
||||||
working-directory: dep-check-no-aide
|
working-directory: dep-check-no-aide
|
||||||
|
|
||||||
- name: Check without aide (upgraded dependencies)
|
- name: Check without aide (upgraded dependencies)
|
||||||
|
if: steps.upgrade.outputs.has_updates == 'true'
|
||||||
run: cargo check
|
run: cargo check
|
||||||
working-directory: dep-check-no-aide
|
working-directory: dep-check-no-aide
|
||||||
|
|
||||||
- name: Build without aide (upgraded dependencies)
|
- name: Build without aide (upgraded dependencies)
|
||||||
|
if: steps.upgrade.outputs.has_updates == 'true'
|
||||||
run: cargo build
|
run: cargo build
|
||||||
working-directory: dep-check-no-aide
|
working-directory: dep-check-no-aide
|
||||||
|
|
||||||
@@ -227,7 +226,7 @@ jobs:
|
|||||||
run: >-
|
run: >-
|
||||||
tea login add
|
tea login add
|
||||||
-u "${{ github.server_url }}"
|
-u "${{ github.server_url }}"
|
||||||
-t "${{ secrets.GITHUB_TOKEN }}"
|
-t "${{ secrets.REPO_WRITE_TOKEN }}"
|
||||||
|
|
||||||
- name: Create Pull Request with dependency updates
|
- name: Create Pull Request with dependency updates
|
||||||
if: steps.upgrade.outputs.has_updates == 'true'
|
if: steps.upgrade.outputs.has_updates == 'true'
|
||||||
@@ -242,9 +241,6 @@ jobs:
|
|||||||
git config user.name "Dependency Bot"
|
git config user.name "Dependency Bot"
|
||||||
git config user.email "bot@gitea.actions"
|
git config user.email "bot@gitea.actions"
|
||||||
|
|
||||||
# Set up authenticated remote URL
|
|
||||||
git remote set-url origin "https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@${{ github.server_url }}/${{ github.repository }}"
|
|
||||||
|
|
||||||
# Commit and push changes
|
# Commit and push changes
|
||||||
git checkout -b "deps/auto-upgrade-${{ github.run_number }}"
|
git checkout -b "deps/auto-upgrade-${{ github.run_number }}"
|
||||||
git add Cargo.toml.liquid
|
git add Cargo.toml.liquid
|
||||||
|
|||||||
20
.gitignore.liquid
Normal file
20
.gitignore.liquid
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Rust
|
||||||
|
/target
|
||||||
|
|
||||||
|
# Env files
|
||||||
|
.env*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
*.exe
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
*.tmp
|
||||||
@@ -6,19 +6,20 @@ description = "{{project-description}}"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
{% if include_aide %}
|
{% if include_aide %}
|
||||||
aide = { version = "0.16.0-alpha.1", features = [
|
aide = { version = "0.16.0-alpha.2", features = [
|
||||||
"axum",
|
"axum",
|
||||||
"axum-json",
|
"axum-json",
|
||||||
"axum-query",
|
"axum-query",
|
||||||
|
"swagger"
|
||||||
] }
|
] }
|
||||||
{% endif %}
|
{% endif %}
|
||||||
anyhow = "1.0.101"
|
anyhow = "1.0.102"
|
||||||
axum = { version = "0.8.8", features = ["json", "query"] }
|
axum = { version = "0.8.8", features = ["json", "query"] }
|
||||||
axum-app-wrapper = { git = "https://gitea.fasharp.io/fa-sharp/axum-app-wrapper", rev = "a8d5e4f962" }
|
axum-app-wrapper = { git = "https://gitea.fasharp.io/fa-sharp/axum-app-wrapper", rev = "a8d5e4f962" }
|
||||||
dotenvy = "0.15.7"
|
dotenvy = "0.15.7"
|
||||||
figment = { version = "0.10.19", features = ["env"] }
|
figment = { version = "0.10.19", features = ["env"] }
|
||||||
{% if include_aide %}
|
{% if include_aide %}
|
||||||
schemars = { version = "1.0", features = [
|
schemars = { version = "1.2", features = [
|
||||||
"chrono04",
|
"chrono04",
|
||||||
"preserve_order",
|
"preserve_order",
|
||||||
"url2",
|
"url2",
|
||||||
@@ -26,8 +27,8 @@ schemars = { version = "1.0", features = [
|
|||||||
] }
|
] }
|
||||||
{% endif %}
|
{% endif %}
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = "1.0.145"
|
serde_json = "1.0.149"
|
||||||
tokio = { version = "1.48.0", default-features = false, features = [
|
tokio = { version = "1.49.0", default-features = false, features = [
|
||||||
"macros",
|
"macros",
|
||||||
"net",
|
"net",
|
||||||
"rt",
|
"rt",
|
||||||
|
|||||||
45
Dockerfile
Normal file
45
Dockerfile
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Image versions (can be overridden by args when building)
|
||||||
|
ARG RUST_VERSION=1.90
|
||||||
|
ARG DEBIAN_VERSION=bookworm
|
||||||
|
|
||||||
|
### Build server ###
|
||||||
|
FROM rust:${RUST_VERSION}-slim-${DEBIAN_VERSION} AS build
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy all necessary files to build the server
|
||||||
|
COPY Cargo.lock Cargo.toml ./
|
||||||
|
COPY ./src ./src
|
||||||
|
# COPY ./migrations ./migrations
|
||||||
|
# etc...
|
||||||
|
|
||||||
|
ARG pkg={{project-name}}
|
||||||
|
|
||||||
|
RUN --mount=type=cache,id=rust_target,target=/app/target \
|
||||||
|
--mount=type=cache,id=cargo_registry,target=/usr/local/cargo/registry \
|
||||||
|
--mount=type=cache,id=cargo_git,target=/usr/local/cargo/git \
|
||||||
|
set -eux; \
|
||||||
|
cargo build --package $pkg --release --locked; \
|
||||||
|
objcopy --compress-debug-sections target/release/$pkg ./run-server
|
||||||
|
|
||||||
|
|
||||||
|
### Run server ###
|
||||||
|
FROM debian:${DEBIAN_VERSION}-slim AS run
|
||||||
|
|
||||||
|
# Create non-root user
|
||||||
|
ARG UID=10001
|
||||||
|
RUN adduser \
|
||||||
|
--disabled-password \
|
||||||
|
--gecos "" \
|
||||||
|
--home "/home/appuser" \
|
||||||
|
--shell "/sbin/nologin" \
|
||||||
|
--uid "${UID}" \
|
||||||
|
appuser
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
# Copy server binary
|
||||||
|
COPY --from=build --chown=appuser /app/run-server /usr/local/bin/
|
||||||
|
|
||||||
|
# Run server
|
||||||
|
WORKDIR /app
|
||||||
|
ENV {{env_prefix}}_HOST=0.0.0.0
|
||||||
|
CMD ["run-server"]
|
||||||
24
README.md
24
README.md
@@ -10,6 +10,7 @@ A production-ready template for building web services with Rust and Axum.
|
|||||||
- **Graceful Shutdown** - Handles SIGTERM and SIGINT signals
|
- **Graceful Shutdown** - Handles SIGTERM and SIGINT signals
|
||||||
- ️**Plugin Architecture** - Modular app initialization with `axum-app-wrapper`
|
- ️**Plugin Architecture** - Modular app initialization with `axum-app-wrapper`
|
||||||
- **Optional OpenAPI** - API documentation with `aide` (optional)
|
- **Optional OpenAPI** - API documentation with `aide` (optional)
|
||||||
|
- **Docker / OCI** - Dockerfile with sensible defaults for quick deployment
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ cargo install cargo-generate
|
|||||||
Generate a new project from this template:
|
Generate a new project from this template:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo generate --git <your-template-repo-url>
|
cargo generate --git https://gitea.fasharp.io/fa-sharp/axum-template
|
||||||
```
|
```
|
||||||
|
|
||||||
You'll be prompted for:
|
You'll be prompted for:
|
||||||
@@ -35,16 +36,9 @@ You'll be prompted for:
|
|||||||
- **Default log level**: trace, debug, info, warn, or error
|
- **Default log level**: trace, debug, info, warn, or error
|
||||||
- **Include aide**: Whether to include OpenAPI documentation support
|
- **Include aide**: Whether to include OpenAPI documentation support
|
||||||
|
|
||||||
### Manual Setup
|
|
||||||
|
|
||||||
1. Clone or download this repository
|
|
||||||
2. Update `Cargo.toml` with your project name and details
|
|
||||||
3. Copy `.env.example` to `.env` and configure your environment variables
|
|
||||||
4. Run `cargo build`
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Configuration is loaded from environment variables. The prefix is configurable during template generation.
|
Configuration is loaded from environment variables and validated in the `config.rs` file. The variable prefix is configurable during template generation.
|
||||||
|
|
||||||
Example with `APP` prefix:
|
Example with `APP` prefix:
|
||||||
|
|
||||||
@@ -58,18 +52,20 @@ APP_PORT=8080
|
|||||||
APP_LOG_LEVEL=info
|
APP_LOG_LEVEL=info
|
||||||
```
|
```
|
||||||
|
|
||||||
In development, you can use the `.env` file.
|
In development, you can use the `.env` file to set environment variables.
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
.
|
.
|
||||||
├── src/
|
├── src/
|
||||||
│ ├── main.rs # Entry point, server setup
|
│ ├── routes/ # API routes
|
||||||
│ ├── lib.rs # App initialization
|
|
||||||
│ ├── config.rs # Configuration management
|
│ ├── config.rs # Configuration management
|
||||||
│ └── state.rs # Application state
|
│ ├── lib.rs # Axum server setup
|
||||||
|
│ ├── main.rs # Entry point
|
||||||
|
│ └── state.rs # Axum server state
|
||||||
├── Cargo.toml # Dependencies
|
├── Cargo.toml # Dependencies
|
||||||
|
├── .env # Local environment variables
|
||||||
└── .env.example # Example environment variables
|
└── .env.example # Example environment variables
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -80,7 +76,7 @@ In development, you can use the `.env` file.
|
|||||||
cargo run
|
cargo run
|
||||||
|
|
||||||
# Run with custom log level
|
# Run with custom log level
|
||||||
RUST_LOG=debug cargo run
|
APP_LOG_LEVEL=debug cargo run
|
||||||
|
|
||||||
# Build for production
|
# Build for production
|
||||||
cargo build --release
|
cargo build --release
|
||||||
|
|||||||
@@ -1,255 +0,0 @@
|
|||||||
# Axum Template Usage Guide
|
|
||||||
|
|
||||||
This repository is a **cargo-generate** template for creating new Axum web services with a pre-configured, production-ready setup.
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
|
|
||||||
Install `cargo-generate`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo install cargo-generate
|
|
||||||
```
|
|
||||||
|
|
||||||
### Generate a New Project
|
|
||||||
|
|
||||||
#### From a Git Repository (Recommended for Production Use)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo generate --git https://gitea.fasharp.io/fa-sharp/axum-template
|
|
||||||
```
|
|
||||||
|
|
||||||
#### From Local Path (For Testing)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo generate --path /path/to/axum-template
|
|
||||||
```
|
|
||||||
|
|
||||||
### Interactive Prompts
|
|
||||||
|
|
||||||
When you run `cargo generate`, you'll be prompted for:
|
|
||||||
|
|
||||||
1. **Project name**: The name of your new project (e.g., `my-api-server`)
|
|
||||||
2. **Project description**: Brief description (e.g., "REST API for user management")
|
|
||||||
3. **Environment variable prefix**: Prefix for configuration env vars (e.g., `APP` → `APP_HOST`, `APP_PORT`)
|
|
||||||
4. **Default port**: Server's default port (e.g., `8080`)
|
|
||||||
5. **Default log level**: Choose from `trace`, `debug`, `info`, `warn`, or `error`
|
|
||||||
6. **Include aide**: Whether to include OpenAPI documentation support (`true` or `false`)
|
|
||||||
|
|
||||||
### Non-Interactive Mode
|
|
||||||
|
|
||||||
You can skip prompts by providing values via CLI:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo generate --git <repo-url> \
|
|
||||||
--name my-service \
|
|
||||||
--define project-description="My awesome service" \
|
|
||||||
--define env_prefix="API" \
|
|
||||||
--define default_port="3000" \
|
|
||||||
--define default_log_level="info" \
|
|
||||||
--define include_aide=true
|
|
||||||
```
|
|
||||||
|
|
||||||
## Generated Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
my-project/
|
|
||||||
├── src/
|
|
||||||
│ ├── main.rs # Application entry point with server setup
|
|
||||||
│ ├── lib.rs # App initialization and plugin registration
|
|
||||||
│ ├── config.rs # Configuration management (figment-based)
|
|
||||||
│ └── state.rs # Application state management
|
|
||||||
├── Cargo.toml # Dependencies (customized based on your choices)
|
|
||||||
├── .env.example # Example environment variables
|
|
||||||
├── .gitignore # Pre-configured for Rust projects
|
|
||||||
└── README.md # Project-specific README
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
The generated project uses environment variables for configuration. The prefix is customizable during generation.
|
|
||||||
|
|
||||||
Example with `APP` prefix:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Required
|
|
||||||
APP_API_KEY=your-secret-api-key
|
|
||||||
|
|
||||||
# Optional (defaults are set during template generation)
|
|
||||||
APP_HOST=127.0.0.1
|
|
||||||
APP_PORT=8080
|
|
||||||
APP_LOG_LEVEL=info
|
|
||||||
```
|
|
||||||
|
|
||||||
In development, copy `.env.example` to `.env`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cp .env.example .env
|
|
||||||
# Edit .env with your values
|
|
||||||
```
|
|
||||||
|
|
||||||
## Features Included
|
|
||||||
|
|
||||||
### Core Features (Always Included)
|
|
||||||
|
|
||||||
- **Axum 0.8** - Modern, ergonomic web framework
|
|
||||||
- **Environment-based Config** - `figment` for flexible configuration
|
|
||||||
- **Structured Logging** - `tracing` with JSON output in production
|
|
||||||
- **Graceful Shutdown** - Handles SIGTERM, SIGINT, and Ctrl-C
|
|
||||||
- **Plugin Architecture** - `axum-app-wrapper` for modular initialization
|
|
||||||
- **Rust 2024 Edition** - Latest language features
|
|
||||||
|
|
||||||
### Optional Features
|
|
||||||
|
|
||||||
- **OpenAPI Documentation** - `aide` and `schemars` (optional during generation)
|
|
||||||
|
|
||||||
## Next Steps After Generation
|
|
||||||
|
|
||||||
1. **Navigate to your project**:
|
|
||||||
```bash
|
|
||||||
cd my-project
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Configure environment**:
|
|
||||||
```bash
|
|
||||||
cp .env.example .env
|
|
||||||
# Edit .env with your actual values
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Run in development**:
|
|
||||||
```bash
|
|
||||||
cargo run
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Add your routes**:
|
|
||||||
- Create a new module for your routes
|
|
||||||
- Implement a plugin function
|
|
||||||
- Register it in `src/lib.rs`
|
|
||||||
|
|
||||||
5. **Build for production**:
|
|
||||||
```bash
|
|
||||||
cargo build --release
|
|
||||||
```
|
|
||||||
|
|
||||||
## Customization Tips
|
|
||||||
|
|
||||||
### Adding Routes
|
|
||||||
|
|
||||||
Create a new module (e.g., `src/routes.rs`):
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use axum::{routing::get, Router};
|
|
||||||
use axum_app_wrapper::AdHocPlugin;
|
|
||||||
use crate::state::AppState;
|
|
||||||
|
|
||||||
pub fn plugin() -> AdHocPlugin<AppState> {
|
|
||||||
AdHocPlugin::new().on_router(|router| async move {
|
|
||||||
let routes = Router::new()
|
|
||||||
.route("/health", get(health_check));
|
|
||||||
|
|
||||||
Ok(router.merge(routes))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn health_check() -> &'static str {
|
|
||||||
"OK"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Register it in `src/lib.rs`:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
pub async fn create_app() -> anyhow::Result<(axum::Router, AppConfig, impl Future + Send)> {
|
|
||||||
let (router, state, on_shutdown) = App::new()
|
|
||||||
.register(config::plugin())
|
|
||||||
.register(routes::plugin()) // Add this line
|
|
||||||
.init()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let app_config = state.config.to_owned();
|
|
||||||
Ok((router.with_state(state), app_config, on_shutdown))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Adding Dependencies
|
|
||||||
|
|
||||||
Just edit `Cargo.toml` as normal:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo add sqlx --features postgres,runtime-tokio
|
|
||||||
```
|
|
||||||
|
|
||||||
### Changing the Environment Prefix
|
|
||||||
|
|
||||||
The prefix is baked in during template generation. To change it later, search and replace in:
|
|
||||||
- `src/config.rs` (the `extract_config` function)
|
|
||||||
- `.env.example`
|
|
||||||
|
|
||||||
## Publishing Your Template
|
|
||||||
|
|
||||||
### GitHub
|
|
||||||
|
|
||||||
1. Push this directory to GitHub
|
|
||||||
2. Enable "Template repository" in Settings
|
|
||||||
3. Others can use: `cargo generate --git https://github.com/you/axum-template`
|
|
||||||
|
|
||||||
### GitLab/Gitea
|
|
||||||
|
|
||||||
1. Push to your instance
|
|
||||||
2. Share the URL: `cargo generate --git https://gitea.example.com/you/axum-template`
|
|
||||||
|
|
||||||
### Private Repositories
|
|
||||||
|
|
||||||
cargo-generate supports authentication:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo generate --git https://github.com/you/private-template --branch main
|
|
||||||
# You'll be prompted for credentials if needed
|
|
||||||
```
|
|
||||||
|
|
||||||
## Template Maintenance
|
|
||||||
|
|
||||||
### Updating Dependencies
|
|
||||||
|
|
||||||
Update the versions in `Cargo.toml.liquid` and commit:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Edit Cargo.toml.liquid
|
|
||||||
git add Cargo.toml.liquid
|
|
||||||
git commit -m "Update dependencies"
|
|
||||||
git push
|
|
||||||
```
|
|
||||||
|
|
||||||
### Testing the Template
|
|
||||||
|
|
||||||
Generate a test project locally:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo generate --path . --name test-project --define include_aide=true
|
|
||||||
cd test-project
|
|
||||||
cargo check
|
|
||||||
cargo test
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### "Failed to parse Cargo.toml"
|
|
||||||
|
|
||||||
- Make sure template syntax is on separate lines in `.liquid` files
|
|
||||||
- Cargo-generate needs `.liquid` extension for files with Jinja2 syntax
|
|
||||||
|
|
||||||
### Missing Imports in Generated Code
|
|
||||||
|
|
||||||
- Check that Rust edition is set to 2024 in `Cargo.toml.liquid`
|
|
||||||
- Edition 2024 includes `Future` in the prelude
|
|
||||||
|
|
||||||
### Conditional Features Not Working
|
|
||||||
|
|
||||||
- Use `{% if variable %}...{% endif %}` syntax
|
|
||||||
- Keep conditionals on their own lines in TOML files
|
|
||||||
- Test with both `true` and `false` values
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Configure the license for your template as appropriate. The generated projects will inherit this license unless modified.
|
|
||||||
@@ -3,10 +3,15 @@ use axum_app_wrapper::App;
|
|||||||
use crate::config::AppConfig;
|
use crate::config::AppConfig;
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
|
mod routes;
|
||||||
mod state;
|
mod state;
|
||||||
|
|
||||||
pub async fn create_app() -> anyhow::Result<(axum::Router, AppConfig, impl Future + Send)> {
|
pub async fn create_app() -> anyhow::Result<(axum::Router, AppConfig, impl Future + Send)> {
|
||||||
let (router, state, on_shutdown) = App::new().register(config::plugin()).init().await?;
|
let (router, state, on_shutdown) = App::new()
|
||||||
|
.register(config::plugin()) // Extract configuration and add to state
|
||||||
|
.register(routes::plugin()) // Add API routes
|
||||||
|
.init()
|
||||||
|
.await?;
|
||||||
let app_config = state.config.to_owned();
|
let app_config = state.config.to_owned();
|
||||||
|
|
||||||
Ok((router.with_state(state), app_config, on_shutdown))
|
Ok((router.with_state(state), app_config, on_shutdown))
|
||||||
|
|||||||
54
src/routes/hello.rs
Normal file
54
src/routes/hello.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
{% if include_aide %}
|
||||||
|
use axum::extract::{Json, Query};
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::Deserialize;
|
||||||
|
{% endif %}
|
||||||
|
use crate::state::AppState;
|
||||||
|
|
||||||
|
{% if include_aide == false %}
|
||||||
|
pub fn routes() -> axum::Router<AppState> {
|
||||||
|
axum::Router::new()
|
||||||
|
.route("/", axum::routing::get(hello_handler))
|
||||||
|
.route("/", axum::routing::post(post_handler))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn hello_handler() -> String {
|
||||||
|
"Hello, World!".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn post_handler() -> String {
|
||||||
|
"Post handler!".to_string()
|
||||||
|
}
|
||||||
|
{% else %}
|
||||||
|
pub fn routes() -> aide::axum::ApiRouter<AppState> {
|
||||||
|
aide::axum::ApiRouter::new()
|
||||||
|
.api_route(
|
||||||
|
"/",
|
||||||
|
aide::axum::routing::get_with(hello_handler, |op| op.summary("Greet user")),
|
||||||
|
)
|
||||||
|
.api_route(
|
||||||
|
"/",
|
||||||
|
aide::axum::routing::post_with(post_handler, |op| op.summary("Relay message")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn hello_handler(Query(query): Query<HelloQuery>) -> String {
|
||||||
|
format!("Hello, {}!", query.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn post_handler(Json(body): Json<PostBody>) -> String {
|
||||||
|
format!("Received message: {}", body.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, JsonSchema)]
|
||||||
|
struct HelloQuery {
|
||||||
|
/// The name of the person to greet
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, JsonSchema)]
|
||||||
|
struct PostBody {
|
||||||
|
/// The message to relay
|
||||||
|
message: String,
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
47
src/routes/mod.rs
Normal file
47
src/routes/mod.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
use axum_app_wrapper::AdHocPlugin;
|
||||||
|
|
||||||
|
use crate::state::AppState;
|
||||||
|
|
||||||
|
pub mod hello;
|
||||||
|
|
||||||
|
/// Adds all API routes to the server under `/api`
|
||||||
|
pub fn plugin() -> AdHocPlugin<AppState> {
|
||||||
|
AdHocPlugin::new().on_setup(|router, _state| {
|
||||||
|
{% if include_aide == false %}
|
||||||
|
let api_routes = axum::Router::new().nest("/hello", hello::routes());
|
||||||
|
|
||||||
|
Ok(router.nest("/api", api_routes))
|
||||||
|
{% else %}
|
||||||
|
// Build API routes
|
||||||
|
let api_router = aide::axum::ApiRouter::new().nest("/hello", hello::routes());
|
||||||
|
|
||||||
|
// OpenAPI configuration
|
||||||
|
let mut openapi = aide::openapi::OpenApi {
|
||||||
|
info: aide::openapi::Info {
|
||||||
|
title: "{{project-name}}".to_string(),
|
||||||
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
|
description: Some("{{project-description}}".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
servers: vec![aide::openapi::Server {
|
||||||
|
url: "/api".to_string(),
|
||||||
|
..Default::default()
|
||||||
|
}],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add API routes to the router under `/api` and also merge them into the OpenAPI docs
|
||||||
|
let router = router.nest("/api", api_router.finish_api(&mut openapi));
|
||||||
|
|
||||||
|
// Add OpenAPI documentation routes
|
||||||
|
let openapi_json = serde_json::to_string_pretty(&openapi).unwrap();
|
||||||
|
let openapi_route = axum::routing::get(|| async move { openapi_json });
|
||||||
|
let swagger_route = aide::swagger::Swagger::new("/api/openapi.json").axum_route();
|
||||||
|
let router = router
|
||||||
|
.route("/api/openapi.json", openapi_route)
|
||||||
|
.route("/api/docs", swagger_route.into());
|
||||||
|
|
||||||
|
Ok(router)
|
||||||
|
{% endif %}
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user