Skip to content
Clone
main
126 lines 5.3 KB Rust
use crate::errors::ApiError;
pub use crate::services::register::*;
use api::http::{Json, State};
use api::middleware::RequestId;
use api::rest::{AppResponseData, NoData};
use session::Session;
#[utoipa::path(
post,
path = "/auth/register/code",
tag = "auth",
operation_id = "auth_register_code",
summary = "发送注册验证码",
description = "校验工作量证明(payload = 邮箱的 UTF-8 字节)与邮箱格式后,把 6 位验证码写入缓存并寄出。缓存键 `auth:code:register:{email}`,有效期由 `verify_code_ttl_secs` 决定,发码限流键 `auth:rate:register:{email}`。",
request_body = RegisterCodeRequest,
responses(
(
status = 200,
description = "验证码已写入缓存并寄出",
body = AppResponseData<RegisterCodeResponse>,
example = json!({"code":200,"message":"success","data":{"expires_in_secs":600}})
),
(
status = 400,
description = "`invalid_email`(邮箱格式)/ `expired`(挑战过期)/ `invalid_payload`(payload 与邮箱不一致)/ `invalid_proof`(未满足难度)",
body = AppResponseData<NoData>,
example = json!({"code": "invalid_email", "message": "email is invalid"})
),
(
status = 409,
description = "`email_taken`(邮箱已注册)",
body = AppResponseData<NoData>,
example = json!({"code": "email_taken", "message": "email is already taken"})
),
(
status = 429,
description = "`rate_limited`(同一邮箱发码过于频繁)",
body = AppResponseData<NoData>,
example = json!({"code": "rate_limited", "message": "too many requests, retry after 60s"})
),
(
status = 500,
description = "`email`(投递失败)/ `cache` / `session` / `serialization` / `internal`",
body = AppResponseData<NoData>,
example = json!({"code": "internal", "message": "internal server error"})
)
)
)]
pub async fn register_code(
State(svc): State<crate::AuthService>,
session: Session,
body: Json<RegisterCodeRequest>,
request_id: RequestId,
) -> Result<AppResponseData<RegisterCodeResponse>, ApiError> {
let response = svc
.send_register_code(session, body.into_inner(), request_id.0)
.await?;
Ok(AppResponseData::ok(response))
}
#[utoipa::path(
post,
path = "/auth/register",
tag = "auth",
operation_id = "auth_register",
summary = "提交注册",
description = "校验工作量证明(payload = 用户名的 UTF-8 字节)、用户名/展示名/出生日期/邮箱、邮箱验证码(键 `auth:code:register:{email}`,尝试上限 `max_verify_attempts`)后,Argon2 重哈希口令并创建用户与初始档案。成功后写会话(user_id、security_version、renew)并记审计事件。",
request_body = UserRegisterRequest,
responses(
(
status = 200,
description = "注册成功且已登录",
body = AppResponseData<UserRegisterResponse>,
example = json!({"code":200,"message":"success","data":{"user_id":1234567,"username":"alice","displayname":"Alice"}})
),
(
status = 400,
description = "`invalid_username` / `invalid_displayname` / `invalid_email` / `invalid_birth_date` / `invalid_password` / `invalid_email_code`(验证码错误或已过期)/ `expired` / `invalid_payload` / `invalid_proof`",
body = AppResponseData<NoData>,
example = json!({"code": "invalid_email_code", "message": "email verification code is invalid"})
),
(
status = 403,
description = "`under_minimum_age`(未满最小年龄)",
body = AppResponseData<NoData>,
example = json!({"code": "under_minimum_age", "message": "you must be at least 13 years old"})
),
(
status = 409,
description = "`username_taken` / `email_taken`",
body = AppResponseData<NoData>,
example = json!({"code": "username_taken", "message": "username is already taken"})
),
(
status = 429,
description = "`email_code_attempts_exceeded`(验证码尝试次数用尽)",
body = AppResponseData<NoData>,
example = json!({"code": "email_code_attempts_exceeded", "message": "too many invalid email verification attempts"})
),
(
status = 500,
description = "`password_hash`(口令重哈希失败)/ `database` / `cache` / `session` / `serialization` / `internal`",
body = AppResponseData<NoData>,
example = json!({"code": "internal", "message": "internal server error"})
)
)
)]
pub async fn register(
State(svc): State<crate::AuthService>,
session: Session,
body: Json<UserRegisterRequest>,
request_id: RequestId,
) -> Result<AppResponseData<UserRegisterResponse>, ApiError> {
let user_agent = session.user_agent()?;
let ip_addr = session.client_ip()?;
let response = svc
.register(
session,
body.into_inner(),
user_agent,
ip_addr,
request_id.0,
)
.await?;
Ok(AppResponseData::ok(response))
}