Skip to content

Commit 31f0eea

Browse files
committed
我的修改
1 parent b262add commit 31f0eea

9 files changed

Lines changed: 269 additions & 176 deletions

File tree

android/app/src/main/AndroidManifest.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
22
<uses-permission android:name="android.permission.INTERNET"/>
33
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
4-
<uses-permission android:name="android.permission.WAKE_LOCK" />
54

65
<application
76
android:label="wild"

lib/main.dart

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'dart:io';
12
import 'package:flutter/material.dart';
23
import 'package:flutter_bloc/flutter_bloc.dart';
34
import 'package:wild/pages/auth_cubit.dart';
@@ -43,6 +44,7 @@ final darkTheme = ThemeData(
4344
);
4445

4546
Future<void> main() async {
47+
HttpOverrides.global = _LoggingHttpOverrides(); // ← 新增這行
4648
WidgetsFlutterBinding.ensureInitialized();
4749
await AppInfo.init();
4850
await RustLib.init();
@@ -185,3 +187,51 @@ class YourApp extends StatelessWidget {
185187
);
186188
}
187189
}
190+
191+
class _LoggingHttpOverrides extends HttpOverrides {
192+
@override
193+
HttpClient createHttpClient(SecurityContext? context) {
194+
final base = super.createHttpClient(context);
195+
return _LoggingHttpClient(base);
196+
}
197+
}
198+
199+
class _LoggingHttpClient implements HttpClient {
200+
final HttpClient _inner;
201+
_LoggingHttpClient(this._inner);
202+
203+
@override
204+
Future<HttpClientRequest> openUrl(String method, Uri url) async {
205+
final startedAt = DateTime.now();
206+
print('[HTTP][$method] $url');
207+
try {
208+
final req = await _inner.openUrl(method, url);
209+
return _LoggingHttpClientRequest(req, url, method, startedAt);
210+
} catch (e) {
211+
print('[HTTP][ERR][$method] $url -> $e');
212+
rethrow;
213+
}
214+
}
215+
216+
// 其他方法交給原本的 client
217+
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
218+
}
219+
220+
class _LoggingHttpClientRequest implements HttpClientRequest {
221+
final HttpClientRequest _inner;
222+
final Uri url;
223+
final String method;
224+
final DateTime startedAt;
225+
_LoggingHttpClientRequest(this._inner, this.url, this.method, this.startedAt);
226+
227+
@override
228+
Future<HttpClientResponse> close() async {
229+
final resp = await _inner.close();
230+
final ms = DateTime.now().difference(startedAt).inMilliseconds;
231+
print('[HTTP][RESP ${resp.statusCode}] $method $url (${ms}ms)');
232+
return resp;
233+
}
234+
235+
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
236+
}
237+

