/** * 注册流程:密码经 hasher 哈希落库 + 首次签发会话。 * * 产品决策(§3 留口):本实现采用「注册即激活并签发会话」路径(欧美刷剧 MVP 追求转化、 * 降低试看→注册摩擦)。验证码确认走独立 sendCode/verifyCode(用于敏感操作 / reset), * 不阻塞注册主路径。库内 grep 不到明文密码持久化、不到自写哈希循环(全走 hasher)。 */ import { AuthError } from './errors.js' import { issueSession } from './session.js' /** * @param {Object} deps * @param {import('./index.js').UserRepo} deps.userRepo * @param {import('./index.js').TokenSigner} deps.tokenSigner * @param {import('./index.js').Hasher} deps.hasher * @param {number} [deps.accessTtlMs] * @param {number} [deps.refreshTtlMs] * @returns {(args: { email: string, password: string, locale?: string, email_verified_at?: Date }) => Promise} */ export function makeRegister(deps) { const { userRepo, hasher } = deps return async function register({ email, password, locale, email_verified_at }) { const normalized = String(email ?? '').trim().toLowerCase() if (!normalized || !password) { throw new AuthError({ code: 'INVALID_CREDENTIALS', message: 'email and password required' }) } const existing = await userRepo.findByEmail(normalized) if (existing) { throw new AuthError({ code: 'EMAIL_TAKEN' }) } const password_hash = await hasher.hash(password) const user = await userRepo.create({ email: normalized, password_hash, locale, email_verified_at }) const issued = await issueSession(deps, { user_id: user.id }) return { user, access_token: issued.access_token, refresh_token: issued.refresh_token } } }