mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-20 17:02:53 -04:00
split commands
This commit is contained in:
@@ -44,6 +44,7 @@ explicitly authorizes a cache cleanup.
|
||||
- Prefer `use` imports over inline fully-qualified paths; only qualify inline for true one-offs or std collisions
|
||||
- TypeScript with Nx, React, Vite, Zustand, and Three.js
|
||||
- Use two spaces for indentation
|
||||
- Each Tauri command belongs in its own file module, named after the command. Keep shared state, types, and helpers in separate support modules; see the [Storyteller command conventions](crates/desktop/artcraft/src/services/storyteller/commands/AGENTS.md).
|
||||
|
||||
### File Layout
|
||||
|
||||
|
||||
@@ -63,7 +63,13 @@ use crate::services::sora::commands::sora_logout_command::sora_logout_command;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::sora::threads::sora_task_polling::sora_task_polling_thread::sora_task_polling_thread;
|
||||
use crate::services::storyteller::commands::storyteller_login_bridge_commands::{DesktopLoginBridgeState, storyteller_get_login_session_command, storyteller_password_login_command, storyteller_password_signup_command, storyteller_create_login_challenge_command, storyteller_poll_login_challenge_command, storyteller_cancel_login_challenge_command};
|
||||
use crate::services::storyteller::commands::login_bridge::DesktopLoginBridgeState;
|
||||
use crate::services::storyteller::commands::storyteller_cancel_login_challenge_command::storyteller_cancel_login_challenge_command;
|
||||
use crate::services::storyteller::commands::storyteller_create_login_challenge_command::storyteller_create_login_challenge_command;
|
||||
use crate::services::storyteller::commands::storyteller_get_login_session_command::storyteller_get_login_session_command;
|
||||
use crate::services::storyteller::commands::storyteller_password_login_command::storyteller_password_login_command;
|
||||
use crate::services::storyteller::commands::storyteller_password_signup_command::storyteller_password_signup_command;
|
||||
use crate::services::storyteller::commands::storyteller_poll_login_challenge_command::storyteller_poll_login_challenge_command;
|
||||
use crate::services::storyteller::commands::storyteller_get_credits_command::storyteller_get_credits_command;
|
||||
use crate::services::storyteller::commands::storyteller_get_subscription_command::storyteller_get_subscription_command;
|
||||
use crate::services::storyteller::commands::storyteller_purge_credentials_command::storyteller_purge_credentials_command;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# Tauri Commands
|
||||
|
||||
- Put each `#[tauri::command]` function in its own `.rs` file module named after
|
||||
that command, such as `storyteller_create_login_challenge_command.rs`. Do not
|
||||
collect multiple commands in a catch-all `*_commands.rs` file.
|
||||
- Keep `mod.rs` for module declarations. Put shared state, response/error types,
|
||||
and implementation helpers in separate support modules, with the narrowest
|
||||
visibility needed by their callers.
|
||||
- Update the imports used by `tauri::generate_handler!` when moving commands.
|
||||
Preserve IPC command names and arguments unless changing that contract is
|
||||
explicitly part of the task.
|
||||
- Authentication HTTP requests run in Rust through `artcraft_client` endpoint
|
||||
bindings. The frontend invokes Tauri commands; it does not call the API
|
||||
directly, and desktop commands do not connect to the backend MySQL database.
|
||||
- For login bridge changes, run the existing native fixture tests with
|
||||
`SQLX_OFFLINE=true cargo test --offline -p artcraft --lib login_bridge_tests`.
|
||||
These tests use loopback HTTP servers and temporary cookie stores.
|
||||
+107
-99
@@ -3,22 +3,21 @@ use std::sync::Arc;
|
||||
|
||||
use artcraft_api_defs::users::login_challenges::{LoginChallengeFailure, LoginChallengeState};
|
||||
use artcraft_api_defs::users::session_info::SessionUserInfo;
|
||||
use artcraft_client::endpoints::users::password_login::PasswordLoginRequest;
|
||||
use artcraft_client::endpoints::users::password_signup::PasswordSignupRequest;
|
||||
use artcraft_client::utils::api_host::ApiHost;
|
||||
use artcraft_client::utils::login_challenge_client::{
|
||||
LoginChallengeClient, LoginChallengeClientError,
|
||||
};
|
||||
use artcraft_client::utils::api_host::ApiHost;
|
||||
use artcraft_client::endpoints::users::password_login::PasswordLoginRequest;
|
||||
use artcraft_client::endpoints::users::password_signup::PasswordSignupRequest;
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::{info, warn};
|
||||
use serde::Serialize;
|
||||
use tauri::{AppHandle, Manager, State};
|
||||
use tauri::{AppHandle, Manager};
|
||||
use tauri_plugin_http::reqwest_cookie_store::{CookieStoreMutex, RawCookie};
|
||||
use tauri_plugin_http::Http;
|
||||
use tokio::sync::Mutex;
|
||||
use uuid_utils::uuid::generate_random_uuid;
|
||||
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::threads::main_window_thread::persist_storyteller_cookies_task::get_credentials_from_cookie_store;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
|
||||
@@ -60,91 +59,101 @@ pub struct DesktopLoginError {
|
||||
pub retryable: bool,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_create_login_challenge_command(
|
||||
config: State<'_, AppEnvConfigs>,
|
||||
state: State<'_, DesktopLoginBridgeState>,
|
||||
) -> Result<DesktopLoginChallenge, DesktopLoginError> {
|
||||
begin_challenge(&config.storyteller_host, &state).await
|
||||
impl DesktopLoginError {
|
||||
fn local(message: &str) -> Self {
|
||||
Self {
|
||||
status: None,
|
||||
message: message.to_owned(),
|
||||
retryable: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn from_client(origin: &str, error: LoginChallengeClientError) -> Self {
|
||||
warn!(
|
||||
"Website login failed: origin={} status={:?} reason={}",
|
||||
origin, error.status, error.message
|
||||
);
|
||||
Self {
|
||||
retryable: error
|
||||
.status
|
||||
.map(|s| s == 429 || s >= 500)
|
||||
.unwrap_or(error.message == "Unable to reach the login server"),
|
||||
status: error.status,
|
||||
message: format!("{}: {} (HTTP {:?})", origin, error.message, error.status),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_poll_login_challenge_command(
|
||||
app: AppHandle,
|
||||
state: State<'_, DesktopLoginBridgeState>,
|
||||
manager: State<'_, StorytellerCredentialManager>,
|
||||
challenge_id: String,
|
||||
) -> Result<DesktopLoginOutcome, DesktopLoginError> {
|
||||
let jar = app
|
||||
.try_state::<Http>()
|
||||
.ok_or_else(|| DesktopLoginError::local("HTTP cookie store unavailable"))?
|
||||
.cookies_jar
|
||||
.clone();
|
||||
poll_challenge(&state, &manager, &jar, &challenge_id).await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_cancel_login_challenge_command(
|
||||
state: State<'_, DesktopLoginBridgeState>,
|
||||
challenge_id: String,
|
||||
) -> Result<(), DesktopLoginError> {
|
||||
state.pending.lock().await.remove(&challenge_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_get_login_session_command(app: AppHandle, config: State<'_, AppEnvConfigs>) -> Result<Option<SessionUserInfo>, DesktopLoginError> {
|
||||
let client = LoginChallengeClient::new(&config.storyteller_host)
|
||||
.map_err(|e| DesktopLoginError::from_client(&config.storyteller_host.to_api_hostname_and_scheme(), e))?;
|
||||
current_login_session(&client, &http_cookie_jar(&app)?).await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_password_login_command(app: AppHandle, config: State<'_, AppEnvConfigs>, manager: State<'_, StorytellerCredentialManager>, request: PasswordLoginRequest) -> Result<SessionUserInfo, DesktopLoginError> {
|
||||
password_auth(&config.storyteller_host, &manager, &http_cookie_jar(&app)?, Some(request), None).await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_password_signup_command(app: AppHandle, config: State<'_, AppEnvConfigs>, manager: State<'_, StorytellerCredentialManager>, request: PasswordSignupRequest) -> Result<SessionUserInfo, DesktopLoginError> {
|
||||
password_auth(&config.storyteller_host, &manager, &http_cookie_jar(&app)?, None, Some(request)).await
|
||||
}
|
||||
|
||||
async fn current_login_session(client: &LoginChallengeClient, jar: &Arc<CookieStoreMutex>) -> Result<Option<SessionUserInfo>, DesktopLoginError> {
|
||||
pub(super) async fn current_login_session(
|
||||
client: &LoginChallengeClient,
|
||||
jar: &Arc<CookieStoreMutex>,
|
||||
) -> Result<Option<SessionUserInfo>, DesktopLoginError> {
|
||||
let credentials = {
|
||||
let store = jar.store.lock().map_err(|_| DesktopLoginError::local("HTTP cookie store unavailable"))?;
|
||||
get_credentials_from_cookie_store(&store, &client.api_url()).map_err(|_| DesktopLoginError::local("Unable to read session cookie"))?
|
||||
let store = jar
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|_| DesktopLoginError::local("HTTP cookie store unavailable"))?;
|
||||
get_credentials_from_cookie_store(&store, &client.api_url())
|
||||
.map_err(|_| DesktopLoginError::local("Unable to read session cookie"))?
|
||||
};
|
||||
let Some(signed) = credentials.session else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(signed) = credentials.session else { return Ok(None); };
|
||||
let session = match client.session(signed.as_str()).await {
|
||||
Ok(session) => session,
|
||||
Err(error) if error.status == Some(401) => return Ok(None),
|
||||
Err(error) => return Err(DesktopLoginError::from_client(&client.api_origin(), error)),
|
||||
};
|
||||
Ok(session.user.filter(|_| session.success && session.logged_in))
|
||||
Ok(
|
||||
session
|
||||
.user
|
||||
.filter(|_| session.success && session.logged_in),
|
||||
)
|
||||
}
|
||||
|
||||
async fn password_auth(host: &ApiHost, manager: &StorytellerCredentialManager, jar: &Arc<CookieStoreMutex>, login: Option<PasswordLoginRequest>, signup: Option<PasswordSignupRequest>) -> Result<SessionUserInfo, DesktopLoginError> {
|
||||
let client = LoginChallengeClient::new(host).map_err(|e| DesktopLoginError::from_client(&host.to_api_hostname_and_scheme(), e))?;
|
||||
pub(super) async fn password_auth(
|
||||
host: &ApiHost,
|
||||
manager: &StorytellerCredentialManager,
|
||||
jar: &Arc<CookieStoreMutex>,
|
||||
login: Option<PasswordLoginRequest>,
|
||||
signup: Option<PasswordSignupRequest>,
|
||||
) -> Result<SessionUserInfo, DesktopLoginError> {
|
||||
let client = LoginChallengeClient::new(host)
|
||||
.map_err(|e| DesktopLoginError::from_client(&host.to_api_hostname_and_scheme(), e))?;
|
||||
let response = match (login, signup) {
|
||||
(Some(request), None) => client.password_login(&request).await,
|
||||
(None, Some(request)) => client.password_signup(&request).await,
|
||||
_ => return Err(DesktopLoginError::local("Invalid authentication request")),
|
||||
}.map_err(|e| DesktopLoginError::from_client(&client.api_origin(), e))?;
|
||||
}
|
||||
.map_err(|e| DesktopLoginError::from_client(&client.api_origin(), e))?;
|
||||
let (response, cookie) = response;
|
||||
if !response.success { return Err(DesktopLoginError::local("Sign-in failed. Check your account details and try again.")); }
|
||||
let signed = response.signed_session.ok_or_else(|| DesktopLoginError::local("Login server did not issue a session"))?;
|
||||
let cookie = cookie.ok_or_else(|| DesktopLoginError::local("Login server did not set a session cookie"))?;
|
||||
if !response.success {
|
||||
return Err(DesktopLoginError::local(
|
||||
"Sign-in failed. Check your account details and try again.",
|
||||
));
|
||||
}
|
||||
let signed = response
|
||||
.signed_session
|
||||
.ok_or_else(|| DesktopLoginError::local("Login server did not issue a session"))?;
|
||||
let cookie =
|
||||
cookie.ok_or_else(|| DesktopLoginError::local("Login server did not set a session cookie"))?;
|
||||
let (cookie, user) = verify_session(&client, &signed, cookie).await?;
|
||||
install_session_cookie(&client, &signed, cookie, manager, jar)?;
|
||||
persist_session_cookie(jar).await?;
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
fn http_cookie_jar(app: &AppHandle) -> Result<Arc<CookieStoreMutex>, DesktopLoginError> {
|
||||
Ok(app.try_state::<Http>().ok_or_else(|| DesktopLoginError::local("HTTP cookie store unavailable"))?.cookies_jar.clone())
|
||||
pub(super) fn http_cookie_jar(app: &AppHandle) -> Result<Arc<CookieStoreMutex>, DesktopLoginError> {
|
||||
Ok(
|
||||
app
|
||||
.try_state::<Http>()
|
||||
.ok_or_else(|| DesktopLoginError::local("HTTP cookie store unavailable"))?
|
||||
.cookies_jar
|
||||
.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn begin_challenge(
|
||||
pub(super) async fn begin_challenge(
|
||||
host: &ApiHost,
|
||||
state: &DesktopLoginBridgeState,
|
||||
) -> Result<DesktopLoginChallenge, DesktopLoginError> {
|
||||
@@ -186,7 +195,7 @@ async fn begin_challenge(
|
||||
})
|
||||
}
|
||||
|
||||
async fn poll_challenge(
|
||||
pub(super) async fn poll_challenge(
|
||||
state: &DesktopLoginBridgeState,
|
||||
manager: &StorytellerCredentialManager,
|
||||
jar: &Arc<CookieStoreMutex>,
|
||||
@@ -251,7 +260,15 @@ async fn poll_challenge(
|
||||
})
|
||||
}
|
||||
|
||||
async fn verify_session(client: &LoginChallengeClient, signed: &str, set_cookie: String) -> Result<(RawCookie<'static>, SessionUserInfo), DesktopLoginError> {
|
||||
pub(super) async fn cancel_challenge(state: &DesktopLoginBridgeState, challenge_id: &str) {
|
||||
state.pending.lock().await.remove(challenge_id);
|
||||
}
|
||||
|
||||
async fn verify_session(
|
||||
client: &LoginChallengeClient,
|
||||
signed: &str,
|
||||
set_cookie: String,
|
||||
) -> Result<(RawCookie<'static>, SessionUserInfo), DesktopLoginError> {
|
||||
let cookie = RawCookie::parse(set_cookie)
|
||||
.map_err(|_| DesktopLoginError::local("Invalid session cookie"))?
|
||||
.into_owned();
|
||||
@@ -272,23 +289,39 @@ async fn verify_session(client: &LoginChallengeClient, signed: &str, set_cookie:
|
||||
Ok((cookie, user))
|
||||
}
|
||||
|
||||
fn install_session_cookie(client: &LoginChallengeClient, signed: &str, cookie: RawCookie<'static>, manager: &StorytellerCredentialManager, jar: &Arc<CookieStoreMutex>) -> Result<(), DesktopLoginError> {
|
||||
let mut store = jar.store.lock().map_err(|_| DesktopLoginError::local("HTTP cookie store unavailable"))?;
|
||||
fn install_session_cookie(
|
||||
client: &LoginChallengeClient,
|
||||
signed: &str,
|
||||
cookie: RawCookie<'static>,
|
||||
manager: &StorytellerCredentialManager,
|
||||
jar: &Arc<CookieStoreMutex>,
|
||||
) -> Result<(), DesktopLoginError> {
|
||||
let mut store = jar
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|_| DesktopLoginError::local("HTTP cookie store unavailable"))?;
|
||||
let mut candidate = store.clone();
|
||||
candidate.store_response_cookies([cookie].into_iter(), &client.api_url());
|
||||
let credentials = get_credentials_from_cookie_store(&candidate, &client.api_url())
|
||||
.map_err(|_| DesktopLoginError::local("Unable to read session cookie"))?;
|
||||
if credentials.session.as_ref().map(|c| c.as_str()) != Some(signed) {
|
||||
return Err(DesktopLoginError::local("Session cookie does not apply to the configured API host"));
|
||||
return Err(DesktopLoginError::local(
|
||||
"Session cookie does not apply to the configured API host",
|
||||
));
|
||||
}
|
||||
manager.set_credentials(&credentials).map_err(|_| DesktopLoginError::local("Unable to store native login credentials"))?;
|
||||
manager
|
||||
.set_credentials(&credentials)
|
||||
.map_err(|_| DesktopLoginError::local("Unable to store native login credentials"))?;
|
||||
*store = candidate;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn persist_session_cookie(jar: &Arc<CookieStoreMutex>) -> Result<(), DesktopLoginError> {
|
||||
let saved = jar.request_save().map_err(|_| DesktopLoginError::local("Unable to persist login cookie"))?;
|
||||
tokio::task::spawn_blocking(move || saved.recv()).await
|
||||
let saved = jar
|
||||
.request_save()
|
||||
.map_err(|_| DesktopLoginError::local("Unable to persist login cookie"))?;
|
||||
tokio::task::spawn_blocking(move || saved.recv())
|
||||
.await
|
||||
.map_err(|_| DesktopLoginError::local("Unable to persist login cookie"))?
|
||||
.map_err(|_| DesktopLoginError::local("Unable to persist login cookie"))?;
|
||||
Ok(())
|
||||
@@ -302,31 +335,6 @@ fn expired() -> DesktopLoginOutcome {
|
||||
}
|
||||
}
|
||||
|
||||
impl DesktopLoginError {
|
||||
fn local(message: &str) -> Self {
|
||||
Self {
|
||||
status: None,
|
||||
message: message.to_owned(),
|
||||
retryable: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_client(origin: &str, error: LoginChallengeClientError) -> Self {
|
||||
warn!(
|
||||
"Website login failed: origin={} status={:?} reason={}",
|
||||
origin, error.status, error.message
|
||||
);
|
||||
Self {
|
||||
retryable: error
|
||||
.status
|
||||
.map(|s| s == 429 || s >= 500)
|
||||
.unwrap_or(error.message == "Unable to reach the login server"),
|
||||
status: error.status,
|
||||
message: format!("{}: {} (HTTP {:?})", origin, error.message, error.status),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "storyteller_login_bridge_tests.rs"]
|
||||
mod login_bridge_tests;
|
||||
@@ -3,4 +3,10 @@ pub mod storyteller_get_credits_command;
|
||||
pub mod storyteller_get_subscription_command;
|
||||
pub mod stripe_checkout;
|
||||
pub mod stripe_customer_portal;
|
||||
pub mod storyteller_login_bridge_commands;
|
||||
pub(crate) mod login_bridge;
|
||||
pub mod storyteller_cancel_login_challenge_command;
|
||||
pub mod storyteller_create_login_challenge_command;
|
||||
pub mod storyteller_get_login_session_command;
|
||||
pub mod storyteller_password_login_command;
|
||||
pub mod storyteller_password_signup_command;
|
||||
pub mod storyteller_poll_login_challenge_command;
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
use tauri::State;
|
||||
|
||||
use crate::services::storyteller::commands::login_bridge::{
|
||||
cancel_challenge, DesktopLoginBridgeState, DesktopLoginError,
|
||||
};
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_cancel_login_challenge_command(
|
||||
state: State<'_, DesktopLoginBridgeState>,
|
||||
challenge_id: String,
|
||||
) -> Result<(), DesktopLoginError> {
|
||||
cancel_challenge(&state, &challenge_id).await;
|
||||
Ok(())
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
use tauri::State;
|
||||
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::services::storyteller::commands::login_bridge::{
|
||||
begin_challenge, DesktopLoginBridgeState, DesktopLoginChallenge, DesktopLoginError,
|
||||
};
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_create_login_challenge_command(
|
||||
config: State<'_, AppEnvConfigs>,
|
||||
state: State<'_, DesktopLoginBridgeState>,
|
||||
) -> Result<DesktopLoginChallenge, DesktopLoginError> {
|
||||
begin_challenge(&config.storyteller_host, &state).await
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
use artcraft_api_defs::users::session_info::SessionUserInfo;
|
||||
use artcraft_client::utils::login_challenge_client::LoginChallengeClient;
|
||||
use tauri::{AppHandle, State};
|
||||
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::services::storyteller::commands::login_bridge::{
|
||||
current_login_session, http_cookie_jar, DesktopLoginError,
|
||||
};
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_get_login_session_command(
|
||||
app: AppHandle,
|
||||
config: State<'_, AppEnvConfigs>,
|
||||
) -> Result<Option<SessionUserInfo>, DesktopLoginError> {
|
||||
let client = LoginChallengeClient::new(&config.storyteller_host).map_err(|error| {
|
||||
DesktopLoginError::from_client(&config.storyteller_host.to_api_hostname_and_scheme(), error)
|
||||
})?;
|
||||
current_login_session(&client, &http_cookie_jar(&app)?).await
|
||||
}
|
||||
+1
-1
@@ -167,7 +167,7 @@ async fn cancelling_during_http_prevents_late_cookie_installation() {
|
||||
poll_challenge(&h.state, &h.manager, &h.jar, &created.challenge_id),
|
||||
async {
|
||||
tokio::time::sleep(Duration::from_millis(30)).await;
|
||||
h.state.pending.lock().await.remove(&created.challenge_id);
|
||||
cancel_challenge(&h.state, &created.challenge_id).await;
|
||||
}
|
||||
);
|
||||
assert!(result.is_err());
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
use artcraft_api_defs::users::session_info::SessionUserInfo;
|
||||
use artcraft_client::endpoints::users::password_login::PasswordLoginRequest;
|
||||
use tauri::{AppHandle, State};
|
||||
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::services::storyteller::commands::login_bridge::{
|
||||
http_cookie_jar, password_auth, DesktopLoginError,
|
||||
};
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_password_login_command(
|
||||
app: AppHandle,
|
||||
config: State<'_, AppEnvConfigs>,
|
||||
manager: State<'_, StorytellerCredentialManager>,
|
||||
request: PasswordLoginRequest,
|
||||
) -> Result<SessionUserInfo, DesktopLoginError> {
|
||||
password_auth(
|
||||
&config.storyteller_host,
|
||||
&manager,
|
||||
&http_cookie_jar(&app)?,
|
||||
Some(request),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
use artcraft_api_defs::users::session_info::SessionUserInfo;
|
||||
use artcraft_client::endpoints::users::password_signup::PasswordSignupRequest;
|
||||
use tauri::{AppHandle, State};
|
||||
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::services::storyteller::commands::login_bridge::{
|
||||
http_cookie_jar, password_auth, DesktopLoginError,
|
||||
};
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_password_signup_command(
|
||||
app: AppHandle,
|
||||
config: State<'_, AppEnvConfigs>,
|
||||
manager: State<'_, StorytellerCredentialManager>,
|
||||
request: PasswordSignupRequest,
|
||||
) -> Result<SessionUserInfo, DesktopLoginError> {
|
||||
password_auth(
|
||||
&config.storyteller_host,
|
||||
&manager,
|
||||
&http_cookie_jar(&app)?,
|
||||
None,
|
||||
Some(request),
|
||||
)
|
||||
.await
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
use tauri::{AppHandle, State};
|
||||
|
||||
use crate::services::storyteller::commands::login_bridge::{
|
||||
http_cookie_jar, poll_challenge, DesktopLoginBridgeState, DesktopLoginError, DesktopLoginOutcome,
|
||||
};
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_poll_login_challenge_command(
|
||||
app: AppHandle,
|
||||
state: State<'_, DesktopLoginBridgeState>,
|
||||
manager: State<'_, StorytellerCredentialManager>,
|
||||
challenge_id: String,
|
||||
) -> Result<DesktopLoginOutcome, DesktopLoginError> {
|
||||
let jar = http_cookie_jar(&app)?;
|
||||
poll_challenge(&state, &manager, &jar, &challenge_id).await
|
||||
}
|
||||
Reference in New Issue
Block a user