lib/pages/home/history_page.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,15 @@ class _HistoryItem extends StatelessWidget {
8585
@override
8686
Widget build(BuildContext context) {
8787
final dateFormat = DateFormat('yyyy-MM-dd HH:mm');
88-
final lastReadAt = DateTime.fromMillisecondsSinceEpoch(history.lastReadAt);
88+
int _asMsInt(Object? v) {
89+
if (v == null) return 0;
90+
if (v is int) return v;
91+
if (v is BigInt) return v.toInt(); // 轉成 int 給 DateTime 用
92+
if (v is String) return int.parse(v); // 以防是字串
93+
throw ArgumentError('Unsupported type for timestamp: ${v.runtimeType}');
94+
}
95+
96+
final lastReadAt = DateTime.fromMillisecondsSinceEpoch(_asMsInt(history.lastReadAt));
8997
final colorScheme = Theme.of(context).colorScheme;
9098
final historyCubit = context.read<HistoryCubit>();
9199

new_update.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
## 問題與解決方案
2+
3+
### 問題背景
4+
在台灣環境執行 `wild`(Flutter + Rust 桌面應用)登入帳密時,驗證碼圖片(captcha)不顯示,只出現轉圈圈。
5+
驗證碼 API 來自 `https://www.wenku8.net/checkcode.php`,該站有 Cloudflare 防護機制(JS 挑戰),在缺少必要 Header / Cookie 或未初始化 Session 時會返回 HTML 而非圖片。
6+
7+
---
8+
9+
### 問題原因
10+
- Rust 端 `checkcode()` 方法直接發送 GET 請求,未攜帶必要的 HTTP 標頭與 Cookie。
11+
- 請求前沒有先建立與伺服器的 Session(未取得 PHPSESSID 與可能的 Cloudflare Token)。
12+
- Cloudflare 攔截時會回傳 HTML 挑戰頁,導致前端無法顯示圖片。
13+
14+
---
15+
16+
### 解決方案
17+
18+
#### 1. 啟用 Cookie 支援
19+
`Cargo.toml` 調整 `reqwest` 依賴:
20+
```
21+
toml
22+
reqwest = { version = "0.12", default-features = false, features = ["cookies", "gzip", "brotli", "deflate", "rustls-tls", "http2"] }
23+
24+
```
25+
#### 2. 新增統一請求標頭方法
26+
建立 default_headers_sync(),加上:
27+
28+
- User-Agent
29+
- Referer
30+
- Accept
31+
- Accept-Language
32+
- Connection
33+
34+
#### 3. 初始化 Session
35+
新增 init_session(),先對 /login.php 發 GET,取得初始 Cookie。
36+
37+
38+
#### 4. 改寫 checkcode() 流程
39+
40+
1. 呼叫 init_session() 建立 Session。
41+
42+
2. 發送請求至 /checkcode.php,攜帶完整標頭與 Cookie。
43+
44+
3. 檢查回應 Content-Type:
45+
- image/* → 回傳圖片二進位資料。
46+
- text/html 且包含 Cloudflare 挑戰字樣(__cf_chl_、Just a moment 等)→ 回傳 cf_challenge 讓前端處理。
47+
48+
4. 修正 Rust E0505 借用衝突:先複製標頭值再讀取 bytes()。
49+
50+
#### 5. Cloudflare 挑戰處理(前端)
51+
收到 cf_challenge 時,可在前端啟動 WebView2 載入 wenku8 1–3 秒,讓 JS 完成挑戰並取得 cf_clearance,再重試抓取驗證碼。
52+
53+
54+
55+
#### 關鍵點
56+
在請求驗證碼前,必須先初始化 Session,攜帶完整 HTTP 標頭與 Cookie,並針對 Cloudflare 挑戰頁進行檢測與處理。
File renamed without changes.

pubspec.lock

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ packages:
1313
dependency: transitive
1414
description:
1515
name: async
16-
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
16+
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
1717
url: "https://pub.dev"
1818
source: hosted
19-
version: "2.12.0"
19+
version: "2.13.0"
2020
bloc:
2121
dependency: transitive
2222
description:
@@ -93,10 +93,10 @@ packages:
9393
dependency: transitive
9494
description:
9595
name: fake_async
96-
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
96+
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
9797
url: "https://pub.dev"
9898
source: hosted
99-
version: "1.3.2"
99+
version: "1.3.3"
100100
ffi:
101101
dependency: transitive
102102
description:
@@ -219,10 +219,10 @@ packages:
219219
dependency: transitive
220220
description:
221221
name: leak_tracker
222-
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
222+
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
223223
url: "https://pub.dev"
224224
source: hosted
225-
version: "10.0.8"
225+
version: "10.0.9"
226226
leak_tracker_flutter_testing:
227227
dependency: transitive
228228
description:
@@ -495,10 +495,10 @@ packages:
495495
dependency: transitive
496496
description:
497497
name: vm_service
498-
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
498+
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
499499
url: "https://pub.dev"
500500
source: hosted
501-
version: "14.3.1"
501+
version: "15.0.0"
502502
web:
503503
dependency: transitive
504504
description:
@@ -511,10 +511,10 @@ packages:
511511
dependency: transitive
512512
description:
513513
name: webdriver
514-
sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8"
514+
sha256: "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade"
515515
url: "https://pub.dev"
516516
source: hosted
517-
version: "3.0.4"
517+
version: "3.1.0"
518518
win32:
519519
dependency: transitive
520520
description:

0 commit comments

Comments
 (0)