feat: 档2 接真后端 — mac app 真注册/登录/拉节点
dev 本机置备(新增 dev/):
- docker-compose.yml: MySQL :13306 + Redis :16379(避让本机已占的 3306/6379)
- run-local.sh 一键: 起容器→openssl 生成密钥(dev/.local gitignore)→migrate→
seed→启动 server :18080(避让 8080);LogMailer 把验证码打日志
- seed.sql(1 provider + 1 HK 节点)、run-local.md 手册、.gitignore
后端两处修复(本地无 gRPC 场景,注释意图与代码不符的 bug):
- main.go: 无 gRPC 时真正构造 Hub-only nodeSvc(原只建 hub 没赋值),/v1/nodes 才能挂
- main.go: /me 改 Route 子路由根 Get,修 Get("/me")+Route("/me") 冲突致 404
客户端接真后端:
- token_store: MacOsOptions(useDataProtectionKeyChain:false) 修 keychain -34018
- auth_screen: 删 dev 旁路(test 账户内存登录),所有登录走真 API
验收: 后端 curl 端到端全通(验证码→注册→登录→/nodes→/me);
server go build/vet/test 通过, client analyze 0 + test 84
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,11 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
|
||||
class TokenStore {
|
||||
const TokenStore({FlutterSecureStorage? storage})
|
||||
: _storage = storage ?? const FlutterSecureStorage();
|
||||
: _storage = storage ?? const FlutterSecureStorage(
|
||||
// macOS 用文件式 keychain,避免未签名 app 缺 keychain-access-groups
|
||||
// entitlement 时 write 报 -34018。
|
||||
mOptions: MacOsOptions(useDataProtectionKeyChain: false),
|
||||
);
|
||||
|
||||
final FlutterSecureStorage _storage;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// auth_screen.dart — 登录 / 注册页(邮箱 + 验证码流程)
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
@@ -41,22 +40,6 @@ class _AuthScreenState extends ConsumerState<AuthScreen> {
|
||||
|
||||
late final AuthApi _api = AuthApi(baseUrl: _kApiUrl);
|
||||
|
||||
// ── Dev-only 测试账户旁路(仅 debug build)─────────────────────
|
||||
// 后端 /v1/auth 未就绪时,用此账户直接进入主界面浏览 UI。
|
||||
// 登录后节点列表回退 kDemoNodes,连接键走 mock 动画。
|
||||
static const _devEmail = 'test@pangolin.dev';
|
||||
static const _devPassword = 'test1234';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// debug 下预填测试账户,方便直接点登录。
|
||||
if (kDebugMode) {
|
||||
_email.text = _devEmail;
|
||||
_pw.text = _devPassword;
|
||||
}
|
||||
}
|
||||
|
||||
bool get _emailValid => RegExp(r'\S+@\S+\.\S+').hasMatch(_email.text);
|
||||
|
||||
@override
|
||||
@@ -97,13 +80,6 @@ class _AuthScreenState extends ConsumerState<AuthScreen> {
|
||||
|
||||
Future<void> _doLogin() async {
|
||||
setState(() { _loading = true; _errorZh = null; });
|
||||
// Dev 旁路:debug build 下用测试账户跳过后端直接登录。
|
||||
// 用 devLogin 只设内存态,不写 keychain(规避 -34018 entitlement 问题)。
|
||||
if (kDebugMode && _email.text.trim() == _devEmail && _pw.text == _devPassword) {
|
||||
ref.read(authProvider.notifier).devLogin('dev-access-token');
|
||||
if (mounted) widget.onDone();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final tokens = await _api.login(
|
||||
email: _email.text.trim(),
|
||||
|
||||
Reference in New Issue
Block a user