mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-08 00:26:37 +08:00
wavelet init
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
import type {InternalAxiosRequestConfig} from 'axios';
|
||||
import {BaseService} from '@/lib/services/core';
|
||||
import type {
|
||||
AuthSource,
|
||||
ChangePasswordRequest,
|
||||
ExternalAccountBinding,
|
||||
LoginRequest,
|
||||
OAuthAuthorizeResponse,
|
||||
OAuthCallbackRequest,
|
||||
OAuthCallbackResult,
|
||||
OAuthLoginUrlResponse,
|
||||
RegisterRequest,
|
||||
UpdateProfileRequest,
|
||||
User,
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* 认证服务
|
||||
* 处理 OAuth 认证、用户信息获取、登出等
|
||||
*/
|
||||
export class AuthService extends BaseService {
|
||||
protected static readonly basePath = '/api/v1';
|
||||
|
||||
/**
|
||||
* 获取 OAuth 登录 URL
|
||||
* @returns OAuth 授权 URL
|
||||
* @throws {ApiErrorBase} 当获取失败时
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const url = await AuthService.getLoginUrl();
|
||||
* window.location.href = url; // 重定向到 OAuth 授权页面
|
||||
* ```
|
||||
*/
|
||||
static async getLoginUrl(): Promise<OAuthLoginUrlResponse> {
|
||||
return this.get<OAuthLoginUrlResponse>('/oauth/login');
|
||||
}
|
||||
|
||||
static async getAuthSources(): Promise<AuthSource[]> {
|
||||
return this.get<AuthSource[]>('/oauth/sources');
|
||||
}
|
||||
|
||||
static async getAuthorizeUrl(source: string, purpose: 'login' | 'bind' = 'login'): Promise<OAuthAuthorizeResponse> {
|
||||
return this.get<OAuthAuthorizeResponse>(`/oauth/${encodeURIComponent(source)}/authorize?purpose=${purpose}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 OAuth 回调
|
||||
* @param request - OAuth 回调参数(state 和 code)
|
||||
* @throws {ApiErrorBase} 当回调处理失败时
|
||||
* @throws {ValidationError} 当 state 无效时
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 在回调页面获取 URL 参数
|
||||
* const params = new URLSearchParams(window.location.search);
|
||||
* const state = params.get('state');
|
||||
* const code = params.get('code');
|
||||
*
|
||||
* if (state && code) {
|
||||
* await AuthService.handleCallback({ state, code });
|
||||
* // 登录成功,跳转到首页
|
||||
* router.push('/home');
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
static async handleCallback(request: OAuthCallbackRequest): Promise<OAuthCallbackResult> {
|
||||
return this.post<OAuthCallbackResult>('/oauth/callback', request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户信息
|
||||
* @returns 用户信息
|
||||
* @throws {UnauthorizedError} 当未登录时
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* try {
|
||||
* const user = await AuthService.getUserInfo();
|
||||
* console.log('当前用户:', user.username);
|
||||
* } catch (error) {
|
||||
* if (error instanceof UnauthorizedError) {
|
||||
* // 跳转到登录页
|
||||
* router.push('/login');
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
static async getUserInfo(): Promise<User> {
|
||||
return this.get<User>('/user-info');
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登出
|
||||
* @throws {ApiErrorBase} 当登出失败时
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* await AuthService.logout();
|
||||
* // 清除本地状态
|
||||
* router.push('/login');
|
||||
* ```
|
||||
*/
|
||||
static async logout(): Promise<void> {
|
||||
await this.get<void>('/oauth/logout');
|
||||
}
|
||||
|
||||
static async login(request: LoginRequest, headers?: Record<string, string>): Promise<User> {
|
||||
return this.post<User>('/user/login', request, headers ? ({ headers } as unknown as InternalAxiosRequestConfig) : undefined);
|
||||
}
|
||||
|
||||
static async register(request: RegisterRequest, headers?: Record<string, string>): Promise<User> {
|
||||
return this.post<User>('/user/register', request, headers ? ({ headers } as unknown as InternalAxiosRequestConfig) : undefined);
|
||||
}
|
||||
|
||||
static async sendEmailCode(email: string, scene: 'register' | 'login', headers?: Record<string, string>): Promise<void> {
|
||||
return this.post<void>('/user/send-email-code', { email, scene }, headers ? ({ headers } as unknown as InternalAxiosRequestConfig) : undefined);
|
||||
}
|
||||
|
||||
static async changePassword(request: ChangePasswordRequest): Promise<void> {
|
||||
return this.post<void>('/user/change-password', request);
|
||||
}
|
||||
|
||||
static async updateProfile(request: UpdateProfileRequest): Promise<User> {
|
||||
return this.put<User>('/user/profile', request);
|
||||
}
|
||||
|
||||
static async getExternalAccountBindings(): Promise<ExternalAccountBinding[]> {
|
||||
return this.get<ExternalAccountBinding[]>('/oauth/external-accounts');
|
||||
}
|
||||
|
||||
static async deleteExternalAccountBinding(id: string): Promise<void> {
|
||||
return this.post<void>(`/oauth/external-accounts/${encodeURIComponent(id)}/delete`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 认证服务模块
|
||||
*
|
||||
* @description
|
||||
* 提供 OAuth 认证相关的功能,包括:
|
||||
* - 获取登录 URL
|
||||
* - 处理 OAuth 回调
|
||||
* - 获取用户信息
|
||||
* - 用户登出
|
||||
* - 发起登录流程
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { AuthService } from '@/lib/services';
|
||||
*
|
||||
* // 发起登录
|
||||
* await AuthService.initiateLogin();
|
||||
*
|
||||
* // 获取用户信息
|
||||
* const user = await AuthService.getUserInfo();
|
||||
* console.log('当前用户:', user.username);
|
||||
*
|
||||
* // 登出
|
||||
* await AuthService.logout();
|
||||
* ```
|
||||
*/
|
||||
|
||||
export { AuthService } from './auth.service';
|
||||
export type {
|
||||
User,
|
||||
OAuthLoginUrlResponse,
|
||||
OAuthCallbackRequest,
|
||||
LoginRequest,
|
||||
RegisterRequest,
|
||||
OAuthAuthorizeResponse,
|
||||
OAuthCallbackResult,
|
||||
AuthSource,
|
||||
ExternalAccountBinding,
|
||||
ChangePasswordRequest,
|
||||
UpdateProfileRequest,
|
||||
} from './types';
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* 用户基本信息
|
||||
*/
|
||||
export interface User {
|
||||
/** 用户 ID */
|
||||
id: string;
|
||||
/** 账户 */
|
||||
username: string;
|
||||
/** 昵称 */
|
||||
nickname: string;
|
||||
/** 头像 URL */
|
||||
avatar_url: string;
|
||||
/** 是否为管理员 */
|
||||
is_admin: boolean;
|
||||
/** 是否需要修改密码 */
|
||||
need_change_password?: boolean;
|
||||
/** 邮箱 */
|
||||
email: string;
|
||||
/** 个人简介 */
|
||||
bio?: string;
|
||||
/** 手机号码 */
|
||||
phone?: string;
|
||||
/** 性别 */
|
||||
gender?: string;
|
||||
/** 个人网站 */
|
||||
website?: string;
|
||||
/** 所在地 */
|
||||
location?: string;
|
||||
}
|
||||
|
||||
export interface UpdateProfileRequest {
|
||||
nickname: string;
|
||||
email: string;
|
||||
avatar_url: string;
|
||||
bio?: string;
|
||||
phone?: string;
|
||||
gender?: string;
|
||||
website?: string;
|
||||
location?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* OAuth 登录 URL 响应
|
||||
* 后端直接返回字符串 URL
|
||||
*/
|
||||
export type OAuthLoginUrlResponse = string;
|
||||
|
||||
/**
|
||||
* OAuth 回调请求参数
|
||||
*/
|
||||
export interface OAuthCallbackRequest {
|
||||
/** 状态码 */
|
||||
state: string;
|
||||
/** 授权码 */
|
||||
code: string;
|
||||
}
|
||||
|
||||
export interface LoginRequest {
|
||||
username: string;
|
||||
password: string;
|
||||
code?: string;
|
||||
}
|
||||
|
||||
export interface RegisterRequest {
|
||||
username: string;
|
||||
password: string;
|
||||
nickname?: string;
|
||||
email?: string;
|
||||
code?: string;
|
||||
}
|
||||
|
||||
export interface OAuthAuthorizeResponse {
|
||||
authorize_url: string;
|
||||
}
|
||||
|
||||
export interface OAuthCallbackResult {
|
||||
status: 'logged_in' | 'bound' | 'need_bind';
|
||||
user?: User;
|
||||
}
|
||||
|
||||
export interface AuthSource {
|
||||
id: string;
|
||||
name: string;
|
||||
type: 'oidc';
|
||||
display_name: string;
|
||||
is_active: boolean;
|
||||
icon_url: string;
|
||||
client_secret_configured: boolean;
|
||||
}
|
||||
|
||||
export interface ExternalAccountBinding {
|
||||
id: string;
|
||||
auth_source_id: string;
|
||||
auth_source_name: string;
|
||||
auth_source_type: string;
|
||||
auth_source_label: string;
|
||||
external_username: string;
|
||||
email: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface ChangePasswordRequest {
|
||||
old_password: string;
|
||||
new_password: string;
|
||||
}
|
||||
Reference in New Issue
Block a user