المسارات
المسارات (Routes) تُعيّن طلبات HTTP الواردة لدوال المعالجات. يُنظّم FORGE المسارات في مجموعتين: مسارات admin لنقاط نهاية الإدارة المُصادق عليها ومسارات عامة للبيانات المُقدّمة للعملاء. تُسجّل المسارات باستخدام Router في Axum وتُركّب مع middleware للمصادقة والتفويض.
تنظيم المسارات
/api/
├── auth/
│ ├── POST login
│ ├── POST register
│ ├── POST refresh
│ ├── POST logout
│ ├── POST forgot-password
│ ├── POST reset-password
│ ├── POST send-otp
│ └── POST verify-otp
├── profile GET, PUT (مُصادق)
├── contents/{slug} GET (عام)
├── lookups/{type} GET (عام)
├── menus/{location} GET (عام)
└── admin/ (مصادقة + صلاحيات مطلوبة)
├── users/ GET, POST
│ └── {id} GET, PUT, DELETE
│ └── roles POST
├── roles/ GET, POST
│ └── {id} GET, PUT, DELETE
├── permissions/ GET
├── contents/ GET, POST
│ └── {id} GET, PUT, DELETE
├── lookups/ GET, POST
│ └── {id} GET, PUT, DELETE
├── menus/ GET, POST
│ └── {id} GET, PUT, DELETE
└── settings/ GET, PUTتسجيل المسارات
الـ Router الرئيسي
الـ router الأعلى مستوى في main.rs يُركّب جميع مجموعات المسارات:
// src/main.rs
let app = Router::new()
// Admin routes (auth and RBAC middleware applied inside)
.nest("/api/admin", routes::admin::routes(pool.clone()))
// Public API routes
.nest("/api", routes::public::routes(pool.clone()))
// Health checks
.route("/health", get(|| async { "OK" }))
.route("/ready", get(health_check))
// Documentation
.merge(SwaggerUi::new("/docs").url("/api-docs/openapi.json", ApiDoc::openapi()))
.merge(Redoc::with_url("/redoc", ApiDoc::openapi()))
// Global middleware
.layer(CorsLayer::permissive())
.layer(middleware::from_fn(middleware::request_id::add_request_id));مسارات Admin
مسارات Admin تُطبّق middleware المصادقة على المجموعة بأكملها وmiddleware الصلاحيات على المسارات الفردية:
// src/routes/admin.rs
use axum::{Router, routing::{get, post, put, delete}, middleware};
use crate::handlers::admin;
use crate::middleware::{auth::auth_middleware, rbac::require_permission};
pub fn routes(pool: PgPool) -> Router {
Router::new()
// User management
.route(
"/users",
get(admin::users::list)
.route_layer(middleware::from_fn_with_state(
pool.clone(),
require_permission("users.view"),
))
)
.route(
"/users",
post(admin::users::create)
.route_layer(middleware::from_fn_with_state(
pool.clone(),
require_permission("users.create"),
))
)
.route(
"/users/:id",
get(admin::users::show)
.route_layer(middleware::from_fn_with_state(
pool.clone(),
require_permission("users.view"),
))
)
.route(
"/users/:id",
put(admin::users::update)
.route_layer(middleware::from_fn_with_state(
pool.clone(),
require_permission("users.edit"),
))
)
.route(
"/users/:id",
delete(admin::users::delete)
.route_layer(middleware::from_fn_with_state(
pool.clone(),
require_permission("users.delete"),
))
)
.route(
"/users/:id/roles",
post(admin::users::assign_roles)
.route_layer(middleware::from_fn_with_state(
pool.clone(),
require_permission("roles.edit"),
))
)
// Role management
.route(
"/roles",
get(admin::roles::list)
.route_layer(middleware::from_fn_with_state(
pool.clone(),
require_permission("roles.view"),
))
)
.route(
"/roles",
post(admin::roles::create)
.route_layer(middleware::from_fn_with_state(
pool.clone(),
require_permission("roles.create"),
))
)
.route(
"/roles/:id",
put(admin::roles::update)
.route_layer(middleware::from_fn_with_state(
pool.clone(),
require_permission("roles.edit"),
))
)
.route(
"/roles/:id",
delete(admin::roles::delete)
.route_layer(middleware::from_fn_with_state(
pool.clone(),
require_permission("roles.delete"),
))
)
// Permissions (read-only)
.route(
"/permissions",
get(admin::permissions::list)
.route_layer(middleware::from_fn_with_state(
pool.clone(),
require_permission("roles.view"),
))
)
// Content management
.route(
"/contents",
get(admin::contents::list)
.route_layer(middleware::from_fn_with_state(
pool.clone(),
require_permission("contents.view"),
))
)
.route(
"/contents",
post(admin::contents::create)
.route_layer(middleware::from_fn_with_state(
pool.clone(),
require_permission("contents.create"),
))
)
.route(
"/contents/:id",
get(admin::contents::show)
.route_layer(middleware::from_fn_with_state(
pool.clone(),
require_permission("contents.view"),
))
)
.route(
"/contents/:id",
put(admin::contents::update)
.route_layer(middleware::from_fn_with_state(
pool.clone(),
require_permission("contents.edit"),
))
)
.route(
"/contents/:id",
delete(admin::contents::delete)
.route_layer(middleware::from_fn_with_state(
pool.clone(),
require_permission("contents.delete"),
))
)
// Settings
.route("/settings", get(admin::settings::list))
.route("/settings", put(admin::settings::update))
// Apply auth middleware to all admin routes
.layer(middleware::from_fn_with_state(
pool.clone(),
auth_middleware,
))
.with_state(pool)
}تحذير
auth_middleware يُطبّق كـ layer على كامل router الـ admin. هذا يعني كل مسار تحت /api/admin/* يتطلب JWT token صالح. المسارات الفردية ثم تُطبّق require_permission للتحكم الدقيق بالوصول.
المسارات العامة
المسارات العامة متاحة بدون مصادقة، رغم أن بعضها قد يستخدمها اختيارياً:
// src/routes/public.rs
use axum::{Router, routing::{get, post}};
use crate::handlers;
pub fn routes(pool: PgPool) -> Router {
Router::new()
// Authentication (no auth required)
.route("/auth/login", post(handlers::auth::login))
.route("/auth/register", post(handlers::auth::register))
.route("/auth/refresh", post(handlers::auth::refresh))
.route("/auth/logout", post(handlers::auth::logout))
.route("/auth/forgot-password", post(handlers::auth::forgot_password))
.route("/auth/reset-password", post(handlers::auth::reset_password))
.route("/auth/send-otp", post(handlers::auth::send_otp))
.route("/auth/verify-otp", post(handlers::auth::verify_otp))
// Profile (auth required)
.route(
"/profile",
get(handlers::profile::show)
.put(handlers::profile::update)
.route_layer(middleware::from_fn_with_state(
pool.clone(),
auth_middleware,
))
)
// Public content (no auth required)
.route("/contents/:slug", get(handlers::contents::show))
.route("/lookups/:type", get(handlers::lookups::by_type))
.route("/menus/:location", get(handlers::menus::by_location))
.with_state(pool)
}تطبيق Middleware
يُطبّق Middleware على مستويات مختلفة حسب النطاق:
Middleware عام (جميع المسارات)
├── CORS
├── Request ID
│
├── /api/admin/* (مسارات admin)
│ ├── Auth Middleware (جميع مسارات admin)
│ │ ├── /users → require_permission("users.view")
│ │ ├── /users POST → require_permission("users.create")
│ │ └── ...
│ │
├── /api/* (مسارات عامة)
│ ├── /auth/* → بدون middleware
│ ├── /profile → Auth Middleware (مستوى المسار)
│ └── /contents/:slug → بدون middlewareنصيحة
طبّق middleware على أضيق نطاق ممكن. Middleware العام يعمل على كل طلب، بما فيها فحوصات الصحة ونقاط نهاية التوثيق. استخدم route_layer لـ middleware خاص بالمسار لإبقاء الحمل الزائد أدنى ما يمكن.
أنماط المسارات
مسارات CRUD للموارد
النمط القياسي لمورد جديد:
// Add to src/routes/admin.rs
.route("/products", get(admin::products::list))
.route("/products", post(admin::products::create))
.route("/products/:id", get(admin::products::show))
.route("/products/:id", put(admin::products::update))
.route("/products/:id", delete(admin::products::delete))مسارات الموارد المتداخلة
للموارد التي تنتمي لأب:
// Comments belong to a content entry
.route("/contents/:content_id/comments", get(admin::comments::list))
.route("/contents/:content_id/comments", post(admin::comments::create))
.route("/contents/:content_id/comments/:id", put(admin::comments::update))
.route("/contents/:content_id/comments/:id", delete(admin::comments::delete))مسارات الإجراءات
للإجراءات غير CRUD على مورد:
// Custom actions
.route("/users/:id/roles", post(admin::users::assign_roles))
.route("/users/:id/activate", post(admin::users::activate))
.route("/users/:id/deactivate", post(admin::users::deactivate))معاملات المسار
يستخرج Axum معاملات المسار باستخدام مُستخرج Path:
// Single parameter
.route("/users/:id", get(show_user))
async fn show_user(Path(id): Path<Uuid>) -> impl IntoResponse { /* ... */ }
// Multiple parameters
.route("/contents/:content_id/comments/:id", get(show_comment))
async fn show_comment(
Path((content_id, id)): Path<(Uuid, Uuid)>,
) -> impl IntoResponse { /* ... */ }انظر أيضاً
- الـ Handlers — الدوال التي تُشير إليها المسارات
- الـ Middleware — تنفيذ middleware المصادقة و RBAC
- التفويض — نظام الصلاحيات المستخدم في حراس المسارات
- نظرة عامة — بنية المشروع الكاملة