From b828917b4a0f9f6a88f4c67bce64d83681ad5f5d Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Tue, 30 Dec 2025 00:14:12 +0300 Subject: [PATCH] restructure a bit --- core/src/errors/files.rs | 14 + core/src/requests/games.rs | 8 + core/src/requests/uploads.rs | 4 +- core/src/types/upload.rs | 2 +- http/src/requests/files.rs | 2 + http/src/requests/games.rs | 18 +- http/src/server/routes.rs | 517 ++---------------------------- http/src/server/routes/authors.rs | 71 ++++ http/src/server/routes/files.rs | 5 + http/src/server/routes/forum.rs | 20 ++ http/src/server/routes/games.rs | 104 ++++++ http/src/server/routes/marks.rs | 78 +++++ http/src/server/routes/uploads.rs | 5 + http/src/server/routes/users.rs | 217 +++++++++++++ 14 files changed, 561 insertions(+), 504 deletions(-) create mode 100644 http/src/server/routes/authors.rs create mode 100644 http/src/server/routes/files.rs create mode 100644 http/src/server/routes/forum.rs create mode 100644 http/src/server/routes/games.rs create mode 100644 http/src/server/routes/marks.rs create mode 100644 http/src/server/routes/uploads.rs create mode 100644 http/src/server/routes/users.rs diff --git a/core/src/errors/files.rs b/core/src/errors/files.rs index 30b4448..2b88e0b 100644 --- a/core/src/errors/files.rs +++ b/core/src/errors/files.rs @@ -2,6 +2,20 @@ use eva::data; use crate::types::file; +#[data(display("unexpected file class for this type of operation"), error)] +pub struct UnexpectedFileClass { + pub expected: file::ClassKind, + pub got: file::ClassKind, +} + +#[data(error)] +pub enum InvalidImage { + #[display("provided image is too big")] + TooBig, + #[display("provided image is too small")] + TooSmall, +} + #[data(copy, display("file {id} was not found"), error)] pub struct NotFound { pub id: file::Id, diff --git a/core/src/requests/games.rs b/core/src/requests/games.rs index f3d80d0..f7bbe34 100644 --- a/core/src/requests/games.rs +++ b/core/src/requests/games.rs @@ -41,6 +41,10 @@ pub mod update { NoSuchBadge(#[from] errors::marks::NoSuchBadge), #[display("{_0}")] NoSuchGenre(#[from] errors::marks::NoSuchGenre), + #[display("{_0}")] + UnexpectedFileClass(#[from] errors::files::UnexpectedFileClass), + #[display("{_0}")] + InvalidImage(#[from] errors::files::InvalidImage), } } @@ -169,6 +173,10 @@ pub mod create { NoSuchTag(#[from] errors::marks::NoSuchTag), #[display("{_0}")] NoSuchGenre(#[from] errors::marks::NoSuchGenre), + #[display("{_0}")] + UnexpectedFileClass(#[from] errors::files::UnexpectedFileClass), + #[display("{_0}")] + InvalidImage(#[from] errors::files::InvalidImage), } } diff --git a/core/src/requests/uploads.rs b/core/src/requests/uploads.rs index dbcd0e4..f455364 100644 --- a/core/src/requests/uploads.rs +++ b/core/src/requests/uploads.rs @@ -26,8 +26,8 @@ pub mod start { /// matter. pub name: Option, - /// Hash of the file. - pub hash: file::Hash, + /// Hash of the file. If specified - verify hashes when finishing. + pub hash: Option, /// Class of the file. pub class: file::ClassKind, diff --git a/core/src/types/upload.rs b/core/src/types/upload.rs index fa44791..31380b4 100644 --- a/core/src/types/upload.rs +++ b/core/src/types/upload.rs @@ -13,5 +13,5 @@ pub struct Upload { pub uploaded: u64, pub size: u64, pub class: file::ClassKind, - pub hash: file::Hash, + pub expected_hash: Option, } diff --git a/http/src/requests/files.rs b/http/src/requests/files.rs index 17c2157..f3d5522 100644 --- a/http/src/requests/files.rs +++ b/http/src/requests/files.rs @@ -16,4 +16,6 @@ const _: () = { use status_code::direct; direct!(NotFound => NOT_FOUND); + direct!(InvalidImage => BAD_REQUEST); + direct!(UnexpectedFileClass => BAD_REQUEST); }; diff --git a/http/src/requests/games.rs b/http/src/requests/games.rs index 474afc7..135aa44 100644 --- a/http/src/requests/games.rs +++ b/http/src/requests/games.rs @@ -25,7 +25,14 @@ pub struct Update { impl_req!(Update => [reqs::update::Ok; reqs::update::Err]); status_code::direct!(reqs::update::Ok => OK); -status_code::map!(reqs::update::Err => [NotFound, NoSuchBadge, NoSuchGenre, NoSuchTag]); +status_code::map!(reqs::update::Err => [ + NotFound, + NoSuchBadge, + NoSuchGenre, + NoSuchTag, + UnexpectedFileClass, + InvalidImage, +]); #[data] pub struct Search { @@ -76,7 +83,14 @@ pub struct Create { impl_req!(Create => [reqs::create::Ok; reqs::create::Err]); status_code::direct!(reqs::create::Ok => CREATED); -status_code::map!(reqs::create::Err => [AlreadyTaken, NoSuchAuthor, NoSuchGenre, NoSuchTag]); +status_code::map!(reqs::create::Err => [ + AlreadyTaken, + NoSuchAuthor, + NoSuchGenre, + NoSuchTag, + UnexpectedFileClass, + InvalidImage, +]); const _: () = { use errors::games::*; diff --git a/http/src/server/routes.rs b/http/src/server/routes.rs index 1cb032d..4cc676a 100644 --- a/http/src/server/routes.rs +++ b/http/src/server/routes.rs @@ -11,505 +11,24 @@ use viendesu_core::{ type SessionOf = Session::Service>>; +mod authors; +mod files; +mod games; +mod marks; +mod uploads; +mod users; + +mod forum; + pub fn make(router: RouterScope) -> RouterScope { router - .nest("/users", users) - .nest("/authors", authors) - .nest("/games", games) - .nest("/boards", boards) - .nest("/threads", threads) - .nest("/messages", messages) - .nest("/uploads", uploads) - .nest("/files", files) - .nest("/tags", tags) - .nest("/badges", badges) - .nest("/genres", genres) -} - -// == Routes == - -fn genres(router: RouterScope) -> RouterScope { - use crate::requests::marks::ListGenres; - - use viendesu_core::{requests::marks::list_genres, service::marks::Genres as _}; - - router.route( - "/", - get( - async move |mut session: SessionOf, ctx: Ctx| { - let ListGenres {} = ctx.request; - - session.genres().list().call(list_genres::Args {}).await - }, - ), - ) -} - -fn badges(router: RouterScope) -> RouterScope { - use crate::requests::marks::{AddBadge, ListBadges}; - - use viendesu_core::{ - requests::marks::{add_badge, list_badges}, - service::marks::Badges as _, - }; - - router - .route( - "/", - get( - async move |mut session: SessionOf, ctx: Ctx| { - let ListBadges { query } = ctx.request; - session - .badges() - .list() - .call(list_badges::Args { query }) - .await - }, - ), - ) - .route( - "/", - post(async move |mut session: SessionOf, ctx: Ctx| { - let AddBadge { text } = ctx.request; - session - .badges() - .add() - .call(add_badge::Args { badge: text }) - .await - }), - ) -} - -fn tags(router: RouterScope) -> RouterScope { - use crate::requests::marks::{AddTag, ListTags}; - - use viendesu_core::{ - requests::marks::{add_tag, list_tags}, - service::marks::Tags as _, - }; - - router - .route( - "/", - get(async move |mut session: SessionOf, ctx: Ctx| { - let ListTags { query } = ctx.request; - session.tags().list().call(list_tags::Args { query }).await - }), - ) - .route( - "/", - post(async move |mut session: SessionOf, ctx: Ctx| { - let AddTag { text } = ctx.request; - session.tags().add().call(add_tag::Args { tag: text }).await - }), - ) -} - -fn users(router: RouterScope) -> RouterScope { - use crate::requests::users::{ - BeginAuth, CheckAuth, ConfirmSignUp, FinishAuth, Get, SignIn, SignUp, Update, - }; - use viendesu_core::{ - requests::users::{ - begin_auth, check_auth, confirm_sign_up, finish_auth, get, sign_in, sign_up, update, - }, - service::{tabs::Tabs, users::Users}, - }; - - fn convert_update(u: Update) -> update::Update { - update::Update { - nickname: u.nickname, - display_name: u.display_name, - bio: u.bio, - password: u.password, - role: u.role, - pfp: u.pfp, - email: u.email, - } - } - - router - .route( - "/{sel}", - get(async |mut session: SessionOf, mut ctx: Ctx| { - let selector: user::Selector = ctx.path().await?; - session - .users() - .get() - .call(get::Args { - user: Some(selector), - }) - .await - }), - ) - .route( - "/begin-auth", - post(async |mut session: SessionOf, ctx: Ctx| { - session - .users() - .begin_auth() - .call(begin_auth::Args { - method: ctx.request.method, - }) - .await - }), - ) - .route( - "/finish-auth/{authsessid}", - post( - async |mut session: SessionOf, mut ctx: Ctx| { - let auth_session: user::AuthSessionId = ctx.path().await?; - session - .users() - .finish_auth() - .call(finish_auth::Args { auth_session }) - .await - }, - ), - ) - .route( - "/check-auth", - get(async |mut session: SessionOf, _ctx: Ctx| { - session.users().check_auth().call(check_auth::Args {}).await - }), - ) - .route( - "/me", - get(async |mut session: SessionOf, _ctx: Ctx| { - session.users().get().call(get::Args { user: None }).await - }), - ) - .route( - "/sign_in", - post(async |mut session: SessionOf, ctx: Ctx| { - session - .users() - .sign_in() - .call(sign_in::Args { - nickname: ctx.request.nickname, - password: ctx.request.password, - }) - .await - }), - ) - .route( - "/sign_up", - post(async |mut session: SessionOf, ctx: Ctx| { - let SignUp { - nickname, - email, - password, - display_name, - } = ctx.request; - session - .users() - .sign_up() - .call(sign_up::Args { - nickname, - email, - display_name, - password, - }) - .await - }), - ) - .route( - "/{sel}", - patch(async |mut session: SessionOf, mut ctx: Ctx| { - let user: user::Selector = ctx.path().await?; - session - .users() - .update() - .call(update::Args { - user: Some(user), - update: convert_update(ctx.request), - }) - .await - }), - ) - .route( - "/me", - patch(async |mut session: SessionOf, ctx: Ctx| { - session - .users() - .update() - .call(update::Args { - user: None, - update: convert_update(ctx.request), - }) - .await - }), - ) - .route( - "/{user}/confirm/{token}", - post( - async |mut session: SessionOf, mut ctx: Ctx| { - let (user, token) = ctx.path().await?; - session - .users() - .confirm_sign_up() - .call(confirm_sign_up::Args { user, token }) - .await - }, - ), - ) - .nest("/{user}/tabs", |router| { - use crate::requests::tabs::{Delete, Insert, List, ListItems}; - use viendesu_core::requests::tabs::{delete, insert, list, list_items}; - - router - .route( - "/", - get(async |mut session: SessionOf, mut ctx: Ctx| { - let user = ctx.path().await?; - let List {} = ctx.request; - session.tabs().list().call(list::Args { user }).await - }), - ) - .nest("/{tab}", |router| { - router - .route( - "/{item}", - delete(async |mut session: SessionOf, mut ctx: Ctx| { - let (user, tab, item) = ctx.path().await?; - let Delete {} = ctx.request; - - session - .tabs() - .delete() - .call(delete::Args { user, tab, item }) - .await - }), - ) - .route( - "/", - post(async |mut session: SessionOf, mut ctx: Ctx| { - let (user, tab) = ctx.path().await?; - let Insert { item } = ctx.request; - - session - .tabs() - .insert() - .call(insert::Args { user, tab, item }) - .await - }), - ) - .route( - "/", - get(async |mut session: SessionOf, mut ctx: Ctx| { - let (user, tab) = ctx.path().await?; - let ListItems { - resolve_marks, - start_from, - limit, - } = ctx.request; - - session - .tabs() - .list_items() - .call(list_items::Args { - tab, - user, - start_from, - limit, - resolve_marks, - }) - .await - }), - ) - }) - }) -} - -fn uploads(router: RouterScope) -> RouterScope { - router -} - -fn files(router: RouterScope) -> RouterScope { - router -} - -fn authors(router: RouterScope) -> RouterScope { - use crate::requests::authors::{Create, Get, Update}; - - use viendesu_core::{ - requests::authors::{create, get, update}, - service::authors::Authors, - types::author, - }; - - router - .route( - "/", - post(async |mut session: SessionOf, ctx: Ctx| { - let Create { - title, - slug, - description, - owner, - } = ctx.request; - session - .authors() - .create() - .call(create::Args { - title, - slug, - description, - owner, - }) - .await - }), - ) - .route( - "/{selector}", - get(async |mut session: SessionOf, mut ctx: Ctx| { - let author: author::Selector = ctx.path().await?; - let Get {} = ctx.request; - - session.authors().get().call(get::Args { author }).await - }), - ) - .route( - "/{selector}", - patch(async |mut session: SessionOf, mut ctx: Ctx| { - let author: author::Selector = ctx.path().await?; - let Update { - title, - description, - pfp, - slug, - verified, - } = ctx.request; - - session - .authors() - .update() - .call(update::Args { - author, - update: update::Update { - title, - description, - pfp, - slug, - verified, - }, - }) - .await - }), - ) -} - -fn games(router: RouterScope) -> RouterScope { - use crate::requests::games::{Create, Get, Search}; - - use viendesu_core::{ - requests::games::{create, get, search}, - service::games::Games, - types::{author, game}, - }; - - router - .route( - "/", - post(async |mut session: SessionOf, ctx: Ctx| { - let Create { - title, - description, - thumbnail, - tags, - genres, - author, - slug, - vndb, - release_date, - } = ctx.request; - session - .games() - .create() - .call(create::Args { - title, - description, - thumbnail, - author, - slug, - tags, - genres, - vndb, - release_date, - }) - .await - }), - ) - .route( - "/{game_id}", - get(async |mut session: SessionOf, mut ctx: Ctx| { - let game_id: game::Id = ctx.path().await?; - let Get { resolve_marks } = ctx.request; - - session - .games() - .get() - .call(get::Args { - game: game_id.into(), - resolve_marks, - }) - .await - }), - ) - .route( - "/{author}/{slug}", - get(async |mut session: SessionOf, mut ctx: Ctx| { - let (author, slug) = ctx.path::<(author::Selector, game::Slug)>().await?; - let Get { resolve_marks } = ctx.request; - - session - .games() - .get() - .call(get::Args { - resolve_marks, - game: game::Selector::FullyQualified(game::FullyQualified { author, slug }), - }) - .await - }), - ) - .route( - "/search", - post(async |mut session: SessionOf, ctx: Ctx| { - let Search { - query, - author, - include, - exclude, - order, - sort_by, - limit, - } = ctx.request; - - session - .games() - .search() - .call(search::Args { - query, - author, - include, - exclude, - order, - sort_by, - limit, - }) - .await - }), - ) -} - -fn boards(router: RouterScope) -> RouterScope { - router -} - -fn threads(router: RouterScope) -> RouterScope { - router -} - -fn messages(router: RouterScope) -> RouterScope { - router + .nest("/users", users::make) + .nest("/authors", authors::make) + .nest("/games", games::make) + .nest("/forum", forum::make) + .nest("/uploads", uploads::make) + .nest("/files", files::make) + .nest("/tags", marks::tags) + .nest("/badges", marks::badges) + .nest("/genres", marks::genres) } diff --git a/http/src/server/routes/authors.rs b/http/src/server/routes/authors.rs new file mode 100644 index 0000000..e57c6c7 --- /dev/null +++ b/http/src/server/routes/authors.rs @@ -0,0 +1,71 @@ +use super::*; + +use crate::requests::authors::{Create, Get, Update}; + +use viendesu_core::{ + requests::authors::{create, get, update}, + service::authors::Authors, + types::author, +}; + +pub fn make(router: RouterScope) -> RouterScope { + router + .route( + "/", + post(async |mut session: SessionOf, ctx: Ctx| { + let Create { + title, + slug, + description, + owner, + } = ctx.request; + session + .authors() + .create() + .call(create::Args { + title, + slug, + description, + owner, + }) + .await + }), + ) + .route( + "/{selector}", + get(async |mut session: SessionOf, mut ctx: Ctx| { + let author: author::Selector = ctx.path().await?; + let Get {} = ctx.request; + + session.authors().get().call(get::Args { author }).await + }), + ) + .route( + "/{selector}", + patch(async |mut session: SessionOf, mut ctx: Ctx| { + let author: author::Selector = ctx.path().await?; + let Update { + title, + description, + pfp, + slug, + verified, + } = ctx.request; + + session + .authors() + .update() + .call(update::Args { + author, + update: update::Update { + title, + description, + pfp, + slug, + verified, + }, + }) + .await + }), + ) +} diff --git a/http/src/server/routes/files.rs b/http/src/server/routes/files.rs new file mode 100644 index 0000000..d9828e4 --- /dev/null +++ b/http/src/server/routes/files.rs @@ -0,0 +1,5 @@ +use super::*; + +pub fn make(router: RouterScope) -> RouterScope { + router +} diff --git a/http/src/server/routes/forum.rs b/http/src/server/routes/forum.rs new file mode 100644 index 0000000..1ca7156 --- /dev/null +++ b/http/src/server/routes/forum.rs @@ -0,0 +1,20 @@ +use super::*; + +pub fn make(router: RouterScope) -> RouterScope { + router + .nest("/boards", boards) + .nest("/threads", threads) + .nest("/messages", messages) +} + +fn boards(router: RouterScope) -> RouterScope { + router +} + +fn threads(router: RouterScope) -> RouterScope { + router +} + +fn messages(router: RouterScope) -> RouterScope { + router +} diff --git a/http/src/server/routes/games.rs b/http/src/server/routes/games.rs new file mode 100644 index 0000000..bc58da2 --- /dev/null +++ b/http/src/server/routes/games.rs @@ -0,0 +1,104 @@ +use super::*; + +use crate::requests::games::{Create, Get, Search}; + +use viendesu_core::{ + requests::games::{create, get, search}, + service::games::Games, + types::{author, game}, +}; + +pub fn make(router: RouterScope) -> RouterScope { + router + .route( + "/", + post(async |mut session: SessionOf, ctx: Ctx| { + let Create { + title, + description, + thumbnail, + tags, + genres, + author, + slug, + vndb, + release_date, + } = ctx.request; + session + .games() + .create() + .call(create::Args { + title, + description, + thumbnail, + author, + slug, + tags, + genres, + vndb, + release_date, + }) + .await + }), + ) + .route( + "/{game_id}", + get(async |mut session: SessionOf, mut ctx: Ctx| { + let game_id: game::Id = ctx.path().await?; + let Get { resolve_marks } = ctx.request; + + session + .games() + .get() + .call(get::Args { + game: game_id.into(), + resolve_marks, + }) + .await + }), + ) + .route( + "/{author}/{slug}", + get(async |mut session: SessionOf, mut ctx: Ctx| { + let (author, slug) = ctx.path::<(author::Selector, game::Slug)>().await?; + let Get { resolve_marks } = ctx.request; + + session + .games() + .get() + .call(get::Args { + resolve_marks, + game: game::Selector::FullyQualified(game::FullyQualified { author, slug }), + }) + .await + }), + ) + .route( + "/search", + post(async |mut session: SessionOf, ctx: Ctx| { + let Search { + query, + author, + include, + exclude, + order, + sort_by, + limit, + } = ctx.request; + + session + .games() + .search() + .call(search::Args { + query, + author, + include, + exclude, + order, + sort_by, + limit, + }) + .await + }), + ) +} diff --git a/http/src/server/routes/marks.rs b/http/src/server/routes/marks.rs new file mode 100644 index 0000000..8fd9a14 --- /dev/null +++ b/http/src/server/routes/marks.rs @@ -0,0 +1,78 @@ +use super::*; + +pub fn genres(router: RouterScope) -> RouterScope { + use crate::requests::marks::ListGenres; + + use viendesu_core::{requests::marks::list_genres, service::marks::Genres as _}; + + router.route( + "/", + get( + async move |mut session: SessionOf, ctx: Ctx| { + let ListGenres {} = ctx.request; + + session.genres().list().call(list_genres::Args {}).await + }, + ), + ) +} + +pub fn badges(router: RouterScope) -> RouterScope { + use crate::requests::marks::{AddBadge, ListBadges}; + + use viendesu_core::{ + requests::marks::{add_badge, list_badges}, + service::marks::Badges as _, + }; + + router + .route( + "/", + get( + async move |mut session: SessionOf, ctx: Ctx| { + let ListBadges { query } = ctx.request; + session + .badges() + .list() + .call(list_badges::Args { query }) + .await + }, + ), + ) + .route( + "/", + post(async move |mut session: SessionOf, ctx: Ctx| { + let AddBadge { text } = ctx.request; + session + .badges() + .add() + .call(add_badge::Args { badge: text }) + .await + }), + ) +} + +pub fn tags(router: RouterScope) -> RouterScope { + use crate::requests::marks::{AddTag, ListTags}; + + use viendesu_core::{ + requests::marks::{add_tag, list_tags}, + service::marks::Tags as _, + }; + + router + .route( + "/", + get(async move |mut session: SessionOf, ctx: Ctx| { + let ListTags { query } = ctx.request; + session.tags().list().call(list_tags::Args { query }).await + }), + ) + .route( + "/", + post(async move |mut session: SessionOf, ctx: Ctx| { + let AddTag { text } = ctx.request; + session.tags().add().call(add_tag::Args { tag: text }).await + }), + ) +} diff --git a/http/src/server/routes/uploads.rs b/http/src/server/routes/uploads.rs new file mode 100644 index 0000000..d9828e4 --- /dev/null +++ b/http/src/server/routes/uploads.rs @@ -0,0 +1,5 @@ +use super::*; + +pub fn make(router: RouterScope) -> RouterScope { + router +} diff --git a/http/src/server/routes/users.rs b/http/src/server/routes/users.rs new file mode 100644 index 0000000..2e48414 --- /dev/null +++ b/http/src/server/routes/users.rs @@ -0,0 +1,217 @@ +use super::*; + +use crate::requests::users::{ + BeginAuth, CheckAuth, ConfirmSignUp, FinishAuth, Get, SignIn, SignUp, Update, +}; +use viendesu_core::{ + requests::users::{ + begin_auth, check_auth, confirm_sign_up, finish_auth, get, sign_in, sign_up, update, + }, + service::{tabs::Tabs, users::Users}, +}; + +fn convert_update(u: Update) -> update::Update { + update::Update { + nickname: u.nickname, + display_name: u.display_name, + bio: u.bio, + password: u.password, + role: u.role, + pfp: u.pfp, + email: u.email, + } +} + +pub fn make(router: RouterScope) -> RouterScope { + router + .route( + "/{sel}", + get(async |mut session: SessionOf, mut ctx: Ctx| { + let selector: user::Selector = ctx.path().await?; + session + .users() + .get() + .call(get::Args { + user: Some(selector), + }) + .await + }), + ) + .route( + "/begin-auth", + post(async |mut session: SessionOf, ctx: Ctx| { + session + .users() + .begin_auth() + .call(begin_auth::Args { + method: ctx.request.method, + }) + .await + }), + ) + .route( + "/finish-auth/{authsessid}", + post( + async |mut session: SessionOf, mut ctx: Ctx| { + let auth_session: user::AuthSessionId = ctx.path().await?; + session + .users() + .finish_auth() + .call(finish_auth::Args { auth_session }) + .await + }, + ), + ) + .route( + "/check-auth", + get(async |mut session: SessionOf, _ctx: Ctx| { + session.users().check_auth().call(check_auth::Args {}).await + }), + ) + .route( + "/me", + get(async |mut session: SessionOf, _ctx: Ctx| { + session.users().get().call(get::Args { user: None }).await + }), + ) + .route( + "/sign_in", + post(async |mut session: SessionOf, ctx: Ctx| { + session + .users() + .sign_in() + .call(sign_in::Args { + nickname: ctx.request.nickname, + password: ctx.request.password, + }) + .await + }), + ) + .route( + "/sign_up", + post(async |mut session: SessionOf, ctx: Ctx| { + let SignUp { + nickname, + email, + password, + display_name, + } = ctx.request; + session + .users() + .sign_up() + .call(sign_up::Args { + nickname, + email, + display_name, + password, + }) + .await + }), + ) + .route( + "/{sel}", + patch(async |mut session: SessionOf, mut ctx: Ctx| { + let user: user::Selector = ctx.path().await?; + session + .users() + .update() + .call(update::Args { + user: Some(user), + update: convert_update(ctx.request), + }) + .await + }), + ) + .route( + "/me", + patch(async |mut session: SessionOf, ctx: Ctx| { + session + .users() + .update() + .call(update::Args { + user: None, + update: convert_update(ctx.request), + }) + .await + }), + ) + .route( + "/{user}/confirm/{token}", + post( + async |mut session: SessionOf, mut ctx: Ctx| { + let (user, token) = ctx.path().await?; + session + .users() + .confirm_sign_up() + .call(confirm_sign_up::Args { user, token }) + .await + }, + ), + ) + .nest("/{user}/tabs", |router| { + use crate::requests::tabs::{Delete, Insert, List, ListItems}; + use viendesu_core::requests::tabs::{delete, insert, list, list_items}; + + router + .route( + "/", + get(async |mut session: SessionOf, mut ctx: Ctx| { + let user = ctx.path().await?; + let List {} = ctx.request; + session.tabs().list().call(list::Args { user }).await + }), + ) + .nest("/{tab}", |router| { + router + .route( + "/{item}", + delete(async |mut session: SessionOf, mut ctx: Ctx| { + let (user, tab, item) = ctx.path().await?; + let Delete {} = ctx.request; + + session + .tabs() + .delete() + .call(delete::Args { user, tab, item }) + .await + }), + ) + .route( + "/", + post(async |mut session: SessionOf, mut ctx: Ctx| { + let (user, tab) = ctx.path().await?; + let Insert { item } = ctx.request; + + session + .tabs() + .insert() + .call(insert::Args { user, tab, item }) + .await + }), + ) + .route( + "/", + get(async |mut session: SessionOf, mut ctx: Ctx| { + let (user, tab) = ctx.path().await?; + let ListItems { + resolve_marks, + start_from, + limit, + } = ctx.request; + + session + .tabs() + .list_items() + .call(list_items::Args { + tab, + user, + start_from, + limit, + resolve_marks, + }) + .await + }), + ) + }) + }) +}