use axum::{
Router,
routing::{get, post, put, delete},
Json,
Extension,
extract::Path,
};
use wacht::{
init_from_env,
middleware::*,
require_permission,
};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
// Define permissions
require_permission!(CanReadPosts, "posts:read", Workspace);
require_permission!(CanWritePosts, "posts:write", Workspace);
require_permission!(CanDeletePosts, "posts:delete", Workspace);
#[derive(Serialize, Deserialize)]
struct Post {
id: String,
title: String,
content: String,
author_id: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
init_from_env().await?;
let app = Router::new()
// Public routes
.route("/posts", get(list_posts_public))
.route("/posts/:id", get(get_post_public))
// Authenticated routes
.route("/posts", post(create_post))
.route("/posts/:id", put(update_post))
.route("/posts/:id", delete(delete_post))
.route("/my-posts", get(list_my_posts))
// Apply auth middleware
.layer(AuthLayer::new());
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
axum::serve(listener, app).await?;
Ok(())
}
// Public endpoint with optional auth
async fn list_posts_public(auth: OptionalAuth) -> Json<Value> {
let posts = match auth.0 {
Some(ctx) => {
// Show more posts for authenticated users
format!("All posts (user: {})", ctx.user_id)
}
None => {
// Show limited posts for guests
"Public posts only".to_string()
}
};
Json(json!({ "posts": posts }))
}
// Public endpoint
async fn get_post_public(Path(id): Path<String>) -> Json<Value> {
Json(json!({
"id": id,
"title": "Sample Post",
"content": "Post content here"
}))
}
// Requires authentication and write permission
async fn create_post(
_perm: CanWritePosts,
Extension(auth): Extension<AuthContext>,
Json(post): Json<Post>,
) -> Json<Value> {
Json(json!({
"message": "Post created",
"post_id": post.id,
"author": auth.user_id
}))
}
// Requires authentication and write permission
async fn update_post(
_perm: CanWritePosts,
Path(id): Path<String>,
Json(post): Json<Post>,
) -> Json<Value> {
Json(json!({
"message": "Post updated",
"post_id": id
}))
}
// Requires authentication and delete permission
async fn delete_post(
_perm: CanDeletePosts,
Path(id): Path<String>,
) -> Json<Value> {
Json(json!({
"message": "Post deleted",
"post_id": id
}))
}
// Requires only authentication
async fn list_my_posts(
_auth: RequireAuth,
Extension(ctx): Extension<AuthContext>,
) -> Json<Value> {
Json(json!({
"user_id": ctx.user_id,
"posts": ["post1", "post2"]
}))
}