Files
cozsweet-frontend-nextjs/src/stores/auth/auth-machine.ts
T
kanban 744e23fc29 refactor(stores): split state and event unions into separate files
Extract `<Name>Context` interface + `initialContext` const into
`<name>-context.ts` and the event union into `<name>-events.ts` for
all four state machines (auth, chat, sidebar, user) under src/stores/.

- `*-machine.ts` keeps helpers, actors, actions, and the
  `setup().createMachine()` body; re-exports the types/initial value
  to preserve its public API.
- `*-types.ts` re-exports from the new files for backward compatibility
  (sidebar keeps enum re-exports, chat keeps GuestChatQuota).
- `index.ts` barrels updated to re-export the new files.
- Removed unused model imports (AuthMode, AuthPanelMode) from
  auth-machine.ts; kept LoginType, UiMessage, UserView where still
  used by actors/helpers.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-10 18:24:04 +08:00

190 lines
5.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Auth 状态机(XState v5
*
* 原始 Dart: lib/ui/auth/bloc/auth_bloc.dart + auth_state.dart + auth_event.dart
*
* 本轮迁移:社交登录改由 NextAuth 处理(`@/lib/auth/nextauth`),状态机仅保留
* 邮箱注册/登录 + 通用 reset 事件。社交登录走 `new GoogleLogin().signIn()` /
* `new FacebookLogin().signIn()`。
*/
import { setup, fromPromise, assign } from "xstate";
import type { LoginType } from "@/models/auth/login-type";
import { authRepository } from "@/data/repositories/auth_repository";
import { AuthStorage } from "@/data/storage/auth/auth_storage";
import { Result } from "@/utils/result";
import { AuthContext, initialContext } from "./auth-context";
import type { AuthEvent } from "./auth-events";
// 重新导出 Context / Event 类型,保持 machine 文件的公共 API 不变
export type { AuthContext } from "./auth-context";
export { initialContext } from "./auth-context";
export type { AuthEvent } from "./auth-events";
// ============================================================
// Helpers
// ============================================================
async function readGuestId(): Promise<string | undefined> {
const r = await AuthStorage.getInstance().getDeviceId();
if (r.success && r.data != null) {
return r.data;
}
return undefined;
}
// ============================================================
// Actors(异步服务)
// ============================================================
// 邮箱登录 actor 仅调 authRepository.emailLogin 拿业务 token。
// 本轮起不再调 /api/auth/marker 写 cookie(统一迁到 NextAuth session
// cookie 检测,邮箱登录 proxy 行为在后续轮恢复)。
const emailLoginActor = fromPromise<LoginType, { email: string; password: string }>(
async ({ input }) => {
const guestId = await readGuestId();
const result = await authRepository.emailLogin({ ...input, guestId });
if (Result.isErr(result)) throw result.error;
return "email" as LoginType;
},
);
const emailRegisterThenLoginActor = fromPromise<
LoginType,
{ email: string; password: string; username: string; confirmPassword: string }
>(async ({ input }) => {
const guestId = await readGuestId();
const registerResult = await authRepository.register({ ...input, guestId });
if (Result.isErr(registerResult)) throw registerResult.error;
// 注册后自动登录(对齐 Dart 行为)
const loginResult = await authRepository.emailLogin({
email: input.email,
password: input.password,
guestId,
});
if (Result.isErr(loginResult)) throw loginResult.error;
return "email" as LoginType;
});
// ============================================================
// Machine
// ============================================================
export const authMachine = setup({
types: {
context: {} as AuthContext,
events: {} as AuthEvent,
},
actors: {
emailLogin: emailLoginActor,
emailRegisterThenLogin: emailRegisterThenLoginActor,
},
}).createMachine({
id: "auth",
initial: "idle",
context: initialContext,
states: {
idle: {
on: {
AuthPanelModeChanged: {
actions: assign({
authPanelMode: ({ event }) => event.mode,
errorMessage: null,
}),
},
AuthModeChanged: {
actions: assign({
authMode: ({ event }) => event.mode,
errorMessage: null,
}),
},
AuthFormCleared: {
actions: assign({
email: "",
password: "",
username: "",
confirmPassword: "",
errorMessage: null,
}),
},
AuthReset: {
actions: assign(() => initialContext),
},
AuthEmailLoginSubmitted: "loadingEmailLogin",
AuthEmailRegisterSubmitted: "loadingEmailRegister",
// 社交登录已迁移到 NextAuth + GoogleLogin / FacebookLogin 类
// 状态机保留事件以兼容旧调用方(实际不再触发)
AuthGoogleLoginSubmitted: {},
AuthFacebookLoginSubmitted: {},
AuthAppleLoginSubmitted: {
actions: assign({
errorMessage: "Apple login not implemented",
}),
},
},
},
loadingEmailLogin: {
invoke: {
src: "emailLogin",
input: ({ event }) => {
if (event.type !== "AuthEmailLoginSubmitted")
return { email: "", password: "" };
return { email: event.email, password: event.password };
},
onDone: {
target: "success",
actions: assign({
loginType: ({ event }) => event.output,
errorMessage: null,
}),
},
onError: {
target: "idle",
actions: assign({
errorMessage: ({ event }) =>
event.error instanceof Error ? event.error.message : String(event.error),
}),
},
},
},
loadingEmailRegister: {
invoke: {
src: "emailRegisterThenLogin",
input: ({ event }) => {
if (event.type !== "AuthEmailRegisterSubmitted")
return { email: "", password: "", username: "", confirmPassword: "" };
return {
email: event.email,
password: event.password,
username: event.username,
confirmPassword: event.confirmPassword,
};
},
onDone: {
target: "success",
actions: assign({
loginType: ({ event }) => event.output,
errorMessage: null,
}),
},
onError: {
target: "idle",
actions: assign({
errorMessage: ({ event }) =>
event.error instanceof Error ? event.error.message : String(event.error),
}),
},
},
},
success: {
// 终态:业务层通过 state.matches("success") 判断
},
},
});
export type AuthMachine = typeof authMachine;