Skip to content
Clone

refactor: rename files and modules for improved organization and clarity

ZhenYi committed
parent 5f84089 commit 163952e Browse files

287 files changed

+2600 −2020

File tree 287
Modified app/src/main.rs View app/src/main.rs
@@ -58,49 +58,35 @@
5858 .await
5959 .map_err(|err| anyhow::anyhow!("failed to start the rocketmq producer: {err}"))?;
6060
61 let auth_config = auth_config()?;
62 let (auth_svc, client, mail, auth_events) = match auth_config.as_ref() {
63 Some(auth_config) => {
64 let db =
65 auth::db::DbConn::new(database.primary().clone(), database.replicas().to_vec());
66 let cache = auth::cache::Cache::connect(&app_config.redis)
67 .await
68 .context("connecting the auth cache")?;
69 let auth_events = auth::events::EventHandler::new(conn.clone());
70 let email_pool: config::email::EmailPoolConfig = auth_config
71 .section_or_default()
72 .context("loading email configuration")?;
73 let mail = auth::email::MailPublisher::new(
74 queue::Publisher::new(conn.clone()),
75 email_pool.mail_topic,
76 );
77 let pow: config::pow::PowConfig = auth_config
78 .section()
79 .context("loading pow configuration")?;
80 let service = auth::server::AuthSvc::new(
81 db,
82 cache,
83 Some(mail.clone()),
84 Some(auth_events.clone()),
85 auth_config
86 .section()
87 .context("loading auth configuration")?,
88 pow,
89 session_config.max_age.max(1) as u64,
90 )
91 .await
92 .context("starting the auth service")?;
93 let client = std::sync::Arc::new(auth::client::KenAuthClient::local(
94 service.clone(),
95 2_000,
96 ));
97 (Some(service), Some(client), Some(mail), Some(auth_events))
98 }
99 None => (None, None, None, None),
100 };
101 let state = export::AppState::with_identity(app_config, database, client, mail)
102 .await
103 .context("building AppState")?;
61 let auth_events = auth::events::EventHandler::new(conn.clone());
62 let email_pool: config::email::EmailPoolConfig = config::loader::figment()
63 .ok()
64 .and_then(|figment| {
65 figment
66 .extract_inner::<config::email::EmailPoolConfig>("email")
67 .ok()
68 })
69 .unwrap_or_default();
70 let mail = auth::email::MailPublisher::new(
71 queue::Publisher::new(conn.clone()),
72 email_pool.mail_topic,
73 );
74 let mut state = export::AppState::with_identity(
75 app_config,
76 database,
77 None,
78 Some(mail),
79 Some(auth_events.clone()),
80 )
81 .await
82 .context("building AppState")?;
83 let auth_service = auth::AuthService::new(state.clone());
84 state.kenauth = Some(std::sync::Arc::new(auth::AuthApiAdapter::new(
85 auth_service,
86 )));
87 if let Some(client) = state.kenauth.clone() {
88 export::identity_client::install(client);
89 }
10490 info!("dependencies ready");
10591
10692 if let Err(err) = export::telemetry::init_meter_provider() {
@@ -118,7 +104,7 @@
118104 tokio::spawn(deletion::run_worker(state.clone()));
119105
120106 let relay = events::outbox::OutboxRelay::start(
121 auth_events,
107 Some(auth_events),
122108 Some(queue::Publisher::new(conn.clone())),
123109 vec![("apihost", state.database.primary().clone())],
124110 );
@@ -139,11 +125,10 @@
139125
140126 let doc = api_doc();
141127 let app_state = state.clone();
142 let auth_service = auth_svc.clone();
143128
144129 let server = HttpServer::new(move || {
145130 let state = app_state.clone();
146 let mut app = App::new()
131 let app = App::new()
147132 .app_data(web::Data::new(state.clone()))
148133 .app_data(
149134 web::JsonConfig::default()
@@ -151,9 +136,6 @@
151136 .error_handler(export::http::json_error),
152137 )
153138 .app_data(web::PayloadConfig::new(webapi::MAX_REQUEST_BODY_BYTES));
154 if let Some(service) = auth_service.clone() {
155 app = app.app_data(web::Data::new(service));
156 }
157139 app.wrap(auth::session::SessionMiddleware::new(
158140 session_config.clone(),
159141 session_store.clone(),
@@ -162,8 +144,8 @@
162144 .wrap(webapi::build_cors(&state.config.web))
163145 .wrap(from_fn(export::middleware::trace_request))
164146 .wrap(from_fn(export::telemetry::http_telemetry))
165 .wrap(from_fn(webapi::auth::session::revalidate::revalidate))
166 .wrap(from_fn(webapi::auth::session::csrf::csrf))
147 .wrap(from_fn(auth::auth_session::revalidate::revalidate))
148 .wrap(from_fn(auth::auth_session::csrf::csrf))
167149 .service(web::resource("/api/docs").route(web::get().to(|| async {
168150 actix_web::HttpResponse::Found()
169151 .insert_header((actix_web::http::header::LOCATION, "/api/docs/"))
@@ -187,29 +169,6 @@
187169 });
188170 let api = async move { server.await.context("actix server error") };
189171
190 let grpc = if let Some(auth_config) = auth_config.as_ref() {
191 let server_config: config::server::ServerConfig = auth_config
192 .section()
193 .context("loading server configuration")?;
194 let listener = tokio::net::TcpListener::bind(server_config.grpc_addr)
195 .await
196 .with_context(|| format!("binding gRPC on {}", server_config.grpc_addr))?;
197 let grpc_cancel = cancel.clone();
198 let incoming = tokio_stream::wrappers::TcpListenerStream::new(listener);
199 Some(tokio::spawn(async move {
200 if let Err(error) = auth::server::grpc::router()
201 .serve_with_incoming_shutdown(tonic::service::Routes::default(), incoming, async move {
202 grpc_cancel.cancelled().await;
203 })
204 .await
205 {
206 warn!(%error, "gRPC server stopped");
207 }
208 }))
209 } else {
210 None
211 };
212
213172 let signal = async {
214173 tokio::select! {
215174 _ = export::telemetry::shutdown_signal() => cancel.cancel(),
@@ -219,9 +178,6 @@
219178 let (api_result, ()) = tokio::join!(api, signal);
220179 if let Err(error) = relay.shutdown().await {
221180 warn!(%error, "shutting down the event outbox relay failed");
222 }
223 if let Some(grpc) = grpc {
224 let _ = grpc.await;
225181 }
226182 for task in push_tasks {
227183 let _ = task.await;
@@ -308,7 +264,7 @@
308264 sync_schema(&db).await.context("running schema sync")?;
309265 if let Some(auth_config) = auth_config()? {
310266 let identity_db = auth::db::DbConn::new(db.clone(), Vec::new());
311 auth::server::migrate::sync_schema(&identity_db)
267 auth::migrate::sync_schema(&identity_db)
312268 .await
313269 .context("synchronizing identity schema")?;
314270 drop(auth_config);
@@ -380,8 +336,8 @@
380336}
381337
382338fn identity_openapi() -> anyhow::Result<()> {
383 use utoipa_identity::OpenApi;
384 let doc = auth::server::openapi::ApiDoc::openapi();
339 use utoipa::OpenApi;
340 let doc = auth::openapi::ApiDoc::openapi();
385341 let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../lib/auth/OpenApi.Auth.json");
386342 std::fs::write(&path, doc.to_pretty_json()?).context("writing identity OpenAPI")?;
387343 println!("wrote {}", path.display());
Modified app/Cargo.toml View app/Cargo.toml
@@ -28,6 +28,3 @@
2828utoipa-swagger-ui = { workspace = true }
2929webapi = { workspace = true }
3030
31utoipa-identity = { package = "utoipa", version = "6.0.0" }
32tokio-stream = { version = "0.1", features = ["net"] }
33tonic = { workspace = true }
Renamed lib/webapi/src/auth/session/cookie.rs → lib/auth/src/auth_session/cookie.rs View lib/auth/src/auth_session/cookie.rs

No content changes.

Renamed lib/webapi/src/auth/session/csrf.rs → lib/auth/src/auth_session/csrf.rs View lib/auth/src/auth_session/csrf.rs

No content changes.

Renamed lib/webapi/src/auth/session/mod.rs → lib/auth/src/auth_session/mod.rs View lib/auth/src/auth_session/mod.rs
@@ -1,6 +1,3 @@
11pub mod cookie;
22pub mod csrf;
3pub mod login;
4pub mod logout;
5pub mod project;
63pub mod revalidate;
Renamed lib/webapi/src/auth/session/revalidate.rs → lib/auth/src/auth_session/revalidate.rs View lib/auth/src/auth_session/revalidate.rs
@@ -7,7 +7,7 @@
77use ::session::Session;
88use export::AppState;
99
10use crate::auth::session::cookie::append_cookie;
10use crate::auth_session::cookie::append_cookie;
1111
1212const SKIP_PREFIXES: [&str; 8] = [
1313 "/healthz",
@@ -76,8 +76,8 @@
7676 .await;
7777 Ok(next.call(request).await?.map_into_boxed_body())
7878 }
79 Err(auth::client::error::KenAuthError::Unauthenticated)
80 | Err(auth::client::error::KenAuthError::NotFound) => {
79 Err(crate::auth_api_error::KenAuthError::Unauthenticated)
80 | Err(crate::auth_api_error::KenAuthError::NotFound) => {
8181 let mut response = HttpResponse::Unauthorized().json(serde_json::json!({
8282 "code": 401,
8383 "message": "session_revoked",
Deleted lib/auth/src/client/error.rs
@@ -1,44 +0,0 @@
1use tonic::Status;
2
3#[derive(Debug, thiserror::Error)]
4pub enum KenAuthError {
5 #[error("kenoauth is unavailable: {0}")]
6 Unavailable(String),
7 #[error("kenoauth rejected the request: {0}")]
8 Rejected(String),
9 #[error("kenoauth could not find the requested object")]
10 NotFound,
11 #[error("kenoauth rejected the credential")]
12 Unauthenticated,
13 #[error("kenoauth configuration is invalid: {0}")]
14 Config(String),
15 #[error("kenoauth request failed: {0}")]
16 Internal(String),
17}
18
19impl KenAuthError {
20 pub fn from_status(status: Status) -> Self {
21 match status.code() {
22 tonic::Code::Unavailable | tonic::Code::DeadlineExceeded => {
23 Self::Unavailable(status.message().to_string())
24 }
25 tonic::Code::Unauthenticated => Self::Unauthenticated,
26 tonic::Code::NotFound => Self::NotFound,
27 tonic::Code::PermissionDenied
28 | tonic::Code::InvalidArgument
29 | tonic::Code::FailedPrecondition
30 | tonic::Code::AlreadyExists
31 | tonic::Code::ResourceExhausted
32 | tonic::Code::Aborted => Self::Rejected(status.message().to_string()),
33 _ => Self::Internal(status.message().to_string()),
34 }
35 }
36
37 pub fn is_not_found(&self) -> bool {
38 matches!(self, Self::NotFound)
39 }
40
41 pub fn is_unauthenticated(&self) -> bool {
42 matches!(self, Self::Unauthenticated)
43 }
44}
Deleted lib/auth/src/client/mod.rs
@@ -1,5 +0,0 @@
1mod client;
2pub mod error;
3pub mod local;
4
5pub use client::KenAuthClient;
Renamed lib/webapi/src/auth/handlers/accept_agreement.rs → lib/auth/src/handlers/account/accept_agreement.rs View lib/auth/src/handlers/account/accept_agreement.rs
@@ -5,9 +5,9 @@
55use serde::{Deserialize, Serialize};
66use utoipa::ToSchema;
77
8use crate::auth::errors::ApiError;
9use crate::auth::services::AuthService;
10use crate::auth::services::agreements::AcceptAgreementResp;
8use crate::api_error::ApiError;
9use crate::service::AuthService;
10use crate::services::agreements::AcceptAgreementResp;
1111use ::session::share::session;
1212
1313#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
Renamed lib/webapi/src/auth/handlers/account_deletion.rs → lib/auth/src/handlers/account/account_deletion.rs View lib/auth/src/handlers/account/account_deletion.rs
@@ -6,8 +6,8 @@
66use time::OffsetDateTime;
77use utoipa::ToSchema;
88
9use crate::auth::errors::{ApiError, AuthError};
10use crate::auth::services::AuthService;
9use crate::api_error::{ApiError, AuthBusinessError};
10use crate::service::AuthService;
1111
1212#[derive(Debug, Clone, Serialize, ToSchema)]
1313pub struct AccountDeletionDto {
@@ -45,7 +45,7 @@
4545 let uid = session::require_uid(&session)?;
4646 let status = deletion::account_deletion_status(&service.state, uid)
4747 .await
48 .map_err(AuthError::from)?;
48 .map_err(AuthBusinessError::from)?;
4949 Ok(AppResponseData::ok(status.map(AccountDeletionDto::from)))
5050}
5151
@@ -68,7 +68,7 @@
6868 let status =
6969 deletion::request_account_deletion(&service.state, uid, "user requested account deletion")
7070 .await
71 .map_err(AuthError::from)?;
71 .map_err(AuthBusinessError::from)?;
7272 Ok(AppResponseData::ok(status.into()))
7373}
7474
@@ -90,6 +90,6 @@
9090 let uid = session::require_uid(&session)?;
9191 deletion::cancel_account_deletion(&service.state, uid)
9292 .await
93 .map_err(AuthError::from)?;
93 .map_err(AuthBusinessError::from)?;
9494 Ok(AppResponseData::empty())
9595}
Renamed lib/webapi/src/auth/handlers/agreement_page.rs → lib/auth/src/handlers/account/agreement_page.rs View lib/auth/src/handlers/account/agreement_page.rs
@@ -1,9 +1,9 @@
11use export::http::{HeaderMap, Html, Path, State};
22use i18n::catalog;
33
4use crate::auth::errors::ApiError;
5use crate::auth::services::AuthService;
6use crate::auth::templates;
4use crate::api_error::ApiError;
5use crate::service::AuthService;
6use crate::templates;
77
88pub async fn agreement_page(
99 State(service): State<AuthService>,
Renamed lib/webapi/src/auth/handlers/create_report.rs → lib/auth/src/handlers/account/create_report.rs View lib/auth/src/handlers/account/create_report.rs
@@ -3,9 +3,9 @@
33use ::session::Session;
44use export::rest::{AppResponseData, NoData};
55
6use crate::auth::errors::ApiError;
7use crate::auth::services::AuthService;
8use crate::auth::services::reports::{CreateReportCmd, ReportDto};
6use crate::api_error::ApiError;
7use crate::service::AuthService;
8use crate::services::reports::{CreateReportCmd, ReportDto};
99use ::session::share::session;
1010
1111#[utoipa::path(
Renamed lib/webapi/src/auth/handlers/delete_notification.rs → lib/auth/src/handlers/account/delete_notification.rs View lib/auth/src/handlers/account/delete_notification.rs
@@ -3,8 +3,8 @@
33use export::rest::{AppResponseData, NoData};
44use uuid::Uuid;
55
6use crate::auth::errors::ApiError;
7use crate::auth::services::AuthService;
6use crate::api_error::ApiError;
7use crate::service::AuthService;
88use ::session::share::session;
99
1010#[utoipa::path(
Renamed lib/webapi/src/auth/handlers/devices.rs → lib/auth/src/handlers/account/devices.rs View lib/auth/src/handlers/account/devices.rs
@@ -1,6 +1,6 @@
1use crate::protos::auth::common::v1::PageRequest;
12use ::session::Session;
23use ::session::share::session;
3use auth::protos::auth::common::v1::PageRequest;
44use export::http::{Query, State};
55use export::pagination::PageQuery;
66use export::rest::AppResponseData;
@@ -10,9 +10,9 @@
1010use utoipa::ToSchema;
1111use uuid::Uuid;
1212
13use crate::auth::errors::ApiError;
14use crate::auth::services::AuthService;
15use crate::auth::share::rpc;
13use crate::api_error::ApiError;
14use crate::service::AuthService;
15use crate::share::rpc;
1616
1717const SESSION_PAGE_SIZE: i32 = 100;
1818
Renamed lib/webapi/src/auth/handlers/get_preferences.rs → lib/auth/src/handlers/account/get_preferences.rs View lib/auth/src/handlers/account/get_preferences.rs
@@ -2,9 +2,9 @@
22use export::http::State;
33use export::rest::{AppResponseData, NoData};
44
5use crate::auth::errors::ApiError;
6use crate::auth::services::AuthService;
7use crate::auth::services::preferences::UserPreferencesDto;
5use crate::api_error::ApiError;
6use crate::service::AuthService;
7use crate::services::preferences::UserPreferencesDto;
88use ::session::share::session;
99
1010#[utoipa::path(
Renamed lib/webapi/src/auth/handlers/get_user.rs → lib/auth/src/handlers/account/get_user.rs View lib/auth/src/handlers/account/get_user.rs
@@ -3,9 +3,9 @@
33use export::http::{Path, State};
44use export::rest::{AppResponseData, NoData};
55
6use crate::auth::errors::ApiError;
7use crate::auth::services::AuthService;
8use crate::auth::share::types::UserPublic;
6use crate::api_error::ApiError;
7use crate::service::AuthService;
8use crate::share::account::types::UserPublic;
99
1010#[utoipa::path(
1111 get,
Renamed lib/webapi/src/auth/handlers/list_agreements.rs → lib/auth/src/handlers/account/list_agreements.rs View lib/auth/src/handlers/account/list_agreements.rs
@@ -1,9 +1,9 @@
11use export::http::State;
22use export::rest::{AppResponseData, NoData};
33
4use crate::auth::errors::ApiError;
5use crate::auth::services::AuthService;
6use crate::auth::share::types::AgreementDoc;
4use crate::api_error::ApiError;
5use crate::service::AuthService;
6use crate::share::account::types::AgreementDoc;
77
88#[utoipa::path(
99 get,
Renamed lib/webapi/src/auth/handlers/list_notifications.rs → lib/auth/src/handlers/account/list_notifications.rs View lib/auth/src/handlers/account/list_notifications.rs
@@ -4,9 +4,9 @@
44use serde::Deserialize;
55use utoipa::IntoParams;
66
7use crate::auth::errors::ApiError;
8use crate::auth::services::AuthService;
9use crate::auth::services::notifications::NotificationDto;
7use crate::api_error::ApiError;
8use crate::service::AuthService;
9use crate::services::notifications::NotificationDto;
1010use ::session::share::session;
1111
1212#[derive(Debug, Clone, Copy, Default, Deserialize, IntoParams)]
Renamed lib/webapi/src/auth/handlers/mark_notifications_read.rs → lib/auth/src/handlers/account/mark_notifications_read.rs View lib/auth/src/handlers/account/mark_notifications_read.rs
@@ -4,8 +4,8 @@
44use serde::Deserialize;
55use uuid::Uuid;
66
7use crate::auth::errors::ApiError;
8use crate::auth::services::AuthService;
7use crate::api_error::ApiError;
8use crate::service::AuthService;
99use ::session::share::session;
1010
1111#[derive(Debug, Deserialize, utoipa::ToSchema)]
Renamed lib/webapi/src/auth/handlers/me.rs → lib/auth/src/handlers/account/me.rs View lib/auth/src/handlers/account/me.rs
@@ -3,9 +3,9 @@
33use serde::Serialize;
44use utoipa::ToSchema;
55
6use crate::auth::errors::ApiError;
7use crate::auth::services::AuthService;
8use crate::auth::services::profile::{ProfileDto, UserMeDto};
6use crate::api_error::ApiError;
7use crate::service::AuthService;
8use crate::services::profile::{ProfileDto, UserMeDto};
99use ::session::Session;
1010use ::session::share::session;
1111
Renamed lib/webapi/src/auth/handlers/mod.rs → lib/auth/src/handlers/account/mod.rs View lib/auth/src/handlers/account/mod.rs

No content changes.

Renamed lib/webapi/src/auth/handlers/password.rs → lib/auth/src/handlers/account/password.rs View lib/auth/src/handlers/account/password.rs
@@ -5,9 +5,9 @@
55use serde::Deserialize;
66use utoipa::ToSchema;
77
8use crate::auth::errors::ApiError;
9use crate::auth::services::AuthService;
10use crate::auth::share::rpc;
8use crate::api_error::ApiError;
9use crate::service::AuthService;
10use crate::share::rpc;
1111
1212#[derive(Debug, Deserialize, ToSchema)]
1313pub struct ChangePasswordCmd {
Renamed lib/webapi/src/auth/handlers/sessions.rs → lib/auth/src/handlers/account/sessions.rs View lib/auth/src/handlers/account/sessions.rs
@@ -1,7 +1,7 @@
1use crate::protos::auth::common::v1::{PageRequest, SessionInfo};
2use crate::protos::auth::v1::ListUserSessionsResponse;
13use ::session::Session;
24use ::session::share::session;
3use auth::protos::auth::common::v1::{PageRequest, SessionInfo};
4use auth::protos::auth::v1::ListUserSessionsResponse;
55use export::http::{Path, Query, State};
66use export::pagination::{Page, PageQuery, paginate};
77use export::rest::{AppResponseData, NoData};
@@ -10,9 +10,9 @@
1010use utoipa::ToSchema;
1111use uuid::Uuid;
1212
13use crate::auth::errors::ApiError;
14use crate::auth::services::AuthService;
15use crate::auth::share::rpc;
13use crate::api_error::ApiError;
14use crate::service::AuthService;
15use crate::share::rpc;
1616
1717const SESSION_PAGE_SIZE: i32 = 100;
1818
@@ -75,7 +75,7 @@
7575 let created_at = info
7676 .created_at
7777 .and_then(|ts| rpc::datetime(ts.seconds, ts.nanos))
78 .ok_or_else(|| ApiError::internal("kenoauth returned a session without created_at"))?;
78 .ok_or_else(|| ApiError::internal("auth service returned a session without created_at"))?;
7979 let last_seen_at = info
8080 .last_seen_at
8181 .and_then(|ts| rpc::datetime(ts.seconds, ts.nanos))
@@ -86,7 +86,7 @@
8686 .unwrap_or(created_at);
8787 Ok(SessionDto {
8888 id: Uuid::parse_str(&info.session_id)
89 .map_err(|_| ApiError::internal("kenoauth returned a malformed session id"))?,
89 .map_err(|_| ApiError::internal("auth service returned a malformed session id"))?,
9090 kind: SessionKind::WebLogin,
9191 client_ip_hash: hash_field(&info.client_ip),
9292 user_agent_hash: hash_field(&info.user_agent),
Renamed lib/webapi/src/auth/handlers/two_factor.rs → lib/auth/src/handlers/account/two_factor.rs View lib/auth/src/handlers/account/two_factor.rs
@@ -1,14 +1,14 @@
1use crate::protos::auth::common::v1::MfaStatus;
12use ::session::Session;
23use ::session::share::session;
3use auth::protos::auth::common::v1::MfaStatus;
44use export::http::{HeaderMap, Json, State};
55use export::rest::{AppResponseData, NoData};
66use serde::{Deserialize, Serialize};
77use utoipa::ToSchema;
88
9use crate::auth::errors::ApiError;
10use crate::auth::services::AuthService;
11use crate::auth::share::rpc;
9use crate::api_error::ApiError;
10use crate::service::AuthService;
11use crate::share::rpc;
1212
1313#[derive(Debug, Serialize, ToSchema)]
1414pub struct TotpStatusResp {
Renamed lib/webapi/src/auth/handlers/unread_notifications_count.rs → lib/auth/src/handlers/account/unread_notifications_count.rs View lib/auth/src/handlers/account/unread_notifications_count.rs
@@ -4,8 +4,8 @@
44use serde::Serialize;
55use utoipa::ToSchema;
66
7use crate::auth::errors::ApiError;
8use crate::auth::services::AuthService;
7use crate::api_error::ApiError;
8use crate::service::AuthService;
99use ::session::share::session;
1010
1111#[derive(Debug, Clone, Serialize, ToSchema)]
Renamed lib/webapi/src/auth/handlers/update_preferences.rs → lib/auth/src/handlers/account/update_preferences.rs View lib/auth/src/handlers/account/update_preferences.rs
@@ -3,9 +3,9 @@
33use ::session::Session;
44use export::rest::{AppResponseData, NoData};
55
6use crate::auth::errors::ApiError;
7use crate::auth::services::AuthService;
8use crate::auth::services::preferences::{UpdatePreferencesCmd, UserPreferencesDto};
6use crate::api_error::ApiError;
7use crate::service::AuthService;
8use crate::services::preferences::{UpdatePreferencesCmd, UserPreferencesDto};
99use ::session::share::session;
1010
1111#[utoipa::path(
Renamed lib/webapi/src/auth/handlers/update_profile.rs → lib/auth/src/handlers/account/update_profile.rs View lib/auth/src/handlers/account/update_profile.rs
@@ -3,9 +3,9 @@
33use ::session::Session;
44use export::rest::{AppResponseData, NoData};
55
6use crate::auth::errors::ApiError;
7use crate::auth::services::AuthService;
8use crate::auth::services::profile::UpdateProfileCmd;
6use crate::api_error::ApiError;
7use crate::service::AuthService;
8use crate::services::profile::UpdateProfileCmd;
99use ::session::share::session;
1010
1111#[utoipa::path(
Renamed lib/webapi/src/auth/handlers/users.rs → lib/auth/src/handlers/account/users.rs View lib/auth/src/handlers/account/users.rs
@@ -5,9 +5,9 @@
55use serde::Deserialize;
66use utoipa::IntoParams;
77
8use crate::auth::errors::ApiError;
9use crate::auth::services::AuthService;
10use crate::auth::share::types::UserPublic;
8use crate::api_error::ApiError;
9use crate::service::AuthService;
10use crate::share::account::types::UserPublic;
1111
1212#[derive(Debug, Deserialize, IntoParams)]
1313pub struct UsersBatchQuery {
Renamed lib/auth/src/server/http/challenge/handler.rs → lib/auth/src/handlers/identity/challenge/handler.rs View lib/auth/src/handlers/identity/challenge/handler.rs
@@ -2,13 +2,12 @@
22use actix_web::web;
33
44use crate::envelope::{Envelope, NoData};
5use crate::server::AuthSvc;
6use crate::server::errors::AuthError;
7use crate::server::http::challenge::types::ChallengeResponse;
5use crate::errors::AuthError;
6use crate::handlers::identity::challenge::types::ChallengeResponse;
87
98#[utoipa::path(
109 post,
11 path = "/api/v1/oauth/challenge",
10 path = "/api/v1/auth/challenge",
1211 tag = "auth",
1312 operation_id = "auth_challenge",
1413 summary = "签发工作量证明挑战",
@@ -29,7 +28,7 @@
2928 )
3029)]
3130pub async fn challenge(
32 svc: web::Data<AuthSvc>,
31 svc: web::Data<crate::AuthSvc>,
3332 session: Session,
3433 request_id: RequestId,
3534) -> Result<Envelope<ChallengeResponse>, AuthError> {
Renamed lib/auth/src/server/http/challenge/mod.rs → lib/auth/src/handlers/identity/challenge/mod.rs View lib/auth/src/handlers/identity/challenge/mod.rs

No content changes.

Renamed lib/auth/src/server/http/challenge/service.rs → lib/auth/src/handlers/identity/challenge/service.rs View lib/auth/src/handlers/identity/challenge/service.rs
@@ -1,9 +1,9 @@
11use crate::events::{Labels, label};
22use crate::session::Session;
33
4use crate::server::AuthSvc;
5use crate::server::errors::AuthResult;
6use crate::server::http::challenge::types::{ChallengeResponse, PowProofRequest};
4use crate::AuthSvc;
5use crate::errors::AuthResult;
6use crate::handlers::identity::challenge::types::{ChallengeResponse, PowProofRequest};
77
88impl AuthSvc {
99 #[tracing::instrument(skip_all, fields(request_id = %request_id, client_ip = ?session.client_ip().ok().flatten()))]
@@ -12,7 +12,7 @@
1212 session: Session,
1313 request_id: String,
1414 ) -> AuthResult<ChallengeResponse> {
15 let response = crate::server::pow::store::issue(&session, self.pow()).await?;
15 let response = crate::pow::store::issue(&session, self.pow()).await?;
1616 self.emit_labeled(
1717 label::AUTH_CHALLENGE_CREATE,
1818 Labels::new()
@@ -33,7 +33,7 @@
3333 expected_payload: &[u8],
3434 request_id: String,
3535 ) -> AuthResult<()> {
36 crate::server::pow::store::verify(&session, self.cache(), proof, expected_payload).await?;
36 crate::pow::store::verify(&session, self.cache(), proof, expected_payload).await?;
3737 self.emit_labeled(
3838 label::AUTH_CHALLENGE_USED,
3939 Labels::new()
Renamed lib/auth/src/server/http/challenge/types.rs → lib/auth/src/handlers/identity/challenge/types.rs View lib/auth/src/handlers/identity/challenge/types.rs

No content changes.

Renamed lib/auth/src/server/http/login/handler.rs → lib/auth/src/handlers/identity/login/handler.rs View lib/auth/src/handlers/identity/login/handler.rs
@@ -2,21 +2,20 @@
22use actix_web::web;
33
44use crate::envelope::{Envelope, NoData};
5use crate::server::AuthSvc;
6use crate::server::errors::AuthError;
5use crate::errors::AuthError;
76
8use crate::server::http::login::types::{
7use crate::handlers::identity::login::types::{
98 LoginCodeRequest, LoginCodeResponse, LoginCodeVerifyRequest, LoginVerifyRequest,
109 UserLoginRequest, UserLoginResponse,
1110};
1211
1312#[utoipa::path(
1413 post,
15 path = "/api/v1/oauth/login",
14 path = "/api/v1/auth/login",
1615 tag = "auth",
1716 operation_id = "auth_login",
1817 summary = "口令登录",
19 description = "用口令登录。`identity` 可以是用户名、邮箱(大小写不敏感)或用户编号,`password_hash` 是客户端先算好的口令摘要,工作量证明的载荷必须是 `identity`。账号启用了多因子时返回 `mfa_required: true` 与 `challenge`,此时会话尚未绑定,需再调 `/api/v1/oauth/login/verify`。",
18 description = "用口令登录。`identity` 可以是用户名、邮箱(大小写不敏感)或用户编号,`password_hash` 是客户端先算好的口令摘要,工作量证明的载荷必须是 `identity`。账号启用了多因子时返回 `mfa_required: true` 与 `challenge`,此时会话尚未绑定,需再调 `/api/v1/auth/login/verify`。",
2019 request_body = UserLoginRequest,
2120 responses(
2221 (status = 200, description = "登录成功;`mfa_required` 为真时还需二次验证", body = Envelope<UserLoginResponse>, example = json!({"code":200,"message":"success","data":{"user_id":1024,"username":"ada","displayname":"Ada","mfa_required":false,"challenge":null}})),
@@ -28,7 +27,7 @@
2827 )
2928)]
3029pub async fn login(
31 svc: web::Data<AuthSvc>,
30 svc: web::Data<crate::AuthSvc>,
3231 session: Session,
3332 body: web::Json<UserLoginRequest>,
3433 request_id: RequestId,
@@ -39,7 +38,7 @@
3938
4039#[utoipa::path(
4140 post,
42 path = "/api/v1/oauth/login/code",
41 path = "/api/v1/auth/login/code",
4342 tag = "auth",
4443 operation_id = "auth_login_code",
4544 summary = "发送邮箱登录验证码",
@@ -55,7 +54,7 @@
5554 )
5655)]
5756pub async fn login_code(
58 svc: web::Data<AuthSvc>,
57 svc: web::Data<crate::AuthSvc>,
5958 session: Session,
6059 body: web::Json<LoginCodeRequest>,
6160 request_id: RequestId,
@@ -68,7 +67,7 @@
6867
6968#[utoipa::path(
7069 post,
71 path = "/api/v1/oauth/login/code/verify",
70 path = "/api/v1/auth/login/code/verify",
7271 tag = "auth",
7372 operation_id = "auth_login_code_verify",
7473 summary = "邮箱验证码换会话",
@@ -84,7 +83,7 @@
8483 )
8584)]
8685pub async fn login_code_verify(
87 svc: web::Data<AuthSvc>,
86 svc: web::Data<crate::AuthSvc>,
8887 session: Session,
8988 body: web::Json<LoginCodeVerifyRequest>,
9089 request_id: RequestId,
@@ -97,11 +96,11 @@
9796
9897#[utoipa::path(
9998 post,
100 path = "/api/v1/oauth/login/verify",
99 path = "/api/v1/auth/login/verify",
101100 tag = "auth",
102101 operation_id = "auth_login_verify",
103102 summary = "二次验证换会话",
104 description = "用 6 位 TOTP 动态码或一枚未使用的恢复码完成二次验证并绑定会话。`challenge` 必须与 `/api/v1/oauth/login` 或 `/api/v1/oauth/login/code/verify` 返回的一致,挑战有效期 300 秒,同一动态码时间步不能重复使用,恢复码用掉即作废。",
103 description = "用 6 位 TOTP 动态码或一枚未使用的恢复码完成二次验证并绑定会话。`challenge` 必须与 `/api/v1/auth/login` 或 `/api/v1/auth/login/code/verify` 返回的一致,挑战有效期 300 秒,同一动态码时间步不能重复使用,恢复码用掉即作废。",
105104 request_body = LoginVerifyRequest,
106105 responses(
107106 (status = 200, description = "登录成功,会话已绑定", body = Envelope<UserLoginResponse>, example = json!({"code":200,"message":"success","data":{"user_id":1024,"username":"ada","displayname":"Ada","mfa_required":false,"challenge":null}})),
@@ -113,7 +112,7 @@
113112 )
114113)]
115114pub async fn login_verify(
116 svc: web::Data<AuthSvc>,
115 svc: web::Data<crate::AuthSvc>,
117116 session: Session,
118117 body: web::Json<LoginVerifyRequest>,
119118 request_id: RequestId,
Renamed lib/auth/src/server/http/login/mod.rs → lib/auth/src/handlers/identity/login/mod.rs View lib/auth/src/handlers/identity/login/mod.rs

No content changes.

Renamed lib/auth/src/server/http/login/service.rs → lib/auth/src/handlers/identity/login/service.rs View lib/auth/src/handlers/identity/login/service.rs
@@ -6,18 +6,18 @@
66use sea_orm::sea_query::{Expr, ExprTrait, Func};
77use sea_orm::{ColumnTrait, Condition, EntityTrait, QueryFilter};
88
9use crate::server::AuthSvc;
10use crate::server::errors::{AuthError, AuthResult};
11use crate::server::http::login::types::{
9use crate::AuthSvc;
10use crate::errors::{AuthError, AuthResult};
11use crate::handlers::identity::login::types::{
1212 LOGIN_ATTEMPTS_MAX, LOGIN_ATTEMPTS_WINDOW_SECS, LoginCodeRequest, LoginCodeResponse,
1313 LoginCodeVerifyRequest, LoginMfaChallenge, LoginVerifyRequest, MFA_CHALLENGE_TTL_SECS,
1414 SESSION_LOGIN_MFA, UserLoginRequest, UserLoginResponse,
1515};
16use crate::server::share::codes::{consume_email_code, store_email_code};
17use crate::server::share::crypto::{now_secs, random_alnum, random_numeric_code};
18use crate::server::share::password::{self, PasswordCheck};
19use crate::server::share::rate::{attempts_load, attempts_reset, attempts_track, rate_limit_once};
20use crate::server::share::validate;
16use crate::share::codes::{consume_email_code, store_email_code};
17use crate::share::crypto::{now_secs, random_alnum, random_numeric_code};
18use crate::share::password::{self, PasswordCheck};
19use crate::share::rate::{attempts_load, attempts_reset, attempts_track, rate_limit_once};
20use crate::share::validate;
2121
2222pub(crate) fn login_code_key(user_id: i64) -> String {
2323 format!("auth:code:login:{user_id}")
@@ -47,7 +47,7 @@
4747 let ip_key = format!(
4848 "auth:rate:login:ip:{}:{}",
4949 client_ip,
50 &crate::server::share::crypto::sha256_hex(identity.as_bytes())[..16]
50 &crate::share::crypto::sha256_hex(identity.as_bytes())[..16]
5151 );
5252 let ip_allowed = attempts_track(
5353 self.cache(),
@@ -61,7 +61,7 @@
6161 Some(user) => format!("auth:rate:login:attempts:{}", user.id),
6262 None => {
6363 // identity 截断后哈希,防任意长度输入撑大 Redis key。
64 let identity_digest = crate::server::share::crypto::sha256_hex(identity.as_bytes());
64 let identity_digest = crate::share::crypto::sha256_hex(identity.as_bytes());
6565 format!(
6666 "auth:rate:login:attempts:anon:{client_ip}:{}",
6767 &identity_digest[..16]
@@ -396,7 +396,7 @@
396396 .exec(self.db().write())
397397 .await?;
398398
399 crate::server::http::me::service::invalidate_me_cache(self.cache(), user.id).await;
399 crate::handlers::identity::me::service::invalidate_me_cache(self.cache(), user.id).await;
400400 self.audit(
401401 session,
402402 user.id,
@@ -423,7 +423,7 @@
423423 .ok_or(AuthError::MfaRequired)?;
424424
425425 if record.mfa_type == MfaType::Totp && record.secret_ciphertext.is_some() {
426 match crate::server::share::mfa::consume_totp(
426 match crate::share::mfa::consume_totp(
427427 self.db(),
428428 &record,
429429 self.auth().totp_encryption_key.as_deref(),
@@ -442,7 +442,7 @@
442442 }
443443
444444 async fn consume_recovery_code(&self, user_id: i64, code: &str) -> AuthResult<()> {
445 crate::server::share::mfa::consume_recovery(self.db(), user_id, code, None).await
445 crate::share::mfa::consume_recovery(self.db(), user_id, code, None).await
446446 }
447447
448448 async fn enabled_mfa(&self, user_id: i64) -> AuthResult<Option<user_mfa::Model>> {
@@ -524,7 +524,7 @@
524524 details: Option<serde_json::Value>,
525525 request_id: &str,
526526 ) -> AuthResult<()> {
527 crate::server::share::audit::entry(
527 crate::share::audit::entry(
528528 self.db(),
529529 user_id,
530530 actor_user_id,
Renamed lib/auth/src/server/http/login/types.rs → lib/auth/src/handlers/identity/login/types.rs View lib/auth/src/handlers/identity/login/types.rs
@@ -1,7 +1,7 @@
11use serde::{Deserialize, Serialize};
22use utoipa::ToSchema;
33
4use crate::server::http::challenge::types::PowProofRequest;
4use crate::handlers::identity::challenge::types::PowProofRequest;
55
66pub const SESSION_LOGIN_MFA: &str = "login_mfa";
77pub const HASH_KEY_SHA256_V1: &str = "sha256-v1";
Renamed lib/auth/src/server/http/logout/handler.rs → lib/auth/src/handlers/identity/logout/handler.rs View lib/auth/src/handlers/identity/logout/handler.rs
@@ -2,24 +2,23 @@
22use actix_web::web;
33
44use crate::envelope::{Envelope, NoData};
5use crate::server::AuthSvc;
6use crate::server::errors::AuthError;
7use crate::server::http::logout::types::LogoutResponse;
5use crate::errors::AuthError;
6use crate::handlers::identity::logout::types::LogoutResponse;
87
98#[utoipa::path(
109 post,
11 path = "/api/v1/oauth/session/logout",
10 path = "/api/v1/auth/session/logout",
1211 tag = "auth",
1312 operation_id = "auth_logout",
1413 summary = "登出",
15 description = "销毁当前会话,并清掉该用户的 `/api/v1/oauth/me` 缓存。匿名调用同样成功,此时 `user_id` 为 `null`。",
14 description = "销毁当前会话,并清掉该用户的 `/api/v1/auth/me` 缓存。匿名调用同样成功,此时 `user_id` 为 `null`。",
1615 responses(
1716 (status = 200, description = "会话已销毁", body = Envelope<LogoutResponse>, example = json!({"code":200,"message":"success","data":{"user_id":1024}})),
1817 (status = 500, description = "服务端故障(`internal`、`database`、`cache`、`session`、`serialization`)", body = Envelope<NoData>, example = json!({"code": "internal", "message": "internal server error"}))
1918 )
2019)]
2120pub async fn logout(
22 svc: web::Data<AuthSvc>,
21 svc: web::Data<crate::AuthSvc>,
2322 session: Session,
2423 request_id: RequestId,
2524) -> Result<Envelope<LogoutResponse>, AuthError> {
Renamed lib/auth/src/server/http/logout/mod.rs → lib/auth/src/handlers/identity/logout/mod.rs View lib/auth/src/handlers/identity/logout/mod.rs

No content changes.

Renamed lib/auth/src/server/http/logout/service.rs → lib/auth/src/handlers/identity/logout/service.rs View lib/auth/src/handlers/identity/logout/service.rs
@@ -3,9 +3,9 @@
33use crate::session::Session;
44use sea_orm::Set;
55
6use crate::server::AuthSvc;
7use crate::server::errors::AuthResult;
8use crate::server::http::logout::types::LogoutResponse;
6use crate::AuthSvc;
7use crate::errors::AuthResult;
8use crate::handlers::identity::logout::types::LogoutResponse;
99
1010impl AuthSvc {
1111 #[tracing::instrument(skip_all, fields(request_id = %request_id, client_ip = ?session.client_ip().ok().flatten()))]
@@ -53,8 +53,8 @@
5353 }
5454
5555 if let Some(uid) = user_id {
56 crate::server::http::me::service::invalidate_me_cache(self.cache(), uid).await;
57 if let Err(error) = crate::server::share::audit::entry(
56 crate::handlers::identity::me::service::invalidate_me_cache(self.cache(), uid).await;
57 if let Err(error) = crate::share::audit::entry(
5858 self.db(),
5959 uid,
6060 Some(uid),
Renamed lib/auth/src/server/http/logout/types.rs → lib/auth/src/handlers/identity/logout/types.rs View lib/auth/src/handlers/identity/logout/types.rs

No content changes.

Renamed lib/auth/src/server/http/me/handler.rs → lib/auth/src/handlers/identity/me/handler.rs View lib/auth/src/handlers/identity/me/handler.rs
@@ -2,13 +2,12 @@
22use actix_web::web;
33
44use crate::envelope::{Envelope, NoData};
5use crate::server::AuthSvc;
6use crate::server::errors::AuthError;
7use crate::server::http::me::types::MeResponse;
5use crate::errors::AuthError;
6use crate::handlers::identity::me::types::MeResponse;
87
98#[utoipa::path(
109 get,
11 path = "/api/v1/oauth/me",
10 path = "/api/v1/auth/me",
1211 tag = "auth",
1312 operation_id = "auth_me",
1413 summary = "当前登录用户",
@@ -20,7 +19,7 @@
2019 )
2120)]
2221pub async fn me(
23 svc: web::Data<AuthSvc>,
22 svc: web::Data<crate::AuthSvc>,
2423 session: Session,
2524 request_id: RequestId,
2625) -> Result<Envelope<MeResponse>, AuthError> {
Renamed lib/auth/src/server/http/me/mod.rs → lib/auth/src/handlers/identity/me/mod.rs View lib/auth/src/handlers/identity/me/mod.rs

No content changes.

Renamed lib/auth/src/server/http/me/service.rs → lib/auth/src/handlers/identity/me/service.rs View lib/auth/src/handlers/identity/me/service.rs
@@ -4,9 +4,9 @@
44use crate::session::Session;
55use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
66
7use crate::server::AuthSvc;
8use crate::server::errors::{AuthError, AuthResult};
9use crate::server::http::me::types::MeResponse;
7use crate::AuthSvc;
8use crate::errors::{AuthError, AuthResult};
9use crate::handlers::identity::me::types::MeResponse;
1010
1111pub const ME_CACHE_TTL_SECS: u64 = 300;
1212const ME_COUNT_TTL_SECS: u64 = 600;
Renamed lib/auth/src/server/http/me/types.rs → lib/auth/src/handlers/identity/me/types.rs View lib/auth/src/handlers/identity/me/types.rs

No content changes.

Renamed lib/auth/src/server/http/organization/error.rs → lib/auth/src/handlers/identity/organization/error.rs View lib/auth/src/handlers/identity/organization/error.rs
@@ -1,4 +1,4 @@
1use crate::server::errors::AuthError;
1use crate::errors::AuthError;
22
33pub(crate) fn reason_error(reason: &'static str, permission: &str) -> AuthError {
44 match reason {
Renamed lib/auth/src/server/http/organization/handler.rs → lib/auth/src/handlers/identity/organization/handler.rs View lib/auth/src/handlers/identity/organization/handler.rs
@@ -2,9 +2,8 @@
22use uuid::Uuid;
33
44use crate::envelope::{Envelope, NoData};
5use crate::server::AuthSvc;
6use crate::server::errors::AuthError;
7use crate::server::http::organization::types::{
5use crate::errors::AuthError;
6use crate::handlers::identity::organization::types::{
87 AddMemberBody, BanMemberBody, CheckPermissionBody, CreateOrganizationBody, CreateRoleBody,
98 InviteSummary, MemberSummary, OrganizationSummary, PermissionCheck, PermissionGrant,
109 RoleSummary, SetMemberRoleBody, SetRolePermissionsBody, UpdateBanBody, UpdateMemberBody,
@@ -146,7 +145,7 @@
146145 )
147146)]
148147pub async fn create_organization(
149 svc: web::Data<AuthSvc>,
148 svc: web::Data<crate::AuthSvc>,
150149 session: Session,
151150 request_id: RequestId,
152151 body: web::Json<CreateOrganizationBody>,
@@ -173,7 +172,7 @@
173172 )
174173)]
175174pub async fn list_organizations(
176 svc: web::Data<AuthSvc>,
175 svc: web::Data<crate::AuthSvc>,
177176 session: Session,
178177 request_id: RequestId,
179178 query: web::Query<OrgListQuery>,
@@ -208,7 +207,7 @@
208207 )
209208)]
210209pub async fn get_organization(
211 svc: web::Data<AuthSvc>,
210 svc: web::Data<crate::AuthSvc>,
212211 session: Session,
213212 request_id: RequestId,
214213 path: web::Path<OrgPath>,
@@ -243,7 +242,7 @@
243242 )
244243)]
245244pub async fn update_organization(
246 svc: web::Data<AuthSvc>,
245 svc: web::Data<crate::AuthSvc>,
247246 session: Session,
248247 request_id: RequestId,
249248 path: web::Path<OrgPath>,
@@ -278,7 +277,7 @@
278277 )
279278)]
280279pub async fn archive_organization(
281 svc: web::Data<AuthSvc>,
280 svc: web::Data<crate::AuthSvc>,
282281 session: Session,
283282 request_id: RequestId,
284283 path: web::Path<OrgPath>,
@@ -312,7 +311,7 @@
312311 )
313312)]
314313pub async fn restore_organization(
315 svc: web::Data<AuthSvc>,
314 svc: web::Data<crate::AuthSvc>,
316315 session: Session,
317316 request_id: RequestId,
318317 path: web::Path<OrgPath>,
@@ -345,7 +344,7 @@
345344 )
346345)]
347346pub async fn delete_organization(
348 svc: web::Data<AuthSvc>,
347 svc: web::Data<crate::AuthSvc>,
349348 session: Session,
350349 request_id: RequestId,
351350 path: web::Path<OrgPath>,
@@ -383,7 +382,7 @@
383382 )
384383)]
385384pub async fn transfer_ownership(
386 svc: web::Data<AuthSvc>,
385 svc: web::Data<crate::AuthSvc>,
387386 session: Session,
388387 request_id: RequestId,
389388 path: web::Path<OrgPath>,
@@ -421,7 +420,7 @@
421420 )
422421)]
423422pub async fn list_members(
424 svc: web::Data<AuthSvc>,
423 svc: web::Data<crate::AuthSvc>,
425424 session: Session,
426425 request_id: RequestId,
427426 path: web::Path<OrgPath>,
@@ -462,7 +461,7 @@
462461 )
463462)]
464463pub async fn get_member(
465 svc: web::Data<AuthSvc>,
464 svc: web::Data<crate::AuthSvc>,
466465 session: Session,
467466 request_id: RequestId,
468467 path: web::Path<OrgMemberPath>,
@@ -497,7 +496,7 @@
497496 )
498497)]
499498pub async fn update_self_member(
500 svc: web::Data<AuthSvc>,
499 svc: web::Data<crate::AuthSvc>,
501500 session: Session,
502501 request_id: RequestId,
503502 path: web::Path<OrgPath>,
@@ -536,7 +535,7 @@
536535 )
537536)]
538537pub async fn update_member(
539 svc: web::Data<AuthSvc>,
538 svc: web::Data<crate::AuthSvc>,
540539 session: Session,
541540 request_id: RequestId,
542541 path: web::Path<OrgMemberPath>,
@@ -573,7 +572,7 @@
573572 )
574573)]
575574pub async fn add_member(
576 svc: web::Data<AuthSvc>,
575 svc: web::Data<crate::AuthSvc>,
577576 session: Session,
578577 request_id: RequestId,
579578 path: web::Path<OrgPath>,
@@ -611,7 +610,7 @@
611610 )
612611)]
613612pub async fn remove_member(
614 svc: web::Data<AuthSvc>,
613 svc: web::Data<crate::AuthSvc>,
615614 session: Session,
616615 request_id: RequestId,
617616 path: web::Path<OrgMemberPath>,
@@ -647,7 +646,7 @@
647646 )
648647)]
649648pub async fn leave_organization(
650 svc: web::Data<AuthSvc>,
649 svc: web::Data<crate::AuthSvc>,
651650 session: Session,
652651 request_id: RequestId,
653652 path: web::Path<OrgPath>,
@@ -680,7 +679,7 @@
680679 )
681680)]
682681pub async fn set_member_role(
683 svc: web::Data<AuthSvc>,
682 svc: web::Data<crate::AuthSvc>,
684683 session: Session,
685684 request_id: RequestId,
686685 path: web::Path<OrgMemberPath>,
@@ -719,7 +718,7 @@
719718 )
720719)]
721720pub async fn ban_member(
722 svc: web::Data<AuthSvc>,
721 svc: web::Data<crate::AuthSvc>,
723722 session: Session,
724723 request_id: RequestId,
725724 path: web::Path<OrgMemberPath>,
@@ -757,7 +756,7 @@
757756 )
758757)]
759758pub async fn unban_member(
760 svc: web::Data<AuthSvc>,
759 svc: web::Data<crate::AuthSvc>,
761760 session: Session,
762761 request_id: RequestId,
763762 path: web::Path<OrgMemberPath>,
@@ -794,7 +793,7 @@
794793 )
795794)]
796795pub async fn list_bans(
797 svc: web::Data<AuthSvc>,
796 svc: web::Data<crate::AuthSvc>,
798797 session: Session,
799798 request_id: RequestId,
800799 path: web::Path<OrgPath>,
@@ -832,7 +831,7 @@
832831 )
833832)]
834833pub async fn get_ban(
835 svc: web::Data<AuthSvc>,
834 svc: web::Data<crate::AuthSvc>,
836835 session: Session,
837836 request_id: RequestId,
838837 path: web::Path<OrgBanPath>,
@@ -869,7 +868,7 @@
869868 )
870869)]
871870pub async fn update_ban(
872 svc: web::Data<AuthSvc>,
871 svc: web::Data<crate::AuthSvc>,
873872 session: Session,
874873 request_id: RequestId,
875874 path: web::Path<OrgBanPath>,
@@ -907,7 +906,7 @@
907906 )
908907)]
909908pub async fn unban_by_user(
910 svc: web::Data<AuthSvc>,
909 svc: web::Data<crate::AuthSvc>,
911910 session: Session,
912911 request_id: RequestId,
913912 path: web::Path<OrgBanPath>,
@@ -952,7 +951,7 @@
952951 )
953952)]
954953pub async fn set_member_permissions(
955 svc: web::Data<AuthSvc>,
954 svc: web::Data<crate::AuthSvc>,
956955 session: Session,
957956 request_id: RequestId,
958957 path: web::Path<OrgMemberPath>,
@@ -988,7 +987,7 @@
988987 )
989988)]
990989pub async fn list_roles(
991 svc: web::Data<AuthSvc>,
990 svc: web::Data<crate::AuthSvc>,
992991 session: Session,
993992 request_id: RequestId,
994993 path: web::Path<OrgPath>,
@@ -1019,7 +1018,7 @@
10191018 )
10201019)]
10211020pub async fn get_role(
1022 svc: web::Data<AuthSvc>,
1021 svc: web::Data<crate::AuthSvc>,
10231022 session: Session,
10241023 request_id: RequestId,
10251024 path: web::Path<OrgRolePath>,
@@ -1053,7 +1052,7 @@
10531052 )
10541053)]
10551054pub async fn create_role(
1056 svc: web::Data<AuthSvc>,
1055 svc: web::Data<crate::AuthSvc>,
10571056 session: Session,
10581057 request_id: RequestId,
10591058 path: web::Path<OrgPath>,
@@ -1091,7 +1090,7 @@
10911090 )
10921091)]
10931092pub async fn update_role(
1094 svc: web::Data<AuthSvc>,
1093 svc: web::Data<crate::AuthSvc>,
10951094 session: Session,
10961095 request_id: RequestId,
10971096 path: web::Path<OrgRolePath>,
@@ -1135,7 +1134,7 @@
11351134 )
11361135)]
11371136pub async fn delete_role(
1138 svc: web::Data<AuthSvc>,
1137 svc: web::Data<crate::AuthSvc>,
11391138 session: Session,
11401139 request_id: RequestId,
11411140 path: web::Path<OrgRolePath>,
@@ -1172,7 +1171,7 @@
11721171 )
11731172)]
11741173pub async fn set_role_permissions(
1175 svc: web::Data<AuthSvc>,
1174 svc: web::Data<crate::AuthSvc>,
11761175 session: Session,
11771176 request_id: RequestId,
11781177 path: web::Path<OrgRolePath>,
@@ -1214,7 +1213,7 @@
12141213 )
12151214)]
12161215pub async fn assign_role(
1217 svc: web::Data<AuthSvc>,
1216 svc: web::Data<crate::AuthSvc>,
12181217 session: Session,
12191218 request_id: RequestId,
12201219 path: web::Path<OrgPath>,
@@ -1250,7 +1249,7 @@
12501249 )
12511250)]
12521251pub async fn unassign_role(
1253 svc: web::Data<AuthSvc>,
1252 svc: web::Data<crate::AuthSvc>,
12541253 session: Session,
12551254 request_id: RequestId,
12561255 path: web::Path<OrgPath>,
@@ -1288,7 +1287,7 @@
12881287 )
12891288)]
12901289pub async fn join_role(
1291 svc: web::Data<AuthSvc>,
1290 svc: web::Data<crate::AuthSvc>,
12921291 session: Session,
12931292 request_id: RequestId,
12941293 path: web::Path<OrgRolePath>,
@@ -1324,7 +1323,7 @@
13241323 )
13251324)]
13261325pub async fn leave_role(
1327 svc: web::Data<AuthSvc>,
1326 svc: web::Data<crate::AuthSvc>,
13281327 session: Session,
13291328 request_id: RequestId,
13301329 path: web::Path<OrgRolePath>,
@@ -1358,7 +1357,7 @@
13581357 )
13591358)]
13601359pub async fn check_permission(
1361 svc: web::Data<AuthSvc>,
1360 svc: web::Data<crate::AuthSvc>,
13621361 session: Session,
13631362 request_id: RequestId,
13641363 path: web::Path<OrgPath>,
@@ -1395,7 +1394,7 @@
13951394 )
13961395)]
13971396pub async fn create_invite(
1398 svc: web::Data<AuthSvc>,
1397 svc: web::Data<crate::AuthSvc>,
13991398 session: Session,
14001399 request_id: RequestId,
14011400 path: web::Path<OrgPath>,
@@ -1436,7 +1435,7 @@
14361435 )
14371436)]
14381437pub async fn list_invites(
1439 svc: web::Data<AuthSvc>,
1438 svc: web::Data<crate::AuthSvc>,
14401439 session: Session,
14411440 request_id: RequestId,
14421441 path: web::Path<OrgPath>,
@@ -1476,7 +1475,7 @@
14761475 )
14771476)]
14781477pub async fn revoke_invite(
1479 svc: web::Data<AuthSvc>,
1478 svc: web::Data<crate::AuthSvc>,
14801479 session: Session,
14811480 request_id: RequestId,
14821481 path: web::Path<OrgInvitePath>,
@@ -1511,7 +1510,7 @@
15111510 )
15121511)]
15131512pub async fn redeem_invite(
1514 svc: web::Data<AuthSvc>,
1513 svc: web::Data<crate::AuthSvc>,
15151514 session: Session,
15161515 request_id: RequestId,
15171516 body: web::Json<RedeemInviteBody>,
Renamed lib/auth/src/server/http/organization/invite.rs → lib/auth/src/handlers/identity/organization/invite.rs View lib/auth/src/handlers/identity/organization/invite.rs
@@ -7,19 +7,19 @@
77};
88use uuid::Uuid;
99
10use crate::AuthSvc;
11use crate::errors::{AuthError, AuthResult};
1012use crate::events::label;
13use crate::handlers::identity::organization::member;
14use crate::handlers::identity::organization::service::{bump_version, normalize_reason};
15use crate::handlers::identity::organization::types::{
16 DEFAULT_INVITE_TTL_SECS, InviteSummary, SYSTEM_ROLE_MEMBER, SYSTEM_ROLE_OWNER,
17};
1118use crate::models::space::{
1219 OrgJoinMethod, OrgMemberRole, OrganizationStatus, organization_invite_redemptions,
1320 organization_invites, organization_roles,
1421};
15use crate::server::AuthSvc;
16use crate::server::errors::{AuthError, AuthResult};
17use crate::server::http::organization::member;
18use crate::server::http::organization::service::{bump_version, normalize_reason};
19use crate::server::http::organization::types::{
20 DEFAULT_INVITE_TTL_SECS, InviteSummary, SYSTEM_ROLE_MEMBER, SYSTEM_ROLE_OWNER,
21};
22use crate::server::share::crypto;
22use crate::share::crypto;
2323
2424impl AuthSvc {
2525 #[allow(clippy::too_many_arguments)]
@@ -98,7 +98,7 @@
9898 }
9999 }
100100 let email = email
101 .map(|value| crate::server::share::validate::validate_email(&value))
101 .map(|value| crate::share::validate::validate_email(&value))
102102 .transpose()?;
103103 let method = match target_user_id {
104104 Some(user_id) => {
@@ -354,7 +354,7 @@
354354 if let Some(role) = role
355355 && role.key != SYSTEM_ROLE_OWNER
356356 {
357 crate::server::http::organization::role::bind_role(
357 crate::handlers::identity::organization::role::bind_role(
358358 &txn,
359359 row.id,
360360 role.id,
@@ -377,7 +377,7 @@
377377 if role.key == SYSTEM_ROLE_OWNER || role.key == SYSTEM_ROLE_MEMBER {
378378 continue;
379379 }
380 crate::server::http::organization::role::bind_role(
380 crate::handlers::identity::organization::role::bind_role(
381381 &txn,
382382 row.id,
383383 role.id,
Renamed lib/auth/src/server/http/organization/join.rs → lib/auth/src/handlers/identity/organization/join.rs View lib/auth/src/handlers/identity/organization/join.rs
@@ -6,16 +6,16 @@
66};
77use uuid::Uuid;
88
9use crate::handlers::identity::organization::types::{
10 AddMemberBody, JoinRequestSummary, JoinRuleSummary, SYSTEM_ROLE_MEMBER, SYSTEM_ROLE_OWNER,
11};
12use crate::handlers::identity::organization::{member, service};
913use crate::models::space::{
1014 OrgJoinMethod, OrgMemberRole, OrganizationJoinRequestStatus, organization_join_requests,
1115 organization_join_rules, organization_members, organization_roles,
1216};
1317use crate::models::users::users;
14use crate::server::http::organization::types::{
15 AddMemberBody, JoinRequestSummary, JoinRuleSummary, SYSTEM_ROLE_MEMBER, SYSTEM_ROLE_OWNER,
16};
17use crate::server::http::organization::{member, service};
18use crate::server::{AuthError, AuthResult, AuthSvc};
18use crate::{AuthError, AuthResult, AuthSvc};
1919
2020fn rule_summary(row: &organization_join_rules::Model) -> JoinRuleSummary {
2121 JoinRuleSummary {
@@ -390,7 +390,7 @@
390390 role_ids: Vec<String>,
391391 request_id: Option<&str>,
392392 ) -> AuthResult<(
393 crate::server::http::organization::types::MemberSummary,
393 crate::handlers::identity::organization::types::MemberSummary,
394394 bool,
395395 )> {
396396 let txn = self.db().write().begin().await?;
Renamed lib/auth/src/server/http/organization/member.rs → lib/auth/src/handlers/identity/organization/member.rs View lib/auth/src/handlers/identity/organization/member.rs
@@ -7,19 +7,19 @@
77};
88use uuid::Uuid;
99
10use crate::AuthSvc;
11use crate::errors::{AuthError, AuthResult};
1012use crate::events::label;
13use crate::handlers::identity::organization::role;
14use crate::handlers::identity::organization::service::{bump_version, normalize_reason};
15use crate::handlers::identity::organization::types::{
16 AddMemberBody, MAX_ALIAS_AVATAR_URL_LEN, MAX_ALIAS_NAME_LEN, MAX_REASON_LEN, MemberSummary,
17 SetMemberRoleBody, UpdateBanBody, UpdateMemberBody, normalize_alias,
18};
1119use crate::models::space::{
1220 OrgJoinMethod, OrgMemberRole, organization_member_permissions, organization_members,
1321};
1422use crate::models::users::users;
15use crate::server::AuthSvc;
16use crate::server::errors::{AuthError, AuthResult};
17use crate::server::http::organization::role;
18use crate::server::http::organization::service::{bump_version, normalize_reason};
19use crate::server::http::organization::types::{
20 AddMemberBody, MAX_ALIAS_AVATAR_URL_LEN, MAX_ALIAS_NAME_LEN, MAX_REASON_LEN, MemberSummary,
21 SetMemberRoleBody, UpdateBanBody, UpdateMemberBody, normalize_alias,
22};
2323
2424pub(crate) async fn find_any<C: ConnectionTrait>(
2525 db: &C,
Renamed lib/auth/src/server/http/organization/mod.rs → lib/auth/src/handlers/identity/organization/mod.rs View lib/auth/src/handlers/identity/organization/mod.rs

No content changes.

Renamed lib/auth/src/server/http/organization/permission.rs → lib/auth/src/handlers/identity/organization/permission.rs View lib/auth/src/handlers/identity/organization/permission.rs
@@ -4,17 +4,17 @@
44use serde::{Deserialize, Serialize};
55use uuid::Uuid;
66
7use crate::AuthSvc;
8use crate::errors::{AuthError, AuthResult};
9use crate::handlers::identity::organization::types::{
10 PERMISSION_CACHE_TTL_SECS, PERMISSIONS, PermissionCheck, Resolution, SYSTEM_ROLE_ADMIN,
11 SYSTEM_ROLE_OWNER, is_admin_permission, is_known_permission,
12};
13use crate::handlers::identity::organization::{member, role, service};
714use crate::models::space::{
815 OrgMemberRole, PermissionEffect, organization_member_permissions, organization_member_roles,
916 organization_members, organization_role_permissions,
1017};
11use crate::server::AuthSvc;
12use crate::server::errors::{AuthError, AuthResult};
13use crate::server::http::organization::types::{
14 PERMISSION_CACHE_TTL_SECS, PERMISSIONS, PermissionCheck, Resolution, SYSTEM_ROLE_ADMIN,
15 SYSTEM_ROLE_OWNER, is_admin_permission, is_known_permission,
16};
17use crate::server::http::organization::{member, role, service};
1818
1919/// 缓存进 Redis 的判定核心。版本号相同的组织共享同一份核心,
2020/// 任何写操作推进 `organizations.version` 都会让整份核心失效。
Renamed lib/auth/src/server/http/organization/role.rs → lib/auth/src/handlers/identity/organization/role.rs View lib/auth/src/handlers/identity/organization/role.rs
@@ -6,21 +6,21 @@
66};
77use uuid::Uuid;
88
9use crate::AuthSvc;
10use crate::errors::{AuthError, AuthResult};
911use crate::events::label;
10use crate::models::space::{
11 PermissionEffect, organization_member_roles, organization_role_permissions, organization_roles,
12};
13use crate::server::AuthSvc;
14use crate::server::errors::{AuthError, AuthResult};
15use crate::server::http::organization::service::{
12use crate::handlers::identity::organization::service::{
1613 bump_version, normalize_role_key, normalize_role_name,
1714};
18use crate::server::http::organization::types::{
15use crate::handlers::identity::organization::types::{
1916 CreateRoleBody, MemberSummary, PermissionGrant, PermissionGrant as Grant, RoleSummary,
2017 SYSTEM_ROLE_ADMIN, SYSTEM_ROLE_MEMBER, SYSTEM_ROLE_OWNER, SetRolePermissionsBody,
2118 is_known_permission,
2219};
23use crate::server::http::organization::{error, member};
20use crate::handlers::identity::organization::{error, member};
21use crate::models::space::{
22 PermissionEffect, organization_member_roles, organization_role_permissions, organization_roles,
23};
2424
2525/// 内置角色的固有授权。OWNER 不需要显式授权(解析链直接短路),
2626/// ADMIN 的固有集写在 permissions.rs 的白名单里,这里只落 MEMBER 的默认值。
@@ -538,7 +538,7 @@
538538 role_key: &str,
539539 request_id: Option<&str>,
540540 ) -> AuthResult<Vec<String>> {
541 let member = crate::server::http::organization::member::load_by_id(
541 let member = crate::handlers::identity::organization::member::load_by_id(
542542 self.db().read(),
543543 organization_id,
544544 member_id,
@@ -607,7 +607,7 @@
607607 if role.key == SYSTEM_ROLE_OWNER {
608608 return Err(AuthError::OrganizationRoleSystem);
609609 }
610 let member = crate::server::http::organization::member::load_by_id(
610 let member = crate::handlers::identity::organization::member::load_by_id(
611611 self.db().read(),
612612 organization_id,
613613 member_id,
@@ -669,7 +669,7 @@
669669 request_id,
670670 )
671671 .await?;
672 let member = crate::server::http::organization::member::load_by_id(
672 let member = crate::handlers::identity::organization::member::load_by_id(
673673 self.db().read(),
674674 organization_id,
675675 member_id,
Renamed lib/auth/src/server/http/organization/service.rs → lib/auth/src/handlers/identity/organization/service.rs View lib/auth/src/handlers/identity/organization/service.rs
@@ -7,22 +7,22 @@
77};
88use uuid::Uuid;
99
10use crate::AuthSvc;
11use crate::errors::{AuthError, AuthResult};
1012use crate::events::{Labels, label};
13use crate::handlers::identity::organization::types::{
14 CreateOrganizationBody, MAX_ORG_DESCRIPTION_LEN, MAX_ORG_NAME_LEN, MAX_PERMISSION_LEN,
15 MAX_ROLE_KEY_LEN, MAX_ROLE_NAME_LEN, MAX_SLUG_LEN, MemberSummary, OrganizationSummary,
16 PermissionGrant, RoleSummary, SYSTEM_ROLE_ADMIN, SYSTEM_ROLE_OWNER, UpdateOrganizationBody,
17 is_known_permission,
18};
19use crate::handlers::identity::organization::{error, member, permission, role};
1120use crate::models::space::{
1221 OrgMemberRole, OrganizationStatus, PermissionEffect, organization_invite_redemptions,
1322 organization_invites, organization_member_permissions, organization_member_roles,
1423 organization_members, organization_role_permissions, organization_roles, organizations,
1524};
1625use crate::models::users::users;
17use crate::server::AuthSvc;
18use crate::server::errors::{AuthError, AuthResult};
19use crate::server::http::organization::types::{
20 CreateOrganizationBody, MAX_ORG_DESCRIPTION_LEN, MAX_ORG_NAME_LEN, MAX_PERMISSION_LEN,
21 MAX_ROLE_KEY_LEN, MAX_ROLE_NAME_LEN, MAX_SLUG_LEN, MemberSummary, OrganizationSummary,
22 PermissionGrant, RoleSummary, SYSTEM_ROLE_ADMIN, SYSTEM_ROLE_OWNER, UpdateOrganizationBody,
23 is_known_permission,
24};
25use crate::server::http::organization::{error, member, permission, role};
2626
2727pub(crate) fn perm_cache_prefix(organization_id: Uuid, user_id: i64) -> String {
2828 format!("auth:org:perm:{organization_id}:{user_id}")
Renamed lib/auth/src/server/http/organization/types.rs → lib/auth/src/handlers/identity/organization/types.rs View lib/auth/src/handlers/identity/organization/types.rs

No content changes.

Renamed lib/auth/src/server/http/password_reset/handler.rs → lib/auth/src/handlers/identity/password_reset/handler.rs View lib/auth/src/handlers/identity/password_reset/handler.rs
@@ -2,17 +2,16 @@
22use actix_web::web;
33
44use crate::envelope::{Envelope, NoData};
5use crate::server::AuthSvc;
6use crate::server::errors::AuthError;
5use crate::errors::AuthError;
76
8use crate::server::http::password_reset::types::{
7use crate::handlers::identity::password_reset::types::{
98 PasswordResetConfirmRequest, PasswordResetConfirmResponse, PasswordResetRequest,
109 PasswordResetResponse,
1110};
1211
1312#[utoipa::path(
1413 post,
15 path = "/api/v1/oauth/password/reset",
14 path = "/api/v1/auth/password/reset",
1615 tag = "auth",
1716 operation_id = "auth_password_reset_request",
1817 summary = "申请密码重置",
@@ -46,7 +45,7 @@
4645 )
4746)]
4847pub async fn password_reset_request(
49 svc: web::Data<AuthSvc>,
48 svc: web::Data<crate::AuthSvc>,
5049 session: Session,
5150 body: web::Json<PasswordResetRequest>,
5251 request_id: RequestId,
@@ -68,7 +67,7 @@
6867/// 并与 `security_version` 绑定;成功后作废该用户其余未消费的重置令牌并递增 `security_version`。
6968#[utoipa::path(
7069 post,
71 path = "/api/v1/oauth/password/reset/confirm",
70 path = "/api/v1/auth/password/reset/confirm",
7271 tag = "auth",
7372 operation_id = "auth_password_reset",
7473 summary = "确认密码重置",
@@ -102,7 +101,7 @@
102101 )
103102)]
104103pub async fn password_reset(
105 svc: web::Data<AuthSvc>,
104 svc: web::Data<crate::AuthSvc>,
106105 session: Session,
107106 body: web::Json<PasswordResetConfirmRequest>,
108107 request_id: RequestId,
Renamed lib/auth/src/server/http/password_reset/mod.rs → lib/auth/src/handlers/identity/password_reset/mod.rs View lib/auth/src/handlers/identity/password_reset/mod.rs

No content changes.

Renamed lib/auth/src/server/http/password_reset/service.rs → lib/auth/src/handlers/identity/password_reset/service.rs View lib/auth/src/handlers/identity/password_reset/service.rs
@@ -5,19 +5,19 @@
55use sea_orm::sea_query::{Expr, ExprTrait, Func};
66use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter};
77
8use crate::AuthSvc;
9use crate::errors::{AuthError, AuthResult};
10use crate::handlers::identity::password_reset::types::{
11 PasswordResetConfirmRequest, PasswordResetConfirmResponse, PasswordResetRequest,
12 PasswordResetResponse,
13};
814use crate::models::users::user_events::UserEventType;
915use crate::models::users::user_verification_tokens::{self, VerificationPurpose};
1016use crate::models::users::users;
11use crate::server::AuthSvc;
12use crate::server::errors::{AuthError, AuthResult};
13use crate::server::http::password_reset::types::{
14 PasswordResetConfirmRequest, PasswordResetConfirmResponse, PasswordResetRequest,
15 PasswordResetResponse,
16};
17use crate::server::share::codes::{consume_email_code, store_email_code};
18use crate::server::share::crypto::{random_b64url, sha256_hex};
19use crate::server::share::password::{hash as hash_password, validate_new_password};
20use crate::server::share::{rate, validate};
17use crate::share::codes::{consume_email_code, store_email_code};
18use crate::share::crypto::{random_b64url, sha256_hex};
19use crate::share::password::{hash as hash_password, validate_new_password};
20use crate::share::{rate, validate};
2121
2222const RESET_CODE_KEY_PREFIX: &str = "auth:code:reset:";
2323const RESET_RATE_KEY_PREFIX: &str = "auth:rate:reset:";
@@ -107,7 +107,7 @@
107107 self.publish_mail(&email, subject, &text, &html, Some(request_id.as_str()))
108108 .await?;
109109
110 crate::server::share::audit::entry(
110 crate::share::audit::entry(
111111 self.db(),
112112 user.id,
113113 Some(user.id),
@@ -229,7 +229,7 @@
229229
230230 self.revoke_pending_resets(user.id, now).await?;
231231
232 crate::server::share::audit::entry(
232 crate::share::audit::entry(
233233 self.db(),
234234 user.id,
235235 Some(user.id),
Renamed lib/auth/src/server/http/password_reset/types.rs → lib/auth/src/handlers/identity/password_reset/types.rs View lib/auth/src/handlers/identity/password_reset/types.rs
@@ -1,7 +1,7 @@
11use serde::{Deserialize, Serialize};
22use utoipa::ToSchema;
33
4use crate::server::http::challenge::types::PowProofRequest;
4use crate::handlers::identity::challenge::types::PowProofRequest;
55
66#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
77pub struct PasswordResetRequest {
Renamed lib/auth/src/server/http/register/age.rs → lib/auth/src/handlers/identity/register/age.rs View lib/auth/src/handlers/identity/register/age.rs
@@ -1,6 +1,6 @@
11use chrono::{Datelike, NaiveDate};
22
3use crate::server::errors::{AuthError, AuthResult};
3use crate::errors::{AuthError, AuthResult};
44
55pub const EARLIEST_BIRTH_YEAR: i32 = 1900;
66
@@ -13,5 +13,5 @@
1313 if parsed.year() < EARLIEST_BIRTH_YEAR {
1414 return Err(AuthError::InvalidBirthDate);
1515 }
16 crate::server::share::validate::validate_age(Some(parsed), min_age)
16 crate::share::validate::validate_age(Some(parsed), min_age)
1717}
Renamed lib/auth/src/server/http/register/handler.rs → lib/auth/src/handlers/identity/register/handler.rs View lib/auth/src/handlers/identity/register/handler.rs
@@ -2,16 +2,15 @@
22use actix_web::web;
33
44use crate::envelope::{Envelope, NoData};
5use crate::server::AuthSvc;
6use crate::server::errors::AuthError;
5use crate::errors::AuthError;
76
8use crate::server::http::register::types::{
7use crate::handlers::identity::register::types::{
98 RegisterCodeRequest, RegisterCodeResponse, UserRegisterRequest, UserRegisterResponse,
109};
1110
1211#[utoipa::path(
1312 post,
14 path = "/api/v1/oauth/register/code",
13 path = "/api/v1/auth/register/code",
1514 tag = "auth",
1615 operation_id = "auth_register_code",
1716 summary = "发送注册验证码",
@@ -51,7 +50,7 @@
5150 )
5251)]
5352pub async fn register_code(
54 svc: web::Data<AuthSvc>,
53 svc: web::Data<crate::AuthSvc>,
5554 session: Session,
5655 body: web::Json<RegisterCodeRequest>,
5756 request_id: RequestId,
@@ -64,7 +63,7 @@
6463
6564#[utoipa::path(
6665 post,
67 path = "/api/v1/oauth/register",
66 path = "/api/v1/auth/register",
6867 tag = "auth",
6968 operation_id = "auth_register",
7069 summary = "提交注册",
@@ -110,7 +109,7 @@
110109 )
111110)]
112111pub async fn register(
113 svc: web::Data<AuthSvc>,
112 svc: web::Data<crate::AuthSvc>,
114113 session: Session,
115114 body: web::Json<UserRegisterRequest>,
116115 request_id: RequestId,
Renamed lib/auth/src/server/http/register/mod.rs → lib/auth/src/handlers/identity/register/mod.rs View lib/auth/src/handlers/identity/register/mod.rs

No content changes.

Renamed lib/auth/src/server/http/register/service.rs → lib/auth/src/handlers/identity/register/service.rs View lib/auth/src/handlers/identity/register/service.rs
@@ -6,18 +6,18 @@
66use sea_orm::sea_query::{Expr, ExprTrait, Func};
77use sea_orm::{ActiveModelTrait, ColumnTrait, Condition, EntityTrait, QueryFilter, SqlErr};
88
9use crate::AuthSvc;
10use crate::errors::{AuthError, AuthResult};
11use crate::handlers::identity::register::age;
12use crate::handlers::identity::register::types::{
13 RegisterCodeRequest, RegisterCodeResponse, UserRegisterRequest, UserRegisterResponse,
14};
915use crate::models::users::user_events::UserEventType;
1016use crate::models::users::users;
11use crate::server::AuthSvc;
12use crate::server::errors::{AuthError, AuthResult};
13use crate::server::http::register::age;
14use crate::server::http::register::types::{
15 RegisterCodeRequest, RegisterCodeResponse, UserRegisterRequest, UserRegisterResponse,
16};
17use crate::server::share::codes::{consume_email_code, store_email_code};
18use crate::server::share::password::{hash as hash_password, validate_new_password};
19use crate::server::share::validate::{validate_display_name, validate_email, validate_username};
20use crate::server::share::{crypto, rate};
17use crate::share::codes::{consume_email_code, store_email_code};
18use crate::share::password::{hash as hash_password, validate_new_password};
19use crate::share::validate::{validate_display_name, validate_email, validate_username};
20use crate::share::{crypto, rate};
2121
2222const REGISTER_CODE_KEY_PREFIX: &str = "auth:code:register:";
2323const REGISTER_RATE_KEY_PREFIX: &str = "auth:rate:register:";
@@ -179,7 +179,7 @@
179179 self.bind_auth_session(&session, &user, false, Some(request_id.as_str()))
180180 .await?;
181181
182 crate::server::share::audit::entry(
182 crate::share::audit::entry(
183183 self.db(),
184184 user.id,
185185 Some(user.id),
Renamed lib/auth/src/server/http/register/types.rs → lib/auth/src/handlers/identity/register/types.rs View lib/auth/src/handlers/identity/register/types.rs
@@ -1,7 +1,7 @@
11use serde::{Deserialize, Serialize};
22use utoipa::ToSchema;
33
4use crate::server::http::challenge::types::PowProofRequest;
4use crate::handlers::identity::challenge::types::PowProofRequest;
55
66#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
77pub struct RegisterCodeRequest {
Renamed lib/auth/src/server/http/ssh_key/handler.rs → lib/auth/src/handlers/identity/ssh_key/handler.rs View lib/auth/src/handlers/identity/ssh_key/handler.rs
@@ -3,14 +3,13 @@
33use uuid::Uuid;
44
55use crate::envelope::{Envelope, NoData};
6use crate::server::AuthSvc;
7use crate::server::errors::AuthError;
8use crate::server::http::ssh_key::types::{SshKeyAddRequest, SshKeySummary};
9use crate::server::share::types::ListQuery;
6use crate::errors::AuthError;
7use crate::handlers::identity::ssh_key::types::{SshKeyAddRequest, SshKeySummary};
8use crate::share::types::ListQuery;
109
1110#[utoipa::path(
1211 get,
13 path = "/api/v1/oauth/ssh-keys",
12 path = "/api/v1/auth/ssh-keys",
1413 tag = "auth",
1514 operation_id = "auth_ssh_key_list",
1615 summary = "列示 SSH 公钥",
@@ -41,7 +40,7 @@
4140 )
4241)]
4342pub async fn ssh_key_list(
44 svc: web::Data<AuthSvc>,
43 svc: web::Data<crate::AuthSvc>,
4544 session: Session,
4645 query: web::Query<ListQuery>,
4746 request_id: RequestId,
@@ -54,7 +53,7 @@
5453
5554#[utoipa::path(
5655 post,
57 path = "/api/v1/oauth/ssh-keys",
56 path = "/api/v1/auth/ssh-keys",
5857 tag = "auth",
5958 operation_id = "auth_ssh_key_add",
6059 summary = "添加 SSH 公钥",
@@ -94,7 +93,7 @@
9493 )
9594)]
9695pub async fn ssh_key_add(
97 svc: web::Data<AuthSvc>,
96 svc: web::Data<crate::AuthSvc>,
9897 session: Session,
9998 body: web::Json<SshKeyAddRequest>,
10099 request_id: RequestId,
@@ -116,7 +115,7 @@
116115/// 只动自己名下的记录,已在吊销状态时重复调用照样返回成功;吊销与递增账号 `security_version`
117116#[utoipa::path(
118117 delete,
119 path = "/api/v1/oauth/ssh-keys/{key_id}",
118 path = "/api/v1/auth/ssh-keys/{key_id}",
120119 tag = "auth",
121120 operation_id = "auth_ssh_key_delete",
122121 summary = "吊销 SSH 公钥",
@@ -152,7 +151,7 @@
152151 )
153152)]
154153pub async fn ssh_key_delete(
155 svc: web::Data<AuthSvc>,
154 svc: web::Data<crate::AuthSvc>,
156155 session: Session,
157156 key_id: web::Path<Uuid>,
158157 request_id: RequestId,
Renamed lib/auth/src/server/http/ssh_key/mod.rs → lib/auth/src/handlers/identity/ssh_key/mod.rs View lib/auth/src/handlers/identity/ssh_key/mod.rs

No content changes.

Renamed lib/auth/src/server/http/ssh_key/service.rs → lib/auth/src/handlers/identity/ssh_key/service.rs View lib/auth/src/handlers/identity/ssh_key/service.rs
@@ -14,14 +14,14 @@
1414};
1515use uuid::Uuid;
1616
17use crate::server::AuthSvc;
18use crate::server::errors::{AuthError, AuthResult};
19use crate::server::http::ssh_key::types::{
17use crate::AuthSvc;
18use crate::errors::{AuthError, AuthResult};
19use crate::handlers::identity::ssh_key::types::{
2020 MAX_SSH_KEY_DESCRIPTION_LEN, MAX_SSH_KEY_NAME_LEN, MAX_SSH_PUBLIC_KEY_LEN,
2121 MIN_SSH_PUBLIC_KEY_LEN, SshKeyAddRequest, SshKeyAlgorithm, SshKeySummary,
2222};
23use crate::server::share::crypto;
24use crate::server::share::types::ListQuery;
23use crate::share::crypto;
24use crate::share::types::ListQuery;
2525
2626pub(crate) fn parse_public_key(raw: &str) -> AuthResult<(PublicKeyType, String)> {
2727 let key_line = raw.trim();
Renamed lib/auth/src/server/http/ssh_key/types.rs → lib/auth/src/handlers/identity/ssh_key/types.rs View lib/auth/src/handlers/identity/ssh_key/types.rs

No content changes.

Renamed lib/auth/src/server/http/mod.rs → lib/auth/src/handlers/identity/mod.rs View lib/auth/src/handlers/identity/mod.rs
@@ -2,9 +2,7 @@
22pub mod login;
33pub mod logout;
44pub mod me;
5pub mod openapi;
65pub mod organization;
76pub mod password_reset;
87pub mod register;
9pub mod router;
108pub mod ssh_key;
Added lib/auth/src/handlers/mod.rs View lib/auth/src/handlers/mod.rs
@@ -0,0 +1,2 @@
1pub mod account;
2pub mod identity;
Renamed lib/auth/src/client/local/agreement.rs → lib/auth/src/internal/bridge/agreement.rs View lib/auth/src/internal/bridge/agreement.rs
@@ -1,10 +1,10 @@
1use crate::server::AuthSvc;
2use crate::server::grpc::context::RequestMeta;
1use crate::AuthSvc;
2use crate::internal::context::RequestMeta;
33use tonic::{Request, Response, Status};
44use uuid::Uuid;
55
6use crate::internal::agreement::AgreementApi;
67use crate::protos::auth::v1::agreement_service_server::AgreementService;
7use crate::server::grpc::agreement::AgreementApi;
88
99pub struct AgreementClient {
1010 api: AgreementApi,
Renamed lib/auth/src/client/local/authority.rs → lib/auth/src/internal/bridge/authority.rs View lib/auth/src/internal/bridge/authority.rs
@@ -1,10 +1,10 @@
1use crate::server::AuthSvc;
2use crate::server::grpc::context::RequestMeta;
1use crate::AuthSvc;
2use crate::internal::context::RequestMeta;
33use tonic::{Request, Response, Status};
44use uuid::Uuid;
55
6use crate::internal::authority::AuthorityApi;
67use crate::protos::auth::internal::v1::authority_service_server::AuthorityService;
7use crate::server::grpc::authority::AuthorityApi;
88
99pub struct AuthorityClient {
1010 api: AuthorityApi,
Renamed lib/auth/src/client/local/authorization.rs → lib/auth/src/internal/bridge/authorization.rs View lib/auth/src/internal/bridge/authorization.rs
@@ -1,10 +1,10 @@
1use crate::server::AuthSvc;
2use crate::server::grpc::context::RequestMeta;
1use crate::AuthSvc;
2use crate::internal::context::RequestMeta;
33use tonic::{Request, Response, Status};
44use uuid::Uuid;
55
6use crate::internal::authorization::AuthorizationApi;
67use crate::protos::auth::v1::authorization_service_server::AuthorizationService;
7use crate::server::grpc::authorization::AuthorizationApi;
88
99pub struct AuthorizationClient {
1010 api: AuthorizationApi,
Renamed lib/auth/src/client/local/identity.rs → lib/auth/src/internal/bridge/identity.rs View lib/auth/src/internal/bridge/identity.rs
@@ -1,10 +1,10 @@
1use crate::server::AuthSvc;
2use crate::server::grpc::context::RequestMeta;
1use crate::AuthSvc;
2use crate::internal::context::RequestMeta;
33use tonic::{Request, Response, Status};
44use uuid::Uuid;
55
6use crate::internal::identity::IdentityApi;
67use crate::protos::auth::v1::identity_service_server::IdentityService;
7use crate::server::grpc::identity::IdentityApi;
88
99pub struct IdentityClient {
1010 api: IdentityApi,
Renamed lib/auth/src/client/local/mfa.rs → lib/auth/src/internal/bridge/mfa.rs View lib/auth/src/internal/bridge/mfa.rs
@@ -1,10 +1,10 @@
1use crate::server::AuthSvc;
2use crate::server::grpc::context::RequestMeta;
1use crate::AuthSvc;
2use crate::internal::context::RequestMeta;
33use tonic::{Request, Response, Status};
44use uuid::Uuid;
55
6use crate::internal::mfa::MfaApi;
67use crate::protos::auth::v1::mfa_service_server::MfaService;
7use crate::server::grpc::mfa::MfaApi;
88
99pub struct MfaClient {
1010 api: MfaApi,
Renamed lib/auth/src/client/local/mod.rs → lib/auth/src/internal/bridge/mod.rs View lib/auth/src/internal/bridge/mod.rs

No content changes.

Renamed lib/auth/src/client/local/organization.rs → lib/auth/src/internal/bridge/organization.rs View lib/auth/src/internal/bridge/organization.rs
@@ -1,10 +1,10 @@
1use crate::server::AuthSvc;
2use crate::server::grpc::context::RequestMeta;
1use crate::AuthSvc;
2use crate::internal::context::RequestMeta;
33use tonic::{Request, Response, Status};
44use uuid::Uuid;
55
6use crate::internal::organization::OrganizationApi;
67use crate::protos::auth::v1::organization_service_server::OrganizationService;
7use crate::server::grpc::organization::OrganizationApi;
88
99pub struct OrganizationClient {
1010 api: OrganizationApi,
Renamed lib/auth/src/client/local/session.rs → lib/auth/src/internal/bridge/session.rs View lib/auth/src/internal/bridge/session.rs
@@ -1,10 +1,10 @@
1use crate::server::AuthSvc;
2use crate::server::grpc::context::RequestMeta;
1use crate::AuthSvc;
2use crate::internal::context::RequestMeta;
33use tonic::{Request, Response, Status};
44use uuid::Uuid;
55
6use crate::internal::session::SessionApi;
67use crate::protos::auth::v1::session_service_server::SessionService;
7use crate::server::grpc::session::SessionApi;
88
99pub struct SessionClient {
1010 api: SessionApi,
Renamed lib/auth/src/client/local/ssh_key.rs → lib/auth/src/internal/bridge/ssh_key.rs View lib/auth/src/internal/bridge/ssh_key.rs
@@ -1,10 +1,10 @@
1use crate::server::AuthSvc;
2use crate::server::grpc::context::RequestMeta;
1use crate::AuthSvc;
2use crate::internal::context::RequestMeta;
33use tonic::{Request, Response, Status};
44use uuid::Uuid;
55
6use crate::internal::ssh_key::SshKeyApi;
67use crate::protos::auth::v1::ssh_key_service_server::SshKeyService;
7use crate::server::grpc::ssh_key::SshKeyApi;
88
99pub struct SshKeyClient {
1010 api: SshKeyApi,
Renamed lib/auth/src/server/grpc/agreement.rs → lib/auth/src/internal/agreement.rs View lib/auth/src/internal/agreement.rs
@@ -8,9 +8,9 @@
88use serde::{Deserialize, Serialize};
99use tonic::{Request, Response, Status};
1010
11use crate::server::AuthSvc;
12use crate::server::errors::{AuthError, AuthResult};
13use crate::server::grpc::{context, convert, error, store};
11use crate::AuthSvc;
12use crate::errors::{AuthError, AuthResult};
13use crate::internal::{context, convert, error, store};
1414
1515pub const DEFAULT_LOCALE: &str = "zh-CN";
1616pub const SUMMARY_MAX_CHARS: usize = 160;
@@ -43,7 +43,7 @@
4343) -> AuthResult<()> {
4444 let record = Acceptance {
4545 version: version.to_owned(),
46 accepted_at: crate::server::share::crypto::now_secs(),
46 accepted_at: crate::share::crypto::now_secs(),
4747 };
4848 cache
4949 .set_json(&acceptance_key(user_id, key), &record)
Renamed lib/auth/src/server/grpc/audit.rs → lib/auth/src/internal/audit.rs View lib/auth/src/internal/audit.rs
@@ -9,9 +9,9 @@
99};
1010use tonic::{Request, Response, Status};
1111
12use crate::server::AuthSvc;
13use crate::server::errors::AuthError;
14use crate::server::grpc::{context, convert, error, store};
12use crate::AuthSvc;
13use crate::errors::AuthError;
14use crate::internal::{context, convert, error, store};
1515
1616const DEFAULT_EXPORT_BATCH: u32 = 500;
1717const MAX_EXPORT_BATCH: u32 = 5_000;
Renamed lib/auth/src/server/grpc/authority.rs → lib/auth/src/internal/authority.rs View lib/auth/src/internal/authority.rs
@@ -13,9 +13,9 @@
1313use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect};
1414use tonic::{Request, Response, Status};
1515
16use crate::server::AuthSvc;
17use crate::server::errors::AuthError;
18use crate::server::grpc::{context, convert, error, store};
16use crate::AuthSvc;
17use crate::errors::AuthError;
18use crate::internal::{context, convert, error, store};
1919
2020pub struct AuthorityApi {
2121 svc: AuthSvc,
@@ -319,7 +319,7 @@
319319 if user_ids.is_empty() {
320320 return Ok(Response::new(BatchGetUserResponse { users: Vec::new() }));
321321 }
322 if user_ids.len() > crate::server::grpc::store::MAX_BATCH_USER_IDS {
322 if user_ids.len() > crate::internal::store::MAX_BATCH_USER_IDS {
323323 return Err(error::invalid_argument(
324324 "too many user ids in one batch",
325325 request_id,
@@ -430,7 +430,7 @@
430430 payload: ListUsersRequest,
431431 ) -> Result<Response<ListUsersResponse>, Status> {
432432 let filter = payload.filter.unwrap_or_default();
433 if filter.user_ids.len() > crate::server::grpc::store::MAX_BATCH_USER_IDS {
433 if filter.user_ids.len() > crate::internal::store::MAX_BATCH_USER_IDS {
434434 return Err(error::invalid_argument(
435435 "too many user ids in one filter",
436436 request_id,
@@ -451,9 +451,7 @@
451451 .offset(offset)
452452 .all(self.svc.db().read())
453453 .await
454 .map_err(|value| {
455 error::status(&crate::server::errors::AuthError::from(value), request_id)
456 })?;
454 .map_err(|value| error::status(&crate::errors::AuthError::from(value), request_id))?;
457455 let next_page_token = if convert::has_more(offset, page_size, rows.len()) {
458456 store::page_token(offset + rows.len() as u64)
459457 } else {
Renamed lib/auth/src/server/grpc/authorization.rs → lib/auth/src/internal/authorization.rs View lib/auth/src/internal/authorization.rs
@@ -10,10 +10,10 @@
1010use sea_orm::EntityTrait;
1111use tonic::{Request, Response, Status};
1212
13use crate::server::AuthSvc;
14use crate::server::errors::{AuthError, AuthResult};
15use crate::server::grpc::store::ALLOWED_APP_SCOPES;
16use crate::server::grpc::{agreement, context, error, store};
13use crate::AuthSvc;
14use crate::errors::{AuthError, AuthResult};
15use crate::internal::store::ALLOWED_APP_SCOPES;
16use crate::internal::{agreement, context, error, store};
1717
1818const MAX_BATCH_CAPABILITIES: usize = 32;
1919const ADMIN_CACHE_TTL_SECS: u64 = 300;
Renamed lib/auth/src/server/grpc/cache_maintenance.rs → lib/auth/src/internal/cache_maintenance.rs View lib/auth/src/internal/cache_maintenance.rs
@@ -8,10 +8,10 @@
88use crate::protos::auth::internal::v1::*;
99use tonic::{Request, Response, Status};
1010
11use crate::server::AuthSvc;
12use crate::server::errors::AuthError;
13use crate::server::grpc::{context, error, store};
14use crate::server::http::me::types::MeResponse;
11use crate::AuthSvc;
12use crate::errors::AuthError;
13use crate::handlers::identity::me::types::MeResponse;
14use crate::internal::{context, error, store};
1515
1616const USER_SCOPED_PREFIXES: [&str; 3] = ["auth:me:", "auth:admin:", "auth:rate:mfa:"];
1717
Renamed lib/auth/src/server/grpc/context.rs → lib/auth/src/internal/context.rs View lib/auth/src/internal/context.rs
@@ -12,7 +12,7 @@
1212use tonic::{Request, Status};
1313use uuid::Uuid;
1414
15use crate::server::grpc::error;
15use crate::internal::error;
1616
1717pub const RPC_KEY_METADATA: &str = "x-rpc-key";
1818pub const REQUEST_ID_METADATA: &str = "gitdata-request-id";
@@ -106,10 +106,7 @@
106106 ));
107107 }
108108 if presented.is_empty()
109 || !crate::server::share::crypto::constant_time_eq(
110 presented.as_bytes(),
111 self.key.as_bytes(),
112 )
109 || !crate::share::crypto::constant_time_eq(presented.as_bytes(), self.key.as_bytes())
113110 {
114111 return Err(error::unauthenticated(
115112 "rpc key is missing or invalid",
Renamed lib/auth/src/server/grpc/convert.rs → lib/auth/src/internal/convert.rs View lib/auth/src/internal/convert.rs
@@ -3,7 +3,7 @@
33use chrono::{DateTime, NaiveDate, TimeZone, Utc};
44use prost_types::Timestamp;
55
6use crate::server::grpc::store;
6use crate::internal::store;
77
88pub const DEFAULT_PAGE_SIZE: i32 = 20;
99pub const MAX_PAGE_SIZE: i32 = 100;
Renamed lib/auth/src/server/grpc/error.rs → lib/auth/src/internal/error.rs View lib/auth/src/internal/error.rs
@@ -4,7 +4,7 @@
44use prost_types::Any;
55use tonic::Status;
66
7use crate::server::errors::AuthError;
7use crate::errors::AuthError;
88
99/// 基础设施故障:调用方必须按可重试错误处理,不能当业务结论报告。
1010pub fn is_infrastructure(error: &AuthError) -> bool {
Renamed lib/auth/src/server/grpc/identity.rs → lib/auth/src/internal/identity.rs View lib/auth/src/internal/identity.rs
@@ -7,9 +7,9 @@
77use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
88use tonic::{Request, Response, Status};
99
10use crate::server::AuthSvc;
11use crate::server::errors::{AuthError, AuthResult};
12use crate::server::grpc::{context, error, store};
10use crate::AuthSvc;
11use crate::errors::{AuthError, AuthResult};
12use crate::internal::{context, error, store};
1313
1414#[derive(Clone)]
1515pub struct IdentityApi {
@@ -40,7 +40,7 @@
4040}
4141
4242fn bot_public(user: &users::Model) -> crate::protos::auth::common::v1::UserPublic {
43 crate::server::grpc::store::user_public(user, String::new(), None)
43 crate::internal::store::user_public(user, String::new(), None)
4444}
4545
4646impl IdentityApi {
@@ -58,24 +58,23 @@
5858 return Err(AuthError::InvalidPassword);
5959 }
6060 if !stored.is_empty() {
61 let check = crate::server::share::password::verify(
61 let check = crate::share::password::verify(
6262 request.old_password_hash.clone(),
6363 stored,
6464 request_id,
6565 )
6666 .await?;
67 if check == crate::server::share::password::PasswordCheck::Mismatch {
67 if check == crate::share::password::PasswordCheck::Mismatch {
6868 return Err(AuthError::InvalidPassword);
6969 }
7070 }
7171
72 crate::server::share::password::validate_new_password(
72 crate::share::password::validate_new_password(
7373 &request.new_password_hash,
7474 &self.svc.auth().password_policy,
7575 )?;
7676 let new_hash =
77 crate::server::share::password::hash(request.new_password_hash.clone(), request_id)
78 .await?;
77 crate::share::password::hash(request.new_password_hash.clone(), request_id).await?;
7978
8079 let now = store::now();
8180 users::Entity::update_many()
@@ -118,7 +117,8 @@
118117 if let Err(err) = store::drop_user_session_states(self.svc.cache(), user.id, keep).await {
119118 tracing::warn!(%err, user_id = user.id, "failed to drop revoked sessions");
120119 }
121 crate::server::http::me::service::invalidate_me_cache(self.svc.cache(), user.id).await;
120 crate::handlers::identity::me::service::invalidate_me_cache(self.svc.cache(), user.id)
121 .await;
122122
123123 store::audit(
124124 db,
@@ -363,10 +363,9 @@
363363 payload.displayname.trim().to_string()
364364 };
365365 let now = Utc::now();
366 let password_hash =
367 crate::server::share::password::hash(format!("bot-{lower}-{now}"), request_id)
368 .await
369 .map_err(|value| error::status(&value, request_id))?;
366 let password_hash = crate::share::password::hash(format!("bot-{lower}-{now}"), request_id)
367 .await
368 .map_err(|value| error::status(&value, request_id))?;
370369 let inserted = users::ActiveModel {
371370 id: Set(random_bot_id()),
372371 username: Set(username.to_string()),
Renamed lib/auth/src/server/grpc/mfa.rs → lib/auth/src/internal/mfa.rs View lib/auth/src/internal/mfa.rs
@@ -8,9 +8,9 @@
88use serde_json::json;
99use tonic::{Request, Response, Status};
1010
11use crate::server::AuthSvc;
12use crate::server::errors::{AuthError, AuthResult};
13use crate::server::grpc::{context, convert, error, store};
11use crate::AuthSvc;
12use crate::errors::{AuthError, AuthResult};
13use crate::internal::{context, convert, error, store};
1414
1515#[derive(Clone)]
1616pub struct MfaApi {
@@ -114,7 +114,7 @@
114114 for _ in 0..count {
115115 let code = recovery_code();
116116 let normalized = normalize_recovery_code(&code);
117 let code_hash = crate::server::share::crypto::sha256_hex(normalized.as_bytes());
117 let code_hash = crate::share::crypto::sha256_hex(normalized.as_bytes());
118118 user_mfa_backup_codes::ActiveModel {
119119 id: Set(uuid::Uuid::new_v4()),
120120 user_id: Set(user_id),
@@ -140,13 +140,10 @@
140140 if stored.is_empty() {
141141 return Err(AuthError::InvalidPassword);
142142 }
143 let check = crate::server::share::password::verify(
144 client_digest.to_owned(),
145 stored.to_owned(),
146 None,
147 )
148 .await?;
149 if check == crate::server::share::password::PasswordCheck::Mismatch {
143 let check =
144 crate::share::password::verify(client_digest.to_owned(), stored.to_owned(), None)
145 .await?;
146 if check == crate::share::password::PasswordCheck::Mismatch {
150147 return Err(AuthError::InvalidPassword);
151148 }
152149 Ok(())
@@ -192,9 +189,9 @@
192189 .await?;
193190 }
194191
195 let secret = crate::server::share::totp::generate_secret();
196 let ciphertext = crate::server::share::totp::encrypt_secret(key, &secret)?;
197 let uri = crate::server::share::totp::otpauth_uri(&secret, &user.username);
192 let secret = crate::share::totp::generate_secret();
193 let ciphertext = crate::share::totp::encrypt_secret(key, &secret)?;
194 let uri = crate::share::totp::otpauth_uri(&secret, &user.username);
198195 let name = request.name.trim();
199196 let name = if name.is_empty() { "TOTP" } else { name };
200197
@@ -265,9 +262,8 @@
265262 .secret_ciphertext
266263 .as_deref()
267264 .ok_or(AuthError::TotpNotConfigured)?;
268 let secret = crate::server::share::totp::decrypt_secret(key, ciphertext)?;
269 let (step, value) =
270 crate::server::share::totp::verify_totp_code(&secret, &request.code, None)?;
265 let secret = crate::share::totp::decrypt_secret(key, ciphertext)?;
266 let (step, value) = crate::share::totp::verify_totp_code(&secret, &request.code, None)?;
271267 let _ = step;
272268
273269 let now = store::now();
@@ -326,12 +322,12 @@
326322
327323 let code = request.code.trim();
328324 let key = self.svc.auth().totp_encryption_key.as_deref();
329 let consumed = crate::server::share::mfa::consume_totp(db, &factor, key, code, None)
325 let consumed = crate::share::mfa::consume_totp(db, &factor, key, code, None)
330326 .await
331327 .is_ok();
332328 let consumed = match consumed {
333329 true => true,
334 false => crate::server::share::mfa::consume_recovery(db, user.id, code, None)
330 false => crate::share::mfa::consume_recovery(db, user.id, code, None)
335331 .await
336332 .is_ok(),
337333 };
@@ -447,7 +443,7 @@
447443 attempt_count: 0,
448444 });
449445 };
450 match crate::server::share::mfa::consume_totp(
446 match crate::share::mfa::consume_totp(
451447 db,
452448 &factor,
453449 self.svc.auth().totp_encryption_key.as_deref(),
@@ -522,9 +518,7 @@
522518 };
523519
524520 let remaining_before = self.remaining_codes(user.id).await?;
525 match crate::server::share::mfa::consume_recovery(db, user.id, &request.code, request_id)
526 .await
527 {
521 match crate::share::mfa::consume_recovery(db, user.id, &request.code, request_id).await {
528522 Ok(()) => {}
529523 Err(AuthError::RecoveryCodeInvalid) => {
530524 return Ok(RedeemRecoveryCodeResponse {
@@ -578,8 +572,8 @@
578572}
579573
580574fn recovery_code() -> String {
581 let left = crate::server::share::crypto::random_alnum(6);
582 let right = crate::server::share::crypto::random_alnum(6);
575 let left = crate::share::crypto::random_alnum(6);
576 let right = crate::share::crypto::random_alnum(6);
583577 format!("{left}-{right}")
584578}
585579
Renamed lib/auth/src/server/grpc/mod.rs → lib/auth/src/internal/mod.rs View lib/auth/src/internal/mod.rs
@@ -2,6 +2,7 @@
22pub mod audit;
33pub mod authority;
44pub mod authorization;
5pub mod bridge;
56pub mod cache_maintenance;
67pub mod context;
78pub mod convert;
@@ -12,11 +13,3 @@
1213pub mod session;
1314pub mod ssh_key;
1415pub mod store;
15
16use tower::layer::util::{Identity, Stack};
17
18pub type GrpcRouter = tonic::transport::Server<Stack<crate::oltp::GrpcRequestLog, Identity>>;
19
20pub fn router() -> GrpcRouter {
21 tonic::transport::Server::builder().layer(crate::oltp::GrpcRequestLog)
22}
Renamed lib/auth/src/server/grpc/organization.rs → lib/auth/src/internal/organization.rs View lib/auth/src/internal/organization.rs
@@ -7,6 +7,15 @@
77use tonic::{Request, Response, Status};
88use uuid::Uuid;
99
10use crate::AuthSvc;
11use crate::errors::{AuthError, AuthResult};
12use crate::handlers::identity::organization::types::{
13 AddMemberBody, CreateOrganizationBody, CreateRoleBody, JoinRequestSummary, JoinRuleSummary,
14 MAX_BATCH_PERMISSIONS, PermissionGrant, SetMemberRoleBody, SetRolePermissionsBody,
15 UpdateBanBody, UpdateMemberBody, UpdateOrganizationBody,
16};
17use crate::handlers::identity::organization::{member, role, service};
18use crate::internal::{context, convert, error, store};
1019use crate::models::space::{
1120 MessageNotifications as DomainNotifications, OrgJoinMethod as DomainJoin,
1221 OrgMemberRole as DomainRole, OrganizationJoinPolicy as DomainJoinPolicy,
@@ -31,15 +40,6 @@
3140 OrganizationJoinMethod as OrgJoinMethodProto, OrganizationMemberRole as OrgMemberRoleProto,
3241 OrganizationStatus as OrganizationStatusProto, PermissionEffect as PermissionEffectProto, *,
3342};
34use crate::server::AuthSvc;
35use crate::server::errors::{AuthError, AuthResult};
36use crate::server::grpc::{context, convert, error, store};
37use crate::server::http::organization::types::{
38 AddMemberBody, CreateOrganizationBody, CreateRoleBody, JoinRequestSummary, JoinRuleSummary,
39 MAX_BATCH_PERMISSIONS, PermissionGrant, SetMemberRoleBody, SetRolePermissionsBody,
40 UpdateBanBody, UpdateMemberBody, UpdateOrganizationBody,
41};
42use crate::server::http::organization::{member, role, service};
4343
4444fn uuid_of(value: &str, request_id: Option<&str>) -> Result<Uuid, Status> {
4545 Uuid::parse_str(value.trim())
@@ -99,7 +99,7 @@
9999}
100100
101101fn org_info(
102 summary: crate::server::http::organization::types::OrganizationSummary,
102 summary: crate::handlers::identity::organization::types::OrganizationSummary,
103103) -> OrganizationInfo {
104104 OrganizationInfo {
105105 id: summary.id.to_string(),
@@ -127,7 +127,7 @@
127127}
128128
129129fn member_info(
130 summary: crate::server::http::organization::types::MemberSummary,
130 summary: crate::handlers::identity::organization::types::MemberSummary,
131131) -> OrganizationMemberInfo {
132132 OrganizationMemberInfo {
133133 id: summary.id.to_string(),
@@ -164,7 +164,7 @@
164164}
165165
166166fn role_info(
167 summary: crate::server::http::organization::types::RoleSummary,
167 summary: crate::handlers::identity::organization::types::RoleSummary,
168168) -> OrganizationRoleInfo {
169169 OrganizationRoleInfo {
170170 id: summary.id.to_string(),
@@ -196,7 +196,7 @@
196196}
197197
198198fn invite_info(
199 summary: crate::server::http::organization::types::InviteSummary,
199 summary: crate::handlers::identity::organization::types::InviteSummary,
200200) -> OrganizationInviteInfo {
201201 OrganizationInviteInfo {
202202 id: summary.id.to_string(),
Renamed lib/auth/src/server/grpc/session.rs → lib/auth/src/internal/session.rs View lib/auth/src/internal/session.rs
@@ -7,9 +7,9 @@
77use tonic::{Request, Response, Status};
88use uuid::Uuid;
99
10use crate::server::AuthSvc;
11use crate::server::errors::AuthError;
12use crate::server::grpc::{context, convert, error, store};
10use crate::AuthSvc;
11use crate::errors::AuthError;
12use crate::internal::{context, convert, error, store};
1313
1414pub const MAX_LIST_PAGE: u64 = 100;
1515pub const SESSION_LIST_MAX: usize = 200;
@@ -277,7 +277,7 @@
277277 .exec(self.svc.db().write())
278278 .await
279279 .map_err(|err| error::status(&AuthError::from(err), request_id))?;
280 crate::server::http::me::service::invalidate_me_cache(
280 crate::handlers::identity::me::service::invalidate_me_cache(
281281 self.svc.cache(),
282282 request.user_id,
283283 )
@@ -336,8 +336,11 @@
336336 revoked_sessions = revoked.rows_affected as u32;
337337 }
338338
339 crate::server::http::me::service::invalidate_me_cache(self.svc.cache(), request.user_id)
340 .await;
339 crate::handlers::identity::me::service::invalidate_me_cache(
340 self.svc.cache(),
341 request.user_id,
342 )
343 .await;
341344 store::audit(
342345 self.svc.db(),
343346 request.user_id,
Renamed lib/auth/src/server/grpc/ssh_key.rs → lib/auth/src/internal/ssh_key.rs View lib/auth/src/internal/ssh_key.rs
@@ -8,9 +8,9 @@
88use tonic::{Request, Response, Status};
99use uuid::Uuid;
1010
11use crate::server::AuthSvc;
12use crate::server::errors::AuthError;
13use crate::server::grpc::{context, convert, error, store};
11use crate::AuthSvc;
12use crate::errors::AuthError;
13use crate::internal::{context, convert, error, store};
1414
1515#[derive(Clone)]
1616pub struct SshKeyApi {
@@ -109,7 +109,7 @@
109109 request_id,
110110 ));
111111 }
112 crate::server::http::ssh_key::service::parse_public_key(public_key)
112 crate::handlers::identity::ssh_key::service::parse_public_key(public_key)
113113 .map_err(|err| error::status(&err, request_id))?
114114 .1
115115 } else {
@@ -166,7 +166,7 @@
166166 .svc
167167 .add_ssh_key_for_user(
168168 request.user_id,
169 crate::server::http::ssh_key::types::SshKeyAddRequest {
169 crate::handlers::identity::ssh_key::types::SshKeyAddRequest {
170170 name: request.name,
171171 description: Some(request.description).filter(|text| !text.is_empty()),
172172 public_key: request.public_key,
@@ -313,7 +313,7 @@
313313 .map_err(|err| error::status(&AuthError::from(err), request_id))?;
314314
315315 if request.bump_security_version {
316 crate::server::http::me::service::invalidate_me_cache(
316 crate::handlers::identity::me::service::invalidate_me_cache(
317317 self.svc.cache(),
318318 request.user_id,
319319 )
Renamed lib/auth/src/server/grpc/store.rs → lib/auth/src/internal/store.rs View lib/auth/src/internal/store.rs
@@ -7,10 +7,10 @@
77use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
88use uuid::Uuid;
99
10use crate::server::errors::{AuthError, AuthResult};
11use crate::server::grpc::context::RequestMeta;
12use crate::server::grpc::convert;
13use crate::server::share::user as user_store;
10use crate::errors::{AuthError, AuthResult};
11use crate::internal::context::RequestMeta;
12use crate::internal::convert;
13use crate::share::user as user_store;
1414
1515pub const ALLOWED_APP_SCOPES: [&str; 12] = [
1616 "user_name",
@@ -142,7 +142,7 @@
142142/// 未知 scope 让要求的权限集合变大:丢弃未知项会把 `required` 收缩成更松的集合,
143143/// 空集合更会让 [`scopes_cover`] 恒真,所以保留原值(零值)并让覆盖判定失败。
144144pub fn required_scope_codes(names: &[String]) -> Vec<i32> {
145 // 只保留真正的令牌 scope:`openid` 这类 OIDC 标准字段在 kenoauth 里没有对应的
145 // 只保留真正的令牌 scope:`openid` 这类 OIDC 标准字段在 auth service 里没有对应的
146146 // TokenScope,若映射成 0 会让 `scope::valid` 判定整组失效(0 非法),
147147 // 结果是持有 "openid profile email event" 的令牌反而被 SCOPE_INSUFFICIENT 拒掉。
148148 names
@@ -215,7 +215,7 @@
215215 client_ip: Option<String>,
216216 user_agent: Option<String>,
217217) -> AuthResult<()> {
218 crate::server::share::audit::entry(
218 crate::share::audit::entry(
219219 db,
220220 user_id,
221221 actor_user_id,
@@ -258,7 +258,7 @@
258258}
259259
260260pub fn sha256_digest(value: &str) -> String {
261 crate::server::share::crypto::sha256_hex(value.as_bytes())
261 crate::share::crypto::sha256_hex(value.as_bytes())
262262}
263263
264264pub fn new_id() -> Uuid {
Renamed lib/auth/src/server/pow/hash.rs → lib/auth/src/pow/hash.rs View lib/auth/src/pow/hash.rs
@@ -1,8 +1,8 @@
11use sha2::{Digest, Sha256};
22
3use crate::server::errors::{AuthError, AuthResult};
4use crate::server::http::challenge::types::{ChallengeResponse, PowProofRequest};
5use crate::server::share::crypto;
3use crate::errors::{AuthError, AuthResult};
4use crate::handlers::identity::challenge::types::{ChallengeResponse, PowProofRequest};
5use crate::share::crypto;
66
77pub fn pow_hash(challenge: &ChallengeResponse, nonce: u64, payload: &[u8]) -> [u8; 32] {
88 let mut hasher = Sha256::new();
Renamed lib/auth/src/server/pow/mod.rs → lib/auth/src/pow/mod.rs View lib/auth/src/pow/mod.rs

No content changes.

Renamed lib/auth/src/server/pow/store.rs → lib/auth/src/pow/store.rs View lib/auth/src/pow/store.rs
@@ -4,11 +4,11 @@
44use crate::config::pow::PowConfig;
55use crate::session::Session;
66
7use crate::server::errors::{AuthError, AuthResult};
8use crate::server::http::challenge::types::{
7use crate::errors::{AuthError, AuthResult};
8use crate::handlers::identity::challenge::types::{
99 ChallengeResponse, PowProofRequest, SESSION_CHALLENGE,
1010};
11use crate::server::share::crypto::{now_secs, random_alnum};
11use crate::share::crypto::{now_secs, random_alnum};
1212
1313pub const USED_KEY_PREFIX: &str = "auth:pow:used:";
1414
@@ -36,7 +36,7 @@
3636 .get::<ChallengeResponse>(SESSION_CHALLENGE)?
3737 .ok_or(AuthError::Expired)?;
3838
39 crate::server::pow::hash::verify_pow(&challenge, proof, expected_payload)?;
39 crate::pow::hash::verify_pow(&challenge, proof, expected_payload)?;
4040
4141 let remaining = challenge.expires_at.saturating_sub(now_secs()).max(1);
4242 let used_key = used_key(&challenge.context);
Deleted lib/auth/src/server/http/openapi.rs
@@ -1,96 +0,0 @@
1use utoipa::OpenApi;
2
3#[derive(OpenApi)]
4#[openapi(
5 info(
6 title = "auth",
7 description = "认证与账号服务:工作量证明、口令与验证码登录、二次验证、注册、口令重置与 SSH 公钥。",
8 version = env!("CARGO_PKG_VERSION")
9 ),
10 paths(
11 crate::server::http::challenge::handler::challenge,
12 crate::server::http::login::handler::login,
13 crate::server::http::login::handler::login_code,
14 crate::server::http::login::handler::login_code_verify,
15 crate::server::http::login::handler::login_verify,
16 crate::server::http::logout::handler::logout,
17 crate::server::http::me::handler::me,
18 crate::server::http::register::handler::register_code,
19 crate::server::http::register::handler::register,
20 crate::server::http::password_reset::handler::password_reset_request,
21 crate::server::http::password_reset::handler::password_reset,
22 crate::server::http::ssh_key::handler::ssh_key_list,
23 crate::server::http::ssh_key::handler::ssh_key_add,
24 crate::server::http::ssh_key::handler::ssh_key_delete,
25 crate::server::http::organization::handler::create_organization,
26 crate::server::http::organization::handler::list_organizations,
27 crate::server::http::organization::handler::get_organization,
28 crate::server::http::organization::handler::update_organization,
29 crate::server::http::organization::handler::archive_organization,
30 crate::server::http::organization::handler::restore_organization,
31 crate::server::http::organization::handler::delete_organization,
32 crate::server::http::organization::handler::transfer_ownership,
33 crate::server::http::organization::handler::leave_organization,
34 crate::server::http::organization::handler::list_members,
35 crate::server::http::organization::handler::get_member,
36 crate::server::http::organization::handler::update_member,
37 crate::server::http::organization::handler::update_self_member,
38 crate::server::http::organization::handler::add_member,
39 crate::server::http::organization::handler::remove_member,
40 crate::server::http::organization::handler::set_member_role,
41 crate::server::http::organization::handler::ban_member,
42 crate::server::http::organization::handler::unban_member,
43 crate::server::http::organization::handler::set_member_permissions,
44 crate::server::http::organization::handler::list_bans,
45 crate::server::http::organization::handler::get_ban,
46 crate::server::http::organization::handler::update_ban,
47 crate::server::http::organization::handler::unban_by_user,
48 crate::server::http::organization::handler::list_roles,
49 crate::server::http::organization::handler::get_role,
50 crate::server::http::organization::handler::create_role,
51 crate::server::http::organization::handler::update_role,
52 crate::server::http::organization::handler::delete_role,
53 crate::server::http::organization::handler::set_role_permissions,
54 crate::server::http::organization::handler::assign_role,
55 crate::server::http::organization::handler::unassign_role,
56 crate::server::http::organization::handler::join_role,
57 crate::server::http::organization::handler::leave_role,
58 crate::server::http::organization::handler::check_permission,
59 crate::server::http::organization::handler::create_invite,
60 crate::server::http::organization::handler::list_invites,
61 crate::server::http::organization::handler::revoke_invite,
62 crate::server::http::organization::handler::redeem_invite,
63 ),
64 components(schemas(
65 crate::server::http::organization::types::OrganizationSummary,
66 crate::server::http::organization::types::MemberSummary,
67 crate::server::http::organization::types::RoleSummary,
68 crate::server::http::organization::types::InviteSummary,
69 crate::server::http::organization::types::PermissionGrant,
70 crate::server::http::organization::types::PermissionCheck,
71 crate::server::http::organization::types::CreateOrganizationBody,
72 crate::server::http::organization::types::UpdateOrganizationBody,
73 crate::server::http::organization::types::AddMemberBody,
74 crate::server::http::organization::types::SetMemberRoleBody,
75 crate::server::http::organization::types::BanMemberBody,
76 crate::server::http::organization::types::CheckPermissionBody,
77 crate::server::http::organization::types::CreateRoleBody,
78 crate::server::http::organization::types::SetRolePermissionsBody,
79 crate::server::http::organization::handler::OrgPath,
80 crate::server::http::organization::handler::OrgMemberPath,
81 crate::server::http::organization::handler::OrgRolePath,
82 crate::server::http::organization::handler::OrgInvitePath,
83 crate::server::http::organization::handler::OrgListQuery,
84 crate::server::http::organization::handler::MemberListQuery,
85 crate::server::http::organization::handler::UpdateRoleBody,
86 crate::server::http::organization::handler::TransferBody,
87 crate::server::http::organization::handler::ReasonBody,
88 crate::server::http::organization::handler::DeleteOrgQuery,
89 crate::server::http::organization::handler::CreateInviteBody,
90 crate::server::http::organization::handler::RedeemInviteBody,
91 crate::server::http::organization::handler::CreateInviteResponse,
92 crate::server::http::organization::handler::RedeemInviteResponse,
93 crate::server::http::organization::handler::AssignRoleBody,
94 ))
95)]
96pub struct ApiDoc;
Deleted lib/auth/src/server/http/router.rs
@@ -1,216 +0,0 @@
1use actix_web::web;
2
3pub fn configure(cfg: &mut web::ServiceConfig) {
4 cfg.service(web::scope("/api/v1").configure(configure_api));
5}
6
7pub fn configure_api(cfg: &mut web::ServiceConfig) {
8 cfg.route(
9 "/oauth/challenge",
10 web::post().to(crate::server::http::challenge::handler::challenge),
11 );
12 cfg.route(
13 "/oauth/login",
14 web::post().to(crate::server::http::login::handler::login),
15 );
16 cfg.route(
17 "/oauth/login/code",
18 web::post().to(crate::server::http::login::handler::login_code),
19 );
20 cfg.route(
21 "/oauth/login/code/verify",
22 web::post().to(crate::server::http::login::handler::login_code_verify),
23 );
24 cfg.route(
25 "/oauth/login/verify",
26 web::post().to(crate::server::http::login::handler::login_verify),
27 );
28 cfg.route(
29 "/oauth/session/logout",
30 web::post().to(crate::server::http::logout::handler::logout),
31 );
32 cfg.route(
33 "/oauth/me",
34 web::get().to(crate::server::http::me::handler::me),
35 );
36 cfg.route(
37 "/oauth/register/code",
38 web::post().to(crate::server::http::register::handler::register_code),
39 );
40 cfg.route(
41 "/oauth/register",
42 web::post().to(crate::server::http::register::handler::register),
43 );
44 cfg.route(
45 "/oauth/password/reset",
46 web::post().to(crate::server::http::password_reset::handler::password_reset_request),
47 );
48 cfg.route(
49 "/oauth/password/reset/confirm",
50 web::post().to(crate::server::http::password_reset::handler::password_reset),
51 );
52 cfg.route(
53 "/oauth/ssh-keys",
54 web::get().to(crate::server::http::ssh_key::handler::ssh_key_list),
55 );
56 cfg.route(
57 "/oauth/ssh-keys",
58 web::post().to(crate::server::http::ssh_key::handler::ssh_key_add),
59 );
60 cfg.route(
61 "/oauth/ssh-keys/{key_id}",
62 web::delete().to(crate::server::http::ssh_key::handler::ssh_key_delete),
63 );
64 cfg.route(
65 "/organizations",
66 web::post().to(crate::server::http::organization::handler::create_organization),
67 );
68 cfg.route(
69 "/organizations",
70 web::get().to(crate::server::http::organization::handler::list_organizations),
71 );
72 cfg.route(
73 "/organizations/invites/redeem",
74 web::post().to(crate::server::http::organization::handler::redeem_invite),
75 );
76 cfg.route(
77 "/organizations/{organization_id}",
78 web::get().to(crate::server::http::organization::handler::get_organization),
79 );
80 cfg.route(
81 "/organizations/{organization_id}",
82 web::patch().to(crate::server::http::organization::handler::update_organization),
83 );
84 cfg.route(
85 "/organizations/{organization_id}",
86 web::delete().to(crate::server::http::organization::handler::delete_organization),
87 );
88 cfg.route(
89 "/organizations/{organization_id}/archive",
90 web::post().to(crate::server::http::organization::handler::archive_organization),
91 );
92 cfg.route(
93 "/organizations/{organization_id}/restore",
94 web::post().to(crate::server::http::organization::handler::restore_organization),
95 );
96 cfg.route(
97 "/organizations/{organization_id}/transfer",
98 web::post().to(crate::server::http::organization::handler::transfer_ownership),
99 );
100 cfg.route(
101 "/organizations/{organization_id}/leave",
102 web::post().to(crate::server::http::organization::handler::leave_organization),
103 );
104 cfg.route(
105 "/organizations/{organization_id}/members",
106 web::get().to(crate::server::http::organization::handler::list_members),
107 );
108 cfg.route(
109 "/organizations/{organization_id}/members",
110 web::post().to(crate::server::http::organization::handler::add_member),
111 );
112 cfg.route(
113 "/organizations/{organization_id}/members/@me",
114 web::patch().to(crate::server::http::organization::handler::update_self_member),
115 );
116 cfg.route(
117 "/organizations/{organization_id}/members/{member_id}",
118 web::get().to(crate::server::http::organization::handler::get_member),
119 );
120 cfg.route(
121 "/organizations/{organization_id}/members/{member_id}",
122 web::patch().to(crate::server::http::organization::handler::update_member),
123 );
124 cfg.route(
125 "/organizations/{organization_id}/members/{member_id}",
126 web::delete().to(crate::server::http::organization::handler::remove_member),
127 );
128 cfg.route(
129 "/organizations/{organization_id}/members/{member_id}/role",
130 web::patch().to(crate::server::http::organization::handler::set_member_role),
131 );
132 cfg.route(
133 "/organizations/{organization_id}/members/{member_id}/ban",
134 web::post().to(crate::server::http::organization::handler::ban_member),
135 );
136 cfg.route(
137 "/organizations/{organization_id}/members/{member_id}/unban",
138 web::post().to(crate::server::http::organization::handler::unban_member),
139 );
140 cfg.route(
141 "/organizations/{organization_id}/members/{member_id}/permissions",
142 web::put().to(crate::server::http::organization::handler::set_member_permissions),
143 );
144 cfg.route(
145 "/organizations/{organization_id}/bans",
146 web::get().to(crate::server::http::organization::handler::list_bans),
147 );
148 cfg.route(
149 "/organizations/{organization_id}/bans/{user_id}",
150 web::get().to(crate::server::http::organization::handler::get_ban),
151 );
152 cfg.route(
153 "/organizations/{organization_id}/bans/{user_id}",
154 web::patch().to(crate::server::http::organization::handler::update_ban),
155 );
156 cfg.route(
157 "/organizations/{organization_id}/bans/{user_id}",
158 web::delete().to(crate::server::http::organization::handler::unban_by_user),
159 );
160 cfg.route(
161 "/organizations/{organization_id}/roles",
162 web::get().to(crate::server::http::organization::handler::list_roles),
163 );
164 cfg.route(
165 "/organizations/{organization_id}/roles",
166 web::post().to(crate::server::http::organization::handler::create_role),
167 );
168 cfg.route(
169 "/organizations/{organization_id}/roles/assign",
170 web::post().to(crate::server::http::organization::handler::assign_role),
171 );
172 cfg.route(
173 "/organizations/{organization_id}/roles/unassign",
174 web::post().to(crate::server::http::organization::handler::unassign_role),
175 );
176 cfg.route(
177 "/organizations/{organization_id}/roles/{role_id}",
178 web::get().to(crate::server::http::organization::handler::get_role),
179 );
180 cfg.route(
181 "/organizations/{organization_id}/roles/{role_id}",
182 web::patch().to(crate::server::http::organization::handler::update_role),
183 );
184 cfg.route(
185 "/organizations/{organization_id}/roles/{role_id}/join",
186 web::post().to(crate::server::http::organization::handler::join_role),
187 );
188 cfg.route(
189 "/organizations/{organization_id}/roles/{role_id}/join",
190 web::delete().to(crate::server::http::organization::handler::leave_role),
191 );
192 cfg.route(
193 "/organizations/{organization_id}/roles/{role_id}",
194 web::delete().to(crate::server::http::organization::handler::delete_role),
195 );
196 cfg.route(
197 "/organizations/{organization_id}/roles/{role_id}/permissions",
198 web::put().to(crate::server::http::organization::handler::set_role_permissions),
199 );
200 cfg.route(
201 "/organizations/{organization_id}/permissions/check",
202 web::post().to(crate::server::http::organization::handler::check_permission),
203 );
204 cfg.route(
205 "/organizations/{organization_id}/invites",
206 web::post().to(crate::server::http::organization::handler::create_invite),
207 );
208 cfg.route(
209 "/organizations/{organization_id}/invites",
210 web::get().to(crate::server::http::organization::handler::list_invites),
211 );
212 cfg.route(
213 "/organizations/{organization_id}/invites/{invite_id}",
214 web::delete().to(crate::server::http::organization::handler::revoke_invite),
215 );
216}
Deleted lib/auth/src/server/mod.rs
@@ -1,18 +0,0 @@
1pub mod account_lifecycle;
2pub mod authority;
3pub mod errors;
4pub mod grpc;
5pub mod http;
6pub mod migrate;
7pub mod pow;
8pub mod share;
9
10pub use errors::{AuthError, AuthResult};
11
12mod auth_svc;
13
14pub use auth_svc::AuthSvc;
15
16pub use http::{
17 challenge, login, logout, me, openapi, organization, password_reset, register, router, ssh_key,
18};
Renamed lib/webapi/src/auth/services/agreements.rs → lib/auth/src/services/agreements.rs View lib/auth/src/services/agreements.rs
@@ -4,9 +4,9 @@
44use time::OffsetDateTime;
55use uuid::Uuid;
66
7use super::AuthService;
8use crate::auth::errors::AuthError;
9use crate::auth::share::types::AgreementDoc;
7use crate::api_error::AuthBusinessError;
8use crate::service::AuthService;
9use crate::share::account::types::AgreementDoc;
1010
1111#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
1212pub struct AcceptAgreementResp {
@@ -33,7 +33,7 @@
3333 uid: i64,
3434 key: &str,
3535 version: &str,
36 ) -> Result<AcceptAgreementResp, AuthError> {
36 ) -> Result<AcceptAgreementResp, AuthBusinessError> {
3737 let Some(doc) = self
3838 .config()
3939 .auth
@@ -41,10 +41,10 @@
4141 .iter()
4242 .find(|doc| doc.key == key)
4343 else {
44 return Err(AuthError::NotFound);
44 return Err(AuthBusinessError::NotFound);
4545 };
4646 if doc.version != version {
47 return Err(AuthError::BadRequest(
47 return Err(AuthBusinessError::BadRequest(
4848 "agreement version mismatch".to_string(),
4949 ));
5050 }
Renamed lib/webapi/src/auth/services/mod.rs → lib/auth/src/services/mod.rs View lib/auth/src/services/mod.rs
@@ -1,10 +1,7 @@
11pub mod agreements;
2mod auth_service;
32pub mod notifications;
43pub mod preferences;
54pub mod profile;
65pub mod reports;
76pub mod suspensions;
87pub mod users;
9
10pub use auth_service::{AuthService, VerifiedAccessKey};
Renamed lib/webapi/src/auth/services/notifications.rs → lib/auth/src/services/notifications.rs View lib/auth/src/services/notifications.rs
@@ -9,8 +9,8 @@
99use time::OffsetDateTime;
1010use uuid::Uuid;
1111
12use super::AuthService;
13use crate::auth::errors::AuthError;
12use crate::api_error::AuthBusinessError;
13use crate::service::AuthService;
1414
1515#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
1616pub struct NotificationDto {
@@ -32,7 +32,7 @@
3232 offset: Option<u64>,
3333 notification_type: Option<NotificationType>,
3434 unread: Option<bool>,
35 ) -> Result<Vec<NotificationDto>, AuthError> {
35 ) -> Result<Vec<NotificationDto>, AuthBusinessError> {
3636 let max_limit = limit.unwrap_or(50).min(100);
3737 let mut query = user_notifications::Entity::find()
3838 .filter(user_notifications::Column::UserId.eq(uid))
@@ -64,7 +64,7 @@
6464 .collect())
6565 }
6666
67 pub async fn unread_notifications_count(&self, uid: i64) -> Result<u64, AuthError> {
67 pub async fn unread_notifications_count(&self, uid: i64) -> Result<u64, AuthBusinessError> {
6868 let count = user_notifications::Entity::find()
6969 .filter(user_notifications::Column::UserId.eq(uid))
7070 .filter(user_notifications::Column::ReadAt.is_null())
@@ -73,7 +73,11 @@
7373 Ok(count)
7474 }
7575
76 pub async fn mark_notification_read(&self, uid: i64, id: Uuid) -> Result<(), AuthError> {
76 pub async fn mark_notification_read(
77 &self,
78 uid: i64,
79 id: Uuid,
80 ) -> Result<(), AuthBusinessError> {
7781 let db = self.db();
7882 let Some(row) = user_notifications::Entity::find()
7983 .filter(user_notifications::Column::Id.eq(id))
@@ -81,7 +85,7 @@
8185 .one(db.write())
8286 .await?
8387 else {
84 return Err(AuthError::NotFound);
88 return Err(AuthBusinessError::NotFound);
8589 };
8690 let mut active = row.into_active_model();
8791 active.read_at = Set(Some(OffsetDateTime::now_utc()));
@@ -93,7 +97,7 @@
9397 &self,
9498 uid: i64,
9599 ids: Vec<Uuid>,
96 ) -> Result<u64, AuthError> {
100 ) -> Result<u64, AuthBusinessError> {
97101 let db = self.db();
98102 let result = user_notifications::Entity::update_many()
99103 .col_expr(
@@ -108,7 +112,7 @@
108112 Ok(result.rows_affected)
109113 }
110114
111 pub async fn mark_all_notifications_read(&self, uid: i64) -> Result<(), AuthError> {
115 pub async fn mark_all_notifications_read(&self, uid: i64) -> Result<(), AuthBusinessError> {
112116 let db = self.db();
113117 user_notifications::Entity::update_many()
114118 .col_expr(
@@ -122,7 +126,7 @@
122126 Ok(())
123127 }
124128
125 pub async fn delete_notification(&self, uid: i64, id: Uuid) -> Result<(), AuthError> {
129 pub async fn delete_notification(&self, uid: i64, id: Uuid) -> Result<(), AuthBusinessError> {
126130 let db = self.db();
127131 let result = user_notifications::Entity::delete_many()
128132 .filter(user_notifications::Column::Id.eq(id))
@@ -130,7 +134,7 @@
130134 .exec(db.write())
131135 .await?;
132136 if result.rows_affected == 0 {
133 return Err(AuthError::NotFound);
137 return Err(AuthBusinessError::NotFound);
134138 }
135139 Ok(())
136140 }
Renamed lib/webapi/src/auth/services/preferences.rs → lib/auth/src/services/preferences.rs View lib/auth/src/services/preferences.rs
@@ -4,8 +4,8 @@
44use serde::{Deserialize, Serialize};
55use time::OffsetDateTime;
66
7use super::AuthService;
8use crate::auth::errors::AuthError;
7use crate::api_error::AuthBusinessError;
8use crate::service::AuthService;
99
1010#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
1111pub struct UserPreferencesDto {
@@ -65,7 +65,7 @@
6565}
6666
6767impl AuthService {
68 pub async fn preferences(&self, uid: i64) -> Result<UserPreferencesDto, AuthError> {
68 pub async fn preferences(&self, uid: i64) -> Result<UserPreferencesDto, AuthBusinessError> {
6969 let Some(row) = user_preferences::Entity::find_by_id(uid)
7070 .one(self.db().read())
7171 .await?
@@ -91,7 +91,7 @@
9191 &self,
9292 uid: i64,
9393 cmd: UpdatePreferencesCmd,
94 ) -> Result<UserPreferencesDto, AuthError> {
94 ) -> Result<UserPreferencesDto, AuthBusinessError> {
9595 let db = self.db();
9696 let now = OffsetDateTime::now_utc();
9797 let existing = user_preferences::Entity::find_by_id(uid)
Renamed lib/webapi/src/auth/services/profile.rs → lib/auth/src/services/profile.rs View lib/auth/src/services/profile.rs
@@ -4,10 +4,10 @@
44use serde::{Deserialize, Serialize};
55use time::{Date, OffsetDateTime};
66
7use super::AuthService;
8use crate::auth::errors::AuthError;
9use crate::auth::share::types::UserPublic;
10use crate::auth::share::validate;
7use crate::api_error::AuthBusinessError;
8use crate::service::AuthService;
9use crate::share::account::types::UserPublic;
10use crate::share::account::validate;
1111
1212#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
1313pub struct UserMeDto {
@@ -65,17 +65,15 @@
6565}
6666
6767impl AuthService {
68 pub async fn me(&self, uid: i64) -> Result<(UserMeDto, Option<ProfileDto>), AuthError> {
68 pub async fn me(&self, uid: i64) -> Result<(UserMeDto, Option<ProfileDto>), AuthBusinessError> {
6969 export::users::ensure_local_user(&self.state, uid).await?;
7070 let db = self.db();
7171 let Some(user) = user::Entity::find_by_id(uid).one(db.read()).await? else {
72 return Err(AuthError::NotFound);
72 return Err(AuthBusinessError::NotFound);
7373 };
74 let client = self
75 .state
76 .kenauth
77 .as_ref()
78 .ok_or_else(|| AuthError::Internal("kenoauth is not configured".to_string()))?;
74 let client = self.state.kenauth.as_ref().ok_or_else(|| {
75 AuthBusinessError::Internal("auth service is not configured".to_string())
76 })?;
7977 let authority = client.get_authority_user(uid).await.map_err(map_kenauth)?;
8078 let public = client.get_user_public(uid).await.map_err(map_kenauth)?;
8179 let status = client.get_user_status(uid).await.map_err(map_kenauth)?;
@@ -110,7 +108,7 @@
110108 user_id: uid,
111109 username: public.username,
112110 display_name,
113 avatar_url: space::services::spaces::resolve_user_avatar_url(uid, &avatar_raw),
111 avatar_url: export::urls::resolve_user_avatar_url(uid, &avatar_raw),
114112 is_active: status.allow_use && status.deleted_at.is_none(),
115113 is_restricted: user.is_restricted,
116114 prohibit_login: authority.forbid_login,
@@ -139,11 +137,15 @@
139137 Ok((me, profile))
140138 }
141139
142 pub async fn update_profile(&self, uid: i64, cmd: UpdateProfileCmd) -> Result<(), AuthError> {
140 pub async fn update_profile(
141 &self,
142 uid: i64,
143 cmd: UpdateProfileCmd,
144 ) -> Result<(), AuthBusinessError> {
143145 export::users::ensure_local_user(&self.state, uid).await?;
144146 let db = self.db();
145147 let Some(user) = user::Entity::find_by_id(uid).one(db.write()).await? else {
146 return Err(AuthError::NotFound);
148 return Err(AuthBusinessError::NotFound);
147149 };
148150 let now = OffsetDateTime::now_utc();
149151 let mut user_active = user.into_active_model();
@@ -154,15 +156,13 @@
154156 .filter(|name| !name.is_empty())
155157 {
156158 if display_name.chars().count() > 64 {
157 return Err(AuthError::BadRequest(
159 return Err(AuthBusinessError::BadRequest(
158160 "display_name must be at most 64 characters".to_string(),
159161 ));
160162 }
161 let client = self
162 .state
163 .kenauth
164 .as_ref()
165 .ok_or_else(|| AuthError::Internal("kenoauth is not configured".to_string()))?;
163 let client = self.state.kenauth.as_ref().ok_or_else(|| {
164 AuthBusinessError::Internal("auth service is not configured".to_string())
165 })?;
166166 let current = client.get_authority_user(uid).await.map_err(map_kenauth)?;
167167 client
168168 .update_authority_user(
@@ -268,23 +268,21 @@
268268 })
269269 })
270270 .await
271 .map_err(AuthError::from)
271 .map_err(AuthBusinessError::from)
272272 }
273273
274274 pub async fn update_username(
275275 &self,
276276 uid: i64,
277277 new_username: &str,
278 ) -> Result<UserPublic, AuthError> {
279 let username = crate::auth::share::validate::validate_username(new_username.trim())?;
278 ) -> Result<UserPublic, AuthBusinessError> {
279 let username = crate::share::account::validate::validate_username(new_username.trim())?;
280280 let db = self.db();
281281 let local = user::Entity::find_by_id(uid).one(db.read()).await?;
282282
283 let client = self
284 .state
285 .kenauth
286 .as_ref()
287 .ok_or_else(|| AuthError::Internal("kenoauth is not configured".to_string()))?;
283 let client = self.state.kenauth.as_ref().ok_or_else(|| {
284 AuthBusinessError::Internal("auth service is not configured".to_string())
285 })?;
288286 let current = client.get_authority_user(uid).await.map_err(map_kenauth)?;
289287 let updated = if current.username != username {
290288 client
@@ -317,26 +315,26 @@
317315 user_id: uid,
318316 username: updated.username,
319317 display_name,
320 avatar_url: space::services::spaces::resolve_user_avatar_url(uid, avatar_raw),
318 avatar_url: export::urls::resolve_user_avatar_url(uid, avatar_raw),
321319 })
322320 }
323321}
324322
325fn map_kenauth(error: auth::client::error::KenAuthError) -> AuthError {
326 use auth::client::error::KenAuthError;
323fn map_kenauth(error: crate::auth_api_error::KenAuthError) -> AuthBusinessError {
324 use crate::auth_api_error::KenAuthError;
327325 match error {
328 KenAuthError::NotFound => AuthError::NotFound,
329 KenAuthError::Unauthenticated => AuthError::Unauthorized,
326 KenAuthError::NotFound => AuthBusinessError::NotFound,
327 KenAuthError::Unauthenticated => AuthBusinessError::Unauthorized,
330328 KenAuthError::Rejected(message) => {
331329 let lower = message.to_lowercase();
332330 if lower.contains("conflict") || lower.contains("taken") || lower.contains("already") {
333 AuthError::Conflict(message)
331 AuthBusinessError::Conflict(message)
334332 } else {
335 AuthError::BadRequest(message)
333 AuthBusinessError::BadRequest(message)
336334 }
337335 }
338336 KenAuthError::Unavailable(message)
339337 | KenAuthError::Config(message)
340 | KenAuthError::Internal(message) => AuthError::Internal(message),
338 | KenAuthError::Internal(message) => AuthBusinessError::Internal(message),
341339 }
342340}
Renamed lib/webapi/src/auth/services/reports.rs → lib/auth/src/services/reports.rs View lib/auth/src/services/reports.rs
@@ -5,8 +5,8 @@
55use time::OffsetDateTime;
66use uuid::Uuid;
77
8use super::AuthService;
9use crate::auth::errors::AuthError;
8use crate::api_error::AuthBusinessError;
9use crate::service::AuthService;
1010
1111#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
1212pub struct CreateReportCmd {
@@ -30,13 +30,15 @@
3030 &self,
3131 reporter_uid: i64,
3232 cmd: CreateReportCmd,
33 ) -> Result<ReportDto, AuthError> {
33 ) -> Result<ReportDto, AuthBusinessError> {
3434 if reporter_uid == cmd.reported_id {
35 return Err(AuthError::BadRequest("cannot report yourself".to_string()));
35 return Err(AuthBusinessError::BadRequest(
36 "cannot report yourself".to_string(),
37 ));
3638 }
3739 let reason = cmd.reason.trim().to_string();
3840 if reason.is_empty() || reason.len() > 500 {
39 return Err(AuthError::BadRequest(
41 return Err(AuthBusinessError::BadRequest(
4042 "reason must be between 1 and 500 characters".to_string(),
4143 ));
4244 }
@@ -45,7 +47,7 @@
4547 .one(db.read())
4648 .await?;
4749 if reported.is_none() {
48 return Err(AuthError::NotFound);
50 return Err(AuthBusinessError::NotFound);
4951 }
5052 let now = OffsetDateTime::now_utc();
5153 let row = user_reports::ActiveModel {
Renamed lib/webapi/src/auth/services/suspensions.rs → lib/auth/src/services/suspensions.rs View lib/auth/src/services/suspensions.rs
@@ -4,8 +4,8 @@
44use time::OffsetDateTime;
55use uuid::Uuid;
66
7use super::AuthService;
8use crate::auth::errors::AuthError;
7use crate::api_error::AuthBusinessError;
8use crate::service::AuthService;
99
1010#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
1111pub struct SuspensionDto {
@@ -17,7 +17,10 @@
1717}
1818
1919impl AuthService {
20 pub async fn active_suspension(&self, uid: i64) -> Result<Option<SuspensionDto>, AuthError> {
20 pub async fn active_suspension(
21 &self,
22 uid: i64,
23 ) -> Result<Option<SuspensionDto>, AuthBusinessError> {
2124 let now = OffsetDateTime::now_utc();
2225 let Some(row) = user_suspensions::Entity::find()
2326 .filter(user_suspensions::Column::UserId.eq(uid))
@@ -47,9 +50,9 @@
4750 }))
4851 }
4952
50 pub async fn assert_not_suspended(&self, uid: i64) -> Result<(), AuthError> {
53 pub async fn assert_not_suspended(&self, uid: i64) -> Result<(), AuthBusinessError> {
5154 if self.active_suspension(uid).await?.is_some() {
52 return Err(AuthError::Forbidden);
55 return Err(AuthBusinessError::Forbidden);
5356 }
5457 Ok(())
5558 }
Renamed lib/webapi/src/auth/services/users.rs → lib/auth/src/services/users.rs View lib/auth/src/services/users.rs
@@ -1,12 +1,12 @@
11use std::collections::HashMap;
22
3use auth::protos::auth::common::v1 as auth_public;
3use crate::protos::auth::common::v1 as auth_public;
44use models::users::user::{self, ProfileVisibility};
55use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
66
7use super::AuthService;
8use crate::auth::errors::AuthError;
9use crate::auth::share::types::UserPublic;
7use crate::api_error::AuthBusinessError;
8use crate::service::AuthService;
9use crate::share::account::types::UserPublic;
1010
1111const MAX_BATCH_USERS: usize = 100;
1212
@@ -17,15 +17,15 @@
1717 local: Option<&user::Model>,
1818 public: &auth_public::UserPublic,
1919 allow_use: bool,
20 ) -> Result<UserPublic, AuthError> {
20 ) -> Result<UserPublic, AuthBusinessError> {
2121 if !allow_use || public.system {
22 return Err(AuthError::NotFound);
22 return Err(AuthBusinessError::NotFound);
2323 }
2424 let visibility = local
2525 .map(|row| row.visibility)
2626 .unwrap_or(ProfileVisibility::Public);
2727 if visibility == ProfileVisibility::Private && caller != public.user_id {
28 return Err(AuthError::NotFound);
28 return Err(AuthBusinessError::NotFound);
2929 }
3030 let display_name = if public.displayname.trim().is_empty() {
3131 public.username.clone()
@@ -40,19 +40,18 @@
4040 user_id: public.user_id,
4141 username: public.username.clone(),
4242 display_name,
43 avatar_url: space::services::spaces::resolve_user_avatar_url(
44 public.user_id,
45 avatar_raw,
46 ),
43 avatar_url: export::urls::resolve_user_avatar_url(public.user_id, avatar_raw),
4744 })
4845 }
4946
50 pub async fn public_user(&self, caller: i64, uid: i64) -> Result<UserPublic, AuthError> {
51 let client = self
52 .state
53 .kenauth
54 .as_ref()
55 .ok_or_else(|| AuthError::Internal("kenoauth is not configured".to_string()))?;
47 pub async fn public_user(
48 &self,
49 caller: i64,
50 uid: i64,
51 ) -> Result<UserPublic, AuthBusinessError> {
52 let client = self.state.kenauth.as_ref().ok_or_else(|| {
53 AuthBusinessError::Internal("auth service is not configured".to_string())
54 })?;
5655 let public = client.get_user_public(uid).await.map_err(map_kenauth)?;
5756 let authority = client.get_authority_user(uid).await.map_err(map_kenauth)?;
5857 let local = user::Entity::find_by_id(uid).one(self.db().read()).await?;
@@ -63,9 +62,9 @@
6362 &self,
6463 caller: i64,
6564 ids: Vec<i64>,
66 ) -> Result<Vec<UserPublic>, AuthError> {
65 ) -> Result<Vec<UserPublic>, AuthBusinessError> {
6766 if ids.len() > MAX_BATCH_USERS {
68 return Err(AuthError::BadRequest(format!(
67 return Err(AuthBusinessError::BadRequest(format!(
6968 "ids must contain at most {MAX_BATCH_USERS} entries"
7069 )));
7170 }
@@ -76,11 +75,9 @@
7675 unique.sort_unstable();
7776 unique.dedup();
7877
79 let client = self
80 .state
81 .kenauth
82 .as_ref()
83 .ok_or_else(|| AuthError::Internal("kenoauth is not configured".to_string()))?;
78 let client = self.state.kenauth.as_ref().ok_or_else(|| {
79 AuthBusinessError::Internal("auth service is not configured".to_string())
80 })?;
8481 let publics = client
8582 .batch_get_user_public(unique.clone())
8683 .await
@@ -116,16 +113,14 @@
116113 &self,
117114 caller: i64,
118115 username: &str,
119 ) -> Result<UserPublic, AuthError> {
116 ) -> Result<UserPublic, AuthBusinessError> {
120117 let name = username.trim();
121118 if name.is_empty() {
122 return Err(AuthError::NotFound);
119 return Err(AuthBusinessError::NotFound);
123120 }
124 let client = self
125 .state
126 .kenauth
127 .as_ref()
128 .ok_or_else(|| AuthError::Internal("kenoauth is not configured".to_string()))?;
121 let client = self.state.kenauth.as_ref().ok_or_else(|| {
122 AuthBusinessError::Internal("auth service is not configured".to_string())
123 })?;
129124 let public = client
130125 .get_user_public_by_ref(auth_public::user_ref::Key::Username(name.to_string()))
131126 .await
@@ -134,14 +129,14 @@
134129 }
135130}
136131
137fn map_kenauth(error: auth::client::error::KenAuthError) -> AuthError {
138 use auth::client::error::KenAuthError;
132fn map_kenauth(error: crate::auth_api_error::KenAuthError) -> AuthBusinessError {
133 use crate::auth_api_error::KenAuthError;
139134 match error {
140 KenAuthError::NotFound => AuthError::NotFound,
141 KenAuthError::Unauthenticated => AuthError::Unauthorized,
142 KenAuthError::Rejected(message) => AuthError::BadRequest(message),
135 KenAuthError::NotFound => AuthBusinessError::NotFound,
136 KenAuthError::Unauthenticated => AuthBusinessError::Unauthorized,
137 KenAuthError::Rejected(message) => AuthBusinessError::BadRequest(message),
143138 KenAuthError::Unavailable(message)
144139 | KenAuthError::Config(message)
145 | KenAuthError::Internal(message) => AuthError::Internal(message),
140 | KenAuthError::Internal(message) => AuthBusinessError::Internal(message),
146141 }
147142}
Added lib/auth/src/share/account/mod.rs View lib/auth/src/share/account/mod.rs
@@ -0,0 +1,2 @@
1pub mod types;
2pub mod validate;
Renamed lib/webapi/src/auth/share/types.rs → lib/auth/src/share/account/types.rs View lib/auth/src/share/account/types.rs

No content changes.

Renamed lib/webapi/src/auth/share/validate.rs → lib/auth/src/share/account/validate.rs View lib/auth/src/share/account/validate.rs
@@ -1,27 +1,35 @@
11use time::{Date, OffsetDateTime};
22
3use crate::auth::errors::AuthError;
3use crate::api_error::AuthBusinessError;
44
5pub fn validate_email(raw: &str) -> Result<String, AuthError> {
5pub fn validate_email(raw: &str) -> Result<String, AuthBusinessError> {
66 let email = raw.trim().to_lowercase();
77 if email.len() < 3 || email.len() > 254 {
8 return Err(AuthError::BadRequest("invalid email address".to_string()));
8 return Err(AuthBusinessError::BadRequest(
9 "invalid email address".to_string(),
10 ));
911 }
1012 if email.chars().any(|c| c.is_whitespace()) {
11 return Err(AuthError::BadRequest("invalid email address".to_string()));
13 return Err(AuthBusinessError::BadRequest(
14 "invalid email address".to_string(),
15 ));
1216 }
1317 let Some(at) = email.find('@') else {
14 return Err(AuthError::BadRequest("invalid email address".to_string()));
18 return Err(AuthBusinessError::BadRequest(
19 "invalid email address".to_string(),
20 ));
1521 };
1622 if at == 0 || at == email.len() - 1 || !email[at + 1..].contains('.') {
17 return Err(AuthError::BadRequest("invalid email address".to_string()));
23 return Err(AuthBusinessError::BadRequest(
24 "invalid email address".to_string(),
25 ));
1826 }
1927 Ok(email)
2028}
2129
22pub fn validate_username(name: &str) -> Result<String, AuthError> {
30pub fn validate_username(name: &str) -> Result<String, AuthBusinessError> {
2331 if name.is_empty() || name.len() > 64 {
24 return Err(AuthError::BadRequest(
32 return Err(AuthBusinessError::BadRequest(
2533 "username must be between 1 and 64 characters".to_string(),
2634 ));
2735 }
@@ -29,14 +37,14 @@
2937 .chars()
3038 .all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '.' | '-'))
3139 {
32 return Err(AuthError::BadRequest(
40 return Err(AuthBusinessError::BadRequest(
3341 "username may only contain letters, digits, underscores, dots and dashes".to_string(),
3442 ));
3543 }
3644 Ok(name.to_string())
3745}
3846
39pub fn derive_username(email: &str, explicit: Option<&str>) -> Result<String, AuthError> {
47pub fn derive_username(email: &str, explicit: Option<&str>) -> Result<String, AuthBusinessError> {
4048 match explicit {
4149 Some(name) => validate_username(name),
4250 None => {
@@ -47,9 +55,9 @@
4755 }
4856}
4957
50pub fn validate_age(date_of_birth: Option<Date>, min_age: u8) -> Result<(), AuthError> {
58pub fn validate_age(date_of_birth: Option<Date>, min_age: u8) -> Result<(), AuthBusinessError> {
5159 let Some(dob) = date_of_birth else {
52 return Err(AuthError::BadRequest(
60 return Err(AuthBusinessError::BadRequest(
5361 "date of birth is required".to_string(),
5462 ));
5563 };
@@ -59,7 +67,7 @@
5967 age -= 1;
6068 }
6169 if age < min_age as i32 {
62 return Err(AuthError::BadRequest(format!(
70 return Err(AuthBusinessError::BadRequest(format!(
6371 "you must be at least {min_age} years old"
6472 )));
6573 }
Renamed lib/auth/src/server/share/audit.rs → lib/auth/src/share/audit.rs View lib/auth/src/share/audit.rs
@@ -6,7 +6,7 @@
66use crate::db::DbConn;
77use crate::models::users::user_events::{self, UserEventType};
88
9use crate::server::errors::AuthResult;
9use crate::errors::AuthResult;
1010
1111/// 写一条用户事件;事件总线之外的持久审计兜底。
1212#[allow(clippy::too_many_arguments)]
Renamed lib/auth/src/server/share/codes.rs → lib/auth/src/share/codes.rs View lib/auth/src/share/codes.rs
@@ -2,8 +2,8 @@
22
33use crate::cache::Cache;
44
5use crate::server::errors::{AuthError, AuthResult};
6use crate::server::share::crypto;
5use crate::errors::{AuthError, AuthResult};
6use crate::share::crypto;
77
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99pub enum CodeAttempt {
Renamed lib/auth/src/server/share/crypto.rs → lib/auth/src/share/crypto.rs View lib/auth/src/share/crypto.rs
@@ -9,7 +9,7 @@
99use rand_core::{OsRng, RngCore};
1010use sha2::{Digest, Sha256};
1111
12use crate::server::errors::{AuthError, AuthResult};
12use crate::errors::{AuthError, AuthResult};
1313
1414const NONCE_LEN: usize = 12;
1515
Renamed lib/webapi/src/auth/share/guard.rs → lib/auth/src/share/guard.rs View lib/auth/src/share/guard.rs

No content changes.

Renamed lib/auth/src/server/share/mfa.rs → lib/auth/src/share/mfa.rs View lib/auth/src/share/mfa.rs
@@ -2,7 +2,7 @@
22use sea_orm::sea_query::{Expr, ExprTrait};
33use sea_orm::{ColumnTrait, Condition, EntityTrait, QueryFilter};
44
5use crate::server::{AuthError, AuthResult};
5use crate::{AuthError, AuthResult};
66
77#[tracing::instrument(skip_all, fields(request_id = %_request_id.unwrap_or_default()))]
88pub async fn consume_totp(
Renamed lib/auth/src/server/share/mod.rs → lib/auth/src/share/mod.rs View lib/auth/src/share/mod.rs
@@ -1,10 +1,13 @@
1pub mod account;
12pub mod audit;
23pub mod codes;
34pub mod crypto;
5pub mod guard;
46pub mod mfa;
57pub mod net;
68pub mod password;
79pub mod rate;
10pub mod rpc;
811pub mod totp;
912pub mod types;
1013pub mod user;
Renamed lib/auth/src/server/share/net.rs → lib/auth/src/share/net.rs View lib/auth/src/share/net.rs

No content changes.

Renamed lib/auth/src/server/share/password.rs → lib/auth/src/share/password.rs View lib/auth/src/share/password.rs
@@ -3,7 +3,7 @@
33
44use crate::config::auth::PasswordPolicy;
55
6use crate::server::errors::{AuthError, AuthResult};
6use crate::errors::{AuthError, AuthResult};
77
88const PHC_PREFIX: &str = "$argon2";
99const CLIENT_DIGEST_LEN: usize = 64;
Renamed lib/auth/src/server/share/rate.rs → lib/auth/src/share/rate.rs View lib/auth/src/share/rate.rs
@@ -2,7 +2,7 @@
22
33use crate::cache::Cache;
44
5use crate::server::errors::{AuthError, AuthResult};
5use crate::errors::{AuthError, AuthResult};
66
77#[tracing::instrument(skip_all, fields(request_id = %_request_id.unwrap_or_default()))]
88pub async fn rate_limit_once(
Renamed lib/webapi/src/auth/share/rpc.rs → lib/auth/src/share/rpc.rs View lib/auth/src/share/rpc.rs
@@ -1,18 +1,15 @@
1use std::sync::Arc;
2
3use auth::client::KenAuthClient;
41use export::AppState;
52use export::http::HeaderMap;
63use sha2::{Digest, Sha256};
74use time::OffsetDateTime;
85
9use crate::auth::errors::ApiError;
6use crate::api_error::ApiError;
107
11pub fn client(state: &AppState) -> Result<&Arc<KenAuthClient>, ApiError> {
8pub fn client(state: &AppState) -> Result<&dyn export::AuthApi, ApiError> {
129 state
1310 .kenauth
14 .as_ref()
15 .ok_or_else(|| ApiError::internal("kenoauth client is not configured"))
11 .as_deref()
12 .ok_or_else(|| ApiError::internal("auth service client is not configured"))
1613}
1714
1815pub fn client_ip(headers: &HeaderMap) -> String {
Renamed lib/auth/src/server/share/totp.rs → lib/auth/src/share/totp.rs View lib/auth/src/share/totp.rs
@@ -2,9 +2,9 @@
22use sha1::Sha1;
33
44use super::crypto::{constant_time_eq, now_secs, random_bytes};
5use crate::server::errors::{AuthError, AuthResult};
5use crate::errors::{AuthError, AuthResult};
66
7pub const TOTP_ISSUER: &str = "KenOAuth";
7pub const TOTP_ISSUER: &str = "Auth service";
88pub const TOTP_PERIOD_SECS: u64 = 30;
99pub const TOTP_DIGITS: u32 = 6;
1010pub const TOTP_SECRET_BYTES: usize = 20;
Renamed lib/auth/src/server/share/types.rs → lib/auth/src/share/types.rs View lib/auth/src/share/types.rs

No content changes.

Renamed lib/auth/src/server/share/user.rs → lib/auth/src/share/user.rs View lib/auth/src/share/user.rs
@@ -2,9 +2,9 @@
22//! gRPC 侧(`grpc::store`)re-export 这些函数,避免 service 反向依赖传输层。
33
44use crate::db::DbConn;
5use crate::errors::{AuthError, AuthResult};
56use crate::models::users::user_mfa::MfaStatus as ModelMfaStatus;
67use crate::models::users::{user_mfa, users};
7use crate::server::errors::{AuthError, AuthResult};
88use chrono::Utc;
99use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
1010
Renamed lib/auth/src/server/share/validate.rs → lib/auth/src/share/validate.rs View lib/auth/src/share/validate.rs
@@ -1,6 +1,6 @@
11use chrono::{Datelike, NaiveDate, Utc};
22
3use crate::server::errors::{AuthError, AuthResult};
3use crate::errors::{AuthError, AuthResult};
44
55pub const MAX_USERNAME_LEN: usize = 64;
66pub const MAX_DISPLAY_NAME_LEN: usize = 64;
Renamed lib/webapi/src/auth/templates/common.rs → lib/auth/src/templates/common.rs View lib/auth/src/templates/common.rs

No content changes.

Renamed lib/webapi/src/auth/templates/mod.rs → lib/auth/src/templates/mod.rs View lib/auth/src/templates/mod.rs

No content changes.

Renamed lib/webapi/src/auth/templates/page.rs → lib/auth/src/templates/page.rs View lib/auth/src/templates/page.rs
@@ -2,7 +2,7 @@
22use i18n::{UiLocale, catalog};
33use maud::html;
44
5use crate::auth::share::types::AgreementDoc;
5use crate::share::account::types::AgreementDoc;
66
77use super::common;
88
Renamed lib/auth/src/server/account_lifecycle.rs → lib/auth/src/account_lifecycle.rs View lib/auth/src/account_lifecycle.rs
@@ -4,10 +4,10 @@
44 ActiveModelTrait, ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, Set, TransactionTrait,
55};
66
7use crate::AuthSvc;
8use crate::errors::{AuthError, AuthResult};
79use crate::models::space::{OrganizationStatus, organization_members, organizations};
810use crate::models::users::{user_mfa, user_mfa_backup_codes, user_sessions, user_ssh_keys, users};
9use crate::server::AuthSvc;
10use crate::server::errors::{AuthError, AuthResult};
1111
1212impl AuthSvc {
1313 /// Soft-delete the account and revoke every credential. The row (and its
@@ -118,7 +118,7 @@
118118 active.update(&txn).await?;
119119 txn.commit().await?;
120120 if let Err(error) =
121 crate::server::grpc::store::drop_user_session_states(self.cache(), user_id, None).await
121 crate::internal::store::drop_user_session_states(self.cache(), user_id, None).await
122122 {
123123 tracing::warn!(%error, %user_id, "failed to drop purged account sessions");
124124 }
Renamed lib/webapi/src/auth/errors.rs → lib/auth/src/api_error.rs View lib/auth/src/api_error.rs
@@ -1,7 +1,7 @@
11use std::fmt;
22
33#[derive(Debug)]
4pub enum AuthError {
4pub enum AuthBusinessError {
55 BadRequest(String),
66 InvalidCredentials,
77 Unauthorized,
@@ -18,7 +18,7 @@
1818 ResetTokenInvalid,
1919}
2020
21impl fmt::Display for AuthError {
21impl fmt::Display for AuthBusinessError {
2222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2323 match self {
2424 Self::BadRequest(message) => write!(f, "{message}"),
@@ -39,9 +39,9 @@
3939 }
4040}
4141
42impl std::error::Error for AuthError {}
42impl std::error::Error for AuthBusinessError {}
4343
44impl From<deletion::DeletionError> for AuthError {
44impl From<deletion::DeletionError> for AuthBusinessError {
4545 fn from(error: deletion::DeletionError) -> Self {
4646 use deletion::DeletionError;
4747 match error {
@@ -58,10 +58,10 @@
5858 }
5959}
6060
61export::db_error_conversions!(AuthError);
62export::redis_error_conversion!(AuthError);
63export::export_error_conversion!(AuthError);
64export::json_payload_conversion!(AuthError);
61export::db_error_conversions!(AuthBusinessError);
62export::redis_error_conversion!(AuthBusinessError);
63export::export_error_conversion!(AuthBusinessError);
64export::json_payload_conversion!(AuthBusinessError);
6565
6666export::api_error! {
6767 log: "auth",
@@ -77,49 +77,49 @@
7777 }
7878}
7979
80impl From<AuthError> for ApiError {
81 fn from(error: AuthError) -> Self {
80impl From<AuthBusinessError> for ApiError {
81 fn from(error: AuthBusinessError) -> Self {
8282 match error {
83 AuthError::BadRequest(msg) => Self::bad_request(msg),
84 AuthError::InvalidCredentials => {
83 AuthBusinessError::BadRequest(msg) => Self::bad_request(msg),
84 AuthBusinessError::InvalidCredentials => {
8585 Self::build(400, Self::payload(400, "invalid credentials"))
8686 }
87 AuthError::Unauthorized => Self::unauthorized(),
88 AuthError::Forbidden => Self::forbidden(),
89 AuthError::NotFound => Self::not_found("auth resource not found"),
90 AuthError::Conflict(msg) => Self::conflict(msg),
91 AuthError::RateLimited => Self::rate_limited_exceeded(),
92 AuthError::TwoFactorExpired => Self::build(
87 AuthBusinessError::Unauthorized => Self::unauthorized(),
88 AuthBusinessError::Forbidden => Self::forbidden(),
89 AuthBusinessError::NotFound => Self::not_found("auth resource not found"),
90 AuthBusinessError::Conflict(msg) => Self::conflict(msg),
91 AuthBusinessError::RateLimited => Self::rate_limited_exceeded(),
92 AuthBusinessError::TwoFactorExpired => Self::build(
9393 400,
9494 Self::payload(400, "two-factor challenge expired")
9595 .with_error_code("two_factor_expired"),
9696 ),
97 AuthError::TwoFactorCodeInvalid => Self::build(
97 AuthBusinessError::TwoFactorCodeInvalid => Self::build(
9898 400,
9999 Self::payload(400, "invalid two-factor code")
100100 .with_error_code("two_factor_code_invalid"),
101101 ),
102 AuthError::RecoveryCodeInvalid => Self::build(
102 AuthBusinessError::RecoveryCodeInvalid => Self::build(
103103 400,
104104 Self::payload(400, "invalid or used recovery code")
105105 .with_error_code("recovery_code_invalid"),
106106 ),
107 AuthError::VerificationCodeExpired => Self::build(
107 AuthBusinessError::VerificationCodeExpired => Self::build(
108108 400,
109109 Self::payload(400, "verification code expired")
110110 .with_error_code("verification_code_expired"),
111111 ),
112 AuthError::VerificationCodeInvalid => Self::build(
112 AuthBusinessError::VerificationCodeInvalid => Self::build(
113113 400,
114114 Self::payload(400, "invalid verification code")
115115 .with_error_code("verification_code_invalid"),
116116 ),
117 AuthError::ResetTokenInvalid => Self::build(
117 AuthBusinessError::ResetTokenInvalid => Self::build(
118118 400,
119119 Self::payload(400, "invalid or expired reset token")
120120 .with_error_code("reset_token_invalid"),
121121 ),
122 AuthError::Internal(message) => Self::internal(message),
122 AuthBusinessError::Internal(message) => Self::internal(message),
123123 }
124124 }
125125}
Modified lib/auth/src/assets.rs View lib/auth/src/assets.rs
@@ -15,7 +15,7 @@
1515
1616/// 非 SPA 的首段路径:这些前缀只服务于接口,拼错时给 JSON 404,
1717/// 避免把已移除的旧路径回落到 index.html 变成 200。
18const API_PREFIXES: [&str; 3] = ["api", "auth", "oauth"];
18const API_PREFIXES: [&str; 2] = ["api", "auth"];
1919
2020fn looks_like_api(path: &str) -> bool {
2121 if path.starts_with(".well-known/") {
@@ -69,7 +69,7 @@
6969}
7070
7171fn api_not_found() -> HttpResponse {
72 HttpResponse::NotFound().json(crate::server::errors::ErrorBody {
72 HttpResponse::NotFound().json(crate::errors::ErrorBody {
7373 code: "not_found".to_owned(),
7474 message: "unknown api route".to_owned(),
7575 })
Added lib/auth/src/auth_api_adapter.rs View lib/auth/src/auth_api_adapter.rs
@@ -0,0 +1,814 @@
1use async_trait::async_trait;
2
3use crate::protos::auth as proto;
4use crate::service::AuthService;
5use export::{AuthApi, KenAuthError};
6
7#[derive(Clone)]
8pub struct AuthApiAdapter {
9 pub inner: AuthService,
10}
11
12impl AuthApiAdapter {
13 pub fn new(inner: AuthService) -> Self {
14 Self { inner }
15 }
16}
17
18#[async_trait]
19impl AuthApi for AuthApiAdapter {
20 async fn check_health(&self) -> Result<(), KenAuthError> {
21 self.inner.check_health().await
22 }
23
24 async fn change_password(
25 &self,
26 user_id: i64,
27 old_password_hash: &str,
28 new_password_hash: &str,
29 keep_session_id: &str,
30 client_ip: &str,
31 ) -> Result<proto::v1::ChangePasswordResponse, KenAuthError> {
32 self.inner
33 .change_password(
34 user_id,
35 old_password_hash,
36 new_password_hash,
37 keep_session_id,
38 client_ip,
39 )
40 .await
41 }
42
43 async fn get_mfa_status(
44 &self,
45 user_id: i64,
46 ) -> Result<proto::v1::GetMfaStatusResponse, KenAuthError> {
47 self.inner.get_mfa_status(user_id).await
48 }
49
50 async fn configure_totp(
51 &self,
52 user_id: i64,
53 name: &str,
54 client_ip: &str,
55 ) -> Result<proto::v1::ConfigureTotpResponse, KenAuthError> {
56 self.inner.configure_totp(user_id, name, client_ip).await
57 }
58
59 async fn enable_totp(
60 &self,
61 user_id: i64,
62 code: &str,
63 password_hash: &str,
64 client_ip: &str,
65 ) -> Result<proto::v1::EnableTotpResponse, KenAuthError> {
66 self.inner
67 .enable_totp(user_id, code, password_hash, client_ip)
68 .await
69 }
70
71 async fn disable_totp(
72 &self,
73 user_id: i64,
74 code: &str,
75 client_ip: &str,
76 ) -> Result<proto::v1::DisableTotpResponse, KenAuthError> {
77 self.inner.disable_totp(user_id, code, client_ip).await
78 }
79
80 async fn regenerate_recovery_codes(
81 &self,
82 user_id: i64,
83 password_hash: &str,
84 client_ip: &str,
85 ) -> Result<proto::v1::RegenerateRecoveryCodesResponse, KenAuthError> {
86 self.inner
87 .regenerate_recovery_codes(user_id, password_hash, client_ip)
88 .await
89 }
90
91 async fn list_user_ssh_keys(
92 &self,
93 user_id: i64,
94 include_revoked: bool,
95 page: proto::common::v1::PageRequest,
96 ) -> Result<proto::v1::ListUserSshKeysResponse, KenAuthError> {
97 self.inner
98 .list_user_ssh_keys(user_id, include_revoked, page)
99 .await
100 }
101
102 async fn add_ssh_key(
103 &self,
104 user_id: i64,
105 name: &str,
106 public_key: &str,
107 description: &str,
108 client_ip: &str,
109 ) -> Result<proto::v1::AddSshKeyResponse, KenAuthError> {
110 self.inner
111 .add_ssh_key(user_id, name, public_key, description, client_ip)
112 .await
113 }
114
115 async fn revoke_ssh_key(
116 &self,
117 user_id: i64,
118 key_id: &str,
119 reason: &str,
120 bump_security_version: bool,
121 ) -> Result<proto::v1::RevokeSshKeyResponse, KenAuthError> {
122 self.inner
123 .revoke_ssh_key(user_id, key_id, reason, bump_security_version)
124 .await
125 }
126
127 async fn list_user_sessions(
128 &self,
129 user_id: i64,
130 include_revoked: bool,
131 include_expired: bool,
132 page: proto::common::v1::PageRequest,
133 ) -> Result<proto::v1::ListUserSessionsResponse, KenAuthError> {
134 self.inner
135 .list_user_sessions(user_id, include_revoked, include_expired, page)
136 .await
137 }
138
139 async fn revoke_user_sessions(
140 &self,
141 user_id: i64,
142 except_session_id: &str,
143 reason: &str,
144 bump_security_version: bool,
145 ) -> Result<proto::v1::RevokeUserSessionsResponse, KenAuthError> {
146 self.inner
147 .revoke_user_sessions(user_id, except_session_id, reason, bump_security_version)
148 .await
149 }
150
151 async fn get_session(
152 &self,
153 user_id: i64,
154 version: i32,
155 session_id: &str,
156 ) -> Result<proto::internal::v1::AuthorityAccess, KenAuthError> {
157 self.inner.get_session(user_id, version, session_id).await
158 }
159
160 async fn delete_account(&self, user_id: i64, reason: &str) -> Result<(), KenAuthError> {
161 self.inner.delete_account(user_id, reason).await
162 }
163
164 async fn cancel_account_deletion(&self, user_id: i64) -> Result<(), KenAuthError> {
165 self.inner.cancel_account_deletion(user_id).await
166 }
167
168 async fn purge_account(&self, user_id: i64) -> Result<(), KenAuthError> {
169 self.inner.purge_account(user_id).await
170 }
171
172 async fn batch_get_authority_users(
173 &self,
174 user_ids: Vec<i64>,
175 ) -> Result<Vec<proto::internal::v1::AuthorityUser>, KenAuthError> {
176 self.inner.batch_get_authority_users(user_ids).await
177 }
178
179 async fn get_user_status(
180 &self,
181 user_id: i64,
182 ) -> Result<proto::common::v1::UserStatus, KenAuthError> {
183 self.inner.get_user_status(user_id).await
184 }
185
186 async fn get_user_public_by_ref(
187 &self,
188 key: proto::common::v1::user_ref::Key,
189 ) -> Result<proto::common::v1::UserPublic, KenAuthError> {
190 self.inner.get_user_public_by_ref(key).await
191 }
192
193 async fn get_authority_user(
194 &self,
195 user_id: i64,
196 ) -> Result<proto::internal::v1::AuthorityUser, KenAuthError> {
197 self.inner.get_authority_user(user_id).await
198 }
199
200 async fn update_authority_user(
201 &self,
202 user_id: i64,
203 username: Option<String>,
204 displayname: Option<String>,
205 clear_displayname: bool,
206 public_show_email: Option<bool>,
207 version: i32,
208 ) -> Result<proto::internal::v1::AuthorityUser, KenAuthError> {
209 self.inner
210 .update_authority_user(
211 user_id,
212 username,
213 displayname,
214 clear_displayname,
215 public_show_email,
216 version,
217 )
218 .await
219 }
220
221 async fn batch_get_user_public(
222 &self,
223 user_ids: Vec<i64>,
224 ) -> Result<Vec<proto::common::v1::UserPublic>, KenAuthError> {
225 self.inner.batch_get_user_public(user_ids).await
226 }
227
228 async fn get_user_public(
229 &self,
230 user_id: i64,
231 ) -> Result<proto::common::v1::UserPublic, KenAuthError> {
232 self.inner.get_user_public(user_id).await
233 }
234
235 async fn revoke_session(
236 &self,
237 user_id: i64,
238 session_id: &str,
239 reason: &str,
240 ) -> Result<(), KenAuthError> {
241 self.inner.revoke_session(user_id, session_id, reason).await
242 }
243
244 async fn lookup_ssh_key(
245 &self,
246 public_key: &str,
247 ) -> Result<Option<proto::common::v1::Principal>, KenAuthError> {
248 self.inner.lookup_ssh_key(public_key).await
249 }
250
251 async fn ensure_bot_user(
252 &self,
253 username: &str,
254 displayname: &str,
255 system: bool,
256 ) -> Result<proto::common::v1::UserPublic, KenAuthError> {
257 self.inner
258 .ensure_bot_user(username, displayname, system)
259 .await
260 }
261
262 async fn ensure_bot_member(
263 &self,
264 organization_id: &str,
265 user_id: i64,
266 role_keys: Vec<String>,
267 role_ids: Vec<String>,
268 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
269 self.inner
270 .ensure_bot_member(organization_id, user_id, role_keys, role_ids)
271 .await
272 }
273
274 async fn get_organization(
275 &self,
276 actor_user_id: i64,
277 organization_id: &str,
278 slug: &str,
279 ) -> Result<proto::v1::GetOrganizationResponse, KenAuthError> {
280 self.inner
281 .get_organization(actor_user_id, organization_id, slug)
282 .await
283 }
284
285 async fn get_member(
286 &self,
287 actor_user_id: i64,
288 organization_id: &str,
289 user_id: i64,
290 ) -> Result<Option<proto::v1::OrganizationMemberInfo>, KenAuthError> {
291 self.inner
292 .get_member(actor_user_id, organization_id, user_id)
293 .await
294 }
295
296 async fn list_members(
297 &self,
298 actor_user_id: i64,
299 organization_id: &str,
300 include_inactive: bool,
301 include_banned: bool,
302 page: proto::common::v1::PageRequest,
303 ) -> Result<proto::v1::ListMembersResponse, KenAuthError> {
304 self.inner
305 .list_members(
306 actor_user_id,
307 organization_id,
308 include_inactive,
309 include_banned,
310 page,
311 )
312 .await
313 }
314
315 async fn list_organizations(
316 &self,
317 actor_user_id: i64,
318 user_id: i64,
319 include_archived: bool,
320 page: proto::common::v1::PageRequest,
321 ) -> Result<proto::v1::ListOrganizationsResponse, KenAuthError> {
322 self.inner
323 .list_organizations(actor_user_id, user_id, include_archived, page)
324 .await
325 }
326
327 async fn list_public_organizations(
328 &self,
329 query: &str,
330 page: proto::common::v1::PageRequest,
331 ) -> Result<proto::v1::ListPublicOrganizationsResponse, KenAuthError> {
332 self.inner.list_public_organizations(query, page).await
333 }
334
335 async fn check_permissions(
336 &self,
337 actor_user_id: i64,
338 organization_id: &str,
339 user_id: i64,
340 permissions: Vec<String>,
341 ) -> Result<proto::v1::CheckPermissionsResponse, KenAuthError> {
342 self.inner
343 .check_permissions(actor_user_id, organization_id, user_id, permissions)
344 .await
345 }
346
347 async fn create_organization(
348 &self,
349 actor_user_id: i64,
350 slug: &str,
351 name: &str,
352 description: &str,
353 avatar_url: &str,
354 join_policy: Option<i32>,
355 allow_join: Option<bool>,
356 require_email_verified: Option<bool>,
357 require_mfa: Option<bool>,
358 welcome_message: Option<String>,
359 member_must_enable_2fa: Option<bool>,
360 allow_invite: Option<bool>,
361 allow_join_level: Option<i32>,
362 ) -> Result<proto::v1::CreateOrganizationResponse, KenAuthError> {
363 self.inner
364 .create_organization(
365 actor_user_id,
366 slug,
367 name,
368 description,
369 avatar_url,
370 join_policy,
371 allow_join,
372 require_email_verified,
373 require_mfa,
374 welcome_message,
375 member_must_enable_2fa,
376 allow_invite,
377 allow_join_level,
378 )
379 .await
380 }
381
382 async fn update_organization(
383 &self,
384 actor_user_id: i64,
385 organization_id: &str,
386 name: Option<String>,
387 description: Option<String>,
388 avatar_url: Option<String>,
389 expected_version: i32,
390 join_policy: Option<i32>,
391 allow_join: Option<bool>,
392 require_email_verified: Option<bool>,
393 require_mfa: Option<bool>,
394 welcome_message: Option<String>,
395 member_must_enable_2fa: Option<bool>,
396 allow_invite: Option<bool>,
397 allow_join_level: Option<i32>,
398 ) -> Result<proto::v1::OrganizationInfo, KenAuthError> {
399 self.inner
400 .update_organization(
401 actor_user_id,
402 organization_id,
403 name,
404 description,
405 avatar_url,
406 expected_version,
407 join_policy,
408 allow_join,
409 require_email_verified,
410 require_mfa,
411 welcome_message,
412 member_must_enable_2fa,
413 allow_invite,
414 allow_join_level,
415 )
416 .await
417 }
418
419 async fn archive_organization(
420 &self,
421 actor_user_id: i64,
422 organization_id: &str,
423 reason: &str,
424 ) -> Result<(), KenAuthError> {
425 self.inner
426 .archive_organization(actor_user_id, organization_id, reason)
427 .await
428 }
429
430 async fn restore_organization(
431 &self,
432 actor_user_id: i64,
433 organization_id: &str,
434 ) -> Result<(), KenAuthError> {
435 self.inner
436 .restore_organization(actor_user_id, organization_id)
437 .await
438 }
439
440 async fn delete_organization(
441 &self,
442 actor_user_id: i64,
443 organization_id: &str,
444 force: bool,
445 reason: &str,
446 ) -> Result<(), KenAuthError> {
447 self.inner
448 .delete_organization(actor_user_id, organization_id, force, reason)
449 .await
450 }
451
452 async fn add_member(
453 &self,
454 actor_user_id: i64,
455 organization_id: &str,
456 user_id: i64,
457 role: i32,
458 role_keys: Vec<String>,
459 ) -> Result<(proto::v1::OrganizationMemberInfo, bool), KenAuthError> {
460 self.inner
461 .add_member(actor_user_id, organization_id, user_id, role, role_keys)
462 .await
463 }
464
465 async fn remove_member(
466 &self,
467 actor_user_id: i64,
468 organization_id: &str,
469 user_id: i64,
470 reason: &str,
471 ) -> Result<bool, KenAuthError> {
472 self.inner
473 .remove_member(actor_user_id, organization_id, user_id, reason)
474 .await
475 }
476
477 async fn leave_organization(
478 &self,
479 actor_user_id: i64,
480 organization_id: &str,
481 ) -> Result<bool, KenAuthError> {
482 self.inner
483 .leave_organization(actor_user_id, organization_id)
484 .await
485 }
486
487 async fn update_member(
488 &self,
489 actor_user_id: i64,
490 organization_id: &str,
491 user_id: i64,
492 alias_name: Option<Option<String>>,
493 alias_avatar_url: Option<Option<String>>,
494 message_notifications: Option<String>,
495 muted: Option<bool>,
496 mute_until: Option<Option<prost_types::Timestamp>>,
497 mobile_push: Option<bool>,
498 suppress_everyone: Option<bool>,
499 suppress_roles: Option<bool>,
500 hide_muted_channels: Option<bool>,
501 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
502 self.inner
503 .update_member(
504 actor_user_id,
505 organization_id,
506 user_id,
507 alias_name,
508 alias_avatar_url,
509 message_notifications,
510 muted,
511 mute_until,
512 mobile_push,
513 suppress_everyone,
514 suppress_roles,
515 hide_muted_channels,
516 )
517 .await
518 }
519
520 async fn ban_member(
521 &self,
522 actor_user_id: i64,
523 organization_id: &str,
524 user_id: i64,
525 reason: &str,
526 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
527 self.inner
528 .ban_member(actor_user_id, organization_id, user_id, reason)
529 .await
530 }
531
532 async fn unban_member(
533 &self,
534 actor_user_id: i64,
535 organization_id: &str,
536 user_id: i64,
537 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
538 self.inner
539 .unban_member(actor_user_id, organization_id, user_id)
540 .await
541 }
542
543 async fn update_ban(
544 &self,
545 actor_user_id: i64,
546 organization_id: &str,
547 user_id: i64,
548 reason: Option<Option<String>>,
549 expires_at: Option<Option<prost_types::Timestamp>>,
550 allow_speak: Option<bool>,
551 allow_push_repo: Option<bool>,
552 allow_comment_any: Option<bool>,
553 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
554 self.inner
555 .update_ban(
556 actor_user_id,
557 organization_id,
558 user_id,
559 reason,
560 expires_at,
561 allow_speak,
562 allow_push_repo,
563 allow_comment_any,
564 )
565 .await
566 }
567
568 async fn create_role(
569 &self,
570 actor_user_id: i64,
571 organization_id: &str,
572 key: &str,
573 name: &str,
574 description: &str,
575 rank: i32,
576 self_assignable: bool,
577 permissions: Vec<String>,
578 ) -> Result<proto::v1::OrganizationRoleInfo, KenAuthError> {
579 self.inner
580 .create_role(
581 actor_user_id,
582 organization_id,
583 key,
584 name,
585 description,
586 rank,
587 self_assignable,
588 permissions,
589 )
590 .await
591 }
592
593 async fn update_role(
594 &self,
595 actor_user_id: i64,
596 organization_id: &str,
597 role_id: &str,
598 name: Option<String>,
599 description: Option<String>,
600 rank: Option<i32>,
601 self_assignable: Option<bool>,
602 expected_version: i32,
603 ) -> Result<proto::v1::OrganizationRoleInfo, KenAuthError> {
604 self.inner
605 .update_role(
606 actor_user_id,
607 organization_id,
608 role_id,
609 name,
610 description,
611 rank,
612 self_assignable,
613 expected_version,
614 )
615 .await
616 }
617
618 async fn set_role_permissions(
619 &self,
620 actor_user_id: i64,
621 organization_id: &str,
622 role_id: &str,
623 permissions: Vec<String>,
624 ) -> Result<proto::v1::OrganizationRoleInfo, KenAuthError> {
625 self.inner
626 .set_role_permissions(actor_user_id, organization_id, role_id, permissions)
627 .await
628 }
629
630 async fn delete_role(
631 &self,
632 actor_user_id: i64,
633 organization_id: &str,
634 role_id: &str,
635 ) -> Result<bool, KenAuthError> {
636 self.inner
637 .delete_role(actor_user_id, organization_id, role_id)
638 .await
639 }
640
641 async fn assign_role(
642 &self,
643 actor_user_id: i64,
644 organization_id: &str,
645 user_id: i64,
646 role_id: &str,
647 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
648 self.inner
649 .assign_role(actor_user_id, organization_id, user_id, role_id)
650 .await
651 }
652
653 async fn unassign_role(
654 &self,
655 actor_user_id: i64,
656 organization_id: &str,
657 user_id: i64,
658 role_id: &str,
659 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError> {
660 self.inner
661 .unassign_role(actor_user_id, organization_id, user_id, role_id)
662 .await
663 }
664
665 async fn create_invite(
666 &self,
667 actor_user_id: i64,
668 organization_id: &str,
669 method: i32,
670 max_uses: i32,
671 expires_at: Option<prost_types::Timestamp>,
672 role_ids: Vec<String>,
673 target_user_id: i64,
674 email: &str,
675 ) -> Result<(proto::v1::OrganizationInviteInfo, String), KenAuthError> {
676 self.inner
677 .create_invite(
678 actor_user_id,
679 organization_id,
680 method,
681 max_uses,
682 expires_at,
683 role_ids,
684 target_user_id,
685 email,
686 )
687 .await
688 }
689
690 async fn revoke_invite(
691 &self,
692 actor_user_id: i64,
693 organization_id: &str,
694 invite_id: &str,
695 ) -> Result<bool, KenAuthError> {
696 self.inner
697 .revoke_invite(actor_user_id, organization_id, invite_id)
698 .await
699 }
700
701 async fn redeem_invite(
702 &self,
703 actor_user_id: i64,
704 token: &str,
705 ) -> Result<proto::v1::RedeemInviteResponse, KenAuthError> {
706 self.inner.redeem_invite(actor_user_id, token).await
707 }
708
709 async fn create_join_rule(
710 &self,
711 actor_user_id: i64,
712 organization_id: &str,
713 role_id: &str,
714 trigger: &str,
715 matcher: &str,
716 enabled: bool,
717 ) -> Result<proto::v1::JoinRuleInfo, KenAuthError> {
718 self.inner
719 .create_join_rule(
720 actor_user_id,
721 organization_id,
722 role_id,
723 trigger,
724 matcher,
725 enabled,
726 )
727 .await
728 }
729
730 async fn update_join_rule(
731 &self,
732 actor_user_id: i64,
733 organization_id: &str,
734 rule_id: &str,
735 role_id: Option<String>,
736 trigger: Option<String>,
737 matcher: Option<String>,
738 enabled: Option<bool>,
739 ) -> Result<proto::v1::JoinRuleInfo, KenAuthError> {
740 self.inner
741 .update_join_rule(
742 actor_user_id,
743 organization_id,
744 rule_id,
745 role_id,
746 trigger,
747 matcher,
748 enabled,
749 )
750 .await
751 }
752
753 async fn delete_join_rule(
754 &self,
755 actor_user_id: i64,
756 organization_id: &str,
757 rule_id: &str,
758 ) -> Result<bool, KenAuthError> {
759 self.inner
760 .delete_join_rule(actor_user_id, organization_id, rule_id)
761 .await
762 }
763
764 async fn create_join_request(
765 &self,
766 actor_user_id: i64,
767 organization_id: &str,
768 note: &str,
769 ) -> Result<proto::v1::JoinRequestInfo, KenAuthError> {
770 self.inner
771 .create_join_request(actor_user_id, organization_id, note)
772 .await
773 }
774
775 async fn withdraw_join_request(
776 &self,
777 actor_user_id: i64,
778 organization_id: &str,
779 request_id: &str,
780 ) -> Result<proto::v1::JoinRequestInfo, KenAuthError> {
781 self.inner
782 .withdraw_join_request(actor_user_id, organization_id, request_id)
783 .await
784 }
785
786 async fn approve_join_request(
787 &self,
788 actor_user_id: i64,
789 organization_id: &str,
790 request_id: &str,
791 ) -> Result<
792 (
793 proto::v1::JoinRequestInfo,
794 Option<proto::v1::OrganizationMemberInfo>,
795 ),
796 KenAuthError,
797 > {
798 self.inner
799 .approve_join_request(actor_user_id, organization_id, request_id)
800 .await
801 }
802
803 async fn reject_join_request(
804 &self,
805 actor_user_id: i64,
806 organization_id: &str,
807 request_id: &str,
808 note: &str,
809 ) -> Result<proto::v1::JoinRequestInfo, KenAuthError> {
810 self.inner
811 .reject_join_request(actor_user_id, organization_id, request_id, note)
812 .await
813 }
814}
Added lib/auth/src/auth_api_error.rs View lib/auth/src/auth_api_error.rs
@@ -0,0 +1 @@
1pub use export::KenAuthError;
Renamed lib/auth/src/server/auth_svc.rs → lib/auth/src/auth_svc.rs View lib/auth/src/auth_svc.rs
@@ -46,7 +46,7 @@
4646
4747impl AuthSvc {
4848 #[allow(clippy::too_many_arguments)]
49 pub async fn new(
49 pub fn new(
5050 db: DbConn,
5151 cache: Cache,
5252 mail: Option<MailPublisher>,
@@ -54,8 +54,8 @@
5454 auth: AuthConfig,
5555 pow: PowConfig,
5656 session_max_age_secs: u64,
57 ) -> AuthResult<Self> {
58 Ok(Self {
57 ) -> Self {
58 Self {
5959 db,
6060 cache,
6161 mail,
@@ -63,7 +63,23 @@
6363 auth: Arc::new(auth),
6464 pow: Arc::new(pow),
6565 session_max_age_secs: session_max_age_secs.max(1),
66 })
66 }
67 }
68
69 pub fn from_state(state: &export::AppState) -> Self {
70 let db = DbConn::new(
71 state.database.primary().clone(),
72 state.database.replicas().to_vec(),
73 );
74 Self::new(
75 db,
76 state.cache.clone(),
77 state.mail.clone(),
78 state.events.clone(),
79 state.config.auth.clone(),
80 state.config.pow.clone(),
81 state.config.session.max_age.max(1) as u64,
82 )
6783 }
6884
6985 pub fn db(&self) -> &DbConn {
Renamed lib/auth/src/server/authority.rs → lib/auth/src/authority.rs View lib/auth/src/authority.rs
@@ -4,8 +4,8 @@
44use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, Set};
55use uuid::Uuid;
66
7use crate::server::share::{crypto, user as store};
8use crate::server::{AuthError, AuthResult, AuthSvc};
7use crate::share::{crypto, user as store};
8use crate::{AuthError, AuthResult, AuthSvc};
99
1010/// 会话凭据摘要的算法标识,落在 `user_sessions.hash_key_id`。
1111pub const SESSION_HASH_KEY_ID: &str = "logical-session-v1";
@@ -87,7 +87,7 @@
8787 .ok_or(AuthError::Unauthenticated)?;
8888 // `token_hash` 是会话凭据的摘要:只有拿着明文会话 ID 的一方能算出来。
8989 // 不校验它,单有数据库读权限的人就能直接拿行里的 ID 复会话。
90 if !crate::server::share::crypto::constant_time_eq(
90 if !crate::share::crypto::constant_time_eq(
9191 row.token_hash.as_bytes(),
9292 session_token_hash(id).as_bytes(),
9393 ) {
@@ -177,7 +177,7 @@
177177 }
178178 let mut active = user.clone().into_active_model();
179179 if let Some(name) = patch.get("username").and_then(|value| value.as_str()) {
180 let name = crate::server::share::validate::validate_username(name)
180 let name = crate::share::validate::validate_username(name)
181181 .map_err(|_| AuthError::InvalidUsername)?;
182182 active.lowername = Set(name.to_lowercase());
183183 active.username = Set(name);
@@ -185,7 +185,7 @@
185185 if let Some(value) = patch.get("displayname") {
186186 active.displayname = Set(match value.as_str() {
187187 Some(value) => Some(
188 crate::server::share::validate::validate_display_name(value)
188 crate::share::validate::validate_display_name(value)
189189 .map_err(|_| AuthError::InvalidDisplayname)?,
190190 ),
191191 None if value.is_null() => None,
@@ -212,7 +212,7 @@
212212 .ok_or(AuthError::VersionMismatch {
213213 current: user.version,
214214 })?;
215 crate::server::http::me::service::invalidate_me_cache(self.cache(), user_id).await;
215 crate::handlers::identity::me::service::invalidate_me_cache(self.cache(), user_id).await;
216216 Ok(updated)
217217 }
218218}
Renamed lib/auth/src/server/errors.rs → lib/auth/src/errors.rs View lib/auth/src/errors.rs

No content changes.

Modified lib/auth/src/lib.rs View lib/auth/src/lib.rs
@@ -1,14 +1,35 @@
1//! 认证中心:账号、会话、令牌、组织与 SSH 公钥的唯一入口。
2//!
3//! 服务端逻辑在 `server`,进程启动与关闭在 `runtime`,进程内 gRPC 客户端在 `client`;
4//! 其余基础设施(config、db、email、envelope、events、cache、models、
5//! oltp、protos、queue、session、shutdown、crypt)按原模块名重新导出。
1//! 认证业务:账号、会话、组织、SSH 公钥与登录页。
62
73#![recursion_limit = "512"]
84
5pub mod account_lifecycle;
6pub mod api_error;
97pub mod assets;
10pub mod client;
11pub mod server;
8pub mod auth_api_adapter;
9pub mod auth_api_error;
10pub mod auth_session;
11pub mod auth_svc;
12pub mod authority;
13pub mod errors;
14pub mod handlers;
15pub mod internal;
16pub mod migrate;
17pub mod openapi;
18pub mod openapi_account;
19pub mod pow;
20pub mod routes;
21pub mod routes_account;
22pub mod service;
23pub mod services;
24pub mod share;
25pub mod templates;
26
27pub use api_error::{ApiError, AuthBusinessError};
28pub use auth_api_adapter::AuthApiAdapter;
29pub use auth_api_error::KenAuthError;
30pub use auth_svc::AuthSvc;
31pub use errors::{AuthError, AuthResult};
32pub use service::AuthService;
1233
1334pub use cache;
1435pub use config;
Renamed lib/auth/src/server/migrate.rs → lib/auth/src/migrate.rs View lib/auth/src/migrate.rs
@@ -1,4 +1,4 @@
1use crate::server::errors::AuthResult;
1use crate::errors::AuthResult;
22
33const USER_PREFIX: &str = "models::users::";
44const SPACE_PREFIX: &str = "models::space::";
Added lib/auth/src/openapi.rs View lib/auth/src/openapi.rs
@@ -0,0 +1,96 @@
1use utoipa::OpenApi;
2
3#[derive(OpenApi)]
4#[openapi(
5 info(
6 title = "auth",
7 description = "认证与账号服务:工作量证明、口令与验证码登录、二次验证、注册、口令重置与 SSH 公钥。",
8 version = env!("CARGO_PKG_VERSION")
9 ),
10 paths(
11 crate::handlers::identity::challenge::handler::challenge,
12 crate::handlers::identity::login::handler::login,
13 crate::handlers::identity::login::handler::login_code,
14 crate::handlers::identity::login::handler::login_code_verify,
15 crate::handlers::identity::login::handler::login_verify,
16 crate::handlers::identity::logout::handler::logout,
17 crate::handlers::identity::me::handler::me,
18 crate::handlers::identity::register::handler::register_code,
19 crate::handlers::identity::register::handler::register,
20 crate::handlers::identity::password_reset::handler::password_reset_request,
21 crate::handlers::identity::password_reset::handler::password_reset,
22 crate::handlers::identity::ssh_key::handler::ssh_key_list,
23 crate::handlers::identity::ssh_key::handler::ssh_key_add,
24 crate::handlers::identity::ssh_key::handler::ssh_key_delete,
25 crate::handlers::identity::organization::handler::create_organization,
26 crate::handlers::identity::organization::handler::list_organizations,
27 crate::handlers::identity::organization::handler::get_organization,
28 crate::handlers::identity::organization::handler::update_organization,
29 crate::handlers::identity::organization::handler::archive_organization,
30 crate::handlers::identity::organization::handler::restore_organization,
31 crate::handlers::identity::organization::handler::delete_organization,
32 crate::handlers::identity::organization::handler::transfer_ownership,
33 crate::handlers::identity::organization::handler::leave_organization,
34 crate::handlers::identity::organization::handler::list_members,
35 crate::handlers::identity::organization::handler::get_member,
36 crate::handlers::identity::organization::handler::update_member,
37 crate::handlers::identity::organization::handler::update_self_member,
38 crate::handlers::identity::organization::handler::add_member,
39 crate::handlers::identity::organization::handler::remove_member,
40 crate::handlers::identity::organization::handler::set_member_role,
41 crate::handlers::identity::organization::handler::ban_member,
42 crate::handlers::identity::organization::handler::unban_member,
43 crate::handlers::identity::organization::handler::set_member_permissions,
44 crate::handlers::identity::organization::handler::list_bans,
45 crate::handlers::identity::organization::handler::get_ban,
46 crate::handlers::identity::organization::handler::update_ban,
47 crate::handlers::identity::organization::handler::unban_by_user,
48 crate::handlers::identity::organization::handler::list_roles,
49 crate::handlers::identity::organization::handler::get_role,
50 crate::handlers::identity::organization::handler::create_role,
51 crate::handlers::identity::organization::handler::update_role,
52 crate::handlers::identity::organization::handler::delete_role,
53 crate::handlers::identity::organization::handler::set_role_permissions,
54 crate::handlers::identity::organization::handler::assign_role,
55 crate::handlers::identity::organization::handler::unassign_role,
56 crate::handlers::identity::organization::handler::join_role,
57 crate::handlers::identity::organization::handler::leave_role,
58 crate::handlers::identity::organization::handler::check_permission,
59 crate::handlers::identity::organization::handler::create_invite,
60 crate::handlers::identity::organization::handler::list_invites,
61 crate::handlers::identity::organization::handler::revoke_invite,
62 crate::handlers::identity::organization::handler::redeem_invite,
63 ),
64 components(schemas(
65 crate::handlers::identity::organization::types::OrganizationSummary,
66 crate::handlers::identity::organization::types::MemberSummary,
67 crate::handlers::identity::organization::types::RoleSummary,
68 crate::handlers::identity::organization::types::InviteSummary,
69 crate::handlers::identity::organization::types::PermissionGrant,
70 crate::handlers::identity::organization::types::PermissionCheck,
71 crate::handlers::identity::organization::types::CreateOrganizationBody,
72 crate::handlers::identity::organization::types::UpdateOrganizationBody,
73 crate::handlers::identity::organization::types::AddMemberBody,
74 crate::handlers::identity::organization::types::SetMemberRoleBody,
75 crate::handlers::identity::organization::types::BanMemberBody,
76 crate::handlers::identity::organization::types::CheckPermissionBody,
77 crate::handlers::identity::organization::types::CreateRoleBody,
78 crate::handlers::identity::organization::types::SetRolePermissionsBody,
79 crate::handlers::identity::organization::handler::OrgPath,
80 crate::handlers::identity::organization::handler::OrgMemberPath,
81 crate::handlers::identity::organization::handler::OrgRolePath,
82 crate::handlers::identity::organization::handler::OrgInvitePath,
83 crate::handlers::identity::organization::handler::OrgListQuery,
84 crate::handlers::identity::organization::handler::MemberListQuery,
85 crate::handlers::identity::organization::handler::UpdateRoleBody,
86 crate::handlers::identity::organization::handler::TransferBody,
87 crate::handlers::identity::organization::handler::ReasonBody,
88 crate::handlers::identity::organization::handler::DeleteOrgQuery,
89 crate::handlers::identity::organization::handler::CreateInviteBody,
90 crate::handlers::identity::organization::handler::RedeemInviteBody,
91 crate::handlers::identity::organization::handler::CreateInviteResponse,
92 crate::handlers::identity::organization::handler::RedeemInviteResponse,
93 crate::handlers::identity::organization::handler::AssignRoleBody,
94 ))
95)]
96pub struct ApiDoc;
Renamed lib/webapi/src/auth/openapi.rs → lib/auth/src/openapi_account.rs View lib/auth/src/openapi_account.rs
@@ -2,16 +2,16 @@
22use models::users::user_notifications::NotificationType;
33use utoipa::OpenApi;
44
5use crate::auth::handlers;
6use crate::auth::handlers::accept_agreement::AcceptAgreementCmd;
7use crate::auth::handlers::me::MeResp;
8use crate::auth::handlers::unread_notifications_count::UnreadCountResp;
9use crate::auth::services::agreements::AcceptAgreementResp;
10use crate::auth::services::notifications::NotificationDto;
11use crate::auth::services::preferences::{UpdatePreferencesCmd, UserPreferencesDto};
12use crate::auth::services::profile::{ProfileDto, UpdateProfileCmd, UserMeDto};
13use crate::auth::services::reports::{CreateReportCmd, ReportDto};
14use crate::auth::share::types::{AgreementDoc, UserPublic};
5use crate::handlers::account as handlers;
6use crate::handlers::account::accept_agreement::AcceptAgreementCmd;
7use crate::handlers::account::me::MeResp;
8use crate::handlers::account::unread_notifications_count::UnreadCountResp;
9use crate::services::agreements::AcceptAgreementResp;
10use crate::services::notifications::NotificationDto;
11use crate::services::preferences::{UpdatePreferencesCmd, UserPreferencesDto};
12use crate::services::profile::{ProfileDto, UpdateProfileCmd, UserMeDto};
13use crate::services::reports::{CreateReportCmd, ReportDto};
14use crate::share::account::types::{AgreementDoc, UserPublic};
1515
1616#[derive(OpenApi)]
1717#[openapi(
Added lib/auth/src/routes.rs View lib/auth/src/routes.rs
@@ -0,0 +1,216 @@
1use actix_web::web;
2
3pub fn configure(cfg: &mut web::ServiceConfig) {
4 cfg.service(web::scope("/api/v1").configure(configure_api));
5}
6
7pub fn configure_api(cfg: &mut web::ServiceConfig) {
8 cfg.route(
9 "/auth/challenge",
10 web::post().to(crate::handlers::identity::challenge::handler::challenge),
11 );
12 cfg.route(
13 "/auth/login",
14 web::post().to(crate::handlers::identity::login::handler::login),
15 );
16 cfg.route(
17 "/auth/login/code",
18 web::post().to(crate::handlers::identity::login::handler::login_code),
19 );
20 cfg.route(
21 "/auth/login/code/verify",
22 web::post().to(crate::handlers::identity::login::handler::login_code_verify),
23 );
24 cfg.route(
25 "/auth/login/verify",
26 web::post().to(crate::handlers::identity::login::handler::login_verify),
27 );
28 cfg.route(
29 "/auth/logout",
30 web::post().to(crate::handlers::identity::logout::handler::logout),
31 );
32 cfg.route(
33 "/auth/me",
34 web::get().to(crate::handlers::identity::me::handler::me),
35 );
36 cfg.route(
37 "/auth/register/code",
38 web::post().to(crate::handlers::identity::register::handler::register_code),
39 );
40 cfg.route(
41 "/auth/register",
42 web::post().to(crate::handlers::identity::register::handler::register),
43 );
44 cfg.route(
45 "/auth/password/reset",
46 web::post().to(crate::handlers::identity::password_reset::handler::password_reset_request),
47 );
48 cfg.route(
49 "/auth/password/reset/confirm",
50 web::post().to(crate::handlers::identity::password_reset::handler::password_reset),
51 );
52 cfg.route(
53 "/auth/ssh-keys",
54 web::get().to(crate::handlers::identity::ssh_key::handler::ssh_key_list),
55 );
56 cfg.route(
57 "/auth/ssh-keys",
58 web::post().to(crate::handlers::identity::ssh_key::handler::ssh_key_add),
59 );
60 cfg.route(
61 "/auth/ssh-keys/{key_id}",
62 web::delete().to(crate::handlers::identity::ssh_key::handler::ssh_key_delete),
63 );
64 cfg.route(
65 "/organizations",
66 web::post().to(crate::handlers::identity::organization::handler::create_organization),
67 );
68 cfg.route(
69 "/organizations",
70 web::get().to(crate::handlers::identity::organization::handler::list_organizations),
71 );
72 cfg.route(
73 "/organizations/invites/redeem",
74 web::post().to(crate::handlers::identity::organization::handler::redeem_invite),
75 );
76 cfg.route(
77 "/organizations/{organization_id}",
78 web::get().to(crate::handlers::identity::organization::handler::get_organization),
79 );
80 cfg.route(
81 "/organizations/{organization_id}",
82 web::patch().to(crate::handlers::identity::organization::handler::update_organization),
83 );
84 cfg.route(
85 "/organizations/{organization_id}",
86 web::delete().to(crate::handlers::identity::organization::handler::delete_organization),
87 );
88 cfg.route(
89 "/organizations/{organization_id}/archive",
90 web::post().to(crate::handlers::identity::organization::handler::archive_organization),
91 );
92 cfg.route(
93 "/organizations/{organization_id}/restore",
94 web::post().to(crate::handlers::identity::organization::handler::restore_organization),
95 );
96 cfg.route(
97 "/organizations/{organization_id}/transfer",
98 web::post().to(crate::handlers::identity::organization::handler::transfer_ownership),
99 );
100 cfg.route(
101 "/organizations/{organization_id}/leave",
102 web::post().to(crate::handlers::identity::organization::handler::leave_organization),
103 );
104 cfg.route(
105 "/organizations/{organization_id}/members",
106 web::get().to(crate::handlers::identity::organization::handler::list_members),
107 );
108 cfg.route(
109 "/organizations/{organization_id}/members",
110 web::post().to(crate::handlers::identity::organization::handler::add_member),
111 );
112 cfg.route(
113 "/organizations/{organization_id}/members/@me",
114 web::patch().to(crate::handlers::identity::organization::handler::update_self_member),
115 );
116 cfg.route(
117 "/organizations/{organization_id}/members/{member_id}",
118 web::get().to(crate::handlers::identity::organization::handler::get_member),
119 );
120 cfg.route(
121 "/organizations/{organization_id}/members/{member_id}",
122 web::patch().to(crate::handlers::identity::organization::handler::update_member),
123 );
124 cfg.route(
125 "/organizations/{organization_id}/members/{member_id}",
126 web::delete().to(crate::handlers::identity::organization::handler::remove_member),
127 );
128 cfg.route(
129 "/organizations/{organization_id}/members/{member_id}/role",
130 web::patch().to(crate::handlers::identity::organization::handler::set_member_role),
131 );
132 cfg.route(
133 "/organizations/{organization_id}/members/{member_id}/ban",
134 web::post().to(crate::handlers::identity::organization::handler::ban_member),
135 );
136 cfg.route(
137 "/organizations/{organization_id}/members/{member_id}/unban",
138 web::post().to(crate::handlers::identity::organization::handler::unban_member),
139 );
140 cfg.route(
141 "/organizations/{organization_id}/members/{member_id}/permissions",
142 web::put().to(crate::handlers::identity::organization::handler::set_member_permissions),
143 );
144 cfg.route(
145 "/organizations/{organization_id}/bans",
146 web::get().to(crate::handlers::identity::organization::handler::list_bans),
147 );
148 cfg.route(
149 "/organizations/{organization_id}/bans/{user_id}",
150 web::get().to(crate::handlers::identity::organization::handler::get_ban),
151 );
152 cfg.route(
153 "/organizations/{organization_id}/bans/{user_id}",
154 web::patch().to(crate::handlers::identity::organization::handler::update_ban),
155 );
156 cfg.route(
157 "/organizations/{organization_id}/bans/{user_id}",
158 web::delete().to(crate::handlers::identity::organization::handler::unban_by_user),
159 );
160 cfg.route(
161 "/organizations/{organization_id}/roles",
162 web::get().to(crate::handlers::identity::organization::handler::list_roles),
163 );
164 cfg.route(
165 "/organizations/{organization_id}/roles",
166 web::post().to(crate::handlers::identity::organization::handler::create_role),
167 );
168 cfg.route(
169 "/organizations/{organization_id}/roles/assign",
170 web::post().to(crate::handlers::identity::organization::handler::assign_role),
171 );
172 cfg.route(
173 "/organizations/{organization_id}/roles/unassign",
174 web::post().to(crate::handlers::identity::organization::handler::unassign_role),
175 );
176 cfg.route(
177 "/organizations/{organization_id}/roles/{role_id}",
178 web::get().to(crate::handlers::identity::organization::handler::get_role),
179 );
180 cfg.route(
181 "/organizations/{organization_id}/roles/{role_id}",
182 web::patch().to(crate::handlers::identity::organization::handler::update_role),
183 );
184 cfg.route(
185 "/organizations/{organization_id}/roles/{role_id}/join",
186 web::post().to(crate::handlers::identity::organization::handler::join_role),
187 );
188 cfg.route(
189 "/organizations/{organization_id}/roles/{role_id}/join",
190 web::delete().to(crate::handlers::identity::organization::handler::leave_role),
191 );
192 cfg.route(
193 "/organizations/{organization_id}/roles/{role_id}",
194 web::delete().to(crate::handlers::identity::organization::handler::delete_role),
195 );
196 cfg.route(
197 "/organizations/{organization_id}/roles/{role_id}/permissions",
198 web::put().to(crate::handlers::identity::organization::handler::set_role_permissions),
199 );
200 cfg.route(
201 "/organizations/{organization_id}/permissions/check",
202 web::post().to(crate::handlers::identity::organization::handler::check_permission),
203 );
204 cfg.route(
205 "/organizations/{organization_id}/invites",
206 web::post().to(crate::handlers::identity::organization::handler::create_invite),
207 );
208 cfg.route(
209 "/organizations/{organization_id}/invites",
210 web::get().to(crate::handlers::identity::organization::handler::list_invites),
211 );
212 cfg.route(
213 "/organizations/{organization_id}/invites/{invite_id}",
214 web::delete().to(crate::handlers::identity::organization::handler::revoke_invite),
215 );
216}
Renamed lib/webapi/src/auth/routes.rs → lib/auth/src/routes_account.rs View lib/auth/src/routes_account.rs
@@ -1,31 +1,26 @@
11use export::http::Router;
22use export::http::routing::{delete, get, post, put};
33
4use crate::auth::handlers::accept_agreement::accept_agreement;
5use crate::auth::handlers::account_deletion::{
4use crate::handlers::account::accept_agreement::accept_agreement;
5use crate::handlers::account::account_deletion::{
66 account_deletion_status, cancel_account_deletion, request_account_deletion,
77};
8use crate::auth::handlers::agreement_page::agreement_page;
9use crate::auth::handlers::create_report::create_report;
10use crate::auth::handlers::delete_notification::delete_notification;
11use crate::auth::handlers::get_preferences::get_preferences;
12use crate::auth::handlers::get_user::get_user;
13use crate::auth::handlers::list_agreements::list_agreements;
14use crate::auth::handlers::list_notifications::list_notifications;
15use crate::auth::handlers::me::me;
16use crate::auth::handlers::unread_notifications_count::unread_notifications_count;
17use crate::auth::handlers::update_preferences::update_preferences;
18use crate::auth::handlers::update_profile::update_profile;
19use crate::auth::handlers::users;
20use crate::auth::services::AuthService;
21use crate::auth::session;
8use crate::handlers::account::agreement_page::agreement_page;
9use crate::handlers::account::create_report::create_report;
10use crate::handlers::account::delete_notification::delete_notification;
11use crate::handlers::account::get_preferences::get_preferences;
12use crate::handlers::account::get_user::get_user;
13use crate::handlers::account::list_agreements::list_agreements;
14use crate::handlers::account::list_notifications::list_notifications;
15use crate::handlers::account::me::me;
16use crate::handlers::account::unread_notifications_count::unread_notifications_count;
17use crate::handlers::account::update_preferences::update_preferences;
18use crate::handlers::account::update_profile::update_profile;
19use crate::handlers::account::users;
20use crate::service::AuthService;
2221
2322pub fn router(_service: AuthService) -> Router<AuthService> {
24 let public = Router::<AuthService>::new()
25 .route("/auth/login", get(session::login::login))
26 .route("/auth/signup", get(session::login::signup))
27 .route("/auth/logout", post(session::logout::logout))
28 .route("/auth/agreements", get(list_agreements));
23 let public = Router::<AuthService>::new().route("/auth/agreements", get(list_agreements));
2924
3025 let authed = Router::<AuthService>::new()
3126 .route("/users", get(users::list_users_batch))
@@ -58,46 +53,46 @@
5853 )
5954 .route(
6055 "/me/notifications/read",
61 put(crate::auth::handlers::mark_notifications_read::mark_notifications_read),
56 put(crate::handlers::account::mark_notifications_read::mark_notifications_read),
6257 )
6358 .route("/me/reports", post(create_report))
6459 .route(
6560 "/auth/password/change",
66 post(crate::auth::handlers::password::change_password),
61 post(crate::handlers::account::password::change_password),
6762 )
6863 .route(
6964 "/auth/2fa/status",
70 get(crate::auth::handlers::two_factor::totp_status),
65 get(crate::handlers::account::two_factor::totp_status),
7166 )
7267 .route(
7368 "/auth/2fa/setup",
74 post(crate::auth::handlers::two_factor::totp_setup),
69 post(crate::handlers::account::two_factor::totp_setup),
7570 )
7671 .route(
7772 "/auth/2fa/enable",
78 post(crate::auth::handlers::two_factor::totp_enable),
73 post(crate::handlers::account::two_factor::totp_enable),
7974 )
8075 .route(
8176 "/auth/2fa/disable",
82 post(crate::auth::handlers::two_factor::totp_disable),
77 post(crate::handlers::account::two_factor::totp_disable),
8378 )
8479 .route(
8580 "/auth/2fa/recovery-codes",
86 get(crate::auth::handlers::two_factor::recovery_codes)
87 .put(crate::auth::handlers::two_factor::regenerate_recovery_codes),
81 get(crate::handlers::account::two_factor::recovery_codes)
82 .put(crate::handlers::account::two_factor::regenerate_recovery_codes),
8883 )
8984 .route(
9085 "/me/sessions",
91 get(crate::auth::handlers::sessions::list_sessions)
92 .delete(crate::auth::handlers::sessions::revoke_all_sessions),
86 get(crate::handlers::account::sessions::list_sessions)
87 .delete(crate::handlers::account::sessions::revoke_all_sessions),
9388 )
9489 .route(
9590 "/me/sessions/{session_id}",
96 delete(crate::auth::handlers::sessions::revoke_session),
91 delete(crate::handlers::account::sessions::revoke_session),
9792 )
9893 .route(
9994 "/me/devices",
100 get(crate::auth::handlers::devices::list_devices),
95 get(crate::handlers::account::devices::list_devices),
10196 )
10297 .wrap(export::http::Guard::Session);
10398
Renamed lib/auth/src/client/client.rs → lib/auth/src/service.rs View lib/auth/src/service.rs
@@ -1,31 +1,88 @@
1use std::sync::Arc;
12use std::time::Duration;
23
3use crate::server::AuthSvc;
4use config::AppConfig;
5use export::{AppDatabase, AppRedis, AppState};
46use tonic::Request;
57
6use crate::client::error::KenAuthError;
8use crate::AuthSvc;
9use crate::auth_api_error::KenAuthError;
710use crate::protos::auth as proto;
811
12#[derive(Clone, Debug)]
13pub struct VerifiedAccessKey {
14 pub user_id: i64,
15 pub scope: models::users::access_scope::AccessKeyScope,
16}
17
918#[derive(Clone)]
10pub struct KenAuthClient {
11 service: AuthSvc,
19pub struct AuthService {
20 pub state: AppState,
21 service: Arc<AuthSvc>,
1222 read_timeout: Duration,
1323 write_timeout: Duration,
1424}
1525
16impl KenAuthClient {
17 pub fn local(service: AuthSvc, timeout_ms: u64) -> Self {
26impl AuthService {
27 pub fn new(state: AppState) -> Self {
28 let service = Arc::new(AuthSvc::from_state(&state));
29 Self {
30 state,
31 service,
32 read_timeout: Duration::from_millis(300),
33 write_timeout: Duration::from_millis(2_000),
34 }
35 }
36
37 pub fn with_domain(state: AppState, service: Arc<AuthSvc>) -> Self {
1838 Self {
39 state,
1940 service,
2041 read_timeout: Duration::from_millis(300),
21 write_timeout: Duration::from_millis(timeout_ms.max(2000)),
42 write_timeout: Duration::from_millis(2_000),
2243 }
2344 }
2445
2546 pub fn service(&self) -> &AuthSvc {
47 self.service.as_ref()
48 }
49
50 pub fn domain(&self) -> &Arc<AuthSvc> {
2651 &self.service
2752 }
2853
54 pub fn config(&self) -> &AppConfig {
55 &self.state.config
56 }
57
58 pub fn db(&self) -> &Arc<AppDatabase> {
59 &self.state.database
60 }
61
62 pub fn redis(&self) -> &AppRedis {
63 &self.state.redis
64 }
65
66 pub async fn mail_locale(
67 &self,
68 uid: Option<i64>,
69 accept_language: Option<&str>,
70 ) -> i18n::UiLocale {
71 let fallback = i18n::UiLocale::parse(self.config().email.default_locale())
72 .unwrap_or(i18n::UiLocale::En);
73 match uid {
74 Some(uid) => i18n::resolve_user_locale(self.db().read(), uid, fallback).await,
75 None => i18n::negotiate_accept_language(accept_language, fallback),
76 }
77 }
78
79 pub async fn verify_access_token(
80 &self,
81 _token: &str,
82 ) -> Result<Option<VerifiedAccessKey>, crate::api_error::AuthBusinessError> {
83 Ok(None)
84 }
85
2986 pub async fn check_health(&self) -> Result<(), KenAuthError> {
3087 self.service
3188 .cache()
@@ -34,36 +91,36 @@
3491 .map_err(|error| KenAuthError::Unavailable(error.to_string()))
3592 }
3693
37 pub fn identity(&self) -> crate::client::local::IdentityClient {
38 crate::client::local::IdentityClient::new(self.service.clone())
94 pub fn identity(&self) -> crate::internal::bridge::IdentityClient {
95 crate::internal::bridge::IdentityClient::new(self.service.as_ref().clone())
3996 }
4097
41 pub fn authority(&self) -> crate::client::local::AuthorityClient {
42 crate::client::local::AuthorityClient::new(self.service.clone())
98 pub fn authority(&self) -> crate::internal::bridge::AuthorityClient {
99 crate::internal::bridge::AuthorityClient::new(self.service.as_ref().clone())
43100 }
44101
45 pub fn session(&self) -> crate::client::local::SessionClient {
46 crate::client::local::SessionClient::new(self.service.clone())
102 pub fn session(&self) -> crate::internal::bridge::SessionClient {
103 crate::internal::bridge::SessionClient::new(self.service.as_ref().clone())
47104 }
48105
49 pub fn organization(&self) -> crate::client::local::OrganizationClient {
50 crate::client::local::OrganizationClient::new(self.service.clone())
106 pub fn organization(&self) -> crate::internal::bridge::OrganizationClient {
107 crate::internal::bridge::OrganizationClient::new(self.service.as_ref().clone())
51108 }
52109
53 pub fn authorization(&self) -> crate::client::local::AuthorizationClient {
54 crate::client::local::AuthorizationClient::new(self.service.clone())
110 pub fn authorization(&self) -> crate::internal::bridge::AuthorizationClient {
111 crate::internal::bridge::AuthorizationClient::new(self.service.as_ref().clone())
55112 }
56113
57 pub fn ssh_key(&self) -> crate::client::local::SshKeyClient {
58 crate::client::local::SshKeyClient::new(self.service.clone())
114 pub fn ssh_key(&self) -> crate::internal::bridge::SshKeyClient {
115 crate::internal::bridge::SshKeyClient::new(self.service.as_ref().clone())
59116 }
60117
61 pub fn mfa(&self) -> crate::client::local::MfaClient {
62 crate::client::local::MfaClient::new(self.service.clone())
118 pub fn mfa(&self) -> crate::internal::bridge::MfaClient {
119 crate::internal::bridge::MfaClient::new(self.service.as_ref().clone())
63120 }
64121
65 pub fn agreement(&self) -> crate::client::local::AgreementClient {
66 crate::client::local::AgreementClient::new(self.service.clone())
122 pub fn agreement(&self) -> crate::internal::bridge::AgreementClient {
123 crate::internal::bridge::AgreementClient::new(self.service.as_ref().clone())
67124 }
68125
69126 #[allow(clippy::too_many_arguments)]
@@ -1371,3 +1428,11 @@
13711428 effect: proto::v1::PermissionEffect::Allow as i32,
13721429 }
13731430}
1431
1432impl std::ops::Deref for AuthService {
1433 type Target = AuthSvc;
1434
1435 fn deref(&self) -> &Self::Target {
1436 self.service.as_ref()
1437 }
1438}
Modified lib/auth/Cargo.toml View lib/auth/Cargo.toml
@@ -8,6 +8,9 @@
88
99[dependencies]
1010models = { workspace = true }
11export = { workspace = true }
12deletion = { workspace = true }
13i18n = { workspace = true }
1114config = { workspace = true }
1215crypt = { workspace = true }
1316db = { workspace = true }
@@ -17,6 +20,7 @@
1720cache = { workspace = true }
1821oltp = { workspace = true }
1922proto = { workspace = true }
23redis = { workspace = true }
2024queue = { workspace = true }
2125session = { workspace = true }
2226tokio-stream = { version = "0.1", features = ["net"] }
@@ -28,6 +32,9 @@
2832base64 = "0.23.1"
2933bytes = "1.12.1"
3034chrono = { version = "0.4.45", features = ["serde"] }
35time = { workspace = true }
36url = { workspace = true }
37pulldown-cmark = { workspace = true }
3138figment = { version = "0.10", default-features = false, features = [
3239 "toml",
3340 "json",
@@ -93,5 +100,5 @@
93100tracing = "0.1.44"
94101tracing-opentelemetry = "0.33.0"
95102tracing-subscriber = { version = "0.3.23", features = ["env-filter", "fmt"] }
96utoipa = { version = "6.0.0", features = ["actix_extras", "chrono", "uuid"] }
103utoipa = { workspace = true, features = ["chrono"] }
97104uuid = { version = "1.28.0", features = ["serde", "v4", "v7"] }
No newline at end of file
Modified lib/deletion/src/lib.rs View lib/deletion/src/lib.rs
@@ -1,7 +1,6 @@
11use std::time::Duration;
22
3use auth::client::error::KenAuthError;
4use export::AppState;
3use export::{AppState, KenAuthError};
54use models::dir::{dir_overwrite_group, dir_overwrite_users, dirs};
65use models::repo::repos;
76use models::room::rooms;
@@ -64,7 +63,7 @@
6463 DeletionError::KenShell(error.to_string())
6564}
6665
67fn client(state: &AppState) -> Result<&auth::client::KenAuthClient, DeletionError> {
66fn client(state: &AppState) -> Result<&dyn export::AuthApi, DeletionError> {
6867 state
6968 .kenauth
7069 .as_deref()
@@ -132,7 +131,7 @@
132131 user_id,
133132 user_id,
134133 true,
135 auth::protos::auth::common::v1::PageRequest {
134 proto::auth::common::v1::PageRequest {
136135 page_size: 100,
137136 page_token: String::new(),
138137 },
@@ -241,7 +240,7 @@
241240 .map_err(ken_auth)?
242241 .organization
243242 .ok_or_else(|| DeletionError::NotFound("organization not found".into()))?;
244 if organization.status != auth::protos::auth::v1::OrganizationStatus::Archived as i32 {
243 if organization.status != proto::auth::v1::OrganizationStatus::Archived as i32 {
245244 client
246245 .archive_organization(actor, &space_id.to_string(), reason)
247246 .await
@@ -590,7 +589,7 @@
590589 row.user_id,
591590 row.user_id,
592591 true,
593 auth::protos::auth::common::v1::PageRequest {
592 proto::auth::common::v1::PageRequest {
594593 page_size: 100,
595594 page_token: String::new(),
596595 },
Modified lib/deletion/Cargo.toml View lib/deletion/Cargo.toml
@@ -6,7 +6,6 @@
66[dependencies]
77config = { workspace = true }
88export = { workspace = true }
9auth = { workspace = true }
109proto = { workspace = true }
1110models = { workspace = true }
1211sea-orm = { workspace = true }
Modified lib/envelope/src/middleware.rs View lib/envelope/src/middleware.rs
@@ -2,11 +2,11 @@
22use std::rc::Rc;
33use std::task::{Context, Poll};
44
5use ::session::{REQUEST_ID_HEADER, RequestId, validate_request_id};
56use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
67use actix_web::http::header::{HeaderMap, HeaderName, HeaderValue};
78use actix_web::{Error, HttpMessage};
89use futures_util::future::LocalBoxFuture;
9use ::session::{REQUEST_ID_HEADER, RequestId, validate_request_id};
1010use uuid::Uuid;
1111
1212use crate::context;
Modified lib/envelope/Cargo.toml View lib/envelope/Cargo.toml
@@ -13,5 +13,5 @@
1313serde_json = { workspace = true }
1414tokio = { workspace = true, features = ["time"] }
1515tracing = { workspace = true }
16utoipa = { version = "6.0.0", features = ["actix_extras", "chrono", "uuid"] }
16utoipa = { workspace = true }
1717uuid = { workspace = true }
Added lib/export/src/auth_api.rs View lib/export/src/auth_api.rs
@@ -0,0 +1,464 @@
1use proto::auth as proto;
2use tonic::Status;
3
4#[derive(Debug, thiserror::Error)]
5pub enum KenAuthError {
6 #[error("kenoauth is unavailable: {0}")]
7 Unavailable(String),
8 #[error("kenoauth rejected the request: {0}")]
9 Rejected(String),
10 #[error("kenoauth could not find the requested object")]
11 NotFound,
12 #[error("kenoauth rejected the credential")]
13 Unauthenticated,
14 #[error("kenoauth configuration is invalid: {0}")]
15 Config(String),
16 #[error("kenoauth request failed: {0}")]
17 Internal(String),
18}
19
20impl KenAuthError {
21 pub fn from_status(status: Status) -> Self {
22 match status.code() {
23 tonic::Code::Unavailable | tonic::Code::DeadlineExceeded => {
24 Self::Unavailable(status.message().to_string())
25 }
26 tonic::Code::Unauthenticated => Self::Unauthenticated,
27 tonic::Code::NotFound => Self::NotFound,
28 tonic::Code::PermissionDenied
29 | tonic::Code::InvalidArgument
30 | tonic::Code::FailedPrecondition
31 | tonic::Code::AlreadyExists
32 | tonic::Code::ResourceExhausted
33 | tonic::Code::Aborted => Self::Rejected(status.message().to_string()),
34 _ => Self::Internal(status.message().to_string()),
35 }
36 }
37
38 pub fn is_not_found(&self) -> bool {
39 matches!(self, Self::NotFound)
40 }
41
42 pub fn is_unauthenticated(&self) -> bool {
43 matches!(self, Self::Unauthenticated)
44 }
45}
46
47#[async_trait::async_trait]
48pub trait AuthApi: Send + Sync {
49 async fn check_health(&self) -> Result<(), KenAuthError>;
50 async fn change_password(
51 &self,
52 user_id: i64,
53 old_password_hash: &str,
54 new_password_hash: &str,
55 keep_session_id: &str,
56 client_ip: &str,
57 ) -> Result<proto::v1::ChangePasswordResponse, KenAuthError>;
58 async fn get_mfa_status(
59 &self,
60 user_id: i64,
61 ) -> Result<proto::v1::GetMfaStatusResponse, KenAuthError>;
62 async fn configure_totp(
63 &self,
64 user_id: i64,
65 name: &str,
66 client_ip: &str,
67 ) -> Result<proto::v1::ConfigureTotpResponse, KenAuthError>;
68 async fn enable_totp(
69 &self,
70 user_id: i64,
71 code: &str,
72 password_hash: &str,
73 client_ip: &str,
74 ) -> Result<proto::v1::EnableTotpResponse, KenAuthError>;
75 async fn disable_totp(
76 &self,
77 user_id: i64,
78 code: &str,
79 client_ip: &str,
80 ) -> Result<proto::v1::DisableTotpResponse, KenAuthError>;
81 async fn regenerate_recovery_codes(
82 &self,
83 user_id: i64,
84 password_hash: &str,
85 client_ip: &str,
86 ) -> Result<proto::v1::RegenerateRecoveryCodesResponse, KenAuthError>;
87 async fn list_user_ssh_keys(
88 &self,
89 user_id: i64,
90 include_revoked: bool,
91 page: proto::common::v1::PageRequest,
92 ) -> Result<proto::v1::ListUserSshKeysResponse, KenAuthError>;
93 async fn add_ssh_key(
94 &self,
95 user_id: i64,
96 name: &str,
97 public_key: &str,
98 description: &str,
99 client_ip: &str,
100 ) -> Result<proto::v1::AddSshKeyResponse, KenAuthError>;
101 async fn revoke_ssh_key(
102 &self,
103 user_id: i64,
104 key_id: &str,
105 reason: &str,
106 bump_security_version: bool,
107 ) -> Result<proto::v1::RevokeSshKeyResponse, KenAuthError>;
108 async fn list_user_sessions(
109 &self,
110 user_id: i64,
111 include_revoked: bool,
112 include_expired: bool,
113 page: proto::common::v1::PageRequest,
114 ) -> Result<proto::v1::ListUserSessionsResponse, KenAuthError>;
115 async fn revoke_user_sessions(
116 &self,
117 user_id: i64,
118 except_session_id: &str,
119 reason: &str,
120 bump_security_version: bool,
121 ) -> Result<proto::v1::RevokeUserSessionsResponse, KenAuthError>;
122 async fn get_session(
123 &self,
124 user_id: i64,
125 version: i32,
126 session_id: &str,
127 ) -> Result<proto::internal::v1::AuthorityAccess, KenAuthError>;
128 async fn delete_account(&self, user_id: i64, reason: &str) -> Result<(), KenAuthError>;
129 async fn cancel_account_deletion(&self, user_id: i64) -> Result<(), KenAuthError>;
130 async fn purge_account(&self, user_id: i64) -> Result<(), KenAuthError>;
131 async fn batch_get_authority_users(
132 &self,
133 user_ids: Vec<i64>,
134 ) -> Result<Vec<proto::internal::v1::AuthorityUser>, KenAuthError>;
135 async fn get_user_status(
136 &self,
137 user_id: i64,
138 ) -> Result<proto::common::v1::UserStatus, KenAuthError>;
139 async fn get_user_public_by_ref(
140 &self,
141 key: proto::common::v1::user_ref::Key,
142 ) -> Result<proto::common::v1::UserPublic, KenAuthError>;
143 async fn get_authority_user(
144 &self,
145 user_id: i64,
146 ) -> Result<proto::internal::v1::AuthorityUser, KenAuthError>;
147 async fn update_authority_user(
148 &self,
149 user_id: i64,
150 username: Option<String>,
151 displayname: Option<String>,
152 clear_displayname: bool,
153 public_show_email: Option<bool>,
154 version: i32,
155 ) -> Result<proto::internal::v1::AuthorityUser, KenAuthError>;
156 async fn batch_get_user_public(
157 &self,
158 user_ids: Vec<i64>,
159 ) -> Result<Vec<proto::common::v1::UserPublic>, KenAuthError>;
160 async fn get_user_public(
161 &self,
162 user_id: i64,
163 ) -> Result<proto::common::v1::UserPublic, KenAuthError>;
164 async fn revoke_session(
165 &self,
166 user_id: i64,
167 session_id: &str,
168 reason: &str,
169 ) -> Result<(), KenAuthError>;
170 async fn lookup_ssh_key(
171 &self,
172 public_key: &str,
173 ) -> Result<Option<proto::common::v1::Principal>, KenAuthError>;
174 async fn ensure_bot_user(
175 &self,
176 username: &str,
177 displayname: &str,
178 system: bool,
179 ) -> Result<proto::common::v1::UserPublic, KenAuthError>;
180 async fn ensure_bot_member(
181 &self,
182 organization_id: &str,
183 user_id: i64,
184 role_keys: Vec<String>,
185 role_ids: Vec<String>,
186 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError>;
187 async fn get_organization(
188 &self,
189 actor_user_id: i64,
190 organization_id: &str,
191 slug: &str,
192 ) -> Result<proto::v1::GetOrganizationResponse, KenAuthError>;
193 async fn get_member(
194 &self,
195 actor_user_id: i64,
196 organization_id: &str,
197 user_id: i64,
198 ) -> Result<Option<proto::v1::OrganizationMemberInfo>, KenAuthError>;
199 async fn list_members(
200 &self,
201 actor_user_id: i64,
202 organization_id: &str,
203 include_inactive: bool,
204 include_banned: bool,
205 page: proto::common::v1::PageRequest,
206 ) -> Result<proto::v1::ListMembersResponse, KenAuthError>;
207 async fn list_organizations(
208 &self,
209 actor_user_id: i64,
210 user_id: i64,
211 include_archived: bool,
212 page: proto::common::v1::PageRequest,
213 ) -> Result<proto::v1::ListOrganizationsResponse, KenAuthError>;
214 async fn list_public_organizations(
215 &self,
216 query: &str,
217 page: proto::common::v1::PageRequest,
218 ) -> Result<proto::v1::ListPublicOrganizationsResponse, KenAuthError>;
219 async fn check_permissions(
220 &self,
221 actor_user_id: i64,
222 organization_id: &str,
223 user_id: i64,
224 permissions: Vec<String>,
225 ) -> Result<proto::v1::CheckPermissionsResponse, KenAuthError>;
226 async fn create_organization(
227 &self,
228 actor_user_id: i64,
229 slug: &str,
230 name: &str,
231 description: &str,
232 avatar_url: &str,
233 join_policy: Option<i32>,
234 allow_join: Option<bool>,
235 require_email_verified: Option<bool>,
236 require_mfa: Option<bool>,
237 welcome_message: Option<String>,
238 member_must_enable_2fa: Option<bool>,
239 allow_invite: Option<bool>,
240 allow_join_level: Option<i32>,
241 ) -> Result<proto::v1::CreateOrganizationResponse, KenAuthError>;
242 async fn update_organization(
243 &self,
244 actor_user_id: i64,
245 organization_id: &str,
246 name: Option<String>,
247 description: Option<String>,
248 avatar_url: Option<String>,
249 expected_version: i32,
250 join_policy: Option<i32>,
251 allow_join: Option<bool>,
252 require_email_verified: Option<bool>,
253 require_mfa: Option<bool>,
254 welcome_message: Option<String>,
255 member_must_enable_2fa: Option<bool>,
256 allow_invite: Option<bool>,
257 allow_join_level: Option<i32>,
258 ) -> Result<proto::v1::OrganizationInfo, KenAuthError>;
259 async fn archive_organization(
260 &self,
261 actor_user_id: i64,
262 organization_id: &str,
263 reason: &str,
264 ) -> Result<(), KenAuthError>;
265 async fn restore_organization(
266 &self,
267 actor_user_id: i64,
268 organization_id: &str,
269 ) -> Result<(), KenAuthError>;
270 async fn delete_organization(
271 &self,
272 actor_user_id: i64,
273 organization_id: &str,
274 force: bool,
275 reason: &str,
276 ) -> Result<(), KenAuthError>;
277 async fn add_member(
278 &self,
279 actor_user_id: i64,
280 organization_id: &str,
281 user_id: i64,
282 role: i32,
283 role_keys: Vec<String>,
284 ) -> Result<(proto::v1::OrganizationMemberInfo, bool), KenAuthError>;
285 async fn remove_member(
286 &self,
287 actor_user_id: i64,
288 organization_id: &str,
289 user_id: i64,
290 reason: &str,
291 ) -> Result<bool, KenAuthError>;
292 async fn leave_organization(
293 &self,
294 actor_user_id: i64,
295 organization_id: &str,
296 ) -> Result<bool, KenAuthError>;
297 async fn update_member(
298 &self,
299 actor_user_id: i64,
300 organization_id: &str,
301 user_id: i64,
302 alias_name: Option<Option<String>>,
303 alias_avatar_url: Option<Option<String>>,
304 message_notifications: Option<String>,
305 muted: Option<bool>,
306 mute_until: Option<Option<prost_types::Timestamp>>,
307 mobile_push: Option<bool>,
308 suppress_everyone: Option<bool>,
309 suppress_roles: Option<bool>,
310 hide_muted_channels: Option<bool>,
311 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError>;
312 async fn ban_member(
313 &self,
314 actor_user_id: i64,
315 organization_id: &str,
316 user_id: i64,
317 reason: &str,
318 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError>;
319 async fn unban_member(
320 &self,
321 actor_user_id: i64,
322 organization_id: &str,
323 user_id: i64,
324 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError>;
325 async fn update_ban(
326 &self,
327 actor_user_id: i64,
328 organization_id: &str,
329 user_id: i64,
330 reason: Option<Option<String>>,
331 expires_at: Option<Option<prost_types::Timestamp>>,
332 allow_speak: Option<bool>,
333 allow_push_repo: Option<bool>,
334 allow_comment_any: Option<bool>,
335 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError>;
336 async fn create_role(
337 &self,
338 actor_user_id: i64,
339 organization_id: &str,
340 key: &str,
341 name: &str,
342 description: &str,
343 rank: i32,
344 self_assignable: bool,
345 permissions: Vec<String>,
346 ) -> Result<proto::v1::OrganizationRoleInfo, KenAuthError>;
347 async fn update_role(
348 &self,
349 actor_user_id: i64,
350 organization_id: &str,
351 role_id: &str,
352 name: Option<String>,
353 description: Option<String>,
354 rank: Option<i32>,
355 self_assignable: Option<bool>,
356 expected_version: i32,
357 ) -> Result<proto::v1::OrganizationRoleInfo, KenAuthError>;
358 async fn set_role_permissions(
359 &self,
360 actor_user_id: i64,
361 organization_id: &str,
362 role_id: &str,
363 permissions: Vec<String>,
364 ) -> Result<proto::v1::OrganizationRoleInfo, KenAuthError>;
365 async fn delete_role(
366 &self,
367 actor_user_id: i64,
368 organization_id: &str,
369 role_id: &str,
370 ) -> Result<bool, KenAuthError>;
371 async fn assign_role(
372 &self,
373 actor_user_id: i64,
374 organization_id: &str,
375 user_id: i64,
376 role_id: &str,
377 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError>;
378 async fn unassign_role(
379 &self,
380 actor_user_id: i64,
381 organization_id: &str,
382 user_id: i64,
383 role_id: &str,
384 ) -> Result<proto::v1::OrganizationMemberInfo, KenAuthError>;
385 async fn create_invite(
386 &self,
387 actor_user_id: i64,
388 organization_id: &str,
389 method: i32,
390 max_uses: i32,
391 expires_at: Option<prost_types::Timestamp>,
392 role_ids: Vec<String>,
393 target_user_id: i64,
394 email: &str,
395 ) -> Result<(proto::v1::OrganizationInviteInfo, String), KenAuthError>;
396 async fn revoke_invite(
397 &self,
398 actor_user_id: i64,
399 organization_id: &str,
400 invite_id: &str,
401 ) -> Result<bool, KenAuthError>;
402 async fn redeem_invite(
403 &self,
404 actor_user_id: i64,
405 token: &str,
406 ) -> Result<proto::v1::RedeemInviteResponse, KenAuthError>;
407 async fn create_join_rule(
408 &self,
409 actor_user_id: i64,
410 organization_id: &str,
411 role_id: &str,
412 trigger: &str,
413 matcher: &str,
414 enabled: bool,
415 ) -> Result<proto::v1::JoinRuleInfo, KenAuthError>;
416 async fn update_join_rule(
417 &self,
418 actor_user_id: i64,
419 organization_id: &str,
420 rule_id: &str,
421 role_id: Option<String>,
422 trigger: Option<String>,
423 matcher: Option<String>,
424 enabled: Option<bool>,
425 ) -> Result<proto::v1::JoinRuleInfo, KenAuthError>;
426 async fn delete_join_rule(
427 &self,
428 actor_user_id: i64,
429 organization_id: &str,
430 rule_id: &str,
431 ) -> Result<bool, KenAuthError>;
432 async fn create_join_request(
433 &self,
434 actor_user_id: i64,
435 organization_id: &str,
436 note: &str,
437 ) -> Result<proto::v1::JoinRequestInfo, KenAuthError>;
438 async fn withdraw_join_request(
439 &self,
440 actor_user_id: i64,
441 organization_id: &str,
442 request_id: &str,
443 ) -> Result<proto::v1::JoinRequestInfo, KenAuthError>;
444 async fn approve_join_request(
445 &self,
446 actor_user_id: i64,
447 organization_id: &str,
448 request_id: &str,
449 ) -> Result<
450 (
451 proto::v1::JoinRequestInfo,
452 Option<proto::v1::OrganizationMemberInfo>,
453 ),
454 KenAuthError,
455 >;
456
457 async fn reject_join_request(
458 &self,
459 actor_user_id: i64,
460 organization_id: &str,
461 request_id: &str,
462 note: &str,
463 ) -> Result<proto::v1::JoinRequestInfo, KenAuthError>;
464}
Modified lib/export/src/health.rs View lib/export/src/health.rs
@@ -4,13 +4,13 @@
44use sea_orm::{ConnectionTrait, DbBackend, Statement};
55
66use crate::http::{Router, State, get};
7use crate::{AppDatabase, AppRedis, AppState};
7use crate::{AppDatabase, AppRedis, AppState, AuthApi};
88
99#[derive(Clone)]
1010struct HealthState {
1111 database: Option<Arc<AppDatabase>>,
1212 redis: AppRedis,
13 kenauth: Option<Arc<auth::client::KenAuthClient>>,
13 kenauth: Option<Arc<dyn AuthApi>>,
1414}
1515
1616async fn healthz() -> HttpResponse {
@@ -53,7 +53,7 @@
5353pub fn router_with(
5454 database: Option<Arc<AppDatabase>>,
5555 redis: AppRedis,
56 kenauth: Option<Arc<auth::client::KenAuthClient>>,
56 kenauth: Option<Arc<dyn AuthApi>>,
5757) -> Router {
5858 Router::<()>::new()
5959 .route("/healthz", get(healthz))
Modified lib/export/src/identity_client.rs View lib/export/src/identity_client.rs
@@ -1,13 +1,13 @@
11use std::sync::{Arc, OnceLock};
22
3use auth::client::KenAuthClient;
3use crate::AuthApi;
44
5static CLIENT: OnceLock<Arc<KenAuthClient>> = OnceLock::new();
5static CLIENT: OnceLock<Arc<dyn AuthApi>> = OnceLock::new();
66
7pub fn install(client: Arc<KenAuthClient>) {
7pub fn install(client: Arc<dyn AuthApi>) {
88 let _ = CLIENT.set(client);
99}
1010
11pub fn client() -> Option<&'static Arc<KenAuthClient>> {
11pub fn client() -> Option<&'static Arc<dyn AuthApi>> {
1212 CLIENT.get()
1313}
Modified lib/export/src/lib.rs View lib/export/src/lib.rs
@@ -1,4 +1,5 @@
11mod api_error;
2pub mod auth_api;
23pub mod cache;
34pub mod database;
45pub mod error;
@@ -24,6 +25,7 @@
2425use std::sync::Arc;
2526
2627pub use api_error::is_unique_violation;
28pub use auth_api::{AuthApi, KenAuthError};
2729use config::AppConfig;
2830pub use database::{AppDatabase, DatabaseRole};
2931pub use error::ExportError;
@@ -35,10 +37,12 @@
3537pub struct AppState {
3638 pub database: Arc<AppDatabase>,
3739 pub redis: AppRedis,
40 pub cache: ::cache::Cache,
3841 pub storage: Arc<AppStorage>,
3942 pub config: AppConfig,
40 pub kenauth: Option<Arc<auth::client::KenAuthClient>>,
41 pub mail: Option<auth::email::MailPublisher>,
43 pub kenauth: Option<Arc<dyn AuthApi>>,
44 pub mail: Option<::email::MailPublisher>,
45 pub events: Option<::events::EventHandler>,
4246}
4347
4448impl AppState {
@@ -50,16 +54,20 @@
5054
5155 pub async fn with_config(config: AppConfig) -> Result<Self, ExportError> {
5256 let database = Arc::new(AppDatabase::connect(config.database.clone()).await?);
53 Self::with_identity(config, database, None, None).await
57 Self::with_identity(config, database, None, None, None).await
5458 }
5559
5660 pub async fn with_identity(
5761 config: AppConfig,
5862 database: Arc<AppDatabase>,
59 identity: Option<Arc<auth::client::KenAuthClient>>,
60 mail: Option<auth::email::MailPublisher>,
63 identity: Option<Arc<dyn AuthApi>>,
64 mail: Option<::email::MailPublisher>,
65 events: Option<::events::EventHandler>,
6166 ) -> Result<Self, ExportError> {
6267 let mut redis = AppRedis::connect(config.redis.clone()).await?;
68 let cache = ::cache::Cache::connect(&config.redis)
69 .await
70 .map_err(|error| ExportError::Connection(error.to_string()))?;
6371 // 入队点遍布各业务 crate,但只有这里有配置:把开关写进句柄,让
6472 // `export::push::enqueue` 能自己判断该不该产生任务。
6573 redis.set_push_enabled(config.push.enabled);
@@ -73,10 +81,12 @@
7381 Ok(Self {
7482 database,
7583 redis,
84 cache,
7685 storage,
7786 config,
7887 kenauth,
7988 mail,
89 events,
8090 })
8191 }
8292}
Modified lib/export/src/users.rs View lib/export/src/users.rs
@@ -1,6 +1,6 @@
11use std::collections::HashMap;
22
3use auth::protos::auth::common::v1::UserPublic;
3use proto::auth::common::v1::UserPublic;
44
55use crate::AppState;
66use crate::error::ExportError;
@@ -38,7 +38,7 @@
3838 .ok_or_else(|| ExportError::InvalidConfig("kenoauth is not configured".to_string()))?;
3939 match client.get_user_public(uid).await {
4040 Ok(user) => Ok(Some(user)),
41 Err(auth::client::error::KenAuthError::NotFound) => Ok(None),
41 Err(crate::KenAuthError::NotFound) => Ok(None),
4242 Err(error) => Err(ExportError::Connection(error.to_string())),
4343 }
4444}
@@ -52,13 +52,13 @@
5252 .as_ref()
5353 .ok_or_else(|| ExportError::InvalidConfig("kenoauth is not configured".to_string()))?;
5454 match client
55 .get_user_public_by_ref(auth::protos::auth::common::v1::user_ref::Key::Email(
55 .get_user_public_by_ref(proto::auth::common::v1::user_ref::Key::Email(
5656 email.to_string(),
5757 ))
5858 .await
5959 {
6060 Ok(user) => Ok(Some(user)),
61 Err(auth::client::error::KenAuthError::NotFound) => Ok(None),
61 Err(crate::KenAuthError::NotFound) => Ok(None),
6262 Err(error) => Err(ExportError::Connection(error.to_string())),
6363 }
6464}
@@ -72,13 +72,13 @@
7272 .as_ref()
7373 .ok_or_else(|| ExportError::InvalidConfig("kenoauth is not configured".to_string()))?;
7474 match client
75 .get_user_public_by_ref(auth::protos::auth::common::v1::user_ref::Key::Username(
75 .get_user_public_by_ref(proto::auth::common::v1::user_ref::Key::Username(
7676 username.to_string(),
7777 ))
7878 .await
7979 {
8080 Ok(user) => Ok(Some(user)),
81 Err(auth::client::error::KenAuthError::NotFound) => Ok(None),
81 Err(crate::KenAuthError::NotFound) => Ok(None),
8282 Err(error) => Err(ExportError::Connection(error.to_string())),
8383 }
8484}
Modified lib/export/Cargo.toml View lib/export/Cargo.toml
@@ -5,13 +5,16 @@
55
66[dependencies]
77config = { workspace = true }
8cache = { workspace = true }
9email = { workspace = true }
10events = { workspace = true }
11proto = { workspace = true }
812models = { workspace = true }
913queue = { workspace = true }
1014storage = { workspace = true }
1115sea-orm = { workspace = true }
1216redis = { workspace = true }
1317tokio = { workspace = true, features = ["macros", "rt-multi-thread", "signal"] }
14auth = { workspace = true }
1518session = { workspace = true }
1619imaging = { workspace = true }
1720actix-web = { workspace = true }
@@ -32,4 +35,7 @@
3235uuid = { workspace = true }
3336bytes = { workspace = true }
3437thiserror = { workspace = true }
38async-trait = { workspace = true }
39tonic = { workspace = true }
3540time = { workspace = true }
41prost-types = { workspace = true }
Modified lib/models/src/space/mod.rs View lib/models/src/space/mod.rs
@@ -1,7 +1,6 @@
11use sea_orm::entity::prelude::*;
22use serde::{Deserialize, Serialize};
33use utoipa::ToSchema;
4use utoipa6 as utoipa;
54
65pub mod organization_invite_redemptions;
76pub mod organization_invites;
Modified lib/models/src/users/user_events.rs View lib/models/src/users/user_events.rs
@@ -1,5 +1,4 @@
11use utoipa::ToSchema;
2use utoipa6 as utoipa;
32
43use sea_orm::entity::Set;
54use sea_orm::entity::prelude::*;
Modified lib/models/Cargo.toml View lib/models/Cargo.toml
@@ -12,5 +12,4 @@
1212serde_json = { workspace = true }
1313proto = { workspace = true }
1414utoipa = { workspace = true }
15utoipa6 = { package = "utoipa", version = "6.0.0", features = ["chrono", "uuid"] }
1615tokio = { workspace = true }
No newline at end of file
Modified lib/space/src/services/spaces.rs View lib/space/src/services/spaces.rs
@@ -38,7 +38,7 @@
3838}
3939
4040impl UserDto {
41 pub fn from_public(public: &auth::protos::auth::common::v1::UserPublic) -> Self {
41 pub fn from_public(public: &proto::auth::common::v1::UserPublic) -> Self {
4242 Self {
4343 user_id: public.user_id,
4444 username: public.username.clone(),
Modified lib/space/src/authority.rs View lib/space/src/authority.rs
@@ -1,9 +1,9 @@
1use auth::protos::auth as proto;
21use export::AppState;
32use models::space::space_extras::{self, Entity as SpaceExtras};
43use models::space::space_member_extras::{self, Entity as MemberExtras};
54use models::space::space_members::{self, MemberStatus, MessageNotifications};
65use models::space::spaces::{self, AllowJoinLevel};
6use proto::auth as proto;
77use sea_orm::{ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, QueryFilter, Set};
88use time::OffsetDateTime;
99use uuid::Uuid;
@@ -12,7 +12,7 @@
1212
1313pub const RPC_KEY: &str = "kenoauth is not configured";
1414
15pub fn client(state: &AppState) -> Result<&auth::client::KenAuthClient, SpaceError> {
15pub fn client(state: &AppState) -> Result<&dyn export::AuthApi, SpaceError> {
1616 state
1717 .kenauth
1818 .as_deref()
@@ -191,7 +191,7 @@
191191 {
192192 Ok(Some(member)) => member,
193193 Ok(None) => return Ok(None),
194 Err(auth::client::error::KenAuthError::Rejected(message))
194 Err(export::KenAuthError::Rejected(message))
195195 if message.to_lowercase().contains("forbidden")
196196 || message.to_lowercase().contains("permission") =>
197197 {
Modified lib/space/src/errors.rs View lib/space/src/errors.rs
@@ -52,15 +52,15 @@
5252 }
5353}
5454
55impl From<auth::client::error::KenAuthError> for SpaceError {
56 fn from(error: auth::client::error::KenAuthError) -> Self {
55impl From<export::KenAuthError> for SpaceError {
56 fn from(error: export::KenAuthError) -> Self {
5757 match error {
58 auth::client::error::KenAuthError::NotFound => Self::NotFound,
59 auth::client::error::KenAuthError::Unauthenticated => Self::Unauthorized,
60 auth::client::error::KenAuthError::Unavailable(message) => {
58 export::KenAuthError::NotFound => Self::NotFound,
59 export::KenAuthError::Unauthenticated => Self::Unauthorized,
60 export::KenAuthError::Unavailable(message) => {
6161 Self::Internal(format!("kenoauth unavailable: {message}"))
6262 }
63 auth::client::error::KenAuthError::Rejected(message) => {
63 export::KenAuthError::Rejected(message) => {
6464 let lower = message.to_lowercase();
6565 if lower.contains("already") || lower.contains("taken") {
6666 Self::Conflict(message)
@@ -70,8 +70,9 @@
7070 Self::BadRequest(message)
7171 }
7272 }
73 auth::client::error::KenAuthError::Config(message)
74 | auth::client::error::KenAuthError::Internal(message) => Self::Internal(message),
73 export::KenAuthError::Config(message) | export::KenAuthError::Internal(message) => {
74 Self::Internal(message)
75 }
7576 }
7677 }
7778}
Modified lib/space/Cargo.toml View lib/space/Cargo.toml
@@ -8,11 +8,11 @@
88config = { workspace = true }
99deletion = { workspace = true }
1010export = { workspace = true }
11proto = { workspace = true }
1112i18n = { workspace = true }
1213imaging = { workspace = true }
1314models = { workspace = true }
1415session = { workspace = true }
15auth = { workspace = true }
1616prost-types = { workspace = true }
1717storage = { workspace = true }
1818actix-web = { workspace = true }
Deleted lib/webapi/src/auth/services/auth_service.rs
@@ -1,56 +0,0 @@
1use std::sync::Arc;
2
3use config::AppConfig;
4use export::{AppDatabase, AppRedis, AppState};
5use models::users::access_scope::AccessKeyScope;
6
7use crate::auth::errors::AuthError;
8
9pub struct VerifiedAccessKey {
10 pub user_id: i64,
11 pub scope: AccessKeyScope,
12}
13
14#[derive(Clone)]
15pub struct AuthService {
16 pub state: AppState,
17}
18
19impl AuthService {
20 pub fn new(state: AppState) -> Self {
21 Self { state }
22 }
23
24 pub fn config(&self) -> &AppConfig {
25 &self.state.config
26 }
27
28 pub fn db(&self) -> &Arc<AppDatabase> {
29 &self.state.database
30 }
31
32 pub fn redis(&self) -> &AppRedis {
33 &self.state.redis
34 }
35
36 /// Pick the mail language for one recipient.
37 pub async fn mail_locale(
38 &self,
39 uid: Option<i64>,
40 accept_language: Option<&str>,
41 ) -> i18n::UiLocale {
42 let fallback = i18n::UiLocale::parse(self.config().email.default_locale())
43 .unwrap_or(i18n::UiLocale::En);
44 match uid {
45 Some(uid) => i18n::resolve_user_locale(self.db().read(), uid, fallback).await,
46 None => i18n::negotiate_accept_language(accept_language, fallback),
47 }
48 }
49
50 pub async fn verify_access_token(
51 &self,
52 _token: &str,
53 ) -> Result<Option<VerifiedAccessKey>, AuthError> {
54 Ok(None)
55 }
56}
Deleted lib/webapi/src/auth/session/login.rs
@@ -1,60 +0,0 @@
1use export::http::{Query, Redirect, Response, State};
2use serde::Deserialize;
3use url::form_urlencoded;
4
5use crate::auth::errors::ApiError;
6use crate::auth::services::AuthService;
7
8#[derive(Debug, Deserialize)]
9pub struct LoginQuery {
10 #[serde(default)]
11 pub redirect: Option<String>,
12}
13
14#[derive(Debug, Deserialize)]
15pub struct SignupQuery {
16 #[serde(default)]
17 pub redirect: Option<String>,
18}
19
20pub async fn login(
21 State(service): State<AuthService>,
22 Query(query): Query<LoginQuery>,
23) -> Result<Response, ApiError> {
24 redirect_to_identity(&service, "/login", query.redirect.as_deref())
25}
26
27pub async fn signup(
28 State(service): State<AuthService>,
29 Query(query): Query<SignupQuery>,
30) -> Result<Response, ApiError> {
31 redirect_to_identity(&service, "/register", query.redirect.as_deref())
32}
33
34fn redirect_to_identity(
35 service: &AuthService,
36 path: &str,
37 redirect: Option<&str>,
38) -> Result<Response, ApiError> {
39 let base = service
40 .config()
41 .web
42 .web_base_url
43 .trim()
44 .trim_end_matches('/');
45 let mut serializer = form_urlencoded::Serializer::new(String::new());
46 serializer.append_pair("redirect", &sanitize_redirect(redirect));
47 let location = if base.is_empty() {
48 format!("{path}?{}", serializer.finish())
49 } else {
50 format!("{base}{path}?{}", serializer.finish())
51 };
52 Ok(Redirect::to(&location).into_response())
53}
54
55fn sanitize_redirect(raw: Option<&str>) -> String {
56 match raw.map(str::trim) {
57 Some(value) if value.starts_with('/') && !value.starts_with("//") => value.to_string(),
58 _ => "/app".to_string(),
59 }
60}
Deleted lib/webapi/src/auth/session/logout.rs
@@ -1,22 +0,0 @@
1use auth::session::Session;
2use export::http::{Redirect, Response, State};
3
4use crate::auth::errors::ApiError;
5use crate::auth::services::AuthService;
6use crate::auth::session::cookie::append_cookie;
7
8pub async fn logout(
9 State(service): State<AuthService>,
10 session: Session,
11) -> Result<Response, ApiError> {
12 if let Some(client) = service.state.kenauth.as_ref() {
13 let request_id = export::middleware::request_id().unwrap_or_default();
14 let _ = client.service().logout(session, request_id).await;
15 }
16 let mut response = Redirect::to("/").into_response();
17 append_cookie(
18 &mut response,
19 &session::clear_session_cookie(&service.config().session),
20 );
21 Ok(response)
22}
Deleted lib/webapi/src/auth/session/project.rs
@@ -1,44 +0,0 @@
1use models::users::user::{self, ProfileVisibility};
2use sea_orm::{ActiveModelTrait, EntityTrait, Set};
3use time::OffsetDateTime;
4
5use crate::auth::errors::AuthError;
6use crate::auth::services::AuthService;
7
8/// Ensure the local settings row for an authenticated account exists.
9///
10/// Identity fields are not copied here: KenApi reads username, display name,
11/// email, account state and avatar from KenOAuth on demand. This row only
12/// anchors local preferences and foreign keys.
13pub async fn upsert(
14 service: &AuthService,
15 public: &auth::protos::auth::common::v1::UserPublic,
16) -> Result<user::Model, AuthError> {
17 let db = service.db().write();
18 let now = OffsetDateTime::now_utc();
19
20 let existing = user::Entity::find_by_id(public.user_id).one(db).await?;
21 match existing {
22 Some(row) => {
23 let mut am: user::ActiveModel = row.into();
24 am.last_login_at = Set(Some(now));
25 am.updated_at = Set(now);
26 Ok(am.update(db).await?)
27 }
28 None => Ok(user::ActiveModel {
29 uid: Set(public.user_id),
30 is_restricted: Set(false),
31 visibility: Set(ProfileVisibility::Public),
32 last_login_at: Set(Some(now)),
33 last_login_ip_hash: Set(None),
34 local_avatar_url: Set(String::new()),
35 timezone: Set(None),
36 locale: Set(None),
37 language: Set(None),
38 version: Set(1),
39 updated_at: Set(now),
40 }
41 .insert(db)
42 .await?),
43 }
44}
Deleted lib/webapi/src/auth/share/mod.rs
@@ -1,6 +0,0 @@
1pub mod guard;
2pub mod rpc;
3pub mod types;
4pub mod validate;
5
6pub use types::{AgreementDoc, UserPublic};
Deleted lib/webapi/src/auth/mod.rs
@@ -1,8 +0,0 @@
1pub mod errors;
2pub mod handlers;
3pub mod openapi;
4pub mod routes;
5pub mod services;
6pub mod session;
7pub mod share;
8pub mod templates;
Modified lib/webapi/src/lib.rs View lib/webapi/src/lib.rs
@@ -1,4 +1,3 @@
1pub mod auth;
21pub mod cleanup;
32pub mod openapi;
43
@@ -33,7 +32,7 @@
3332}
3433
3534pub fn scope(state: AppState) -> Scope {
36 let auth = crate::auth::services::AuthService::new(state.clone());
35 let auth = ::auth::AuthService::new(state.clone());
3736 let chat = chat::services::service::ChatService::new(state.clone());
3837 let issues = issues::services::service::IssueService::new(state.clone());
3938 let space = space::services::service::SpaceService::new(state.clone());
@@ -44,15 +43,14 @@
4443
4544 let cleanup_router = cleanup::router(space.clone(), issues.clone(), git.clone());
4645
47 let mut public = Router::<()>::new();
48 if state.kenauth.is_some() {
49 public = public.merge(
50 Router::<()>::new()
51 .setup(|scope| scope.configure(::auth::server::http::router::configure_api)),
52 );
53 }
54 let public = public
55 .merge(crate::auth::routes::router(auth.clone()).with_state(auth.clone()))
46 let auth_domain = auth.service().clone();
47 let public = Router::<()>::new()
48 .setup(move |scope| {
49 scope
50 .app_data(web::Data::new(auth_domain))
51 .configure(::auth::routes::configure_api)
52 })
53 .merge(::auth::routes_account::router(auth.clone()).with_state(auth.clone()))
5654 .merge(chat::routes::router(chat.clone(), space.clone()).with_state(chat.clone()))
5755 .merge(issues::routes::router(issues.clone()).with_state(issues))
5856 .merge(space::routes::router(space.clone()).with_state(space))
@@ -69,7 +67,7 @@
6967 Router::<()>::new()
7068 .nest("/api/v1/internal", internal)
7169 .nest("/api/v1", public)
72 .nest("/pages", crate::auth::routes::pages_router(auth.clone()))
70 .nest("/pages", ::auth::routes_account::pages_router(auth.clone()))
7371 .merge(export::health::router(state))
7472 .into_scope(web::scope(""))
7573 .default_service(web::to(::auth::assets::serve))
Modified lib/webapi/src/openapi.rs View lib/webapi/src/openapi.rs
@@ -101,7 +101,8 @@
101101 }
102102 }};
103103 }
104 m!("auth", crate::auth::openapi::AuthApiDoc::openapi());
104 m!("auth", auth::openapi::ApiDoc::openapi());
105 m!("auth-account", auth::openapi_account::AuthApiDoc::openapi());
105106 m!("chat", chat::openapi::ChatApiDoc::openapi());
106107 m!("issues", issues::openapi::IssuesApiDoc::openapi());
107108 m!("space", space::openapi::SpaceApiDoc::openapi());
@@ -113,27 +114,18 @@
113114 doc.info.description = Some(CONVENTIONS.to_string());
114115 doc.info.license = None;
115116 {
116 use utoipa::openapi::security::{
117 ApiKey, ApiKeyValue, Http, HttpAuthScheme, SecurityScheme,
118 };
117 use utoipa::openapi::security::{ApiKey, ApiKeyValue, SecurityScheme};
119118 let components = doc
120119 .components
121120 .get_or_insert_with(utoipa::openapi::Components::default);
122121 components.add_security_scheme(
123122 "session_cookie",
124 SecurityScheme::ApiKey(ApiKey::Cookie(ApiKeyValue::new("gitdata_session"))),
125 );
126 components.add_security_scheme(
127 "basic_access_key",
128 SecurityScheme::Http(Http::new(HttpAuthScheme::Basic)),
123 SecurityScheme::ApiKey(ApiKey::Cookie(ApiKeyValue::new("session"))),
129124 );
130125 }
131 doc.security = Some(vec![
132 utoipa::openapi::security::SecurityRequirement::new("session_cookie", Vec::<String>::new()),
133 utoipa::openapi::security::SecurityRequirement::new(
134 "basic_access_key",
135 Vec::<String>::new(),
136 ),
137 ]);
126 doc.security = Some(vec![utoipa::openapi::security::SecurityRequirement::new(
127 "session_cookie",
128 Vec::<String>::new(),
129 )]);
138130 doc
139131}
Renamed view/public/media/gitdataai-red-panda.png → view/auth/public/media/gitdataai-red-panda.png View view/auth/public/media/gitdataai-red-panda.png

Binary file not shown.

Renamed view/src/app/routing/router.tsx → view/auth/src/app/routing/router.tsx View view/auth/src/app/routing/router.tsx

No content changes.

Renamed view/src/app/styles/index.css → view/auth/src/app/styles/index.css View view/auth/src/app/styles/index.css

No content changes.

Renamed view/src/app/app.tsx → view/auth/src/app/app.tsx View view/auth/src/app/app.tsx

No content changes.

Renamed view/src/app/index.ts → view/auth/src/app/index.ts View view/auth/src/app/index.ts

No content changes.

Renamed view/src/app/main.tsx → view/auth/src/app/main.tsx View view/auth/src/app/main.tsx

No content changes.

Renamed view/src/entities/auth-callback/model/callback-url.ts → view/auth/src/entities/auth-callback/model/callback-url.ts View view/auth/src/entities/auth-callback/model/callback-url.ts

No content changes.

Renamed view/src/entities/auth-callback/index.ts → view/auth/src/entities/auth-callback/index.ts View view/auth/src/entities/auth-callback/index.ts

No content changes.

Renamed view/src/entities/password-reset/model/state.ts → view/auth/src/entities/password-reset/model/state.ts View view/auth/src/entities/password-reset/model/state.ts

No content changes.

Renamed view/src/entities/password-reset/index.ts → view/auth/src/entities/password-reset/index.ts View view/auth/src/entities/password-reset/index.ts

No content changes.

Renamed view/src/entities/registration/model/draft.ts → view/auth/src/entities/registration/model/draft.ts View view/auth/src/entities/registration/model/draft.ts

No content changes.

Renamed view/src/entities/registration/index.ts → view/auth/src/entities/registration/index.ts View view/auth/src/entities/registration/index.ts

No content changes.

Renamed view/src/entities/session/api/session.ts → view/auth/src/entities/session/api/session.ts View view/auth/src/entities/session/api/session.ts

No content changes.

Renamed view/src/entities/session/model/mfa-challenge.ts → view/auth/src/entities/session/model/mfa-challenge.ts View view/auth/src/entities/session/model/mfa-challenge.ts

No content changes.

Renamed view/src/entities/session/model/types.ts → view/auth/src/entities/session/model/types.ts View view/auth/src/entities/session/model/types.ts

No content changes.

Renamed view/src/entities/session/model/use-session.ts → view/auth/src/entities/session/model/use-session.ts View view/auth/src/entities/session/model/use-session.ts

No content changes.

Renamed view/src/entities/session/index.ts → view/auth/src/entities/session/index.ts View view/auth/src/entities/session/index.ts

No content changes.

Renamed view/src/entities/verification-code/model/cooldown.ts → view/auth/src/entities/verification-code/model/cooldown.ts View view/auth/src/entities/verification-code/model/cooldown.ts

No content changes.

Renamed view/src/entities/verification-code/index.ts → view/auth/src/entities/verification-code/index.ts View view/auth/src/entities/verification-code/index.ts

No content changes.

Renamed view/src/features/login-by-code/api/login-code.ts → view/auth/src/features/login-by-code/api/login-code.ts View view/auth/src/features/login-by-code/api/login-code.ts

No content changes.

Renamed view/src/features/login-by-code/model/use-login-by-code.ts → view/auth/src/features/login-by-code/model/use-login-by-code.ts View view/auth/src/features/login-by-code/model/use-login-by-code.ts

No content changes.

Renamed view/src/features/login-by-code/ui/login-code-form.tsx → view/auth/src/features/login-by-code/ui/login-code-form.tsx View view/auth/src/features/login-by-code/ui/login-code-form.tsx

No content changes.

Renamed view/src/features/login-by-code/index.ts → view/auth/src/features/login-by-code/index.ts View view/auth/src/features/login-by-code/index.ts

No content changes.

Renamed view/src/features/login-by-password/api/login.ts → view/auth/src/features/login-by-password/api/login.ts View view/auth/src/features/login-by-password/api/login.ts

No content changes.

Renamed view/src/features/login-by-password/model/use-login-by-password.ts → view/auth/src/features/login-by-password/model/use-login-by-password.ts View view/auth/src/features/login-by-password/model/use-login-by-password.ts

No content changes.

Renamed view/src/features/login-by-password/ui/login-form.tsx → view/auth/src/features/login-by-password/ui/login-form.tsx View view/auth/src/features/login-by-password/ui/login-form.tsx

No content changes.

Renamed view/src/features/login-by-password/index.ts → view/auth/src/features/login-by-password/index.ts View view/auth/src/features/login-by-password/index.ts

No content changes.

Renamed view/src/features/register-account/api/register.ts → view/auth/src/features/register-account/api/register.ts View view/auth/src/features/register-account/api/register.ts

No content changes.

Renamed view/src/features/register-account/model/use-register-account.ts → view/auth/src/features/register-account/model/use-register-account.ts View view/auth/src/features/register-account/model/use-register-account.ts

No content changes.

Renamed view/src/features/register-account/model/use-register-verify.ts → view/auth/src/features/register-account/model/use-register-verify.ts View view/auth/src/features/register-account/model/use-register-verify.ts

No content changes.

Renamed view/src/features/register-account/ui/register-form.tsx → view/auth/src/features/register-account/ui/register-form.tsx View view/auth/src/features/register-account/ui/register-form.tsx

No content changes.

Renamed view/src/features/register-account/ui/register-verify-form.tsx → view/auth/src/features/register-account/ui/register-verify-form.tsx View view/auth/src/features/register-account/ui/register-verify-form.tsx

No content changes.

Renamed view/src/features/register-account/index.ts → view/auth/src/features/register-account/index.ts View view/auth/src/features/register-account/index.ts

No content changes.

Renamed view/src/features/request-password-reset/api/request-reset.ts → view/auth/src/features/request-password-reset/api/request-reset.ts View view/auth/src/features/request-password-reset/api/request-reset.ts

No content changes.

Renamed view/src/features/request-password-reset/model/use-request-password-reset.ts → view/auth/src/features/request-password-reset/model/use-request-password-reset.ts View view/auth/src/features/request-password-reset/model/use-request-password-reset.ts

No content changes.

Renamed view/src/features/request-password-reset/model/use-resend-reset-email.ts → view/auth/src/features/request-password-reset/model/use-resend-reset-email.ts View view/auth/src/features/request-password-reset/model/use-resend-reset-email.ts

No content changes.

Renamed view/src/features/request-password-reset/ui/forgot-password-form.tsx → view/auth/src/features/request-password-reset/ui/forgot-password-form.tsx View view/auth/src/features/request-password-reset/ui/forgot-password-form.tsx

No content changes.

Renamed view/src/features/request-password-reset/ui/reset-sent-actions.tsx → view/auth/src/features/request-password-reset/ui/reset-sent-actions.tsx View view/auth/src/features/request-password-reset/ui/reset-sent-actions.tsx

No content changes.

Renamed view/src/features/request-password-reset/index.ts → view/auth/src/features/request-password-reset/index.ts View view/auth/src/features/request-password-reset/index.ts

No content changes.

Renamed view/src/features/reset-password/api/reset-password.ts → view/auth/src/features/reset-password/api/reset-password.ts View view/auth/src/features/reset-password/api/reset-password.ts

No content changes.

Renamed view/src/features/reset-password/model/use-reset-password.ts → view/auth/src/features/reset-password/model/use-reset-password.ts View view/auth/src/features/reset-password/model/use-reset-password.ts

No content changes.

Renamed view/src/features/reset-password/ui/reset-password-form.tsx → view/auth/src/features/reset-password/ui/reset-password-form.tsx View view/auth/src/features/reset-password/ui/reset-password-form.tsx

No content changes.

Renamed view/src/features/reset-password/index.ts → view/auth/src/features/reset-password/index.ts View view/auth/src/features/reset-password/index.ts

No content changes.

Renamed view/src/features/theme-toggle/model/use-theme.ts → view/auth/src/features/theme-toggle/model/use-theme.ts View view/auth/src/features/theme-toggle/model/use-theme.ts

No content changes.

Renamed view/src/features/theme-toggle/ui/theme-switch.tsx → view/auth/src/features/theme-toggle/ui/theme-switch.tsx View view/auth/src/features/theme-toggle/ui/theme-switch.tsx

No content changes.

Renamed view/src/features/theme-toggle/index.ts → view/auth/src/features/theme-toggle/index.ts View view/auth/src/features/theme-toggle/index.ts

No content changes.

Renamed view/src/features/verify-mfa/api/verify-mfa.ts → view/auth/src/features/verify-mfa/api/verify-mfa.ts View view/auth/src/features/verify-mfa/api/verify-mfa.ts

No content changes.

Renamed view/src/features/verify-mfa/model/use-verify-mfa.ts → view/auth/src/features/verify-mfa/model/use-verify-mfa.ts View view/auth/src/features/verify-mfa/model/use-verify-mfa.ts

No content changes.

Renamed view/src/features/verify-mfa/ui/mfa-form.tsx → view/auth/src/features/verify-mfa/ui/mfa-form.tsx View view/auth/src/features/verify-mfa/ui/mfa-form.tsx

No content changes.

Renamed view/src/features/verify-mfa/index.ts → view/auth/src/features/verify-mfa/index.ts View view/auth/src/features/verify-mfa/index.ts

No content changes.

Renamed view/src/pages/account/ui/account-page.tsx → view/auth/src/pages/account/ui/account-page.tsx View view/auth/src/pages/account/ui/account-page.tsx

No content changes.

Renamed view/src/pages/account/index.ts → view/auth/src/pages/account/index.ts View view/auth/src/pages/account/index.ts

No content changes.

Renamed view/src/pages/forgot-password/ui/forgot-password-page.tsx → view/auth/src/pages/forgot-password/ui/forgot-password-page.tsx View view/auth/src/pages/forgot-password/ui/forgot-password-page.tsx

No content changes.

Renamed view/src/pages/forgot-password/index.ts → view/auth/src/pages/forgot-password/index.ts View view/auth/src/pages/forgot-password/index.ts

No content changes.

Renamed view/src/pages/login/ui/login-page.tsx → view/auth/src/pages/login/ui/login-page.tsx View view/auth/src/pages/login/ui/login-page.tsx

No content changes.

Renamed view/src/pages/login/index.ts → view/auth/src/pages/login/index.ts View view/auth/src/pages/login/index.ts

No content changes.

Renamed view/src/pages/login-code/ui/login-code-page.tsx → view/auth/src/pages/login-code/ui/login-code-page.tsx View view/auth/src/pages/login-code/ui/login-code-page.tsx

No content changes.

Renamed view/src/pages/login-code/index.ts → view/auth/src/pages/login-code/index.ts View view/auth/src/pages/login-code/index.ts

No content changes.

Renamed view/src/pages/login-verify/ui/login-verify-page.tsx → view/auth/src/pages/login-verify/ui/login-verify-page.tsx View view/auth/src/pages/login-verify/ui/login-verify-page.tsx

No content changes.

Renamed view/src/pages/login-verify/index.ts → view/auth/src/pages/login-verify/index.ts View view/auth/src/pages/login-verify/index.ts

No content changes.

Renamed view/src/pages/not-found/ui/not-found-page.tsx → view/auth/src/pages/not-found/ui/not-found-page.tsx View view/auth/src/pages/not-found/ui/not-found-page.tsx

No content changes.

Renamed view/src/pages/not-found/index.ts → view/auth/src/pages/not-found/index.ts View view/auth/src/pages/not-found/index.ts

No content changes.

Renamed view/src/pages/register/ui/register-page.tsx → view/auth/src/pages/register/ui/register-page.tsx View view/auth/src/pages/register/ui/register-page.tsx

No content changes.

Renamed view/src/pages/register/index.ts → view/auth/src/pages/register/index.ts View view/auth/src/pages/register/index.ts

No content changes.

Renamed view/src/pages/register-verify/ui/register-verify-page.tsx → view/auth/src/pages/register-verify/ui/register-verify-page.tsx View view/auth/src/pages/register-verify/ui/register-verify-page.tsx

No content changes.

Renamed view/src/pages/register-verify/index.ts → view/auth/src/pages/register-verify/index.ts View view/auth/src/pages/register-verify/index.ts

No content changes.

Renamed view/src/pages/reset-complete/ui/reset-complete-page.tsx → view/auth/src/pages/reset-complete/ui/reset-complete-page.tsx View view/auth/src/pages/reset-complete/ui/reset-complete-page.tsx

No content changes.

Renamed view/src/pages/reset-complete/index.ts → view/auth/src/pages/reset-complete/index.ts View view/auth/src/pages/reset-complete/index.ts

No content changes.

Renamed view/src/pages/reset-password/ui/reset-password-page.tsx → view/auth/src/pages/reset-password/ui/reset-password-page.tsx View view/auth/src/pages/reset-password/ui/reset-password-page.tsx

No content changes.

Renamed view/src/pages/reset-password/index.ts → view/auth/src/pages/reset-password/index.ts View view/auth/src/pages/reset-password/index.ts

No content changes.

Renamed view/src/pages/reset-sent/ui/reset-sent-page.tsx → view/auth/src/pages/reset-sent/ui/reset-sent-page.tsx View view/auth/src/pages/reset-sent/ui/reset-sent-page.tsx

No content changes.

Renamed view/src/pages/reset-sent/index.ts → view/auth/src/pages/reset-sent/index.ts View view/auth/src/pages/reset-sent/index.ts

No content changes.

Renamed view/src/shared/api/auth/auth.schemas.ts → view/auth/src/shared/api/auth/auth.schemas.ts View view/auth/src/shared/api/auth/auth.schemas.ts

No content changes.

Renamed view/src/shared/api/auth/auth.ts → view/auth/src/shared/api/auth/auth.ts View view/auth/src/shared/api/auth/auth.ts

No content changes.

Renamed view/src/shared/api/error.ts → view/auth/src/shared/api/error.ts View view/auth/src/shared/api/error.ts

No content changes.

Renamed view/src/shared/api/http.ts → view/auth/src/shared/api/http.ts View view/auth/src/shared/api/http.ts

No content changes.

Renamed view/src/shared/api/index.ts → view/auth/src/shared/api/index.ts View view/auth/src/shared/api/index.ts

No content changes.

Renamed view/src/shared/api/pow.ts → view/auth/src/shared/api/pow.ts View view/auth/src/shared/api/pow.ts

No content changes.

Renamed view/src/shared/api/pow.worker.ts → view/auth/src/shared/api/pow.worker.ts View view/auth/src/shared/api/pow.worker.ts

No content changes.

Renamed view/src/shared/config/index.ts → view/auth/src/shared/config/index.ts View view/auth/src/shared/config/index.ts

No content changes.

Renamed view/src/shared/config/routes.ts → view/auth/src/shared/config/routes.ts View view/auth/src/shared/config/routes.ts

No content changes.

Renamed view/src/shared/i18n/index.ts → view/auth/src/shared/i18n/index.ts View view/auth/src/shared/i18n/index.ts

No content changes.

Renamed view/src/shared/i18n/locale.ts → view/auth/src/shared/i18n/locale.ts View view/auth/src/shared/i18n/locale.ts

No content changes.

Renamed view/src/shared/i18n/messages.ts → view/auth/src/shared/i18n/messages.ts View view/auth/src/shared/i18n/messages.ts

No content changes.

Renamed view/src/shared/i18n/translate.ts → view/auth/src/shared/i18n/translate.ts View view/auth/src/shared/i18n/translate.ts

No content changes.

Renamed view/src/shared/lib/http/http.ts → view/auth/src/shared/lib/http/http.ts View view/auth/src/shared/lib/http/http.ts

No content changes.

Renamed view/src/shared/lib/http/index.ts → view/auth/src/shared/lib/http/index.ts View view/auth/src/shared/lib/http/index.ts

No content changes.

Renamed view/src/shared/lib/index.ts → view/auth/src/shared/lib/index.ts View view/auth/src/shared/lib/index.ts

No content changes.

Renamed view/src/shared/lib/navigation.ts → view/auth/src/shared/lib/navigation.ts View view/auth/src/shared/lib/navigation.ts

No content changes.

Renamed view/src/shared/lib/password.ts → view/auth/src/shared/lib/password.ts View view/auth/src/shared/lib/password.ts

No content changes.

Renamed view/src/shared/lib/store.ts → view/auth/src/shared/lib/store.ts View view/auth/src/shared/lib/store.ts

No content changes.

Renamed view/src/shared/lib/use-countdown.ts → view/auth/src/shared/lib/use-countdown.ts View view/auth/src/shared/lib/use-countdown.ts

No content changes.

Renamed view/src/shared/lib/use-task.ts → view/auth/src/shared/lib/use-task.ts View view/auth/src/shared/lib/use-task.ts

No content changes.

Renamed view/src/shared/lib/validation.ts → view/auth/src/shared/lib/validation.ts View view/auth/src/shared/lib/validation.ts

No content changes.

Renamed view/src/shared/ui/agreement.tsx → view/auth/src/shared/ui/agreement.tsx View view/auth/src/shared/ui/agreement.tsx

No content changes.

Renamed view/src/shared/ui/alert.tsx → view/auth/src/shared/ui/alert.tsx View view/auth/src/shared/ui/alert.tsx

No content changes.

Renamed view/src/shared/ui/back-link.tsx → view/auth/src/shared/ui/back-link.tsx View view/auth/src/shared/ui/back-link.tsx

No content changes.

Renamed view/src/shared/ui/birth-date.tsx → view/auth/src/shared/ui/birth-date.tsx View view/auth/src/shared/ui/birth-date.tsx

No content changes.

Renamed view/src/shared/ui/button.tsx → view/auth/src/shared/ui/button.tsx View view/auth/src/shared/ui/button.tsx

No content changes.

Renamed view/src/shared/ui/field.tsx → view/auth/src/shared/ui/field.tsx View view/auth/src/shared/ui/field.tsx

No content changes.

Renamed view/src/shared/ui/flow-required.tsx → view/auth/src/shared/ui/flow-required.tsx View view/auth/src/shared/ui/flow-required.tsx

No content changes.

Renamed view/src/shared/ui/form-navigation.tsx → view/auth/src/shared/ui/form-navigation.tsx View view/auth/src/shared/ui/form-navigation.tsx

No content changes.

Renamed view/src/shared/ui/heading.tsx → view/auth/src/shared/ui/heading.tsx View view/auth/src/shared/ui/heading.tsx

No content changes.

Renamed view/src/shared/ui/index.ts → view/auth/src/shared/ui/index.ts View view/auth/src/shared/ui/index.ts

No content changes.

Renamed view/src/shared/ui/legal-link.tsx → view/auth/src/shared/ui/legal-link.tsx View view/auth/src/shared/ui/legal-link.tsx

No content changes.

Renamed view/src/shared/ui/link.tsx → view/auth/src/shared/ui/link.tsx View view/auth/src/shared/ui/link.tsx

No content changes.

Renamed view/src/shared/ui/otp.tsx → view/auth/src/shared/ui/otp.tsx View view/auth/src/shared/ui/otp.tsx

No content changes.

Renamed view/src/shared/ui/resend-button.tsx → view/auth/src/shared/ui/resend-button.tsx View view/auth/src/shared/ui/resend-button.tsx

No content changes.

Renamed view/src/shared/ui/retry-hint.tsx → view/auth/src/shared/ui/retry-hint.tsx View view/auth/src/shared/ui/retry-hint.tsx

No content changes.

Renamed view/src/widgets/app-shell/ui/app-shell.tsx → view/auth/src/widgets/app-shell/ui/app-shell.tsx View view/auth/src/widgets/app-shell/ui/app-shell.tsx

No content changes.

Renamed view/src/widgets/app-shell/index.ts → view/auth/src/widgets/app-shell/index.ts View view/auth/src/widgets/app-shell/index.ts

No content changes.

Renamed view/index.html → view/auth/index.html View view/auth/index.html

No content changes.

Modified Cargo.lock View Cargo.lock
@@ -773,14 +773,17 @@
773773 "config 1.0.0",
774774 "crypt",
775775 "db",
776 "deletion",
776777 "email",
777778 "envelope",
778779 "events",
780 "export",
779781 "figment",
780782 "futures-util",
781783 "hmac 0.12.1",
782784 "http 1.5.0",
783785 "http-body 1.1.0",
786 "i18n",
784787 "lettre",
785788 "maud",
786789 "models",
@@ -792,9 +795,11 @@
792795 "prost 0.14.4",
793796 "prost-types 0.14.4",
794797 "proto",
798 "pulldown-cmark",
795799 "queue",
796800 "rand 0.10.3",
797801 "rand_core 0.6.4",
802 "redis",
798803 "reqwest 0.13.5",
799804 "rocketmq-client-rust",
800805 "rocketmq-common",
@@ -807,6 +812,7 @@
807812 "sha2 0.10.9",
808813 "sqlparser",
809814 "thiserror 2.0.21",
815 "time 0.3.55",
810816 "tokio",
811817 "tokio-stream",
812818 "tokio-util",
@@ -816,7 +822,8 @@
816822 "tracing",
817823 "tracing-opentelemetry",
818824 "tracing-subscriber",
819 "utoipa 6.0.0",
825 "url 2.5.8",
826 "utoipa 5.5.0",
820827 "uuid",
821828]
822829
@@ -1851,7 +1858,6 @@
18511858name = "deletion"
18521859version = "1.0.0"
18531860dependencies = [
1854 "auth",
18551861 "config 1.0.0",
18561862 "export",
18571863 "models",
@@ -2227,7 +2233,7 @@
22272233 "session",
22282234 "tokio",
22292235 "tracing",
2230 "utoipa 6.0.0",
2236 "utoipa 5.5.0",
22312237 "uuid",
22322238]
22332239
@@ -2324,9 +2330,12 @@
23242330version = "1.0.0"
23252331dependencies = [
23262332 "actix-web",
2327 "auth",
2333 "async-trait",
23282334 "bytes",
2335 "cache",
23292336 "config 1.0.0",
2337 "email",
2338 "events",
23302339 "http 1.5.0",
23312340 "imaging",
23322341 "models",
@@ -2334,6 +2343,8 @@
23342343 "opentelemetry-http",
23352344 "opentelemetry-otlp",
23362345 "opentelemetry_sdk",
2346 "prost-types 0.14.4",
2347 "proto",
23372348 "queue",
23382349 "redis",
23392350 "reqwest 0.12.28",
@@ -2346,6 +2357,7 @@
23462357 "thiserror 2.0.21",
23472358 "time 0.3.55",
23482359 "tokio",
2360 "tonic",
23492361 "tower",
23502362 "tracing",
23512363 "tracing-opentelemetry",
@@ -3802,13 +3814,10 @@
38023814 "serde_json",
38033815 "session",
38043816 "tokio",
3805 "tokio-stream",
38063817 "tokio-util",
3807 "tonic",
38083818 "tracing",
38093819 "tracing-subscriber",
38103820 "utoipa 5.5.0",
3811 "utoipa 6.0.0",
38123821 "utoipa-swagger-ui",
38133822 "webapi",
38143823]
@@ -4351,7 +4360,6 @@
43514360 "time 0.3.55",
43524361 "tokio",
43534362 "utoipa 5.5.0",
4354 "utoipa 6.0.0",
43554363 "uuid",
43564364]
43574365
@@ -7170,7 +7178,6 @@
71707178dependencies = [
71717179 "actix-web",
71727180 "async-trait",
7173 "auth",
71747181 "config 1.0.0",
71757182 "deletion",
71767183 "export",
@@ -7178,6 +7185,7 @@
71787185 "imaging",
71797186 "models",
71807187 "prost-types 0.14.4",
7188 "proto",
71817189 "sea-orm",
71827190 "serde",
71837191 "serde_json",
@@ -8403,7 +8411,6 @@
84038411dependencies = [
84048412 "proc-macro2",
84058413 "quote",
8406 "regex",
84078414 "syn 3.0.9",
84088415 "uuid",
84098416]
Modified orval.config.ts View orval.config.ts
@@ -1,12 +1,12 @@
11import { defineConfig } from 'orval'
22
3// 生成 view/src/shared/api/auth 下的 fetch 客户端;OpenApi.Auth.json 来自
3// 生成 view/auth/src/shared/api/auth 下的 fetch 客户端;OpenApi.Auth.json 来自
44// `cargo run -p kenapi -- identity-openapi`。
55export default defineConfig({
66 auth: {
77 input: './lib/auth/OpenApi.Auth.json',
88 output: {
9 target: './view/src/shared/api/auth',
9 target: './view/auth/src/shared/api/auth',
1010 mode: 'split',
1111 client: 'fetch',
1212 httpClient: 'fetch',
@@ -15,7 +15,7 @@
1515 formatter: 'prettier',
1616 override: {
1717 enumGenerationType: 'union',
18 mutator: { path: './view/src/shared/lib/jwtmanager/http.ts', name: 'authClient' },
18 mutator: { path: './view/auth/src/shared/lib/http/http.ts', name: 'authClient' },
1919 },
2020 },
2121 },
Modified package.json View package.json
@@ -7,9 +7,9 @@
77 "dev": "vite",
88 "build": "tsc -b && vite build",
99 "typecheck": "tsc -b",
10 "lint": "steiger view/src",
11 "lint:fsd": "steiger view/src",
12 "lint:fsd:strict": "steiger view/src --fail-on-warnings",
10 "lint": "steiger view/auth/src",
11 "lint:fsd": "steiger view/auth/src",
12 "lint:fsd:strict": "steiger view/auth/src --fail-on-warnings",
1313 "preview": "vite preview",
1414 "format": "biome format --write .",
1515 "format:check": "biome format .",
@@ -19,15 +19,18 @@
1919 "@fontsource-variable/geist": "^5.3.0",
2020 "@fontsource-variable/noto-sans-sc": "^5.3.1",
2121 "@noble/hashes": "^2.4.0",
22 "lucide-preact": "^1.57.0",
23 "preact": "^10.29.8"
22 "lucide-react": "^1.66.0",
23 "react": "^19.3.1",
24 "react-dom": "^19.3.1"
2425 },
2526 "devDependencies": {
2627 "@biomejs/biome": "^2.5.15",
2728 "@feature-sliced/steiger-plugin": "0.8.0",
28 "@preact/preset-vite": "^2.10.6",
2929 "@tailwindcss/vite": "^4.3.3",
3030 "@types/node": "^24.13.3",
31 "@types/react": "^19.3.0",
32 "@types/react-dom": "^19.3.0",
33 "@vitejs/plugin-react": "^6.1.2",
3134 "orval": "^8.43.0",
3235 "prettier": "^3.6.2",
3336 "steiger": "0.7.0",
Modified pnpm-lock.yaml View pnpm-lock.yaml
@@ -17,12 +17,15 @@
1717 '@noble/hashes':
1818 specifier: ^2.4.0
1919 version: 2.4.0
20 lucide-preact:
21 specifier: ^1.57.0
22 version: 1.60.0(preact@10.29.8)
23 preact:
24 specifier: ^10.29.8
25 version: 10.29.8
20 lucide-react:
21 specifier: ^1.66.0
22 version: 1.66.0(@types/react@19.3.0)(react@19.3.1)
23 react:
24 specifier: ^19.3.1
25 version: 19.3.1
26 react-dom:
27 specifier: ^19.3.1
28 version: 19.3.1(react@19.3.1)
2629 devDependencies:
2730 '@biomejs/biome':
2831 specifier: ^2.5.15
@@ -30,15 +33,21 @@
3033 '@feature-sliced/steiger-plugin':
3134 specifier: 0.8.0
3235 version: 0.8.0(typescript@6.0.3)
33 '@preact/preset-vite':
34 specifier: ^2.10.6
35 version: 2.10.6(@babel/core@7.29.7)(preact@10.29.8)(vite@8.3.4(@types/node@24.19.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.1))
3636 '@tailwindcss/vite':
3737 specifier: ^4.3.3
3838 version: 4.3.3(vite@8.3.4(@types/node@24.19.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.1))
3939 '@types/node':
4040 specifier: ^24.13.3
4141 version: 24.19.2
42 '@types/react':
43 specifier: ^19.3.0
44 version: 19.3.0
45 '@types/react-dom':
46 specifier: ^19.3.0
47 version: 19.3.0(@types/react@19.3.0)
48 '@vitejs/plugin-react':
49 specifier: ^6.1.2
50 version: 6.1.2(vite@8.3.4(@types/node@24.19.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.1))
4251 orval:
4352 specifier: ^8.43.0
4453 version: 8.43.0(prettier@3.9.10)(typescript@6.0.3)(zod@3.25.76)
@@ -57,99 +66,6 @@
5766
5867packages:
5968
60 '@babel/code-frame@7.29.7':
61 resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
62 engines: {node: '>=6.9.0'}
63
64 '@babel/compat-data@7.29.7':
65 resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==}
66 engines: {node: '>=6.9.0'}
67
68 '@babel/core@7.29.7':
69 resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==}
70 engines: {node: '>=6.9.0'}
71
72 '@babel/generator@7.29.8':
73 resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==}
74 engines: {node: '>=6.9.0'}
75
76 '@babel/helper-annotate-as-pure@7.29.7':
77 resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==}
78 engines: {node: '>=6.9.0'}
79
80 '@babel/helper-compilation-targets@7.29.7':
81 resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==}
82 engines: {node: '>=6.9.0'}
83
84 '@babel/helper-globals@7.29.7':
85 resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==}
86 engines: {node: '>=6.9.0'}
87
88 '@babel/helper-module-imports@7.29.7':
89 resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==}
90 engines: {node: '>=6.9.0'}
91
92 '@babel/helper-module-transforms@7.29.7':
93 resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==}
94 engines: {node: '>=6.9.0'}
95 peerDependencies:
96 '@babel/core': ^7.0.0
97
98 '@babel/helper-plugin-utils@7.29.7':
99 resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==}
100 engines: {node: '>=6.9.0'}
101
102 '@babel/helper-string-parser@7.29.7':
103 resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==}
104 engines: {node: '>=6.9.0'}
105
106 '@babel/helper-validator-identifier@7.29.7':
107 resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
108 engines: {node: '>=6.9.0'}
109
110 '@babel/helper-validator-option@7.29.7':
111 resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==}
112 engines: {node: '>=6.9.0'}
113
114 '@babel/helpers@7.29.10':
115 resolution: {integrity: sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==}
116 engines: {node: '>=6.9.0'}
117
118 '@babel/parser@7.29.9':
119 resolution: {integrity: sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==}
120 engines: {node: '>=6.0.0'}
121 hasBin: true
122
123 '@babel/plugin-syntax-jsx@7.29.7':
124 resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==}
125 engines: {node: '>=6.9.0'}
126 peerDependencies:
127 '@babel/core': ^7.0.0-0
128
129 '@babel/plugin-transform-react-jsx-development@7.29.7':
130 resolution: {integrity: sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g==}
131 engines: {node: '>=6.9.0'}
132 peerDependencies:
133 '@babel/core': ^7.0.0-0
134
135 '@babel/plugin-transform-react-jsx@7.29.7':
136 resolution: {integrity: sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A==}
137 engines: {node: '>=6.9.0'}
138 peerDependencies:
139 '@babel/core': ^7.0.0-0
140
141 '@babel/template@7.29.7':
142 resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==}
143 engines: {node: '>=6.9.0'}
144
145 '@babel/traverse@7.29.10':
146 resolution: {integrity: sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==}
147 engines: {node: '>=6.9.0'}
148
149 '@babel/types@7.29.8':
150 resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==}
151 engines: {node: '>=6.9.0'}
152
15369 '@biomejs/biome@2.5.15':
15470 resolution: {integrity: sha512-WZTW4slm/pdkh92K6t/3aEN++44JD1PQ7squ7RCMLI1flHGl43DVOIGXvCjHiZgBMO1V4uYxoFNtqrIA4qGuIQ==}
15571 engines: {node: '>=14.21.3'}
@@ -523,31 +439,6 @@
523439 '@oxc-project/types@0.154.0':
524440 resolution: {integrity: sha512-KYOUGQFlLWR+s4QMV2Q9ko38TZm7EdO87Qm7rvZLqk/webse/cDjtyxKhg/47l0byCbs/k46LT8qc4PKjAikzA==}
525441
526 '@preact/preset-vite@2.10.6':
527 resolution: {integrity: sha512-ZZ5RIT2ZVXg8UxRA8VZpoT8CdpDG7RFIqOT4bELqNBp85+HKvQBbgmh3gsoW1UOQXx26TLe+ZRNKI7q281Kckw==}
528 peerDependencies:
529 '@babel/core': 7.x
530 vite: 2.x || 3.x || 4.x || 5.x || 6.x || 7.x || 8.x
531
532 '@prefresh/babel-plugin@0.5.4':
533 resolution: {integrity: sha512-GSwqHGVem7AjnOKAkmLNbAExqZZFddf9S3ySOwowGEsJR8vwNKJ7Z/262fov5wra8LkDVSmFw3CjMGRdLoCmRA==}
534 peerDependencies:
535 '@babel/core': ^7.0.0 || ^8.0.0
536
537 '@prefresh/core@1.5.11':
538 resolution: {integrity: sha512-Ml00PP8jeHaADxfQqxUs4+M5JS+T5J26UnvPVfsrmlpGw2I3e9XtM0DMeuzkmvg+Tn7VdHSMpLBrDPG0iHoDqQ==}
539 peerDependencies:
540 preact: ^10.0.0 || ^11.0.0-0
541
542 '@prefresh/utils@1.2.1':
543 resolution: {integrity: sha512-vq/sIuN5nYfYzvyayXI4C2QkprfNaHUQ9ZX+3xLD8nL3rWyzpxOm1+K7RtMbhd+66QcaISViK7amjnheQ/4WZw==}
544
545 '@prefresh/vite@2.4.12':
546 resolution: {integrity: sha512-FY1fzXpUjiuosznMV0YM7XAOPZjB5FIdWS0W24+XnlxYkt9hNAwwsiKYn+cuTEoMtD/ZVazS5QVssBr9YhpCQA==}
547 peerDependencies:
548 preact: ^10.4.0 || ^11.0.0-0
549 vite: '>=2.0.0'
550
551442 '@rolldown/binding-android-arm-eabi@1.2.14':
552443 resolution: {integrity: sha512-aWR+XKH9+rW7m5fWq/5360J3mHycrx/o6xZM2DQFdJnTMdyP7/WzxPjhtLbVWdKo2y612/4U1bWqb3HQhiF7vg==}
553444 engines: {node: ^20.19.0 || >=22.12.0}
@@ -647,19 +538,6 @@
647538 '@rolldown/pluginutils@1.0.1':
648539 resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
649540
650 '@rollup/pluginutils@4.2.1':
651 resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
652 engines: {node: '>= 8.0.0'}
653
654 '@rollup/pluginutils@5.4.0':
655 resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==}
656 engines: {node: '>=14.0.0'}
657 peerDependencies:
658 rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
659 peerDependenciesMeta:
660 rollup:
661 optional: true
662
663541 '@scalar/helpers@0.16.3':
664542 resolution: {integrity: sha512-He0CySU4Iv4DwyVF40EoVpvPneRQ1zqC/g1eBfR2i2G9RE/BEq/F5AiUrjIkbtTPjSbIiIL1AXMkXvBvgHVdsA==}
665543 engines: {node: '>=22'}
@@ -805,12 +683,33 @@
805683 peerDependencies:
806684 vite: ^5.2.0 || ^6 || ^7 || ^8
807685
808 '@types/estree@1.0.9':
809 resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
810
811686 '@types/node@24.19.2':
812687 resolution: {integrity: sha512-93d49wCWJkCyN87MxLpNN6KXPCwFw7bVUg79mGUV1OS40JGWwVCr8n61DXCSqz7CV6/VfPkb7xec7uP2M+QHWQ==}
813688
689 '@types/react-dom@19.3.0':
690 resolution: {integrity: sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==}
691 peerDependencies:
692 '@types/react': ^19.3.0
693
694 '@types/react@19.3.0':
695 resolution: {integrity: sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==}
696
697 '@vitejs/plugin-react@6.1.2':
698 resolution: {integrity: sha512-fhJNGUdscQa4HkI0xFxwonWyhnVReCPDbXI7FH7jmzpnVCrFQI7PRrhWecO263610vOrs9tr2Y4xcf8uAdFxbw==}
699 engines: {node: ^20.19.0 || >=22.12.0}
700 peerDependencies:
701 '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0
702 babel-plugin-react-compiler: ^1.0.0
703 oxc-transform-react: ^0.152.0
704 vite: ^8.0.0
705 peerDependenciesMeta:
706 '@rolldown/plugin-babel':
707 optional: true
708 babel-plugin-react-compiler:
709 optional: true
710 oxc-transform-react:
711 optional: true
712
814713 acorn@8.19.0:
815714 resolution: {integrity: sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==}
816715 engines: {node: '>=0.4.0'}
@@ -844,31 +743,10 @@
844743 argparse@3.0.3:
845744 resolution: {integrity: sha512-Sn98ftV3KfsO8xfLPHG6oT8y+b0yvbrLgZjgPDXsharNY15tTAjqIsIju4S32nl6/iEUNNE4v+yL2Zhm8ooP3w==}
846745
847 babel-plugin-transform-hook-names@1.0.2:
848 resolution: {integrity: sha512-5gafyjyyBTTdX/tQQ0hRgu4AhNHG/hqWi0ZZmg2xvs2FgRkJXzDNKBZCyoYqgFkovfDrgM8OoKg8karoUvWeCw==}
849 peerDependencies:
850 '@babel/core': ^7.12.10
851
852 baseline-browser-mapping@2.11.30:
853 resolution: {integrity: sha512-YJs6Pz17RMNv8HoyKREtCY38E3BV1nPkWVmIb+XN1uHC1Vqt3KZCH/uJH9ZKRoiKb/icZ6tvehyee6RZNbFfRA==}
854 engines: {node: '>=6.0.0'}
855 hasBin: true
856
857 boolbase@1.0.0:
858 resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
859
860746 braces@3.0.3:
861747 resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
862748 engines: {node: '>=8'}
863749
864 browserslist@4.29.4:
865 resolution: {integrity: sha512-2TcbbiwXEq+N0kD36tykpCfyyQigUXmuwNQbnPp4Xk4PX9JOqVIKjJlIDjpH7uY6tmRGoAd+apeT1p60T8wsdA==}
866 engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
867 hasBin: true
868
869 caniuse-lite@1.0.30001817:
870 resolution: {integrity: sha512-uZ1JG80BwhtMalIAGPxPfmNhHyd/B/nTdr3hITWlRamSyAdUStP1Gop01e7SD5q6J90ixJvWDbvmiFVfDgn4bw==}
871
872750 chokidar@4.0.3:
873751 resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
874752 engines: {node: '>= 14.16.0'}
@@ -895,9 +773,6 @@
895773 compare-versions@6.1.1:
896774 resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==}
897775
898 convert-source-map@2.0.0:
899 resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
900
901776 cosmiconfig@10.0.1:
902777 resolution: {integrity: sha512-/zvGoYn0y27QTBmK+0B+oPdEPC4fKGUmJsMTQbDjqph00TrkqfVFW/M+C6ya54sYCgHC6QCwK/mI6djt8cEGHQ==}
903778 engines: {node: ^22.18 || >= 24}
@@ -906,46 +781,17 @@
906781 resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
907782 engines: {node: '>= 8'}
908783
909 css-select@5.2.2:
910 resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
911
912 css-what@6.2.2:
913 resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==}
914 engines: {node: '>= 6'}
915
916 debug@4.4.3:
917 resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
918 engines: {node: '>=6.0'}
919 peerDependencies:
920 supports-color: '*'
921 peerDependenciesMeta:
922 supports-color:
923 optional: true
784 csstype@3.2.3:
785 resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
924786
925787 detect-libc@2.1.2:
926788 resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
927789 engines: {node: '>=8'}
928790
929 dom-serializer@2.0.0:
930 resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
931
932 domelementtype@2.3.0:
933 resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
934
935 domhandler@5.0.3:
936 resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
937 engines: {node: '>= 4'}
938
939 domutils@3.2.2:
940 resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
941
942791 effector@23.4.4:
943792 resolution: {integrity: sha512-QkZboRN28K/iwxigDhlJcI3ux3aNbt8kYGGH/GkqWG0OlGeyuBhb7PdM89Iu+ogV8Lmz16xIlwnXR2UNWI6psg==}
944793 engines: {node: '>=11.0.0'}
945794
946 electron-to-chromium@1.5.461:
947 resolution: {integrity: sha512-3WZFARz1By+/rYcqQ0OLj4UcEdhd6tz2LEcDtKBUq7ey77EeJ1OtetY/5hjSUxw7dC5r5mA7FfkhRbqO9vnTCg==}
948
949795 emoji-regex@8.0.0:
950796 resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
951797
@@ -957,10 +803,6 @@
957803 resolution: {integrity: sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==}
958804 engines: {node: '>=10.13.0'}
959805
960 entities@4.5.0:
961 resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
962 engines: {node: '>=0.12'}
963
964806 env-paths@2.2.1:
965807 resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
966808 engines: {node: '>=6'}
@@ -974,9 +816,6 @@
974816 resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
975817 engines: {node: '>=6'}
976818
977 estree-walker@2.0.2:
978 resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
979
980819 esutils@2.0.3:
981820 resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
982821 engines: {node: '>=0.10.0'}
@@ -1024,10 +863,6 @@
1024863 engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
1025864 os: [darwin]
1026865
1027 gensync@1.0.0-beta.2:
1028 resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
1029 engines: {node: '>=6.9.0'}
1030
1031866 get-caller-file@2.0.5:
1032867 resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
1033868 engines: {node: 6.* || 8.* || >= 10.*}
@@ -1050,10 +885,6 @@
1050885 graceful-fs@4.2.11:
1051886 resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
1052887
1053 he@1.2.0:
1054 resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
1055 hasBin: true
1056
1057888 human-signals@8.0.1:
1058889 resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==}
1059890 engines: {node: '>=18.18.0'}
@@ -1104,9 +935,6 @@
1104935 resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
1105936 hasBin: true
1106937
1107 js-tokens@4.0.0:
1108 resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
1109
1110938 js-yaml@4.3.2:
1111939 resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==}
1112940 hasBin: true
@@ -1128,9 +956,6 @@
1128956 engines: {node: '>=6'}
1129957 hasBin: true
1130958
1131 kolorist@1.8.0:
1132 resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
1133
1134959 lightningcss-android-arm64@1.32.0:
1135960 resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
1136961 engines: {node: '>= 12.0.0'}
@@ -1282,13 +1107,14 @@
12821107 lodash-es@4.18.1:
12831108 resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==}
12841109
1285 lru-cache@5.1.1:
1286 resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
1287
1288 lucide-preact@1.60.0:
1289 resolution: {integrity: sha512-SXcv6sTczS1Xvnn7egHznYlAR9ZOh7IYkaujqtT9HElYkEVcokX3NvEiSwE/xHfagdoOygV9RRbmu1d0DwbQqw==}
1110 lucide-react@1.66.0:
1111 resolution: {integrity: sha512-8zjbi/Us+ETXqfBwmXF/FHrWGIqOdE6zT1yUdRJLQVoptomSkB0zdFWVIM667t9K8djWDaXdkBkqi2qVTuDNiw==}
12901112 peerDependencies:
1291 preact: ^10.27.2 || ^11.0.0-0
1113 '@types/react': '*'
1114 react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
1115 peerDependenciesMeta:
1116 '@types/react':
1117 optional: true
12921118
12931119 magic-string@0.30.21:
12941120 resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
@@ -1300,9 +1126,6 @@
13001126 micromatch@4.0.8:
13011127 resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
13021128 engines: {node: '>=8.6'}
1303
1304 ms@2.1.3:
1305 resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
13061129
13071130 nanoid@3.3.20:
13081131 resolution: {integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==}
@@ -1314,20 +1137,10 @@
13141137 engines: {node: ^18 || >=20}
13151138 hasBin: true
13161139
1317 node-html-parser@6.1.13:
1318 resolution: {integrity: sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==}
1319
1320 node-releases@2.0.59:
1321 resolution: {integrity: sha512-5qr2okC4ItAI0hG5SaV0HoYMpnM/Y6+Oqr7AmPYy/2uEoGvS+GbSKLeRxSS1uAdu59yz0ouzcuXievSgg///WA==}
1322 engines: {node: '>=18'}
1323
13241140 npm-run-path@6.0.0:
13251141 resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==}
13261142 engines: {node: '>=18'}
13271143
1328 nth-check@2.1.1:
1329 resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
1330
13311144 orval@8.43.0:
13321145 resolution: {integrity: sha512-0gSCXFygPF/9mx42bZw1QA8Dn4a8u/cRlAz5Essdtap1TNAoDA3GMH31Mo/SETLxx9soy3FTg7gvFSMBDjCRZg==}
13331146 engines: {node: '>=22.18.0'}
@@ -1383,14 +1196,6 @@
13831196 resolution: {integrity: sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==}
13841197 engines: {node: ^10 || ^12 || >=14}
13851198
1386 preact@10.29.8:
1387 resolution: {integrity: sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==}
1388 peerDependencies:
1389 preact-render-to-string: '>=5'
1390 peerDependenciesMeta:
1391 preact-render-to-string:
1392 optional: true
1393
13941199 prettier@3.9.10:
13951200 resolution: {integrity: sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==}
13961201 engines: {node: '>=14'}
@@ -1407,6 +1212,15 @@
14071212 queue-microtask@1.2.3:
14081213 resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
14091214
1215 react-dom@19.3.1:
1216 resolution: {integrity: sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==}
1217 peerDependencies:
1218 react: ^19.3.1
1219
1220 react@19.3.1:
1221 resolution: {integrity: sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==}
1222 engines: {node: '>=0.10.0'}
1223
14101224 readdirp@4.1.2:
14111225 resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
14121226 engines: {node: '>= 14.18.0'}
@@ -1438,9 +1252,8 @@
14381252 run-parallel@1.2.0:
14391253 resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
14401254
1441 semver@6.3.1:
1442 resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
1443 hasBin: true
1255 scheduler@0.28.0:
1256 resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==}
14441257
14451258 shebang-command@2.0.0:
14461259 resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
@@ -1453,9 +1266,6 @@
14531266 signal-exit@4.1.0:
14541267 resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
14551268 engines: {node: '>=14'}
1456
1457 simple-code-frame@1.3.0:
1458 resolution: {integrity: sha512-MB4pQmETUBlNs62BBeRjIFGeuy/x6gGKh7+eRUemn1rCFhqo7K+4slPqsyizCbcbYLnaYqaoZ2FWsZ/jN06D8w==}
14591269
14601270 sisteransi@1.0.5:
14611271 resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
@@ -1468,14 +1278,6 @@
14681278 resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==}
14691279 engines: {node: '>=0.10.0'}
14701280
1471 source-map@0.7.6:
1472 resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}
1473 engines: {node: '>= 12'}
1474
1475 stack-trace@1.0.0:
1476 resolution: {integrity: sha512-H6D7134xi6qONvh7ZHKgviXf+rd3vhGBSvebPZCaUkd8zvQ+7PtDw6CljPTe4cXWNf2IKZGNqw6VJXSb9IgBpA==}
1477 engines: {node: '>=20.0.0'}
1478
14791281 steiger@0.7.0:
14801282 resolution: {integrity: sha512-KXqt/6LqQE5O9QhJ9ZsA4M/Nwys6pslryXk67PltNZ8UBzbV9X9PEwMqskEdOObgnHsb+hEOzPH1UbSGFMuRYg==}
14811283 engines: {node: '>=22.18.0'}
@@ -1560,17 +1362,6 @@
15601362 resolution: {integrity: sha512-lzlXPoVB0Uy/FvTaUgX39RbixYiSKoc3JSoSf01+0c2B6FR3qdYIPN7Qjo/AV7n6sqLCk0509cT3LRj1oSZ1+A==}
15611363 engines: {node: '>=20'}
15621364
1563 update-browserslist-db@1.3.4:
1564 resolution: {integrity: sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==}
1565 hasBin: true
1566 peerDependencies:
1567 browserslist: '>= 4.21.0'
1568
1569 vite-prerender-plugin@0.5.14:
1570 resolution: {integrity: sha512-X2w9AS1OhwEUec9HSryGAmEnEQJGhW6eFoiCVJv8BfOTVpM8SOjk75cEw/IC28APDHZudeIRbP8Sp7wfABq+Aw==}
1571 peerDependencies:
1572 vite: 5.x || 6.x || 7.x || 8.x
1573
15741365 vite@8.3.4:
15751366 resolution: {integrity: sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ==}
15761367 engines: {node: ^20.19.0 || >=22.12.0}
@@ -1644,9 +1435,6 @@
16441435 resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
16451436 engines: {node: '>=10'}
16461437
1647 yallist@3.1.1:
1648 resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
1649
16501438 yaml@2.9.1:
16511439 resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==}
16521440 engines: {node: '>= 14.6'}
@@ -1655,161 +1443,29 @@
16551443 yargs-parser@21.1.1:
16561444 resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
16571445 engines: {node: '>=12'}
1658
1659 yargs@17.7.3:
1660 resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==}
1661 engines: {node: '>=12'}
1662
1663 yoctocolors@2.2.0:
1664 resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==}
1665 engines: {node: '>=18'}
1666
1667 zimmerframe@1.1.5:
1668 resolution: {integrity: sha512-msJxIvYDYcoNL+PJsu+7qmpDWsYmAxTY+2TNYXXF0hzBzBk0BMecOqDOG/EckUoKCuKwObfbugIl8QpqHDXeFA==}
1669
1670 zod-validation-error@3.5.4:
1671 resolution: {integrity: sha512-+hEiRIiPobgyuFlEojnqjJnhFvg4r/i3cqgcm67eehZf/WBaK3g6cD02YU9mtdVxZjv8CzCA9n/Rhrs3yAAvAw==}
1672 engines: {node: '>=18.0.0'}
1673 peerDependencies:
1674 zod: ^3.24.4
1675
1676 zod@3.25.76:
1677 resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
1678
1679 zod@4.6.7:
1680 resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==}
1681
1682snapshots:
1683
1684 '@babel/code-frame@7.29.7':
1685 dependencies:
1686 '@babel/helper-validator-identifier': 7.29.7
1687 js-tokens: 4.0.0
1688 picocolors: 1.1.1
1689
1690 '@babel/compat-data@7.29.7': {}
1691
1692 '@babel/core@7.29.7':
1693 dependencies:
1694 '@babel/code-frame': 7.29.7
1695 '@babel/generator': 7.29.8
1696 '@babel/helper-compilation-targets': 7.29.7
1697 '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7)
1698 '@babel/helpers': 7.29.10
1699 '@babel/parser': 7.29.9
1700 '@babel/template': 7.29.7
1701 '@babel/traverse': 7.29.10
1702 '@babel/types': 7.29.8
1703 '@jridgewell/remapping': 2.3.5
1704 convert-source-map: 2.0.0
1705 debug: 4.4.3
1706 gensync: 1.0.0-beta.2
1707 json5: 2.2.3
1708 semver: 6.3.1
1709 transitivePeerDependencies:
1710 - supports-color
1711
1712 '@babel/generator@7.29.8':
1713 dependencies:
1714 '@babel/parser': 7.29.9
1715 '@babel/types': 7.29.8
1716 '@jridgewell/gen-mapping': 0.3.13
1717 '@jridgewell/trace-mapping': 0.3.31
1718 jsesc: 3.1.0
1719
1720 '@babel/helper-annotate-as-pure@7.29.7':
1721 dependencies:
1722 '@babel/types': 7.29.8
1723
1724 '@babel/helper-compilation-targets@7.29.7':
1725 dependencies:
1726 '@babel/compat-data': 7.29.7
1727 '@babel/helper-validator-option': 7.29.7
1728 browserslist: 4.29.4
1729 lru-cache: 5.1.1
1730 semver: 6.3.1
1731
1732 '@babel/helper-globals@7.29.7': {}
1733
1734 '@babel/helper-module-imports@7.29.7':
1735 dependencies:
1736 '@babel/traverse': 7.29.10
1737 '@babel/types': 7.29.8
1738 transitivePeerDependencies:
1739 - supports-color
1740
1741 '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)':
1742 dependencies:
1743 '@babel/core': 7.29.7
1744 '@babel/helper-module-imports': 7.29.7
1745 '@babel/helper-validator-identifier': 7.29.7
1746 '@babel/traverse': 7.29.10
1747 transitivePeerDependencies:
1748 - supports-color
1749
1750 '@babel/helper-plugin-utils@7.29.7': {}
1751
1752 '@babel/helper-string-parser@7.29.7': {}
1753
1754 '@babel/helper-validator-identifier@7.29.7': {}
1755
1756 '@babel/helper-validator-option@7.29.7': {}
1757
1758 '@babel/helpers@7.29.10':
1759 dependencies:
1760 '@babel/template': 7.29.7
1761 '@babel/types': 7.29.8
17621446
1763 '@babel/parser@7.29.9':
1764 dependencies:
1765 '@babel/types': 7.29.8
1447 yargs@17.7.3:
1448 resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==}
1449 engines: {node: '>=12'}
17661450
1767 '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)':
1768 dependencies:
1769 '@babel/core': 7.29.7
1770 '@babel/helper-plugin-utils': 7.29.7
1451 yoctocolors@2.2.0:
1452 resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==}
1453 engines: {node: '>=18'}
17711454
1772 '@babel/plugin-transform-react-jsx-development@7.29.7(@babel/core@7.29.7)':
1773 dependencies:
1774 '@babel/core': 7.29.7
1775 '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7)
1776 transitivePeerDependencies:
1777 - supports-color
1455 zod-validation-error@3.5.4:
1456 resolution: {integrity: sha512-+hEiRIiPobgyuFlEojnqjJnhFvg4r/i3cqgcm67eehZf/WBaK3g6cD02YU9mtdVxZjv8CzCA9n/Rhrs3yAAvAw==}
1457 engines: {node: '>=18.0.0'}
1458 peerDependencies:
1459 zod: ^3.24.4
17781460
1779 '@babel/plugin-transform-react-jsx@7.29.7(@babel/core@7.29.7)':
1780 dependencies:
1781 '@babel/core': 7.29.7
1782 '@babel/helper-annotate-as-pure': 7.29.7
1783 '@babel/helper-module-imports': 7.29.7
1784 '@babel/helper-plugin-utils': 7.29.7
1785 '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7)
1786 '@babel/types': 7.29.8
1787 transitivePeerDependencies:
1788 - supports-color
1461 zod@3.25.76:
1462 resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
17891463
1790 '@babel/template@7.29.7':
1791 dependencies:
1792 '@babel/code-frame': 7.29.7
1793 '@babel/parser': 7.29.9
1794 '@babel/types': 7.29.8
1464 zod@4.6.7:
1465 resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==}
17951466
1796 '@babel/traverse@7.29.10':
1797 dependencies:
1798 '@babel/code-frame': 7.29.7
1799 '@babel/generator': 7.29.8
1800 '@babel/helper-globals': 7.29.7
1801 '@babel/parser': 7.29.9
1802 '@babel/template': 7.29.7
1803 '@babel/types': 7.29.8
1804 debug: 4.4.3
1805 transitivePeerDependencies:
1806 - supports-color
1467snapshots:
18071468
1808 '@babel/types@7.29.8':
1809 dependencies:
1810 '@babel/helper-string-parser': 7.29.7
1811 '@babel/helper-validator-identifier': 7.29.7
1812
18131469 '@biomejs/biome@2.5.15':
18141470 optionalDependencies:
18151471 '@biomejs/cli-darwin-arm64': 2.5.15
@@ -2099,47 +1755,6 @@
20991755
21001756 '@oxc-project/types@0.154.0': {}
21011757
2102 '@preact/preset-vite@2.10.6(@babel/core@7.29.7)(preact@10.29.8)(vite@8.3.4(@types/node@24.19.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.1))':
2103 dependencies:
2104 '@babel/core': 7.29.7
2105 '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7)
2106 '@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7)
2107 '@prefresh/vite': 2.4.12(preact@10.29.8)(vite@8.3.4(@types/node@24.19.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.1))
2108 '@rollup/pluginutils': 5.4.0
2109 babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.29.7)
2110 debug: 4.4.3
2111 magic-string: 0.30.21
2112 picocolors: 1.1.1
2113 vite: 8.3.4(@types/node@24.19.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.1)
2114 vite-prerender-plugin: 0.5.14(vite@8.3.4(@types/node@24.19.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.1))
2115 zimmerframe: 1.1.5
2116 transitivePeerDependencies:
2117 - preact
2118 - rollup
2119 - supports-color
2120
2121 '@prefresh/babel-plugin@0.5.4(@babel/core@7.29.7)':
2122 dependencies:
2123 '@babel/core': 7.29.7
2124
2125 '@prefresh/core@1.5.11(preact@10.29.8)':
2126 dependencies:
2127 preact: 10.29.8
2128
2129 '@prefresh/utils@1.2.1': {}
2130
2131 '@prefresh/vite@2.4.12(preact@10.29.8)(vite@8.3.4(@types/node@24.19.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.1))':
2132 dependencies:
2133 '@babel/core': 7.29.7
2134 '@prefresh/babel-plugin': 0.5.4(@babel/core@7.29.7)
2135 '@prefresh/core': 1.5.11(preact@10.29.8)
2136 '@prefresh/utils': 1.2.1
2137 '@rollup/pluginutils': 4.2.1
2138 preact: 10.29.8
2139 vite: 8.3.4(@types/node@24.19.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.1)
2140 transitivePeerDependencies:
2141 - supports-color
2142
21431758 '@rolldown/binding-android-arm-eabi@1.2.14':
21441759 optional: true
21451760
@@ -2187,17 +1802,6 @@
21871802
21881803 '@rolldown/pluginutils@1.0.1': {}
21891804
2190 '@rollup/pluginutils@4.2.1':
2191 dependencies:
2192 estree-walker: 2.0.2
2193 picomatch: 2.3.2
2194
2195 '@rollup/pluginutils@5.4.0':
2196 dependencies:
2197 '@types/estree': 1.0.9
2198 estree-walker: 2.0.2
2199 picomatch: 4.0.7
2200
22011805 '@scalar/helpers@0.16.3': {}
22021806
22031807 '@scalar/helpers@0.16.4': {}
@@ -2329,13 +1933,24 @@
23291933 '@tailwindcss/oxide': 4.3.3
23301934 tailwindcss: 4.3.3
23311935 vite: 8.3.4(@types/node@24.19.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.1)
2332
2333 '@types/estree@1.0.9': {}
23341936
23351937 '@types/node@24.19.2':
23361938 dependencies:
23371939 undici-types: 7.24.6
23381940
1941 '@types/react-dom@19.3.0(@types/react@19.3.0)':
1942 dependencies:
1943 '@types/react': 19.3.0
1944
1945 '@types/react@19.3.0':
1946 dependencies:
1947 csstype: 3.2.3
1948
1949 '@vitejs/plugin-react@6.1.2(vite@8.3.4(@types/node@24.19.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.1))':
1950 dependencies:
1951 '@rolldown/pluginutils': 1.0.1
1952 vite: 8.3.4(@types/node@24.19.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.1)
1953
23391954 acorn@8.19.0: {}
23401955
23411956 ajv-draft-04@1.0.0(ajv@8.20.0):
@@ -2363,28 +1978,10 @@
23631978
23641979 argparse@3.0.3: {}
23651980
2366 babel-plugin-transform-hook-names@1.0.2(@babel/core@7.29.7):
2367 dependencies:
2368 '@babel/core': 7.29.7
2369
2370 baseline-browser-mapping@2.11.30: {}
2371
2372 boolbase@1.0.0: {}
2373
23741981 braces@3.0.3:
23751982 dependencies:
23761983 fill-range: 7.1.1
23771984
2378 browserslist@4.29.4:
2379 dependencies:
2380 baseline-browser-mapping: 2.11.30
2381 caniuse-lite: 1.0.30001817
2382 electron-to-chromium: 1.5.461
2383 node-releases: 2.0.59
2384 update-browserslist-db: 1.3.4(browserslist@4.29.4)
2385
2386 caniuse-lite@1.0.30001817: {}
2387
23881985 chokidar@4.0.3:
23891986 dependencies:
23901987 readdirp: 4.1.2
@@ -2408,8 +2005,6 @@
24082005 commander@15.0.0: {}
24092006
24102007 compare-versions@6.1.1: {}
2411
2412 convert-source-map@2.0.0: {}
24132008
24142009 cosmiconfig@10.0.1:
24152010 dependencies:
@@ -2422,44 +2017,12 @@
24222017 shebang-command: 2.0.0
24232018 which: 2.0.2
24242019
2425 css-select@5.2.2:
2426 dependencies:
2427 boolbase: 1.0.0
2428 css-what: 6.2.2
2429 domhandler: 5.0.3
2430 domutils: 3.2.2
2431 nth-check: 2.1.1
2432
2433 css-what@6.2.2: {}
2434
2435 debug@4.4.3:
2436 dependencies:
2437 ms: 2.1.3
2020 csstype@3.2.3: {}
24382021
24392022 detect-libc@2.1.2: {}
24402023
2441 dom-serializer@2.0.0:
2442 dependencies:
2443 domelementtype: 2.3.0
2444 domhandler: 5.0.3
2445 entities: 4.5.0
2446
2447 domelementtype@2.3.0: {}
2448
2449 domhandler@5.0.3:
2450 dependencies:
2451 domelementtype: 2.3.0
2452
2453 domutils@3.2.2:
2454 dependencies:
2455 dom-serializer: 2.0.0
2456 domelementtype: 2.3.0
2457 domhandler: 5.0.3
2458
24592024 effector@23.4.4: {}
24602025
2461 electron-to-chromium@1.5.461: {}
2462
24632026 emoji-regex@8.0.0: {}
24642027
24652028 empathic@1.1.0: {}
@@ -2469,8 +2032,6 @@
24692032 graceful-fs: 4.2.11
24702033 tapable: 2.3.3
24712034
2472 entities@4.5.0: {}
2473
24742035 env-paths@2.2.1: {}
24752036
24762037 esbuild@0.28.2:
@@ -2504,8 +2065,6 @@
25042065
25052066 escalade@3.2.0: {}
25062067
2507 estree-walker@2.0.2: {}
2508
25092068 esutils@2.0.3: {}
25102069
25112070 execa@9.6.1:
@@ -2556,8 +2115,6 @@
25562115 fsevents@2.3.3:
25572116 optional: true
25582117
2559 gensync@1.0.0-beta.2: {}
2560
25612118 get-caller-file@2.0.5: {}
25622119
25632120 get-stream@9.0.1:
@@ -2585,8 +2142,6 @@
25852142
25862143 graceful-fs@4.2.11: {}
25872144
2588 he@1.2.0: {}
2589
25902145 human-signals@8.0.1: {}
25912146
25922147 ignore@7.0.13: {}
@@ -2615,8 +2170,6 @@
26152170
26162171 jiti@2.7.0: {}
26172172
2618 js-tokens@4.0.0: {}
2619
26202173 js-yaml@4.3.2:
26212174 dependencies:
26222175 argparse: 2.0.1
@@ -2630,8 +2183,6 @@
26302183 json-schema-traverse@1.0.0: {}
26312184
26322185 json5@2.2.3: {}
2633
2634 kolorist@1.8.0: {}
26352186
26362187 lightningcss-android-arm64@1.32.0:
26372188 optional: true
@@ -2733,13 +2284,11 @@
27332284
27342285 lodash-es@4.18.1: {}
27352286
2736 lru-cache@5.1.1:
2287 lucide-react@1.66.0(@types/react@19.3.0)(react@19.3.1):
27372288 dependencies:
2738 yallist: 3.1.1
2739
2740 lucide-preact@1.60.0(preact@10.29.8):
2741 dependencies:
2742 preact: 10.29.8
2289 react: 19.3.1
2290 optionalDependencies:
2291 '@types/react': 19.3.0
27432292
27442293 magic-string@0.30.21:
27452294 dependencies:
@@ -2752,28 +2301,15 @@
27522301 braces: 3.0.3
27532302 picomatch: 2.3.2
27542303
2755 ms@2.1.3: {}
2756
27572304 nanoid@3.3.20: {}
27582305
27592306 nanoid@5.1.16: {}
27602307
2761 node-html-parser@6.1.13:
2762 dependencies:
2763 css-select: 5.2.2
2764 he: 1.2.0
2765
2766 node-releases@2.0.59: {}
2767
27682308 npm-run-path@6.0.0:
27692309 dependencies:
27702310 path-key: 4.0.0
27712311 unicorn-magic: 0.3.0
27722312
2773 nth-check@2.1.1:
2774 dependencies:
2775 boolbase: 1.0.0
2776
27772313 orval@8.43.0(prettier@3.9.10)(typescript@6.0.3)(zod@3.25.76):
27782314 dependencies:
27792315 '@commander-js/extra-typings': 15.0.0(commander@15.0.0)
@@ -2843,8 +2379,6 @@
28432379 nanoid: 3.3.20
28442380 picocolors: 1.1.1
28452381 source-map-js: 1.2.2
2846
2847 preact@10.29.8: {}
28482382
28492383 prettier@3.9.10: {}
28502384
@@ -2856,6 +2390,13 @@
28562390
28572391 queue-microtask@1.2.3: {}
28582392
2393 react-dom@19.3.1(react@19.3.1):
2394 dependencies:
2395 react: 19.3.1
2396 scheduler: 0.28.0
2397
2398 react@19.3.1: {}
2399
28592400 readdirp@4.1.2: {}
28602401
28612402 readdirp@5.1.1: {}
@@ -2893,7 +2434,7 @@
28932434 dependencies:
28942435 queue-microtask: 1.2.3
28952436
2896 semver@6.3.1: {}
2437 scheduler@0.28.0: {}
28972438
28982439 shebang-command@2.0.0:
28992440 dependencies:
@@ -2903,20 +2444,12 @@
29032444
29042445 signal-exit@4.1.0: {}
29052446
2906 simple-code-frame@1.3.0:
2907 dependencies:
2908 kolorist: 1.8.0
2909
29102447 sisteransi@1.0.5: {}
29112448
29122449 slash@5.1.0: {}
29132450
29142451 source-map-js@1.2.2: {}
29152452
2916 source-map@0.7.6: {}
2917
2918 stack-trace@1.0.0: {}
2919
29202453 steiger@0.7.0(typescript@6.0.3):
29212454 dependencies:
29222455 '@clack/prompts': 0.9.1
@@ -2991,22 +2524,6 @@
29912524
29922525 unicorn-magic@0.4.1: {}
29932526
2994 update-browserslist-db@1.3.4(browserslist@4.29.4):
2995 dependencies:
2996 browserslist: 4.29.4
2997 escalade: 3.2.0
2998 picocolors: 1.1.1
2999
3000 vite-prerender-plugin@0.5.14(vite@8.3.4(@types/node@24.19.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.1)):
3001 dependencies:
3002 kolorist: 1.8.0
3003 magic-string: 0.30.21
3004 node-html-parser: 6.1.13
3005 simple-code-frame: 1.3.0
3006 source-map: 0.7.6
3007 stack-trace: 1.0.0
3008 vite: 8.3.4(@types/node@24.19.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.1)
3009
30102527 vite@8.3.4(@types/node@24.19.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.1):
30112528 dependencies:
30122529 lightningcss: 1.33.0
@@ -3048,8 +2565,6 @@
30482565
30492566 y18n@5.0.8: {}
30502567
3051 yallist@3.1.1: {}
3052
30532568 yaml@2.9.1: {}
30542569
30552570 yargs-parser@21.1.1: {}
@@ -3065,8 +2580,6 @@
30652580 yargs-parser: 21.1.1
30662581
30672582 yoctocolors@2.2.0: {}
3068
3069 zimmerframe@1.1.5: {}
30702583
30712584 zod-validation-error@3.5.4(zod@3.25.76):
30722585 dependencies:
Modified pnpm-workspace.yaml View pnpm-workspace.yaml
@@ -2,3 +2,5 @@
22# esbuild 的 postinstall 只负责把平台二进制落到 node_modules,是 vite 构建的前置。
33allowBuilds:
44 esbuild: true
5minimumReleaseAgeExclude:
6 - lucide-react@1.66.0
Modified tsconfig.app.json View tsconfig.app.json
@@ -8,9 +8,7 @@
88 "allowArbitraryExtensions": true,
99 "skipLibCheck": true,
1010 "paths": {
11 "@/*": ["./view/src/*"],
12 "react": ["./node_modules/preact/compat/"],
13 "react-dom": ["./node_modules/preact/compat/"]
11 "@/*": ["./view/auth/src/*"]
1412 },
1513
1614 /* Bundler mode */
@@ -20,7 +18,7 @@
2018 "moduleDetection": "force",
2119 "noEmit": true,
2220 "jsx": "react-jsx",
23 "jsxImportSource": "preact",
21 "jsxImportSource": "react",
2422
2523 /* Linting */
2624 "noUnusedLocals": true,
@@ -28,5 +26,5 @@
2826 "erasableSyntaxOnly": true,
2927 "noFallthroughCasesInSwitch": true
3028 },
31 "include": ["view/src"]
29 "include": ["view/auth/src"]
3230}
Modified vite.config.ts View vite.config.ts
@@ -1,16 +1,16 @@
11import { fileURLToPath } from 'node:url'
2import preact from '@preact/preset-vite'
32import tailwindcss from '@tailwindcss/vite'
3import react from '@vitejs/plugin-react'
44import { defineConfig, loadEnv } from 'vite'
55
66export default defineConfig(({ mode }) => {
77 const env = loadEnv(mode, process.cwd(), '')
88 return {
9 root: 'view',
9 root: 'view/auth',
1010 publicDir: 'public',
11 build: { outDir: 'dist', emptyOutDir: true },
12 plugins: [preact(), tailwindcss()],
13 resolve: { alias: { '@': fileURLToPath(new URL('./view/src', import.meta.url)) } },
11 build: { outDir: '../dist', emptyOutDir: true },
12 plugins: [react(), tailwindcss()],
13 resolve: { alias: { '@': fileURLToPath(new URL('./view/auth/src', import.meta.url)) } },
1414 optimizeDeps: { include: ['@noble/hashes/sha2.js'] },
1515 server: {
1616 port: 15474,