gitdataai/libs/service/auth/register.rs
ZhenYi 773da34fab refactor(service): migrate auth, git service, agent from slog to tracing
- Remove all use slog::* imports and slog::Logger fields/parameters
- Replace slog::info!/warn!/error! with tracing::info!/warn!/error!
- AppService: remove pub logs: slog::Logger field, update callers of
  AppEmail::init(), MessageProducer::new(), RoomService::new(),
  start_email_worker(), start_room_workers()
- auth/: captcha, email, login, logout, password, register, rsa, totp
- git/: archive, blame, blob, branch, commit, contributors, diff,
  refs, star, tag, tree, watch
- agent/: billing (ai_usage_recorded), code_review, pr_summary, sync
- project/activity.rs, workspace/alert.rs
2026-04-21 22:28:33 +08:00

160 lines
5.6 KiB
Rust

use crate::AppService;
use crate::error::AppError;
use argon2::password_hash::{Salt, SaltString};
use argon2::{Argon2, PasswordHasher};
use models::users::{user, user_activity_log, user_email, user_password};
use models::workspaces::{WorkspaceRole, workspace, workspace_membership};
use sea_orm::*;
use serde::{Deserialize, Serialize};
use session::Session;
use uuid::Uuid;
#[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)]
pub struct RegisterParams {
pub username: String,
pub email: String,
pub password: String,
pub captcha: String,
}
impl AppService {
pub async fn auth_register(
&self,
params: RegisterParams,
context: &Session,
) -> Result<user::Model, AppError> {
self.auth_check_captcha(&context, params.captcha).await?;
let password = self.auth_rsa_decode(&context, params.password).await?;
if self
.utils_find_user_by_username(params.username.clone())
.await
.is_ok()
{
tracing::warn!(username = %params.username, "Registration failed: username already exists");
return Err(AppError::UserNameExists);
}
if self
.utils_find_user_by_email(params.email.clone())
.await
.is_ok()
{
tracing::warn!(email = %params.email, "Registration failed: email already exists");
return Err(AppError::EmailExists);
}
let user_uid = Uuid::now_v7();
let now = chrono::Utc::now();
let txn = self.db.begin().await.map_err(|_| AppError::TxnError)?;
let user_model = user::ActiveModel {
uid: Set(user_uid),
username: Set(params.username.clone()),
display_name: Set(Some(params.username.clone())),
avatar_url: Set(None),
website_url: Set(None),
organization: Set(None),
last_sign_in_at: Set(None),
created_at: Set(now),
updated_at: Set(now),
};
let user = user_model.insert(&txn).await.map_err(|e| {
tracing::error!(error = ?e, "Failed to insert user");
AppError::UserNotFound
})?;
let user_email_model = user_email::ActiveModel {
user: Set(user_uid),
email: Set(params.email),
created_at: Set(now),
};
user_email_model.insert(&txn).await.map_err(|e| {
tracing::error!(error = ?e, "Failed to insert user email");
AppError::UserNotFound
})?;
let salt = SaltString::generate(&mut rsa::rand_core::OsRng::default());
let password_hash = Argon2::default()
.hash_password(password.as_bytes(), Salt::from_b64(&*salt.to_string())?)
.map_err(|e| {
tracing::error!(error = ?e, "Failed to hash password");
AppError::UserNotFound
})?
.to_string();
let user_password_model = user_password::ActiveModel {
user: Set(user_uid),
password_hash: Set(password_hash),
password_salt: Set(Some(salt.to_string())),
is_active: Set(true),
created_at: Set(now),
updated_at: Set(now),
};
user_password_model.insert(&txn).await.map_err(|e| {
tracing::error!(error = ?e, "Failed to insert user password");
AppError::UserNotFound
})?;
let _ = user_activity_log::ActiveModel {
user_uid: Set(Option::from(user_uid)),
action: Set("register".to_string()),
ip_address: Set(context.ip_address()),
user_agent: Set(context.user_agent()),
details: Set(serde_json::json!({
"username": user.username.clone(),
"method": "password"
})
.into()),
created_at: Set(now),
..Default::default()
}
.insert(&txn)
.await;
// Auto-create personal workspace for the new user
let personal_slug = format!("~{}", params.username);
let ws = workspace::ActiveModel {
id: Set(Uuid::now_v7()),
slug: Set(personal_slug),
name: Set(format!("{} 的工作空间", params.username)),
description: Set(None),
avatar_url: Set(None),
plan: Set("free".to_string()),
billing_email: Set(None),
stripe_customer_id: Set(None),
stripe_subscription_id: Set(None),
plan_expires_at: Set(None),
deleted_at: Set(None),
created_at: Set(now),
updated_at: Set(now),
};
let ws = ws.insert(&txn).await.map_err(|e| {
tracing::error!(error = ?e, "Failed to insert personal workspace");
AppError::UserNotFound
})?;
let _ = workspace_membership::ActiveModel {
id: Default::default(),
workspace_id: Set(ws.id),
user_id: Set(user_uid),
role: Set(WorkspaceRole::Owner.to_string()),
status: Set("active".to_string()),
invited_by: Set(None),
joined_at: Set(now),
invite_token: Set(None),
invite_expires_at: Set(None),
}
.insert(&txn)
.await;
txn.commit().await.map_err(|_| AppError::TxnError)?;
context.set_user(user_uid);
context.set_current_workspace_id(ws.id);
context.remove(Self::RSA_PRIVATE_KEY);
context.remove(Self::RSA_PUBLIC_KEY);
tracing::info!(user_uid = %user_uid, username = %user.username, "User registered successfully");
Ok(user)
}
}