mirror of
https://github.com/xweiba/location-spoofer.git
synced 2026-09-21 22:30:46 +08:00
docs: explain third-party client integration
This commit is contained in:
@@ -430,6 +430,45 @@ struct FirstSetupView: View {
|
||||
Text("除 Shadowrocket 外,当前客户端配置尚未完成真机验证,页面只提供模块导入入口和通用配置提醒。")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
DisclosureGroup("第三方客户端适配说明") {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text("工作原理")
|
||||
.font(.subheadline.bold())
|
||||
Text("App 不连接远程坐标服务器,而是向 Apple 域名发起一个约定请求。第三方客户端需要在本机拦截该请求、保存 WGS-84 坐标并返回 JSON;定位模块再读取同一份数据,修改 Apple WLOC 响应。")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
Text("配置接口")
|
||||
.font(.subheadline.bold())
|
||||
Text(ThirdPartyProxyManager.configurationEndpoint.absoluteString)
|
||||
.font(.caption.monospaced())
|
||||
.textSelection(.enabled)
|
||||
Text("""
|
||||
查询:GET ?action=query
|
||||
保存:GET ?lon=<经度>&lat=<纬度>&acc=<精度>
|
||||
清除:GET ?action=clear
|
||||
""")
|
||||
.font(.caption.monospaced())
|
||||
.textSelection(.enabled)
|
||||
|
||||
Text("返回格式")
|
||||
.font(.subheadline.bold())
|
||||
Text("""
|
||||
成功:{"success":true,"longitude":113.0,"latitude":22.0,"accuracy":25}
|
||||
失败:{"success":false,"error":"错误说明"}
|
||||
""")
|
||||
.font(.caption.monospaced())
|
||||
.textSelection(.enabled)
|
||||
|
||||
Text("适配要求")
|
||||
.font(.subheadline.bold())
|
||||
Text("客户端需要支持请求脚本、持久化存储、HTTP 200 JSON 响应、Apple WLOC 响应脚本,以及 gs-loc.apple.com / gs-loc-cn.apple.com 的 HTTPS 解密。保存接口和 WLOC 响应脚本必须读取同一份持久化数据。")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding(.top, 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -134,6 +134,57 @@ In this mode:
|
||||
- Wi-Fi, 4G, and 5G support depends on the client;
|
||||
- The configuration may remain active after Location Spoofer closes.
|
||||
|
||||
#### Configuration API and Client Integration
|
||||
|
||||
The app does not upload coordinates to a project server. It sends the following GET request, which the third-party
|
||||
client must intercept locally on the device:
|
||||
|
||||
```text
|
||||
https://gs-loc.apple.com/wloc-settings/save
|
||||
```
|
||||
|
||||
| Action | Query parameters | Purpose |
|
||||
|---|---|---|
|
||||
| Query | `action=query` | Verify module connectivity and read the stored coordinate |
|
||||
| Save | `lon=<WGS-84 longitude>&lat=<WGS-84 latitude>&acc=<accuracy>` | Store the selected coordinate |
|
||||
| Clear | `action=clear` | Remove the stored test coordinate |
|
||||
|
||||
The interception script must return HTTP 200 with JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"longitude": 113.0,
|
||||
"latitude": 22.0,
|
||||
"accuracy": 25
|
||||
}
|
||||
```
|
||||
|
||||
Failures use:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Error description"
|
||||
}
|
||||
```
|
||||
|
||||
When no coordinate is stored, a query may return `{"success":false,"error":"无已保存的坐标"}`. The app interprets
|
||||
that response as “module connected, virtual location inactive.” A successful save response must echo the requested
|
||||
WGS-84 longitude and latitude.
|
||||
|
||||
To integrate another third-party client:
|
||||
|
||||
1. Add an HTTP request script for `gs-loc.apple.com/wloc-settings/save` that parses the parameters and returns the
|
||||
specified JSON;
|
||||
2. Store coordinates, accuracy, and optional state in the client's persistent storage;
|
||||
3. Enable HTTPS decryption for `gs-loc.apple.com` and `gs-loc-cn.apple.com`;
|
||||
4. Intercept `gs-loc(-cn).apple.com/clls/wloc` responses, read the same persistent data, and modify the WLOC response;
|
||||
5. Publish an importable module and verify query, save, clear, and location restoration on a real device.
|
||||
|
||||
The app validates only the configuration endpoint's HTTP status, JSON shape, and coordinate round trip. It does not
|
||||
manage the third-party client's certificate, MITM, VPN, or proxy state.
|
||||
|
||||
Do not enable App Mode interception and Third-party Proxy Mode interception at the same time.
|
||||
|
||||
## Runtime Modes
|
||||
|
||||
@@ -130,6 +130,53 @@ WLOC 配置接口
|
||||
- 是否支持 Wi-Fi、4G 或 5G 取决于客户端;
|
||||
- App 关闭后,第三方客户端中的配置可能继续生效。
|
||||
|
||||
#### 配置接口与客户端适配
|
||||
|
||||
App 不会把坐标上传到项目服务器。它会发起以下 GET 请求,第三方客户端必须在设备本地拦截:
|
||||
|
||||
```text
|
||||
https://gs-loc.apple.com/wloc-settings/save
|
||||
```
|
||||
|
||||
| 操作 | 查询参数 | 用途 |
|
||||
|---|---|---|
|
||||
| 查询 | `action=query` | 检查模块是否连接,并读取当前保存的坐标 |
|
||||
| 保存 | `lon=<WGS-84 经度>&lat=<WGS-84 纬度>&acc=<精度>` | 保存当前选点 |
|
||||
| 清除 | `action=clear` | 删除已保存的测试坐标 |
|
||||
|
||||
拦截脚本必须返回 HTTP 200 和 JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"longitude": 113.0,
|
||||
"latitude": 22.0,
|
||||
"accuracy": 25
|
||||
}
|
||||
```
|
||||
|
||||
失败时返回:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "错误说明"
|
||||
}
|
||||
```
|
||||
|
||||
查询时没有已保存坐标,可以返回 `{"success":false,"error":"无已保存的坐标"}`。App 会把它识别为
|
||||
“模块已连接,但虚拟定位未开启”。保存成功时,响应中的经纬度必须与请求中的 WGS-84 坐标一致。
|
||||
|
||||
要适配新的第三方客户端,需要:
|
||||
|
||||
1. 为 `gs-loc.apple.com/wloc-settings/save` 添加 HTTP 请求脚本,解析上述参数并返回约定 JSON;
|
||||
2. 使用客户端的持久化存储保存坐标、精度和可选状态;
|
||||
3. 为 `gs-loc.apple.com` 和 `gs-loc-cn.apple.com` 配置 HTTPS 解密;
|
||||
4. 拦截 `gs-loc(-cn).apple.com/clls/wloc` 响应,读取同一份持久化数据并修改 WLOC 响应;
|
||||
5. 提供可订阅的模块文件,并确认查询、保存、清除和定位恢复都能在真机完成。
|
||||
|
||||
App 只验证配置接口的 HTTP 状态、JSON 格式和坐标回读,不管理第三方客户端的证书、MITM、VPN 或代理状态。
|
||||
|
||||
不要同时启用 APP 模式代理和第三方代理模式,避免两个代理链路互相干扰。
|
||||
|
||||
## 运行模式
|
||||
|
||||
@@ -47,12 +47,12 @@ extension URLSession: ThirdPartyProxyRequesting {}
|
||||
final class ThirdPartyProxyManager: ObservableObject {
|
||||
static let shared = ThirdPartyProxyManager()
|
||||
static let interceptionHostname = "gs-loc.apple.com"
|
||||
static let configurationEndpoint = URL(string: "https://gs-loc.apple.com/wloc-settings/save")!
|
||||
|
||||
@Published private(set) var connectionState: ThirdPartyProxyConnectionState = .unknown
|
||||
@Published private(set) var activeSettings: ThirdPartyProxySettingsResponse?
|
||||
@Published private(set) var isRequesting = false
|
||||
private let requester: any ThirdPartyProxyRequesting
|
||||
private let endpoint = URL(string: "https://gs-loc.apple.com/wloc-settings/save")!
|
||||
|
||||
init(requester: (any ThirdPartyProxyRequesting)? = nil) {
|
||||
if let requester {
|
||||
@@ -134,7 +134,7 @@ final class ThirdPartyProxyManager: ObservableObject {
|
||||
isRequesting = true
|
||||
defer { isRequesting = false }
|
||||
|
||||
var components = URLComponents(url: endpoint, resolvingAgainstBaseURL: false)!
|
||||
var components = URLComponents(url: Self.configurationEndpoint, resolvingAgainstBaseURL: false)!
|
||||
switch action {
|
||||
case .query:
|
||||
components.queryItems = [URLQueryItem(name: "action", value: "query")]
|
||||
|
||||
@@ -53,6 +53,14 @@ test "$(grep -c '^## ' "$ZH")" -eq "$(grep -c '^## ' "$EN")" \
|
||||
grep -q '当前项目不支持在 Windows 上直接构建 iOS 应用' "$ZH" || fail "Chinese README must reject Windows source builds"
|
||||
grep -q 'Building the iOS app directly on Windows is not supported' "$EN" || fail "English README must reject Windows source builds"
|
||||
grep -q 'docs/COMMUNITY_TUTORIALS.md' "$ZH" || fail "Chinese README must link the community tutorial submission guide"
|
||||
grep -q '^#### 配置接口与客户端适配$' "$ZH" \
|
||||
|| fail "Chinese README must document the third-party integration contract"
|
||||
grep -q '^#### Configuration API and Client Integration$' "$EN" \
|
||||
|| fail "English README must document the third-party integration contract"
|
||||
for contract in 'action=query' 'action=clear' 'lon=<WGS-84'; do
|
||||
grep -q "$contract" "$ZH" || fail "Chinese README is missing third-party contract: $contract"
|
||||
grep -q "$contract" "$EN" || fail "English README is missing third-party contract: $contract"
|
||||
done
|
||||
grep -q '除敏感信息遮挡外,不要自行添加箭头、编号、边框、说明文字或其他标注' \
|
||||
"$ROOT/docs/COMMUNITY_TUTORIALS.md" \
|
||||
|| fail "tutorial submissions must keep source screenshots free of non-privacy annotations"
|
||||
|
||||
@@ -27,6 +27,16 @@ grep -Fq 'Label("打开 \(client.name)"' "$SETUP" \
|
||||
grep -Fq 'Label("打开 \(thirdPartyClient.selectedClient.name)"' "$SETTINGS" || fail "Settings must expose a client launch action"
|
||||
! grep -q '在浏览器打开模块文件' "$SETTINGS" || fail "Settings must not open the module URL as the primary client action"
|
||||
grep -q 'requestThirdPartySetup' "$SETTINGS" || fail "Settings must reopen third-party setup"
|
||||
grep -q 'static let configurationEndpoint' "$MANAGER" \
|
||||
|| fail "the third-party configuration endpoint must have one shared owner"
|
||||
grep -q 'DisclosureGroup("第三方客户端适配说明")' "$SETUP" \
|
||||
|| fail "client selection must expose the third-party integration contract"
|
||||
grep -Fq '查询:GET ?action=query' "$SETUP" \
|
||||
|| fail "client integration guidance must document the query action"
|
||||
grep -Fq '保存:GET ?lon=<经度>&lat=<纬度>&acc=<精度>' "$SETUP" \
|
||||
|| fail "client integration guidance must document WGS-84 coordinate saving"
|
||||
grep -Fq '清除:GET ?action=clear' "$SETUP" \
|
||||
|| fail "client integration guidance must document the clear action"
|
||||
|
||||
for file in wloc.module wloc.sgmodule wloc.conf wloc.lpx wloc.stoverride; do
|
||||
test -s "$MODULES/$file" || fail "missing bundled module: $file"
|
||||
|
||||
Reference in New Issue
Block a user