From 23bd1f2c82a309b070b8fafa05deb5daa82796b7 Mon Sep 17 00:00:00 2001 From: demola malomo Date: Wed, 15 Oct 2025 02:39:49 +0100 Subject: [PATCH 1/4] feat: add Rust Todo List example with IaD Postgres integration --- iad/todos/.gitignore | 4 ++ iad/todos/Cargo.toml | 13 ++++++ iad/todos/README.md | 13 ++++++ iad/todos/migrations/0001_init.sql | 4 ++ iad/todos/src/main.rs | 69 ++++++++++++++++++++++++++++++ templates.toml | 7 +++ 6 files changed, 110 insertions(+) create mode 100644 iad/todos/.gitignore create mode 100644 iad/todos/Cargo.toml create mode 100644 iad/todos/README.md create mode 100644 iad/todos/migrations/0001_init.sql create mode 100644 iad/todos/src/main.rs diff --git a/iad/todos/.gitignore b/iad/todos/.gitignore new file mode 100644 index 00000000..ffd57120 --- /dev/null +++ b/iad/todos/.gitignore @@ -0,0 +1,4 @@ + +/target +.shuttle* +Secrets*.toml diff --git a/iad/todos/Cargo.toml b/iad/todos/Cargo.toml new file mode 100644 index 00000000..e8611c39 --- /dev/null +++ b/iad/todos/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "todos" +version = "0.1.0" +edition = "2021" + +[dependencies] +axum = "0.8" +serde = { version = "1", features = ["derive"] } +shuttle-axum = "0.57.0" +shuttle-runtime = "0.57.0" +shuttle-shared-db = { version = "0.57.0", features = ["postgres", "sqlx"] } +sqlx = "0.8" +tokio = "1.28.2" diff --git a/iad/todos/README.md b/iad/todos/README.md new file mode 100644 index 00000000..cccd4458 --- /dev/null +++ b/iad/todos/README.md @@ -0,0 +1,13 @@ +# Shuttle shared Postgres DB with Axum + +This template shows how to connect a Postgres database and use it for a simple TODO list app. + +## Example usage + +```bash +curl -X POST -H 'content-type: application/json' localhost:8000/todos --data '{"note":"My todo"}' +# {"id":1,"note":"My todo"} + +curl localhost:8000/todos/1 +# {"id":1,"note":"My todo"} +``` diff --git a/iad/todos/migrations/0001_init.sql b/iad/todos/migrations/0001_init.sql new file mode 100644 index 00000000..0e3918a5 --- /dev/null +++ b/iad/todos/migrations/0001_init.sql @@ -0,0 +1,4 @@ +CREATE TABLE IF NOT EXISTS todos ( + id serial PRIMARY KEY, + note TEXT NOT NULL +); diff --git a/iad/todos/src/main.rs b/iad/todos/src/main.rs new file mode 100644 index 00000000..7ae68e0d --- /dev/null +++ b/iad/todos/src/main.rs @@ -0,0 +1,69 @@ +use axum::{ + extract::{Path, State}, + http::StatusCode, + response::IntoResponse, + routing::{get, post}, + Json, Router, +}; +use serde::{Deserialize, Serialize}; +use sqlx::{FromRow, PgPool}; + +async fn retrieve( + Path(id): Path, + State(state): State, +) -> Result { + match sqlx::query_as::<_, Todo>("SELECT * FROM todos WHERE id = $1") + .bind(id) + .fetch_one(&state.pool) + .await + { + Ok(todo) => Ok((StatusCode::OK, Json(todo))), + Err(e) => Err((StatusCode::BAD_REQUEST, e.to_string())), + } +} + +async fn add( + State(state): State, + Json(data): Json, +) -> Result { + match sqlx::query_as::<_, Todo>("INSERT INTO todos (note) VALUES ($1) RETURNING id, note") + .bind(&data.note) + .fetch_one(&state.pool) + .await + { + Ok(todo) => Ok((StatusCode::CREATED, Json(todo))), + Err(e) => Err((StatusCode::BAD_REQUEST, e.to_string())), + } +} + +#[derive(Clone)] +struct MyState { + pool: PgPool, +} + +#[shuttle_runtime::main] +async fn main(#[shuttle_shared_db::Postgres] pool: PgPool) -> shuttle_axum::ShuttleAxum { + sqlx::migrate!() + .run(&pool) + .await + .expect("Failed to run migrations"); + + let state = MyState { pool }; + let router = Router::new() + .route("/todos", post(add)) + .route("/todos/{id}", get(retrieve)) + .with_state(state); + + Ok(router.into()) +} + +#[derive(Deserialize)] +struct TodoNew { + pub note: String, +} + +#[derive(Serialize, FromRow)] +struct Todo { + pub id: i32, + pub note: String, +} diff --git a/templates.toml b/templates.toml index b8a3c74d..ac74bce7 100644 --- a/templates.toml +++ b/templates.toml @@ -390,6 +390,13 @@ path = "mcp/mcp-sse-oauth" use_cases = ["MCP", "AI", "AI Agents"] tags = ["axum", "mcp", "sse", "oauth"] +[templates.iad-todos] +title = "Rust Todo List with IaD Postgres" +description = "Todo list with a Postgres database provisioned by Infrastructure as Data" +path = "iad/todos" +use_cases = ["Web app", "IaD", "Storage"] +tags = ["axum", "postgres", "database", "iad"] + ## EXAMPLES ## [examples.metadata] From e88936c545fe48979f31ca0376985caed4f1b934 Mon Sep 17 00:00:00 2001 From: demola malomo Date: Wed, 15 Oct 2025 09:23:59 +0100 Subject: [PATCH 2/4] enhanced Todo model --- iad/todos/migrations/0001_init.sql | 6 ++++-- iad/todos/src/main.rs | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/iad/todos/migrations/0001_init.sql b/iad/todos/migrations/0001_init.sql index 0e3918a5..c395e9f6 100644 --- a/iad/todos/migrations/0001_init.sql +++ b/iad/todos/migrations/0001_init.sql @@ -1,4 +1,6 @@ CREATE TABLE IF NOT EXISTS todos ( id serial PRIMARY KEY, - note TEXT NOT NULL -); + note TEXT NOT NULL, + description TEXT NOT NULL DEFAULT '', + completed BOOLEAN NOT NULL DEFAULT FALSE +); \ No newline at end of file diff --git a/iad/todos/src/main.rs b/iad/todos/src/main.rs index 7ae68e0d..782d7a8f 100644 --- a/iad/todos/src/main.rs +++ b/iad/todos/src/main.rs @@ -26,10 +26,14 @@ async fn add( State(state): State, Json(data): Json, ) -> Result { - match sqlx::query_as::<_, Todo>("INSERT INTO todos (note) VALUES ($1) RETURNING id, note") - .bind(&data.note) - .fetch_one(&state.pool) - .await + match sqlx::query_as::<_, Todo>( + "INSERT INTO todos (note) VALUES ($1) RETURNING id, note, description, completed", + ) + .bind(&data.note) + .bind(&data.description) + .bind(data.completed) + .fetch_one(&state.pool) + .await { Ok(todo) => Ok((StatusCode::CREATED, Json(todo))), Err(e) => Err((StatusCode::BAD_REQUEST, e.to_string())), @@ -60,10 +64,14 @@ async fn main(#[shuttle_shared_db::Postgres] pool: PgPool) -> shuttle_axum::Shut #[derive(Deserialize)] struct TodoNew { pub note: String, + pub description: String, + pub completed: bool, } #[derive(Serialize, FromRow)] struct Todo { pub id: i32, pub note: String, + pub description: String, + pub completed: bool, } From 034b4abb3b74de4fd1e4d88cab96acc82d5398ab Mon Sep 17 00:00:00 2001 From: demola malomo Date: Thu, 23 Oct 2025 02:06:24 +0100 Subject: [PATCH 3/4] feat: add Upload Manager API with PostgreSQL integration --- iad/todos/README.md | 39 +++-- iad/upload-manager/.gitignore | 4 + iad/upload-manager/Cargo.toml | 16 +++ iad/upload-manager/README.md | 252 +++++++++++++++++++++++++++++++++ iad/upload-manager/src/main.rs | 239 +++++++++++++++++++++++++++++++ 5 files changed, 542 insertions(+), 8 deletions(-) create mode 100644 iad/upload-manager/.gitignore create mode 100644 iad/upload-manager/Cargo.toml create mode 100644 iad/upload-manager/README.md create mode 100644 iad/upload-manager/src/main.rs diff --git a/iad/todos/README.md b/iad/todos/README.md index cccd4458..39205d54 100644 --- a/iad/todos/README.md +++ b/iad/todos/README.md @@ -1,13 +1,36 @@ -# Shuttle shared Postgres DB with Axum +# Todo API - Infrastructure as Data with Shuttle -This template shows how to connect a Postgres database and use it for a simple TODO list app. +A Rust-based Todo API demonstrating **Shuttle's Infrastructure as Data (IaD)** approach. This project showcases how Shuttle provisions and manages infrastructure resources through simple function annotations, eliminating the need for complex configuration files or manual cloud setup. -## Example usage +## What is Infrastructure as Data? -```bash -curl -X POST -H 'content-type: application/json' localhost:8000/todos --data '{"note":"My todo"}' -# {"id":1,"note":"My todo"} +**Infrastructure as Data (IaD)** is Shuttle's approach to infrastructure provisioning where you declare your infrastructure needs directly in your code using Rust attributes. Instead of writing YAML files, Terraform scripts, or clicking through cloud consoles, you simply annotate your function parameters, and Shuttle handles the rest. -curl localhost:8000/todos/1 -# {"id":1,"note":"My todo"} +### Key Benefits + +- **No Configuration Files**: Infrastructure defined in code, not YAML or JSON +- **Type-Safe**: Leverage Rust's type system for infrastructure +- **Automatic Provisioning**: Resources created on deployment +- **Zero DevOps**: No manual cloud console configuration +- **Instant Local Development**: Same code works locally and in production + +## Infrastructure as Data in Action + +This project demonstrates IaD with a PostgreSQL database: + +```rust +#[shuttle_runtime::main] +async fn main( + #[shuttle_shared_db::Postgres] pool: PgPool +) -> shuttle_axum::ShuttleAxum { + // Your database is ready to use! +} ``` + +**That's it!** With just one annotation (`#[shuttle_shared_db::Postgres]`), Shuttle: +1. ✅ Provisions a PostgreSQL database +2. ✅ Configures connection credentials +3. ✅ Injects a connection pool into your app +4. ✅ Manages the database lifecycle + +No environment variables, no connection strings, no manual setup. diff --git a/iad/upload-manager/.gitignore b/iad/upload-manager/.gitignore new file mode 100644 index 00000000..ffd57120 --- /dev/null +++ b/iad/upload-manager/.gitignore @@ -0,0 +1,4 @@ + +/target +.shuttle* +Secrets*.toml diff --git a/iad/upload-manager/Cargo.toml b/iad/upload-manager/Cargo.toml new file mode 100644 index 00000000..1240c989 --- /dev/null +++ b/iad/upload-manager/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "upload-manager" +version = "0.1.0" +edition = "2021" + +[dependencies] +axum = "0.8" +shuttle-axum = "0.57.0" +shuttle-runtime = "0.57.0" +shuttle-aws-rds = { version = "0.57.0", features = ["postgres"] } +tokio = { version = "1.28.2", features = ["full"] } +sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "postgres", "chrono", "uuid"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +chrono = { version = "0.4", features = ["serde"] } +uuid = { version = "1.0", features = ["serde", "v4"] } diff --git a/iad/upload-manager/README.md b/iad/upload-manager/README.md new file mode 100644 index 00000000..a560655d --- /dev/null +++ b/iad/upload-manager/README.md @@ -0,0 +1,252 @@ +# S3 Upload Manager API + +A Rust-based API for managing S3 upload metadata with AWS RDS PostgreSQL database storage. This API stores details about files uploaded to S3 (assuming S3 upload is handled separately). + +## Features + +- **Create Upload Records**: Store metadata about S3 uploads in PostgreSQL +- **Retrieve Upload Records**: Get all uploads or specific upload by ID +- **AWS RDS Database**: Uses dedicated AWS RDS PostgreSQL database via Shuttle +- **UUID-based**: Each upload record gets a unique UUID identifier +- **Metadata Tracking**: Track filename, S3 key, bucket, URL, file size, content type, and timestamp + +## Prerequisites + +- Rust (latest stable version) +- Shuttle account (for deployment) +- S3 upload mechanism (handled separately) + +## AWS RDS Configuration + +This application uses **AWS RDS PostgreSQL** as its database backend. When you deploy with Shuttle, it will automatically provision a dedicated AWS RDS PostgreSQL instance for your application. + +### How It Works + +- Shuttle's `shuttle-aws-rds` resource automatically provisions an AWS RDS PostgreSQL database +- The database connection is injected into your application at runtime +- Database credentials and connection details are managed securely by Shuttle +- The database is dedicated to your application (not shared with other projects) + +### Database Provisioning + +On first deployment, Shuttle will: +1. Create a new AWS RDS PostgreSQL instance +2. Configure security groups and networking +3. Provide the connection pool to your application +4. Run the table creation migration automatically + +**Note**: AWS RDS provisioning may take a few minutes on first deployment. + +## Database Schema + +The application automatically creates the following table on startup: + +```sql +CREATE TABLE s3_uploads ( + id UUID PRIMARY KEY, + filename VARCHAR(255) NOT NULL, + s3_key VARCHAR(512) NOT NULL, + s3_bucket VARCHAR(255) NOT NULL, + s3_url VARCHAR(512) NOT NULL, + file_size BIGINT NOT NULL, + content_type VARCHAR(100), + uploaded_at TIMESTAMPTZ NOT NULL +); +``` + +## API Endpoints + +### 1. Health Check +``` +GET / +``` +Returns a simple health check message. + +**Response:** +``` +S3 Upload Manager API is running! +``` + +### 2. Create Upload Record +``` +POST /uploads +``` +Store metadata about an S3 upload in the database. + +**Request Body:** +```json +{ + "filename": "document.pdf", + "s3_key": "uploads/2024/document.pdf", + "s3_bucket": "my-bucket", + "s3_url": "https://my-bucket.s3.amazonaws.com/uploads/2024/document.pdf", + "file_size": 1048576, + "content_type": "application/pdf" +} +``` + +**Example using curl:** +```bash +curl -X POST http://localhost:8000/uploads \ + -H "Content-Type: application/json" \ + -d '{ + "filename": "document.pdf", + "s3_key": "uploads/2024/document.pdf", + "s3_bucket": "my-bucket", + "s3_url": "https://my-bucket.s3.amazonaws.com/uploads/2024/document.pdf", + "file_size": 1048576, + "content_type": "application/pdf" + }' +``` + +**Response (201 Created):** +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "filename": "document.pdf", + "s3_url": "https://my-bucket.s3.amazonaws.com/uploads/2024/document.pdf", + "uploaded_at": "2024-01-20T10:30:00Z" +} +``` + +### 3. Get All Upload Records +``` +GET /uploads +``` +Retrieve a list of all upload records, ordered by most recent first. + +**Response:** +```json +{ + "uploads": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "filename": "document.pdf", + "s3_key": "uploads/2024/document.pdf", + "s3_bucket": "my-bucket", + "s3_url": "https://my-bucket.s3.amazonaws.com/uploads/2024/document.pdf", + "file_size": 1048576, + "content_type": "application/pdf", + "uploaded_at": "2024-01-20T10:30:00Z" + } + ], + "count": 1 +} +``` + +### 4. Get Upload Record by ID +``` +GET /uploads/:id +``` +Retrieve metadata for a specific upload by its UUID. + +**Example:** +```bash +curl http://localhost:8000/uploads/550e8400-e29b-41d4-a716-446655440000 +``` + +**Response:** +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "filename": "document.pdf", + "s3_key": "uploads/2024/document.pdf", + "s3_bucket": "my-bucket", + "s3_url": "https://my-bucket.s3.amazonaws.com/uploads/2024/document.pdf", + "file_size": 1048576, + "content_type": "application/pdf", + "uploaded_at": "2024-01-20T10:30:00Z" +} +``` + +## Request Validation + +The create endpoint validates: +- **filename**: Cannot be empty +- **s3_key**: Cannot be empty +- **file_size**: Must be greater than 0 + +## Error Responses + +The API returns appropriate HTTP status codes: + +- `200 OK`: Successful GET request +- `201 Created`: Upload record successfully created +- `400 Bad Request`: Invalid request (validation errors) +- `404 Not Found`: Upload record not found +- `500 Internal Server Error`: Server-side errors + +Error responses follow this format: +```json +{ + "error": "Error message description" +} +``` + +## Local Development + +1. Install dependencies: +```bash +cargo build +``` + +2. Run locally with Shuttle: +```bash +cargo shuttle run +``` + +The API will be available at `http://localhost:8000` + +## Deployment with Shuttle + +### Using Shuttle CLI + +1. Login to Shuttle: +```bash +cargo shuttle login +``` + +2. Deploy the application: +```bash +cargo shuttle deploy +``` + +### Using Shuttle MCP Server + +You can also use the Shuttle MCP server tools to deploy: + +1. List your projects: +```bash +# Use mcp0_project_list tool +``` + +2. Create a new project (if needed): +```bash +# Use mcp0_project_create tool with project name +``` + +3. Deploy: +```bash +# Use mcp0_deploy tool with project_id and working directory +``` + +4. Check deployment status: +```bash +# Use mcp0_deployment_status tool +``` + +## Integration Example + +This API is designed to work with a separate S3 upload mechanism. Here's a typical workflow: + +1. **Client uploads file to S3** (using pre-signed URLs, direct upload, etc.) +2. **After successful S3 upload**, client calls `POST /uploads` with the file metadata +3. **API stores the metadata** in PostgreSQL and returns the record ID +4. **Client can retrieve upload records** using `GET /uploads` or `GET /uploads/:id` + +## Technology Stack + +- **Framework**: Axum (Rust web framework) +- **Database**: PostgreSQL (via AWS RDS) +- **ORM**: SQLx +- **Deployment**: Shuttle diff --git a/iad/upload-manager/src/main.rs b/iad/upload-manager/src/main.rs new file mode 100644 index 00000000..0a1b04d9 --- /dev/null +++ b/iad/upload-manager/src/main.rs @@ -0,0 +1,239 @@ +use axum::{ + extract::{Path, State}, + http::StatusCode, + response::Json, + routing::{get, post}, + Router, +}; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use sqlx::{FromRow, PgPool}; +use std::sync::Arc; +use uuid::Uuid; + +// Application state +#[derive(Clone)] +struct AppState { + db: PgPool, +} + +// Database model for S3 upload records +#[derive(Debug, Clone, Serialize, Deserialize, FromRow)] +struct S3Upload { + id: Uuid, + filename: String, + s3_key: String, + s3_bucket: String, + s3_url: String, + file_size: i64, + content_type: Option, + uploaded_at: DateTime, +} + +// Request model for creating upload record +#[derive(Debug, Deserialize)] +struct CreateUploadRequest { + filename: String, + s3_key: String, + s3_bucket: String, + s3_url: String, + file_size: i64, + content_type: Option, +} + +// Response models +#[derive(Debug, Serialize)] +struct CreateUploadResponse { + id: Uuid, + filename: String, + s3_url: String, + uploaded_at: DateTime, +} + +#[derive(Debug, Serialize)] +struct UploadListResponse { + uploads: Vec, + count: usize, +} + +#[derive(Debug, Serialize)] +struct ErrorResponse { + error: String, +} + +// Health check endpoint +async fn health_check() -> &'static str { + "S3 Upload Manager API is running!" +} + +// Create upload record - stores S3 upload details in database +async fn create_upload( + State(state): State>, + Json(payload): Json, +) -> Result<(StatusCode, Json), (StatusCode, Json)> { + // Validate input + if payload.filename.is_empty() { + return Err(( + StatusCode::BAD_REQUEST, + Json(ErrorResponse { + error: "Filename cannot be empty".to_string(), + }), + )); + } + + if payload.s3_key.is_empty() { + return Err(( + StatusCode::BAD_REQUEST, + Json(ErrorResponse { + error: "S3 key cannot be empty".to_string(), + }), + )); + } + + if payload.file_size <= 0 { + return Err(( + StatusCode::BAD_REQUEST, + Json(ErrorResponse { + error: "File size must be greater than 0".to_string(), + }), + )); + } + + let upload_id = Uuid::new_v4(); + let uploaded_at = Utc::now(); + + // Insert upload record into database + sqlx::query( + r#" + INSERT INTO s3_uploads (id, filename, s3_key, s3_bucket, s3_url, file_size, content_type, uploaded_at) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + "#, + ) + .bind(upload_id) + .bind(&payload.filename) + .bind(&payload.s3_key) + .bind(&payload.s3_bucket) + .bind(&payload.s3_url) + .bind(payload.file_size) + .bind(&payload.content_type) + .bind(uploaded_at) + .execute(&state.db) + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(ErrorResponse { + error: format!("Failed to save upload record: {}", e), + }), + ) + })?; + + Ok(( + StatusCode::CREATED, + Json(CreateUploadResponse { + id: upload_id, + filename: payload.filename, + s3_url: payload.s3_url, + uploaded_at, + }), + )) +} + +// Get all upload records +async fn get_uploads( + State(state): State>, +) -> Result, (StatusCode, Json)> { + let uploads = sqlx::query_as::<_, S3Upload>( + r#" + SELECT id, filename, s3_key, s3_bucket, s3_url, file_size, content_type, uploaded_at + FROM s3_uploads + ORDER BY uploaded_at DESC + "#, + ) + .fetch_all(&state.db) + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(ErrorResponse { + error: format!("Failed to fetch uploads: {}", e), + }), + ) + })?; + + let count = uploads.len(); + + Ok(Json(UploadListResponse { uploads, count })) +} + +// Get a specific upload record by ID +async fn get_upload_by_id( + State(state): State>, + Path(id): Path, +) -> Result, (StatusCode, Json)> { + let upload = sqlx::query_as::<_, S3Upload>( + r#" + SELECT id, filename, s3_key, s3_bucket, s3_url, file_size, content_type, uploaded_at + FROM s3_uploads + WHERE id = $1 + "#, + ) + .bind(id) + .fetch_optional(&state.db) + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(ErrorResponse { + error: format!("Database error: {}", e), + }), + ) + })?; + + match upload { + Some(u) => Ok(Json(u)), + None => Err(( + StatusCode::NOT_FOUND, + Json(ErrorResponse { + error: "Upload record not found".to_string(), + }), + )), + } +} + +#[shuttle_runtime::main] +async fn main( + #[shuttle_aws_rds::Postgres] db: PgPool, +) -> shuttle_axum::ShuttleAxum { + // Run database migrations - create table if it doesn't exist + sqlx::query( + r#" + CREATE TABLE IF NOT EXISTS s3_uploads ( + id UUID PRIMARY KEY, + filename VARCHAR(255) NOT NULL, + s3_key VARCHAR(512) NOT NULL, + s3_bucket VARCHAR(255) NOT NULL, + s3_url VARCHAR(512) NOT NULL, + file_size BIGINT NOT NULL, + content_type VARCHAR(100), + uploaded_at TIMESTAMPTZ NOT NULL + ) + "#, + ) + .execute(&db) + .await + .expect("Failed to create s3_uploads table"); + + // Create application state + let state = Arc::new(AppState { db }); + + // Build router with API endpoints + let router = Router::new() + .route("/", get(health_check)) + .route("/uploads", post(create_upload)) + .route("/uploads", get(get_uploads)) + .route("/uploads/:id", get(get_upload_by_id)) + .with_state(state); + + Ok(router.into()) +} From 74a79dbb6eb973a417e1c04118473a832a298e1b Mon Sep 17 00:00:00 2001 From: demola malomo Date: Thu, 23 Oct 2025 02:15:44 +0100 Subject: [PATCH 4/4] feat: add Upload Manager API template for IaD with AWS RDS Postgres integration --- templates.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/templates.toml b/templates.toml index ac74bce7..3fb7a9ce 100644 --- a/templates.toml +++ b/templates.toml @@ -397,6 +397,13 @@ path = "iad/todos" use_cases = ["Web app", "IaD", "Storage"] tags = ["axum", "postgres", "database", "iad"] +[templates.iad-upload-manager] +title = "Rust API using AWS RDS Postgres for IaD" +description = "Upload manager API to manage S3 artifacts with AWS RDS Postgres database" +path = "iad/upload-manager" +use_cases = ["Web app", "IaD", "Storage"] +tags = ["axum", "postgres", "database", "iad"] + ## EXAMPLES ## [examples.metadata]