Files
pangolin/client/lib/services/auth_api.dart
T
wangjia dbd183ee00 fix(ci): 修 flutter analyze 闸长期 exit 1(形同虚设)
发现:CI flutter analyze 退出码 1(1204 个 info,0 error/warning),其中 1191 来自
第三方 vendored 包 lucide_icons_patched —— 这道闸一直红/无效。
- analysis_options.yaml:exclude packages/lucide_icons_patched/**(去 1191 噪音)
- ci.yml:flutter analyze --no-fatal-infos(info 是建议级不该硬挡 merge;
  error/warning 仍致命)→ analyze EXIT 0、闸真正可用
- 顺手清 11 个 clean 文件 info:auth_api 删 >>>AUTHLOG 调试 print 残留(+unused sw)、
  account_page/plan_card withOpacity→withValues、vpn_bridge where(is Map)→whereType、
  测试文件删多余 import + final→const
- 剩 2 个 info 在 stats-overhaul dirty 文件(stats_page/device_stat_row),留其合并清
- 验证:flutter analyze --no-fatal-infos EXIT 0(1204→2)、flutter test 48 全过

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 07:39:14 +08:00

137 lines
4.5 KiB
Dart
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_api.dart — 控制面认证 HTTP 客户端
//
// 职责:封装 /v1/auth/* 接口调用。
// 关键约束:所有 HTTP 错误统一包装为 AuthApiException
// UI 层通过 e.statusCode / e.messageZh 显示错误文案。
import 'dart:convert';
import 'package:http/http.dart' as http;
/// 认证接口调用失败时抛出。
/// [statusCode] < 0 表示网络层错误。
class AuthApiException implements Exception {
const AuthApiException({
required this.statusCode,
required this.messageZh,
required this.messageEn,
});
final int statusCode;
final String messageZh;
final String messageEn;
@override
String toString() => 'AuthApiException($statusCode): $messageZh';
}
/// 登录 / 注册成功后返回的 JWT 令牌对。
class AuthTokens {
const AuthTokens({required this.accessToken, required this.refreshToken});
final String accessToken;
final String refreshToken;
factory AuthTokens.fromJson(Map<String, dynamic> m) => AuthTokens(
accessToken: m['access_token'] as String? ?? '',
refreshToken: m['refresh_token'] as String? ?? '',
);
}
/// [AuthApi] 封装 /v1/auth 接口族。
class AuthApi {
AuthApi({required this.baseUrl, http.Client? client})
: _client = client ?? http.Client();
final String baseUrl;
final http.Client _client;
// ── 发送验证码:POST /v1/auth/code ─────────────────────────────
/// 向 [email] 发送 6 位验证码。成功无返回,失败抛 [AuthApiException]。
Future<void> sendCode(String email) async {
final resp = await _post('/v1/auth/code', {'email': email});
if (resp.statusCode != 204 && resp.statusCode != 200) {
_throwFromResponse(resp);
}
}
// ── 注册:POST /v1/auth/register ───────────────────────────────
Future<AuthTokens> register({
required String email,
required String code,
required String password,
}) async {
final resp = await _post('/v1/auth/register', {
'email': email,
'code': code,
'password': password,
});
if (resp.statusCode != 200 && resp.statusCode != 201) {
_throwFromResponse(resp);
}
return AuthTokens.fromJson(jsonDecode(resp.body) as Map<String, dynamic>);
}
// ── 登录:POST /v1/auth/login ───────────────────────────────────
Future<AuthTokens> login({
required String email,
required String password,
}) async {
final resp = await _post('/v1/auth/login', {
'email': email,
'password': password,
});
if (resp.statusCode != 200) _throwFromResponse(resp);
return AuthTokens.fromJson(jsonDecode(resp.body) as Map<String, dynamic>);
}
// ── 刷新 tokenPOST /v1/auth/refresh ──────────────────────────
Future<AuthTokens> refresh(String refreshToken) async {
final resp = await _post('/v1/auth/refresh', {'refresh_token': refreshToken});
if (resp.statusCode != 200) _throwFromResponse(resp);
return AuthTokens.fromJson(jsonDecode(resp.body) as Map<String, dynamic>);
}
// ── 内部 ────────────────────────────────────────────────────────
Future<http.Response> _post(String path, Map<String, dynamic> body) async {
final uri = Uri.parse('$baseUrl$path');
try {
final resp = await _client
.post(
uri,
headers: {'Content-Type': 'application/json'},
body: jsonEncode(body),
)
.timeout(const Duration(seconds: 15));
return resp;
} on Exception catch (e) {
throw AuthApiException(
statusCode: -1,
messageZh: '网络请求失败,请检查连接后重试',
messageEn: 'Network error: $e',
);
}
}
void _throwFromResponse(http.Response resp) {
String messageZh = '操作失败 (HTTP ${resp.statusCode})';
String messageEn = 'Request failed (HTTP ${resp.statusCode})';
try {
final body = jsonDecode(resp.body) as Map<String, dynamic>;
messageZh = body['message_zh'] as String? ?? messageZh;
messageEn = body['message_en'] as String? ?? messageEn;
} catch (_) {}
throw AuthApiException(
statusCode: resp.statusCode,
messageZh: messageZh,
messageEn: messageEn,
);
}
void dispose() => _client.close();
}