मुख्य सामग्री पर जाएँ

प्रोजेक्ट संरचना

यह पृष्ठ अंग्रेज़ी से अनुवादित है। अंग्रेज़ी पृष्ठ पढ़ें

हर Zeyn ऐप का लेआउट एक जैसा है, इसलिए इंसान या एजेंट को हर प्रोजेक्ट में चीज़ें उसी जगह मिलती हैं।

config/app.json          defaults; APP_* environment variables override them
locales/<lang>.json      every user facing text; all files have the same keys
src/main.rs              loads configuration and starts the app
src/app.rs               the App: services, features, security settings
src/features/<name>/     one folder per feature
    mod.rs               the Feature: route prefix, guards, screens, APIs, menu
    models.rs            #[entity] and #[model] types
    store.rs             services that read and write data
    actions.rs           #[action] functions
    screens.rs           #[screen] functions
tests/                   tests that drive the app through its router

ऐप

src/app.rs App बनाता है। यह सेवाएँ, feature और वे सेटिंग पंजीकृत करता है जो हर Screen पर लागू होती हैं:

pub fn app(config: Config) -> App {
    App::new(t!("app.name"))
        .config(config)
        .i18n(i18n())
        .service(NoteStore::default())
        .auth(ForwardAuth::new("x-forwarded-user").groups("x-forwarded-groups"))
        .rate_limit(RateLimit::per_ip(600, Duration::from_secs(60)))
        .home(features::home::home)
        .feature(features::home::feature())
        .feature(features::notes::feature())
}

App::serve(addr) ऐप की जाँच करता है और सर्वर शुरू करता है। App::router() axum राउटर लौटाता है, जिसे टेस्ट बिना पोर्ट खोले इस्तेमाल करते हैं।

एक feature

feature रूट प्रीफ़िक्स, गार्ड, Screen, API, कमांड और मेनू को समूहित करती है:

pub fn feature() -> Feature {
    Feature::new("notes")
        .route_prefix("/notes")
        .guard(SignedIn)
        .screen(note_list)
        .screen(note_editor)
        .api(search_notes)
        .menu(|m| m.group(t!("menu.notes"), Icon::Edit).item(t!("notes.title"), note_list))
}

सेवाएँ

सेवा क्लोन करने में सस्ता मान है जिसे action और Screen आर्ग्युमेंट के रूप में पाती हैं। इसे App::service से एक बार पंजीकृत करें; जब किसी Screen को ऐसी सेवा चाहिए जो किसी ने पंजीकृत नहीं की, तो ऐप शुरू होने से मना कर देता है।

#[derive(Clone, Default)]
pub struct NoteStore(Arc<RwLock<Vec<Note>>>);

impl Service for NoteStore {}

कॉन्फ़िगरेशन

Config::load स्रोतों को क्रम से पढ़ता है; बाद के स्रोत जीतते हैं। config.get("key") मान लौटाता है और config.secret("key") Secret लौटाता है जो कभी नहीं छपता।

let config = Config::load(vec![Box::new(JsonFileSource("config/app.json".into())), Box::new(EnvSource("APP"))]).await?;
let addr = config.get("server.addr").unwrap_or("127.0.0.1:3000").to_string();

टेस्ट

टेस्ट राउटर बनाते हैं और उसे मेमोरी में कॉल करते हैं:

#[tokio::test]
async fn notes_open() {
    let router = app(Config::default()).router().unwrap();
    let res = router.oneshot(Request::get("/notes").body(Body::empty()).unwrap()).await.unwrap();
    assert_eq!(res.status(), StatusCode::OK);
}